| Overall Statistics |
|
Total Trades 396 Average Win 0.17% Average Loss -0.06% Compounding Annual Return -2.515% Drawdown 3.600% Expectancy 0.003 Net Profit -1.878% Sharpe Ratio -0.916 Sortino Ratio -1.013 Probabilistic Sharpe Ratio 8.147% Loss Rate 74% Win Rate 26% Profit-Loss Ratio 2.89 Alpha -0.043 Beta -0.035 Annual Standard Deviation 0.048 Annual Variance 0.002 Information Ratio -0.529 Tracking Error 0.149 Treynor Ratio 1.261 Total Fees $456.50 Estimated Strategy Capacity $3900000.00 Lowest Capacity Asset PEP R735QTJ8XC9X Portfolio Turnover 4.55% |
from AlgorithmImports import *
from datetime import timedelta, datetime
class SMAPairsTrading(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2018, 7, 1)
self.SetEndDate(2019, 3, 31)
self.SetCash(100000)
self.ko = self.AddEquity("KO", Resolution.Hour, dataNormalizationMode=DataNormalizationMode.Raw).Symbol
self.pep = self.AddEquity("PEP", Resolution.Hour, dataNormalizationMode=DataNormalizationMode.Raw).Symbol
self.spreadMean = SimpleMovingAverage(500)
self.spreadStd = StandardDeviation(500)
self.period = timedelta(hours=2)
#1. Call for 500 bars of history data for each symbol in the pair and save to the variable history
history = self.History[TradeBar]([self.ko, self.pep], 500)
#2. Iterate through the history and update the mean and standard deviation with historical data
for bars in history:
time, spread = self.GetSpread(bars)
if spread:
self.UpdateIndicators(time, spread)
def GetSpread(self, bars: TradeBars):
pep_bar = bars.get(self.pep)
ko_bar = bars.get(self.ko)
if not (pep_bar and ko_bar):
return None, None
return pep_bar.EndTime, abs(pep_bar.Close - ko_bar.Close)
def UpdateIndicators(self, time, spread):
self.spreadMean.Update(time, spread)
self.spreadStd.Update(time, spread)
def OnData(self, data):
_, spread = self.GetSpread(data.Bars)
if not spread:
return
self.UpdateIndicators(self.Time, spread)
weight = .5
spreadMean = self.spreadMean.Current.Value
upperthreshold = spreadMean + self.spreadStd.Current.Value
lowerthreshold = spreadMean - self.spreadStd.Current.Value
# Close positions if reverses to the mean
ko_holdings = self.Portfolio[self.ko]
if ko_holdings.IsLong and spread < spreadMean:
self.PlotSpread(spread, lowerthreshold, spreadMean, upperthreshold)
self.Liquidate()
return
if ko_holdings.IsShort and spread > spreadMean:
self.Liquidate()
self.PlotSpread(spread, lowerthreshold, spreadMean, upperthreshold)
return
if spread > upperthreshold:
self.PlotSpread(spread, lowerthreshold, spreadMean, upperthreshold)
self.SetHoldings([
PortfolioTarget(self.ko, weight),
PortfolioTarget(self.pep, -weight)
])
if spread < lowerthreshold:
self.PlotSpread(spread, lowerthreshold, spreadMean, upperthreshold)
self.SetHoldings([
PortfolioTarget(self.ko, -weight),
PortfolioTarget(self.pep, weight)
])
def PlotSpread(self, spread, lowerthreshold, mean, upperthreshold):
self.Plot('Spread', 'Current', spread)
self.Plot('Spread', 'Mean', mean)
self.Plot('Spread', 'Lower', lowerthreshold)
self.Plot('Spread', 'Upper', upperthreshold)