Hello,

I this is my first algo on quantconnect. I tried to implement an pairs trading algo with automatic pairs selection.

I use predefined models except for my alpha model -> See in first code Snippet.

import statsmodels.api as sm
from statsmodels.tsa.stattools import adfuller
import numpy as np
import pandas as pd
from QuantConnect.Algorithm.Framework.Alphas import *
class PairsAlpha(AlphaModel):
def __init__(self,algorithm):
try:
self.securities =[]
self.look_back_window = 30
self.pairs=dict()
self.coint_p_val = 0.05
self.max_trade_pairs = 15
algorithm.Debug("Init Alpha")
self.asset1priceHist=[]
self.asset2priceHist=[]


except:

algorithm.Debug("Init Alpha exep")
def setPairs(self,pairs):
self.pairs = pairs

def OnSecuritiesChanged(self, algorithm, changes):
algorithm.Debug("Onchange Alpha")
for security in changes.AddedSecurities:
if not security in self.securities:
self.securities.append(security)
for security in changes.RemovedSecurities:

if security in self.securities:
self.securities.remove(security)
def Update(self, algorithm, data):
algorithm.Debug("Update Alpha")

pairs = self.pairs


if(len(pairs)==0):
self.find_pairs(algorithm)
pairs = self.pairs
if(len(pairs)==0):
return
pairs_w_weights = []

for pair in pairs:
pairs_w_weights.append([pair[0],pair[1],self.get_weights(self,algorithm,pair,1.25,3.0)])
algorithm.Debug(pairs_w_weights[:5])
Insights=[]
for pairWeight in pairs_w_weights[:15]:
if pairWeight[2]>0:
InsightA=Insight.Price(pairWeight[0], timedelta(minutes = 20), InsightDirection.Up, pairWeight[2], 0.95,None,None,None)
InsightB=Insight.Price(pairWeight[1], timedelta(minutes = 20), InsightDirection.Down, pairWeight[2], 0.95,None,None,None)
Insights.append([InsightA,InsightB])
elif pairWeight[2]<0:

InsightA=Insight.Price(pairWeight[0], timedelta(minutes = 20), InsightDirection.Down, pairWeight[2], 0.95,None,None,None)
InsightB=Insight.Price(pairWeight[1], timedelta(minutes = 20), InsightDirection.Up, pairWeight[2], 0.95,None,None,None)
Insights.append([InsightA,InsightB])
return Insight.Group(Insights)
# Skipping magnitude, confidence and source model and assigning 25% to weighting.
# insight = Insight.Price("IBM", timedelta(minutes = 20), InsightDirection.Up, None, None, None, 0.25)

#higher price stock (stock A) long -> the difference between the pair will grow if get_weights > 0





def get_weights(self,algorithm,pair,realizeProfitThreshold,stopLossThreshold):
algorithm.Log("getWeights regular")
try:
asset1PriceHist=pair.get_asset1hist

asset2PriceHist=pair.get_asset2hist


pricedif=asset1PriceHist.sub(asset2PriceHist)
hist_mean_pricedif=pricedif.mean()
hist_std_pricedif=pricedif.std()
asset1_curPrice= algorithm.History(pair[0], 3, Resolution.Minute).mean()
asset2_curPrice= algorithm.History(pair[1], 3, Resolution.Minute).mean()
curPriceDif=asset1_curPrice-asset2_curPrice
curDeviation=abs(curPriceDif-hist_mean_pricedif)/hist_std_pricedif
asset1_curPrice=asset1_curPrice.mean()
asset2_curPrice=asset2_curPrice.mean()
maxprice=max(asset1_curPrice,asset2_curPrice)
weight=0
if(hist_std_pricedif/maxprice > 0.1):
return 0
if(curDeviation>realizeProfitThreshold and curDeviation<stopLossThreshold):
if curPriceDif > hist_mean_pricedif:
weight=(-1.0*curDeviation)
else:
weight=(1.0*curDeviation)
return weight
except:
algorithm.Log("getWeight")

def find_pairs(self,algorithm):



algorithm.Debug("find_pairs")
try:
algorithm.Log("before assign Symbols")



symbols = list()
for kvp in algorithm.ActiveSecurities:
security = kvp.Value
symbol = security.Symbol

symbols.append(security)
lengthofSymbols = len(symbols)



lengthofSymbols = len(symbols)
if(len(symbols)==0):
return

for i in range(0, len(symbols)):
algorithm.Log("symbol loop start")
asset_i = symbols[i]
algorithm.Log("assign asset symbol")
for j in range(1 + i, len(symbols)):
asset_j = symbols[j]

pair_symbol = (asset_i, asset_j)
invert = (asset_j, asset_i)

if len(pair_symbol)==0:
return

PairPassTest = self.HasPassedTest(algorithm=algorithm,asset1=asset_i,asset2=asset_j,significance_level= self.coint_p_val)
self
if not PairPassTest: #jedes mal neu prüfen ob noch cointegrated, wenn nicht mehr -> dann raus aus der Liste
if pair_symbol in self.pairs:
self.pairs.pop(pair_symbol)
elif invert in self.pairs:
self.pairs.pop(invert)
continue
if(len(self.pairs)>=self.max_trade_pairs):
continue
pair = Pair(asset_i,asset_j)
pair.set_asset1hist(hist=self.asset1PriceHist)
pair.set_asset2hist(hist=self.asset2PriceHist)
self.pairs[pair_symbol] = pair #eql to pair hist

except Exception as e:
algorithm.Log(str(e))
algorithm.Log("UpdatePairs Exception")
def HasPassedTest(self,algorithm, asset1,asset2,significance_level):



