Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Shocks

Shock grids represent stochastic variables that define their own transition probabilities (based on the discretization method). Unlike regular grids, they compute their own grid points and transition matrices — you don’t specify them in state_transitions.

Import Convention

We recommend importing shock modules and using qualified names:

import lcm.shocks.iid
import lcm.shocks.ar1

# Recommended
shock = lcm.shocks.iid.Normal(n_points=5, gauss_hermite=False, mu=0.0, sigma=1.0, n_std=2.0)

# Not recommended — can cause name collisions (e.g., Normal from scipy)
from lcm.shocks.iid import Normal  # noqa

Qualified access makes the shock’s origin clear in code review and avoids collisions with common names like Normal from other libraries.

IID Shocks

Shocks that are independent across periods. Import: import lcm.shocks.iid

Normal

Discretized normal distribution N(μ,σ2)N(\mu, \sigma^2).

lcm.shocks.iid.Normal(n_points=7, gauss_hermite=False, mu=0.0, sigma=1.0, n_std=2.0)

Parameters:

LogNormal

Discretized log-normal distribution where lnXN(μ,σ2)\ln X \sim N(\mu, \sigma^2).

lcm.shocks.iid.LogNormal(n_points=7, gauss_hermite=False, mu=0.0, sigma=0.5, n_std=2.0)

Same parameters as Normal. Grid points are exp() of the underlying normal grid.

Uniform

Discretized uniform distribution U(start,stop)U(\text{start}, \text{stop}). Both endpoints are included in the grid.

lcm.shocks.iid.Uniform(n_points=5, start=0.0, stop=1.0)

Equally spaced points with uniform probabilities (all 1/n_points).

NormalMixture

Two-component normal mixture: εp1N(μ1,σ12)+(1p1)N(μ2,σ22)\varepsilon \sim p_1 \, N(\mu_1, \sigma_1^2) + (1 - p_1) \, N(\mu_2, \sigma_2^2).

lcm.shocks.iid.NormalMixture(
    n_points=9, n_std=2.0, p1=0.9, mu1=0.0, sigma1=0.1, mu2=0.0, sigma2=1.0,
)

Grid spans the mixture mean ±nstd\pm n_\text{std} mixture standard deviations.

AR(1) Shocks

Shocks with serial correlation. Import: import lcm.shocks.ar1

The process is yt=μ+ρyt1+εty_t = \mu + \rho \, y_{t-1} + \varepsilon_t. The innovation distribution depends on the method:

Tauchen

Discretization via Tauchen (1986). Uses CDF-based transition probabilities.

lcm.shocks.ar1.Tauchen(
    n_points=7, gauss_hermite=False, rho=0.9, sigma=0.1, mu=0.0, n_std=2.0,
)

Rouwenhorst

Discretization via Rouwenhorst (1995) / Kopecky & Suen (2010). Better for highly persistent processes (ρ\rho close to 1).

lcm.shocks.ar1.Rouwenhorst(n_points=7, rho=0.95, sigma=0.1, mu=0.0)

TauchenNormalMixture

AR(1) with mixture-of-normals innovations, discretized via Tauchen. Following Fella et al. (2019).

lcm.shocks.ar1.TauchenNormalMixture(
    n_points=9, rho=0.9, mu=0.0, n_std=2.0,
    p1=0.9, mu1=0.0, sigma1=0.1, mu2=0.0, sigma2=1.0,
)

Using Shocks in a Regime

Shock grids go in states. They must not appear in state transitions (they manage their own):

import lcm.shocks.iid
from lcm import LinSpacedGrid, Regime

working = Regime(
    transition=next_regime,
    states={
        "wealth": LinSpacedGrid(start=0, stop=100, n_points=50),
        "income_shock": lcm.shocks.iid.Normal(
            n_points=5, gauss_hermite=False, mu=0.0, sigma=1.0, n_std=2.0,
        ),
    },
    state_transitions={
        "wealth": next_wealth,
        # income_shock does NOT appear here — it manages its own transitions
    },
    actions={...},
    functions={
        "utility": utility,
        "earnings": lambda wage, income_shock: wage * jnp.exp(income_shock),
    },
)

Key Rules

  1. Shock grids go in states (they define the values the shock can take).

  2. Shock grids must not have a transition parameter (validation error if they do).

  3. Shock parameters can be specified at init or deferred to runtime (set to None).

  4. Runtime params follow the same hierarchy as other params (see Parameters).

Runtime Parameters

Set shock parameters to None at grid creation to supply them at runtime:

lcm.shocks.iid.Normal(n_points=5, gauss_hermite=False, mu=None, sigma=None, n_std=None)

Then supply the values in the params dict:

params = {
    "regime_name": {
        "mu": 0.0,
        "sigma": 1.0,
        "n_std": 2.0,
    },
}

See Also

References
  1. Tauchen, G. (1986). Finite State Markov-Chain Approximations to Univariate and Vector Autoregressions. Economics Letters, 20(2), 177–181. 10.1016/0165-1765(86)90168-0
  2. Rouwenhorst, K. G. (1995). Asset Pricing Implications of Equilibrium Business Cycle Models. In T. F. Cooley (Ed.), Frontiers of Business Cycle Research (pp. 294–330). Princeton University Press. 10.1515/9780691218052-014
  3. Kopecky, K. A., & Suen, R. M. H. (2010). Finite State Markov-Chain Approximations to Highly Persistent Processes. Review of Economic Dynamics, 13(3), 701–714. 10.1016/j.red.2010.02.002
  4. Fella, G., Gallipoli, G., & Pan, J. (2019). Markov-Chain Approximations for Life-Cycle Models. Review of Economic Dynamics, 34, 183–201. 10.1016/j.red.2019.03.013