Overall Statistics
Total Trades
26
Average Win
3.83%
Average Loss
-10.96%
Compounding Annual Return
36.681%
Drawdown
13.500%
Expectancy
-0.264
Net Profit
25.642%
Sharpe Ratio
1.395
Probabilistic Sharpe Ratio
55.555%
Loss Rate
45%
Win Rate
55%
Profit-Loss Ratio
0.35
Alpha
-0.149
Beta
2.005
Annual Standard Deviation
0.298
Annual Variance
0.089
Information Ratio
0.644
Tracking Error
0.207
Treynor Ratio
0.207
Total Fees
$977.22
Estimated Strategy Capacity
$15000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
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, 30), \
                        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):
                if slice.ContainsKey(self.contract) and slice[self.contract] is not None and not slice[self.contract].IsFillForward:
                    self.Debug("option less than 2 days before expiry")
                    self.Liquidate(self.contract)
                    self.contract = None
                
        if self.trade and self.Time.hour == 11:
            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