2.1. Description

DSGE (Dynamic Stochastic General Equilibrium) models are a type of macroeconomic model that are widely used in modern macroeconomics. Some of the strengths of these models include:

  1. Microfoundations: DSGE models are based on microeconomic principles and are therefore able to provide a consistent and coherent framework for analyzing macroeconomic phenomena.

  2. Flexibility: DSGE models can be used to analyze a wide range of macroeconomic phenomena, including business cycles, inflation, and growth.

  3. Quantitative: DSGE models are able to generate quantitative predictions about the economy, and forecasts that compare well with other classes of models such as Bayesian VARs.

  4. Policy Analysis: DSGE models can be used to analyze the effects of different policy changes, such as changes in interest rates, government spending or exchange rate regimes.

  5. International aspects: Some DSGE models are able to incorporate international aspects such as trade, currency and capital flows.

RISE extends the constant-parameter DSGE model to allow for regime switching. The regime-switching DSGE model is given by:

\[E_{t}\sum_{r_{t+1}=1}^{h}p_{r_{t},r_{t+1}}\left( \mathcal{I}_{t}\right) f_{r_{t}}\left( x_{t+1}\left( r_{t+1}\right) ,x_{t}\left( r_{t}\right) ,x_{t-1},\theta_{r_{t}},\theta_{r_{t+1}},\varepsilon_{t}\right) =0\]
  • \(p_{r_{t},r_{t+1}}\left( I_{t}\right)\) : probability of going from state \(r_{t}\) in the current period to state \(r_{t+1}\) in the next one

  • \(f_{r_{t}}\): (potentially) nonlinear function of its arguments

  • \(x_{t}\left( r_{t}\right)\) : vector of all endogenous variables in the current regime \(r_{t}\)

  • \(\theta _{r_{t}}\): parameters in the current regime

  • \(\varepsilon _{t}\sim N\left( 0,I\right)\) : vector of stochastic shocks

2.2. An example

See the previous chapter.

2.3. The model(rise/dsge) file language

2.3.1. Conventions

Comments and block comments

RISE supports single line comments %, or //:

% this is a comment

// this is also a comment

RISE supports block comments of the Matlab style %, or //:

%{
This is a block comment
That RISE will ignore
%}

Time and steady state

Our preferred way to denote time uses curly braces. But RISE would also accept parentheses although in that case it becomes difficult to know whether we are referring to a variable or a function. That said,

  • For contemporaneous variables we have : X = X{0} = X{t+0} = X{t} = X(0) = X(t+0) = X(t)

  • For lagged variables we have : X{-1} = X{t-1} = X(-1) = X(t-1)

  • For lead variables we have : X{1} = X{+1} = X{t+1} = X(1) = X(+1) = X(t+1)

  • For the steady state we have : X{stst} = steady_state(X) = X(stst)

In brief, RISE is flexible. Use the notation that you prefer!

Auxiliary variables

RISE automatically creates auxiliary variables so that the maximum lag and the maximum lead for any model is always 1. The user should not worry about these variables.

RISE also handles expectations that appear inside an equation. E.g.

\[Y_{t}=1/E_t\left(X_{t+1}\right)\]

In order to avoid RISE computing

\[Y_{t}=E_t\left(1/X_{t+1}\right)\]

It is the responsibility of the user to create the appropriate auxiliary variable, say, \(A_{t}=E_t\left(X_{t+1}\right)\) and then rewrite the equation as

\[Y{t}=1/A_{t}\]

Or to write the equation differently

\[E_t\left(Y_{t} X_{t+1}\right)=1\]

Or explicitly use the expectations operator by writing the equation as

Y{t} = 1/@E[t](X{t+1})

Note

RISE automatically creates auxiliary variables to deal with

  • leads and lags greater than 1 and

  • switching parameters with a different dating than the current period

  • see the @E section for the expectation operator

Functions

Known functions

RISE supports four basic arithmetical operations (+, -, *, /) as well as powers (^). RISE relies on Matlab for these operations and so one has to be careful with the power and rdivide (/) operators :

  • \({a^b}^c = (a^b)^c\) for a^b^c

  • \(a/b^c = a/(b^c)\) for a/b^c

Naturally, the precedence of arithmetical operators can be changed using parentheses.

The list of functions that RISE knows how to differentiate includes : abs, acos, acosh, and, asin, asinh, atan, atanh, betacdf, betainv, betapdf, char, cos, cosh, cot, eq, erf, exp, ge, get, gt, kron, le, log, log10, lt, max, min, minus, mpower, mrdivide, mtimes, ne, normcdf, norminv, normpdf, or, plus, power, rdivide, sign, sin, sinh, sqrt, tan, tanh, times, uminus, uplus.

External functions

Sometimes, the user may have a function that is unknown to RISE. In that case, the user should write the function such that RISE can evaluate it as well as its derivatives.

The function should be written so that it is possible to call it in two different ways:

x=f(a1,a2,...,an)

x=f(a1,a2,...,an,'diff',der)

where :

  • x is the output of the function

  • a1, a2, …, an : are the main inputs of the function

  • ‘diff’ is an input that signals that triggers the computation of derivatives

  • der is a scalar of or a 1 x k vector specifying the derivatives requested. The number of elements, k, in der is the order of differentiation. The numbers in the vector are the location of the elements to differentiate. E.g:

    • x=f(a1,a2,…,an,’diff’,1) : first-order derivative wrt first argument

    • x=f(a1,a2,…,an,’diff’,[1,2]) : second-order derivative wrt arguments 1 and then 2

    • x=f(a1,a2,…,an,’diff’,[1,2,3]) : third-order derivative wrt arguments 1, 2, and 3

    • x=f(a1,a2,…,an,’diff’,[i,j,k,l,m]) : fifth-order derivative wrt arguments i, k, k, l, and m

It is expected that

  • f(a,’diff’,[i,j])=f(a,’diff’,[j,i])

  • f(a,’diff’,[i,j,k]) = f(a,’diff’,[i,k,j]) = f(a,’diff’,[j,i,k]) = f(a,’diff’,[j,k,i]) = f(a,’diff’,[k,i,j]) = f(a,’diff’,[k,j,i])

  • etc

Discountinous functions

On local approximations

RISE approximates discontinuous functions in the same way it does with continuous functions. As a result, inequality constraints such as max or min will not hold when solving a model using perturbation.

One way to assuage the problem is to replace discontinuous functions with smooth approximations and solve the model using higher-order perturbations. Even so, the inequalities will not necessarily be satisfied.

Another way to deal with the problem is to use algorithms designed for occasionally-binding constraints. In that respect, RISE implements four classes of algorithms :

  1. perfect foresight (and extended path)

  2. piecewise linear approximations

  3. anticipated shocks

  4. regime switching

