Overall Statistics
Total Trades
448
Average Win
0.63%
Average Loss
-0.53%
Compounding Annual Return
13.558%
Drawdown
22.700%
Expectancy
0.113
Net Profit
12.378%
Sharpe Ratio
0.622
Probabilistic Sharpe Ratio
31.881%
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
1.18
Alpha
0
Beta
0
Annual Standard Deviation
0.175
Annual Variance
0.031
Information Ratio
0.622
Tracking Error
0.175
Treynor Ratio
0
Total Fees
$418.00
Estimated Strategy Capacity
$69000.00
Lowest Capacity Asset
TSLA 31TOO71Y13QLI|TSLA UNU3P8Y3WFAD
from System.Drawing import Color
from AlgorithmImports import *

class Benchmark:
    
    def __init__(self, algo, underlying, shares, indicators):
        self.algo = algo
        self.underlying = underlying
        # Variable to hold the last calculated benchmark value
        self.benchmarkCash = None
        self.benchmarkShares = shares
        self.indicators = indicators

        self.tradingChart = Chart('Trade Plot')
        # On the Trade Plotter Chart we want 3 series: trades and price:
        self.tradingChart.AddSeries(Series('Call', SeriesType.Scatter, '$', Color.Blue, ScatterMarkerSymbol.Circle))
        self.tradingChart.AddSeries(Series('Put', SeriesType.Scatter, '$', Color.Red, ScatterMarkerSymbol.Triangle))
        self.tradingChart.AddSeries(Series('Price', SeriesType.Scatter, '$', Color.Green))
    
    def PrintBenchmark(self):
        self.__PrintBuyHold()
        self.__PrintTrades()
        self.__PrintCash()
        self.__PrintIndicators()
    
    def PrintTrade(self, option, option_type):
        ''' Prints the price of the option on our trade chart. '''
        self.algo.Plot('Trade Plot', 'Call' if option_type == OptionRight.Call else 'Put', option.ID.StrikePrice)
    
    def __PrintIndicators(self):
        ''' Prints the indicators array values to the Trade Plot chart.  '''
        for indicator in self.indicators:
            if indicator.IsReady:
                self.algo.Plot('Trade Plot', indicator.Name, indicator.Current.Value)
    
    def __PrintCash(self):
        ''' Prints the cash in the portfolio in a separate chart. '''
        self.algo.Plot('Cash', 'Options gain', self.algo.Portfolio.Cash)        

    def __PrintTrades(self):
        ''' Prints the underlying price on the trades chart. '''
        self.algo.Plot('Trade Plot', 'Price', self.__UnderlyingPrice())
    
    def __PrintBuyHold(self):
        ''' Simulate buy and hold the shares. We use the same number of shares as the backtest.
            In this situation is 100 shares + the cash of the portfolio.'''
        if not self.benchmarkCash:
            self.benchmarkCash = self.algo.Portfolio.TotalPortfolioValue - self.benchmarkShares * self.__UnderlyingPrice()
            
        self.algo.Plot("Strategy Equity", "Buy & Hold", self.benchmarkCash + self.benchmarkShares * self.__UnderlyingPrice())
        
    def __UnderlyingPrice(self):
        return self.algo.Securities[self.underlying].Close
#region imports
from AlgorithmImports import *
#endregion
from datetime import timedelta

class OptionsAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2014, 11, 1)
        self.SetEndDate(2017, 11, 1)
        self.SetCash(20000)
        self.syl = 'IBM'
        equity = self.AddEquity(self.syl, Resolution.Minute)
        equity.SetDataNormalizationMode(DataNormalizationMode.Raw)
        self.macd = self.MACD(self.syl, 12, 26, 9, MovingAverageType.Exponential, Resolution.Daily)
        self.underlyingsymbol = equity.Symbol
        # use the underlying equity as the benchmark
        self.SetBenchmark(equity.Symbol)
     
        
    def OnData(self,slice):
        
        if self.macd.IsReady:
            if self.Portfolio[self.syl].Quantity == 0 and self.macd.Current.Value > self.macd.Signal.Current.Value:  
                self.Buy(self.syl,100)
            
            # # <1> if there is a MACD short signal, liquidate the stock            
            # elif self.Portfolio[self.syl].Quantity > 0 and self.macd.Current.Value < self.macd.Signal.Current.Value:
            #     self.Liquidate()
            
           
            # # <2> if today's close < lowest close of last 30 days, liquidate the stock   
            # history = self.History([self.syl], 30, Resolution.Daily).loc[self.syl]['close']
            # self.Plot('Stock Plot','stop loss frontier', min(history))
            # if self.Portfolio[self.syl].Quantity > 0:        
            #     if self.Securities[self.syl].Price < min(history):
            #         self.Liquidate()
            
            
            # <3> if there is a MACD short signal, trade the options     
            elif self.Portfolio[self.syl].Quantity > 0 and self.macd.Current.Value < self.macd.Signal.Current.Value:
                try:               
                    if self.Portfolio[self.syl].Invested and not self.Portfolio[self.contract].Invested \
                      and self.Time.hour != 0 and self.Time.minute == 1:
                        self.SellCall() 
                except:
                    if self.Portfolio[self.syl].Invested and self.Time.hour != 0 and self.Time.minute == 1:
                        self.SellCall() 
                        
                
    def BuyPut(self):
        contracts = self.OptionChainProvider.GetOptionContractList(self.underlyingsymbol, self.Time.date())
        if len(contracts) == 0: return
        filtered_contracts = self.InitialFilter(self.underlyingsymbol, contracts, -3, 3, 0, 30)
        put = [x for x in filtered_contracts if x.ID.OptionRight == 1] 
        # sorted the contracts according to their expiration dates and choose the ATM options
        contracts = sorted(sorted(put, key = lambda x: abs(self.Securities[self.syl].Price - x.ID.StrikePrice)), 
                                        key = lambda x: x.ID.Date, reverse=True)
        self.contract = contracts[0]
        self.AddOptionContract(self.contract, Resolution.Minute)
        self.Buy(self.contract, 1)
        
    def SellCall(self):
        contracts = self.OptionChainProvider.GetOptionContractList(self.underlyingsymbol, self.Time.date())
        if len(contracts) == 0: return
        filtered_contracts = self.InitialFilter(self.underlyingsymbol, contracts, -3, 3, 0, 30)
        put = [x for x in filtered_contracts if x.ID.OptionRight == 0] 
        # sorted the contracts according to their expiration dates and choose the ATM options
        contracts = sorted(sorted(put, key = lambda x: abs(self.Securities[self.syl].Price - x.ID.StrikePrice)), 
                                        key = lambda x: x.ID.Date, reverse=True)
        self.contract = contracts[0]
        self.AddOptionContract(self.contract, Resolution.Minute)
        self.Sell(self.contract, 1)

    def InitialFilter(self, underlyingsymbol, symbol_list, min_strike_rank, max_strike_rank, min_expiry, max_expiry):
        
        ''' This method is an initial filter of option contracts
            according to the range of strike price and the expiration date '''
            
        if len(symbol_list) == 0 : return
        # fitler the contracts based on the expiry range
        contract_list = [i for i in symbol_list if min_expiry < (i.ID.Date.date() - self.Time.date()).days < max_expiry]
        # find the strike price of ATM option
        atm_strike = sorted(contract_list,
                            key = lambda x: abs(x.ID.StrikePrice - self.Securities[underlyingsymbol].Price))[0].ID.StrikePrice
        strike_list = sorted(set([i.ID.StrikePrice for i in contract_list]))
        # find the index of ATM strike in the sorted strike list
        atm_strike_rank = strike_list.index(atm_strike)
        try: 
            min_strike = strike_list[atm_strike_rank + min_strike_rank]
            max_strike = strike_list[atm_strike_rank + max_strike_rank]
        except:
            min_strike = strike_list[0]
            max_strike = strike_list[-1]
           
        filtered_contracts = [i for i in contract_list if i.ID.StrikePrice >= min_strike and i.ID.StrikePrice <= max_strike]

        return filtered_contracts
# Covered call options Algo. Trying to generate income by selling calls against existing portfolio positions. Some rules:
# 
# - Sell calls against a defined size of the position. This is important for TSLA if you don't want to default to all shares.
# - Set a defined expiration date rollover. 2 weeks seems good but when a rollover happens we should consider between 2 weeks - 3 months.
# - Always rollover for a credit. This is a must.
# - If 2 or more contracts expire on the same day and they are ITM consider selling 1 contract ATM for credit and release some shares.
# - When doing a rollover and the contract is ITM always pick a strike above to get closer to the equity price.
# - When doing a rollover and the contract is OTM always pick a strike below to get closer to the equity price.
# - With a rollover down and out consider adding another contract in order to create the loop above.
# - Roll up and out 2 ITM -> 1 ATM contracts | Roll down and out 1 OTM -> 2 ATM contracts !! <- not sure about this
# - When rolling the contract do that based on a risk reward algo/function. https://www.thebalance.com/risk-to-reward-ratio-1031350

