Skip to content

Instantly share code, notes, and snippets.

@thiagomarzagao
Last active January 28, 2021 00:22
Show Gist options
  • Save thiagomarzagao/95749a4af1024c1a1188b5d9ebf657e8 to your computer and use it in GitHub Desktop.
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)
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