Danger

There is a widespread belief that projection methods can successfully deal with problems of with discontinuous functions. This is untrue !

Smooth approximations

RISE implements smooth approximations of abs, min and max functions. They can be accessed respectively as :

  • sabs

  • smin

  • smax

Shock distributions

In RISE, all shocks have zero mean and standard deviation 1. By default, they are normally distributed. Skewed shocks or more generally shocks having non-normal distributions are easily accommodated. However, even they should have mean zero and variance 1. Therefore, we will not have variance-covariance matrices of shocks.

The way to handle correlated shocks in RISE is to create auxiliary endogenous variables that are combinations of uncorrelated shocks.

First and second-order moments of shocks

All shocks have mean 0 and standard deviation 1

Higher-order moments of shocks

Skewed shocks are declared in a structure. Each element of the structure represents a particular shock and is given in a 1-by-2 cell array of functions.

  • The first element of the cell array computes moments and takes two inputs : the model object and the moment order

  • The second element of the cell array computes draws from the distribution. It should take as inputs the same inputs as matlab’s rand or randn functions.

Eventually, the structure is passed to the model object through the option solve_user_defined_shocks e.g. m=set(m,’solve_user_defined_shocks’,myshocks)

Here is an example on how to write a function of skewed shocks

function myshocks=online_skewed_shocks(dof)
if nargin==0
    dof=1;
end
myshocks=struct();
momfunE=@(obj,order)moments.tailored_chisquare(order,dof);
tmpfun=@(varargin)if_then_else(length(varargin)>1,[varargin{:}],[1,varargin{1}]);
drawfunE=@(varargin)reshape(...
    (sum(randn(dof,prod([varargin{:}])).^2,1)-dof)/sqrt(2*dof),...
    tmpfun(varargin{:}));
myshocks.E={momfunE,drawfunE};

norm_moments=[0,1,0,3,0,15,0,105,0,945];
momfunOMEGA=@(obj,order)norm_moments(1:order);
drawfunOMEGA=@(varargin)reshape(randn(1,prod([varargin{:}])),tmpfun(varargin{:}));
myshocks.OMEGA={momfunOMEGA,drawfunOMEGA};
end

2.3.2. Ways to declare a model

A model in RISE can be declared in three main ways: using a model file, as an inline snippet embedded in a script, or as a cell array directly in MATLAB.

Declaring a model in a file

A model can be declared in a text file with extensions “.rs”, “.rz”, or “.dsge”. These files contain the full specification of the model, including the endogenous and exogenous variables, parameters, and model equations.

By placing double comments “%%” at the top of the file, followed by a comment, RISE will use that comment as a description of the model.

Declaring a Model as an Inline Snippet

A RISE model can also be declared directly within a MATLAB script as an inline snippet. This is a piece of model code embedded directly in the script using a specific format for defining endogenous variables, parameters, and equations. The snippet is extracted and processed by RISE using a function call.

Example of an inline snippet within a MATLAB script:

m = rise.from_code_snippet('myModel');

%{
\begin{snippet}{myModel}
    @endogenous x
    @parameters alpha
    @model
    x = alpha * x{-1};
\end{snippet}
%}

Or alternatively, using hash rather than backslashes

m = rise.from_code_snippet('myModel');

%{
#begin{snippet}{myModel}
    @endogenous x
    @parameters alpha
    @model
    x = alpha * x{-1};
#end{snippet}
%}

Declaring a model as a Cell Array

You can also declare a model in RISE directly in MATLAB using a cell array. This approach allows you to write the model specification as a structured collection of strings representing different model components, such as variables, equations, and parameters.

Example of declaring a model as a cell array:

simpleModel = {
    '%% model without parameters and shocks',
    '@endogenous x',
    '@model',
    'x=1.05*x{-1};'
};

m = rise(simpleModel);

2.3.3. Handling Multiple Model Specifications

This section outlines various methods to create models in RISE by defining endogenous variables, exogenous variables, parameters, and equations using different formats.

Method 1: Simple Model File

To create a RISE model from a single model file:

m = rise('modelFile');

Or with an explicit extension:

m = rise('modelFile.rs');

Method 2: Multiple Model Files

To create a RISE model from multiple model files:

m = rise({'modelFile1.rs', 'modelFile2.rs', ..., 'modelFilen.rs'});

You can also omit the extensions if the files are already known to have .rs, .rz, or .dsge extensions:

m = rise({'modelFile1', 'modelFile2', ..., 'modelFilen'});

Method 3: Declaring a Model with a Cell Array

To declare a RISE model online using a cell array

m = rise({
    '@endogenous x y z',
    '@parameters alpha beta',
    '@model',
    'x = alpha * y',
    'y = beta * z'
})

Method 4: Multiple Cell Arrays for Different Model Parts

To declare multiple RISE model components using a cell array of cell arrays:

onlineCellstr1 = {
    '@endogenous e',
    '@exogenous x',
    '@model',
    'x = e;'
};

onlineCellstr2 = ... % Another set of model definitions

onlineCellstr3 = {
    '@endogenous(log) C, I'
};

m = rise({onlineCellstr1, onlineCellstr2, onlineCellstr3});

Method 5: Combination of Model Files and Cell Arrays

To create a RISE model by combining model files and cell arrays of model components:

info = struct();
info.disk_files = {'modelFile1.rs', 'modelFile2.rs', ..., 'modelFilen.rs'};
info.online_data = {'online_info1', ..., 'online_infoM'};

% You can also use cell arrays for online data
info.online_data = {onlineCellstr1, onlineCellstr2, ...};

m = rise(info);

This approach allows you to combine model components from both disk-based model files and online specifications written directly in MATLAB.

2.3.4. Atoms declarations

Successive atoms can be separated by a blank spaces, a comma or both.

Each variable and parameter name begins with a letter. RISE is case sensitive and supports Latin alphabet only. Digits and underscores are allowed in names provided they do not come first.

@endogenous

@endogenous is the keyword for declaring the endogenous variables. Each variable name is optionally followed by its description in double quote marks. Example

@endogenous C, I, R "Interest rate", PAI

This block is compulsory!.

The keyword @endogenous(log) can be used to declare variables for which a log-transform will be applied. Example

@endogenous(log) C, I

There can be multiple blocks of @endogenous(log).

@endogenous(log_vars) and @endogenous(log_variables) are accepted aliases.

By default, a log variable is handled by an inline substitution: every occurrence of X in the equations is replaced by exp(X), so the variable that RISE solves for is internally the log and the level is recovered as exp(X).

An alternative treatment is available through the parse-time option explicit_log_levels (default false), set when the model is created:

m = rise('my_model','explicit_log_levels',true);

