| Overall Statistics |
|
Total Trades 13 Average Win 33.16% Average Loss -0.25% Compounding Annual Return 79228162514264337593543950335% Drawdown 1.300% Expectancy 110.103 Net Profit 429.014% Sharpe Ratio 2.5850910150313346E+21 Probabilistic Sharpe Ratio 100.000% Loss Rate 17% Win Rate 83% Profit-Loss Ratio 132.32 Alpha 9.431259192293879E+21 Beta 4.688 Annual Standard Deviation 3.648 Annual Variance 13.31 Information Ratio 2.614276119555494E+21 Tracking Error 3.608 Treynor Ratio 2.0116827907094746E+21 Total Fees $10.00 Estimated Strategy Capacity $290000.00 Lowest Capacity Asset QQQ Y4WV0TYZQ606|QQQ RIWIV7K5Z9LX |
#region imports
from AlgorithmImports import *
#endregion
from datetime import timedelta
class RSI_Slope(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2023,1,1)
self.SetEndDate(2023,1,10)
self.SetCash(100000)
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage)
self.SetWarmUp(5000, Resolution.Minute)
#QQQ option initialize
self.equityqqq = self.AddEquity("QQQ", Resolution.Minute)
self.equityqqq.SetDataNormalizationMode(DataNormalizationMode.Raw)
self.symbolqqq = self.equityqqq.Symbol
self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(self.equityqqq)))
self.SetBenchmark(self.symbolqqq)
#Make lists to guide trading algo
self.qqqequitylist = dict()
self.qqqequitylist[0] = 0
self.qqqcoveredcalllist = dict()
self.qqqcoveredcalllist[0] = 0
self.callcontractqqq = str()
self.callcontractsAddedqqq = set()
self.numberofshares = dict()
self.numberofshares[0] = 0
# parameters ------------------------------------------------------------
self.CallDTE = 14 # target days till expiration
self.CallOTM = 1.0075 # target percentage OTM of call
# ------------------------------------------------------------------------
self.Schedule.On(self.DateRules.EveryDay(),self.TimeRules.AfterMarketOpen("QQQ",-1), self.OpeningBarQQQ)
self.Schedule.On(self.DateRules.EveryDay(),self.TimeRules.AfterMarketOpen("QQQ",1), self.Sellingcalls)
def OpeningBarQQQ(self):
if self.qqqcoveredcalllist[0] == 1:
self.qqqcoveredcalllist[0] = 0
self.callcontractqqq = str()
self.callcontractsAddedqqq = set()
self.Log("Equitylist: {0}".format(self.qqqequitylist[0]))
def Sellingcalls(self):
if self.qqqcoveredcalllist[0] == 0:
self.qqqcoveredcalllist[0] = 5
def OnData(self, data):
if self.IsWarmingUp: return
QQQprice = self.Securities["QQQ"].Price
investedQQQequity = [symbol for symbol, holding in self.Portfolio.items() if holding.IsLong and holding.Type == SecurityType.Equity]
shortcalloptioninvestedQQQ = [symbol for symbol, holding in self.Portfolio.items() if holding.IsShort and holding.Type == SecurityType.Option and symbol.Underlying.Equals(self.symbolqqq) and symbol.ID.OptionRight == OptionRight.Call]
#trading logic for QQQ covered calls
#if algo restarts and option invested this will update the lists and option logs
if investedQQQequity:
if self.qqqequitylist[0] == 0:
self.qqqequitylist[0] = 1
if shortcalloptioninvestedQQQ:
if self.qqqcoveredcalllist[0] == 0:
self.qqqcoveredcalllist[0] = 1
if not self.Portfolio["QQQ"].Invested:
self.qqqequitylist[0] = 0
self.Log("Out of QQQ")
if self.qqqequitylist[0] == 0:
quantity = self.CalculateOrderQuantity("QQQ", 0.5)
rounded = round(quantity, -2)
self.MarketOrder("QQQ", rounded)
self.Log("Bought shares of QQQ")
self.numberofshares[0] = rounded / 100
self.Log("Number of countracts to sell: {0}".format(self.numberofshares[0]))
self.qqqequitylist[0] = 1
if self.qqqcoveredcalllist[0] == 5:
self.Buyqqqcall(data)
#options filter and buy logic will find a call and buy a call
def Buyqqqcall(self, data):
Contractstosell = self.numberofshares[0]
# get option data
if self.callcontractqqq == str():
self.Log("Obtaining QQQ call contract")
self.callcontractqqq = self.OptionsFilterqqqcall(data)
return
# if not invested and option data added successfully, buy option
if not self.Portfolio[self.callcontractqqq].Invested and data.ContainsKey(self.callcontractqqq):
self.MarketOrder(self.callcontractqqq, -Contractstosell)
self.qqqcoveredcalllist[0] = 1
def OptionsFilterqqqcall(self, data):
''' OptionChainProvider gets a list of option contracts for an underlying symbol at requested date.
Then you can manually filter the contract list returned by GetOptionContractList.
The manual filtering will be limited to the information included in the Symbol
(strike, expiration, type, style) and/or prices from a History call '''
callcontractsqqq = self.OptionChainProvider.GetOptionContractList(self.symbolqqq, data.Time)
self.underlyingPriceqqq = self.Securities[self.symbolqqq].Price
# filter the out-of-money put options from the contract list which expire close to self.DTE num of days from now
qqqotm_calls = [i for i in callcontractsqqq if i.ID.OptionRight == OptionRight.Call and
i.ID.StrikePrice > self.CallOTM * self.underlyingPriceqqq and
(i.ID.Date - data.Time).days < self.CallDTE]
if len(qqqotm_calls) > 0:
# sort options by closest to self.DTE days from now and desired strike, and pick first
callqqqcontract = sorted(sorted(qqqotm_calls, key = lambda x: abs(x.ID.StrikePrice - self.underlyingPriceqqq)))[0]
if callqqqcontract not in self.callcontractsAddedqqq:
self.callcontractsAddedqqq.add(callqqqcontract)
# use AddOptionContract() to subscribe the data for specified contract
self.AddOptionContract(callqqqcontract, Resolution.Minute)
self.Log("QQQ Call contract {0}".format(callqqqcontract))
return callqqqcontract
else:
return str()
def OnOrderEvent(self, orderEvent):
self.Log(str(orderEvent))