The stock market is the apex of legalized gambling. There are countless get rich quick schemes, penny stocks, Ponzi schemes, as well as legitimate and stable ways to make money. The market is studied by some of the brightest minds and any arbitrary, risk free profit making scheme (called, cunningly enough, an arbitrage) is supposed to be non-existent. Despite Dire Straits' infinite wisdom, there's not supposed to be money for nothing
But can we play on people's irrationality? Are people superstitious enough that the market behaves differently on unlucky days?
I thought this would be easy enough to google, but frankly, I just didn't trust their math. So here is mine, as well as some simple code I used to pull it together.
I choose the S&P 500 as a representation of the stock market. There are a few advantages:
The dataset has the opening and closing prices by day. If you don't care about the coding, you can skip to the graphs. The steps I had to follow, and are mirrored in the comments in the code (bottom of page) are:
The red line represents the average daily change, which is essentially 0%. The two dotted lines are the 95% confidence intervals, so any change within this range would be considered "normal" (assuming normal distribution yadda yadda yadda).
When we look by the day of month, we can see there is more volatility, but still pretty close to 0%. The 13th is between the blue lines, and we can see it is line with the other days.
Look at this table that averages the change for every day and day of week combination:
Looking back up to the initial graphs, this -0.01% for Friday the 13th falls easily within our confidence intervals of roughly ±2.5%.
Examining our table, all of the days fall within our confidence interval as well. From this I would conclude an investing strategy built around the time of month and day of week will not work consistently. Maybe this is a duh statement. But it also shows, at least in this singular regard, people are behaving rationally and not selling all their stocks on unlucky days. That is slightly comforting to me.
###Friday the 13th
#import libraries
import matplotlib.pyplot as plt
from matplotlib.pyplot import figure
import math
import pandas as pd
import os
import numpy as np
from pandas.plotting import table
#Import data
os.chdir('/Users/erikolson/Desktop/Writing Projects/Data Sci Cat Blog/Culture Society/')
dfm = pd.read_csv("Yahoo_S&P.csv")
#parse date to day of week
dfm['date_time'] = pd.to_datetime(dfm['Date'])
dfm['weekday'] = dfm['date_time'].dt.day_name()
dfm['year'] = dfm['date_time'].dt.year
dfm['day'] = dfm['date_time'].dt.day
#filter the data
dfm = dfm[(dfm.year >=2000)]
#calculate change
dfm['change_perc']=(dfm['Open']-dfm['Close'])/dfm['Open']
dfm['change_mm_perc']=(dfm['High']-dfm['Low'])/dfm['Open']
#used later for naming columns and selecting columns
cols = ['Open','Close','High','Low','change_perc','change_mm_perc']
stdcols =[]
for x in cols: stdcols=stdcols+[x+'_std']
#groupby and get mean and standard deviation
dfmWday = dfm.groupby(['weekday'])[cols].mean()
dfmWdayS = dfm.groupby(['weekday'])[cols].agg(np.std)
dfmWdayS.columns = stdcols
dfmWday = pd.concat([dfmWday, dfmWdayS[['change_perc_std','change_mm_perc_std']]], axis=1, sort=False)
del dfmWdayS
#calculate confidence intervals
dfmWday['change_perc_CI_L'] = dfmWday['change_perc']-1.96*dfmWday['change_perc_std']
dfmWday['change_perc_CI_H'] = dfmWday['change_perc']+1.96*dfmWday['change_perc_std']
dfmWdaySum = dfmWday[['change_perc','change_perc_CI_L','change_perc_CI_H']]
dfmWdaySum.style.format({'change_perc': '{:,.2%}'.format, 'change_perc_CI_L': '{:,.2%}'.format,
'change_perc_CI_H': '{:,.2%}'.format})
dfmWdaySum = dfmWdaySum.reindex(index = ['Monday','Tuesday','Wednesday','Thursday','Friday'])
#groupby and get mean and standard deviation, but now for day of month
dfmDay = dfm.groupby(['day'])[cols].mean()
dfmDay['day_change_perc'] = (dfmDay['Open']-dfmDay['Close'])/dfmDay['Open']
dfmDayS = dfm.groupby(['day'])[cols].agg(np.std)
dfmDayS.columns = stdcols
dfmDay = pd.concat([dfmDay, dfmDayS[['change_perc_std','change_mm_perc_std']]], axis=1, sort=False)
del dfmDayS
dfmDay['change_perc_CI_L'] = dfmDay['change_perc']-1.96*dfmDay['change_perc_std']
dfmDay['change_perc_CI_H'] = dfmDay['change_perc']+1.96*dfmDay['change_perc_std']
dfmDaySum = dfmDay[['change_perc','change_perc_CI_L','change_perc_CI_H']]
dfmDaySum.style.format({'change_perc': '{:,.2%}'.format, 'change_perc_CI_L': '{:,.2%}'.format,
'change_perc_CI_H': '{:,.2%}'.format})
#make table that displays day and month
WdaybyDay = pd.pivot_table(dfm, values='change_perc', index=['day'],columns=['weekday'], aggfunc=np.mean)
WdaybyDay = WdaybyDay[['Monday','Tuesday','Wednesday','Thursday','Friday']]
WdaybyDay.style.format({'Monday': '{:,.2%}'.format, 'Tuesday': '{:,.2%}'.format, 'Wednesday': '{:,.2%}'.format,
'Thursday': '{:,.2%}'.format,'Friday': '{:,.2%}'.format})
os.chdir('/Users/erikolson/Desktop/Writing Projects/Data Sci Cat Blog/Blog Graphs/')
#WdaybyDay.to_csv('Friday13WDay.csv')
#graph by day of week
fig= plt.figure(figsize=(12, 6))
plt.ylim(-.03, .03)
plt.xlim(0, 4)
plt.text(2, .03, "S&P Daily Change 2000-2019", fontsize=18, ha="center")
plt.xticks(fontsize=14)
# Remove the plot frame lines. Ensure that the axis ticks only show up on the bottom and left of the plot.
ax = plt.subplot(111)
fig.patch.set_facecolor((239/255,238/255,236/255))
ax.set_facecolor((239/255,238/255,236/255))
ax.spines["top"].set_visible(False)
ax.spines["bottom"].set_visible(True)
ax.spines["right"].set_visible(False)
ax.spines["left"].set_visible(True)
ax.yaxis.set_major_formatter(FuncFormatter(lambda y, _: '{:,.0%}'.format(y)))
plt.xlabel('Day', fontsize=18)
plt.ylabel('Average Percentage Change', fontsize=18)
ax.get_xaxis().tick_bottom()
ax.get_yaxis().tick_left()
plt.tick_params(axis="both",which="both",bottom=False,top=False,labelbottom=True,left=False,right=False,\
labelleft=True,labelsize = 12)
plt.plot(dfmWdaySum['change_perc'],"-",color="red")#,label="Change")
plt.plot(dfmWdaySum['change_perc_CI_L'],":",color="red")#,label="Change")
plt.plot(dfmWdaySum['change_perc_CI_H'],":",color="red")#,label="Change")
plt.plot((0, 32),(-0.02,-0.02), "--", lw=0.5, color="black", alpha=0.3)
plt.plot((0, 32),(-0.01,-0.01), "--", lw=0.5, color="black", alpha=0.3)
plt.plot((0, 32),(0,0), "--", lw=0.5, color="black", alpha=0.3)
plt.plot((0, 32),(0.01,0.01), "--", lw=0.5, color="black", alpha=0.3)
plt.plot((0, 32),(0.02,0.02), "--", lw=0.5, color="black", alpha=0.3)
os.chdir('/Users/erikolson/Desktop/Writing Projects/Data Sci Cat Blog/Blog Graphs/')
plt.savefig('Friday13WDay.png',bbox_inches = 'tight',facecolor=(239/255,238/255,236/255))
#graph by day lof month
fig= plt.figure(figsize=(12, 6))
plt.ylim(-.03, .03)
plt.xlim(1, 31)
plt.text(16, .03, "S&P Daily Change 2000-2019", fontsize=18, ha="center")
# Make sure your axis ticks are large enough to be easily read. From 0 to 91, count by 10
plt.xticks(fontsize=14)
# Remove the plot frame lines. Ensure that the axis ticks only show up on the bottom and left of the plot.
ax = plt.subplot(111)
fig.patch.set_facecolor((239/255,238/255,236/255))
ax.set_facecolor((239/255,238/255,236/255))
ax.spines["top"].set_visible(False)
ax.spines["bottom"].set_visible(True)
ax.spines["right"].set_visible(False)
ax.spines["left"].set_visible(True)
ax.yaxis.set_major_formatter(FuncFormatter(lambda y, _: '{:,.0%}'.format(y)))
plt.xlabel('Day', fontsize=18)
plt.ylabel('Average Percentage Change', fontsize=18)
ax.get_xaxis().tick_bottom()
ax.get_yaxis().tick_left()
plt.tick_params(axis="both",which="both",bottom=False,top=False,labelbottom=True,left=False,right=False,\
labelleft=True,labelsize = 12)
plt.plot(dfmDaySum['change_perc'],"-",color="red")#,label="Change")
plt.plot(dfmDaySum['change_perc_CI_L'],":",color="red")#,label="Change")
plt.plot(dfmDaySum['change_perc_CI_H'],":",color="red")#,label="Change")
plt.plot((0, 32),(-0.02,-0.02), "--", lw=0.5, color="black", alpha=0.3)
plt.plot((0, 32),(-0.01,-0.01), "--", lw=0.5, color="black", alpha=0.3)
plt.plot((0, 32),(0,0), "--", lw=0.5, color="black", alpha=0.3)
plt.plot((0, 32),(0.01,0.01), "--", lw=0.5, color="black", alpha=0.3)
plt.plot((0, 32),(0.02,0.02), "--", lw=0.5, color="black", alpha=0.3)
plt.plot((12.5,12.5),(-0.03,0.03), ":", lw=1, color="blue", alpha=0.3)
plt.plot((13.5,13.5),(-0.03,0.03), ":", lw=1, color="blue", alpha=0.3)
os.chdir('/Users/erikolson/Desktop/Writing Projects/Data Sci Cat Blog/Blog Graphs/')
plt.savefig('Friday13Day.png',bbox_inches = 'tight',facecolor=(239/255,238/255,236/255))