Introduction to the dsge Package

Overview

The dsge package provides tools for specifying, solving, and estimating dynamic stochastic general equilibrium (DSGE) models by maximum likelihood and Bayesian methods. It supports linear models (formula interface), nonlinear models (first-order perturbation), and Bayesian estimation of linear models (adaptive RWMH). This vignette walks through examples demonstrating the core workflow.

A Simple AR(1) Model

We start with the simplest possible DSGE model: a single observed variable driven by a single autoregressive state variable.

The model is: \[y_t = z_t\] \[z_{t+1} = \rho z_t + \varepsilon_{t+1}\]

where \(\varepsilon_t \sim N(0, \sigma^2)\).

Simulate Data

set.seed(42)
true_rho <- 0.8
true_sd <- 1.0
n <- 200

z <- numeric(n)
for (i in 2:n) z[i] <- true_rho * z[i - 1] + true_sd * rnorm(1)
dat <- data.frame(y = z)

Specify the Model

library(dsge)

m <- dsge_model(
  obs(y ~ z),
  state(z ~ rho * z),
  start = list(rho = 0.5)
)

print(m)
#> Linear DSGE Model
#>   Observed controls:   y 
#>   Unobserved controls:  
#>   Exogenous states:    z 
#>   Parameters:          rho 
#>   Equations:            2 
#> 
#> Equations:
#>    y ~ z   [observed] 
#>    z ~ rho * z   [state]

Solve at Known Parameters

Before estimating, we can solve the model at specific parameter values to inspect the state-space solution.

sol <- solve_dsge(m, params = c(rho = 0.8))
print(sol)
#> DSGE Solution
#>   Stable:  TRUE 
#>   Stable eigenvalues:  1 / 1 
#> 
#> Policy matrix (G):
#>   z
#> y 1
#> 
#> Transition matrix (H):
#>     z
#> z 0.8

Estimate the Model

fit <- estimate(m, data = dat)
summary(fit)
#> 
#> DSGE Model -- Summary
#> ============================================================ 
#> 
#>   Log-likelihood:  -278.60475 
#>   AIC:             561.21 
#>   BIC:             567.806 
#>   Observations:    200 
#>   Convergence:     Yes 
#> 
#> Structural parameters:
#>                       Estimate    Std. Err.        z    P>|z|   [95% CI]
#> ------------------------------------------------------------------------------ 
#>   rho                 0.787161     0.043366    18.15   0.0000   [  0.7022,   0.8722]
#> 
#> Shock standard deviations:
#>   sd(e.z)             0.972066     0.048605    20.00   0.0000   [  0.8768,   1.0673]
#> 
#> 
#> Stability:
#>   Saddle-path stable:  TRUE 
#>   Eigenvalue moduli:   0.7872

The estimated value of rho should be close to the true value of 0.8.

Postestimation

# Policy matrix (maps states to controls)
policy_matrix(fit, se = FALSE)
#>   z
#> y 1

# Transition matrix (state dynamics)
transition_matrix(fit, se = FALSE)
#>           z
#> z 0.7871615

# Stability check
stability(fit)
#> DSGE Stability Check
#>   Saddle-path stable:  TRUE 
#>   Stable eigenvalues:  1  /  1 
#>   Required stable:     1 
#> 
#> Eigenvalues:
#>   0.787161  |lambda| = 0.787161  [stable]

Impulse-Response Functions

irfs <- irf(fit, periods = 20, se = FALSE)
plot(irfs)

The IRF shows the response of y to a one-standard-deviation shock to the state variable z. The response decays geometrically at rate rho.

Forecasting

fc <- forecast(fit, horizon = 12)
plot(fc)

The forecast converges toward the unconditional mean of the series.

A Three-Equation New Keynesian Model

A more realistic example is a simple New Keynesian model with three observed/unobserved variables:

  • \(p_t\): inflation (observed)
  • \(x_t\): output gap (unobserved)
  • \(r_t\): interest rate (observed)

The linearized model is:

