Overall Statistics
Total Trades
70
Average Win
1.77%
Average Loss
-1.29%
Compounding Annual Return
35.950%
Drawdown
8.100%
Expectancy
0.156
Net Profit
6.570%
Sharpe Ratio
1.259
Probabilistic Sharpe Ratio
52.670%
Loss Rate
51%
Win Rate
49%
Profit-Loss Ratio
1.38
Alpha
0.166
Beta
1.073
Annual Standard Deviation
0.211
Annual Variance
0.044
Information Ratio
1.181
Tracking Error
0.146
Treynor Ratio
0.247
Total Fees
$70.00
Estimated Strategy Capacity
$98000.00
Lowest Capacity Asset
SPY Y6TSBE5T7HIE|SPY R735QTJ8XC9X
Portfolio Turnover
4.01%
# 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
        option.SetFilter(-1, 1, timedelta(0), timedelta(days = 15))
        self.call = TICKER # Initialize the call contract
        self.qty = 1 # number of contracts traded

    # 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