When true, for each log variable X RISE introduces an explicit linear log-state LOG_X, replaces X with exp(LOG_X) in the structural equations, and adds a single static equation X = exp(LOG_X). The level X then exists as an explicit endogenous variable in its own right, while LOG_X is the variable the perturbation is taken in.

Note

explicit_log_levels is currently supported for stationary models only. A balanced-growth solve (solve_bgp=true) is refused, because a level that grows multiplicatively (X=exp(LOG_X) with LOG_X trending) cannot have a linear solution. Under this option no variable carries the internal log flag; X is a genuine level (so, for example, an observed X is treated as a level, not a log).

Deprecated since version 20260524: The legacy block-level forms @log_variables, @log_vars and @level_variables (with their @all_but complement) are still parsed for backwards compatibility but are deprecated. Use @endogenous for level variables and @endogenous(log) for log variables instead. The two styles cannot be mixed in the same model file.

@exogenous

@exogenous is the keyword for declaring the exogenous variables or shocks. Each variable name is optionally followed by its description in double quote marks. Example

@exogenous EA, EZ "Technology shock", ER "Interest rate", EG

This is only necessary if the model has some exogenous variables.

@parameters

@parameters is the keyword for listing the parameters. Each parameter name is optionally followed by its description in double quote marks. Example

@parameters alpha, beta "discount factor", rho

This is only necessary if there are parameters whose values can change

In RISE, every parameter is controlled by a named Markov process. The name of the process controlling the constant parameters does not appear explicitly. That name cannot be changed, it is const.

In the presence of a regime switching model, @parameters is followed by the name of a markov process and the number of states of the markov process and then the list of the parameters controlled by the markov process. Example

@parameters(volatility,2) sige, siga "volatility of pref. shock", sigz

In the RISE “religion”, we use

  • “state” to denote the modes of a particular markov process. e.g. “volatility” in the code snippet above can be in state 1 or in state 2.

  • “regime” to denote a combination of states from different independent Markov processes. e.g. consider a model with two independent Markov processes : one controlling the behavior of policy (hawkish or dovish) and one controlling the heteroskedasticity of shocks (low volatility or high volatility). We can have

    • a regime of dovish stance of monetary policy and high volatility

    • a regime of hawkish stance of monetary policy and high volatility

    • a regime of dovish stance of monetary policy and low volatility

    • a regime of hawkish stance of monetary policy and low volatility

Parameters for Transition probabilities

Markov processes give rise to transition probabilities. Transition probabilities have a specific syntax. The following rules have to be followed :

  • each transition probability starts with the name of the markov process it caracterizes.

  • each transition probability name must have exactly 3 underscores.

  • It is forbidden to declare diagonal elements of a transition matrix.

  • When transition probabilities are constant, they can be declared as parameters.

  • the general syntax is mcName_tp_i_j, {i,j}=1,2,…, where

    • mcName is the name of Markov process

    • tp stands for transition probability

    • i is the state today

    • j is the state tomorrow

Parameters for Measurement Errors

Just like regular shocks, measurement errors are mean-zero and variance-one processes. However, by default, measurement errors are scaled by zero when not declared. The variability of measurement errors is changed by declaring parameters with a specific syntax. The syntax for declaring measurement error parameters and possibly their correlation is as follows

  • stderr_OBSNAME : scale on measurement error for observed endogenous variable OBSNAME

  • corr_OBSNAME1_OBSNAME2 : correlation of measurement errors on observed endogenous variables OBSNAME1 and OBSNAME2.

Warning

for any observed variable \(X\) declared as log variable, the measurement errored variable is \(X_{obs} = X\exp\left(stderr\_X\times E_{X}\right)\) so that in log terms \(x_{obs} = x + stderr\_X\times E_{X}\).

Parameters for Bounded Rationality

RISE implements bounded rationality through the declaration of special parameters. See the section on tools for bounded rationality for details.

The perturbation parameter perturbator

For any DSGE model, RISE always creates a parameter by default. The name of the parameter is perturbator. This parameter cannot be declared but may be used in the equations of your model. It has the property that in steady state it assumes a value of 0. Otherwise it assumes a value of 1.

Note

  • If the model has time-varying transition probabilities, RISE will automatically transform the equations defining those probabilities to satisfy the requirements of the Maih-Waggoner perturbation should it be used.

  • Likewise, if a model has constant/exogenous transition probabilities and that those transition probabilities are used in the equations of the model, RISE will apply the same scheme to all the occurrences of those exogenous transition probabilities.

  • However, the scheme will only matter if the Maih-Waggoner perturbation strategy is chosen as the perturbation strategy.

@observables

@observables is the keyword for declaring the variables to be observed during filtration. Only declared endogenous or exogenous variables can be observed. Example

@observables PAI, R, Y

Three remarks are in order :

  • Only observed endogenous variables can give rise to measurement error parameters

  • An exogenous variable that is observed is considered deterministic. For instance one can observed a time trend that affect some measurement equation. This gives us the flexibility to accommodate all kinds of exogenous observed variables.

  • time-varying probabilities end up being endogenous variables and so they can be observed.

This block is optional. Necessary only if there is filtration.

Log-approximated variables

Use @endogenous(log) (see @endogenous above) to declare endogenous variables for which the approximation should be taken in logs. By default - i.e. when the variable is listed under a plain @endogenous block - the approximation is taken in levels.

There are a few rules to be aware of when choosing log approximation:

  • log approximation cannot be applied to variables that are zero or negative at the steady state;

  • log approximation is optional for stationary variables with a positive steady state;

  • for nonstationary variables, log approximation is required when the growth rate is computed as \(X_{t}/X_{t-1}\).

See also the computation-of-the-steady-state section for the consequences of this choice.

Deprecated since version 20260524: The legacy block forms @log_variables, @log_vars and @level_variables (optionally with @all_but) are still parsed for backwards compatibility but are deprecated. Prefer @endogenous + @endogenous(log). The two styles cannot be mixed inside the same model file.

On-the-fly atoms declarations

We can also declare certain types of variables or parameters on the fly, i.e. directly in the model block. Each atom is followed by an appostrophe, followed by its type.

  • endogenous : X’e | X’e{t+1} | X’e{t-1} | X’e(t+1) | X’e(t-1) …

  • exogenous : E’x | E’e{t+1} | E’e{t-1} | E’e(t+1) | E’e(t-1) …

  • parameters : theta’p | theta’e{t+1} | theta’e{t-1} | theta’e(t+1) | theta’e(t-1) …

Note

If an atom is dated, the date comes after they type is declared.

2.3.5. The @functions block

This block serves to define carry-around functions that appear in the other blocks such as @model, @steady_state_model @optimization_problem, @parameter_restrictions.

Example

@functions

    Uc(a,b,xc) = 1/sigma*xc - 1/sigma*a + k{t};

    Uh(a,b,xh) = log(psi)-nu*xh+nu*b;

