Vix Spx Seasonality By Month, Even Keel

While seeing some “sell in may” headlines a while ago, thought i’d pull up the monthly mean returns for spx and vix. I wanted to see them on an even keel, so that each month starts at 0%, to better gauge their monthly behaviour

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import quandl
import calendar

%matplotlib inline
sns.set(style="whitegrid")

Since yahoo data went dark, had to pull it in manually. There is a more intelligent solution posted in Trading With Python blog

spx = pd.read_csv("../Data/Spx.csv", index_col="Date")
spx.index = pd.to_datetime(spx.index, format="%Y-%m-%d")
spx = spx.apply(pd.to_numeric, errors="coerce")

vix = pd.read_csv("../Data/Vix.csv", index_col="Date")
vix.index = pd.to_datetime(vix.index, format="%Y-%m-%d")
vix = vix.apply(pd.to_numeric, errors="coerce")

Adding dates and stuff to dataframes

def DatesAndPct(df):
 df["day"] = df.index.dayofyear
 df["month"] = df.index.month
 df["year"] = df.index.year
 df["pct"] = np.log(df["Adj Close"]).diff()
 return df

spx = DatesAndPct(spx)
vix = DatesAndPct(vix)

Making a function for plots and plotting e‘m all at once

plt.figure(figsize=(16, 9))
label_props = dict(boxstyle="square", facecolor="w", edgecolor="none", alpha=0.69)

def plotMonths(df, color, line=1, m_names=True):
 df.index = pd.to_datetime(df.index, format="%Y-%m-%d")
 df.set_index(df["day"], inplace=True, drop=True)

 for i in range(1, 13):
 month_name = calendar.month_abbr[i] # Adding month name

 data = df[df["month"] == i]
 out = data["pct"].groupby(data.index).mean()
 out.iloc[0] = 0 # Setting returns to start from zero

 # Getting coordinates for month name labels
 x = out.index[-1]+2
 y = out.cumsum().iloc[-1]-0.01

 # Plotting
 plt.plot(out.cumsum(), linewidth=line, color=color, label="_nolabel_")
 if m_names == True:
 plt.text(x, y, month_name, size=13, bbox=label_props)

plotMonths(spx, "#555555", 2, m_names=False)
plotMonths(vix, "crimson", m_names=True)

plt.title("Vix and Spx mean returns from month start (log scale)") 
plt.plot([], [], label="Vix (since 1990)", color="crimson") # Adding custom legends
plt.plot([], [], label="Spx (since 1950)", color="#555555") # Adding custom legends
plt.axhline(linestyle="--", color="#555555", alpha=0.55)
plt.grid(alpha=0.21)
plt.legend(loc="upper left")
plt.xlabel("Day of year")
plt.ylabel("Log cumulative monthly return")

Unknown-18

While im at it, since Crude and Gold also have volatility indexes available, also pulled summaries for them. Crude vs Ovx and Gold vs Gvz

Unknown-19

Unknown-20

Thanks for your time

Advertisements

Vix Below Low Redux

Well, Its here, spot Vix close below 10. From what i read from the web, people are piling into short vol strategies on an escalating scale. I suspect unwinding of that trade will be rather brutal. I wish good luck to every short vol trader out there and dont forget to wear a helmet :)


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import quandl

sns.set(style="whitegrid")
%matplotlib inline

vix = quandl.get("YAHOO/INDEX_VIX", authtoken="YOUR_KEY")
vix.index = pd.to_datetime(vix.index)
vix.drop({"Open", "High", "Low", "Close", "Volume"}, inplace=True, axis=1)
vix.columns = ["close"]
vix["pct"] = np.log(vix["close"]).diff()

Lets look at all instances where Vix closed below 10 (if prev close was >= 10)


heat = vix[(vix["close"].shift(1) > 10) & (vix["close"] < 10)].transpose().iloc[:1]
heat.columns = heat.columns.map(lambda x: x.strftime("%Y-%m-%d"))

cmap = sns.dark_palette("red", as_cmap=True)

fig, ax = plt.subplots(1, figsize=(16, 9))
ax = sns.heatmap(heat, square=True, cmap=cmap, linewidths=1,
 annot=True, cbar=False, annot_kws={"size":42}, fmt="g")
ax.axes.get_yaxis().set_visible(False)
plt.title("All Vix closes < 10 since 1993 (if previous close was >= 10)")

Unknown-7

Closes below 10 are rare


ecd = np.arange(1, len(vix)+1) / len(vix)

plt.figure(figsize=(11, 11))
plt.plot(np.sort(vix["close"]), ecd, linestyle="none", marker=".", alpha=0.55, color="#555555")
plt.axvline(10, linestyle="--", color="crimson", label="Vix below 10 threshold")
plt.grid(alpha=0.21)
plt.title("Vix daily close values Ecdf")
plt.xlabel("Vix close")
plt.ylabel("Percentage of closes that are less than corresponding closes on x")
plt.legend(loc="center right")

Unknown-8

According to the few past instances, Vix should live’n up in the coming days


def getRets(df, days):
df = df.reset_index()
df_out = pd.DataFrame()
for index, row in df.iterrows():
if df["close"].iloc[index-1] >= 10 and df["close"].iloc[index] < 10:
ret = df["pct"].iloc[index:index+days]
#ret = np.log(ret).diff()
ret.iloc[:1] = 0
ret.reset_index(drop=True, inplace=True)
df_out[index] = ret

return df_out

vix_21rets = getRets(vix, 90+1)

plt.figure(figsize=(16, 9))
plt.plot(vix_21rets.cumsum(), color="#555555", alpha=0.34, label="_nolegend_")
plt.plot(vix_21rets.mean(axis=1).cumsum(), color="crimson", label="Mean rets")
plt.grid(alpha=0.21)
plt.title("Vix returns after closing below 10")
plt.ylabel("Vix % return")
plt.xlabel("Days after closing below 10")
plt.axhline(linestyle="--", linewidth=1, color="#333333")
plt.xticks(np.arange(0, 90+1, 5))
plt.legend(loc="upper left")

Unknown-9

Notebook:
https://github.com/Darellblg/voodoomarkets/blob/master/BlogVixBelowLow.ipynb

Thanks your time and feel free to leave a comment

Vix Below Low

Vix homing in on a close below 10. Has’nt happened during my time as a volatility speculator

All instances of Vix closing below 10 (if previous close was >= 10):
Unknown-6