| Overall Statistics |
|
Total Orders 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Start Equity 100000 End Equity 100000 Net Profit 0% Sharpe Ratio 0 Sortino Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio -2.216 Tracking Error 0.101 Treynor Ratio 0 Total Fees $0.00 Estimated Strategy Capacity $0 Lowest Capacity Asset Portfolio Turnover 0% |
from AlgorithmImports import *
from itertools import combinations, permutations
from statsmodels.tsa.stattools import adfuller
class PairsTradingADF(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2023, 12, 1)
self.SetEndDate(2024, 12, 1)
self.SetCash(100000)
# 3 tech stocks (top by market cap)
self.symbols = ["AAPL", "MSFT", "NVDA"]
for symbol in self.symbols:
self.AddEquity(symbol, Resolution.Daily)
# Generate 6 UNIQUE PAIRS (using combinations)
self.stock_pairs = list(combinations(self.symbols, 2))
self.stock_pairs = list(permutations(self.symbols, 2))
def OnEndOfAlgorithm(self):
# Get 1 year of CLOSE prices
history = self.History(self.symbols, 252, Resolution.Daily)
if history.empty:
self.Debug("No historical data.")
return
# Compute daily returns
closes = history['close'].unstack(level=0)
returns = closes.pct_change().dropna()
# Calculate ADF for each pair
self.pair_adfs = {}
for pair in self.stock_pairs:
stock1, stock2 = pair
if stock1 in returns.columns and stock2 in returns.columns:
spread = returns[stock1] - returns[stock2] # return spread
adf_result = adfuller(spread.dropna())
self.pair_adfs[pair] = {
"ADF Stat": adf_result[0],
"P-Value": adf_result[1],
"5% Critical": adf_result[4]['5%'] # add critical value
}
# Print all results line-by-line
self.Debug("ADF Results (All Pairs):")
for pair, stats in self.pair_adfs.items():
self.Debug(f"{pair}: ADF={stats['ADF Stat']:.4f} | "
f"P-Val={stats['P-Value']:.4f} | Critical_5%={stats['5% Critical']:.4f}")
# Extreme pairs
if self.pair_adfs:
min_pair = min(self.pair_adfs, key=lambda k: self.pair_adfs[k]["ADF Stat"])
max_pair = max(self.pair_adfs, key=lambda k: self.pair_adfs[k]["ADF Stat"])
self.Debug(f"\nLOWEST ADF: {min_pair} ({self.pair_adfs[min_pair]['ADF Stat']:.4f})")
self.Debug(f"HIGHEST ADF: {max_pair} ({self.pair_adfs[max_pair]['ADF Stat']:.4f})")