The requirements are as follows:

  • The function should hold in one line and end with a semicolon.

  • The input arguments should be non-dated.

  • the right-hand side can have dates

  • preferably the function should not be called with arguments containing complicated comma structures. For instance in the expression below, if a=minus(plus(e,b),rdivide(c,d)) plus and rdivide will be correctly parsed but minus will not

  • The block can have comments but not equation tags

2.3.6. The @model block

The @model block is compulsory. This is where we declare the model equations. There are three types of equations that can enter the @model block.

Regular equations

E.g.

log(X{t}) = (1-rho)*log(xss) + rho*log(X{t-1}) + sig*E{t};

Equations broken across multiple lines

An Equation can be broken across multiple lines

E.g.

x{t} =
        rho1*x{t-1} +
        rho2*x{t-2} +
        rho3*x{t-3} +
stde*e{t};

As in Matlab, one can add "…" to denote the breaking of lines

E.g.

x{t} = ...
        rho1*x{t-1} + ...
        rho2*x{t-2} + ...
        rho3*x{t-3} + ...
stde*e{t};

As in Matlab, the "…" can be optionally followed by comments with no incidence on the subsequent computations.

E.g.

x{t} = ...
        rho1*x{t-1} + ... lag 1
        rho2*x{t-2} + ... lag 2
        rho3*x{t-3} + ... lag 3
stde*e{t};

The @E operator for expectations

Expectations are not always taken at time t. They can be taken earlier or after. The syntax is simple : @E[s](expression) or @E[t+s](expression) for expectations taken s period ahead (earlier if s is negative). Note that @E[0](expression) = expression, @E[s](y{t}) = y{t} if s>0.

E.g.

c{t}*theta*h{t}^(1+psi)=(@E[1]((1-alpha)*y{t})+@E[-2]((1-alpha)*y{t}))/2;

Equations with auxiliary expressions :"…$[]$…"

E.g.

exp(lb{t})=beta*exp(lb{t+1})*(alpha*exp($s{t+1}+y{t+1}$-k{t})+1-delta);

RISE will treat the term $[]$ as a forward-looking term and create an auxiliary variable to it. The auxiliary variable will have prefix DISCRDOLL. In the example above, the equation above becomes

exp(lb{t})=beta*exp(lb{t+1})*(alpha*exp(DISCRDOLL1{t+1}-k{t})+1-delta);

And the definition of the auxiliary variable is

DISCRDOLL1{t} = s{t} + y{t};

There is no requirement that all variables between $ signs be forward-looking. RISE will always lag the terms correctly when creating the corresponding auxiliary variable.

Hint

Relevance : When solving discretionary or stochastic replanning problems, the derivatives of forward-looking terms enter the equations of the model. To reduced the costs of computation, to the extent that this is feasible, we can gather together several forward-looking variables instead of implicitly creating the derivatives of each one of them separately.

Regular equations with equation simplification :"…#…"

Regular structural equations can optionally be followed by a # sign and then a simplified version of the same equation to be used when computing he steady state or the balanced growth path. E.g.

log(X{t}) = (1-rho)*log(xss) + rho*log(X{t-1}) + sig*E{t} # ...
        X{t}=xss;

Definitions : # Equation

RISE uses the pound sign at the beginning of an equation to declare a definition. This is an atom that is not declared elsewhere. Examples

# beta = alpha + gamma;

# OMG = Y{t}/X{t} + delta;

The rules are as follows :

  • definitions can be functions of variables and parameters

  • if a definition is a function of variables, as is the case for the second definition above, then it has to be systematically substituded.

  • if a definition is a function of parameters only, it does not have to be substituted.

  • If one definition is a function of variables, all definitions have to be substituted

  • If definitions are functions of parameters, they have to be written in a recursive fashion such that one definition is either independent of the previous one or is a function of it. e.g

    # alpha = delta + omicron;
    # varrho = cos(alpha);
    
  • The substitution of definitions is done by setting the option definitions_inserted to true while “rising” the model

    m=rise(...,'definitions_inserted',true)
    

Time-varying transition probabilities : ! Equation

When the transition probabilities are time varying, they can no longer be declared as parameters. They have to be declared in the model block. In order to differentiate them from other equations, we prefix them with an exclamation mark. E.g.

! volatility_tp_1_2 = 1/(1+exp(alpha*X{t}));

! pol_tp_1_2 = R>1;

Caution

time-varying probabilities should always evaluate in [0,1].

Hint

time-varying probabilities end up being endogenous variables and so they can

  • be observed

  • have measurement errors, e.g. stderr_mpeff_tp_1_2, stderr_mpeff_tp_2_1 for some markov process mpeff with transition probabilities mpeff_tp_1_2 and mpeff_tp_2_1

  • be dynamic and used in other equations

Useful cumulative density functions that can be used for transition probablities

  1. arc sine: \(u=\frac{2}{\pi }\sin ^{-1}\left( \sqrt{\frac{x-a}{b-a}}\right)\)

  2. Exponential: \(u=1-\exp \left( -g\left( x-c\right) \right)\)

  3. Gumbel: \(u=\exp \left( -\exp \left( -\frac{x-\mu }{b}\right) \right)\)

  4. Inverse Gaussian: \(u=normcdf\left( \sqrt{\frac{\lambda }{x}}\left(\frac{x}{\mu }-1\right) \right) +\exp \left( \frac{2\lambda }{\mu }\right)normcdf\left( -\sqrt{\frac{\lambda }{x}}\left( \frac{x}{\mu }+1\right)\right)\)

  5. Kumaraswamy: \(u=1-\left( 1-\left( \frac{x-c}{d-c}\right) ^{a}\right)^{b}\) for \(x\in \left[ c,d\right]\)

  6. Logistic: \(u=\frac{1}{1+\exp \left( -g\left( x-c\right) \right) }\) for \(x\in \left( -\infty ,+\infty \right)\)

  7. Log Normal : \(u=0.5\left[ 1+ \operatorname{erf}\left( \frac{\log \left( x\right) -a}{b\sqrt{2}}\right) \right]\) for \(x\in \left( -\infty ,+\infty \right)\)

  8. Normal: \(u=0.5\left[ 1+ \operatorname{erf}\left( \frac{x-a}{b\sqrt{2}}\right) \right]\) for \(x\in \left( -\infty ,+\infty \right)\)

  9. Pareto: \(u=1-\left( \frac{a}{x}\right) ^{b}\) for \(x\in \left[ a,+\infty \right)\)

  10. Raised cosine: \(u=0.5\left[ 1+\frac{x-\mu }{s}+\frac{1}{\pi }\sin\ \left( \frac{\left( x-\mu \right) }{s}\pi \right) \right]\) for \(x\in \left[ \mu -s,\mu +s\right]\) and \(s>0\)

  11. Weibull: \(u=1-\exp \left( -\left( \frac{x}{b}\right) ^{a}\right)\) for \(x\in \left[ 0,+\infty \right)\)