# NOTES:
# - Rolling up and out works best when we are in a down movement (price is down).!!!
# - Rolling down and out works best when we are in an up movement (price is up).!!!
# - When we have earnings the contracts don't change value that much. Consider leaving them until after?!
# - Open the first covered call on an up day.
# - Roll if no EXT value
# - 7 - 60 DTE range max
# - Think of Risk Management. Position sizing. Making sure it does not get out of hand. 

# TODOs:
# Test rolling when:
# - 3 more days remaining
# - price starts going over strike
# - price is down
# - there is low ext value
# - always for a credit
# - small debit to catch price
# - 2 calls for 1 sold PUT?!
# - ITM and credit 2 - 3 weeks out! roll there! -> 2 calls at one point same expiration for 1 PUT switch.
# - a given profit % is hit then consider closing the call and roll at a later date!


# IDEA!! I THINK I KNOW WHAT RANDY MEANT when he said switch from 2 to 1 contract. I think he ment 2 ITM calls to 1 SOLD PUT?!?! TEST!


# EXT value
# For example, if a call option has a strike price of $20, and the underlying stock 
# is trading at $22, that option has $2 of intrinsic value. The actual option may 
# trade at $2.50, so the extra $0.50 is extrinsic value.
# sprice = $109 // stock price = $109
# oprice = $21.35 // option price = $21.35
# strike = $90 // option strike = $90
# oprice - abs(strike - sprice) // ext value = $2.35


# RESOURCES:
# - https://www.quantconnect.com/tutorials/introduction-to-options/quantconnect-options-api

from datetime import timedelta
from optionsfinder import OptionsFinder
from portfoliohandler import PortfolioHandler
from benchmark import Benchmark
from AlgorithmImports import *

