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))