| Overall Statistics |
|
Total Orders 1525 Average Win 1.49% Average Loss -0.68% Compounding Annual Return -6.097% Drawdown 28.300% Expectancy -0.020 Start Equity 100000 End Equity 73369.74 Net Profit -26.630% Sharpe Ratio -0.535 Sortino Ratio -0.568 Probabilistic Sharpe Ratio 0.010% Loss Rate 69% Win Rate 31% Profit-Loss Ratio 2.20 Alpha -0.058 Beta 0.068 Annual Standard Deviation 0.099 Annual Variance 0.01 Information Ratio -0.662 Tracking Error 0.194 Treynor Ratio -0.78 Total Fees $0.00 Estimated Strategy Capacity $1000.00 Lowest Capacity Asset WTICOUSD 8I Portfolio Turnover 50.14% |
#region imports
from AlgorithmImports import *
from sklearn.linear_model import LinearRegression
#endregion
class TradeWtiBrentSpreadAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2018, 1, 1)
self.set_end_date(2022, 12, 1)
self.set_cash(100000)
# import the spot price data
self._wti = self.add_cfd("WTICOUSD", Resolution.DAILY, Market.OANDA).symbol
self._b = self.add_cfd("BCOUSD", Resolution.DAILY, Market.OANDA).symbol
# Recalibrate model every month
self.train(self.date_rules.month_start(), self.time_rules.at(0,0), self._my_training_method)
# create the moving average indicator of the pread = WTI price - BRENT price
self._spread_sma = SimpleMovingAverage(20)
hist = self.history([self._wti, self._b], 252, Resolution.DAILY).unstack(level=0)['close'].ffill().dropna()
wti_hist = hist[self._wti.id.to_string()]
b_hist = hist[self._b.id.to_string()]
spread = wti_hist - b_hist
for index, value in spread.iloc[-20:].items():
self._spread_sma.update(index, value)
# linear regression to decide the fair value
self._regr = 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
spread_plot = Chart("Spread Plot")
spread_plot.add_series(Series("Spread", SeriesType.LINE, 0))
spread_plot.add_series(Series("Long Spread Trade", SeriesType.SCATTER, 0))
spread_plot.add_series(Series("Short Spread Trade", SeriesType.SCATTER, 0))
self.add_chart(spread_plot)
def _my_training_method(self):
hist = self.history([self._wti, self._b], 252, Resolution.DAILY).unstack(level=0)['close'].ffill().dropna()
wti_hist = hist[self._wti.id.to_string()]
b_hist = hist[self._b.id.to_string()]
# linear regression to decide the fair value
self._regr.fit(wti_hist.values.reshape(-1, 1), b_hist.values.reshape(-1, 1))
def on_data(self, data):
if not (data.contains_key(self._wti) and data.contains_key(self._b)):
return
spread = data[self._wti].price - data[self._b].price
self.plot("Spread Plot", "Spread", spread)
self._spread_sma.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._spread_sma.current.value and not (self.portfolio[self._wti].is_short and self.portfolio[self._b].is_long):
self.set_holdings(self._wti, -0.5)
self.set_holdings(self._b, 0.5)
self.plot("Spread Plot", "Long Spread Trade", spread)
elif spread < self._spread_sma.current.value and not (self.portfolio[self._wti].is_long and self.portfolio[self._b].is_short):
self.set_holdings(self._wti, 0.5)
self.set_holdings(self._b, -0.5)
self.plot("Spread Plot", "Short Spread Trade", spread)
if self.portfolio[self._wti].is_short and self.portfolio[self._b].is_long and spread < fair_value:
self.liquidate()
if self.portfolio[self._wti].is_long and self.portfolio[self._b].is_short and spread > fair_value:
self.liquidate()