def rebalance(context, data): # Wait for 756 trading days (3 yrs) of historical prices before trading if context.day < context.min_data_window - 1: return # Get expanding window of past prices and compute returns context.today = get_datetime().date() prices = data.history(context.assets, "price", context.day, "1d") if context.first_rebal_date is None: context.first_rebal_date = context.today context.first_rebal_idx = context.day print('Starting dynamic allocation simulation...') # Get investment horizon in days ie number of trading days next month context.tau = rnr.get_num_days_nxt_month(context.today.month, context.today.year) # Calculate HFP distribution asset_rets = np.array(prices.pct_change(context.tau).iloc[context.tau:, :]) num_scenarios = len(asset_rets) # Set Flexible Probabilities Using Exponential Smoothing half_life_prjn = 252 * 2 # in days lambda_prjn = np.log(2) / half_life_prjn probs_prjn = np.exp(-lambda_prjn * (np.arange(0, num_scenarios)[::-1])) probs_prjn = probs_prjn / sum(probs_prjn) mu_pc, sigma2_pc = rnr.fp_mean_cov(asset_rets.T, probs_prjn) # Perform shrinkage to mitigate estimation risk mu_shrk, sigma2_shrk = rnr.simple_shrinkage(mu_pc, sigma2_pc) weights, _, _ = rnr.efficient_frontier_qp_rets(context.n_portfolio, sigma2_shrk, mu_shrk) print('Optimal weights calculated 1 day before month end on %s (day=%s)' \ % (context.today, context.day)) #print(weights) min_var_weights = weights[0,:] # Rebalance portfolio accordingly for stock, weight in zip(prices.columns, min_var_weights): order_target_percent(stock, np.asscalar(weight)) context.weights = min_var_weights
n_scenarios = len(pnl_portfolio) # Equal probs equal_probs = np.ones(n_scenarios) / n_scenarios # Time-conditioned flexible probs with exponential decay half_life = 252 * 2 # half life of 2 years es_lambda = math.log(2) / half_life exp_probs = np.exp(-es_lambda * (np.arange(0, n_scenarios)[::-1])) exp_probs = exp_probs / sum(exp_probs) # effective number of scenarios ens_exp_probs = np.exp(sum(-exp_probs * np.log(exp_probs))) # Projected Distribution of Portfolio P&L at Horizon with flexible probabilities import rnr_meucci_functions as rnr mu_port, sigma2_port = rnr.fp_mean_cov(pnl_portfolio.T, equal_probs) mu_port_e, sigma2_port_e = rnr.fp_mean_cov(pnl_portfolio.T, exp_probs) print( 'Ex-ante portfolio $P&L mean over horizon (equal probs) : {:,.0f}'.format( mu_port)) print('Ex-ante portfolio $P&L volatility over horizon (equal probs) : {:,.0f}'. format(np.sqrt(sigma2_port))) print('') print('Ex-ante portfolio $P&L mean over horizon (flex probs) : {:,.0f}'.format( mu_port_e)) print('Ex-ante portfolio $P&L volatility over horizon (flex probs) : {:,.0f}'. format(np.sqrt(sigma2_port_e))) fig = plt.figure(figsize=(9, 8)) ax = fig.add_subplot(111)
# distribution directly as input into mean-variance optimizer # Projected linear returns to the horizon - historical simulation asset_rets = np.array(prices.pct_change(tau).ix[tau:, asset_tickers]) # Mean-variance inputs # Distribution of asset returns at horizon with flexible probabilities # Time-conditioned flexible probs with exponential decay half_life = 252 * 2 # half life of 2 years es_lambda = math.log(2) / half_life exp_probs = np.exp(-es_lambda * (np.arange(0, n_scenarios)[::-1])) exp_probs = exp_probs / sum(exp_probs) # Apply flexible probabilities to asset return scenarios import rnr_meucci_functions as rnr mu_pc, sigma2_pc = rnr.fp_mean_cov(asset_rets.T, exp_probs) # Perform shrinkage to mitigate estimation risk mu_shrk, cov_shrk = rnr.simple_shrinkage(mu_pc, sigma2_pc) # Step 1: m-v quadratic optimization for efficient frontier n_portfolio = 40 weights_pc, rets_pc, vols_pc = rnr.efficient_frontier_qp_rets( n_portfolio, cov_shrk, mu_shrk) # Step 2: evaluate satisfaction for all allocations on the frontier satisfaction_pc = -vols_pc # Choose the allocation that maximises satisfaction max_sat_idx = np.asscalar(np.argmax(satisfaction_pc)) max_sat = satisfaction_pc[max_sat_idx]
n_scenarios = len(pnl_portfolio) # Equal probs equal_probs = np.ones(n_scenarios) / n_scenarios # Time-conditioned flexible probs with exponential decay half_life = 252 * 2 # half life of 2 years es_lambda = math.log(2) / half_life exp_probs = np.exp(-es_lambda * (np.arange(0, n_scenarios)[::-1])) exp_probs = exp_probs / sum(exp_probs) # effective number of scenarios ens_exp_probs = np.exp(sum(-exp_probs * np.log(exp_probs))) # Projected Distribution of Portfolio P&L at Horizon with flexible probabilities import rnr_meucci_functions as rnr mu_port, sigma2_port = rnr.fp_mean_cov(pnl_portfolio.T, equal_probs) mu_port_e, sigma2_port_e = rnr.fp_mean_cov(pnl_portfolio.T, exp_probs) print('Ex-ante portfolio $P&L mean over horizon (equal probs) : {:,.0f}'.format(mu_port)) print('Ex-ante portfolio $P&L volatility over horizon (equal probs) : {:,.0f}'.format(np.sqrt(sigma2_port))) print('') print('Ex-ante portfolio $P&L mean over horizon (flex probs) : {:,.0f}'.format(mu_port_e)) print('Ex-ante portfolio $P&L volatility over horizon (flex probs) : {:,.0f}'.format(np.sqrt(sigma2_port_e))) fig = plt.figure(figsize=(9, 8)) ax = fig.add_subplot(111) ax.hist(pnl_portfolio, 50, weights=exp_probs) ax.set_title('Ex-ante Distribution of Portfolio P&L (flexbile probabilities with exponential decay)') plt.show()
ax4.plot(vix.index, state_probs, marker='o', markersize=3, linestyle='None', alpha=0.7) ax4.set_title("State-conditioned Probabilities (VIX > 20)") plt.tight_layout() plt.show() # Stress analysis import rnr_meucci_functions as rnr tmp_tickers = ['AAPL', 'JPM', 'WMT', 'SPY', 'TLT'] # HFP distribution of invariants using equal probs mu, sigma2 = rnr.fp_mean_cov(invariants.ix[:, tmp_tickers].T, equal_probs) # HFP distribution of invariants using state-conditioned probs (VIX > 20) mu_s, sigma2_s = rnr.fp_mean_cov(invariants.ix[:, tmp_tickers].T, state_probs) # Calculate correlations from statsmodels.stats.moment_helpers import cov2corr corr = cov2corr(sigma2) corr_s = cov2corr(sigma2_s) # Plot correlation heatmaps rnr.plot_2_corr_heatmaps(corr, corr_s, tmp_tickers, "HFP Correlation Heatmap - equal probs", "HFP Correlation Heatmap - state probs (VIX > 20)")
# distribution directly as input into mean-variance optimizer # Projected linear returns to the horizon - historical simulation asset_rets = np.array(prices.pct_change(tau).ix[tau:, asset_tickers]) # Mean-variance inputs # Distribution of asset returns at horizon with flexible probabilities # Time-conditioned flexible probs with exponential decay half_life = 252 * 2 # half life of 2 years es_lambda = math.log(2) / half_life exp_probs = np.exp(-es_lambda * (np.arange(0, n_scenarios)[::-1])) exp_probs = exp_probs / sum(exp_probs) # Apply flexible probabilities to asset return scenarios import rnr_meucci_functions as rnr mu_pc, sigma2_pc = rnr.fp_mean_cov(asset_rets.T, exp_probs) # Perform shrinkage to mitigate estimation risk mu_shrk, cov_shrk = rnr.simple_shrinkage(mu_pc, sigma2_pc) # Step 1: m-v quadratic optimization for efficient frontier n_portfolio = 40 weights_pc, rets_pc, vols_pc = rnr.efficient_frontier_qp_rets(n_portfolio, cov_shrk, mu_shrk) # Step 2: evaluate satisfaction for all allocations on the frontier satisfaction_pc = -vols_pc # Choose the allocation that maximises satisfaction max_sat_idx = np.asscalar(np.argmax(satisfaction_pc)) max_sat = satisfaction_pc[max_sat_idx]