Overall Statistics
Total Trades
18
Average Win
0.83%
Average Loss
-4.99%
Compounding Annual Return
24.342%
Drawdown
59.000%
Expectancy
-0.470
Net Profit
731.208%
Sharpe Ratio
0.97
Probabilistic Sharpe Ratio
31.922%
Loss Rate
55%
Win Rate
45%
Profit-Loss Ratio
0.17
Alpha
-0.037
Beta
1.953
Annual Standard Deviation
0.319
Annual Variance
0.101
Information Ratio
0.827
Tracking Error
0.159
Treynor Ratio
0.158
Total Fees
$1811.51
Estimated Strategy Capacity
$620000.00
Lowest Capacity Asset
SSO TJNNZWL5I4IT
class VXXoptions(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2012, 1, 1)
        # self.SetEndDate(2014, 1, 1)
        self.cash = 1000000
        self.SetCash(self.cash)
        stock = self.AddEquity("SSO", Resolution.Minute) #portfolio holdings
        self.stock = stock.Symbol
        self.SetWarmUp(200)
        
        self.vxx_option = self.AddEquity("VXX", Resolution.Minute)
        self.vxx1_option = self.AddEquity("VXX.1", Resolution.Minute)
        self.vxx_option.SetDataNormalizationMode(DataNormalizationMode.Raw)
        self.vxx1_option.SetDataNormalizationMode(DataNormalizationMode.Raw)
        self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x)))
        self.hedge = None
        self.contract = None
        
        self.hedge_weight = 0.05 # % of portfolio invested into hedge each month
        self.stock_weight = 0.95 # % of portfolio allocated to stock each month
        self.hedge_premium = 1.3 # % strike price premium on option call hedge
        self.target_DTE = 30 # target days to expiration for selected option contract
        
        
    def OnData(self, data):
        ## VXX expired and was relisted in 2019, requires switching tickers
        ## --> https://www.quantconnect.com/forum/discussion/6804/vxx-minute-the-issue-starts-from-jan-30th-2009-and-continues-until-jan-17th-2018/p1
        if self.Time.year < 2019:
            self.hedge = self.vxx1_option.Symbol
        else:
            self.hedge = self.vxx_option.Symbol
        
        if self.IsWarmingUp:
            return
        
        if self.contract is None:
            self.contract = self.GetContract()
            if self.contract is not None:
                self.SetHoldings([PortfolioTarget(self.contract, self.hedge_weight), PortfolioTarget(self.stock, self.stock_weight)])
        
        #Liquidate any options that are within 1.5 days of expiration    
        option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
        if option_invested:
            expirydate = self.contract.ID.Date
            if (expirydate - self.Time) < timedelta(1.5):
                self.Debug("option less than 2 days before expiry")
                self.Liquidate(self.contract)
                self.contract = None
        
            
    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 = [x for x in calls if abs(x.ID.StrikePrice - targetStrike) < 2]
        calls = [x for x in calls if 0 <= ((x.ID.Date - self.Time).days - self.target_DTE) <= 30]
        # self.Debug(f"VXX Calls found: {len(calls)}")
        calls = sorted(sorted(calls, key = lambda x: x.ID.Date), key = lambda x: x.ID.StrikePrice)
        
        if len(calls) == 0:
            self.Debug(f"!!! no options available")
            return None
        
        self.Debug(f"Selected option contract: {calls[0]}")    
        self.AddOptionContract(calls[0], Resolution.Minute)
        return calls[0]