Overall Statistics |
Total Orders 352 Average Win 0.02% Average Loss -1.15% Compounding Annual Return 2.443% Drawdown 1.300% Expectancy 0.014 Start Equity 1000000 End Equity 1029320 Net Profit 2.932% Sharpe Ratio -7.008 Sortino Ratio -3.34 Probabilistic Sharpe Ratio 98.429% Loss Rate 1% Win Rate 99% Profit-Loss Ratio 0.02 Alpha -0.038 Beta 0 Annual Standard Deviation 0.005 Annual Variance 0 Information Ratio -0.882 Tracking Error 0.109 Treynor Ratio -261.257 Total Fees $0.00 Estimated Strategy Capacity $4000.00 Lowest Capacity Asset SPXW YPZ4T6Y6SOKU|SPX 31 Portfolio Turnover 0.01% |
# region imports from AlgorithmImports import * # endregion class OneDTEIndexOptionUniverseAlgorithm(QCAlgorithm): def initialize(self): self.set_start_date(2024, 1, 1) self.set_cash(1_000_000) self.settings.automatic_indicator_warm_up = True # Add 1DTE SPY contracts. self._index = self.add_index('SPX') self._index.std = IndicatorExtensions.Of(StandardDeviation(21), self.roc(self._index.symbol, 1, Resolution.DAILY)) self._option = self.add_index_option(self._index.symbol, 'SPXW') self._option.set_filter(lambda u: u.include_weeklys().expiration(1, 1).strikes(-40, 40)) # Create a member to track when the algorithm should trade. self._can_trade = False date_rule = self.date_rules.every_day(self._option.symbol) self.schedule.on(date_rule, self.time_rules.at(15, 45), lambda: setattr(self, '_can_trade', True)) self.schedule.on(date_rule, self.time_rules.at(16, 15), lambda: setattr(self, '_can_trade', False)) self.set_warm_up(timedelta(31)) def on_data(self, data): if self.is_warming_up or self.portfolio.invested: return # Only process data between 3:45 PM and 4:15 PM on Thursdays. if not self._can_trade: return # Get the Option chain. chain = data.option_chains.get(self._option.symbol) if not chain: return upper_threshold = self._index.price * (1 + 4*self._index.std.current.value) lower_threshold = self._index.price * (1 - 4*self._index.std.current.value) self.plot('STD', "Price", self._index.price) self.plot('STD', "upper_threshold", upper_threshold) self.plot('STD', "lower_threshold", lower_threshold) # Order the OTM calls by strike to find the nearest to ATM call_contracts = sorted( [contract for contract in chain if contract.right == OptionRight.CALL and contract.strike > upper_threshold], key=lambda x: x.strike ) if not call_contracts: return # Order the OTM puts by strike to find the nearest to ATM put_contracts = sorted( [contract for contract in chain if contract.right == OptionRight.PUT and contract.strike < lower_threshold], key=lambda x: x.strike, reverse=True ) if not put_contracts: return # Select the call and put contracts. call = call_contracts[0] put = put_contracts[0] # Place the trade. self.buy(OptionStrategies.short_strangle(self._option.symbol, call.strike, put.strike, call.expiry), 10)