Created
February 20, 2019 04:43
-
-
Save choffstein/239af64d5f1506ba82802268fc69c48d to your computer and use it in GitHub Desktop.
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 pandas | |
import numpy | |
import scipy.optimize | |
# based upon rules set out at https://allocatesmartly.com/adam-butler-gestaltu-adaptive-asset-allocation/ | |
tickers = ['spy', 'ezu', 'ewj', 'eem', 'vnq', 'rwx', 'ief', 'tlt', 'dbc', 'gld'] | |
# assumes tickers are .csv files in the same folder as the code | |
# with a column named 'adjusted_close' with daily adjusted closing prices | |
data = {} | |
for ticker in tickers: | |
data[ticker] = pandas.DataFrame.from_csv(ticker + '.csv')['adjusted_close'] | |
data = pandas.DataFrame(data).dropna() | |
# calculate rolling 126-day linear returns | |
mom = data.pct_change(126).dropna() | |
# calculate rolling correlation and volatility using log returns | |
correlation = data.apply(numpy.log).diff().rolling(126).corr().dropna() | |
volatility = data.apply(numpy.log).diff().rolling(20).std().dropna() * numpy.sqrt(252.) | |
# make sure we use the same dates | |
shared_dates = mom.index.intersection(correlation.items).intersection(volatility.index) | |
mom = mom.ix[shared_dates] | |
correlation = correlation.ix[shared_dates] | |
volatility = volatility.ix[shared_dates] | |
# take all the data and resample it on a monthly basis | |
monthly_mom = mom.resample('M').last() | |
monthly_correlation = correlation.resample('M').last() | |
monthly_volatility = volatility.resample('M').last() | |
# calculate allocations using min-variance optimization | |
allocations = {} | |
for date in monthly_mom.index: | |
six_month_returns = monthly_mom.ix[date] | |
# rank the assets and choose the top 5 | |
selected_assets = six_month_returns.rank(ascending=False) <= 5 | |
# extract their names | |
selected_assets = six_month_returns.index[selected_assets].tolist() | |
# extract the correlation / volatility | |
correlation = monthly_correlation.ix[date][selected_assets].ix[selected_assets] | |
volatility = monthly_volatility[selected_assets].ix[date] | |
# turn the correlation / volatility into a covariance matrix using | |
# V*C*V where V is a diagonal volatility matrix | |
volatility = pandas.DataFrame(numpy.diag(volatility), index = volatility.index, columns = volatility.index) | |
covariance = volatility.dot(correlation).dot(volatility) | |
# set up optimization problem + constraints | |
def _sums_to_one(w): | |
return (w.sum() - 1.)**2. | |
bounds = [(0., 1.)] * 5 | |
# _fmin is minimum variance | |
def _fmin(w): | |
w = pandas.Series(w, index = selected_assets) | |
return w.dot(covariance).dot(w) | |
# set our initial guess | |
x0 = pandas.Series(0.2, index = selected_assets) | |
# use SLSQP optimization function to find minimum variance portfolio | |
res = scipy.optimize.fmin_slsqp(_fmin, x0, eqcons = [_sums_to_one], bounds = bounds, disp=-1) | |
allocations[date] = pandas.Series(res, index = selected_assets) | |
# turn our results into a DataFrame; transpose to get assets on columns; fill missing data with 0's | |
allocations = pandas.DataFrame(allocations).transpose().fillna(0.) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment