Last active
January 28, 2021 00:22
-
-
Save thiagomarzagao/95749a4af1024c1a1188b5d9ebf657e8 to your computer and use it in GitHub Desktop.
portfolio analyzer (computes Sharpe ratio, alpha, beta, etc; it also suggests allocation based on efficient frontier)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
import pandas as pd | |
import statsmodels.api as sm | |
import matplotlib.pyplot as plt | |
from datetime import datetime | |
from pandas_datareader import data as wb | |
from pypfopt.risk_models import CovarianceShrinkage | |
from pypfopt.expected_returns import mean_historical_return | |
from pypfopt import plotting | |
from pypfopt import objective_functions | |
from pypfopt.efficient_frontier import EfficientFrontier | |
# pick tickers and weights | |
tickers = { | |
'VOO': 1/3, | |
'VWO': 1/3, | |
'BSV': 1/6, | |
'BNDX': 1/6 | |
} | |
# get price data | |
start = '2013-06-07' # bc of BNDX | |
end = '2021-01-26' | |
pf = pd.DataFrame() | |
for t in tickers.keys(): | |
pf[t] = wb.DataReader( | |
t, | |
data_source = 'yahoo', | |
start = start, | |
end = end | |
)['Adj Close'] | |
# convert prices to base 100 | |
pf = 100 * (pf / pf.iloc[0, :]) | |
# plot prices | |
pf.plot() | |
plt.show() | |
# get daily returns for each ticker | |
pf_returns = pf.pct_change() | |
# see correlogram | |
print(' ') | |
print('correlogram of daily returns:') | |
print(pf_returns.corr()) | |
# get portfolio returns | |
weights = np.array(list(tickers.values())) | |
pf_return = pf_returns * weights | |
pf_return = pf_return.sum(axis = 1) | |
pf_return.plot.hist(bins = 60) | |
plt.show() | |
# get cumulative portfolio returns | |
pf_return_cum = (1 + pf_return).cumprod() | |
pf_return_cum.plot() | |
plt.show() | |
# get mean annual return | |
t0 = datetime.strptime(start, '%Y-%m-%d') | |
t1 = datetime.strptime(end, '%Y-%m-%d') | |
t = (t1 - t0).days / 365 | |
usd_t0 = pf_return_cum[0] | |
usd_t1 = pf_return_cum[-1] | |
r = ((usd_t1/usd_t0) ** (1/t)) - 1 | |
print(' ') | |
print('mean annual return:', r) | |
# get portfolio volatility | |
pf_cov = pf_returns.cov() | |
pf_vol = np.sqrt(np.dot(weights.T, np.dot(pf_cov, weights))) | |
print(' ') | |
print('daily volatility:', pf_vol) | |
vol_year = pf_vol * np.sqrt(252) | |
print('annual volatility:', vol_year) | |
# get alpha e beta of portfolio | |
y = pf_return.values[1:] | |
X = pf_returns['VOO'].values[1:] | |
X = sm.add_constant(X) | |
model = sm.OLS(y, X) | |
res = model.fit() | |
alfa, beta = res.params | |
p_alfa, p_beta = res.pvalues | |
print(' ') | |
print('alfa:', alfa, 'p:', p_alfa) | |
print('beta:', beta, 'p:', p_beta) | |
# get Sharpe ratio of each ticker | |
print(' ') | |
for ticker in tickers.keys(): | |
avg = pf_returns[ticker].mean() | |
std = pf_returns[ticker].std() | |
sharpe = (avg/std) * np.sqrt(252 * 5) | |
print(ticker + ' 5-year Sharpe:', sharpe) | |
# get Sharpe ratio of portfolio | |
avg = pf_return.mean() | |
std = pf_return.std() | |
sharpe = (avg/std) * np.sqrt(252 * 5) | |
print(' ') | |
print('overall 5-year Sharpe:', sharpe) | |
# optimize portfolio | |
mu = mean_historical_return(pf) | |
S = CovarianceShrinkage(pf).ledoit_wolf() | |
ef = EfficientFrontier(mu, S) | |
weights_opt = ef.max_sharpe() | |
weights_opt = ef.clean_weights() | |
print(' ') | |
print('allocation that maximizes Sharpe:') | |
print(weights_opt) | |
print(' ') | |
print('1-year metrics w/ maximized Sharpe:') | |
print(ef.portfolio_performance(verbose = True)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment