Overall Statistics
Total Trades
358
Average Win
8.16%
Average Loss
-8.40%
Compounding Annual Return
8.736%
Drawdown
87.400%
Expectancy
0.185
Net Profit
126.142%
Sharpe Ratio
0.452
Probabilistic Sharpe Ratio
0.076%
Loss Rate
40%
Win Rate
60%
Profit-Loss Ratio
0.97
Alpha
0.593
Beta
-0.124
Annual Standard Deviation
1.265
Annual Variance
1.599
Information Ratio
0.315
Tracking Error
1.277
Treynor Ratio
-4.631
Total Fees
$53766.73
Estimated Strategy Capacity
$2200000.00
Lowest Capacity Asset
VXXB XSAXXQLXNYRQ|VXXB WRBPJAJZ2Q91
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
        
        option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
        if not option_invested:
            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]