Python rolling Sharpe ratio with Pandas or NumPy
Asked Answered
A

2

6

I am trying to generate a plot of the 6-month rolling Sharpe ratio using Python with Pandas/NumPy.

My input data is below:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style("whitegrid")

# Generate sample data
d = pd.date_range(start='1/1/2008', end='12/1/2015')
df = pd.DataFrame(d, columns=['Date'])
df['returns'] = np.random.rand(d.size, 1)
df = df.set_index('Date')
print(df.head(20))

             returns
Date                
2008-01-01  0.232794
2008-01-02  0.957157
2008-01-03  0.079939
2008-01-04  0.772999
2008-01-05  0.708377
2008-01-06  0.579662
2008-01-07  0.998632
2008-01-08  0.432605
2008-01-09  0.499041
2008-01-10  0.693420
2008-01-11  0.330222
2008-01-12  0.109280
2008-01-13  0.776309
2008-01-14  0.079325
2008-01-15  0.559206
2008-01-16  0.748133
2008-01-17  0.747319
2008-01-18  0.936322
2008-01-19  0.211246
2008-01-20  0.755340

What I want

The type of plot I am trying to produce is this or the first plot from here (see below). enter image description here

My attempt

Here is the equation I am using:

def my_rolling_sharpe(y):
    return np.sqrt(126) * (y.mean() / y.std()) # 21 days per month X 6 months = 126

# Calculate rolling Sharpe ratio
df['rs'] = calc_sharpe_ratio(df['returns'])

fig, ax = plt.subplots(figsize=(10, 3))
df['rs'].plot(style='-', lw=3, color='indianred', label='Sharpe')\
        .axhline(y = 0, color = "black", lw = 3)

plt.ylabel('Sharpe ratio')
plt.legend(loc='best')
plt.title('Rolling Sharpe ratio (6-month)')
fig.tight_layout()
plt.show()

enter image description here

The problem is that I am getting a horizontal line since my function is giving a single value for the Sharpe ratio. This value is the same for all the Dates. In the example plots, they appear to be showing many ratios.

Question

Is it possible to plot a 6-month rolling Sharpe ratio that changes from one day to the next?

Await answered 4/3, 2018 at 1:29 Comment(0)
B
12

Approximately correct solution using df.rolling and a fixed window size of 180 days:

df['rs'] = df['returns'].rolling('180d').apply(my_rolling_sharpe)

This window isn't exactly 6 calendar months wide because rolling requires a fixed window size, so trying window='6MS' (6 Month Starts) throws a ValueError.

To calculate the Sharpe ratio for a window exactly 6 calendar months wide, I'll copy this super cool answer by SO user Mike:

df['rs2'] = [my_rolling_sharpe(df.loc[d - pd.offsets.DateOffset(months=6):d, 'returns']) 
             for d in df.index]

# Compare the two windows
df.plot(y=['rs', 'rs2'], linewidth=0.5)

Sharpe ratio comparison

Basifixed answered 4/3, 2018 at 2:2 Comment(0)
A
2

I have prepared an alternative solution to your question, this one is based on using solely the window functions from pandas.

Here I have defined "on the fly" the calculation of the Sharpe Ratio, please consider for your solution the following parameters:

  • I have used a Risk Free rate of 2%
  • The dash line is just a Benchmark for the rolling Sharpe Ratio, the value is 1.6

So the code is the following

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style("whitegrid")

# Generate sample data
d = pd.date_range(start='1/1/2008', end='12/1/2015')
df = pd.DataFrame(d, columns=['Date'])
df['returns'] = np.random.rand(d.size, 1)
df = df.set_index('Date')

df['rolling_SR'] = df.returns.rolling(180).apply(lambda x: (x.mean() - 0.02) / x.std(), raw = True)
df.fillna(0, inplace = True)
df[df['rolling_SR'] > 0].rolling_SR.plot(style='-', lw=3, color='orange', 
                                         label='Sharpe', figsize = (10,7))\
                                         .axhline(y = 1.6, color = "blue", lw = 3,
                                                 linestyle = '--')

plt.ylabel('Sharpe ratio')
plt.legend(loc='best')
plt.title('Rolling Sharpe ratio (6-month)')
plt.show()

print('---------------------------------------------------------------')
print('In case you want to check the result data\n')
print(df.tail()) # I use tail, beacause of the size of your window.

You should get something similar to this picture

rolling sharpe ratio

Appealing answered 8/4, 2019 at 6:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.