Overall Statistics
Total Trades
Average Win
Average Loss
Compounding Annual Return
Net Profit
Sharpe Ratio
Loss Rate
Win Rate
Profit-Loss Ratio
Annual Standard Deviation
Annual Variance
Information Ratio
Tracking Error
Treynor Ratio
Total Fees
import numpy as np
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 QuantConnect.Algorithm.Framework.Alphas import *
import json

#from earnings_dates import dates 

class  MultidimensionalHorizontalShield(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2018,7,20)  # Set Start Date
        self.SetEndDate(2018,7,30) # Set End Date
        self.SetCash(30000)  # Set Strategy Cash
        # 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 
        # Set InteractiveBrokers Brokerage model
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
        self.tickers = ['AAPL','NFLX','AMZN','GOOG']
        self.earningDates = {}
        #Select multiple stocks from API
        if self.LiveMode:
            url = 'https://cloud.iexapis.com/stable/stock/market/batch?symbols=aapl,nflx,amzn,goog&types=estimates&token=pk_7fed8485fc2e4539aa40b42e33d2e9eb'
            apiResponse = self.Download(url)
            apiData = json.loads(apiResponse)
            # In this case self.earningDates is a dictionary that would have stock symbols in keys
            # and dates in values.
            self.earningDates = {}
            for map in apiData:
                dates = apiData[map]['estimates']['estimates'][0]['reportDate']
                dates_object = datetime.datetime.strptime(dates, "%Y-%m-%d")
                self.earningDates[map] = dates_object
        url = 'https://cloud.iexapis.com/stable/stock/market/batch?symbols=aapl,nflx,amzn,goog&types=earnings&last=10&token=pk_7fed8485fc2e4539aa40b42e33d2e9eb'
        apiResponse = self.Download(url)
        apiData = json.loads(apiResponse)
        for map in apiData:
            for i in range(0,len(apiData)):
                dates = apiData[map]['earnings']['earnings'][i]['EPSReportDate']
                dates_object = datetime.datetime.strptime(dates, "%Y-%m-%d")
                self.earningDates.setdefault(map, [])
        for ticker in self.tickers:
            equity = self.AddEquity(ticker, Resolution.Minute)
        self.equity = equity.Symbol
        self.openTransaction = None
        self.symbolToTrade = None
        self.closeByExpiration = True
        self.closeByProfitLoss = False
        self.maxOpenPositions = 5
        self.limitOrders = []
        self.symbols = []
        # Add the options 
        for ticker in self.tickers:
            option = self.AddOption(ticker,Resolution.Minute)
            option.SetFilter(lambda universe: universe.WeeklysOnly().Strikes(-10,+10).Expiration(timedelta(0), timedelta(30)))

            #option.SetFilter(-10, +10, timedelta(0), timedelta(30))
        #option = self.AddOption(self.symbol, Resolution.Minute)

        #self.option_symbol = option.Symbol

        # The default setFilter is with monthly expiration. I comment this to use weekly expiration
        #option.SetFilter(-10, +10, 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
        # One Schedule function that will run all days at 12:00 to look if the current time is 2 days before earnging date
        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),         
    def OnData(self, slice):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
                data: Slice object keyed by symbol containing the stock data


        # 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(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
                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:
        # Loop over the self.earningDates dictionary and if the earning dates of a stock is two days
        # ahead the current date, allow to trade setting the self.openTransaction to True and grab
        # that stock in self.symbolToTrade
        # 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 stock, dates in self.earningDates.items():
            for date in dates:
                if (two_days_ahead) == date.date() and self.Time.date().isoweekday() <=5:
                    self.Debug('Two days before earning release for stock %s %s %s ' % (two_days_ahead, date.date(),stock))
                    self.openTransaction = True
                    self.symbolToTrade = stock
    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())
        for key in self.earningDates:
            if key == self.symbolToTrade:
                self.Debug('key is %s' % key)
                self.Debug('symbolToTrade is %s' % self.symbolToTrade)
                for kvp in slice.OptionChains:
                    self.Debug('kvpkey %s' % kvp.Key)
                    if kvp.Key == key:
                        chain = kvp.Value       # Retrieve option chain only for the stock within 2 days for earnings report
                        self.Debug('symbol to look option chain %s' % key)
                        # 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 >= 7]
                        atm_puts = [x for x in chain if x.Strike >= x.UnderlyingLastPrice and  x.Right == 1 and (x.Expiry.date() - self.Time.date()).days >= 7]
                        # Debugging messages that are comment to prevent flooding the screen
                        deltasCall = [x.Greeks.Delta for x in atm_calls]
                        deltasPut = [x.Greeks.Delta for x in atm_puts]
                        #self.Debug('Deltas for call contracts are %s' % deltasCall)
                        #self.Debug('Deltas for put contracts are %s' % deltasPut)
                        # 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:
        # Use BDay(2) to substract 2 days to current date
        # five_days_ahead = (self.Time.date() - BDay(2)).date()
        # Use BDay(2) to substract 1 days to current date
        five_days_ahead = (self.Time.date() - BDay(1)).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.Debug('sell %s after 5 days of order date' % order.Symbol)
                    self.openTransaction = False
                    self.limitOrders.remove(order)  # remove the order from self.limitOrders after sold
        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:
                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.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:'''

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