Backward induction solves a sequential decision problem by starting at the final state and recursively evaluating earlier states. In goals-based investing, dynamic programming uses it to determine which asset allocation maximizes the probability of reaching a wealth goal. section 6.2.1
A practical implementation proceeds in three stages:
- Discretize wealth into states and estimate transition probabilities between adjacent time periods using midpoint boundaries and a return-distribution CDF. section 6.2.1
- Set the terminal value to one for wealth states exceeding the $2 million goal and zero otherwise. section 6.2.1
- Work backward through time, calculate each allocation's expected future value, retain the maximum, and record the corresponding allocation. section 6.2.1
Listing 6.2 Computing transition probabilities
from scipy.stats import norm
import warnings
warnings.filterwarnings('ignore')
def compute_mu_sigma(a):
mu = a/(nA-1)*mu_stock + (1-a/(nA-1))*mu_bond
sig = a/(nA-1)*sig_stock
return mu, sig
def compute_midpoints(W):
W_midpts = np.zeros((nW+1,T+1))
W_midpts[0,:] = 0.000001
W_midpts[nW,:] = np.inf
W_midpts[1:nW,:] = (W[:nW-1,:]+W[1:nW,:])/2
return W_midpts
def compute_transition_probs(w, t, a, W_midpts):
mu, sig = compute_mu_sigma(a)
W_tplus1_dist = np.zeros(nW)
cdfs = norm.cdf(np.float64((np.log(W_midpts[:,t+1]/w)
- (mu-0.5*sig**2))/sig)) #1
W_tplus1_dist = cdfs[1:nW+1] - cdfs[0:nW] #2
return W_tplus1_dist
The implementation computes portfolio mean and volatility, handles wealth-grid boundaries, and obtains transition probabilities from differences between normal CDF values. section 6.2.1
