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

@endogenous

Declare endogenous variables.

@exogenous

Declare exogenous innovations (shocks).

@parameters

Declare parameters whose value does not switch.

@parameters(chain,N[,names...])

Declare parameters that switch across N regimes of a Markov chain.

@observables

Declare the variables observed in estimation / filtering.

@model

The list of equations. Required.

@steady_state_model

Closed-form steady state (optional but recommended for non-trivial models).

@transition_functions

Endogenous (time-varying) transition probabilities – one equation per off-diagonal entry per chain.

@optimization_problem

Replace one or more equations with the first-order conditions of a planner’s problem (optimal policy).

@epilogue

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.

Comments

% starts a line comment. Block comments are not supported.

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_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.