| 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