QuantLearn
Trading Strategies
Oil Money Trading
Commodity-currency correlation strategies
Historical Econometric Analysis
Comprehensive analysis of oil-currency relationships across multiple countries and time periods, demonstrating when causality exists versus pure correlation.

Granger Causality Test Results
Statistical results showing periods when oil prices have significant predictive power for various petrocurrencies.

Oil-Currency Regime Analysis
Rolling correlation analysis identifying strong vs weak oil-currency relationship periods with structural break detection.

Strategy Performance by Currency
Comparative performance across different petrocurrencies (CAD, NOK, RUB, MXN) during various oil market cycles.
Petrocurrency Causality Analysis
Oil Money Trading investigates whether crude oil truly drives petrocurrency movements or if the relationship is merely correlational.
Traditional research examines correlation between oil and currencies like CAD, NOK, RUB, but correlation ≠ causation.
The strategy requires rigorous econometric testing including Granger causality, cointegration, and structural break analysis.
Petrocurrencies should theoretically strengthen when oil prices rise due to improved terms of trade and current account balances.
Reality is more complex: central bank interventions, monetary policy, and global risk sentiment can override oil fundamentals.
The strategy combines quantitative oil-currency modeling with fundamental economic analysis.
Success depends on identifying when the oil-currency relationship is strong versus when other factors dominate.
Mathematical Foundation
1Granger Causality Test
Tests if oil prices have predictive power for currency movements beyond what currency's own history provides.
2Error Correction Model
Long-run relationship between oil and currency with short-term adjustment dynamics.
3Structural Break Test
Tests for structural breaks in oil-currency relationship using Andrews' supremum F-test.
4Rolling Correlation
Time-varying correlation coefficient over rolling window w to detect regime changes.
Core Algorithm Implementation
The Oil Money algorithm combines econometric testing with dynamic regime detection to identify tradeable oil-currency relationships.
Data Preparation and Testing
def prepare_oil_currency_data(oil_prices, fx_rates, country='CAD'):
"""Prepare and test oil-currency data for econometric analysis"""
# Align data and handle missing values
data = pd.concat([oil_prices, fx_rates], axis=1).dropna()
data.columns = ['oil', 'fx']
# Log transformation for interpretation as elasticities
data['log_oil'] = np.log(data['oil'])
data['log_fx'] = np.log(data['fx'])
# Calculate returns
data['oil_returns'] = data['log_oil'].diff()
data['fx_returns'] = data['log_fx'].diff()
# Test for unit roots (stationarity)
from statsmodels.tsa.stattools import adfuller
adf_oil = adfuller(data['log_oil'].dropna())
adf_fx = adfuller(data['log_fx'].dropna())
# Test for cointegration
from statsmodels.tsa.vector_ar.vecm import coint_johansen
if adf_oil[1] > 0.05 and adf_fx[1] > 0.05: # Both non-stationary
johansen_test = coint_johansen(data[['log_oil', 'log_fx']].dropna(), det_order=0, k_ar_diff=1)
cointegration_p = johansen_test.lr1[0] # Trace statistic
else:
cointegration_p = np.nan
return data, {
'oil_unit_root_p': adf_oil[1],
'fx_unit_root_p': adf_fx[1],
'cointegration_evidence': cointegration_p,
'country': country
}Comprehensive data preparation including stationarity testing and cointegration analysis for oil-currency pairs.
Granger Causality Analysis
def granger_causality_analysis(data, max_lags=5):
"""Test for Granger causality between oil and currency"""
from statsmodels.tsa.stattools import grangercausalitytests
# Test oil -> currency causality
oil_to_fx_results = grangercausalitytests(
data[['fx_returns', 'oil_returns']].dropna(),
maxlag=max_lags,
verbose=False
)
# Test currency -> oil causality
fx_to_oil_results = grangercausalitytests(
data[['oil_returns', 'fx_returns']].dropna(),
maxlag=max_lags,
verbose=False
)
# Extract p-values for each lag
oil_to_fx_pvals = [oil_to_fx_results[lag+1][0]['ssr_ftest'][1] for lag in range(max_lags)]
fx_to_oil_pvals = [fx_to_oil_results[lag+1][0]['ssr_ftest'][1] for lag in range(max_lags)]
# Find optimal lag based on minimum p-value
best_lag_oil_fx = np.argmin(oil_to_fx_pvals) + 1
best_lag_fx_oil = np.argmin(fx_to_oil_pvals) + 1
return {
'oil_to_fx_pval': min(oil_to_fx_pvals),
'fx_to_oil_pval': min(fx_to_oil_pvals),
'best_lag_oil_fx': best_lag_oil_fx,
'best_lag_fx_oil': best_lag_fx_oil,
'oil_granger_causes_fx': min(oil_to_fx_pvals) < 0.05,
'fx_granger_causes_oil': min(fx_to_oil_pvals) < 0.05
}Rigorous Granger causality testing to establish whether oil has predictive power for currency movements.
Dynamic Regime Detection
def detect_oil_fx_regimes(data, window=60, correlation_threshold=0.3):
"""Detect regime changes in oil-currency relationship"""
# Rolling correlations and statistics
data['rolling_corr'] = data['oil_returns'].rolling(window).corr(data['fx_returns'])
data['rolling_oil_vol'] = data['oil_returns'].rolling(window).std() * np.sqrt(252)
data['rolling_fx_vol'] = data['fx_returns'].rolling(window).std() * np.sqrt(252)
# Regime identification
data['strong_oil_regime'] = (
(abs(data['rolling_corr']) > correlation_threshold) &
(data['rolling_oil_vol'] > data['rolling_oil_vol'].quantile(0.6))
)
# Structural break detection using CUSUM test
from statsmodels.stats.diagnostic import breaks_cusumolsresid
try:
# Simple regression for CUSUM test
y = data['fx_returns'].dropna()
x = data['oil_returns'].dropna()
aligned_data = pd.concat([y, x], axis=1).dropna()
if len(aligned_data) > 30:
cusum_stat, cusum_p = breaks_cusumolsresid(
aligned_data.iloc[:, 0],
aligned_data.iloc[:, 1:2]
)
data['cusum_break_detected'] = cusum_p < 0.05
else:
data['cusum_break_detected'] = False
except:
data['cusum_break_detected'] = False
return dataDynamic regime detection using rolling correlations and structural break tests to identify periods of strong oil-currency relationships.
Trading Signal Generation
def generate_oil_money_signals(data, causality_results, min_correlation=0.4):
"""Generate trading signals based on oil-currency analysis"""
data['signal'] = 0
data['regime_strength'] = 0
# Only trade when Granger causality is detected
if not causality_results['oil_granger_causes_fx']:
return data
# Get optimal lag from causality analysis
lag = causality_results['best_lag_oil_fx']
for i in range(lag, len(data)):
current_corr = data['rolling_corr'].iloc[i]
in_strong_regime = data['strong_oil_regime'].iloc[i]
if pd.notna(current_corr) and abs(current_corr) > min_correlation and in_strong_regime:
# Oil returns for signal generation
oil_signal = np.sign(data['oil_returns'].iloc[i-lag:i].mean())
# Direction depends on correlation sign
if current_corr > 0: # Positive correlation
fx_signal = oil_signal # Same direction
else: # Negative correlation
fx_signal = -oil_signal # Opposite direction
data.loc[data.index[i], 'signal'] = fx_signal
data.loc[data.index[i], 'regime_strength'] = abs(current_corr)
# Risk management: exit signals during regime breaks
break_points = data['cusum_break_detected'].shift(1).fillna(False)
data.loc[break_points, 'signal'] = 0
# Position management
data['position'] = data['signal'].replace(0, np.nan).fillna(method='ffill').fillna(0)
return dataSignal generation that only trades when Granger causality is detected and during strong oil-currency correlation regimes.
Implementation Steps
- 1Select appropriate oil-currency pairs (WTI/Brent with CAD, NOK, RUB, MXN)
- 2Test for unit roots and cointegration between oil prices and exchange rates
- 3Conduct Granger causality tests to establish predictive relationships
- 4Implement rolling correlation analysis to detect regime changes
- 5Use structural break tests to identify relationship instability
- 6Generate signals only during periods of strong, causal oil-currency relationships
- 7Apply position sizing based on regime strength and volatility
- 8Monitor for structural breaks and adjust exposure accordingly
- 9Incorporate fundamental analysis (oil inventories, OPEC decisions, geopolitical events)
Key Metrics
Risk Considerations
Practice Implementation
Prerequisites
Mathematical Background
- • Linear regression and OLS estimation
- • Time series analysis (stationarity, unit roots)
- • Hypothesis testing and p-values
- • Basic econometrics (error correction models)
Technical Skills
- • Python programming (pandas, numpy)
- • Statistical libraries (statsmodels)
- • Data visualization (matplotlib)
- • Financial data handling (yfinance)
Complete Implementation
Access the full Python implementation from the original quantitative trading repository:
# Complete pair trading implementation
git clone https://github.com/je-suis-tm/quant-trading.git
cd quant-trading
python "Pair trading backtest.py"
# Modify tickers and parameters for your own analysisLearning Checkpoints
Understand Cointegration
Can you explain why two assets might be cointegrated and what breaks this relationship?
Interpret Statistical Tests
Practice reading ADF test results and understanding when to accept/reject cointegration.
Signal Generation
Implement Z-score calculations and understand threshold selection (±1σ vs ±2σ).
Risk Management
Understand position sizing, monitoring regime changes, and exit strategies.
Recommended Learning Path
Immediate Actions
- Download and run the Python script
- Test with different asset pairs
- Experiment with threshold parameters
Advanced Studies
- Learn Johansen cointegration test
- Study Vector Error Correction Models
- Explore multiple asset pair trading
Important Disclaimer
This strategy involves significant risk. Historical cointegration relationships can break permanently. Always use proper risk management, position sizing, and never risk more than you can afford to lose. Paper trade extensively before using real capital.