Overall Statistics
Total Trades
18
Average Win
24.12%
Average Loss
-5.81%
Compounding Annual Return
113.712%
Drawdown
9.900%
Expectancy
0.546
Net Profit
30.155%
Sharpe Ratio
5.231
Probabilistic Sharpe Ratio
89.719%
Loss Rate
70%
Win Rate
30%
Profit-Loss Ratio
4.15
Alpha
-0.132
Beta
1.041
Annual Standard Deviation
0.23
Annual Variance
0.053
Information Ratio
-4.017
Tracking Error
0.02
Treynor Ratio
1.155
Total Fees
$22.50
# https://quantpedia.com/Screener/Details/20
from datetime import timedelta
from decimal import Decimal

class BullCallSpreadAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2020, 5, 1)
        self.SetEndDate(2020, 9, 4)
        self.SetCash(100000)
        equity = self.AddEquity("QQQ", Resolution.Minute)
        option = self.AddOption("QQQ", Resolution.Minute)
        self.symbol = equity.Symbol
        option.SetFilter(self.UniverseFunc)
        self.SetBenchmark(equity.Symbol)
        # self.delta50_call = None
        # self.delta50_put = None
        # self.delta25_put = None
        
    def OnData(self,slice):

        for i in slice.OptionChains:
            chains = i.Value
            if not self.Portfolio.Invested:
                # divide option chains into call and put options 
                calls = list(filter(lambda x: x.Right == OptionRight.Call, chains))
                puts = list(filter(lambda x: x.Right == OptionRight.Put, chains))
                # if lists are empty return
                if not calls or not puts: return
                underlying_price = self.Securities[self.symbol].Price
                expiries = [i.Expiry for i in puts]
                
                #sell_delta50_call_expiry = [call if call.Expiry == self.delta50_call.Expiry for call in calls]
                
                #sell_delta50_call = [call if call.Strike == self.delta50_call.Strike for call in sell_delta50_call_expiry]
                                            
                #sell_delta50_put = [put if put.Expiry == self.delta50_put.Expiry if
                                            #put.Strike == self.delta50_put.Strike for put in puts]       
                # determine expiration date nearly one month
                expiry = min(expiries, key=lambda x: abs((x.date()-self.Time.date()).days-30))
                strikes = [i.Strike for i in puts]
                # determine at-the-money strike
                strike = min(strikes, key=lambda x: abs(x-Decimal(0.50)*underlying_price))
                # determine 15% out-of-the-money strike
                otm_strike = min(strikes, key = lambda x:abs(x-Decimal(0.25)*underlying_price))
                self.atm_call = [i for i in calls if i.Expiry == expiry and i.Strike == strike]
                self.atm_put = [i for i in puts if i.Expiry == expiry and i.Strike == strike]
                self.otm_put = [i for i in puts if i.Expiry == expiry and i.Strike == otm_strike]
                
                #if (self.delta25_put.Expiry - self.Time).days <= 2:

                if self.atm_call and self.atm_put and self.otm_put:
                    self.Debug("Underlying Price: " + str(underlying_price))
                    self.delta50_call = self.atm_call[0]
                    self.Debug("50 Delta Call: " + str(self.delta50_call.Strike))
                    self.delta50_put = self.atm_put[0]
                    self.Debug("50 Delta Put: " + str(self.delta50_put.Strike))
                    self.delta25_put = self.otm_put[0]
                    self.Debug("25 Delta Put: " + str(self.delta25_put.Strike))
                    # sell at-the-money straddle
                    self.Buy(self.delta50_call.Symbol, 5)
                    self.Buy(self.delta50_put.Symbol, 5)
                    # buy 25% out-of-the-money put
                    self.Sell(self.delta25_put.Symbol, 5)

            if self.Portfolio.Invested:
                # if (self.delta50_call.Expiry - self.Time).days <= 2:
                #         days = (self.delta50_call.Expiry-self.Time).days
                #         self.Debug(days)
                        self.Liquidate(self.symbol)
                        # self.delta50_call = None
                        # self.delta50_put = None
                        # self.delta25_put = None
                        
                
        
         
    def OnOrderEvent(self, orderEvent):
        self.Log(str(orderEvent)) 

    def UniverseFunc(self, universe):
        return universe.IncludeWeeklys().Strikes(-40, 40).Expiration(timedelta(25), timedelta(35))