Overall Statistics
Total Trades
375
Average Win
0.05%
Average Loss
0%
Compounding Annual Return
7.059%
Drawdown
0.300%
Expectancy
0
Net Profit
7.956%
Sharpe Ratio
14.353
Probabilistic Sharpe Ratio
100%
Loss Rate
0%
Win Rate
100%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0.003
Annual Variance
0
Information Ratio
14.353
Tracking Error
0.003
Treynor Ratio
0
Total Fees
$2594.50
Estimated Strategy Capacity
$0
Lowest Capacity Asset
SPY 32549215ADBVQ|SPY R735QTJ8XC9X
from AlgorithmImports import *

index_names = ["SPX", "VIX", "NDX"]
option_names = ["SPXW", "VIXW", "NQX"]
index = 0
pos_sizes = [0.0006, 0.005, 0.01, 0.025, 0.05, 0.1, 0.2]  # 0-6
stop_losses = [0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 0]  # 0-6
pos_size = 0
stop_loss = -1
option_type = 0  # 0 - both, 1 - put, 2 - call
trade_start = (11, 5, 0)
trade_end = (0, 0, 0)
start_date = (2022, 1, 1)
end_date = (2023, 2, 14)
cash = 100_000
take_profit = 0


class SPXW(QCAlgorithm):
    def Initialize(self):
        self.Log([f"{x}" for x in self.GetParameters()])
        global pos_size, stop_loss, index, option_type
        pos_size = pos_sizes[int(self.GetParameter("pos_size") or pos_size)]
        stop_loss = stop_losses[int(self.GetParameter("stop_loss") or stop_loss)]
        index = int(self.GetParameter("index") or index)
        option_type = int(self.GetParameter("option_type") or option_type)
        self.SetBrokerageModel(InteractiveBrokersBrokerageModel(AccountType.Margin))

        self.SetStartDate(*start_date)
        self.SetEndDate(*end_date)
        self.SetCash(cash)

        self.index_security = self.AddIndex(index_names[index], Resolution.Minute)
        self.index_security = self.AddEquity("SPY", Resolution.Minute)

        # weekly option SPX contracts
        # index_options = self.AddIndexOption(self.index_security.Symbol, option_names[index])
        index_options = self.AddOption(self.index_security.Symbol, Resolution.Minute)
        # set our strike/expiry filter for this option chain
        index_options.SetFilter(lambda u: (u.Strikes(-25, 25)
                                           .Expiration(0, 0)
                                           .IncludeWeeklys()))

        self.option_symbol = index_options.Symbol
        self.ResetTrade()

        self.Schedule.On(self.DateRules.EveryDay(self.option_symbol), self.TimeRules.At(1, 0, 0), self.ResetTrade)
        self.Schedule.On(self.DateRules.EveryDay(self.option_symbol), self.TimeRules.At(*trade_start), self.StartTrade)
        self.Schedule.On(self.DateRules.EveryDay(self.option_symbol), self.TimeRules.At(*trade_end), self.StopTrade)
        # self.SetWarmUp(90, Resolution.Minute)

        if take_profit:
            self.AddRiskManagement(MaximumUnrealizedProfitPercentPerSecurity(take_profit))

    def ResetTrade(self):
        self.trade = False
        self.day_low = 100000
        self.day_high = 0

    def StartTrade(self):
        self.trade = True

    def StopTrade(self):
        self.trade = False

    def OnData(self, slice):
        current_price = self.index_security.Price
        if current_price < self.day_low:
            self.day_low = current_price
        if current_price > self.day_high:
            self.day_high = current_price

        delta = 0.01

        if not self.trade: return

        chain = slice.OptionChains.GetValue(self.option_symbol)
        if chain is None: return

        call = [x for x in chain if x.Right == OptionRight.Call]
        put = [x for x in chain if x.Right == OptionRight.Put]

        # we sort the contracts to find contract with the right delta
        put_contract = sorted(put, key=lambda x: abs(abs(x.Greeks.Delta) - delta))
        call_contract = sorted(call, key=lambda x: abs(x.Greeks.Delta - delta))

        # if found, trade it
        if option_type != 2:
            if len(put_contract) == 0:
                return
            else:
                # if current_price <= 1.001 * self.day_low:
                if current_price <= self.day_low:
                    put_ = put_contract[0].Symbol
                    self.SetHoldings(put_, -pos_size)
                    # self.MarketOrder(put_, -1000 * pos_size)
                    self.trade = False
                    return

        if option_type != 1:
            if len(call_contract) == 0:
                return
            else:
                # if current_price >= 0.999 * self.day_high:
                if current_price >= self.day_high:
                    call_ = call_contract[0].Symbol
                    # self.MarketOrder(call_, -1000 * pos_size)
                    self.SetHoldings(call_, -pos_size)
                    self.trade = False