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.
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)\).
Before estimating, we can solve the model at specific parameter values to inspect the state-space solution.
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.7872The estimated value of rho should be close to the true
value of 0.8.
# 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]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.
A more realistic example is a simple New Keynesian model with three observed/unobserved variables:
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}\]
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.
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.438666 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.6054The IRF panels show how inflation, interest rate, and the output gap respond to monetary policy (u) and demand (g) shocks.
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.
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.025solve_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]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.
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.
# 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:
inst/examples/bayesian_nk_fred.Rinst/examples/bayesian_rbc_nonlinear.Rinst/examples/bayesian_nk_nonlinear.Rinst/examples/bayesian_rbc_capital_demo.R (quick demo, ~2.5
min) or bayesian_rbc_capital_full.R (full, ~9 min)Included in v0.4.0:
Not yet supported:
lead() or E() marks
forward expectations.VAR(+1) denotes the next-period
value.