\[p_t = \beta E_t[p_{t+1}] + \kappa x_t\] \[x_t = E_t[x_{t+1}] - (r_t - E_t[p_{t+1}] - g_t)\] \[r_t = \psi p_t + u_t\] \[u_{t+1} = \rho_u u_t + \varepsilon_{u,t+1}\] \[g_{t+1} = \rho_g g_t + \varepsilon_{g,t+1}\]

Model Specification

nk <- dsge_model(
  obs(p   ~ beta * lead(p) + kappa * x),
  unobs(x ~ lead(x) - (r - lead(p) - g)),
  obs(r   ~ psi * p + u),
  state(u ~ rhou * u),
  state(g ~ rhog * g),
  fixed = list(beta = 0.96),
  start = list(kappa = 0.1, psi = 1.5, rhou = 0.7, rhog = 0.9)
)

print(nk)
#> Linear DSGE Model
#>   Observed controls:   p, r 
#>   Unobserved controls: x 
#>   Exogenous states:    u, g 
#>   Parameters:          beta, kappa, psi, rhou, rhog 
#>   Fixed:               beta = 0.96 
#>   Equations:            5 
#> 
#> Equations:
#>    p ~ beta * lead(p) + kappa * x   [observed] 
#>    x ~ lead(x) - (r - lead(p) - g)   [unobserved] 
#>    r ~ psi * p + u   [observed] 
#>    u ~ rhou * u   [state] 
#>    g ~ rhog * g   [state]

This model has five equations, three control variables (two observed, one unobserved), and two exogenous state variables. The discount factor \(\beta\) is constrained to 0.96.

Simulate and Estimate

We simulate data from the model at known parameter values and then estimate the parameters by maximum likelihood.

# Solve at true parameters to get state-space form
true_params <- c(beta = 0.96, kappa = 0.085, psi = 1.94,
                 rhou = 0.70, rhog = 0.95)
true_sd <- c(u = 2.3, g = 0.57)
sol <- solve_dsge(nk, params = true_params, shock_sd = true_sd)

# Simulate data
set.seed(123)
n <- 200
states <- matrix(0, n, 2)
colnames(states) <- c("u", "g")
for (t in 2:n) {
  states[t, "u"] <- 0.70 * states[t - 1, "u"] + 2.3 * rnorm(1)
  states[t, "g"] <- 0.95 * states[t - 1, "g"] + 0.57 * rnorm(1)
}
Z <- sol$D %*% sol$G
obs_data <- states %*% t(Z)
colnames(obs_data) <- c("p", "r")
nk_dat <- as.data.frame(obs_data)

# Estimate
nk_fit <- estimate(nk, data = nk_dat,
                   start = list(kappa = 0.1, psi = 1.5,
                                rhou = 0.7, rhog = 0.9),
                   control = list(maxit = 500))
summary(nk_fit)
#> 
#> DSGE Model -- Summary
#> ============================================================ 
#> 
#>   Log-likelihood:  -594.45234 
#>   AIC:             1200.9 
#>   BIC:             1220.69 
#>   Observations:    200 
#>   Convergence:     Yes 
#> 
#> Structural parameters:
#>                       Estimate    Std. Err.        z    P>|z|   [95% CI]
#> ------------------------------------------------------------------------------ 
#>   beta                0.960000      (fixed)                  
#>   kappa               0.175999     0.065131     2.70   0.0069   [  0.0483,   0.3037]
#>   psi                 1.888534     0.438667     4.31   0.0000   [  1.0287,   2.7483]
#>   rhou                0.605425     0.055990    10.81   0.0000   [  0.4957,   0.7152]
#>   rhog                0.866206     0.034497    25.11   0.0000   [  0.7986,   0.9338]
#> 
#> Shock standard deviations:
#>   sd(e.u)             2.086544     0.424927     4.91   0.0000   [  1.2537,   2.9194]
#>   sd(e.g)             0.627729     0.123601     5.08   0.0000   [  0.3855,   0.8700]
#> 
#> 
#> Stability:
#>   Saddle-path stable:  TRUE 
#>   Eigenvalue moduli:   0.8662, 0.6054

