Overall Statistics |
Total Orders 43 Average Win 3.48% Average Loss -2.84% Compounding Annual Return 22.041% Drawdown 14.000% Expectancy 0.484 Start Equity 100000 End Equity 131272.3 Net Profit 31.272% Sharpe Ratio 0.821 Sortino Ratio 1.209 Probabilistic Sharpe Ratio 58.596% Loss Rate 33% Win Rate 67% Profit-Loss Ratio 1.23 Alpha -0.026 Beta 0.975 Annual Standard Deviation 0.127 Annual Variance 0.016 Information Ratio -0.401 Tracking Error 0.074 Treynor Ratio 0.107 Total Fees $167.70 Estimated Strategy Capacity $6000.00 Lowest Capacity Asset QQQ YIJ7SWNISVJA|QQQ RIWIV7K5Z9LX Portfolio Turnover 0.51% |
from AlgorithmImports import * from datetime import datetime, timedelta from collections import deque class FocusedYellowLemur(QCAlgorithm): def Initialize(self): self.SetStartDate(2023, 1, 1) # Set Start Date self.SetEndDate(2024, 5, 13) self.SetCash(100000) # Set Strategy Cash self.ticker = "QQQ" self.res = Resolution.Minute self.equitySymbol = None self.optionSymbol = None equity = self.AddEquity(self.ticker, self.res) equity.SetDataNormalizationMode(DataNormalizationMode.Raw) option = self.AddOption(equity.Symbol) option.set_filter(0, +5, 30, 45) self.equitySymbol = equity.Symbol self.optionSymbol = option.Symbol self.consolidator = None self.consolidators = dict() self.lkb = 20 self.lkbOption = 20 self.atmCall = None # Underlying # Underlying # Create a minutes=30 QuoteBarConsolidator consolidator = QuoteBarConsolidator(timedelta(minutes=30)) # Create the custom indicator self.indicator = CustomIndicator("indicator", self.lkb) # Register the custom indicator to update with the consolidated data self.RegisterIndicator(self.equitySymbol, self.indicator, consolidator) # Subscribe to the consolidated data self.SubscriptionManager.AddConsolidator(self.equitySymbol, consolidator) # Option self.atm_indicator = CustomIndicator("atm_indicator", self.lkbOption) self.AddRiskManagement(MaximumUnrealizedProfitPercentPerSecurity(0.70)) def OnData(self, data): if not data.ContainsKey(self.equitySymbol): return option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option] if option_invested: if self.Time + timedelta(7) > option_invested[0].ID.Date: self.Liquidate(option_invested[0], "Too close to expiration") return self.updateIndicators(data, self.optionSymbol, self.indicator, self.atm_indicator) def updateIndicators(self, data, OS, underlying_indicator, atm_indicator): calls = None chain = data.option_chains.get_value(OS) if chain is None: return calls = [i for i in chain if i.Right == OptionRight.Call] if not calls: return call_contracts = sorted(calls, key = lambda x: x.Strike) #self.log("Checking") if len(call_contracts) < 1: return #self.Log("through") self.atmCall = call_contracts[0] if not underlying_indicator.IsReady or not atm_indicator.IsReady: return long_condition = self.securities[self.equitySymbol].Close > underlying_indicator.Value and self.securities[self.atmCall.Symbol].ask_price > atm_indicator.Value if not self.Portfolio[self.atmCall.Symbol].IsLong: if long_condition: self.MarketOrder(self.atmCall.Symbol, 6) self.Log("Bought atm call") self.Plot("Underlying", "Underlying Indicator", underlying_indicator.Value) self.Plot("Underlying", "Underlying Value", self.securities[self.equitySymbol].Close) self.Plot("Option", "Option Indicator", atm_indicator.Value) self.Plot("Option", "Option Value", self.securities[self.atmCall.Symbol].ask_price) def OnDataConsolidated(self, sender, quotebar): if self.atmCall != None: # Check if the quotebar is for thje atm call option if quotebar.Symbol == self.atmCall.Symbol: self.atm_indicator.Update(quotebar) #self.Log("OnDataConsolidated called for Call Option at " + str(self.Time)) def OnSecuritiesChanged(self, changes): for security in changes.AddedSecurities: if security.Type == SecurityType.Equity: # Check if the security is an Equity #self.Log(f"{security}") self.consolidator = QuoteBarConsolidator(timedelta(minutes=30)) self.consolidator.DataConsolidated += self.OnDataConsolidated self.SubscriptionManager.AddConsolidator(security.Symbol, self.consolidator) self.consolidators[security.Symbol] = self.consolidator else: self.consolidator = QuoteBarConsolidator(timedelta(minutes=30)) self.consolidator.DataConsolidated += self.OnDataConsolidated self.SubscriptionManager.AddConsolidator(security.Symbol, self.consolidator) self.consolidators[security.Symbol] = self.consolidator for security in changes.RemovedSecurities: if security.Symbol in self.consolidators: self.consolidator = self.consolidators.pop(security.Symbol) self.SubscriptionManager.RemoveConsolidator(security.Symbol, self.consolidator) self.consolidator.DataConsolidated -= self.OnDataConsolidated class CustomIndicator(PythonIndicator): def __init__(self, name, period): super().__init__() self.Name = name self.Value = 0 self.period = period self.queue = deque(maxlen=period) def Update(self, input): if not isinstance(input, QuoteBar): raise TypeError('CustomIndicator.Update: input must be a QuoteBar') self.queue.append(input.Close) self.Value = sum(self.queue) / self.period return self.IsReady @property def IsReady(self): return len(self.queue) == self.period