Skip to content

Instantly share code, notes, and snippets.

@choffstein
Created February 20, 2019 04:43
Show Gist options
  • Save choffstein/239af64d5f1506ba82802268fc69c48d to your computer and use it in GitHub Desktop.
Save choffstein/239af64d5f1506ba82802268fc69c48d to your computer and use it in GitHub Desktop.
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