Overall Statistics
Total Trades
8
Average Win
0.00%
Average Loss
0%
Compounding Annual Return
31.964%
Drawdown
11.300%
Expectancy
0
Net Profit
20.536%
Sharpe Ratio
1.642
Probabilistic Sharpe Ratio
62.766%
Loss Rate
0%
Win Rate
100%
Profit-Loss Ratio
0
Alpha
0.028
Beta
1.945
Annual Standard Deviation
0.206
Annual Variance
0.042
Information Ratio
1.772
Tracking Error
0.101
Treynor Ratio
0.174
Total Fees
$82.15
# No data for VXX prior to 6/1/2019, and VIXY data is spotty prior to that


class VXXTailHedge(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2016, 6, 1)
        self.SetEndDate(2018, 12, 15)
        self.SetCash(1000000)
        stock = self.AddEquity("SSO", Resolution.Minute) #portfolio holdings
        hedge = self.AddEquity("vxx1", Resolution.Minute) #hedge instrument
        stock.SetDataNormalizationMode(DataNormalizationMode.Raw)
        hedge.SetDataNormalizationMode(DataNormalizationMode.Raw)
        self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x)))
        self.stock = stock.Symbol
        self.hedge = hedge.Symbol
        self.contract = None
        self.SetWarmUp(200)
        
        # schedule trade execution 30 minutes after the first trading day of each month
        self.Schedule.On(self.DateRules.MonthStart(self.stock), \
                        self.TimeRules.AfterMarketOpen(self.stock, 30), \
                        self.Rebalance)
        # schedule Plotting function 30 minutes after every market open
        self.Schedule.On(self.DateRules.EveryDay(self.stock), \
                        self.TimeRules.AfterMarketOpen(self.stock, 30), \
                        self.Plotting)
        
        ### Hedge Paramaters
        self.hedge_weight = 0.01 # % of portfolio invested into hedge each month
        self.hedge_premium = 1.2 # % strike price premium on option call hedge
        
        
    def OnData(self, data):
        if self.IsWarmingUp:
            return
    

    def Rebalance(self):
        self.Log("Rebalance fired at : {0}".format(self.Time))

        self.SetHoldings(self.stock, (1-self.hedge_weight))
        
        if self.contract is None:
            self.contract = self.GetContract()
            if self.contract is not None:
                self.SetHoldings(self.contract, self.hedge_weight)
            
        else:
            self.Liquidate(self.contract)
            self.RemoveSecurity(self.contract)
            self.contract = None
            self.contract = self.GetContract()
            if self.contract is not None:
                self.SetHoldings(self.contract, self.hedge_weight)
            
            
    def GetContract(self):
        targetStrike = self.Securities[self.hedge].Price * self.hedge_premium
        contracts = self.OptionChainProvider.GetOptionContractList(self.hedge, self.Time)
        
        self.Debug(f"VXX Total Contracts Found: {len(contracts)}")
        calls = [x for x in contracts if x.ID.OptionRight == OptionRight.Call]
        calls = sorted(sorted(calls, key = lambda x: x.ID.Date, reverse = True),
                       key = lambda x: x.ID.StrikePrice)
        self.Debug(f"VXX Calls found: {len(calls)}")
        calls = [x for x in calls if (x.ID.StrikePrice - targetStrike) >= 0
            and (x.ID.StrikePrice - targetStrike) < 6]
        calls = [x for x in calls if 0 < (x.ID.Date - self.Time).days <= 365]
        if len(calls) == 0:
            self.Debug(f"!!! no options available")
            return None
        self.AddOptionContract(calls[0], Resolution.Minute)
        return calls[0]
     
        
    def Plotting (self):
        #plot strike price of option
        option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
        if option_invested:
            self.Plot("Data Chart", "strike", option_invested[0].ID.StrikePrice)