| 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()