Hint

Note that those functions are monotonically increasing. One way to get them decreasing is to use the complement i.e. 1-u or 1-cdf(x,a,b)

2.3.7. The @optimization_problem block

The @optimization_problem block, formerly known as @planner_objective, is necessary only when solving optimal policy problems. It handles various types of policy, including:

  1. Commitment: With or without regime switching.

  2. Discretion: Linear-quadratic or nonlinear, with or without regime switching.

  3. Loose Commitment: Switching between discretion and commitment with constant probability in a linear-quadratic framework.

  4. Stochastic Replanning: Switching between discretion and commitment with possibly time-varying probabilities in a nonlinear framework.

In these cases, the number of equations should be strictly less than the number of endogenous variables.

Hint

By default, the “solver” option is always empty if not set

  • RISE will check whether the system can be solved by eigenvalue methods. If this is the case, the option will be set to the default RISE solver for constant-parameter models : rise_1

  • else RISE will use the default solver for regime-switching models : mfi

  • Given the way RISE is organized, discretionary policy can be solved using rise_1. This is because the problem can be solved conditional on the knowledge of the solution of the forward-looking variables. In this way, one can in principle, study the indeterminacy properties of a discretionary model.

  • But we believe that insisting that the solution should be determinate is a waste of time. One thing is to say that for a particular parameterization we have a unique solution. Yet another thing is to say that for a solution to be interesting or useful, it has to be determinate. There is no justification for such a stance other than the fact that people sometimes say : “If I have many solutions I don’t know which one to choose.” Note that the only people who would make such complains are the ones solving linear or linearized DSGE models.

  • With that said, the user is nowfree to choose which solver hewants to use. But I am just stressing that the behavior of rise_1, which insists on determinacy is different from that of mfi, which applies functional iteration to find a good solution. Both of those solvers are different from the Newton solvers mn and mnk, which are locally stable around an equilibrium and try to find the solution that is the closest to the starting point. Other solvers are dsge_groebner, which tries to find all possible solutions.

  • Should you want to solve for all possible solutions, there are also dsge_schur and dsge_udc that can do the job but they are not appropriate for this context as they can be computationally taxing.

Generic Syntax

The generic syntax for defining an optimal policy in RISE is

@optimization_problem[options]{core problem settings}

Options:

  • @no_u_turn = true/false : If true, only Ramsey/commitment policies will be computed, and discretion will not be considered. Default is false.

  • @commitment = 1/0 : 1 for Ramsey/commitment, 0 for discretion. Default is @commitment = 1.

  • @markov_process = nameOfMarkovProcess : Specifies the Markov process controlling the game. The default is “const”, meaning there are no switches in the game.

Core Problem Settings:

  • @objective = objective function : The objective function of the policymaker, e.g., log(C{t}).

  • @order = integer : The hierarchy among players in the presence of multiple policymakers. This is optional and defaults to 1.

  • @instrument = variableName : The policy instrument, optional for a single player and mandatory with multiple players.

  • @discount = beta : The player’s discount factor, optional and defaults to 0.99.

  • @alias = aliasName : A shorthand name for the policymaker. This option is discontinued!

  • @constraint = constraint(s) : Constraints to the optimization problem, e.g., R>=1.

    • This basic syntax applies particularly for perfect foresight (or potentially other solution techniques).

    • Multiple constraints are separated by &, e.g., R>=1 & K<2.

    • Inequalities are strictly enforced only under perfect foresight.

    • In a stochastic environment, it maybe useful to transform the problem into a regime-switching one. This is done by adding a regime indicator in front of the constraint as “regime_indicator_m:Inequality_constraints_m”. e.g. gam:R>=1 & delta:K<2. In this case,

    • The regime indicators components are switching parameters such that when they assume value 1, the inequality restrictions (constraint) become binding. Conversely, when they are 0, the constraints do not bind.

    • Internally, a Lagrange multiplier will be associated with the constraints such that a constraint gam:R>=1 written as Lambda.(R-1)=0 becomes (1-gam).Lambda+gam.(R-1)=0.

Caution

Each constraint must be an inequality “<”, “>”, “<=” or “<=”

Attention

Strict inequalities will be treated as loose inequalities.

Error

The inequalities cannot be represented by functions such as “le”, “lt”, “gt”, “ge”.

Error

It is not possible to combine inequalities with regime indicators and inequalities without regime indicators. Either you do perfect foresight or you do regime switching.

Tip

It is possible to define a regime indicator as an endogenous variable instead of a parameter and still use it in a constraint for the purpose of doing perfect foresight.

Tip

In the solving of the steady state, in the absence of an analytical solution it often helps to have a steady state version of the euler equation that gives the solution of the interest rate both in normal times and in abnormal times

RHO = 40/41*RHO(+1)+1/41*(I-PAI(+1))
# (1-gam)*(I-0) + gam*(I-elb) = 0
;

Optimal Policy with No U-Turn

RISE provides the option to switch seamlessly between discretion and commitment, but this flexibility comes at a computational cost. If you intend to solve only for Ramsey/Commitment policies, it is advisable to set the @no_u_turn option to true:

@optimization_problem[@no_u_turn=true, ...]{ @objective = pi_c^2 + y^2, ... }

Setting @no_u_turn to true disables the standard algorithms for solving discretionary or stochastic replanning problems, limiting the solver to loose commitment approaches.

Hint

If @no_u_turn is set to true,

  • the only appropriate solver is “loose_commitment”, which works well in linear-quadratic setups but results in lower accuracy for nonlinear problems.

  • Higher-order perturbation methods cannot be applied under this setting.

Ramsey Policy

To solve for a Ramsey (commitment) policy, the syntax is:

@optimization_problem[@no_u_turn=false, @commitment=1]{
        @objective = pi_c^2 + y^2, @discount = beta }

or, preferably:

@optimization_problem[@no_u_turn=false]{ @objective = pi_c^2 + y^2, @discount = beta }

Then set the model to solve for Ramsey policy:

m = set(m, 'solve_policy_type', 'ramsey');

The following obsolete syntax is still supported but should be avoided:

@optimization_problem {discount=beta} pi_c^2 + y^2;

Discretionary Policy

To solve for a discretionary policy, the syntax is:

@optimization_problem[@no_u_turn=false, @commitment=0]{
        @objective = pi_c^2 + y^2, @discount = beta }

or, preferably:

@optimization_problem[@no_u_turn=false]{ @objective = pi_c^2 + y^2, @discount = beta }

Then set the model to solve for discretionary policy:

m = set(m, 'solve_policy_type', 'discretion');

