Overall Statistics |
Total Orders 23 Average Win 37715.81% Average Loss -77.15% Compounding Annual Return -100.000% Drawdown 100.000% Expectancy 292.912 Start Equity 100000 End Equity 40.35 Net Profit -99.960% Sharpe Ratio 0.665 Sortino Ratio 0.825 Probabilistic Sharpe Ratio 27.154% Loss Rate 40% Win Rate 60% Profit-Loss Ratio 488.85 Alpha -2.35 Beta 33.139 Annual Standard Deviation 4.969 Annual Variance 24.692 Information Ratio 0.637 Tracking Error 4.916 Treynor Ratio 0.1 Total Fees $1272.20 Estimated Strategy Capacity $43000.00 Lowest Capacity Asset SPY YHRN4T1I63QE|SPY R735QTJ8XC9X Portfolio Turnover 32.02% |
from AlgorithmImports import * from datetime import timedelta, datetime class SupportResistanceOptionsTradingAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2024, 1, 1) self.SetEndDate(2024, 6, 29) self.SetCash(100000) self.symbols = ["SPY"] self.trade_amount = self.Portfolio.Cash # Allocate all available cash to trades for symbol in self.symbols: equity = self.AddEquity(symbol, Resolution.Minute) equity.SetLeverage(1) option = self.AddOption(symbol, Resolution.Minute) option.SetFilter(-10, 10, timedelta(0), timedelta(30)) self.support_levels = {} self.resistance_levels = {} self.entry_contracts = {} self.traded_today = {symbol: False for symbol in self.symbols} self.open_orders = {} for symbol in self.symbols: self.Schedule.On(self.DateRules.EveryDay(symbol), self.TimeRules.AfterMarketOpen(symbol, 30), Action(lambda symbol=symbol: self.IdentifySupportResistanceLevels(symbol))) self.Schedule.On(self.DateRules.EveryDay(symbol), self.TimeRules.BeforeMarketClose(symbol, 10), Action(lambda symbol=symbol: self.CloseExpiringPositions(symbol))) self.Schedule.On(self.DateRules.EveryDay(symbol), self.TimeRules.BeforeMarketClose(symbol, 10), Action(self.LiquidateUnexpectedShares)) # Schedule to reset the traded_today flag after market open self.Schedule.On(self.DateRules.EveryDay(symbol), self.TimeRules.AfterMarketOpen(symbol, 0), Action(lambda symbol=symbol: self.ResetTradedTodayFlag(symbol))) def ResetTradedTodayFlag(self, symbol): self.traded_today[symbol] = False self.Debug(f"{symbol}: Reset traded_today flag for the new day.") def OnData(self, data): if self.IsWarmingUp: return for symbol in self.symbols: if symbol in data.Bars and not self.traded_today[symbol]: if self.IsMarketOpen(symbol): if self.IsSupportConfirmed(symbol): self.Debug(f"{symbol}: Support confirmed. Executing trade.") self.ExecuteTrade(symbol) def IdentifySupportResistanceLevels(self, symbol): history = self.History([symbol], 60, Resolution.Minute) if not history.empty: data_4_hour = history.loc[symbol].resample('4H').agg({'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last'}) data_daily = history.loc[symbol].resample('D').agg({'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last'}) self.support_levels[symbol] = min(data_4_hour['low'].min(), data_daily['low'].min()) self.resistance_levels[symbol] = max(data_4_hour['high'].max(), data_daily['high'].max()) self.Debug(f"{symbol} Support Levels: {self.support_levels[symbol]}") self.Debug(f"{symbol} Resistance Levels: {self.resistance_levels[symbol]}") def IsSupportConfirmed(self, symbol): current_price = self.Securities[symbol].Price support_level = self.support_levels.get(symbol, None) return current_price >= support_level if support_level is not None else False def ExecuteTrade(self, symbol): # Ensure only one trade per day per symbol if self.traded_today[symbol]: self.Debug(f"{symbol}: Already traded today.") return contract = self.GetClosestToResistanceOptionContract(symbol, OptionRight.Call) if contract and self.Securities.ContainsKey(contract): if not self.Securities[contract].Invested and self.Securities[contract].HasData: price = self.Securities[contract].Price # Ensure price is non-zero and contract is liquid if price > 0 and self.Securities[contract].Volume > 0: quantity = int(self.Portfolio.Cash // (price * 100)) # Allocate all cash to trade if quantity > 0: self.MarketOrder(contract, quantity) self.entry_contracts[symbol] = contract self.traded_today[symbol] = True # Set flag to prevent further trades for today self.open_orders[contract] = quantity # Track the open order self.Debug(f"Executed trade for {symbol}, contract: {contract}, quantity: {quantity}, price: {price}") else: self.Debug(f"{symbol}: Quantity calculated as zero, trade not executed.") else: self.Debug(f"{symbol}: Price is zero or volume is too low, trade not executed.") else: self.Debug(f"{symbol}: No valid contract or already invested.") else: self.Debug(f"{symbol}: No suitable contract found.") def GetClosestToResistanceOptionContract(self, symbol, option_right): contracts = self.OptionChainProvider.GetOptionContractList(symbol, self.Time) if not contracts: self.Debug(f"No contracts found for {symbol}") return None resistance_level = self.resistance_levels.get(symbol, None) if resistance_level is None or resistance_level == 0: self.Debug(f"No resistance level found or it's zero for {symbol}") return None # Filter for liquid contracts with sufficient volume and non-zero price liquid_contracts = [ contract for contract in contracts if contract.ID.OptionRight == option_right and contract.ID.Date > self.Time # Ensure it's not expiring today and self.Securities.ContainsKey(contract) and self.Securities[contract].Price > 0 # Ensure non-zero price and self.Securities[contract].Volume > 0 # Ensure it has volume ] if not liquid_contracts: self.Debug(f"No liquid contracts found for {symbol}") return None # Find the contract with the strike price closest to the resistance level closest_contract = min( liquid_contracts, key=lambda x: abs(x.ID.StrikePrice - resistance_level), default=None ) if closest_contract: self.Debug(f"Selected contract closest to resistance for {symbol}: Strike: {closest_contract.ID.StrikePrice}, Expiry: {closest_contract.ID.Date}, Right: {closest_contract.ID.OptionRight}") return closest_contract self.Debug(f"No suitable contract found for {symbol}") return None def CloseExpiringPositions(self, symbol): if symbol not in self.entry_contracts: self.Debug(f"{symbol}: No entry contracts found.") return contract = self.entry_contracts[symbol] if contract.ID.Date <= self.Time + timedelta(days=1): if contract in self.Portfolio and self.Portfolio[contract].Invested: quantity = self.Portfolio[contract].Quantity self.MarketOrder(contract, -quantity) self.traded_today[symbol] = False if contract in self.open_orders: del self.open_orders[contract] self.Debug(f"Closed expiring position for {symbol}, contract: {contract}, quantity: {quantity}") def LiquidateUnexpectedShares(self): for security in self.Portfolio.Values: if security.Invested and security.Type == SecurityType.Equity: self.Debug(f"Unexpected shares found for {security.Symbol}. Liquidating...") self.Liquidate(security.Symbol) def OnOrderEvent(self, orderEvent): self.Debug(str(orderEvent))