Overall Statistics
Total Trades
8
Average Win
0.29%
Average Loss
-0.65%
Compounding Annual Return
-21.226%
Drawdown
1.600%
Expectancy
-0.419
Net Profit
-1.363%
Sharpe Ratio
-5.682
Probabilistic Sharpe Ratio
0.007%
Loss Rate
60%
Win Rate
40%
Profit-Loss Ratio
0.45
Alpha
-0.101
Beta
0.116
Annual Standard Deviation
0.036
Annual Variance
0.001
Information Ratio
3.142
Tracking Error
0.22
Treynor Ratio
-1.774
Total Fees
$20.00
Estimated Strategy Capacity
$470000.00
Lowest Capacity Asset
SPY 31BIVCUZR6WDI|SPY R735QTJ8XC9X
from datetime import timedelta
from datetime import datetime
import math

from QuantConnect.Securities.Option import OptionPriceModels

class BullPutSpreadAlgorithm(QCAlgorithm):
    
    ordersList = list()
   
    def Initialize(self):
        
        self.SetStartDate(2020,2, 10)
        self.SetEndDate(2020,2, 29)
        self.SetCash(600000)
        
        self.SetWarmUp(30, Resolution.Minute)  
        
        equity = self.AddEquity("SPY", Resolution.Minute)
        option = self.AddOption("SPY", Resolution.Minute)
        
        self.symbol = option.Symbol
    
        option.SetFilter(lambda universe: universe.WeeklysOnly().Strikes(-30, -5).Expiration(timedelta(0), timedelta(29)))
       
        # use the underlying equity SPY as the benchmark
        option.PriceModel = OptionPriceModels.CrankNicolsonFD() 
        self.SetBenchmark(equity.Symbol)
        self.SetWarmUp(timedelta(45))
        self.optionMuliplier = option.ContractMultiplier
        
        self.Schedule.On(self.DateRules.WeekStart(), self.TimeRules.At(9,35), self.OpenWeekTrade)
            
    def OnData(self,slice):
        
        optionchain = slice.OptionChains
        for i in slice.OptionChains:
            if i.Key != self.symbol: continue
            chains = i.Value
            contract_list = [x for x in chains]
        
        # if there is no contracts in this optionchain, pass the instance
        if (slice.OptionChains.Count == 0) or (len(contract_list) == 0): 
            return

        self.options_data = optionchain
        
    def OnOrderEvent(self, orderEvent):
        order = self.Transactions.GetOrderById(orderEvent.OrderId)
        quantity = orderEvent.FillQuantity
        fill_price = orderEvent.FillPrice
        underlying = orderEvent.Symbol.Underlying
        sym = orderEvent.Symbol
        
        if order.Type == OrderType.OptionExercise:
            self.Debug("Assignment: " + str(orderEvent.Symbol) + " quan: " + str(quantity) + " time: " + str(self.Time) )
            self.ExerciseOnAssignment(sym)
        
    
    def ExerciseOnAssignment(self, sym):
        ticket = None
        option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
        s_options = sorted(option_invested, key = lambda x: x.ID.StrikePrice, reverse=True )
        
        self.Debug("Length of s_options: " + str(len(s_options)))
        
        for option in s_options:
            if (option.Underlying == sym ):
                self.Log( str(self.ExerciseOption(option, 20 )) + " : Exercised: " + str(self.Time.second ) ) 
       
        self.Log("SPY Quan: " + str(self.Portfolio[sym].Quantity))
        
        if self.Portfolio[sym].Quantity != 0:
            self.Log("Exercise failed: " + str(sym) + ":" + str(self.Portfolio[option.Underlying].Quantity ) )
            self.Debug("Quantity after exercise " + str(self.Portfolio[option.Underlying].Quantity))
    
    def OpenWeekTrade(self):
      
        for i in self.options_data:
                
            if i.Key != self.symbol: continue
            chain = i.Value
            
            expiry = sorted(chain,key = lambda x: x.Expiry)[0].Expiry
            put = [i for i in chain if i.Right == 1 and i.Greeks.Delta > -0.1 and i.Strike % 1 == 0 and (i.Expiry-self.Time).days == 3 ]
            put_contracts = sorted(put,key = lambda x: x.Strike, reverse=True)
        
            if len(put_contracts) == 0: 
                continue
            
            #5 point wide spread
            self.long_put = put_contracts[5]
            self.short_put = put_contracts[0]
            
            self.Order(self.short_put.Symbol, -20)
            self.Order(self.long_put.Symbol, 20)