Quant BuffetRelax, Not Over Thinking

Trading VIX ETFs

Log in to collect

Academic paper

Easy Volatility Investing

AuthorsTony Cooper

Institute
  • ?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)