Overall Statistics
Total Trades
397
Average Win
1.33%
Average Loss
-0.63%
Compounding Annual Return
-31.632%
Drawdown
98.000%
Expectancy
-0.186
Net Profit
-97.664%
Sharpe Ratio
-0.049
Probabilistic Sharpe Ratio
0.004%
Loss Rate
74%
Win Rate
26%
Profit-Loss Ratio
2.11
Alpha
0.269
Beta
-3.072
Annual Standard Deviation
0.724
Annual Variance
0.524
Information Ratio
-0.164
Tracking Error
0.82
Treynor Ratio
0.012
Total Fees
$2162.42
Estimated Strategy Capacity
$260000.00
Lowest Capacity Asset
VIXY Y5BMQ7C65TD2|VIXY UT076X30D0MD
# The investment universe consists of a stock/bond portfolio with a proportion of 60-percent stocks and 40-percent bonds.
# Stocks are represented by the SPDR S&P 500 ETF Trust (SPY) and bonds by the iShares 7-10 Year Treasury Bond ETF (IEF). 
# The strategy firstly invests 0-100 basis points (bsp) in the desired VIX call option, then allocates 60 percent of the 
# portfolio to the SPY and the remaining 40 percent to the IEF. The option is bought at the level of 135% of the moneyness
# of the underlying VIX futures price. The strategy is systematically purchasing an equal amount in one-month, two-month, 
# three-month and four-month VIX call options on VIX futures. If the VIX Index is between 15 and 30, the weight of VIX calls 
# in the portfolio is 1%. If the VIX Index is between 30 and 50, the weight in the portfolio is 0.5%. If the VIX Index is over
# 50 or under 15, then the weight of options in the portfolio is 0%. Each month, on the day before expiration, the options are
# rolled to the appropriate expiry. VIX call options are purchased at the offer and sold at the bid to keep the assumptions
# conservative. The options are held to maturity and closed the Tuesday afternoon before the Wednesday morning of VIX futures
# and options expiration. If the contracts have any intrinsic value, they are sold at the bid price, and the cash is used at 
# the end of the month to rebalance the stock/bond portion of the portfolio.

from AlgorithmImports import *

class PortfolioHedgingUsingVIXOptions(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2013, 1, 1)
        self.SetCash(1000000)
        
        data = self.AddEquity("SPY", Resolution.Daily)
        data.SetLeverage(5)
        self.spy = data.Symbol
        
        data = self.AddEquity("IEF", Resolution.Daily)
        data.SetLeverage(5)
        self.ief = data.Symbol
        
        data = self.AddEquity("VIXY", Resolution.Daily)
        data.SetLeverage(5)
        self.vix = data.Symbol
        
        option = self.AddOption('VIXY', Resolution.Daily)
        #option = self.AddOption('SPY', Resolution.Daily)

        self.SetWarmUp(30, Resolution.Daily)

        #public void SetFilter(int minStrike, int maxStrike, TimeSpan minExpiry, TimeSpan maxExpiry)
        # By default weekly options are excluded from options expiry so we get only monthly options.
        option.SetFilter(-20, 20, 0, 180)
        self.Debug("yolo")   # Print to the console
        
    def OnData(self,slice):
        # OptionChains is a collection of OptionChain keyed by the option's underlying symbol.
        # The elements in Slice.OptionChains have properties Key(the underlying symbol object) and Value(the option chain).
        
        if self.IsWarmingUp:
            return

        for i in slice.OptionChains:
            chains = i.Value
            
            # If there are max of 2 positions open - spy and ief - that means option expired.
            invested = [x.Key for x in self.Portfolio if x.Value.Invested]
            #invested = ['SPX','VIXoption','Bond']

            if len(invested) < 6:
                # Get the right being purchased x.Right = 1  call option[right to buy] 
                # x.Right = 0  put option[right to sell]
                calls = list(filter(lambda x: x.Right == OptionRight.Call, chains))
                
                if not calls: return
            
                underlying_price = self.Securities[self.vix].Price
               
                expiries = list(set([i.Expiry for i in calls]))
                #expiries = ['Dec', 'Jan', etc]
                
                strikes = [i.Strike for i in calls]
                
                # Determine out-of-the-money strike.
                otm_strike = min(strikes, key = lambda x:abs(x - (float(1.35) * underlying_price)))
                #otm_call = [i for i in calls if i.Expiry == expiry and i.Strike == otm_strike]
                otm_call = [i for i in calls if (j for j in expiries if j==i.Expiry) and i.Strike == otm_strike]


                if len(otm_call)==4:
                    self.Debug("yolo1")
                    # Option weighting.
                    weight = 0.0
                    opt_price_1 = self.Securities[otm_call[0].Symbol].Price
                    opt_price_2 = self.Securities[otm_call[1].Symbol].Price
                    opt_price_3 = self.Securities[otm_call[2].Symbol].Price
                    opt_price_4 = self.Securities[otm_call[3].Symbol].Price

                    if underlying_price >= 15 and underlying_price <= 30:
                        weight = 0.01
                        w1=0.01/4
                        w2=0.01/4
                        w3=0.01/4
                        w4=0.01/4
                    elif underlying_price > 30 and underlying_price <= 50:
                        weight = 0.005
                        w1 = 0.005/4
                        w2 = 0.005/4
                        w3 = 0.005/4
                        w4 = 0.005/4
                      
                    if weight != 0: 
                        if opt_price_1!=0:
                            options_q1 = int((self.Portfolio.MarginRemaining * weight) / (opt_price_1 * 100))
                        else:
                            options_q1=0
                        if opt_price_2!=0:
                            options_q2 = int((self.Portfolio.MarginRemaining * weight) / (opt_price_2 * 100))
                        else:
                            options_q2=0
                        if opt_price_3!=0:
                            options_q3 = int((self.Portfolio.MarginRemaining * weight) / (opt_price_3 * 100))
                        else:
                            options_q3=0
                        if opt_price_4!=0:
                            options_q4 = int((self.Portfolio.MarginRemaining * weight) / (opt_price_4 * 100))
                        else:
                            options_q4=0
    
                        # Set max leverage.
                        self.Securities[otm_call[0].Symbol].MarginModel = BuyingPowerModel(5)
                        
                        # Sell out-the-money call.
                        if options_q1:
                            self.Buy(otm_call[0].Symbol, options_q1)
                        if options_q2:
                            self.Buy(otm_call[1].Symbol, options_q2)
                        if options_q3:
                            self.Buy(otm_call[2].Symbol, options_q3)
                        if options_q4:
                            self.Buy(otm_call[3].Symbol, options_q4)
                        
                        self.SetHoldings(self.spy, 0.6)
                        self.SetHoldings(self.ief, 0.4)