Overall Statistics
Total Trades
4
Average Win
0%
Average Loss
-0.56%
Compounding Annual Return
14.820%
Drawdown
8.000%
Expectancy
-1
Net Profit
3.530%
Sharpe Ratio
1.035
Probabilistic Sharpe Ratio
49.409%
Loss Rate
100%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0.029
Beta
-0.19
Annual Standard Deviation
0.126
Annual Variance
0.016
Information Ratio
1.075
Tracking Error
0.62
Treynor Ratio
-0.688
Total Fees
$4.00
from QuantConnect.Data.Custom.CBOE import CBOE
from math import sqrt,floor

class ModifiedCollar(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2020, 1, 2)  # Set Start Date
        self.SetEndDate(2020, 4, 1)
        self.SetCash(100000)  # Set Strategy Cash
        
        self.SetBenchmark("SPY")
        
        self.stock = self.AddEquity("SPY", Resolution.Minute)
        self.stockSymbol = self.stock.Symbol
        self.stock.SetDataNormalizationMode(DataNormalizationMode.Raw)
        
        self.vixSymbol = self.AddData(CBOE, "VIX").Symbol
        self.vix = 0
        self.simpleMoving = SimpleMovingAverage(60)
        self.vixSMA = self.SMA(self.vixSymbol, 60, Resolution.Daily)
        self.simpleMoving.Updated += self.OnVixSMA
        
        self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x)))
        self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.AfterMarketOpen("SPY", 10), self.LogData)
        self.length = None
         
        self.callContract = self.putContract = None
        
        self.maxPutPrice = self.callPrice = 0
        
        self.callTicket = self.putTicket = None
        
        self.optionPercent = .04
        self.optionPercentOfStock = self.optionPercent / (1 - self.optionPercent)
        
        self.putsPerHundred, self.protectionNumber = 2, 1
        
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
        
        self.SetWarmUp(timedelta(days=60))
        
    def OnVixSMA(self, sender, updated):
        return
        
    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
            Argumen
                data: Slice object keyed by symbol containing the stock data
        '''
        self.Log("hello")
        for volatility in data.Get(CBOE).Values:
            self.vix = volatility.Value
            self.simpleMoving.Update(self.Time, self.vix)
            
        if self.IsWarmingUp:
            return
    
        if (not self.Portfolio[self.stockSymbol].Invested):
            number = self.CalculateOrderQuantity(self.stockSymbol, 1-self.optionPercent)
            self.protectionNumber = number // 100 
            number = self.protectionNumber * 100
            self.ticket = self.MarketOrder(self.stockSymbol, number)
            
        if (self.putContract is  None):
            self.putContract = self.GetPutContract()

     #   if (self.callContract is None):
     #       self.callContract = self.GetCallContract()
            
        if (self.putContract is not None and not self.Portfolio[self.putContract.Symbol].Invested):
            self.putTicket = self.MarketOrder(self.putContract.Symbol, self.protectionNumber * self.putsPerHundred)
            self.Debug("bought")
            self.maxPutPrice = self.putTicket.AverageFillPrice
           # self.Debug("bought price: {}".format(self.maxPutPrice))
        
     #   if (self.callContract is not None and not self.Portfolio[self.callContract.Symbol].Invested):
     #       self.callTicket = self.MarketOrder(self.callContract.Symbol, -self.protectionNumber)
     #       self.callPrice = self.callTicket.AverageFillPrice
            
     #   if (self.callContract.AskPrice * 2 > self.callPrice):
     #       self.Liquidate(self.callContract.Symbol)
     #      self.callContract = None
            
        if (self.putContract.BidPrice > self.maxPutPrice):
            self.maxPutPrice = self.putContract.BidPrice
            
        if (self.maxPutPrice * .8 >= self.putContract.AskPrice):
            self.putTicket = self.MarketOrder(self.putContract.Symbol, -self.protectionNumber * self.putsPerHundred)
            self.Debug("sold")
           # self.Debug("sold price: {}".format(self.putTicket.AverageFillPrice))
            self.RemoveSecurity(self.putContract.Symbol)
            self.maxPutPrice = 0
            self.putContract = None
            
        
    def GetPutContract(self):
        
        max_price = self.Portfolio.TotalPortfolioValue * self.optionPercent/ 100 / (self.protectionNumber * self.putsPerHundred)
        
        contracts = self.OptionChainProvider.GetOptionContractList(self.stock.Symbol, self.Time)
        x = 1
        contracts = [x for x in contracts if 330 <= (x.ID.Date - self.Time).days <= 400]
        x = 2
        contracts = [x for x in contracts if x.ID.StrikePrice < self.stock.Price]
        contracts = [x for x in contracts if x.ID.OptionRight == OptionRight.Put]
        contracts = sorted(sorted(contracts, key = lambda x: x.ID.StrikePrice, reverse = True), key = lambda x: abs(x.ID.Date - self.Time))
        
        for i in contracts:
            contract = self.AddOptionContract(i)
            if contract.AskPrice < max_price:
                return contract
            else:
                self.RemoveSecurity(i)
        
        return None
        
    def GetCallContract(self):
        
      #  if (self.vix >= self.vixSMA.Current.Value * 1.5):
      #      return None
        
        max_price = floor((1 + (self.vix/100 / sqrt(52) )) * self.Securities[self.stockSymbol].Price) 
        
        contracts = self.OptionChainProvider.GetOptionContractList(self.stock.Symbol, self.Time)
        contracts = [x for x in contracts if 5 <= (x.ID.Date - self.Time).days <= 7]
        contracts = [x for x in contracts if x.ID.StrikePrice > self.stock.Price]
        contracts = [x for x in contracts if x.ID.OptionRight == OptionRight.Call]
        contracts = sorted(sorted(contracts, key = lambda x: x.ID.StrikePrice, reverse = False), key = lambda x: x.ID.Date)
        
        for i in contracts:
            call = self.AddOptionContract(i)
            if i.ID.StrikePrice >= max_price:
                call = self.AddOptionContract(i)
                return call
        
        return None    
        
        
    def LogData(self):
        if self.putContract is not None:
            self.Debug("time: {}, stock price: {}, ask price: {}, bid price: {}, max price: {}, percentage different: {}".format(self.Time, self.stock.Price, self.putContract.AskPrice, self.putContract.BidPrice, self.maxPutPrice, (self.maxPutPrice - self.putContract.AskPrice)/self.maxPutPrice))