Overall Statistics
Total Trades
446
Average Win
1.75%
Average Loss
-1.33%
Compounding Annual Return
5.158%
Drawdown
40.200%
Expectancy
0.180
Net Profit
64.935%
Sharpe Ratio
0.35
Probabilistic Sharpe Ratio
0.692%
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
1.32
Alpha
-0.012
Beta
0.571
Annual Standard Deviation
0.124
Annual Variance
0.015
Information Ratio
-0.483
Tracking Error
0.111
Treynor Ratio
0.076
Total Fees
$10904.17
Estimated Strategy Capacity
$1900000.00
Lowest Capacity Asset
VIXY UT076X30D0MD
from AlgorithmImports import *
import numpy as np

class RollContract(QCAlgorithm):

    def Initialize(self):
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
        self.SetStartDate(2013, 1, 2)
        self.SetEndDate(2022,12,12)
        self.SetCash(1000000)
        res = Resolution.Minute 
        #CBOE only lists VIX Index at Daily level but can get vix options at minute level so get the options underlying price to get the minute VIX spot
        ticker =  'VIX'
        self.index_symbol = self.AddIndex(ticker, res).Symbol
        option = self.AddIndexOption(self.index_symbol, res)
        option.SetFilter(-1, 1, timedelta(0), timedelta(45))
        self.option_symbol = option.Symbol
        #add the front month UX futures, set to Raw
        # if we dont the beginning of the futures curve in history is going to be backwards stiched at astronoimical levels and will hide the true 
        # shape of the vol curve at that moment in time  
        self.ux_1 = self.AddFuture(Futures.Indices.VIX,
                                dataNormalizationMode=DataNormalizationMode.Raw,dataMappingMode = DataMappingMode.OpenInterest,contractDepthOffset = 0)
        
        #get etfs 
        self.spy = self.AddEquity("SPY",res).Symbol
        self.vixy = self.AddEquity("VIXY",res).Symbol
        self.svxy = self.AddEquity("SVXY",res).Symbol
        
        self.SetBenchmark("SPY")

        self.EnableAutomaticIndicatorWarmUp = True
        
  

    def OnData(self, slice):
        #create empty list for portfolio
        current_port_symbols = []
        
        # In case warming is required (for later use)
        if self.IsWarmingUp:
            return
        
        if slice.OptionChains.ContainsKey(self.option_symbol) and self.spy in slice.Bars and self.vixy in slice.Bars :
            if self.Time.hour == 10 and self.Time.minute == 0:
                #get basis (vix fut/vix spot -1)
                vix_basis = (self.ux_1.Price/slice.OptionChains[self.option_symbol].Underlying.Price)-1
                #plot basis 
                self.Plot("VIX Basis", "Basis",vix_basis)
                
                #check current weight spy
                current_port_symbols = [ x.Symbol.Value for x in self.Portfolio.Values if x.Invested ]

                #currentweight_spy = (self.Portfolio["SPY"].Quantity * Close) /self.Portfolio.TotalPortfolioValue
                self.Debug(f"current port symbols: {str(current_port_symbols)} DateTime: {self.Time}")
                if vix_basis > 0:
                    #if invested in vixy then liquidate vixy and go long 100% spy 
                    if "VIXY" in current_port_symbols:
                    #if 100% long in spy do nothing if not set spy holdings to 100% 
                        self.Liquidate("VIXY")
                        self.SetHoldings("SPY",1)
                #if backwardated vol curve, set holdings to 75% SPY 25% VIXY
                elif vix_basis < 0:
                    if not "VIXY" in current_port_symbols:
                        self.SetHoldings("SPY",.75)
                        self.SetHoldings("VIXY",.25)
                    


                
            
            # self.Plot("option_symbol", "Price", slice.OptionChains[self.option_symbol].Underlying.Price)
            # self.Plot(self.ux_1.Symbol.ID.Symbol, self.ux_1.Symbol.ID.Symbol, self.ux_1.Price)
            # self.Plot("VIX Basis", "Basis",vix_basis)