Impulse-Response Functions

nk_irfs <- irf(nk_fit, periods = 20)
plot(nk_irfs)

The IRF panels show how inflation, interest rate, and the output gap respond to monetary policy (u) and demand (g) shocks.

Forecasting

nk_fc <- forecast(nk_fit, horizon = 12)
plot(nk_fc)

Nonlinear DSGE Models

The package also supports nonlinear DSGE models via first-order perturbation (linearization around the deterministic steady state). Nonlinear models are specified using string-based equations with VAR(+1) notation for leads.

A Simple RBC Model

rbc <- dsgenl_model(
  "1/C = beta / C(+1) * (alpha * exp(Z) * K^(alpha-1) + 1 - delta)",
  "K(+1) = exp(Z) * K^alpha - C + (1 - delta) * K",
  "Z(+1) = rho * Z",
  observed = "C",
  endo_state = "K",
  exo_state = "Z",
  fixed = list(alpha = 0.33, beta = 0.99, delta = 0.025),
  start = list(rho = 0.9),
  ss_guess = c(C = 2, K = 30, Z = 0)
)
print(rbc)
#> Nonlinear DSGE Model
#>   Equations:     3 
#>   Controls:      1 ( 1 observed, 0 unobserved )
#>   States:        2 ( 1 exogenous, 1 endogenous )
#>   Parameters:    4 ( 1 free, 3 fixed )
#> 
#>   [1] 1/C = beta / C(+1) * (alpha * exp(Z) * K^(alpha-1) + 1 - delta)
#>   [2] Z(+1) = rho * Z
#>   [3] K(+1) = exp(Z) * K^alpha - C + (1 - delta) * K
#> 
#>   Fixed:  alpha = 0.33, beta = 0.99, delta = 0.025

Steady State

params <- c(alpha = 0.33, beta = 0.99, delta = 0.025, rho = 0.9)
ss <- steady_state(rbc, params = params)
print(ss)
#> Deterministic Steady State
#>   Converged: TRUE  ( 5  iterations)
#>   Max residual: 0 
#> 
#>   Values:
#>      C = 2.306617 
#>      Z = 0 
#>      K = 28.34842

Solve and Inspect

solve_dsge() automatically linearizes and solves:

sol <- solve_dsge(rbc, params = params, shock_sd = c(Z = 0.01))
print(sol)
#> DSGE Solution
#>   Stable:  TRUE 
#>   Stable eigenvalues:  2 / 2 
#> 
#> Policy matrix (G):
#>          Z        K
#> C 0.451385 0.048867
#> 
#> Transition matrix (H):
#>          Z        K
#> Z 0.900000 0.000000
#> K 2.563943 0.961234
stability(sol)
#> DSGE Stability Check
#>   Saddle-path stable:  TRUE 
#>   Stable eigenvalues:  2  /  2 
#>   Required stable:     2 
#> 
#> Eigenvalues:
#>   0.961234  |lambda| = 0.961234  [stable]
#>   0.9  |lambda| = 0.900000  [stable]

IRFs

rbc_irfs <- irf(sol, periods = 40, se = FALSE)
plot(rbc_irfs)

The IRF shows how consumption responds to a TFP shock, with the response decaying as capital adjusts back to steady state.

Bayesian Estimation

The package supports Bayesian estimation of both linear and nonlinear DSGE models via adaptive Random-Walk Metropolis-Hastings (RWMH). For nonlinear models, the steady state is re-solved and the model re-linearized at each candidate parameter draw.

Specifying Priors

Use prior() to define prior distributions for structural parameters:

my_priors <- list(
  beta  = prior("beta", shape1 = 95, shape2 = 5),
  kappa = prior("beta", shape1 = 30, shape2 = 70),
  psi   = prior("gamma", shape = 20, rate = 13.3),
  rhou  = prior("beta", shape1 = 70, shape2 = 20),
  rhog  = prior("beta", shape1 = 70, shape2 = 20)
  # shock SDs default to inv_gamma(0.01, 0.01)
)