self.asset1PriceHist=algorithm.History(asset1.Symbol, self.look_back_window, Resolution.Daily)
self.asset2PriceHist=algorithm.History(asset2.Symbol, self.look_back_window, Resolution.Daily)

return self.is_cointegrated(algorithm=algorithm,data_x=self.asset1PriceHist,data_y=self.asset2PriceHist,cutoff=significance_level)

'''Check whether the assets pass a pairs trading test
Args:
self: The self instance that experienced the change in securities
asset1: The first asset's symbol in the pair
asset2: The second asset's symbol in the pair
Returns:
True if the statistical test for the pair is successful'''
def is_stationary(self,algorithm,data_x,cutoff):

#Augmented Dickey-Fuller unit root test
p_value = adfuller(data_x)[1]
if p_value < cutoff:
return True
else:
return False

def is_cointegrated(self,algorithm,data_x,data_y,cutoff):
#Check linear reg
algorithm.Debug(cutoff)
X=pd.DataFrame(data_x)





Y=pd.DataFrame(data_y)
if(Y.empty or X.empty):
return False

X=sm.add_constant(X['open'])
Y=pd.Series(Y['open'])
index=0
YPrice=[x for x in range(0,self.look_back_window)]
for data in Y:
YPrice[index]=data
index+=1

XonlyVals=[x for x in range(0,self.look_back_window)]

index=0
for xdata in X['open']:
XonlyVals[index]=xdata
index+=1


xdata
X=sm.add_constant(XonlyVals)


Y=YPrice
Xtype=type(X)
Ytype=type(Y)
Ylen=len(Y)
#Xlen=len(X)
#algorithm.Debug(len(Y))
#X.reindex(Y.index)
#Ylen=len(Y)
#Xlen=len(X)
linear_comb=sm.OLS(Y,X,'drop',True)
fitted=linear_comb.fit()
const,beta=fitted.params
residual=fitted.resid

if (beta < 0):

return False
else:

return self.is_stationary(algorithm=algorithm,data_x=residual,cutoff=cutoff)

class Pair():
def __init__(self,asset1Symbol,asset2Symbol):
self.assetList = [asset1Symbol,asset2Symbol]
self.asset1hist=[]
self.asset2hist=[]
def get_asset1hist(self):
return self.asset1hist
def get_asset2hist(self):
return self.asset2hist
def set_asset1hist(self,hist):
self.asset1hist=hist
def set_asset2hist(self,hist):
self.asset2hist=hist
def __getitem__(self, key):
return self.assetList[key]

For your Interest my algo:
 

from Execution.ImmediateExecutionModel import ImmediateExecutionModel
import time
from PairAlpha import PairsAlpha
from Selection.QC500UniverseSelectionModel import QC500UniverseSelectionModel

class TransdimensionalUncoupledShield(QCAlgorithm):

def Initialize(self):

self.SetStartDate(2019, 1, 5) # Set Start Date
self.SetEndDate(2019,6,25)
self.SetCash(100000) # Set Strategy Cash



self.look_back_window=30
self.coint_p_val=0.05
self.SetUniverseSelection(QC500UniverseSelectionModel())
self.UniverseSettings.Resolution = Resolution.Daily
self.max_trade_pairs = 15



self.SetPortfolioConstruction(InsightWeightingPortfolioConstructionModel())
self.SetRiskManagement(MaximumDrawdownPercentPerSecurity(0.02))
self.SetExecution(ImmediateExecutionModel())
self.SetWarmup(timedelta(7))
self.Debug("init algoparts done")
self.RemoveSecurity("MSG")
self.SetAlpha(PairsAlpha(algorithm=self))





def OnData(self, data):
self.Debug("onData")
data
'''OnData event is the primary entry point for your self. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''

# if not self.Portfolio.Invested:
# self.SetHoldings("SPY", 1)

# sort the data by daily dollar volume and take the top '5'



def CoarseSelectionFunction(self, coarse):
# sort descending by daily dollar volume
sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True)
MinPrice = [ x.Symbol for x in sortedByDollarVolume if x.Price > 20 ]

# return the symbol objects of the top entries from our sorted collection
return MinPrice[:100]

and the error message/Stacktrace:
System.TimeoutException: Algorithm took longer than 10 minutes on a single time loop. CurrentTimeStepElapsed: 10.0 minutes
at QuantConnect.Isolator.MonitorTask (System.Threading.Tasks.Task task, System.TimeSpan timeSpan, System.Func`1[TResult] withinCustomLimits, System.Int64 memoryCap, System.Int32 sleepIntervalMillis) [0x002d3] in <78602b4cadea4f3a881b10b839873497>:0
at QuantConnect.Isolator.ExecuteWithTimeLimit (System.TimeSpan timeSpan, System.Func`1[TResult] withinCustomLimits, System.Action codeBlock, System.Int64 memoryCap, System.Int32 sleepIntervalMillis, QuantConnect.Util.WorkerThread workerThread) [0x00092] in <78602b4cadea4f3a881b10b839873497>:0
at QuantConnect.Lean.Engine.Engine.Run (QuantConnect.Packets.AlgorithmNodePacket job, QuantConnect.Lean.Engine.AlgorithmManager manager, System.String assemblyPath, QuantConnect.Util.WorkerThread workerThread) [0x009f0] in Lean.Engine.AlgorithmManager manager, System.String assemblyPath, QuantConnect.Util.WorkerThread workerThread) [0x009f0] in <7aff72eb1cda49b9aff04ff68484be84>:0