<jupyter_start><jupyter_text>Leverage Stupidity or genius?Updated 2020-August-28.* This notebook looks at what the last 93 years of daily S&P 500 data has to say about the now well-known intra-day leverage.  * Automatic reinvestment of dividends is assumed.<jupyter_code>import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns<jupyter_output><empty_output><jupyter_text>Constants<jupyter_code># Number of trading days in a year
N_YEAR = 252

# 3x dividends as a fraction of S&P 500 dividends, 
# assumed from the current ratio of UPRO to SPY dividends
DIV3 = 0.15

# Explored leverage space (0% leverage to 100% leverage)
lev = np.linspace(0, 1, 41)<jupyter_output><empty_output><jupyter_text>Load Data<jupyter_code># S&P 500 daily - from Yahoo Finance
df = pd.read_csv('../data/^GSPC.csv', index_col=0, parse_dates=[0])
# S&P 500 annual average dividend - from Quandl
dfi = pd.read_csv('../data/MULTPL-SP500_DIV_YIELD_YEAR-annual.csv', 
                  index_col=0, parse_dates=[0])

dividend_year = dict(zip(dfi.index.year, dfi.Value.to_numpy()))
df['DividendYear'] = df.index.map(lambda x: dividend_year[x.year]) / 100
df<jupyter_output><empty_output><jupyter_text>Create Daily Leverage<jupyter_code>dl = (df.Close / df.Close.shift()).iloc[1:]
div = df.DividendYear.iloc[1:]<jupyter_output><empty_output><jupyter_text>Each entry of `dl` is the end of day multiple of the previous trading day's closing price, such that 1.05 would indicate a 5% increase.<jupyter_code># How many trading days in a year, i.e., how long to rebalance? 
# We will settle on the standard 252 trading days
dl.groupby(dl.index.year).count().value_counts()
# Long-term accuracy is good, as expected
assert np.round(np.product(dl), 5) == np.round(df.Close[-1] / df.Close[0], 5)
dl2 = 2*(dl-1) + 1

dl3 = 3*(dl-1) + 1<jupyter_output><empty_output><jupyter_text>Probably a bad idea to simply buy and hold 2x or 3xSo many of the twentieth century is way down, simply because of the Great Depression.<jupyter_code>print(np.product(dl), np.product(dl2), np.product(dl3), sep='\t')
plt.figure(figsize=(8, 6))
np.cumprod(dl).plot(label='Normal S&P (w/out Dividend Reinvestment)')
np.cumprod(dl2).plot(label='2x Daily S&P')
np.cumprod(dl3).plot(label='3x Daily S&P')
plt.legend()
plt.yscale('log')
plt.xlabel(None)
plt.xticks([dl.index[0], dl.index[-1]]);
plt.savefig('plots/3x_vs_1x.png', dpi=300);
# 3x daily once even got to 0.09% of principal!!
np.cumprod(dl3).min()<jupyter_output><empty_output><jupyter_text>Over the Entire Time Period (Jan 1928 - Aug 2020)Just to get a hint of what we're going to do, except on a 92-year, rather than a 10-year basis.<jupyter_code>n_split = N_YEAR
rebalance_idxs = np.arange(n_split, dl.size, n_split)

all_divs = np.array([np.average(s) for s in np.split(div, rebalance_idxs)])
all_divs3 = DIV3*all_divs
all_dlsplit = np.array([np.product(s) for s in np.split(dl, rebalance_idxs)]) + all_divs*n_split/N_YEAR
all_dlsplit3 = np.array([np.product(s) for s in np.split(dl3, rebalance_idxs)]) + all_divs3*n_split/N_YEAR
all_agg3 = (1-lev).reshape(-1, 1)*all_dlsplit + lev.reshape(-1, 1)*all_dlsplit3
all_results3 = np.product(all_agg3, axis=1)
plt.figure(figsize=(6.4, 4.8))
plt.plot(lev, all_results3)
plt.axvline(x=lev[np.argmax(all_results3)], 
            linestyle=':', c='r', label='Optimal 3x Leverage')
plt.xticks(lev[::4], ['{:.0%}'.format(p) for p in lev[::4]])
plt.xlim(0, 1)
plt.xlabel('Percent Leveraged')
plt.title('Return on Initial Investment from Jan 1928 to Aug 2020.\n\
Annual Rebalancing of Leverage Rates and Assumed\n\
Reinvestment of Dividends with a 3x dividend fraction of {}.'.format(DIV3))
plt.legend()
plt.tight_layout();
plt.savefig('plots/annual_rebalancing-max_timeperiod.png', dpi=300);
all_agg_time = np.cumprod(all_agg3, axis=1)

for i, row in enumerate(all_agg_time[::5]):
    plt.plot(row, label='{:.0%}'.format(lev[5*i]))
    
plt.yscale('log')
plt.legend(title='3x Leverage Rate', loc='upper left')
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14498880875404127, 'fraction_numerical': 0.03655807013180801, 'mean_word_length': 5.550488599348534, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '74253', 'license': 'MIT', 'n_tokens_mistral': 1678, 'n_tokens_neox': 1539, 'n_words': 383, 'path': 'entire_period.ipynb', 'repo_name': 'jessebmurray/leverage', 'size': '4021'}