Supported distributions: normal, beta, gamma, uniform, inv_gamma.

Running the Sampler

fit_bayes <- bayes_dsge(nk, data = nk_dat, priors = my_priors,
                        chains = 2, iter = 10000, warmup = 5000,
                        seed = 42)

summary(fit_bayes)  # posterior table with ESS, R-hat, MCSE

Posterior Diagnostics and IRFs

# MCMC diagnostics
plot(fit_bayes, type = "trace")            # trace plots (all chains)
plot(fit_bayes, type = "density")          # posterior density + prior overlay
plot(fit_bayes, type = "prior_posterior")   # dedicated prior-vs-posterior comparison
plot(fit_bayes, type = "running_mean")     # cumulative mean convergence
plot(fit_bayes, type = "acf")              # autocorrelation diagnostics
plot(fit_bayes, type = "pairs")            # pairwise scatter + correlations
plot(fit_bayes, type = "all")              # combined trace + density panel

# Parameter selection (works with all types above)
plot(fit_bayes, type = "trace", pars = c("kappa", "psi"))
plot(fit_bayes, type = "density", pars = "rhou")

# Posterior IRFs with credible bands
plot(fit_bayes, type = "irf", periods = 12, n_draws = 500)
# Or equivalently:
bayes_irfs <- irf(fit_bayes, periods = 12, n_draws = 500)
plot(bayes_irfs)

Available plot types for dsge_bayes objects:

Type Description
"trace" MCMC trace plots (all chains overlaid)
"density" Posterior density with prior overlay
"prior_posterior" Dedicated prior-vs-posterior comparison
"running_mean" Cumulative posterior mean by iteration
"acf" Autocorrelation function plots
"pairs" Pairwise scatter, correlations, histograms
"all" Combined trace + density side by side
"irf" Posterior IRFs with credible bands

The pars argument selects specific parameters (e.g., pars = c("kappa", "psi")). Forecast plotting is not yet available for Bayesian fits.

Note: Bayesian nonlinear estimation uses first-order perturbation (linearization around parameter-specific steady states). This is not a fully nonlinear filtering solution; higher-order approximations and particle filters are not yet implemented.

For examples:

  • Linear Bayesian on real data: inst/examples/bayesian_nk_fred.R
  • Nonlinear Bayesian RBC: inst/examples/bayesian_rbc_nonlinear.R
  • Nonlinear Bayesian NK: inst/examples/bayesian_nk_nonlinear.R
  • RBC with capital accumulation: inst/examples/bayesian_rbc_capital_demo.R (quick demo, ~2.5 min) or bayesian_rbc_capital_full.R (full, ~9 min)

What This Package Currently Supports

Included in v0.4.0:

  • Linear DSGE models via formula interface
  • Nonlinear DSGE models via first-order perturbation
  • Bayesian estimation of linear and nonlinear DSGE models (adaptive RWMH)
  • Prior specification: normal, beta, gamma, uniform, inverse-gamma
  • MCMC diagnostics: ESS, R-hat, MCSE, acceptance rates
  • Posterior IRFs with credible bands
  • Deterministic steady-state solver
  • Solution via undetermined coefficients (Klein 2000)
  • Maximum likelihood estimation via the Kalman filter
  • Delta-method standard errors
  • IRFs with confidence bands; forecasts with fan charts
  • Policy/transition matrix extraction and stability diagnostics

Not yet supported:

  • Higher-order perturbation (second-order, third-order)
  • Nonlinear filtering (particle filter)
  • Variance and historical shock decomposition

Notes

  • For linear models, lead() or E() marks forward expectations.
  • For nonlinear models, VAR(+1) denotes the next-period value.
  • The number of exogenous state variables (with shocks) must equal the number of observed control variables.
  • Nonlinear models are solved by first-order linearization around the deterministic steady state. The resulting linear system uses the same solver and estimation pipeline as linear models.