Overall Statistics
Total Trades
32
Average Win
1.83%
Average Loss
-1.50%
Compounding Annual Return
1.274%
Drawdown
3.100%
Expectancy
0.111
Net Profit
2.387%
Sharpe Ratio
0.436
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
1.22
Alpha
-0.039
Beta
2.978
Annual Standard Deviation
0.025
Annual Variance
0.001
Information Ratio
-0.234
Tracking Error
0.025
Treynor Ratio
0.004
Total Fees
$32.00
# Your New Python File

#import requests
#from bs4 import BeautifulSoup
#import datetime 



#url_to_scrape = 'https://finance.yahoo.com/calendar/earnings?symbol=aapl'

#Load html's plain data into a variable
#plain_html_text = requests.get(url_to_scrape)

#soup = BeautifulSoup(plain_html_text.text, 'html.parser')

#table = soup.find('table', attrs={'class':'W(100%)'})

#table_rows = table.find_all('tr') 

#earning_dates = []
#for tr in table_rows:
#    td = tr.find_all('td')
#    row = [tr.text for tr in td]
#    if row == []:
#        pass
#    else:
#        earning_dates.append(row[2][:-9])
#
#earnings_dates_clean = []        
#for i in earning_dates:
#    earnings_dates_clean.append((i.replace("'","").replace(",","")))
    
    
#earn = 'Jan 31 2017'
#earn_2 = 'Oct 25 2016'

#earnings_dates_clean.append(earn)
#earnings_dates_clean.append(earn_2)

#def month_to_number(item):
    
#    if item[:3] == 'Apr':
#        item = item.replace('Apr','04')
        
#    if item[:3] == 'Oct':
#        item = item.replace('Oct','10')
        
#    if item[:3] == 'Nov':
#        item = item.replace('Nov','11')
        
#    if item[:3] == 'Jan':
#        item = item.replace('Jan','01')
        
#    if item[:3] == 'Feb':
#        item = item.replace('Feb','02')
        
#    if item[:3] == 'Mar':
#        item = item.replace('Mar','10')
        
#    if item[:3] == 'Jun':
#        item = item.replace('Jun','06')
        
#    if item[:3] == 'Jul':
#        item = item.replace('Jul','07')
        
#    if item[:3] == 'Aug':
#        item = item.replace('Aug','08')
        
#    if item[:3] == 'Sep':
#        item = item.replace('Sep','09')
    
#    if item[:3] == 'May':
#        item = item.replace('May','05')    
    
#    if item[:3] == 'Dec':
#        item = item.replace('Dec','11')
    
#    return item

# dates = [month_to_number(earnings_dates_clean[i]) for i in range(0,len(earnings_dates_clean))]



#dates_obj = [datetime.datetime.strptime(dates[i], "%m %d %Y") for i in range(0,len(dates))]
import numpy as np
from datetime import datetime
from datetime import timedelta 
import decimal 
import time 
import pandas as pd 
from pandas.tseries.offsets import BDay
from QuantConnect.Algorithm import *
from QuantConnect.Data import *
from QuantConnect.Securities.Option import OptionPriceModels

#from earnings_dates import dates 

