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