Trading VIX ETFs
Log in to collectAcademic paper
Tony Cooper
- ?Double-Digit Numerics
Strategy in a nutshell
The strategy trades four volatility ETNs (XIV, VXX, ZIV, VXZ), ranking them daily by their 83-day momentum. The ETN with the highest positive momentum is held, and if none are positive, the portfolio stays in cash. Daily rebalancing ensures alignment with momentum signals, capturing trends in volatility ETNs efficiently.
Economic rationale
The strategy exploits the volatility premium, where long volatility provides crisis protection at a negative premium, and short volatility yields positive premium but high tail risk. Using a momentum filter allows dynamic exposure, capturing returns while reducing extreme losses in crisis periods.
Backtest performance
Annualised return84.6%
Volatility51.2%
Beta0.043
Sharpe ratio1.57
Sortino ratio0.14
Maximum drawdown-43.2%
Win rate55%
Full Python code
from AlgorithmImports import *
class TradingVIXETFs(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2010, 1, 1)
self.SetCash(100000)
self.symbols = ["SVXY", "VXX", "ZIV", "VXZ"]
self.data = {}
self.period = 83
self.SetWarmUp(self.period)
for symbol in self.symbols:
data = self.AddEquity(symbol, Resolution.Daily)
self.data[symbol] = RollingWindow[float](self.period)
def OnData(self, data):
for symbol in self.data:
symbol_obj = self.Symbol(symbol)
if symbol_obj in data.Keys:
if data[symbol_obj]:
price = data[symbol_obj].Value
if price != 0:
self.data[symbol].Add(price)
if self.IsWarmingUp: return
self.Liquidate()
returns = {}
for symbol in self.symbols:
if self.data[symbol].IsReady:
prices = [x for x in self.data[symbol]]
returns[symbol] = prices[0] / prices[-1] - 1
if len(returns) != 0:
sorted_by_return = sorted(returns.items(), key = lambda x: x[1], reverse = True)
symbols = [x[0] for x in sorted_by_return]
top_symbol = symbols[0]
top_val = returns[top_symbol]
# if self.Portfolio.Invested:
# if not self.Securities[top_symbol].Invested:
# self.Liquidate()
# if top_val > 0:
# self.SetHoldings(top_symbol, 1/2)
# else:
# self.SetHoldings(top_symbol, 1/2)
if top_val > 0:
if self.Securities[top_symbol] != 0:
self.SetHoldings(top_symbol, 1/2)