Overall Statistics
Total Trades
66
Average Win
32.91%
Average Loss
-8.33%
Compounding Annual Return
24.793%
Drawdown
40.700%
Expectancy
2.959
Net Profit
202.942%
Sharpe Ratio
0.828
Probabilistic Sharpe Ratio
26.136%
Loss Rate
20%
Win Rate
80%
Profit-Loss Ratio
3.95
Alpha
0.088
Beta
1.121
Annual Standard Deviation
0.316
Annual Variance
0.1
Information Ratio
0.423
Tracking Error
0.253
Treynor Ratio
0.233
Total Fees
$685.50
Estimated Strategy Capacity
$0.039
Lowest Capacity Asset
SPY XUERCXXVY92E|SPY R735QTJ8XC9X
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")

from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *
from datetime import timedelta
from QuantConnect.Securities.Option import OptionPriceModels

class DCAintoLEAPs(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2016, 6, 1)
        self.SetEndDate(2021, 6, 1)
        self.SetWarmUp(90, Resolution.Daily)
        self.UniverseSettings.SetLeverage = 1
        self.num_buys = 24
        self.amt_per_txn = 500000//24

        self.SetCash(500000)
        self.AddEquity("SPY", Resolution.Daily)

        option = self.AddOption("SPY")
        option.PriceModel = OptionPriceModels.CrankNicolsonFD()
        self.option_symbol = option.Symbol
        # set our strike/expiry filter for this option chain
        option.SetFilter(self.UniverseFunc)

        # use the underlying equity as the benchmark
        self.SetBenchmark("SPY")
        
        self.contracts = None
        
        self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.AfterMarketOpen("SPY", 10), self.OnDataTrade)
        self.Schedule.On(self.DateRules.WeekStart("SPY"), self.TimeRules.AfterMarketOpen("SPY", 10), self.sell_expiring)


    def UniverseFunc(self, universe):
        return universe.IncludeWeeklys().Strikes(-100, 100).Expiration(timedelta(380), timedelta(445))


    def OnData(self,slice):
        if not (self.Time.hour == 9 and self.Time.minute == 38): return
        for kvp in slice.OptionChains:
            if kvp.Key != self.option_symbol: continue
            chain = kvp.Value
            chain = [x for x in chain if x.Right == OptionRight.Call]
            closest_expiry_beyond_1y = sorted(chain,key = lambda x: x.Expiry)[0].Expiry
            chain = [x for x in chain if x.Expiry == closest_expiry_beyond_1y]
            contracts = sorted(chain, key = lambda x: abs(x.Greeks.Delta-0.4))
            x = contracts[0]
            columns=['idx', 'type(call 0, put 1)', 'strike', 'expiry', 'ask price', 'bid price', 'delta', x.Symbol.Value,\
            x.Right,float(x.Strike),x.Expiry,float(x.BidPrice),float(x.AskPrice),float(x.Greeks.Delta)]
            self.Log(str(columns))
            self.contracts = contracts
            
    def sell_expiring(self):
            # Sell calls that are expiring within a week
            leaps = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
            for x in leaps:
                if (self.Securities[x].Expiry.date() - self.Time.date()).days <= 14:
                    self.Liquidate(x)
        
    def OnDataTrade(self):
            # Buy calls
            if len(self.contracts) == 0: pass
            symbol = self.contracts[0].Symbol
            self.MarketOrder(symbol, (self.amt_per_txn/100)//self.contracts[0].AskPrice)


    def OnOrderEvent(self, orderEvent):
        self.Log(str(orderEvent))