Hint

By default, RISE will check whether the system can be solved by eigenvalue methods, and if possible, use the default solver rise_1. Otherwise, it will use the mfi solver for regime-switching models.

Loose Commitment

Loose commitment is defined as switching between commitment and discretion with a constant probability in a linear-quadratic system.

@optimization_problem[@no_u_turn=true]{ @objective = pi_c^2 + y^2, @discount = beta }

@parameters(looseCommit, 2) commitment

@parameters looseCommit_tp_1_2, looseCommit_tp_2_1

Alternatively, the probabilities can be declared as endogenous:

@endogenous looseCommit_tp_1_2, looseCommit_tp_2_1

With the following restriction:

looseCommit_tp_1_2 = 1 - looseCommit_tp_2_1;

Note

The name of the Markov process “looseCommit” is arbitrary, but:

  • The number of states must be exactly 2.

  • The “commitment” indicator takes values 1 (commitment) or 0 (discretion).

  • It is up to the user to choose the state in which commitment=1 and the state in which commitment = 0

Hint

For estimation, one way to enforce the restriction looseCommit_tp_1_2 = 1 - looseCommit_tp_2_1 is

  • to declare an auxiliary parameter, say probCom, as non-switching

  • set both looseCommit_tp_1_2 and looseCommit_tp_2_1 as endogenous probabilities i.e.:

    ! looseCommit_tp_1_2 = probCom;
    ! looseCommit_tp_2_1 = 1 - probCom;
    
  • estimate probCom

Stochastic Replanning

In stochastic replanning, the system switches between discretion and commitment with potentially time-varying probabilities.

@optimization_problem[@no_u_turn=false]{ @objective = pi_c^2 + y^2, @discount = beta }

@parameters(stochrepl, 2) commitment

The transition probabilities stochrepl_tp_1_2 and stochrepl_tp_2_1 can be either endogenous or exogenous.

Note

The Markov process “stochrepl” is arbitrary, but the number of states must be exactly 2.

Multiple Policymakers : Non-cooperative games

For multiple policymakers, each player is declared with the following format, where the name of the policymaker is followed by a colon:

Example:

@optimization_problem{
        Monetary_policy: @order = 1, @discount = beta1, @objective = objective1, @instrument = R;
        Fiscal_policy: @order = 3, @discount = beta2, @objective = objective2, @instrument = TAX;
        Macroprudential_policy: @order = 2, @discount = beta3, @objective = objective3, @instrument = Y
}

Note

  • Under Nash bargaining all players move simultaneously

  • Under Stackelberg bargaining there are leaders and followers

  • @order here refers to the hierarchy with 1 > 2 > 3,… etc.

  • Several players can move at the same time

Hint

For non-cooperative games you can additionally choose between two equilibrium concepts at solve time: open-loop equilibrium (OLE, the historical default, each player takes the opponent’s path as given) and Markov-perfect equilibrium (MPE, each player takes the opponent’s policy function as given). Toggle with:

m = solve(m, 'solve_policy_equilibrium', 'OLE');   % default
m = solve(m, 'solve_policy_equilibrium', 'MPE');

See Optimal (Ramsey) policy for the conceptual difference and the small restrictions (stochastic replanning rejects the option; @no_u_turn = true makes the MPE addition a no-op).

Switching Games

RISE also allows the solving of problems in which the game to be played can change from one period to another (e.g., independent monetary and fiscal policy in some states, cooperative in others).

When there is a switch in the type of game being played, several steps must be followed:

  1. Declare the Markov process controlling the switching process as option @markov_process, e.g., @markov_process=buscycl.

  2. Specify the corresponding transition probabilities either as parameters or as endogenous variables if time-varying.

  3. Use @state(istate){sub-core problem} to define the core problem for each state.

Example:

@optimization_problem[@no_u_turn=true, @markov_process=buscycl]{
        @state(1){
            @objective = omega_welf1*UTIL1{t} + omega_welf2*UTIL2{t}, @discount = betap
        }

        @state(2){
            monetary_authority: @objective = UTIL1{t}, @discount = betap,
                        @order = 1, @instrument = INFL;
            financial_regulator: @objective = UTIL2{t}, @discount = betap,
                        @order = 2, @instrument = BT
        }
}

In this example, two states are defined but the number can be arbitrary. In state 1, a single welfare function is optimized, while in state 2, two separate policymakers (monetary authority and financial regulator) optimize their objectives with the monetary authority as leader and the financial regulator as follower.

2.3.8. The @steady_state_model block (optional)

RISE gives you the flexibility of setting the program for solving the steady state or the balanced-growth path of the model either in the model file or outside the model file. The former strategy, presented below, is appealing when the user does not want to create too many files and that the model is small. The latter strategy gives the most flexibility including the possibility of overriding the information set inside the model file.

The @steady_state_model block should compute the steady state (and the balanced-growth path) analytically.In the absence of this block, all variables are assumed to be zero in steady state. Of course, RISE will check whether this is true and try to improve on this if indeed the zero assumption does not hold.

The program will calculate both the variables and the parameters whose values are set in steady state. It is important that the calculation be done in a recursive fashion.

If the model is stationary, a single value is required for each variable.

If the model is nonstationary, each variable should be a 1 x 2 vector in which the first element is the steady state and the second element is the growth rate.

You can also loop around the steady state model conditional on the knowledge of variables not calculated in steady state. This arises typically in optimal policy problems, where for instance the value of, say inflation is not known but the system is such that if we knew the value of inflation we could solve for many other endogenous variables.

Warning

In the steady state model, avoid using temporary variable that correspond to the names of endogenous variables!

Hint

For level nonstationary variables, the growth rate is computed as \(X_{t}-X_{t-1}\) whereas for log nonstationary variables, the growth rate is computes as \(X_{t}/X_{t-1}\)

Hint

The steady state for nonstationary variables is undefined but can be normalized. Suppose, say, we have the following system

\[ \begin{align}\begin{aligned} h_{t}=\frac{1}{3}\\ Y_{t}=Z_{t}h_{t}\\\log \left( \frac{Z_{t}}{Z_{t-1}}\right) = \log \left(\mu_{z}\right) +\sigma _{z}\varepsilon _{z,t}\end{aligned}\end{align} \]

where \(\varepsilon _{z,t}\), \(Y_{t}\) is output, \(Z_{t}\) is the TFP and \(h_{t}\) is the labor input.

Assuming we do not take a log approximation of \(h_{t}\), the RISE steady state is given by

\[ \begin{align}\begin{aligned}h = \left[\frac{1}{3},0\right]\\Z = \left[zss,\mu_{z}\right]\\Y = \left[Z\left(1\right)\times h\left(1\right),Z\left(2\right)\right]\end{aligned}\end{align} \]

