Overall Statistics
Total Trades
6
Average Win
0%
Average Loss
-0.08%
Compounding Annual Return
-1.458%
Drawdown
0.300%
Expectancy
-1
Net Profit
-0.257%
Sharpe Ratio
-2.765
Probabilistic Sharpe Ratio
1.205%
Loss Rate
100%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
-0.011
Beta
0.005
Annual Standard Deviation
0.004
Annual Variance
0
Information Ratio
1.034
Tracking Error
0.199
Treynor Ratio
-2.305
Total Fees
$3.00
earningDates = {'BABA': [['10-30-2020',
               '08-13-2020',
               '05-13-2020',
               '01-28-2020',
               '11-01-2019',
               '08-15-2019',
               '05-15-2019',
               '01-30-2019',
               '11-02-2018',
               '08-23-2018',
               '05-04-2018',
               '02-01-2018',
               '11-02-2017',
               '08-17-2017',
               '05-18-2017']],
             'BLMN': [['11-04-2020',
               '07-29-2020',
               '04-24-2020',
               '02-12-2020',
               '11-06-2019',
               '07-31-2019',
               '04-26-2019',
               '02-14-2019',
               '10-29-2018',
               '07-30-2018',
               '04-26-2018',
               '02-22-2018',
               '11-03-2017',
               '07-26-2017',
               '04-26-2017',
               '02-17-2017']],
             'BZUN': [['11-19-2020',
               '08-19-2020',
               '05-27-2020',
               '10-04-2020',
               '11-21-2019',
               '08-21-2019',
               '05-29-2019',
               '10-06-2019',
               '11-21-2018',
               '08-14-2018',
               '05-17-2018',
               '10-06-2018',
               '11-21-2017',
               '08-21-2017',
               '02-21-2017']],
             'EAT': [['10-28-2020',
               '08-11-2020',
               '04-28-2020',
               '01-29-2020',
               '10-30-2019',
               '08-13-2019',
               '04-30-2019',
               '01-29-2019',
               '10-30-2018',
               '08-14-2018',
               '05-01-2018',
               '01-30-2018',
               '11-01-2017',
               '08-10-2017',
               '04-25-2017']],
             'HD': [['11-17-2020',
               '08-18-2020',
               '05-19-2020',
               '02-25-2020',
               '11-19-2019',
               '08-20-2019',
               '05-21-2019',
               '02-26-2019',
               '11-13-2018',
               '08-14-2018',
               '05-15-2018',
               '02-20-2018',
               '11-14-2017',
               '08-15-2017',
               '05-16-2017',
               '02-21-2017']],
             'MCD': [['10-20-2020',
               '07-24-2020',
               '04-28-2020',
               '01-28-2020',
               '10-22-2019',
               '07-26-2019',
               '04-30-2019',
               '01-30-2019',
               '10-23-2018',
               '07-26-2018',
               '04-30-2018',
               '01-30-2018',
               '10-24-2017',
               '07-25-2017',
               '04-25-2017']],
             'PLAY': [['09-08-2020',
               '06-09-2020',
               '10-31-2020',
               '11-11-2019',
               '09-10-2019',
               '06-11-2019',
               '04-02-2019',
               '11-11-2018',
               '09-14-2018',
               '06-12-2018',
               '04-04-2018',
               '04-03-2018',
               '11-07-2017',
               '09-05-2017',
               '06-08-2017',
               '10-28-2017']],
             'PZZA': [['11-04-2020',
               '08-04-2020',
               '05-05-2020',
               '02-24-2020',
               '11-06-2019',
               '08-06-2019',
               '05-07-2019',
               '02-26-2019',
               '11-06-2018',
               '08-07-2018',
               '05-08-2018',
               '02-27-2018',
               '10-31-2017',
               '08-01-2017',
               '05-02-2017',
               '02-21-2017']],
             'SBUX': [['10-29-2020',
               '07-28-2020',
               '04-28-2020',
               '01-28-2020',
               '10-30-2019',
               '07-25-2019',
               '04-25-2019',
               '01-25-2019',
               '11-01-2018',
               '07-26-2018',
               '04-26-2018',
               '01-25-2018',
               '11-02-2017',
               '07-27-2017',
               '04-27-2017']],
             'ULTA': [['11-03-2020',
               '08-27-2020',
               '05-29-2020',
               '10-13-2020',
               '11-05-2019',
               '08-29-2019',
               '05-31-2019',
               '10-14-2019',
               '11-06-2018',
               '08-30-2018',
               '05-31-2018',
               '10-15-2018']],
             'VIPS': [['11-11-2020',
               '08-12-2020',
               '05-20-2020',
               '02-19-2020',
               '11-13-2019',
               '08-14-2019',
               '05-22-2019',
               '02-21-2019',
               '11-15-2018',
               '08-13-2018',
               '05-14-2018',
               '02-12-2018',
               '11-20-2017',
               '08-16-2017',
               '05-15-2017',
               '02-20-2017']],
             'WING': [['10-28-2020',
               '07-30-2020',
               '05-05-2020',
               '02-25-2020',
               '10-30-2019',
               '08-01-2019',
               '05-07-2019',
               '02-27-2019',
               '10-29-2018',
               '08-02-2018',
               '05-03-2018',
               '02-22-2018',
               '11-02-2017',
               '08-03-2017',
               '05-04-2017',
               '10-02-2017']],
             'YUM': [['10-28-2020',
               '07-30-2020',
               '04-29-2020',
               '02-05-2020',
               '10-30-2019',
               '08-01-2019',
               '05-01-2019',
               '02-07-2019',
               '10-31-2018',
               '08-02-2018',
               '05-02-2018',
               '02-08-2018',
               '11-02-2017',
               '08-03-2017',
               '05-03-2017',
               '02-09-2017']],
             'YUMC': [['10-27-2020',
               '07-28-2020',
               '04-27-2020',
               '01-29-2020',
               '10-29-2019',
               '07-30-2019',
               '04-29-2019',
               '01-31-2019',
               '10-30-2018',
               '08-01-2018',
               '05-01-2018',
               '02-05-2018',
               '04-05-2017',
               '02-07-2017']]}
