Overall Statistics |
Total Orders 12 Average Win 1.68% Average Loss -2.18% Compounding Annual Return -23.767% Drawdown 27.300% Expectancy -0.409 Start Equity 200000 End Equity 155944.35 Net Profit -22.028% Sharpe Ratio -1.51 Sortino Ratio -0.985 Probabilistic Sharpe Ratio 0.456% Loss Rate 67% Win Rate 33% Profit-Loss Ratio 0.77 Alpha -0.184 Beta -0.269 Annual Standard Deviation 0.142 Annual Variance 0.02 Information Ratio -1.655 Tracking Error 0.198 Treynor Ratio 0.796 Total Fees $30.19 Estimated Strategy Capacity $200000000.00 Lowest Capacity Asset MSFT R735QTJ8XC9X Portfolio Turnover 1.85% |
#region imports from AlgorithmImports import * #endregion # Your New Python File #region imports from AlgorithmImports import * #endregion import numpy as np import pandas as pd from datetime import timedelta, datetime import math import statsmodels.api as sm from statsmodels.tsa.stattools import coint, adfuller ''' This is an example of implementation of the pairs trading strategy discussed in lecture8 ''' class PairsTradingAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2023, 1, 1) self.SetEndDate(2023,12,1) self.SetCash(200000) self.enter = 2 # Set the enter threshold self.exit = 0 # Set the exit threshold self.lookback = 90 # Set the loockback period 90 days self.pairs =['GOOG','MSFT'] self.symbols =[] for ticker in self.pairs: self.AddEquity(ticker, Resolution.Daily) self.symbols.append(self.Symbol(ticker)) self.sym1 = self.symbols[0] self.sym2 = self.symbols[1] def stats(self, symbols): #Use Statsmodels package to compute linear regression and ADF statistics self.df = self.History(symbols, self.lookback) self.dg = self.df["open"].unstack(level=0) #self.Debug(self.dg) ticker1= str(symbols[0]) ticker2= str(symbols[1]) Y = self.dg[ticker1].apply(lambda x: math.log(x)) X = self.dg[ticker2].apply(lambda x: math.log(x)) X = sm.add_constant(X) model = sm.OLS(Y,X) results = model.fit() sigma = math.sqrt(results.mse_resid) # standard deviation of the residual slope = results.params[1] intercept = results.params[0] res = results.resid #regression residual mean of res =0 by definition zscore = res/sigma adf = adfuller (res) return [adf, zscore, slope] def OnData(self, data): self.IsInvested = (self.Portfolio[self.sym1].Invested) or (self.Portfolio[self.sym2].Invested) self.ShortSpread = self.Portfolio[self.sym1].IsShort self.LongSpread = self.Portfolio[self.sym1].IsLong stats = self.stats([self.sym1, self.sym2]) self.beta = stats[2] zscore= stats[1][-1] self.wt1 = 1/(1+self.beta) self.wt2 = self.beta/(1+self.beta) self.pos1 = self.Portfolio[self.sym1].Quantity self.px1 = self.Portfolio[self.sym1].Price self.pos2 = self.Portfolio[self.sym2].Quantity self.px2 = self.Portfolio[self.sym2].Price self.equity =self.Portfolio.TotalPortfolioValue if self.IsInvested: if self.ShortSpread and zscore <= self.exit or \ self.LongSpread and zscore >= self.exit: self.Liquidate() else: if zscore > self.enter: #short spread #rememebr SetHoldings take a Symbol as its first variable. self.SetHoldings(self.sym1, -self.wt1) self.SetHoldings(self.sym2, self.wt2) if zscore < - self.enter: #long the spread self.SetHoldings(self.sym1, self.wt1) self.SetHoldings(self.sym2, -self.wt2) self.pos1 = self.Portfolio[self.sym1].Quantity self.pos2 = self.Portfolio[self.sym2].Quantity self.Debug("sym1 " + str(self.sym1.Value) + " /w "+ str(self.pos1) + " sym2 " +str(self.sym2.Value) + " /w "+str(self.pos2)) self.Debug("Total Account Equity: "+ str( self.equity) + "Total Marginused: "+ str( self.Portfolio.TotalMarginUsed))