Overall Statistics |
Total Trades 115 Average Win 0% Average Loss 0% Compounding Annual Return 0.885% Drawdown 5.400% Expectancy 0 Net Profit 11.403% Sharpe Ratio 0.458 Probabilistic Sharpe Ratio 1.636% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0.001 Beta 0.06 Annual Standard Deviation 0.014 Annual Variance 0 Information Ratio -0.635 Tracking Error 0.138 Treynor Ratio 0.103 Total Fees $284.05 Estimated Strategy Capacity $0 Lowest Capacity Asset VX V1NCGGOCJHT5 Portfolio Turnover 0.04% |
#region imports from AlgorithmImports import * #endregion from collections import deque import statsmodels.api as sm class TermStructureOfVixAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2011, 1, 1) # Set Start Date #self.SetEndDate(2017, 1, 1) # Set End Date self.SetCash(1000000) # Set Strategy Cash vix = self.AddIndex("VIX", Resolution.Daily) self.vix = vix.Symbol self.vix_multiplier = vix.SymbolProperties.ContractMultiplier self.vx1 = self.AddFuture(Futures.Indices.VIX, resolution = Resolution.Daily, extendedMarketHours = True, dataNormalizationMode = DataNormalizationMode.BackwardsRatio, dataMappingMode = DataMappingMode.OpenInterest, contractDepthOffset = 0 ) self.vx1_multiplier = self.vx1.SymbolProperties.ContractMultiplier self.es1 = self.AddFuture(Futures.Indices.SP500EMini, resolution = Resolution.Daily, extendedMarketHours = True, dataNormalizationMode = DataNormalizationMode.BackwardsRatio, dataMappingMode = DataMappingMode.OpenInterest, contractDepthOffset = 0 ) self.es1_multiplier = self.es1.SymbolProperties.ContractMultiplier # the rolling window to save the front month VX future price self.price_VX = RollingWindow[float](252) # the rolling window to save the front month ES future price self.price_ES = RollingWindow[float](252) # the rolling window to save the time-to-maturity of the contract self.days_to_maturity = RollingWindow[float](252) stockPlot = Chart("Trade") stockPlot.AddSeries(Series("VIX", SeriesType.Line, 0)) stockPlot.AddSeries(Series("VIX Futures", SeriesType.Line, 0)) stockPlot.AddSeries(Series("Buy", SeriesType.Scatter, 0)) stockPlot.AddSeries(Series("Sell", SeriesType.Scatter, 0)) stockPlot.AddSeries(Series("Daily Roll", SeriesType.Scatter, 0)) stockPlot.AddSeries(Series("Hedge Ratio", SeriesType.Scatter, 0)) self.SetWarmUp(253, Resolution.Daily) def OnData(self, data): if data.Bars.ContainsKey(self.vx1.Symbol) and data.Bars.ContainsKey(self.es1.Symbol): # update the rolling window price and time-to-maturity series every day vx1_price = data.Bars[self.vx1.Symbol].Close self.price_VX.Add(float(data.Bars[self.vx1.Symbol].Close)) self.price_ES.Add(float(data.Bars[self.es1.Symbol].Close)) self.days_to_maturity.Add((self.vx1.Mapped.ID.Date-self.Time).days) if self.IsWarmingUp or not self.price_VX.IsReady or not self.price_ES.IsReady or not self.days_to_maturity.IsReady\ or (self.vx1.Mapped.ID.Date - self.Time).days == 0: return if data.Bars.ContainsKey(self.vx1.Symbol) and data.Bars.ContainsKey(self.es1.Symbol): # calculate the daily roll daily_roll = (vx1_price*self.vx1_multiplier - self.Securities[self.vix].Price*self.vix_multiplier)\ /(self.vx1.Mapped.ID.Date - self.Time).days self.Plot("Trade", "VIX", vx1_price) self.Plot("Trade", "VIX Futures", vx1_price) self.Plot("Trade", "Daily Roll", daily_roll) if not self.Portfolio[self.vx1.Mapped].Invested: # Short if the contract is in contango with adaily roll greater than 0.10 if daily_roll > 0.1: hedge_ratio = self.CalculateHedgeRatio(data.Bars[self.es1.Symbol].Close) self.Plot("Trade", "Sell", vx1_price) qty = self.CalculateOrderQuantity(self.vx1.Mapped, -1) self.MarketOrder(self.vx1.Mapped, qty // self.vx1.SymbolProperties.ContractMultiplier) qty = self.CalculateOrderQuantity(self.es1.Mapped, -1*hedge_ratio) self.MarketOrder(self.es1.Mapped, qty // self.es1.SymbolProperties.ContractMultiplier) # Long if the contract is in backwardation with adaily roll less than -0.10 elif daily_roll < -0.1: hedge_ratio = self.CalculateHedgeRatio(data.Bars[self.es1.Symbol].Close) self.Plot("Trade", "Buy", vx1_price) qty = self.CalculateOrderQuantity(self.vx1.Mapped, 1) self.MarketOrder(self.vx1.Mapped, qty // self.vx1.SymbolProperties.ContractMultiplier) qty = self.CalculateOrderQuantity(self.es1.Mapped, 1*hedge_ratio) self.MarketOrder(self.es1.Mapped, qty // self.es1.SymbolProperties.ContractMultiplier) # exit if the daily roll being less than 0.05 if holding short positions if self.Portfolio[self.vx1.Mapped].IsShort and daily_roll < 0.05: self.Liquidate() return # exit if the daily roll being greater than -0.05 if holding long positions if self.Portfolio[self.vx1.Mapped].IsLong and daily_roll > -0.05: self.Liquidate() return if self.vx1.Mapped and self.es1.Mapped: # if these exit conditions are not triggered, trades are exited two days before it expires if self.Portfolio[self.vx1.Mapped].Invested and self.Portfolio[self.es1.Mapped].Invested: if (self.vx1.Mapped.ID.Date-self.Time).days <= 2 or (self.es1.Mapped.ID.Date-self.Time).days <= 2: self.Liquidate() def CalculateHedgeRatio(self, es1_price): price_VX = np.array(list(self.price_VX))[::-1]/self.vx1_multiplier price_ES = np.array(list(self.price_ES))[::-1]/self.es1_multiplier delta_VX = np.diff(price_VX) res_ES = np.diff(price_ES)/price_ES[:-1]*100 tts = np.array(list(self.days_to_maturity))[::-1][1:] df = pd.DataFrame({"delta_VX":delta_VX, "SPRET":res_ES, "product":res_ES*tts}).dropna() # remove rows with zero value df = df[(df != 0).all(1)] y = df['delta_VX'].astype(float) X = df[['SPRET', "product"]].astype(float) X = sm.add_constant(X) model = sm.OLS(y, X).fit() beta_1 = model.params[1] beta_2 = model.params[2] hedge_ratio = (beta_1 + beta_2*((self.vx1.Mapped.ID.Date-self.Time).days))/float(es1_price) self.Plot("Trade", "Hedge Ratio", hedge_ratio) return hedge_ratio