Overall Statistics
Total Trades
8
Average Win
0%
Average Loss
-1.67%
Compounding Annual Return
-76.666%
Drawdown
52.400%
Expectancy
-1
Net Profit
-22.199%
Sharpe Ratio
-0.281
Probabilistic Sharpe Ratio
28.545%
Loss Rate
100%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
-0.389
Beta
1.972
Annual Standard Deviation
1.358
Annual Variance
1.844
Information Ratio
-0.565
Tracking Error
0.681
Treynor Ratio
-0.194
Total Fees
$735.25
#From V1, this V2 adds stop loss to hedge after it exceeds certain performance


class TailHedge(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2020, 3, 1)
        self.SetEndDate(2020, 5, 1)
        self.SetCash(1000000)
        stock = self.AddEquity("SSO", Resolution.Minute) #portfolio holdings
        hedge = self.AddEquity("VXX", 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)
        self.Schedule.On(self.DateRules.MonthStart(self.stock), self.TimeRules.AfterMarketOpen(self.stock, 90), self.Rebalance)
        
        ### Hedge Paramaters
        self.hedge_weight = 0.05 # % of portfolio invested into hedge each month
        self.hedge_premium = 1.80 # % strike price premium on option call hedge
        
        self.hedge_stoploss_ticket = None
        self.hedge_peak_price = None
        
        self.hedge_init_stoploss = 0.4 # stoploss set at original hedge purchase, in % of contract price
        self.hedge_vol_stoploss =  0.9 # stoploss that kicks in after volatility spike, in % of contract price
        self.hedge_vol_spike = 1.00 # % increase in hedge purchase price that triggers new vol_stoploss
        
        
    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()
            self.SetHoldings(self.contract, self.hedge_weight)
            
        else:
            self.Liquidate(self.contract)
            self.RemoveSecurity(self.contract)
            self.contract = None
            self.contract = self.GetContract()
            self.SetHoldings(self.contract, self.hedge_weight)
        
        # calculate initial stoploss price and quantity
        price = self.Securities[self.contract].Price
        self.hedge_peak_price = price
        quantity = self.Portfolio[self.contract].Quantity
        stop_loss = price * self.hedge_init_stoploss
        
        # setup initial stop loss sell order
        self.hedge_stoploss_ticket = self.StopMarketOrder(self.contract, -quantity, stop_loss)
        
        
    # Everyday we will update our stoploss order if necessary    
    def OnEndOfDay(self):

        # only check if hedge position exists
        if self.contract is None or self.hedge_peak_price is None:
            return
        
        # current market price of contract
        current_contract_price = self.Securities[self.contract].Price
        if current_contract_price > self.hedge_peak_price:
            self.hedge_peak_price = current_contract_price
        
        # if volatily spike exceeds threshold, we'll update with trailing stoploss price
        if self.Portfolio[self.contract].UnrealizedProfitPercent > self.hedge_vol_spike:
            
            # % drawdown from peak price of contract
            stop_loss = self.hedge_peak_price * self.hedge_vol_stoploss
            self.hedge_stoploss_ticket.UpdateStopPrice(stop_loss)
            self.Debug(f"!!! updated contract price: {current_contract_price}")
            self.Debug(f"!!! new stop loss: {stop_loss}")
            
        # if profits not yet > volatily spike threshold %, we don't trail our stop loss
        else:
            
            pass
            # do nothing
            
            
    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 60 < (x.ID.Date - self.Time).days <= 150]
        if len(calls) == 0:
            self.Debug(f"!!! no options available")
            return None
        self.AddOptionContract(calls[0], Resolution.Minute)
        return calls[0]