Consumption is the use of resources for present satisfaction. In financial planning, it represents spending drawn from wealth and becomes a decision variable alongside asset allocation: choose consumption c_t and stock allocation a_t to maximize expected discounted utility while wealth evolves after consumption and investment returns. section 6.3.2
A quantitative consumption model specifies:
- Utility U(c_t) converts each period’s consumption into reward, while the discount factor γ weights future satisfaction. section 6.3.2
- The budget constraint subtracts current consumption from wealth before applying stock and bond returns; labor income can extend the constraint. section 6.3.2
- Dynamic programming searches a discretized wealth, time, allocation, and consumption state space; reinforcement learning instead updates action values from simulated episodes. section 6.3.2 section 6.3.2
A discretized spending policy
A model can begin with $1,000,000 of wealth, a ten-year horizon, 8% expected stock returns, 4.5% bond returns, and 20% stock volatility. With CRRA utility of 3 and γ = 0.95, backward induction evaluates candidate consumption fractions and asset allocations, then records the maximizing policy for each wealth–time state. section 6.3.2
Listing 6.7 Creating a state space grid for the spending example
W0 = 1000000
T = 10
nW = 101
mu_stock = 0.08
mu_bond = 0.045
sig_stock = 0.20
sig_bond = 0.0
Cbar = np.zeros(T+1)
Cbar[0] = 0
for t in range(1, T+1):
Cbar[t] = Cbar[t-1] + 1/(T-t+1)
lnW = np.zeros((nW,T+1))
W = np.zeros((nW, T+1))
for t in range(T+1):
lnW_min = log(W0) - t*log(3) #1
lnW_max = log(W0) - Cbar[t] + (mu_stock-0.5*sig_stock**2)*t
+ 2.5*sig_stock*sqrt(t)
lnW[:,t] = np.linspace(lnW_min, lnW_max, nW)
W = np.exp(lnW)
Inspect the Python construction of the wealth grid used to represent feasible financial states over time. section 6.3.2
Listing 6.8 DP solution for the spending example
nA = 11
nC = 40
V = np.zeros((nW, T+1))
A = np.zeros((nW, T))
C = np.zeros((nW, T)) #1
EV = np.zeros((nA, nC))
W_midpts = compute_midpoints(W)
gamma = 0.95 #2
CRRA = 3 #3
def utility(c): #4
if (CRRA == 1):
util = log(c)
else:
util = (c**(1-CRRA))/(1-CRRA)
return util
for j in range(nW):
V[j, T] = utility(W[j,T]) #5
for t in range(T-1, -1, -1):
for j in range(nW):
for a in range(nA):
for c in range(nC):
cons = (c+1)/(1.5*nC)*W[j,t] #6
W_tplus1_dist = compute_transition_probs(W[j,t]-cons, t, #7
a W_midpts)
EV[a,c] = np.dot(W_tplus1_dist, utility(cons)+gamma*V[:,t+1]) #8
V[j,t] = EV.max()
A[j,t], C[j,t] = np.unravel_index(EV.argmax(), EV.shape) #9
Inspect the backward-induction implementation that evaluates utility, transitions, and joint consumption–allocation choices. section 6.3.2
