Spending Policies¶
Spending policies determine how much to withdraw each month in retirement. All policies implement the SpendingPolicy protocol.
SpendingPolicy Protocol¶
SpendingPolicy
¶
Bases: Protocol
Protocol for computing monthly spending amounts.
compute(state)
¶
Compute monthly spending need for each path.
Returns:
| Type | Description |
|---|---|
ndarray
|
Array of shape (n_paths,) with monthly spending amounts. |
ConstantRealSpending¶
ConstantRealSpending
¶
Spend a fixed real amount each month, adjusted for inflation.
The nominal spending grows with cumulative inflation to maintain constant purchasing power.
compute(state)
¶
Return inflation-adjusted monthly spending for each path.
PercentOfPortfolioSpending¶
PercentOfPortfolioSpending
¶
Spend a fixed percentage of total portfolio value each month.
Monthly spending = (annual_rate / 12) * total_wealth. Returns zero when wealth is zero.
compute(state)
¶
Return percentage-based monthly spending for each path.
GuardrailsSpending¶
GuardrailsSpending
¶
Guyton-Klinger guardrails: adjust spending based on withdrawal rate.
Decision rules: - Start with initial_withdrawal_rate * portfolio value. - Prosperity rule: if current withdrawal rate drops below initial_rate * (1 - upper_threshold), raise spending by raise_pct. - Capital preservation rule: if current withdrawal rate exceeds initial_rate * (1 + lower_threshold), cut spending by cut_pct.
compute(state)
¶
Compute monthly spending with guardrails adjustments.
VPWSpending¶
VPWSpending
¶
Variable Percentage Withdrawal: rate varies by remaining years.
Withdrawal rate = 1 / remaining_years, bounded by min_rate and max_rate. Monthly spending = (rate / 12) * total_wealth.
compute(state)
¶
Compute monthly spending based on remaining life expectancy.
FloorCeilingSpending¶
FloorCeilingSpending
¶
Spend a percentage of portfolio, clamped between a floor and ceiling.
Monthly spending = clamp(rate/12 * wealth, floor * inflation, ceiling * inflation). Floor and ceiling are specified in today's dollars and grow with inflation.
compute(state)
¶
Return clamped monthly spending for each path.
Supporting Modules¶
Contributions¶
contributions
¶
Contribution policy: fixed monthly contributions during accumulation.
apply_contributions(state, monthly_contributions, target_weights, growth_factor=1.0)
¶
Add monthly contributions to each account, allocated by target weights.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
SimulationState
|
Current simulation state (positions will be mutated). |
required |
monthly_contributions
|
list[float]
|
Base monthly contribution for each account. |
required |
target_weights
|
ndarray
|
(n_assets,) target asset allocation weights. |
required |
growth_factor
|
float
|
Cumulative income growth factor (1.0 = no growth). |
1.0
|
compute_monthly_contributions(annual_contributions)
¶
Convert annual contributions to monthly amounts.
Withdrawals¶
withdrawals
¶
Withdrawal ordering policy: taxable -> traditional -> roth.
withdraw(state, amount_needed, withdrawal_order, tax_rate)
¶
Withdraw from accounts in priority order.
For traditional accounts, gross up the withdrawal to cover taxes: to get $X after tax, withdraw $X / (1 - tax_rate).
Positions are reduced pro-rata across assets within each account.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
SimulationState
|
Current simulation state (positions will be mutated). |
required |
amount_needed
|
ndarray
|
(n_paths,) after-tax spending needed. |
required |
withdrawal_order
|
list[str]
|
Priority list of account types. |
required |
tax_rate
|
float
|
Flat effective tax rate on traditional withdrawals. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
(n_paths,) actual after-tax amount withdrawn (may be less than needed |
ndarray
|
if accounts are depleted). |
Rebalancing¶
rebalancing
¶
Rebalancing policies: calendar and threshold-based.
rebalance_to_targets(state, target_weights)
¶
Rebalance all accounts to target allocation weights.
For each account, redistributes positions across assets so that
the dollar allocation matches target_weights while preserving
the total account balance. Vectorized across all paths.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
SimulationState
|
Current simulation state (positions will be mutated). |
required |
target_weights
|
ndarray
|
(n_assets,) target allocation weights summing to 1. |
required |
rebalance_if_drifted(state, target_weights, threshold)
¶
Rebalance only paths where any asset drifted beyond threshold.
For each path, compute the current portfolio-level weights. If any
asset's weight deviates from target by more than threshold,
rebalance that path's accounts to targets. Paths within tolerance
are left untouched.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
SimulationState
|
Current simulation state (positions will be mutated). |
required |
target_weights
|
ndarray
|
(n_assets,) target allocation weights summing to 1. |
required |
threshold
|
float
|
Maximum allowed absolute drift (e.g. 0.05 for 5%). |
required |