where zss is the normalization for the steady state of Z or more precisely the point around which the approximation will be taken.

Hint

Sometimes, part of the steady state cannot be computed analytically. You can write a separate function in an m-file that solves the sub-problem using a root-finding procedure. That function can then be called from within the @steady_state_model block.

Warning

If you write a separate function that solves part of the steady state and that can fail at times for some parameterizations of the model, there is no way to inform RISE of the failure when using a @steady_state_model block. The most flexible way to deal with this is to use a steady-state file.

The alternative to the @steady_state_model block is the steady state file.

2.3.9. The @transition_functions block (optional)

The @transition_functions block is where time-varying transition probabilities (TVTPs) are declared. A TVTP is a transition probability that depends on model variables (and/or parameters), so that the probability of moving between regimes changes over time — for example an endogenous-switching zero-lower-bound or a regime that becomes more likely as a variable approaches a threshold.

Each entry has the form

chain_tp_i_j = expression ;

where chain is the name of a declared Markov chain and i, j index the transition from state i to state j. An optional description in double quotes may precede the entry. Example

@transition_functions

        "probability of leaving the low-rate regime"
        zlb_tp_1_2 = 1 - 1/(1+exp(-0.1*(R-1)));

        zlb_tp_2_1 = 0.3 ;

The Markov chain must be declared, which is done by attaching a switching parameter to it, e.g. @parameters(zlb, 2) some_param. The right-hand side may reference contemporaneous endogenous variables, exogenous variables, parameters, and #-definitions; it must not contain leads or lags. In optimal-policy models the right-hand side may also reference a constraint multiplier through @multiplier(n) (state-dependent switching driven by how binding a constraint is).

Multiple @transition_functions blocks are allowed in the same file and are stitched together by the parser; you can put a chain’s two probabilities in two separate blocks, or interleave them with other declarations, with no change in behaviour.

Solver note. When the right-hand side depends on an endogenous variable, the default functional-iteration solver ('mfi') can fail to converge. Use the Newton-iteration solver explicitly: solve(m,'solver','mn'). For a stable solution keep the transition probabilities strictly inside (0,1) — a common trick is p_ij = p_min + (p_max - p_min)*f(state) with p_min, p_max in (0,1).

Note

Transition-probability names (chain_tp_i_j) must be declared here, not in @endogenous — listing them as endogenous variables is an error. The legacy syntax of writing !chain_tp_i_j = ... inside the @model block is still parsed but deprecated; prefer @transition_functions. A constant transition probability does not need this block: it can simply be a plain parameter.

2.3.10. The @epilogue block (optional)

The @epilogue block declares post-solve derived series — quantities that are not part of the structural model but are computed from the model variables after the model has been solved and simulated (for example annualised inflation, great-ratios, or other transformations you want returned alongside the model’s output). Each entry is an assignment

@epilogue

        annual_inflation = 4*PAI ;

        real_rate = (1+R) / (1+annual_inflation/4) - 1 ;

Unlike the @model block, leads and lags inside @epilogue are interpreted as literal time shifts on the simulated path, not as expectations: annual_inflation{-1} resolves to lag(annual_inflation, 1), annual_inflation{+2} to lead(annual_inflation, 2).

The right-hand side may reference endogenous, exogenous, parameter, and #-definition names, as well as earlier @epilogue entries: RISE topologically sorts the entries before compilation so the order of declaration matters only up to dependency, and mutual or self-references are rejected with a clear error. Multiple @epilogue blocks are stitched together in the same way as @transition_functions.

Epilogue variables never enter the structural solution. Transition-probability (chain_tp_i_j) names are not allowed here — declare those in @transition_functions.

The runtime evaluator is invoked automatically on the simulation, IRF, forecast, filter, perfect-foresight, counterfactual, and brute-force-simulate outputs, so an @epilogue series appears as a regular field on the returned ts struct alongside the model variables.

2.3.11. The @parameterization block (optional)

RISE gives you the flexibility of setting the parameterization of the model either in the model file or outside the model file. The former strategy, presented below, is appealing when the user does not want to create too many files and that the model is small. The latter strategy gives the most flexibility including the possibility of overriding the information set inside the model file.

Inside the model file, the parameterization is set under the “@parameterization” block. This concerns both the parameters that are fixed and the parameters that are to be estimated. A parameter cannot be repeated and the start value for estimation will also serve as the baseline calibration, should the parameter not be estimated later on.

For fixed parameters, the syntax is

paramName, value ;

paramName = value ;

For estimated parameters, the syntax is

paramName, start_value, arg1, arg2,...,argn ;

paramName = start_value, arg1, arg2,...,argn ;

where arg1, arg2,…,argn refers to additional arguments. See the section on priors for detail. Example

rho, 0.75, 0.4, 0.9, beta(0.9);

rho = 0.75, 0.4, 0.9, beta(0.9);

Switching parameters are written as

sigz(vol,1)
sigz(vol,2)

or as

sigz_vol_1
sigz_vol_2

2.3.12. The @parameter_restrictions block (optional)

The @parameter_restrictions block sets the restrictions to be enforced during estimation

The syntax is

@parameter_restrictions
        f1(theta)>=f2(theta);
        ...
        h1(theta)<=h2(theta);

This can also be set outside the model file. See e.g. the chapter on estimation.

2.3.13. Tools for bounded rationality (Non-rational expectations)

Hybrid Expectations

The key equations in a hybrid expectations system are the following (see e.g. [Gelain et al., 2013]):

\[ \begin{align}\begin{aligned}F_{t}X_{t+1} = F_{t-1}X_{t} + \lambda\left(X_t-F_{t-1}X_t\right)\\E_t^{he}X_{t+1} = \omega F_t X_{t+1} + \left(1-\omega\right)E_t X_{t+1}\end{aligned}\end{align} \]

where \(0<\lambda\le 1\) and \(0\le\omega\le 1\).

RISE implements Hybrid expectations through the declaration of the following two parameters :

  • hbe_w : weight on adaptive (or backward-looking ) expectations

  • hbe_lambda : weight on current relative to past in backward-looking expectations

Cognitive discounting

RISE implements cognitive discounting a-la Gabaix in two distinct ways:

  1. Through the creation of parameter names of the form “mcd_VARIABLENAME” where VARIABLENAME is the name of an endogenous variable present in the model

  2. Through the declaration of list of endogenous variables to which one wants to apply cognitive discounting. e.g. @cognitive_discount VARIABLE1, VARIABLE2,… In this case, RISE will create the corresponding cognitive discounting parameters “mcd_VARIABLE1”, “mcd_VARIABLE2”, …

This way of setting cognitive discounting can be applied to nonlinear and regime-switching models and in that case, the degree of discounting can change from one regime to another.

