Buffer-Stock Consumption Extension#
Based on Section 3.9.4 of Delli Gatti et al. (2011)
The buffer-stock extension replaces the baseline consumption rule with a target savings-to-income ratio mechanism. Households aim to maintain savings equal to \(h\) times their income. When savings fall below the target, they cut spending; when savings exceed it, they spend more freely.
Quick Start#
import bamengine as bam
from extensions.buffer_stock import BUFFER_STOCK
sim = bam.Simulation.init(seed=42)
sim.use(BUFFER_STOCK)
results = sim.run(n_periods=1000, collect=True)
Note
The BufferStock role is registered at the household level (it tracks
per-household state). The Extension bundle handles the
agent count automatically; when using the manual pattern, pass
n_agents=sim.n_households to use_role().
Role Fields#
Field |
Description |
|---|---|
|
Previous period’s income (used to detect income changes) |
|
Buffer-stock MPC (may exceed 1.0 when households dissave) |
Events#
Event |
Hook |
Description |
|---|---|---|
|
replaces |
Compute MPC from income changes and savings-target gap |
|
replaces |
Allocate spending using buffer-stock MPC |
Configuration#
Parameter |
Default |
Description |
|---|---|---|
|
2.0 |
Target savings-to-income ratio. Higher values mean more precautionary saving. |
Expected behavior: More realistic wealth distribution, precautionary saving by low-income households, and counter-cyclical consumption smoothing.
API Reference#
Buffer-stock consumption extension (Section 3.9.4).
This extension implements Section 3.9.4 of Delli Gatti et al. (2011), replacing the baseline mean-field MPC with an individual adaptive rule based on buffer-stock saving theory.
Usage:
from extensions.buffer_stock import BUFFER_STOCK
sim = bam.Simulation.init(**config)
sim.use(BUFFER_STOCK)
results = sim.run()
Or manually:
from extensions.buffer_stock import (
BufferStock,
BUFFER_STOCK_EVENTS,
BUFFER_STOCK_CONFIG,
)
sim = bam.Simulation.init(**config)
sim.use_role(BufferStock, n_agents=sim.n_households)
sim.use_events(*BUFFER_STOCK_EVENTS)
sim.use_config(BUFFER_STOCK_CONFIG)
results = sim.run()
Components#
- BufferStockrole
Tracks
prev_incomeandpropensityper household.- ConsumersCalcBufferStockPropensityevent
Replaces
consumers_calc_propensity.- ConsumersDecideBufferStockSpendingevent
Replaces
consumers_decide_income_to_spend.
Design Decisions#
Pipeline hook strategy — Both consumers_calc_propensity and
consumers_decide_income_to_spend are replaced via @event(replace=...).
The buffer-stock rule changes how MPC is computed (no mean-field) and
how it is applied (income-only for employed, savings-only for unemployed),
so both events must be replaced. Keeping them as two separate events
preserves the baseline pipeline structure (propensity -> spending).
Income tracking — prev_income is stored on the BufferStock role
(not on Consumer) to keep src/ untouched. con.income is still
zeroed after consumption, preserving compatibility with the wage payment
mechanism (workers_receive_wage adds wage to con.income in-place).
Desired savings-income ratio (h) — A single homogeneous config
parameter (buffer_stock_h) for all households. The book mentions a
“personal desired ratio” but does not specify a distribution. Starting
homogeneous lets validation (Figure 3.8) determine if heterogeneity is
needed.
MPC bounds and dissaving — No explicit c_max. The formula
c_t = 1 + (d - h*g)/(1+g) naturally produces c > 1 when income
drops (negative g), which is dissaving from savings. Budget is capped at
total wealth (savings + income) to prevent negative savings; c is
clipped at 0 minimum to prevent negative consumption.
Unemployed households — Three-case MPC logic:
Case |
Condition |
MPC formula |
Consumption source |
|---|---|---|---|
Normal |
W > 0 and W_prev > 0 |
Eq. 3.20 (g clamped -0.99) |
Income (c * W) |
Fresh start |
W > 0 and W_prev <= 0 |
c = 1 - h + S/W |
Income (c * W) |
Unemployed |
W <= 0 |
c = 1/h |
Savings (c * S) |
At the target buffer S = h·W, first-period unemployment spending equals
(1/h)·h·W = W — the last employed income (consumption smoothing). The
drawdown is geometric: after h periods, ~37% of savings remain ((1-1/h)^h
→ 1/e).
Dividends — Stay in savings, not counted as income W. The buffer-stock
MPC is driven by labor income dynamics only. The core Shareholder role
tracks per-period dividends so validation metrics can subtract the dividend
artifact from MPC calculations (adjusted_c = c - D/W).
Composability — Fully composable with R&D/Growth+. RnD modifies firm behavior (productivity); BufferStock modifies consumer behavior (MPC). They operate on different agent types with no conflicts.
Distribution fitting — Wealth CCDF is fitted with Singh-Maddala
(scipy.stats.burr12), Dagum (scipy.stats.mielke), and GB2
(scipy.stats.betaprime) distributions for validation against
Figure 3.8. Singh-Maddala and Dagum have x^k terms in their PDFs;
unconstrained MLE sometimes finds extreme k (>100), causing overflow.
The validation fitting uses a profile likelihood fallback: when the
default fit overflows, k is fixed at values in [5..50] and the
remaining parameters are optimized, selecting the k with maximum
log-likelihood.
- class extensions.buffer_stock.BufferStock(prev_income, propensity)[source]#
Buffer-stock consumption state for households.
Tracks previous-period income and buffer-stock propensity for each household. Used by the buffer-stock consumption extension to compute individual MPC based on adaptive savings rules.
- Parameters:
prev_income (
Float) – Previous period income (W_{t-1}). Used to compute income growth rate for the buffer-stock MPC formula.propensity (
Float) – Buffer-stock MPC (c_t). May exceed 1.0 when households dissave. Separate fromConsumer.propensityto avoid conflicts when the extension is not active.
- prev_income#
- propensity#
- name = 'BufferStock'#
- class extensions.buffer_stock.ConsumersCalcBufferStockPropensity[source]#
Compute individual MPC using buffer-stock adaptive rule.
Three-case logic:
Normal (prev_income > 0 and income > 0): Full buffer-stock formula (Eq. 3.20).
Fresh start (prev_income <= 0 and income > 0): Just re-employed:
c = 1 - h + S/W.Unemployed (income <= 0):
c = 1/h(gradual savings drawdown).
- name = 'consumers_calc_buffer_stock_propensity'#
- class extensions.buffer_stock.ConsumersDecideBufferStockSpending[source]#
Allocate spending budget using buffer-stock MPC.
Two-path consumption:
Employed (income > 0): budget = c * income
Unemployed (income <= 0): budget = c * savings
Budget is capped at total wealth (savings + income) and floored at 0. Savings are updated and floored at 0 (non-negative).
- name = 'consumers_decide_buffer_stock_spending'#
Buffer-stock consumption role definition.
This module defines the BufferStock role that tracks previous-period income and buffer-stock propensity for each household.
See Section 3.9.4 of Delli Gatti et al. (2011).
- class extensions.buffer_stock.role.BufferStock(prev_income, propensity)[source]#
Buffer-stock consumption state for households.
Tracks previous-period income and buffer-stock propensity for each household. Used by the buffer-stock consumption extension to compute individual MPC based on adaptive savings rules.
- Parameters:
prev_income (
Float) – Previous period income (W_{t-1}). Used to compute income growth rate for the buffer-stock MPC formula.propensity (
Float) – Buffer-stock MPC (c_t). May exceed 1.0 when households dissave. Separate fromConsumer.propensityto avoid conflicts when the extension is not active.
- prev_income#
- propensity#
- name = 'BufferStock'#
Buffer-stock consumption events.
This module provides two replacement events for the buffer-stock consumption extension (Section 3.9.4 of Delli Gatti et al., 2011).
The extension replaces the baseline mean-field MPC with an individual adaptive rule based on buffer-stock saving theory. Each household maintains a personal desired savings-income ratio and adjusts consumption to keep that ratio constant.
Key Equations#
Buffer-Stock MPC (Equation 3.20):
Where: - \(h\) is the desired savings-income ratio (config parameter) - \(g_t = W_t / W_{t-1} - 1\) is the income growth rate - \(d_t = S_t / W_{t-1} - h\) is the divergence from desired ratio
Fresh Start Formula (derived):
Applied when a household has just been re-employed (no previous income).
- class extensions.buffer_stock.events.ConsumersCalcBufferStockPropensity[source]#
Compute individual MPC using buffer-stock adaptive rule.
Three-case logic:
Normal (prev_income > 0 and income > 0): Full buffer-stock formula (Eq. 3.20).
Fresh start (prev_income <= 0 and income > 0): Just re-employed:
c = 1 - h + S/W.Unemployed (income <= 0):
c = 1/h(gradual savings drawdown).
- name = 'consumers_calc_buffer_stock_propensity'#
- class extensions.buffer_stock.events.ConsumersDecideBufferStockSpending[source]#
Allocate spending budget using buffer-stock MPC.
Two-path consumption:
Employed (income > 0): budget = c * income
Unemployed (income <= 0): budget = c * savings
Budget is capped at total wealth (savings + income) and floored at 0. Savings are updated and floored at 0 (non-negative).
- name = 'consumers_decide_buffer_stock_spending'#
See also
Model Extensions for writing custom extensions
Buffer-stock validation:
python -m validation.scenarios.buffer_stock