Overall Statistics
Total Trades
1
Average Win
0%
Average Loss
0%
Compounding Annual Return
14.295%
Drawdown
32.700%
Expectancy
0
Net Profit
14.373%
Sharpe Ratio
0.621
Probabilistic Sharpe Ratio
30.815%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
-0.003
Beta
0.938
Annual Standard Deviation
0.255
Annual Variance
0.065
Information Ratio
-0.815
Tracking Error
0.017
Treynor Ratio
0.169
Total Fees
$1.60
Estimated Strategy Capacity
$83000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
from datetime import timedelta

class VIXCallProtection(QCAlgorithm):

    def Initialize(self):
        # set start/end date for backtest
        self.SetStartDate(2019, 10, 1)
        self.SetEndDate(2020, 10, 1)
        # set starting balance for backtest
        self.SetCash(100000)
        
        # add asset
        self.equity = self.AddEquity("SPY", Resolution.Minute)
        self.equity.SetDataNormalizationMode(DataNormalizationMode.Raw)
     
        # add VIX data to get price/close data
        self.data_vix = self.AddData( CBOE, "VIX" )
        self.data_vix.SetDataNormalizationMode(DataNormalizationMode.Raw)
        
        # VIX index for option chain
        self.index_vix = self.AddIndex('VIX').Symbol
        #self.index_vix = self.AddEquity('SPY').Symbol
        
        # initialize the option contract with empty string
        self.contract = str()
        self.contractsAdded = set()
        
        self.port_value_options = 0
        
        self.ordered = False
        
        # parameters ------------------------------------------------------------
        self.DaysBeforeExp = 2 # number of days before expiry to exit
        self.DTE = 30 # target days till expiration
        self.OTM = 0.1 # target percentage OTM
        self.lookbackIV = 150 # lookback length of IV indicator
        self.percentage = 0.95 # percentage of portfolio for underlying asset
        self.options_alloc = 0.01 # percentage of options
        # ------------------------------------------------------------------------
    
        # schedule Plotting function 30 minutes after every market open
        self.Schedule.On(self.DateRules.EveryDay(self.equity.Symbol), \
                        self.TimeRules.AfterMarketOpen(self.equity.Symbol, 30), \
                        self.Plotting)

        # warmup for IV indicator of data
        self.SetWarmUp(timedelta(self.lookbackIV)) 
        
    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
            Arguments:
                data: Slice object keyed by symbol containing the stock data
        '''
        if(self.IsWarmingUp):
            return
        
        # buy underlying asset
        if not self.Portfolio[self.equity.Symbol].Invested:
            self.SetHoldings(self.equity.Symbol, self.percentage)
        
        #  buy the option
        if self.Securities[self.data_vix.Symbol].Close > 10:
            if self.ordered == False:
                self.FindOption(data)
                self.BuyOption(data)
            
        if self.contract:
            # calculate value of options
            self.port_value_options = self.Portfolio[self.contract.Symbol].Quantity * self.Securities[self.contract.Symbol].Close
            
            # close put before it expires
            #if (self.contract.ID.Date - self.Time) <= timedelta(self.DaysBeforeExp):
            #    self.Liquidate(self.contract.Symbol)
            #    self.RemoveSecurity(self.contract.Symbol)
                
            #    self.Log("Closed: too close to expiration")
            #    self.contract = str()
                
    def BuyOption(self, data):
        if self.contract == str():
            return
        
        # if not invested and option data added successfully, buy option
        if not self.Portfolio[self.contract.Symbol].Invested: #and data.ContainsKey(self.contract.Symbol):
            port_value = self.Portfolio[self.equity.Symbol].Quantity * self.Securities[self.equity.Symbol].Price
            contract_value = self.Securities[self.contract.Symbol].AskPrice
            if contract_value > 0:
                #num_contract = round (port_value * self.options_alloc / contract_value)
                num_contract =100
                if self.contract.BidSize >= 100:
                    self.MarketOrder(self.contract.Symbol, num_contract)
                    self.ordered = True                

        if self.ordered == False:
            self.contract = str()
            
        
    
    def FindOption(self, data):
        # get option data if self.contract empty
        if self.contract == str():
            
            # self.underlyingPrice = self.Securities[self.data_vix.Symbol].Price
            self.underlyingPrice = 20
            min_strike = self.underlyingPrice - 10
            max_strike = self.underlyingPrice + 200
            min_expiry = self.Time + timedelta(days=20)
            max_expiry = self.Time + timedelta(days=60)
            sorted_function = lambda x: x.BidSize
            self.contract = self.GetOption(data, self.index_vix, OptionRight.Call, min_strike, max_strike, min_expiry, max_expiry, sorted_function, True)
            if self.contract == None:
                self.contract = str()
    
            
    def GetOption(self,slice,underlying,optionright,MinStrike,\
                  MaxStrike,MinExpiry,MaxExpiry,sortedfunction,reverse):
                      
        ## Get list of Options Contracts for a specific time
        chainProvider = self.OptionChainProvider 
        contracts = chainProvider.GetOptionContractList(underlying, self.Time)  
        
        if len(contracts) == 0: return
       
        filtered_options = [x for x in contracts if x.ID.OptionRight == optionright and\
                                                     x.ID.StrikePrice >= MinStrike and\
                                                     x.ID.StrikePrice <= MaxStrike and\
                                                     x.ID.Date > MinExpiry and\
                                                     x.ID.Date < MaxExpiry]
           
        if len(filtered_options) == 0: 
            return
           
        added_contracts = []
           
        for x in filtered_options:
            option = self.AddOptionContract(x, Resolution.Minute)
            optionsymbol = self.Securities[option.Symbol]
            added_contracts.append(optionsymbol)
            #added_contracts.append(option)
            
 
        sorted_options=sorted(added_contracts,key=sortedfunction,reverse=reverse)
        selected = sorted_options[0]
        if selected.BidSize == 0:
            selected = None
           
        for x in sorted_options:
            if x != selected:
                self.RemoveSecurity(x.Symbol)
                
        return selected            
   
    

    def Plotting(self):

        # plot underlying's price
        self.Plot("Data Chart Portfolio", self.equity.Symbol, self.Securities[self.equity.Symbol].Close)
        # plot strike of put option
        #self.Plot("Data Chart Options", self.contract, self.port_value_options)
        
        
        #option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
        #if option_invested:
        #    self.Plot("Data Chart", "strike", option_invested[0].ID.StrikePrice)

    def OnOrderEvent(self, orderEvent):
        # log order events
        self.Log(str(orderEvent))