1.1. Model file language
A RISE model lives in a plain-text file with the extension .rs.
The file is read by the RISE parser when you call dsge_model('mymodel.rs')
and is converted into a model object on which the engines (solve,
estimate, filter, forecast, …) operate. This chapter is the
reference for everything you can write in that file.
The model file describes the model: which variables exist, which parameters they depend on, and what equations they satisfy. It does not carry calibration, priors, or solve-time choices – those live in companion MATLAB files or as options on the relevant calls. See What does not go in the model file below.
1.1.1. Block keywords at a glance
A .rs file is a sequence of blocks, each introduced by an
@ keyword. The blocks may appear in any order; the parser
collects them and links them together.
Keyword |
Purpose |
|---|---|
|
Declare endogenous variables. |
|
Declare exogenous innovations (shocks). |
|
Declare parameters whose value does not switch. |
|
Declare parameters that switch across |
|
Declare the variables observed in estimation / filtering. |
|
The list of equations. Required. |
|
Closed-form steady state (optional but recommended for non-trivial models). |
|
Endogenous (time-varying) transition probabilities – one equation per off-diagonal entry per chain. |
|
Replace one or more equations with the first-order conditions of a planner’s problem (optimal policy). |
|
Equations evaluated after solve and filter, e.g. for diagnostics or to derive variables not needed in the solution. |
A complete minimal example – a three-equation New Keynesian model with two shocks – appears at the end of this chapter.
1.1.2. Declarations
Endogenous variables
Variables determined inside the model:
@endogenous y pi i
You can attach a comment to a variable; comments survive in the solution table and in plots:
@endogenous Y "Output", C "Consumption", PAI "Inflation",
R "Interest rate (net)"
Variables in logs. Tag a variable to be interpreted in logs by adding
(log) to the declaration:
@endogenous(log) Y C
@endogenous PAI R
The shorter form is one line per group, but you may also mix them in the same block:
@endogenous Y "Output" C "Consumption"
@endogenous(log) MU "Technology growth"
A bulk form is available for the case where most variables are in logs:
@log_variables @all_but MU R
means every endogenous variable is in logs except MU and R.
The @endogenous(log) form is preferred when only a few variables
are in logs; @log_variables @all_but is the older bulk form, kept
for backwards compatibility.
Exogenous variables (shocks)
White-noise innovations – one per Gaussian shock you want to feed into the model:
@exogenous eps_m "Monetary shock", eps_a "Technology shock"
Parameters
Non-switching parameters:
@parameters beta sigma kappa phi_pi phi_y rho_g
std_eps_m std_eps_g
Switching parameters
A parameter that takes different values in different regimes of a
Markov chain is declared with @parameters(chain, N) where N
is the number of states of the chain:
@parameters(mon,2) phi_pi
@parameters(fis,2) gamma_b
This declares two chains – mon and fis – each with two
states. phi_pi takes two values; gamma_b takes two values.
The model has 2 x 2 = 4 composite regimes.
You can label the regimes inline:
@parameters(zlb,2,"normal times","lower bound") gam
The labels appear in solution tables and plots.
The chain is introduced by its first appearance as the first
argument of @parameters(chain,N); the user names it there.
The transition probabilities are declared either as ordinary
parameters (constant probabilities) or as equations in a
@transition_functions block (endogenous / time-varying
probabilities); see Markov chains below.
Observables
The variables you observe in the data when filtering or estimating:
@observables y pi i
There must be at least as many shocks as observables, otherwise the predicted observation covariance becomes singular (“stochastic singularity”).
1.1.3. The @model block
The @model block contains the equations. Each equation ends with
;. Equations may span several lines.
Time indexing
Variables are indexed by time. Three equivalent notations are
available; the {...} form is recommended for readability:
x{t} x{t-1} x{t+1} % explicit time index
x x{-1} x{+1} % t implicit, offsets explicit
x x(-1) x(+1) % parenthesis form
A steady-state value of a variable is written {stst}:
R{stst} % steady-state value of R
Equation labels
A quoted string immediately before an equation labels it. The label appears in error messages, in printed solutions, and in historical decompositions:
"Dynamic IS curve"
x{t} = x{t+1} - (1/sigma)*(i{t} - pi{t+1}) + g{t};
"Phillips curve"
pi{t} = beta*pi{t+1} + kappa*x{t};
Auxiliary definitions
A line that begins with # inside @model introduces a
short-hand. It is substituted into subsequent equations at parse
time:
@model
# rho_theta = rho_e;
# phi = (thetass-1)/psi;
# sig_theta = sig_e*phi;
log(THETA) = (1-rho_theta)*log(thetass)
+ rho_theta*log(THETA{-1})
+ sig_theta*EPS_THETA;
Definitions can reference each other in declaration order and may use any parameter or steady-state value already declared.
Steady-state-form companion equations
A dynamic equation may be followed by # and a simpler
steady-state form, separated from the rest of the equation by a
# token and terminated by the same ;. RISE uses the
steady-state form when computing the deterministic steady state,
which often converges much faster than evaluating the full dynamic
equation at the steady state:
"Euler equation"
-B*LAMBDA + betad*B{+1}*LAMBDA{+1}*(1+R)/PAI{+1} = 0
#
1+R = PAI*exp(MU)/betad
;
Both forms have to be mathematically consistent at the steady state.
1.1.4. Steady state
A model file describes the model; the steady state belongs in
a separate function. The reason is the same as the reason
calibration belongs in a separate *_params.m file: a model and
its steady state are two different things, and either can change
without the other. Conflating them inside the .rs file is bad
practice.
Steady-state file (recommended)
Write a MATLAB function – the name is your choice – that takes
the parameter struct (and the model object, if you need it),
populates the steady-state values, and returns the updated
parameter struct. Point RISE at it via the sstate_file
option:
m = set(m, sstate_file = 'my_steady_state');
There is no enforced naming convention: my_steady_state,
compute_ss, model_ss, anything works. The convention
<modelname>_sstate.m is a habit, not a requirement.
The steady-state file is regime-agnostic: it sees the
parameter struct as is, returns the bare-name steady-state values,
and does not branch on chain states. Any parameter implied by a
steady-state condition (a “steady-state-derived” parameter)
belongs in the steady-state file, not as a numeric in the
*_params.m file.
@steady_state_model block (in-file alternative)
The steady state may also be written directly inside the model
file in a @steady_state_model block:
@steady_state_model
A = 1;
Y = (thetass-1)/thetass*(zss-beta*gam)/(zss-gam);
C = Y;
...
The block is evaluated, not solved – the right-hand side may reference parameters and earlier steady-state expressions in declaration order. The separate steady-state file is the recommended pattern; the in-file block is available when keeping everything in one file is the user’s preference.
No steady-state specification
When neither a sstate_file nor a @steady_state_model is
provided, RISE attempts a numerical steady-state solve from a
default initial guess of zero. This works for log-linearized
models whose steady state is zero by construction.
1.1.5. Markov chains
RISE handles Markov-switching as a first-class concept. A chain is
introduced by writing parameters that switch on it (via
@parameters(chain,N)) and by declaring its transition
probabilities – either as constant parameters or as equations in
a @transition_functions block.
Constant transition probabilities
For a chain mon with N states, the off-diagonal transition
probabilities are declared as ordinary parameters with the name
pattern mon_tp_<i>_<j>:
@parameters mon_tp_1_2 mon_tp_2_1
@parameters(mon,2) phi_pi
The diagonal probabilities mon_tp_1_1 and mon_tp_2_2 are
inferred (each row of the transition matrix must sum to 1) and
must not be declared explicitly.
Calibrate them like any other parameter:
p.mon_tp_1_2 = 0.05;
p.mon_tp_2_1 = 0.05;
p.phi_pi_mon_1 = 2.5;
p.phi_pi_mon_2 = 0.7;
m = set(m, parameters = p);
Endogenous transition probabilities
When the probabilities depend on endogenous variables (TVTP),
declare them as equations in a @transition_functions block:
@parameters p_min "transition-prob floor",
p_max "transition-prob ceiling",
theta_zlb "sensitivity to log(B)"
@parameters(zlb,2,"normal times","lower bound") gam
@transition_functions
"Probability normal -> bound: rises as preference demand falls"
zlb_tp_1_2 = p_min + (p_max - p_min) / (1 + B^theta_zlb);
"Probability bound -> normal: rises as preference demand recovers"
zlb_tp_2_1 = p_min + (p_max - p_min) / (1 + B^(-theta_zlb));
The right-hand side may reference any endogenous variable, parameter,
or steady-state value. Keep the probabilities strictly interior
(0 < p < 1) – a transition-function that touches zero or one
makes the chain absorbing and is rarely intended.
Occasionally-binding constraints
An OBC is a Markov chain whose switching is determined endogenously by the binding state of the constraint, not by exogenous probabilities. Declare the chain in the usual way:
@parameters(zlb,2) bind
with the constraint encoded in the model:
i{t} = bind*i_floor + (1-bind)*i_taylor{t};
and tell solve to honor the constraint at solve time (see
Modeling -> Solving). The transition probabilities of an OBC
chain must be zero (the switching is endogenous, not stochastic);
solve enforces this.
1.1.6. Optimal policy
When you want one equation in the model to be replaced by the
first-order conditions of a planner’s problem – i.e., to choose an
instrument optimally rather than according to a rule – declare an
@optimization_problem block.
Single-player problem
@optimization_problem{
@objective = -0.5*(pi^2 + lambda_y*y^2 + lambda_i*i^2),
@discount = beta
}
@model
% no Taylor rule -- i is the planner's instrument, chosen
% to minimize the discounted sum of -objective
y{t} = y{t+1} - (1/sigma)*(i{t} - pi{t+1}) + g{t};
pi{t} = beta*pi{t+1} + kappa*y{t};
...
The instrument is whichever endogenous variable does not appear on
the left-hand side of any equation in @model. The choice
between commitment and discretion is made at solve time:
m = solve(m, solve_policy_type = "ramsey"); % commitment
m = solve(m, solve_policy_type = "discretion");
There is no @commitment parameter and no @-prefixed marker
in the model file – the solve choice is an option, not a model
property.
Multi-player problem (Nash, Stackelberg)
Two players with separate objectives and instruments:
@optimization_problem[@no_u_turn=false]{
Monetary:
@order = 1
@discount = beta
@objective = -0.5*(pi^2 + lambda_y*y^2 + lambda_i*i^2)
@instrument = i ;
Fiscal:
@order = 2
@discount = beta
@objective = -0.5*(tau^2 + lambda_b*b^2)
@instrument = tau ;
}
When all players have the same @order, the game is Nash. When
the orders differ, it is a Stackelberg cascade with the lower
@order as the leader.
1.1.7. Epilogue
Equations that are not needed to solve the model but should be
evaluated after solve and filter – diagnostics, accounting
identities, output transformations – live in an @epilogue
block:
@epilogue
"Annualised inflation"
pi_ann = 400*pi;
"Real interest rate"
r_real = i - pi{+1};
Epilogue equations may reference any endogenous variable but they do not affect the solution. They are evaluated after the fact, which keeps the dynamic system small.
1.1.8. What does not go in the model file
The model file describes the model. By the modern convention several things are kept out:
Numerical parameter values
Calibration lives in a companion MATLAB file (e.g.
mymodel_params.m) that returns a struct of parameter values:
function p = mymodel_params()
p = struct();
p.beta = 0.99;
p.sigma = 1.0;
p.kappa = 0.30;
...
end
and is bound to the model with set:
m = set(m, parameters = mymodel_params());
This keeps model and calibration on independent change cycles.
The .rs file may also carry a @parameterization block;
the separate *_params.m file is the recommended pattern.
Steady-state values
Same reasoning, applied to the steady state. The steady-state
function (a plain MATLAB file, name of your choice) is bound via
sstate_file:
m = set(m, sstate_file = 'my_steady_state');
The @steady_state_model block inside the model file is also
supported when keeping everything in one file is the user’s
preference. See Steady state above.
Solve-time choices
Choices about how the model is solved – commitment vs discretion,
perturbation strategy, solver, solve paradigm, …
are options on the solve call, not declarations in the model
file:
m = solve(m, solver = '+mn', solve_policy_type = "ramsey");
The model file does not change when you switch from commitment to discretion.
Estimation priors
Priors live in the same companion _params.m (or in a dedicated
_priors.m) as a separate output of the same function:
function [p, priors] = mymodel_est_params()
p = struct();
p.beta = 0.99;
...
priors = struct();
priors.kappa = {0.30, 0.10, 0.60, 'beta_pdf(0.9)', 1e-6, 0.999};
...
end
and are bound with:
m = set(m, parameters = p, estim_priors = priors);
Priors may also be declared inside the .rs file’s
@parameterization block; the separate *_est_params.m file
is the recommended pattern, since priors are an estimation choice
rather than a model property.
Comments
%starts a line comment. Block comments are not supported.