#!/usr/bin/env python # coding: utf-8 # # Drawdowns: Magnitudes and Frequencies # # This will be an analysis of the market drawdowns from an empirical perspective, using daily data for the past 24 years. # # I will be focusing on the daily returns of the S&P500 index from January 1993 onwards. # # The focus on drawdowns is because its is a far more visceral measure of risk than the standard deviation, it's the negative outcome that the investor fundamentally cares about. # # In[67]: get_ipython().run_line_magic('matplotlib', 'inline') import pandas as pd import numpy as np import matplotlib.pyplot as plt import math # In[ ]: df = pd.read_csv("SP500.csv") # Taking a peek, we find the following # In[3]: df.head() # Get log returns. # In[7]: df["log_returns"] = np.log(df["Adj Close"]).diff() df["log_returns"][0] = 0 # In[8]: df.head() # Now we can define drawdown # In[9]: def drawdown(returns): out = [] cum_returns = [0] for i in range(len(returns)): cum_returns.append(cum_returns[-1] + returns[i]) out.append(max(cum_returns)-cum_returns[-1]) return out # In[45]: def max_drawdown(drawdowns): out = [] for i in range(len(drawdowns)+1): out.append(max(drawdowns[:i+1])) return out[:-1] # Lets try it out with some fake data # In[87]: print(max_drawdown(drawdown([0.05, 0.001, -0.10, 0.002]))) print(drawdown([0.05, 0.001, -0.10, 0.002])) # We can get now get drawdown magnitudes from our returns data # In[48]: df["drawdown"] = drawdown(df["log_returns"]) df["maxdrawdown"] = max_drawdown(df["drawdown"]) # In[49]: df.head() # Lets have a look at our data # In[92]: fig, ax = plt.subplots(3) ax[0].hist(df["log_returns"], bins="auto", normed=True) ax[0].set_title("Returns") ax[1].hist(df["drawdown"], bins="auto", normed=True) ax[1].set_title("Drawdown") ax[2].hist(df["maxdrawdown"], bins="auto", normed=True) ax[2].set_title("Maximum Drawdown") plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=1.0) plt.show() # In[55]: def print_moments(data): print("Mean: {}".format(data.mean())) print("Variance: {}".format(data.var())) print("Skewness: {}".format(data.skew())) print("Kurtosis: {}".format(data.kurt())) # In[56]: print_moments(df["log_returns"]) # In[57]: print_moments(df["drawdown"]) # In[58]: print_moments(df["maxdrawdown"]) # Now plotting as timeseries, several variables of interest # In[64]: df.plot(x='Date', y='maxdrawdown') # In[65]: df.plot(x='Date', y='log_returns') # Here we can see that large drawdown spikes happen in 2001 and 2008, corresponding to the tech and the housing bubble # In[66]: df.plot(x='Date', y='drawdown') # Return multiple over the period # In[70]: math.pow(1 + df["log_returns"].mean(), len(df["log_returns"])) # In[74]: df["Adj Close"].iloc[-1]/df["Adj Close"].iloc[0] # Omniscient Return over the period # In[86]: df["abs"] = df["log_returns"].map(math.fabs) print(df["abs"].mean()) math.pow(1+ df["abs"].mean(), len(df["abs"])) # A buy and hold position, yields a 9.5X multiple over the 25 year period. # # While an omniscient market timer who is long before every up day # and short before ever down day yields a # 9.7*10^20 multiple over the 25 year period. # # # In[ ]: