Overall Statistics |
Total Orders 855 Average Win 8.21% Average Loss -2.25% Compounding Annual Return 1537.302% Drawdown 49.100% Expectancy 0.551 Start Equity 100000 End Equity 396328.69 Net Profit 296.329% Sharpe Ratio 12.042 Sortino Ratio 21.99 Probabilistic Sharpe Ratio 81.917% Loss Rate 67% Win Rate 33% Profit-Loss Ratio 3.65 Alpha 17.609 Beta 6.895 Annual Standard Deviation 1.567 Annual Variance 2.457 Information Ratio 12.399 Tracking Error 1.507 Treynor Ratio 2.737 Total Fees $16512.89 Estimated Strategy Capacity $10000.00 Lowest Capacity Asset GOOG YAAU38D9I9GM|GOOG T1AZ164W5VTX Portfolio Turnover 40.88% |
from AlgorithmImports import * from datetime import timedelta, datetime class SupportResistanceOptionsTradingAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2023, 1, 1) self.SetEndDate(2023, 6, 29) self.SetCash(100000) self.symbols = ["TSLA", "NVDA", "GOOGL", "NFLX", "AMD", "AAPL", "META", "MSFT", "AMZN"] for symbol in self.symbols: equity = self.AddEquity(symbol, Resolution.Minute, extendedMarketHours=True) equity.SetLeverage(1) option = self.AddOption(symbol) 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 = {} self.stop_losses = {} self.scale_out_thresholds = {} 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.ScaleOutPositions(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(), self.TimeRules.At(15, 50), self.LiquidateUnexpectedShares) self.Schedule.On(self.DateRules.WeekEnd(), self.TimeRules.At(15, 50), self.CloseAllPositions) def OnData(self, data): if self.IsWarmingUp: return for symbol in self.symbols: if symbol in data.Bars and not self.traded_today[symbol] and self.Time.weekday() < 4: # Only trade from Monday to Thursday if self.IsMarketOpen(symbol): if self.IsSupportConfirmed(symbol): self.Debug(f"{symbol}: Support confirmed. Executing call trade.") self.ExecuteTrade(symbol, OptionRight.Call) elif self.IsSupportBroken(symbol): self.Debug(f"{symbol}: Support broken. Closing position.") self.ClosePosition(symbol) self.UpdateLevels(symbol, broken_support=True) self.CheckStopLoss(symbol) def IdentifySupportResistanceLevels(self, symbol): history = self.History([symbol], 60, Resolution.Minute, extendedMarketHours=True) if not history.empty: data_30_min = history.loc[symbol].resample('30T').agg({'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last'}) self.support_levels[symbol] = data_30_min['low'].min() self.resistance_levels[symbol] = data_30_min['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 IsSupportBroken(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 UpdateLevels(self, symbol, broken_support): if broken_support: self.resistance_levels[symbol] = self.support_levels[symbol] self.support_levels[symbol] = None self.Debug(f"{symbol}: Support broken. New Resistance Level: {self.resistance_levels[symbol]}") else: self.support_levels[symbol] = self.resistance_levels[symbol] self.resistance_levels[symbol] = None self.Debug(f"{symbol}: Resistance broken. New Support Level: {self.support_levels[symbol]}") def ExecuteTrade(self, symbol, option_right): if self.traded_today[symbol]: self.Debug(f"{symbol}: Already traded today.") return contract = self.GetClosestToResistanceOptionContract(symbol, option_right) if contract and self.Securities.ContainsKey(contract): if not self.Securities[contract].Invested and self.Securities[contract].HasData: position_size = self.CalculatePositionSize() price = self.Securities[contract].Price if price != 0: quantity = position_size // (price * 100) # 100 shares per contract if quantity > 0: self.MarketOrder(contract, quantity) self.entry_contracts[symbol] = contract self.traded_today[symbol] = True self.open_orders[contract] = quantity # Track the open order self.stop_losses[contract] = min(price * 0.5, price - (price - self.support_levels[symbol])) # 50% stop loss or support break self.scale_out_thresholds[contract] = self.CalculateScaleOutThresholds(contract, quantity) # Scale out thresholds self.Debug(f"Executed trade for {symbol}, contract: {contract}, quantity: {quantity}") else: self.Debug(f"Quantity calculated as zero for {symbol}, skipping trade.") else: self.Debug(f"Price is zero for {symbol}, skipping trade.") def CalculateScaleOutThresholds(self, contract, quantity): if contract.Underlying not in self.resistance_levels: self.Debug(f"{contract.Underlying} not in resistance levels.") return [] price = self.Securities[contract].Price increment = (self.resistance_levels[contract.Underlying] - price) / 3 return [(price + increment * (i + 1), quantity // 3) for i in range(3)] def CalculatePositionSize(self): available_cash = self.Portfolio.Cash position_size = available_cash / len(self.symbols) if len(self.symbols) > 0 else 0 self.Debug(f"Calculated position size: {position_size}") return position_size def ClosePosition(self, symbol): contract = self.entry_contracts.get(symbol, None) if contract and 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] if contract in self.stop_losses: del self.stop_losses[contract] if contract in self.scale_out_thresholds: del self.scale_out_thresholds[contract] self.Debug(f"Closed position for {symbol}, contract: {contract}, quantity: {quantity}") def CheckStopLoss(self, symbol): contract = self.entry_contracts.get(symbol, None) if contract and contract in self.Portfolio and self.Portfolio[contract].Invested: current_price = self.Securities[contract].Price stop_loss_price = self.stop_losses.get(contract, None) if stop_loss_price is not None and (current_price <= stop_loss_price or self.IsSupportBroken(symbol)): self.Debug(f"{symbol}: Stop loss triggered. Closing position.") self.ClosePosition(symbol) def ScaleOutPositions(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 not in self.scale_out_thresholds: self.Debug(f"{symbol}: No scale out thresholds found for {contract}.") return thresholds = self.scale_out_thresholds[contract] for price, qty in thresholds: if self.Securities[contract].Price >= price: self.Debug(f"Scaling out position for {symbol}, contract: {contract}, at price: {price}") self.MarketOrder(contract, -qty) thresholds.remove((price, qty)) break if not thresholds: self.traded_today[symbol] = False self.Debug(f"All scale out thresholds met for {symbol}. Resetting trade status.") 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: self.Debug(f"No resistance level found for {symbol}") return None closest_contract = min( (contract for contract in contracts if contract.ID.OptionRight == option_right and contract.ID.Date.weekday() == 4), 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] if contract in self.stop_losses: del self.stop_losses[contract] if contract in self.scale_out_thresholds: del self.scale_out_thresholds[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 CloseAllPositions(self): for symbol in self.symbols: self.ClosePosition(symbol) def OnOrderEvent(self, orderEvent): self.Debug(str(orderEvent))