Backtest Period
Markets Traded
Equities / Options
Maximum Drawdown
Period of Rebalancing
Daily
Return (Annual)
Sharpe Ratio
Standard Deviation (Annual)
Original paper
SSRN-id2771019.pdf668.3KB
Trading rules
- Utilize VIX futures for trading and E-mini S&P 500 for hedging
- Sell nearest VIX futures with 10+ trading days to maturity when in contango, with daily roll > 0.10 points
- Buy nearest VIX futures with 10+ trading days to maturity when in backwardation, with daily roll < -0.10 points
- Hold position for 5 trading days, hedge against spot VIX changes with corresponding E-mini S&P 500 positions
- Calculate daily roll as (front VIX futures price - VIX) / number of business days until settlement
- Determine hedge ratios using regression analysis of VIX futures price changes and E-mini S&P 500 percentage changes
Python code
Backtrader
import backtrader as bt
import numpy as np
from sklearn.linear_model import LinearRegression
class VIXFuturesStrategy(bt.Strategy):
def __init__(self):
self.vix = self.datas[0]
self.vix_futures = self.datas[1]
self.es = self.datas[2]
self.order = None
self.hold_days = 0
def next(self):
if self.order:
return
if self.hold_days > 0:
self.hold_days -= 1
return
days_to_maturity = self.vix_futures.days_to_maturity() # Implement this in your data feed
if days_to_maturity < 10:
return
daily_roll = (self.vix_futures[0] - self.vix[0]) / days_to_maturity
if daily_roll > 0.10:
position_size = self.broker.getvalue() * 0.9 / self.vix_futures # Adjust based on your risk management
self.sell(data=self.vix_futures, size=position_size)
hedge_ratio = self.calculate_hedge_ratio()
self.buy(data=self.es, size=position_size * hedge_ratio)
self.hold_days = 5
elif daily_roll < -0.10:
position_size = self.broker.getvalue() * 0.9 / self.vix_futures # Adjust based on your risk management
self.buy(data=self.vix_futures, size=position_size)
hedge_ratio = self.calculate_hedge_ratio()
self.sell(data=self.es, size=position_size * hedge_ratio)
self.hold_days = 5
def calculate_hedge_ratio(self):
vix_futures_returns = np.diff(np.log(self.vix_futures.get(size=252))) # 1-year lookback
es_returns = np.diff(np.log(self.es.get(size=252))) # 1-year lookback
lr = LinearRegression()
lr.fit(es_returns.reshape(-1, 1), vix_futures_returns)
return -lr.coef_[0]
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
return
if order.status in [order.Completed]:
if order.isbuy():
self.log(f'BUY EXECUTED, {order.executed.price}')
elif order.issell():
self.log(f'SELL EXECUTED, {order.executed.price}')
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
self.order = None