Created with Highcharts 12.1.2EquityDec 2Dec 9Dec 16Dec 23Dec 30Jan 6Jan 13Jan 20Jan 27Feb 3Feb 10Feb 17Feb 24Mar 3Mar 10Mar 17900k950k1,000k1,050k-10-5000.050.10.15050k100k150k-0.05-0.02500.025
Overall Statistics
Total Orders
272
Average Win
0.08%
Average Loss
-0.07%
Compounding Annual Return
-16.193%
Drawdown
5.800%
Expectancy
-0.483
Start Equity
1000000
End Equity
952000
Net Profit
-4.800%
Sharpe Ratio
-5.839
Sortino Ratio
-6.585
Probabilistic Sharpe Ratio
0.151%
Loss Rate
74%
Win Rate
26%
Profit-Loss Ratio
1.01
Alpha
-0.181
Beta
-0.05
Annual Standard Deviation
0.029
Annual Variance
0.001
Information Ratio
0.321
Tracking Error
0.133
Treynor Ratio
3.429
Total Fees
$0.00
Estimated Strategy Capacity
$43000.00
Lowest Capacity Asset
SPXW 32P827391JGBY|SPX 31
Portfolio Turnover
2.75%
# region imports
from AlgorithmImports import *
# endregion


class OneDTEIndexOptionUniverseAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2024, 12, 1)
        self.set_cash(1_000_000)
        self.set_security_initializer(BrokerageModelSecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))
        self._spy = self.add_equity('SPY')
        self._spy.roc = self.roc(self._spy.symbol, 1, Resolution.DAILY)
        self._index = self.add_index('SPX')
        self._index.canonical = Symbol.create_canonical_option(self._index.symbol, "SPXW", Market.USA, "?SPXW")
        # Create a member to track when the algorithm should trade.
        date_rule = self.date_rules.every_day(self._index.symbol)
        self.schedule.on(date_rule, self.time_rules.before_market_close(self._index.symbol, 14), self._trade)
        self.schedule.on(date_rule, self.time_rules.before_market_close(self._index.symbol, 1), self.liquidate)
        self.add_risk_management(TrailingStopRiskManagementModel(0.05))

    def _trade(self):
        self.log(f"{self.time} - Trade")
        close = self._index.price
        # Get the 1DTE Options.
        chain = self.option_chain(self._index.canonical, flatten=True).data_frame
        expiry = chain[chain.expiry > self.time].expiry.min()
        chain = chain[chain.expiry == expiry]
        # Select ATM contracts.
        strike_below = chain[chain.strike <= close].strike.max()
        strike_above = chain[chain.strike >= close].strike.min()
        contracts_below = list(chain[chain.strike == strike_below].index)
        contracts_above = list(chain[chain.strike == strike_above].index)
        # Add the contracts.
        for symbol in contracts_below + contracts_above:
            self.add_option_contract(symbol)
        
        # Select call/put.
        if contracts_above[0].id.option_right == OptionRight.CALL:
            call = contracts_above[0]
            put = contracts_above[1]
        else:
            call = contracts_above[1]
            put = contracts_above[0]
        # Place entry trades.
        self.plot('ROC', 'Value', self._spy.roc.current.value)
        if self._spy.roc.current.value > 0: # Price drifts upward => Sell call, Buy put
            self.buy(call, 5)
            self.sell(put, 5)
        else: # Price drifts downward => Sell put, Buy call
            self.buy(put, 5)
            self.sell(call, 5)