class CoveredCallOptionsAlgorithm(QCAlgorithm):

    # def Initialize(self): is run before back testing/live trading commences. In it we set important variables, modules, add data and warm up indicators and so forth.
    # We can also use Scheduled Events in Initialize() to trigger code to run at specific times of the day.
    def Initialize(self):
        self.SetStartDate(2021, 1, 1)
        self.SetEndDate(2021, 12, 1)
        self.SetCash(200000)
        # We also add data within the Initialize() method.
        # The general template to do so is very simple.
        # For equities we use self.AddEquity() which returns a security object that we can assign internally for future use, for example with the TSLA:
        # Resolution is the time period of a data bar, and the default options for Equities are:
        #   Resolution.Tick
        #   Resolution.Second
        #   Resolution.Minute
        #   Resolution.Hour
        #   Resolution.Daily
        symbol = self.GetParameter("equity")
        self.rangeStart = int(self.GetParameter("rangeStart"))
        self.rangeStop = int(self.GetParameter("rangeStop"))
        self.minCredit = float(self.GetParameter("minCredit"))
        equity = self.AddEquity(symbol, Resolution.Daily)
        
        self.underlying = equity.Symbol
        self.sliceData = None
        # option = self.AddOption(self.underlying, Resolution.Minute)
        equity.SetDataNormalizationMode(DataNormalizationMode.Raw) # Not sure what this does yet
        # To fix the missing price history for option contracts https://www.quantconnect.com/forum/discussion/8779/security-doesn-039-t-have-a-bar-of-data-trade-error-options-trading/p1
        self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x))) 
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage) # not sure what this does
        # self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x))) # this was added to fix the warmup error. Not sure if needed.
        
        # We would also set up indicators that we wanted to use in the main algorithm in Initialize(), such as RSI or MACD.
        # For example for an RSI indicator, we would set key variables such as the look back period and overbought and oversold levels, 
        # remembering to set them to self so that they are referenceable throughout different methods in the algorithm.
        #
        # self.RSI = self.RSI("TSLA", 14)
        self.ema = self.EMA(self.underlying, 50)
        self.emaSlow = self.EMA(self.underlying, 100)
        
        # Here is an example of a scheduled event that is set firstly to run every day, and then more specifically every 10 minutes within every day- 
        # setting off the method/function self.ExampleFunc, which could be absolutely anything we want it to be:
        #
        # self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.Every(timedelta(minutes=10)), self.ExampleFunc)
        
        # You can also set a “warm up” period that allows components such as technical indictaors, historical data arrays and so forth to prime or 
        # populate prior to the main algorithm going live. No trades will be executed during the warm up period.
        #
        # self.SetWarmUp(35) # Warm up 35 bars for all subscribed data.
        
        # Create an instance of OptionsFinder to handle the finding of option contracts
        self.optionHandler = OptionsFinder(self, self.underlying, self.rangeStart, self.rangeStop, -80, 80)
        self.benchmark = Benchmark(self, self.underlying, 100, [self.ema, self.emaSlow])
        self.portfolioHandler = PortfolioHandler(self)
        
    # def OnData(self, data): is activated each time new data is passed to your algorithm, so this can be hourly, daily or weekly etc. 
    # depending on the data resolution you request in Initialization(self).
    # You might fire trading logic in here, or update indicators or dataframes.
    # Remember you can also activate functions on regular time intervals/certain events not related to a data update with Scheduled Events.
    def OnData(self, slice):
        self.sliceData = slice
        # self.BuyUnderlying()
        
        self.RollForCredit()

        # self.CashSecuredPuts()

        # self.CoveredCalls()
        
        self.benchmark.PrintBenchmark()
        # self.portfolioHandler.PrintPortfolio()

    def OnOrderEvent(self, orderEvent):
        if orderEvent.IsAssignment:
            order = self.Transactions.GetOrderById(orderEvent.OrderId)
            if order.Type == OrderType.OptionExercise:
                pass # do stuff

    def CoveredCalls(self):
        # consider integrating the sold_puts into the strategy and see how it fares. Consider always keeping a ratio of 2x calls to 1x puts so margin is not affected.
        covered_calls = self.portfolioHandler.UnderlyingSoldOptions(self.underlying, OptionRight.Call)
        if len(covered_calls) == 0:
            self.__InitialOptionSell(OptionRight.Call)
        else:
           # Do we have existing covered calls? Then we should check and roll them if needed.
            self.__RollOption(covered_calls, OptionRight.Call)

    def CashSecuredPuts(self):
        sold_puts = self.portfolioHandler.UnderlyingSoldOptions(self.underlying, OptionRight.Put)
        if len(sold_puts) > 0:
            self.__RollOption(sold_puts, OptionRight.Put)
        #if len(sold_puts) == 0:
        #    self.__InitialOptionSell(OptionRight.Put)
        #else:
            # Do we have existing covered calls? Then we should check and roll them if needed.
        #    self.__RollOption(sold_puts, OptionRight.Put)
    
    def BuyUnderlying(self):
        if not self.Portfolio[self.underlying].Invested:
            self.MarketOrder(self.underlying, 100) # buy 100 shares of underlying stocks
    
    # Sell an option
    def __InitialOptionSell(self, option_type):
        # If we added the Option contract data and we have not Invested then we should short the option selected.
        # if self.Portfolio[self.underlying].Invested:
        option = self.optionHandler.AddContract(self.Securities[self.underlying].Price, option_type) # Add the option contract (subscribe the contract data)
        if self.Securities.ContainsKey(option) and not self.Portfolio[option].Invested:
            self.benchmark.PrintTrade(option, option_type)
            self.Sell(option, 1) # short the option
    
    def RollForCredit(self):
        covered_calls = self.portfolioHandler.UnderlyingSoldOptions(self.underlying, OptionRight.Call, 10)
        sold_puts = self.portfolioHandler.UnderlyingSoldOptions(self.underlying, OptionRight.Put, 10)

        if len(covered_calls) == 0 and len(sold_puts) == 0:
            if self.portfolioHandler.lastTradeBid > 0:
                optionContracts = self.optionHandler.ContractsWithCredit(self.portfolioHandler.lastTradeBid)
                self.__SellOptions(optionContracts)
            else:
                self.__InitialOptionSell(OptionRight.Call)
        else:
            roll            = False
            soldPut         = None
            soldCall        = None
            # TODO: consider switching this to groupby(contracts, expiry..)..
            groupedByExpiry = dict()

            for option in covered_calls:
                groupedByExpiry.setdefault(int(option.Security.Expiry.timestamp()), []).append(option)

            for option in sold_puts:
                groupedByExpiry.setdefault(int(option.Security.Expiry.timestamp()), []).append(option)
            
            if len(groupedByExpiry) == 0:
                return

            firstExpiry = list(sorted(groupedByExpiry))[0]
            security = groupedByExpiry[firstExpiry][0].Security
            expiresIn = self.ExpiresIn(security)
            
            # if expiresIn <= 2:
            #     roll = True

            soldCalls = [x for x in groupedByExpiry[firstExpiry] if x.Security.Right == OptionRight.Call]
            if len(soldCalls) > 0:
                soldCall = soldCalls[0]
            
            soldPuts = [x for x in groupedByExpiry[firstExpiry] if x.Security.Right == OptionRight.Put]
            if len(soldPuts) > 0:
                soldPut = soldPuts[0]
            
            optionsBid = sum([x.Security.BidPrice for x in groupedByExpiry[firstExpiry]])
            self.portfolioHandler.lastTradeBid = optionsBid
            
            if soldCall is None and soldPut is not None:
                profit = soldPut.UnrealizedProfitPercent * 100
                self.Buy(soldPut.Symbol, soldPut.Quantity)    
            elif soldCall is not None and soldPut is None:
                profit = soldCall.UnrealizedProfitPercent * 100
                self.Buy(soldCall.Symbol, soldCall.Quantity)
            elif soldCall is not None and soldPut is not None:
                profit = (soldCall.UnrealizedProfitPercent + soldPut.UnrealizedProfitPercent) / 2 * 100
                self.Buy(soldCall.Symbol, soldCall.Quantity)
                self.Buy(soldPut.Symbol, soldPut.Quantity)

            if profit >= 75:
                self.__InitialOptionSell(OptionRight.Call)
            elif expiresIn <= 4:
                optionContracts = self.optionHandler.ContractsWithCredit(optionsBid)
                self.__SellOptions(optionContracts)
            
    def __SellOptions(self, contracts):
        # clean contracts and remove None and blanks.
        contracts = [i for i in contracts if i]
        if len(contracts) == 0: return

        for option in contracts:
            if self.Securities.ContainsKey(option) and not self.Portfolio[option].Invested:
                self.benchmark.PrintTrade(option, option.ID.OptionRight)
                self.Sell(option, 1) # sell the new contract

    # Take the option that is closest to expiration and roll it forward.
    def __RollOption(self, contracts, option_type = OptionRight.Call):
        for option in contracts:
            security = option.Security
            strike = security.Symbol.ID.StrikePrice
            stockPrice = self.Securities[option.Symbol.ID.Symbol].Price
            profit = option.UnrealizedProfitPercent * 100
            expiresIn = self.ExpiresIn(security)
            roll = False

            # if profit < -100 or ((stockPrice * 1.2 > strike) and profit > -25) or ((stockPrice * 1.1 > strike) and profit > -50):
            #     roll = True
            
            #if security.Right == OptionRight.Call:
            #    if stockPrice > strike and profit > -25:
            #        roll = True
            #    
            #    if stockPrice > strike and profit > -50:
            #        roll = True
            #elif security.Right == OptionRight.Put:
            #    if stockPrice < strike and profit > -25:
            #        roll = True
            #    
            #    if stockPrice < strike and profit > -50:
            #        roll = True

            if profit < -100:
                roll = True

            if profit > 80:
                roll = True

            if expiresIn <= 2:
                roll = True
                
            if roll:
                # TODO:
                # - 3 more days remaining
                # - price starts going over strike
                # - price is down
                # - there is low ext value
                # - always for a credit
                # - small debit to catch price
                # - 2 calls for 1 sold PUT?!
                # - ITM and credit 2 - 3 weeks out! roll there! -> 2 calls at one point same expiration for 1 PUT switch.
                # - a given profit % is hit then consider closing the call and roll at a later date!
                
                # optionStrike = self.RollUpPercent(strike, stockPrice, 1.25) if option_type == OptionRight.Call else self.RollDownPercent(strike, stockPrice, 1.25)
                # optionStrike = self.RollToEMA(stockPrice)
                optionBid = security.BidPrice
                
                # optionContract = self.optionHandler.AddContract(optionStrike, None, option_type) # Add the call option contract (subscribe the contract data)
                optionContract = self.optionHandler.AddContract(None, optionBid, option_type) # Add the call option contract (subscribe the contract data)
                
                if optionContract is None: return
                
                # if the above returns NULL then the self.Sell method will sell the option ATM (current strike price)
                self.Buy(option.Symbol, option.Quantity)
            
                if self.Securities.ContainsKey(optionContract) and not self.Portfolio[optionContract].Invested:
                    self.benchmark.PrintTrade(optionContract, option_type)
                    self.Sell(optionContract, 1) # short the new call option
    
    # Method that returns a boolean if the security expires in the given days
    # @param security [Security] the option contract
    def ExpiresIn(self, security):
        return (security.Expiry.date() - self.Time.date()).days
    
    def RollToEMA(self, stock_price):
        return self.ema.Current.Value * 1.07
        
    
    # Roll to middle strike between old strike and current price.
    def RollUpMiddle(self, strike, stock_price):
        if strike < stock_price:
            new_strike = strike + ((stock_price - strike) / 2)
        else:
            new_strike = strike / 1.05
        return new_strike
        
    # Roll to a strike a certain percentage in price up
    def RollUpPercent(self, strike, stock_price, percent):
        if strike < stock_price:
            new_strike = strike * percent
        else:
            new_strike = strike / 1.05
        return new_strike
        
    def RollDownPercent(self, strike, stock_price, percent):
        if strike < stock_price:
            new_strike = strike / 1.05
        else:
            new_strike = strike * percent
        return new_strike
    
    # class used to improve readability of the coarse selection function
    class SelectionData:
        def __init__(self, period):
            self.Ema = ExponentialMovingAverage(period)
    
        @property
        def EmaValue(self):
            return float(self.Ema.Current.Value)
        
        def Update(self, time, value):
            return self.Ema.Update(time, value)
