Overall Statistics |
Total Trades 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0 Tracking Error 0 Treynor Ratio 0 Total Fees $0.00 |
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) self.Debug(apiData) # 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, []) self.earningDates[map].append(dates_object) self.Debug(self.earningDates) for ticker in self.tickers: equity = self.AddEquity(ticker, Resolution.Minute) equity.SetDataNormalizationMode(DataNormalizationMode.Raw) 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) 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)) #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 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) #self.Debug(options_info) 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') return 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) # 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 break 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 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: return # 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.Sell(order.Symbol,order.QuantityFilled) 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: 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))