Overall Statistics
Total Trades
106
Average Win
2.02%
Average Loss
-1.19%
Compounding Annual Return
35.036%
Drawdown
11.700%
Expectancy
0.121
Net Profit
6.860%
Sharpe Ratio
1.075
Probabilistic Sharpe Ratio
48.857%
Loss Rate
58%
Win Rate
42%
Profit-Loss Ratio
1.70
Alpha
0.059
Beta
1.271
Annual Standard Deviation
0.252
Annual Variance
0.064
Information Ratio
0.569
Tracking Error
0.183
Treynor Ratio
0.213
Total Fees
$106.00
Estimated Strategy Capacity
$1800000.00
Lowest Capacity Asset
SPY Y6GZIMV0GG5I|SPY R735QTJ8XC9X
Portfolio Turnover
2.15%
# region imports
from AlgorithmImports import *
# endregion

# ------------------------------------------------------------
TICKER = "SPY"; PERIOD = 3
# ------------------------------------------------------------

class MeasuredMagentaDogfish(QCAlgorithm):

    def Initialize(self):
        self.SetSecurityInitializer(lambda x: x.SetDataNormalizationMode(DataNormalizationMode.Raw))
        self.SetStartDate(2023, 1, 1)  # Set Start Date
        self.SetCash(10000)  # Set Strategy Cash
        self.equity = self.AddEquity(TICKER, Resolution.Minute).Symbol
        self.SetBenchmark(TICKER)

        ## Set up options chain
        option = self.AddOption(TICKER, Resolution.Minute)
        self.symbol = option.Symbol
     

        self.call = TICKER # Initialize the call contract
        self.qty = 1 # number of contracts traded
        option.SetFilter(self.UniverseFunc)


    def UniverseFunc(self, universe):
        return universe.IncludeWeeklys().Strikes(-1, 1).Expiration(timedelta(0), timedelta(15))

    # Enter at 9:45 am each morning
    def OnData(self, slice):
        if self.Time.hour == 9 and self.Time.minute == 45:
            self.BuyCalls(slice)


    # Check position P&L and set TP and SL
    def OnEndOfDay(self, symbol):
        self.Liquidate(tag="Close positions at EOD")
    
    
    def BuyCalls(self,slice):
        self.Log("FINDING options chain...")

        if slice.OptionChains.Count == 0: return
        self.Log("FOUND options chain...")   
        
        for i in slice.OptionChains:
            if i.Key != self.symbol: continue
            chain = i.Value
            call = [x for x in chain if x.Right == 0] # filter the call options contracts
            # sorted the contracts according to their expiration dates and choose the ATM options
            contracts = sorted(sorted(call, key = lambda x: x.Expiry, reverse=False), 
                                            key = lambda x: abs(chain.Underlying.Price - x.Strike))

            df = pd.DataFrame([[x.Right,x.Strike,x.Expiry,x.BidPrice,x.AskPrice] for x in chain],
                           index=[x.Symbol.Value for x in chain],
                           columns=['type(call 0, put 1)', 'strike', 'expiry', 'ask price', 'bid price'])
            
            self.Log(str(df))
            
            if len(contracts) == 0: return
            self.Log("Length of contract is NOT zero, entering order...")

            contract = contracts[0] # select the closest expiry contract
            self.call = contract.Symbol
            self.Buy(self.call, self.qty) # buy the call option
            self.tradeLimit = 1