import decimal
import datetime 
import pandas as pd 
import numpy as np


from EarningDates import (earningDates)

class EarningsOptionsTrade(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2018, 1, 22)  # Set Start Date
        self.SetEndDate(2018, 3, 26)
        self.SetCash(100000)  # Set Strategy Cash
        # self.AddEquity("SPY", Resolution.Minute)

       # self.UniverseSettings.Resolution = Resolution.Daily
        
    #    self.AddUniverse(self.MyCoarseFilterFunction,self.FineSelectionFunction)

        self.earningDates = earningDates
        
        self.symbols = []
        for ticker in self.earningDates.keys():
            #print(ticker)
            equity = self.AddEquity(ticker, Resolution.Minute)
            equity.SetDataNormalizationMode(DataNormalizationMode.Raw)
            option = self.AddOption(ticker,Resolution.Minute)
            self.symbols.append(option.Symbol)
            option.SetFilter(lambda universe: universe.WeeklysOnly().Strikes(-10, 10).Expiration(timedelta(0), timedelta(30)))

#            option.SetFilter(-10, +10, timedelta(0), timedelta(30))
         
        self.marketOrder = {}
        self.symbolToTrade = {}
        self.price = 0.05
    
      # One Schedule function that will run all days at 12:00 to look if the current time is 2 days before earnging date
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(9,31),  self.symbolDate)
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.Every(timedelta(minutes=60)), self.portfolio)

    def MyCoarseFilterFunction(self,coarce):
        
        # The first filter is to select stocks withs fundamental data, and price between the MyLeastPrice and MyMostPrice values
        filtered = [x for x in coarce if (x.HasFundamentalData) and  x.Volume > 1000000]
        
        first_filter_symbol = [x.Symbol for x in filtered]
        
        return first_filter_symbol
        
        
    def FineSelectionFunction(self,fine):
        '''
        This function filter stocks that belong to the Restaurant and RetailApparel and Specialty
        industry groups from Morning Stars. Secondly, the function select symbols with a market cap
        higher than 2 billions
        '''
        
        market_cap = {}

        #first_filter = [x for x in fine if ((x.AssetClassification.MorningstarIndustryGroupCode == 10216) or
        #(x.AssetClassification.MorningstarIndustryGroupCode == 10217))] 
        
        filteredFine = [x for x in fine if x.AssetClassification.MorningstarIndustryGroupCode == MorningstarIndustryGroupCode.Restaurants] 
      #  or  x.AssetClassification.MorningstarIndustryGroupCode == MorningstarIndustryGroupCode.RetailApparelAndSpecialty]
        
        # Calculate the market cap and add the "MarketCap" property to fine universe object
        
        for i in filteredFine:
            market_cap[i] = (i.EarningReports.BasicAverageShares.ThreeMonths *
                     i.EarningReports.BasicEPS.TwelveMonths *
                     i.ValuationRatios.PERatio)
        
      #  self.Debug(self.Time.date())
      #  self.Debug(self.Time.date() + timedelta(days=1))
        
        
        filteredFine = [x for x in filteredFine if (market_cap[x] > 1000000000) and 
        (self.Time.date() - timedelta(days=2) <= x.EarningReports.FileDate.date())  and x.EarningReports.FileDate != datetime.time()]
        
        earningDates = [x.EarningReports.FileDate for x in filteredFine]
        #self.Debug(earningDates)
        symbols = [x.Symbol for x in filteredFine]
        
     #   symbolMethods = [dir(x.ID)[22:] for x in symbols_]
     #   self.Debug(symbolMethods)
        
        classificationCode = [x.AssetClassification.MorningstarIndustryGroupCode for x in filteredFine]
        marketCap = [market_cap[x] for x in filteredFine]
        
        tickers = [x.ID.Symbol for x in symbols]
        
        earningDatesBySymbol = list(zip(tickers,earningDates,classificationCode,marketCap))
         
        #self.Debug(earningDatesBySymbol)
        #self.Debug([x.ID.Symbol for x in symbols])
        
        df = pd.DataFrame(earningDatesBySymbol, columns=['ticker', 'earningDates','MorningStarCode','MarketCap'])
        df.sort_values('earningDates',inplace=True,ascending=False)
        if len(df) > 1:
            self.Debug('Current date is %s' % self.Time.date())
            self.Debug(df)
            self.Log(df)
        
        return symbols
    
    def OnData(self, slice):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
            Arguments:
                data: Slice object keyed by symbol containing the stock data
        '''
        
#        if symbol not in self.symbolToTrade.keys():
#            self.symbolToTrade[symbol] = False
        for ticker in self.earningDates.keys():
            if ticker not in self.symbolToTrade.keys():
                self.symbolToTrade[ticker] = False
                
      #  self.Debug(self.Time)
       # if self.Time.hour ==15 and self.Time.minute ==45:
        for symbol in self.earningDates.keys():
            if self.symbolToTrade[symbol]: 
                self.Debug('For symbol {} current Date of {} is one day prior Earnings'.format(symbol,self.Time))
                self.selectOption(slice,symbol)
                self.symbolToTrade[symbol] = False
        
    
    def symbolDate(self):
        '''
        This function runs every day at market open, and check if current date is one day before
        any dates within the earningDates. If this is the case, the symbolToTrade dictionary
        for that symbol is equal to True, and this trigger a transaction for that symbol in
        the OnData function. 
        '''
        
        symbolDates = self.earningDates
        # self.Debug(symbolDates)
        for key, val in symbolDates.items():
            # self.Debug('Date is {}'.format(date))
            for date in val[0]:   
                earningDate = datetime.datetime.strptime(date,'%m-%d-%Y').date()
                if earningDate.year == self.Time.year and earningDate.month == self.Time.month:
                #    self.Debug('Earning Date for symbol {} is {}'.format(key,earningDate))
            
                #    self.Debug('Current Date {}'.format(self.Time.date()))
            
                    if (earningDate - timedelta(days=1)) == self.Time.date() :
                        print('One Day prior Earngings for symbol {} on Date {}'.format(key,self.Time.date()))
                        self.symbolToTrade[key] = True
                    else:
                        self.symbolToTrade[key] = False
                
                
     #   return self.symbolToTrade[symbol]
        
    def selectOption(self,slice,ticker):
        '''
        In this function we select the right contract for the symbol that is one day prior it 
        earning date and look for a contract with a price of 0.05 or most near to 0.05 over
        the option chain for that symbol. Then, send a market order to buy that contract.
        '''
        
        #for symbol in self.symbols:
     
     #       self.Debug('symbol To String {}'.format(symbol.ToString()))
     #       self.Debug('ticker {}'.format(ticker))
        for kvp in slice.OptionChains:        
            # self.Debug('option Chain for symbol {}'.format(ticker))
            if (slice.OptionChains.Count == 0):
                self.Debug('There are not options contracts in this chain at date %s' % self.Time)
                return 
        
                
                #    self.Debug('kvp.Key %s' % kvp.Key)
                #   self.Debug(dir(kvp.Key)[12:])
                #   self.Debug(dir(kvp.Key.ID)[22:])
                #    self.Debug('chain symbol %s' % kvp.Key.ID.Symbol)
                #    self.Debug('kvp.Key.Value {}'.format(kvp.Key.Value))
                #   self.Debug('kvp.key.ID.Date is %s' % kvp.Key.ID.Date)
            #self.Debug('symbol is {} ticker is {}'.format(symbol,ticker))
            self.Debug('ticker is %s' % ticker)
            self.Debug('kvp.Key.ID.Symbol %s' % kvp.Key.ID.Symbol )
            if ticker ==  kvp.Key.ID.Symbol: #in kvp.Key.ID.Symbol: #.ToString:
        
                chain = kvp.Value
                # Select At the Money calls and puts
                # Make 2 lists called atm_calls and atm_puts
                otm_puts = [x for x in chain if x.Strike <= x.UnderlyingLastPrice and  x.Right == 1 and (x.Expiry.date() - self.Time.date()).days <= 7]
                
                if len(otm_puts) == 0:
                    self.Debug('There are not options contract that meet conditions for symbol {}'.format(symbol))
                
                prices = [x.LastPrice for x in otm_puts]
                expiration = [x.Expiry for x in otm_puts]
                contracts = [x.Symbol.Value for x in otm_puts]
                self.Debug(contracts)
                self.Debug(expiration)
                self.Debug(prices)
            
                contractIndex = (np.abs(np.array(prices)- self.price)).argmin()
                contract = otm_puts[contractIndex]
                price = contract.LastPrice
                strike = contract.Strike
                expiration = contract.Expiry.date()
                underlying = contract.UnderlyingLastPrice
                symbol = contract.Symbol.Value
                self.contract = contract.Symbol
                 
                self.Debug('Contract selected is {} with price {} expiry {} strike {} underlying {}'.format(self.contract,price,expiration,strike,underlying))
                self.marketOrder[symbol] = self.Buy(self.contract,1)
                    
    def portfolio(self):
        
        option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
        
        for contract in option_invested:
            optionContract = self.Securities[contract].Symbol 
            #self.Debug(optionContract)
            #self.Debug(dir(optionContract))
            #self.Debug(contract.Underlying)
            #self.Debug(dir(contract)[12:])
            underlying = self.Securities[contract.Underlying].Price
            quantity = self.Portfolio[contract].Quantity
            
            lastPrice = self.Securities[contract].Price
            
            profits = round(self.Portfolio[contract].UnrealizedProfit,0)
            profit_percentage = self.Portfolio[contract].UnrealizedProfitPercent
            self.Debug('On Date {} Profit percentage and profit for contract {} are {} {}'.format(self.Time.date(),optionContract,profit_percentage,profits))
            if (profit_percentage > 5):
                self.Liquidate(contract)
                self.Debug('Sell contract {} with profit/loss of {} {}' % (contract, profits,profit_percentage))
                
    def OnOrderEvent(self, orderEvent):
        ''' Event when the order is filled. Debug log the order fill. :OrderEvent:'''

        self.Log(str(orderEvent))
        order = self.Transactions.GetOrderById(orderEvent.OrderId)
        
        self.Debug("{0}: {1}: {2}".format(self.Time, order.Type, orderEvent))