| 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))