def rolling_z_score_test(): s = pd.Series(range(10)).cumsum() z = util.rolling_z_score(s, lookback=3) assert_true(np.isnan(z[0])) assert_true(np.isnan(z[1])) assert_almost_equals(z[2], 1.091089, places=6) assert_almost_equals(z[3], 1.059626, places=6) assert_almost_equals(z[4], 1.044074, places=6) assert_almost_equals(z[5], 1.034910, places=6) assert_almost_equals(z[6], 1.028887, places=6) assert_almost_equals(z[7], 1.024631, places=6) assert_almost_equals(z[8], 1.021466, places=6) assert_almost_equals(z[9], 1.019020, places=6)
def rolling_ratio_mean_revert_strategy(x, y, lookback): hedge_ratios = pd.rolling_mean(x / y, lookback) weights = pd.DataFrame({ 'x': -hedge_ratios, 'y': np.ones(shape=len(y)) }) prices = pd.DataFrame({'x': x, 'y': y}) port = (prices * weights).sum(axis=1) # Keep units inverse to the rolling z score. # Remember, we're betting on mean reversion here. units = -util.rolling_z_score(port, lookback=lookback) return (prices, weights, units)
def bollinger_mean_revert(x, y, model=None): if model is None: model = bollinger_mean_revert_fit(x, y) lookback = model['lookback'] hedge_lookback = model['hedge_lookback'] entry_z_score = model['entry_z_score'] exit_z_score = model['exit_z_score'] static_hedges = model.get('static_hedges') or False # Setup mean reversion portfolio to use for tradable zScore strategy = rolling_hedge_mean_revert_strategy (prices, weights, units) = strategy(x, y, lookback, hedge_lookback) port = portfolio.portfolio_price(prices, weights) # Ok, now let's look at the half-life of this portfolio, this needs to be low (under 30) # TODO: Test this? Throw it out if it doesn't meet standard? halflife = mr.halflife(port) cadf = sms.adfuller(port, maxlag=1, regression='c')[0] hurst = mr.hurst_exponent(port.pct_change().fillna(0))[0] # Collect zScore and theoretical results in the form of a sharpe ratio zScore = util.rolling_z_score(port, lookback) rets = portfolio.portfolio_returns(prices, weights, -zScore)['returns'] theoretical_sharpe = returns.annual_sharpe(rets) # Collect units of this portfolio according to bollinger band strategy (buying and selling # against the given zScore) units = bollinger_band_units(zScore, entryZScore=entry_z_score, exitZScore=exit_z_score) # Random experiment - static weights. Basically, force hedge weights to be static for the duration # of any particular trade. if static_hedges == True: unit_changes = units != units.shift().fillna(0) weights[~unit_changes] = np.nan weights['x'][0] = 0 weights['y'][0] = 0 weights = weights.fillna(method='ffill') # Finally, collect the results results = { 'lookback': lookback, 'hedge_lookback': hedge_lookback, 'static_hedges': static_hedges, 'halflife': halflife, 'cadf': cadf, 'hurst': hurst, 'theoretical_sharpe': theoretical_sharpe, 'prices': prices, 'weights': weights, 'units': units } port_rets = portfolio.portfolio_returns(prices, weights, units) results.update(port_rets) rets = port_rets['returns'] results.update( returns.report(rets)) results.update( report(units)) return results