I tried using the INTRADAY information but it does not go back far enough in time to be useful. So I wrapped up my options script by adding command line arguments. Now you can use it for your own stock symbol and create your own bins. For my next post I will move on to something else.
#!/usr/bin/env python3# -*- coding: utf-8 -*-"""Created on Sun Apr 7 07:11:46 2019@author: TomObtain the last 100 days of stock market data on the ticker symbol SPYand compute the change in price from close to close. Then eliminate thedays that are not options expiration days and build a 7 bin histogram"""importargparseimportpandasaspd# Series objectsimportnumpyasnp# Numerical recipesimportmatplotlib.pyplotasplt# To display plotsimportseabornassns# Sweet plotting packageimportalphavantageasav# My personal alphavantage libraryfromscipy.statsimportnorm# Just me drawing a superimposed normal curveparser=argparse.ArgumentParser()parser.add_argument("symbol")args=parser.parse_args()# A little warning here, this is just to prove that indeed the data is not # normal. I do not expect it to behave as a normal distribution over the period# in question. I only go back 100 days because that is what the compact size# gets you but also anything before that is unlikely to mean much.# Get your own API KEY from the websitefull=av.getSeries(args.symbol,'GETYOUROWN')# Convert the json data into a pandas dataframes=av.getDailyClosingPrices(full.json())# Compute the change in price from previous close to this close.day_to_day=pd.Series([s[n]-s[n-1]forninrange(1,len(s))],s.index[1:len(s)])
As part of learning how to code super slick sweet python and maintain a blog about it I had to come up with a way to make the display of the code blocks look professional. My first code block was ugly and I had to go through and insert <br> after every line. This was unacceptable. After much searching around the web I came across the obvious answer. There exists a package in Python called Pygments. Pygments comes with a script entitled “pygmentize.” This script can be used to convert your Python source into succulent looking html code viewable from any browser.
pygmentize -l python -f html -o filename.html filename.py
Pretty simple except for one thing. While it creates tags for all of the different types of text you might find in a Python script it does not create styles for those. So I made the styles myself. By opening the preferences in my Spyder editor and seeing the colored syntax highlighting I was able to make a one for one style for each syntax tag I found in the html file. So when you look at the code blocks you are in essence seeing exactly what I see in my editor.
Today I decided to create a library of “get” calls that would convert the json string from my previous post into individual Pandas Series. In each Series the index is the date and the data is the data specified in the name of the library function. I named the library alphavantage.py.
#!/usr/bin/env python3# -*- coding: utf-8 -*-"""Created on Mon Apr 8 06:23:22 2019@author: Tom"""importpandasaspd## getValueDict( j, which_value )# Arguments:# j - json strong returned from alphavantage query as a price history# which_value - specific value to return.# Return:# Pandas Series where the indexes are date strings and the elements# are floating point valuesdefgetValueDict(j,which_value):# Ignoring the Metadata contained in the json string for nowall_daily=j['Time Series (Daily)']# each element in the all_daily dictionary contains a key and a dictionary# values is the name of this particular dictionary. It has keys that # represent open, close, high, low and volumefordate,valuesinall_daily.items():# adding one element at a time to the final Seriesforkey,valueinvalues.items():# the argument passed in determines which Series to obtainifkey==which_value:# the values are type string, we need floatdata=float(value)# if the Series already exists then append this value to ittry:new_price=pd.Series(data,index=[date])prices=prices.append(new_price)# if the Series does not exist create itexceptNameError:prices=pd.Series(data,index=[date])returnpricesdefgetOpeningPrices(j):returngetValueDict(j,'1. open')defgetClosingPrices(j):returngetValueDict(j,'4. close')defgetHighPrices(j):returngetValueDict(j,'2. high')defgetLowPrices(j):returngetValueDict(j,'3. low')defgetVolume(j):returngetValueDict(j,'5. volume')
Rewriting the original script using the new library required scaling down to a Series from a DataFrame. I also decided to use the date as the index, which required some customization.
#!/usr/bin/env python3# -*- coding: utf-8 -*-"""Created on Sun Apr 7 07:11:46 2019@author: TomObtain the last 100 days of stock market data on the ticker symbol SPYand compute the change in price from close to close. Then eliminate thedays that are not options expiration days and build a 7 bin histogram"""importrequests# Necessary to get information from the urlimportpandasaspd# Sexy dataframe objectsimportnumpyasnp# Numerical recipesimportseabornassns# Sweet plotting packageimportalphavantageasav# My personal alphavantage libraryfromscipy.statsimportnorm# Just me drawing a superimposed normal curve# A little warning here, this is just to prove that indeed the data is not # normal. I do not expect it to behave as a normal distribution over the period# in question. I only go back 100 days because that is what the compact size# gets you but also anything before that is unlikely to mean much.# Get your own API KEY from the websiteAPI_KEY='GETYOUROWN'ticker='SPY'url='https://www.alphavantage.co/'query='query?function=TIME_SERIES_DAILY'symbol='symbol='+tickeroutputsize='outputsize=compact'# Compile the request string. I broke this apart because I believe in the# future I will want to use other queries.request=url+query+'&'+symbol+'&'+outputsize+'&'+'apikey='# Get 100 days of datafull=requests.get(request+API_KEY)# Convert the json data into a pandas dataframes=av.getClosingPrices(full.json())# Compute the change in price from previous close to this close.day_to_day=pd.Series([s[n]-s[n-1]forninrange(1,len(s))],s.index[1:len(s)])# Utility lists to make the logic below understandabledays_of_week=['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']options_exp=['Monday','Wednesday','Friday']# Determine list of days that are not options expiration days to delete from the mixforiinrange(0,len(day_to_day)-1):ifdays_of_week[pd.to_datetime(day_to_day.index[i]).dayofweek]notinoptions_exp:try:deles.append(day_to_day.index[i])exceptNameError:deles=day_to_day.index[i]# Obtain a Series of all changes that occurred from the previous close to the# end of the next options expirations dayoptions_day_to_day=day_to_day.drop(index=deles)# Get histogram databins=np.histogram(options_day_to_day,bins=7)sns.set(color_codes=True)# Plot the histogramax=sns.distplot(options_day_to_day,fit=norm,bins=7,kde=False,color="g")# Print out the number of elements in each binprint(bins[0])# Print out the edges of the binsprint(bins[1])
Again running the program using the latest information as of today we can obtain the histogram and the edges of the bins as before.
The two largest bins, bin 4 and bin 5 represent 44/60 or 73% of the data.
The left edge of bin 4 is -2.89 and the right edge of bin 5 is 3.08. The closing price today was 288.21. Let’s look at selling a call at 288.21+3.08 rounded to the nearest 50 cents which would be 291.50. Then we sell a put at 288.21 – 2.89 rounded to the nearest 50 cents which would be 285.50. Currently my Robinhood App is not reporting options prices for tomorrow so we will go with those reported on Yahoo Finance. Unfortunately they do not report prices broken down in 50 cent increments that far away from the current asking price of the underlying security. So we will use the put at 286 and the call at 291. This makes the sale price .08 and .02 for a total of .10. Since a contract is for a hundred shares the maximum we can make on this spread is $10.
Welcome to my python sandbox. Here I will attempt to build apps in python that I think of from time to time. I have been writing python code for about a year but I write software in so many languages that it is hard to become an expert in any single one. I find python to be so powerful that I want it to become my default language.
My first app is a stock market app. I searched the Internet for a decent place to start and ran aground several places, until I found this link:
I feel it necessary to credit him but I used the documentation and code from the website he references to create my own custom application.
I want to sell options on SPY for every options expiration date. The app I use for trading is Robinhood. Robinhood will not let me sell options on the day they expire. If I want to sell options to be held for the shortest term possible I must sell them no later than the day prior to options expirations day.
I wish to sell one out of the money put and one out of the money call. Maximum money is made in this spread by the price closing in between my two strike prices. To maximize my chances I want to know something about the expected change in the value of SPY over the time period extending from the close of the previous day to the close of the next day. There are many things that can happen along the way that are not considered in this script. My plan is that over time I will improve upon it.
The script layout is something like this:
Step 1. Obtain from the alphaadvantage.co website historical daily prices on SPY
Step 2. Compute the change in closing price from day to day. Notice that I am not computing the difference between open and close on a given day because of changes in price after hours.
Step 3. Delete the days that are not options expirations days. I have to do this after computing the changes because, while I care about the price at the end of the day prior to options expirations days, I do not care about the change in price over that day.
Step 4. For now compute a 7 bin histogram. Print out the values at the edges of the bins and plot a picture of the histogram. I am not going to automate further yet. You can visually inspect the data and pick a range where the odds of success are high.
To do this for yourself you will need to go to the alphaadvantage website and obtain an API key:
My philosophy is that if I put anything into a pandas dataframe I can use it in some way. So after I obtain the data I do just that. Then I operate on it and display it in seaborn.
#!="" usr="" bin="" env="" python3
@author: Tom
Obtain the last 100 days of stock market data on the ticker symbol SPY
and compute the change in price from close to close. Then eliminate the
days that are not options expiration days and build a 7 bin histogram """
import requests # Necessary to get information from the url
import pandas as pd # Sexy dataframe objects
import numpy as np # Numerical recipes
import seaborn as sns # Sweet plotting package
from scipy.stats import norm # Just me drawing a superimposed normal curve
# A little warning here, this is just to prove that indeed the data is not
# normal. I do not expect it to behave as a normal distribution over the period
# in question. I only go back 100 days because that is what the compact size
# gets you but also anything before that is unlikely to mean much.
# Get your own API KEY from the website
API_KEY = 'GETYOUROWN'
ticker = 'SPY'
url = 'https://www.alphavantage.co/'
query = 'query?function=TIME_SERIES_DAILY'
symbol = 'symbol=' + ticker
outputsize = 'outputsize=compact'
# Compile the request string. I broke this apart because I believe in the
# future I will want to use other queries.
request = url + query + '&' + symbol + '&' + outputsize + '&' + 'apikey='
# Get 100 days of data
full = requests.get(request + API_KEY)
# Convert the json data into a pandas dataframe
df = pd.DataFrame.from_dict(full.json()['Time Series (Daily)'], orient='columns')
# Compute the change in price from previous close to this close.
c = df.columns day_to_day = pd.Series( [float(df[c[i]][3]) - float(df[c[i-1]][3])
# Utility lists to make the logic below understandable
days_of_week = ['Monday','Tuesday','Wednesday','Thursday','Friday', 'Saturday','Sunday']
options_exp = ['Monday','Wednesday','Friday']
# Determine list of days that are not options expiration days to delete from the mix
for i in range(1,len(c)):
if days_of_week[pd.to_datetime( df.columns[i] ).dayofweek] not in options_exp:
try:
deles.append(I)
except NameError:
deles = [i]
# Obtain a Series of all changes that occurred from the previous close to the
# end of the next options expirations day
options_day_to_day = day_to_day.drop( index=deles )
# Get histogram data
bins = np.histogram( options_day_to_day, bins = 7 )
sns.set(color_codes=True)
# Plot the histogram
ax = sns.distplot( options_day_to_day, fit=norm, bins = 7, kde=False, color="g" )
# Print out the number of elements in each bin
print( bins[0] )
# Print out the edges of the bins
print( bins[1] )
When I ran the script on 4/7/2019 I got the following histogram:
The total number of data points is 60. So taking the two largest bins and adding their totals we see that 43/60 or 72% of all cases fall within this range.
Assuming this is acceptable we would then take the lower edge of bin 3 (-3.08) as our put sell value and the upper edge of bin 4 (2.89) as our call value. Then we would add the two values to the current price and round it off to the nearest 50 cents and sell our put and our call at that price. Assuming I could sell options today, the price of SPY is 288.89. So the put sale would be 288.89 – 3.08 or 285.81. Rounding to the nearest 50 cents would mean 286. The call sale would be 288.89+2.89 or 291.78. Rounding that to the nearest 50 cents would mean 292. The sale value of the put is .33. The sale value of the call is .12. Since the contracts are for a hundred shares, one put contract plus one call contract would net $45 maximum assuming both expired out of the money.
In a future post I hope to explore automating the bin choice and edge value and see if we can simulate what would have happened if we followed this strategy over the course of a hundred days.
Update: SPY closed yesterday at 288.80, which is between my $286 put and my $292 call, meaning
had I sold that spread I would have made about $45. There was an opportunity for the buyer to cash in and get a dollar back on both so worst case I made $43.