Backtested Dual Momentum Strategy
symbols =['VCVSX','VWINX','VWEHX','VGHCX','VUSTX','VFIIX','VWAHX','FGOVX','FFXSX']
cash_proxy = 'CASHX'
risk_free = 'FFXSX'
rs_lookback = 1
risk_lookback = 1
n_top = 3
# get data
tickers = symbols.copy()
if cash_proxy != 'CASHX' :
tickers = list(set(tickers + [cash_proxy]))
if isinstance(risk_free, str) :
tickers = list(set(tickers + [risk_free]))
# Define which online source one should use
data_source = 'yahoo'
# We would like all available data from 01/01/2000 until today.
start_date = '2000-01-01'
end_date = datetime.today().strftime('%Y-%m-%d')
# User pandas_reader.data.DataReader to load the desired data. As simple as that.
panel_data = web.DataReader(tickers, data_source, start_date, end_date)
data = panel_data['Adj Close'].sort_index(ascending=True)
inception_dates = pd.DataFrame([data[ticker].first_valid_index() for ticker in data.columns],
index=data.keys(), columns=['inception'])
print (inception_dates)
prices = data.copy().dropna()
end_points = endpoints(period='M', trading_days=prices.index)
prices_m = prices.loc[end_points]
returns = prices_m[symbols].pct_change(rs_lookback)[rs_lookback:]
absolute_momentum_rule = returns > 0
if isinstance(risk_free, int) :
excess_returns = returns
else :
risk_free_returns = prices_m[risk_free].pct_change(rs_lookback)[rs_lookback:]
excess_returns = returns.subtract(risk_free_returns, axis=0).dropna()
rebalance_dates = excess_returns.index.join(absolute_momentum_rule.index, how='inner')
# relative strength ranking
ranked = excess_returns.loc[rebalance_dates][symbols].rank(ascending=False, axis=1, method='dense')
# elligibility rule - top n_top ranked securities
elligible = ranked[ranked<=n_top] > 0
# equal weight allocations
elligible = elligible.multiply(1./elligible.sum(1), axis=0)
# downside protection
weights = pd.DataFrame(0.,index=elligible.index, columns=prices.columns)
if cash_proxy == 'CASHX' :
weights[cash_proxy] = 0
prices[cash_proxy] = 1.
weights[symbols] = (elligible * absolute_momentum_rule).dropna()
weights[cash_proxy] += 1 - weights[symbols].sum(axis=1)
# backtest
p_value, p_holdings, p_weights = backtest(prices, weights, 10000., offset=0, commission=10.)
p_value.plot(figsize=(15,10), grid=True)