from AlgorithmImports import *
from itertools import groupby

class OptionsFinder:
    
    def __init__(self, algo, underlying, rangeStart, rangeStop, minStrike, maxStrike):
        self.algo = algo
        self.underlying = underlying
        self.rangeStart = rangeStart
        self.rangeStop = rangeStop
        self.minStrike = minStrike
        self.maxStrike = maxStrike
        # Right now the below lines could be conditional as we don't need them if we are not using
        # the contractBid method where we look for credit and not really the strike.
        self.underlyingOption = self.algo.AddOption(self.underlying, Resolution.Daily)
        self.underlyingOption.SetFilter(
            lambda u: u.IncludeWeeklys()
                        .Strikes(self.minStrike, self.maxStrike)
                        .Expiration(timedelta(self.rangeStart), timedelta(self.rangeStop))
        )

    # Just adds the options data.
    def AddContract(self, strike = None, bid = None, optionType = OptionRight.Call):
        ''' We are adding the option contract data to the algo '''
        # Replace this line below with different methods that filter the contracts in a more special way.
        if strike is not None:
            return self.__GetContractStrike(strike, optionType)
            
        if bid is not None:
            return self.__GetContractBid(bid, optionType)

    def ContractsWithCredit(self, bid):
        # Check if there is options data for this symbol.
        # Important that when we do GetValue we use the underlyingOption.Symbol because the 
        # key returned by the equity is `TSLA` and the one from option is `?TSLA`
        chain = self.algo.sliceData.OptionChains.GetValue(self.underlyingOption.Symbol)
        
        if chain is None:
            return []

        callContract        = None
        putContract         = None

        # Don't look at contracts that are closer than the defined rangeStart.
        contracts = [x for x in chain if self.__ExpiresIn(x) >= self.rangeStart and self.__ExpiresIn(x) <= self.rangeStop]

        # Limit the Strikes to be between -5% and +5% of the UnderlyingPrice.
        contracts = [x for x in contracts if x.UnderlyingLastPrice / 1.05 <= x.Strike <= x.UnderlyingLastPrice * 1.05 ]
        
        # Make sure we have the contracts sorted by Strike and Expiry
        contracts = sorted(contracts, key = lambda x: (x.Expiry, x.Strike))

        # Iterate over the contracts that are grouped per day.
        for expiry, group in groupby(contracts, lambda x: x.Expiry):
            group = list(group)
            # sort group by type
            group = sorted(group, key = lambda x: x.Right)

            if callContract is not None and putContract is not None:
                return [callContract.Symbol, putContract.Symbol]

            # Reset the contracts after each expiration day. We want to force the options to Expire on the same date.
            callContract = None
            putContract  = None
            
            for right, rightGroup in groupby(group, lambda x: x.Right):
                rightGroup = list(rightGroup)
                # sort the options by credit
                creditOptions = sorted(rightGroup, key = lambda x: x.BidPrice, reverse = True)
                if right == OptionRight.Call:
                    # find any contract that has a BidPrice > bid
                    creditCalls = [x for x in creditOptions if x.BidPrice > bid]
                    creditCalls = sorted(creditCalls, key = lambda x: x.BidPrice)
                    # if 
                    #   we do have a call that can replace the one we are buying for a credit then return that.
                    # else
                    #   sort the calls by BidPrice and pick the highest bid contract
                    if len(creditCalls) > 0:
                        return [creditCalls[0].Symbol]
                    else:
                        # TODO: here instead of picking the bigest credit contract (this would be the one with the smallest Strike)
                        #       we should consider picking one that is closer to strike as possible.
                        callContract = creditOptions[0]
                
                # Only look for PUT contracts if we can't find a call contract with enough credit to replace the bid value.
                if right == OptionRight.Put and callContract is not None:
                    # find any contract that has a BidPrice > bid - callContract.BidPrice
                    creditPuts = [x for x in creditOptions if x.BidPrice > (bid - callContract.BidPrice)]
                    creditPuts = sorted(creditPuts, key = lambda x: x.BidPrice)
                    if len(creditPuts) > 0:
                        putContract = creditPuts[0]

        return []

    # Method that returns a boolean if the security expires in the given days
    # @param security [Security] the option contract
    def __ExpiresIn(self, security):
        return (security.Expiry.date() - self.algo.Time.date()).days

    def __GetContractBid(self, bid, optionType):
        # TODO: this method can be changed to return PUTs and CALLs if the strike selected is not
        #       sensible enough. Like don't allow selection of contracts furthen than 15% of the stock price.
        #       This should force the selection of puts to offset the calls or the other way around until we can get into
        #       a situation where just calls are present.

        # Check if there is options data for this symbol.
        # Important that when we do GetValue we use the underlyingOption.Symbol because the 
        # key returned by the equity is `TSLA` and the one from option is `?TSLA`
        chain = self.algo.sliceData.OptionChains.GetValue(self.underlyingOption.Symbol)
        
        if chain is None:
            return None

        # Don't look at contracts that are closer than the defined rangeStart.
        contracts = [x for x in chain if (x.Expiry.date() - self.algo.Time.date()).days > self.rangeStart]

        contracts = sorted(
            contracts,
            key = lambda x: abs(x.UnderlyingLastPrice - x.Strike)
        ) 

        # Get the contracts that have more credit than the one we are buying. The bid here might be too small
        # so we should consider sorting by:
        # - strike > stock price
        # - closest expiration
        # - closest strike to stockPrice with credit! SORT THIS BELOW?!?
        # TODO: right now we have a problem as it keeps going down in strike when it's loosing. Maybe send in the profit and if it's over -100 -200% switch to PUT?!
        # TODO: consider using a trend indicator like EMA to determine a contract type switch CALL -> PUT etc.
        contracts = [x for x in contracts if x.BidPrice >= bid * 1.20]

        # If possible only trade the same contract type. This should have a limit of how low it should go before it switches to a put.
        # Maybe by doing the limit we can remove the trend indicator. 

        if optionType == OptionRight.Call:
            typeContracts = [x for x in contracts if x.Right == optionType and (x.UnderlyingLastPrice / 1.05) < x.Strike]
            if len(typeContracts) > 0:
                contracts = typeContracts

        if optionType == OptionRight.Put:
            typeContracts = [x for x in contracts if x.Right == optionType and (x.UnderlyingLastPrice * 1.05) < x.Strike]
            if len(typeContracts) > 0:
                contracts = typeContracts

        # Grab us the contract nearest expiry
        contracts = sorted(contracts, key = lambda x: x.Expiry)
        
        if len(contracts) == 0:
            return None
        return contracts[0].Symbol
                        
    def __GetContractStrike(self, strike, optionType):
        filtered_contracts = self.__DateOptionFilter()
        
        if len(filtered_contracts) == 0: return str()
        else:
            contracts = self.__FindOptionsStrike(filtered_contracts, strike, optionType)
            
            if len(contracts):
                self.algo.AddOptionContract(contracts[0], Resolution.Daily)
                return contracts[0]
            else:
                return str()
    
    def __FindOptionsBid(self, contracts, bid, optionType = OptionRight.Call):
        ''' We are filtering based on bid price. '''
        # select only the specified options types
        contracts = [x for x in contracts if x.ID.OptionRight == optionType]
        # TODO!! THis does not work. Most probably we have to do self.algo.AddOptionContract on all contracts above and then 
        #.       use that to filter by BidPrice as the value does not seem to exist. :(
        # pick a contract that is above the bid price provided.
        contracts = [x for x in contracts if x.ID.BidPrice >= bid]
        
        contracts = sorted(
            contracts,
            key = lambda x: abs(bid - x.ID.BidPrice)
        ) 
        
        # prefer shorter expirations
        contracts = sorted(
            contracts,
            key = lambda x: x.ID.Date,
            reverse=False
        )
        
        # sorted the contracts according to their expiration dates and choose the ATM options
        return contracts 

 
    # Returns options that are ATM.
    def __FindOptionsStrike(self, contracts, strike, optionType = OptionRight.Call):
        ''' We are filtering based on strike rank. '''
        
        # select only the specified options types
        contracts = [x for x in contracts if x.ID.OptionRight == optionType]
        
        # never want to go beow strike
        contracts = [x for x in contracts if x.ID.StrikePrice >= strike]

        contracts = sorted(
            contracts,
            key = lambda x: abs(strike - x.ID.StrikePrice)
        ) 
        
        # prefer shorter expirations
        contracts = sorted(
            contracts,
            key = lambda x: x.ID.Date,
            reverse=False
        )
        
        # sorted the contracts according to their expiration dates and choose the ATM options
        return contracts 
        
        
        # find the strike price of ATM option
        # atm_strike = sorted(contracts,
        #                     key = lambda x: abs(x.ID.StrikePrice - strike))
        # atm_strike = atm_strike[0].ID.StrikePrice
        # strike_list = sorted(set([i.ID.StrikePrice for i in contracts]))
        # # find the index of ATM strike in the sorted strike list
        # atm_strike_rank = strike_list.index(atm_strike)
        # try: 
        #     strikes = strike_list[(atm_strike_rank + self.minStrike):(atm_strike_rank + self.maxStrike)]
        # except:
        #     strikes = strike_list
        # filtered_contracts = [i for i in contracts if i.ID.StrikePrice in strikes]
        
        # # select only call options
        # call = [x for x in filtered_contracts if x.ID.OptionRight == optionType] 
        # # sorted the contracts according to their expiration dates and choose the ATM options
        # return sorted(sorted(call, key = lambda x: abs(strike - x.ID.StrikePrice)), 
        #                     key = lambda x: x.ID.Date, reverse=True)
        
 
    def __DateOptionFilter(self):
        ''' We are filtering the options based on the expiration date. It does return weekly contracts as well. '''
        contracts = self.algo.OptionChainProvider.GetOptionContractList(self.underlying, self.algo.Time.date())
        if len(contracts) == 0 : return []
        # fitler the contracts based on the expiry range
        contract_list = [i for i in contracts if self.rangeStart < (i.ID.Date.date() - self.algo.Time.date()).days < self.rangeStop]
        
        return contract_list
        
    # Returns options that can be rolled for a credit and higher strike
    def __CreditUpOptions(self, contracts, existing_contract):
        # TODO: this!
        return []
