Overall Statistics
Total Trades
25
Average Win
2.38%
Average Loss
-4.72%
Compounding Annual Return
29.714%
Drawdown
13.200%
Expectancy
0.094
Net Profit
20.114%
Sharpe Ratio
1.176
Probabilistic Sharpe Ratio
50.389%
Loss Rate
27%
Win Rate
73%
Profit-Loss Ratio
0.50
Alpha
-0.228
Beta
1.981
Annual Standard Deviation
0.292
Annual Variance
0.085
Information Ratio
0.269
Tracking Error
0.204
Treynor Ratio
0.173
Total Fees
$788.39
Estimated Strategy Capacity
$1100000.00
Lowest Capacity Asset
VIX 31Q7PYN2B6EB2|VIX 31
class VirtualRedDogfish(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2021, 1, 1)
        # self.SetEndDate(2012, 1, 30)
        self.SetCash(1000000)

        self.stock_wt = 0.80
        self.option_wt = 0.15
        self.trade = False   #Setup flag to trade

        self.stock = self.AddEquity("SPY", Resolution.Minute).Symbol
        self.option = self.AddIndex('VIX', Resolution.Minute)
        self.option.SetDataNormalizationMode(DataNormalizationMode.Raw)
        self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x)))
        
        self.contract = None # initialize the option contract
        self.call_premium = 0.85 # % strike price premium on option call hedge
        self.put_premium = 1.5 # % strike price premium on option put hedge
        
        
        self.Schedule.On(self.DateRules.EveryDay(self.stock), \
                        self.TimeRules.AfterMarketOpen(self.stock, 1), \
                        self.SetupTrade)
            
            
    def OnData(self, slice):
        #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.IndexOption]
        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
                
        if self.trade:
            self.InitiateTrade()
            
            
    def SetupTrade(self):
        #Check to see if already invested in option
        option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.IndexOption]
        
        #If no option held, select and purchase contract
        if not option_invested:
            self.contract = self.GetPut()
            self.trade = True
    
      
    def GetPut(self):
        targetStrike = self.Securities[self.option.Symbol].Price * self.put_premium
        contracts = self.OptionChainProvider.GetOptionContractList(self.option.Symbol, self.Time)
        self.Debug(f"VIX Total Contracts Found: {len(contracts)}")
        
        puts = [x for x in contracts if x.ID.OptionRight == OptionRight.Put]
        puts = sorted(sorted(puts, key = lambda x: x.ID.Date),
                       key = lambda x: x.ID.StrikePrice, reverse = False)
        self.Debug(f"VIX Puts found: {len(puts)}")
        
        puts = [x for x in puts if abs(targetStrike - x.ID.StrikePrice) <= 1]
        puts = [x for x in puts if 7 < (x.ID.Date - self.Time).days <= 35]
        if len(puts) == 0:
            self.Debug(f"!!! no put options available")
            return None
        
        # Use AddOptionContract() to subscribe the data for specified contract
        self.AddOptionContract(puts[0], Resolution.Minute)
        return puts[0]
        
        
    def InitiateTrade(self):
        if self.contract is not None:
            self.SetHoldings([PortfolioTarget(self.stock, self.stock_wt), \
                            PortfolioTarget(self.contract, self.option_wt)])
        
        self.trade = False    #reset trading flag