Overall Statistics |
Total Trades 1516 Average Win 1.51% Average Loss -0.67% Compounding Annual Return -6.064% Drawdown 28.100% Expectancy -0.020 Net Profit -26.505% Sharpe Ratio -0.378 Probabilistic Sharpe Ratio 0.011% Loss Rate 70% Win Rate 30% Profit-Loss Ratio 2.24 Alpha -0.044 Beta 0.068 Annual Standard Deviation 0.099 Annual Variance 0.01 Information Ratio -0.661 Tracking Error 0.194 Treynor Ratio -0.551 Total Fees $0.00 Estimated Strategy Capacity $1000.00 Lowest Capacity Asset WTICOUSD 8I |
#region imports from AlgorithmImports import * from sklearn import linear_model #endregion class TradeWtiBrentSpreadAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2018, 1, 1) self.SetEndDate(2022, 12, 1) self.SetCash(100000) # import the spot price data self.wti = self.AddCfd("WTICOUSD", Resolution.Daily, Market.Oanda).Symbol self.b = self.AddCfd("BCOUSD", Resolution.Daily, Market.Oanda).Symbol # Recalibrate model every month self.Train(self.DateRules.MonthStart(), self.TimeRules.At(0,0), self.MyTrainingMethod) # create the moving average indicator of the pread = WTI price - BRENT price self.SpreadSMA = SimpleMovingAverage(20) hist = self.History([self.wti, self.b], 252, Resolution.Daily).unstack(level=0)['close'].ffill().dropna() wti_hist = hist[self.wti.ID.ToString()] b_hist = hist[self.b.ID.ToString()] spread = wti_hist - b_hist for index, value in spread.iloc[-20:].items(): self.SpreadSMA.Update(index, value) # linear regression to decide the fair value self.regr = linear_model.LinearRegression() self.regr.fit(wti_hist.values.reshape(-1, 1), b_hist.values.reshape(-1, 1)) # Add the spread plot and mark the long/short spread point spreadPlot = Chart("Spread Plot") spreadPlot.AddSeries(Series("Spread", SeriesType.Line, 0)) spreadPlot.AddSeries(Series("Long Spread Trade", SeriesType.Scatter, 0)) spreadPlot.AddSeries(Series("Short Spread Trade", SeriesType.Scatter, 0)) self.AddChart(spreadPlot) def MyTrainingMethod(self): hist = self.History([self.wti, self.b], 252, Resolution.Daily).unstack(level=0)['close'].ffill().dropna() wti_hist = hist[self.wti.ID.ToString()] b_hist = hist[self.b.ID.ToString()] # linear regression to decide the fair value self.regr.fit(wti_hist.values.reshape(-1, 1), b_hist.values.reshape(-1, 1)) def OnData(self, data): if not (data.ContainsKey(self.wti) and data.ContainsKey(self.b)): return spread = data[self.wti].Price - data[self.b].Price self.Plot("Spread Plot", "Spread", spread) self.SpreadSMA.Update(self.Time, spread) fair_value = data[self.wti].Price - self.regr.predict(np.array([data[self.wti].Price]).reshape(1, -1))[0] if spread > self.SpreadSMA.Current.Value and not (self.Portfolio[self.wti].IsShort and self.Portfolio[self.b].IsLong): self.Log("spread > self.SpreadSMA.Current.Value") self.SetHoldings(self.wti, -0.5) self.SetHoldings(self.b, 0.5) self.Plot("Spread Plot", "Long Spread Trade", spread) elif spread < self.SpreadSMA.Current.Value and not (self.Portfolio[self.wti].IsLong and self.Portfolio[self.b].IsShort): self.Log("spread < self.SpreadSMA.Current.Value") self.SetHoldings(self.wti, 0.5) self.SetHoldings(self.b, -0.5) self.Plot("Spread Plot", "Short Spread Trade", spread) if self.Portfolio[self.wti].IsShort and self.Portfolio[self.b].IsLong and spread < fair_value: self.Liquidate() if self.Portfolio[self.wti].IsLong and self.Portfolio[self.b].IsShort and spread > fair_value: self.Liquidate()