from AlgorithmImports import *

# Class that handles portfolio data. We have here any method that would search the portfolio for any of the contracts we need.
class PortfolioHandler:
    
    def __init__(self, algo):
        self.algo = algo
        self.lastTradeBid = 0
        
    # Returns all the covered calls of the specified underlying
    # @param underlying [String]
    # @param optionType [OptionRight.Call | OptionRight.Put] 
    # @param maxDays [Integer] number of days in the future that the contracts are filtered by
    def UnderlyingSoldOptions(self, underlying, optionType, maxDays = 60):
        contracts = []
        for option in self.algo.Portfolio.Values:
            security = option.Security
            if (option.Type == SecurityType.Option and 
                str(security.Underlying) == underlying and 
                security.Right == optionType and
                option.Quantity < 0 and 
                (security.Expiry.date() - self.algo.Time.date()).days < maxDays):
                contracts.append(option)
        return contracts
    
    def PrintPortfolio(self):
        # self.Debug("Securities:")
        # self.Securities 
        #   contains Securities that you subscribe to but it does not mean that you are invested.
        #   calling self.AddOptionContract will add the option to self.Securities                
        for kvp in self.Securities:
            symbol = kvp.Key # key of the array
            security = kvp.Value # value of the array (these are not attributes)
            holdings = security.Holdings
            self.Debug(str(security.Symbol))
            # self.Debug(str(security.Underlying))
            # self.Debug(str(security.Holdings))
            
        # self.Debug("Portfolio:")
        # self.Portfolio
        #   contains the Security objects that you are invested in.
        for kvp in self.Portfolio:
            symbol = kvp.Key
            holding = kvp.Value 
            holdings = holding.Quantity
            # self.Debug(str(holding.Holdings))
#region imports
from AlgorithmImports import *
#endregion
class UnderlyingEma(object):
    def __init__(self, underlying):
        self.underlying = underlying # is not used
        self.tolerance = 1.01 # probably not needed
        self.fast = ExponentialMovingAverage(100)
        self.slow = ExponentialMovingAverage(300)
        self.is_uptrend = False
        self.scale = 0

    def update(self, time, value):
        if self.fast.Update(time, value) and self.slow.Update(time, value):
            fast = self.fast.Current.Value
            slow = self.slow.Current.Value
            self.is_uptrend = fast > slow * self.tolerance

        if self.is_uptrend:
            self.scale = (fast - slow) / ((fast + slow) / 2.0)