One way to apply regime switching to cognitive discounting parameters is just to add them to the list of parameters controlled by a particular markov process. But another way to do the same using the second approach above is just to add the markov process to the declaration block. e.g

@cognitive_discount(MCCHAIN,nstates) VARIABLE1, VARIABLE2,...

2.3.14. Macro Language for preparsing

Inclusion of files

@include(filename)
@include "filename"
@import(filename)
@import "filename"

Pseudo functions

Without arguments, the function returns the list of model keywords

Moving averages

movave(X,n) = movavg(X,n)
movavg(X,n) = (X{t} + X{t-1} + X{t-2} + ... + X{t-n+1})/abs(n)
movave(X) = movavg(X) = movave(X,-4)

Moving products

movprod(X,n) = prod(X{t},X{t-1},X{t-2},...X{t-n+1})
movprod(X) = movprod(X,-4)

Geometric averages

movgeom(X,n) = prod(X{t},X{t-1},X{t-2},...X{t-n+1})^(1/abs(n))
movgeom(X) = movgeom(X,-4)

Moving sum:

movsum(X,n) = X{t} + X{t-1} + X{t-2} + ... + X{t-n+1}
movsum(X) = movsum(X,-4)

Differences:

diff(X,n) = X{t} - X{t+n}
diff(X) = diff(X,-1)

log differences

difflog(X,n) = log(X{t}) - log(X{t+n})
difflog(X) = difflog(X-1)

dot products

dot(X,n) = X{t}/X{t+n}
dot(X) = dot(X,-1)

concatenation

concat(x,'_ss') = x_ss

Control flow : for loops

@for c=[1,4,6]
        Y_@{c}=rho_@{c}*Y_@{c}{-1}+sig_@{c}*E_@{c};
@end

will produce

Y_1=rho_1*Y_1{-1}+sig_1*E_1;
Y_4=rho_4*Y_4{-1}+sig_4*E_4;
Y_6=rho_6*Y_6{-1}+sig_6*E_6;
@for c=1:3
        Y_@{c}=rho_@{c}*Y_@{c}{-1}+sig_@{c}*E_@{c};
@end

will produce

Y_1=rho_1*Y_1{-1}+sig_1*E_1;
Y_2=rho_2*Y_2{-1}+sig_2*E_2;
Y_3=rho_3*Y_3{-1}+sig_3*E_3;
@for c={'','_f'}
        Y@{c}=rho@{c}*Y@{c}{-1}+sig@{c}*E@{c};
@end

will produce

Y=rho*Y{-1}+sig*E;
Y_f=rho_f*Y_f{-1}+sig_f*E_f;
@for c={'ca','fr','de','be'}
   Y_@{c}=rho_@{c}*Y_@{c}{-1}+sig_@{c}*E_@{c};
@end

will produce

Y_ca=rho_ca*Y_ca{-1}+sig_ca*E_ca;
Y_fr=rho_fr*Y_fr{-1}+sig_fr*E_fr;
Y_de=rho_de*Y_de{-1}+sig_de*E_de;
Y_be=rho_be*Y_be{-1}+sig_be*E_be;

control flows : zipped for loops

@for (aa,bb)=zip({'S','I','R'},{'Susceptible','Infected','Recovered'})
        BIG_U@{aa} "welfare @{bb}", C@{aa} "consumption (@{bb})"
@end

will produce

BIG_US "welfare Susceptible", CS "consumption (Susceptible)"
BIG_UI "welfare Infected", CI "consumption (Infected)"
BIG_UR "welfare Recovered", CR "consumption (Recovered)"

control flows : enumerated for loops

@for (aa,bb)=enumerate({'Susceptible','Infected','Recovered'})
   BIG_U@{aa} "welfare @{bb}", C@{aa} "consumption (@{bb})"
@end

will produce

BIG_U1 "welfare Susceptible", C1 "consumption (Susceptible)"
BIG_U2 "welfare Infected", C2 "consumption (Infected)"
BIG_U3 "welfare Recovered", C3 "consumption (Recovered)"

Evaluations

@{term to be evaluated} where the term to be evaluated is not an index

@for c=[1,4,6,8]
        Y_@{c}=rho_@{3*@{c}+1}*Y_@{c}{@{-1+3}}+sig_@{c}*E_@{c};
@end

will produce

Y_1=rho_4*Y_1{2}+sig_1*E_1;
Y_4=rho_13*Y_4{2}+sig_4*E_4;
Y_6=rho_19*Y_6{2}+sig_6*E_6;
Y_8=rho_25*Y_8{2}+sig_8*E_8;

Control flow : @if … @elseif … @else … @end

@if strcmp(Policy,'Taylor rule')
   R{t}=rho*R{t-1}+(1-rho)*[psi_pai*PAI+psi_y*Y]+sigr*ER;
@if strcmp(Policy,'Money growth rule')
   DM{t}=rhom*DM{t-1}+(1-rhom)*[-delta_pai*PAI-delta_y*Y]+sigm*EM;
@else
   omgr*R{t}+omgm*DM{t}=omgr1*R{t-1}+omgm1*DM{t-1}+omg_pai*PAI+omg_y*Y+sigrm*ERM;
@end

Control flow : @switch …@case … @otherwise … @end

@switch Policy
@case 'Taylor rule'
   R{t}=rho*R{t-1}+(1-rho)*[psi_pai*PAI+psi_y*Y]+sigr*ER;
@case 'Money growth rule'
   DM{t}=rhom*DM{t-1}+(1-rhom)*[-delta_pai*PAI-delta_y*Y]+sigm*EM;
@otherwise
   omgr*R{t}+omgm*DM{t}=omgr1*R{t-1}+omgm1*DM{t-1}+omg_pai*PAI+omg_y*Y+sigrm*ERM;
@end

lower and upper cases

@for cc = {'cmr','rca','cng'}
    YSTAR_CEMAC_@{cc} ='
    @for cc2 = {'CMR','RCA','CNG'}
        @if ~strcmp('@{cc2}','@{cc}')
            + w_@:{cc}_@.{cc2}_5*YGAP_@{cc2}
        @end
    @end
    ;
@end

    will produce

    YSTAR_CEMAC_cmr =
    + w_CMR_cmr_5*YGAP_CMR
    + w_CMR_rca_5*YGAP_RCA
    + w_CMR_cng_5*YGAP_CNG
    ;
    YSTAR_CEMAC_rca =
    + w_RCA_cmr_5*YGAP_CMR
    + w_RCA_rca_5*YGAP_RCA
    + w_RCA_cng_5*YGAP_CNG
    ;
    YSTAR_CEMAC_cng =
    + w_CNG_cmr_5*YGAP_CMR
    + w_CNG_rca_5*YGAP_RCA
    + w_CNG_cng_5*YGAP_CNG
    ;