class MultidimensionalHorizontalShield(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2017,1,10)  # Set Start Date
        self.SetEndDate(2018,11,20) # Set End Date
        self.SetCash(30000)  # Set Strategy Cash
        
        equity = self.AddEquity("AAPL", Resolution.Minute)
        equity.SetDataNormalizationMode(DataNormalizationMode.Raw)
        self.equity = equity.Symbol
        
        self.openTransaction = None
        
        self.closeByExpiration = True
        self.closeByProfitLoss = False
        
        self.maxOpenPositions = 5
        
        self.limitOrders = []
        
        # self.earningsDates is a list with  AAPL earnings dates. The code to get these dates is 
        # in the file earnings_dates
        # Is possible to do this approach with other stocks 
        
        self.earningsDates = [datetime(2020, 4, 28),datetime(2020, 1, 27),datetime(2019, 10, 30),
        datetime(2019, 7, 29), datetime(2019, 4, 30), datetime(2019, 1, 29),  datetime(2018, 11, 1),
        datetime(2018, 7, 31), datetime(2018, 5, 1), datetime(2018, 2, 1), datetime(2017, 11, 2),
        datetime(2017, 8, 1), datetime(2017, 5, 2), datetime(2017, 1, 31), datetime(2016, 10, 25)]

        option = self.AddOption("AAPL", Resolution.Minute)

        self.option_symbol = option.Symbol

        # The default setFilter is with monthly expiration. I comment this to use weekly expiration
       # option.SetFilter(-30, +30, timedelta(0), timedelta(30))
        
        # Select contract by weekly expiration
        option.SetFilter(lambda universe: universe.WeeklysOnly().Strikes(-10,+10).Expiration(timedelta(0), timedelta(30)))
        
        # This is the Pricing model to calculate the Delta for each contract
        option.PriceModel = OptionPriceModels.CrankNicolsonFD() # Pricing model to gets delta
        
        # Warm up period
        self.SetWarmUp(TimeSpan.FromDays(10)) 
        
        
        # 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(12,0),  self.ActivateTransaction)

        # Schudele function to run the conditions to sell. Will run all days at 9:45 am
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(9, 45),         
                 self.CloseTrade)
    
    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(self.IsWarmingUp):
            return

        # At 15:45 each day if self.openTransaction is True, look for options contracts to buy
        if self.Time.hour ==15 and self.Time.minute ==45 and self.openTransaction==True:
            options_info = self.select_options(slice)
            
            if options_info is not None:
                call , midPriceCall, put, midPricePut = options_info
            
                self.Debug('call contract is %s' % call)
                CallPrice = round(midPriceCall + 0.04,2)
                PutPrice = round(midPricePut + 0.04,2)
                # Buy Limit Orders for put and call 
                Callticket = self.LimitOrder(call,1,CallPrice)
                Putticket = self.LimitOrder(put,1,PutPrice)
                
                self.Debug('ticket is %s' % Callticket)
                
                self.limitOrders.append(Callticket)
                self.limitOrders.append(Putticket) # append the order ticket in the limitOrders list 
                self.openTransaction = False # Set self.openTransaction to false in order to stop buying in the current day
            else:
                self.Debug('Cannot processing options information')
              
             
    def ActivateTransaction(self):
        '''
        This function look if the current date is 2 business days before the earnings date. 
        If this is the case the flag self.openTransaction is equal to True and the algo is
        ready to make orders
        
        '''
        
       # if self.Time.date().isoweekday() ==1:
        self.Debug(self.Time)
        
        # If current date is two days before earnings date and if is weekday, set self.openTransaction
        # to True. This means to allow orders.
        
        two_days_ahead = (self.Time.date() + BDay(2)).date() 
        for date in self.earningsDates:
            if (two_days_ahead) == date.date() and self.Time.date().isoweekday() <=5:
                self.Debug('Two days before earning release %s %s ' % (two_days_ahead, date.date()))
                self.openTransaction = True
                
              
    def select_options(self,slice):
        
        if (slice.OptionChains.Count == 0):
            self.Debug('There are not options contracts in this chain at date %s' % self.Time.date())
            return 
        
        for kvp in slice.OptionChains:
            chain = kvp.Value

            # Select At the Money calls and puts
            # Make 2 lists called atm_calls and atm_puts
            
            atm_calls = [x for x in chain if x.Strike <= x.UnderlyingLastPrice and  x.Right == 0 and (x.Expiry.date() - self.Time.date()).days <= 20]
            atm_puts = [x for x in chain if x.Strike >= x.UnderlyingLastPrice and  x.Right == 1 and (x.Expiry.date() - self.Time.date()).days <= 20]
            
            # Debugging messages that are comment to prevent flooding the screen
           
            #self.Debug('Deltas for call contracts are %s' % deltasCall)
            #self.Debug('Deltas for put contracts are %s' % deltasPut)

            
            deltasCall = [x.Greeks.Delta for x in atm_calls]
            deltasPut = [x.Greeks.Delta for x in atm_puts]
            
            # Find the neares delta to 0.5 for both put and call
            nearCallDelta = (np.abs(np.array(deltasCall)-0.5)).argmin()
            nearPutDelta = (np.abs(np.array(deltasPut)+0.5)).argmin()
            
            # The method argmin() return the index with the delta nearest to 0.5. Then we use that 
            # index to lookup atm_calls and atm_puts
            call = atm_calls[nearCallDelta]
            put = atm_puts[nearPutDelta]

            self.Debug('Nearest call delta to 0.5 is %s' % call.Greeks.Delta)
            self.Debug('Nearest put delta to -0.5 is %s' % put.Greeks.Delta)
            
            callbid = call.BidPrice
            callask = call.AskPrice
            self.midPriceCall = round((callbid + callask)/2,2)
            
            putbid = put.BidPrice
            putask = put.AskPrice
            self.midPricePut = round((putbid + putask)/2,2)
            
            self.call = call.Symbol
            self.put = put.Symbol
            self.Debug('Underlying is %s' % self.Securities[self.equity].Price)
            self.Debug('Call contract is %s delta %s expiration %s strike %s' % (call.Symbol,call.Greeks.Delta,call.Expiry,call.Strike))
            self.Debug('Put contract is %s delta %s expiration %s strike %s' % (put.Symbol, put.Greeks.Delta,put.Expiry,put.Strike))
            
            return self.call, self.midPriceCall , self.put, self.midPricePut 
           
        
            
    def CloseTrade(self):
        
        # Get all orders by calling the limitOrders list which have all order tickets
        
        orders = self.limitOrders # self.Transactions.GetOrders()
        if len(orders) == 0:
            return
        
        # Use BDay(5) to substract 5 days to current date
        five_days_ahead = (self.Time.date() - BDay(5)).date()
        
        # The logic is the following: is self.closeByExpiration is set to True in Initialize,
        # loop over orders and if current date is 5 days after order date and order status is filled
        # sell the contract using order.Symbol by the QuantityFilled method.
        
        if self.closeByExpiration:
            for order in orders:
                if (five_days_ahead == order.Time.date()) and (order.Status == OrderStatus.Filled):
                    self.Sell(order.Symbol,order.QuantityFilled)
                    self.Debug('sell %s after 5 days of order date' % order.Symbol)
                    self.openTransaction = False
    
        option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
    
        option_invested_values = [x.Value for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
            
        
        # If self.closeByProfitLoss is set to True in Initialize, loop over contract in Portfolio and 
        # get the current profit percentage of each contrct. If this profit percentage is greater than 
        # 50% or lower than -11%, Liquidate(sell) the contract. 
        if self.closeByProfitLoss:
            
            for contract in option_invested:
                self.Debug(self.Securities[contract].Symbol)
                underlying = self.Securities[self.equity].Price
                quantity = self.Portfolio[contract].Quantity
                lastPrice = self.Securities[contract].Price
                profits = round(self.Portfolio[contract].UnrealizedProfit,0)
                profit_percentage = self.Portfolio[contract].UnrealizedProfitPercent
                if (profit_percentage > 0.5) or (profit_percentage < -0.11):
                    self.Liquidate(contract)
                    self.Debug('Sell contract %s with profit/loss of %s' % (contract, profit_percentage))
                
                #self.Debug("Contract: " + str(contract)  +   " - Underlying Price: " + str(underlying) +  " - Quantity: "  + str(quantity) + " - Last Price: " + str(lastPrice))
                #self.Debug('Unrealized profits and profit percentage at date %s for contract %s are  %s  and  %s' % (self.Time.date(), contract, profits,"{0:.0%}".format(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))