Skip to content

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