The DSGE.jl package implements the FRBNY DSGE model and provides general code to estimate many user-specified DSGE models. The package is introduced in the Liberty Street Economics blog post The FRBNY DSGE Model Meets Julia.
This Julia-language implementation mirrors the MATLAB code included in the Liberty Street Economics blog post The FRBNY DSGE Model Forecast.
FRBNY is currently working on extending the code to include forecasts and other features. Extensions of the DSGE model code may be released in the future at the discretion of FRBNY.
DSGE.jl is an object-oriented approach to solving the FRBNY DSGE model that takes advantage of Julia's type system, multiple dispatch, package-handling mechanism, and other features. A single model object centralizes all information about the model's parameters, states, equilibrium conditions, and settings in a single data structure. The model object also keeps track of file locations for all I/O operations.
The following objects define a model:
- Parameters: Have values, bounds, fixed-or-not status, priors. An
instance of the
AbstractParameter
type houses all information about a given parameter in a single data structure. - States: Mappings of names to indices (e.g.
π_t
➜ 1). - Equilibrium Conditions: A function that takes parameters and model
indices, then returns
Γ0
,Γ1
,C
,Ψ
, andΠ
(which fully describe the model in canonical form). - Measurement Equation: A function mapping states to observables.
These are enough to define the model structure. Everything else is essentially a function of these basics, and we can solve the model and forecast observables via the following chain:
- Parameters + Model Indices + Equilibrium conditions ➜ Transition matrices in state-space form
- Transition matrices + Data ➜ Estimated parameter values
- Estimated parameters + Transition matrices + Data ➜ Forecast (not yet implemented)
To run the estimation step in Julia, simply create an instance of the model object and pass
it to the estimate
function -- see an example.
# construct a model object
m = Model990()
# estimate as of 2015-Q3 using the default data vintage from 2015 Nov 27
m <= Setting(:data_vintage, "151127")
m <= Setting(:date_mainsample_end, quartertodate("2015-Q3"))
# reoptimize parameter vector, compute Hessian at mode, and full posterior
# parameter sampling
estimate(m)
# produce LaTeX tables of parameter moments
compute_moments(m)
By default, the estimate
routine loads the dataset, reoptimizes the initial parameter
vector, computes the Hessian at the mode, and conducts full posterior parameter sampling.
(The initial parameter vector used is specified in the model's constructor.)
To use updated data or alternative user-specified datasets, see Input Data.
The user may want to avoid reoptimizing the parameter vector and calculating the Hessian matrix at this new vector. Please see Reoptimizing below.
For more details on changing the model's default settings, parameters, equilibrium conditions, etc., see Advanced usage.
The DSGE.jl estimation uses data files as input and produces large data files as outputs. One estimation saves several GB of parameter draws and related outputs. It is useful to understand how these files are loaded/saved and how to control this behavior.
The following subdirectory tree indicates the default locations of these input and outputs. Square brackets indicate directories in the tree that will become relevant as future features are implemented.
-
<dataroot>/
: Root data directory.data/
: Macroeconomic input data series.cond/
: Conditional data, i.e. "nowcast".user/
: User-created or sample model input files.
-
<saveroot>/
: Root save directory.output_data/
m990/
: Input/output files for theModel990
type. A model of typeSPEC
will create its own save directorySPEC/
at this level in the directory tree.ss0/
: Subdirectory for subspec 0. A model of a different subspec will have similar directories at this level of the tree.estimate/
figures/
: Plots and other figurestables/
: LaTeX tablesraw/
: Raw output data from estimation stepwork/
: Derived data files created usingraw/
files as input
- [
xxx/
]: Other model outputs, such as forecasts, impulse response functions, and shock decompositions; subdirectory structure mirrors that ofestimate
.
By default, input/output directories are located in the DSGE.jl package, along with the source code. Default values of the input/output directory roots:
saveroot(m)
:"$(Pkg.dir())/DSGE/save"
dataroot(m)
:"$(Pkg.dir())/DSGE/save/input_data"
Note these locations can be overridden as desired. See Settings below for more details.
m <= Setting(:saveroot, "path/to/my/save/root")
m <= Setting(:dataroot, "path/to/my/data/root")
Utility functions are provided to create paths to input/output files. These should be used for best results.
inpath
: Return path to directory/file for input data, input conditional data, and user-provided sample files, respectively. See?inpath
for more details.- Try
inpath(m, "data")
, a helpful call that displays the containing directory of input data files.
- Try
rawpath
: Return path to directory/file for a given output type for raw model output. See?rawpath
for more details.workpath
: Return path to directory/file for a given output type for transformed or intermediate model output. See?workpath
for more details.tablespath
: Return path to directory/file for a given output type for results tables or other textual results.figurespath
: Return path to directory/file for a given output type for results figures.
The package directory structure follows Julia module conventions.
doc/
: Code and model documentation.save/
: Sample input files; default input/output directories.src/
DSGE.jl
: The main module file.abstractdsgemodel.jl
: Defines theAbstractModel
type.parameters.jl
: Implements theAbstractParameter
type and its subtypes.settings.jl
: Implements theSetting
type.defaults.jl
: Specifies defaultSetting
s.distributions_ext.jl
: DSGE-specific extensions ofDistributions
functionality.data/
: Manipulating and updating input dataset.solve/
: Solving the model; includesgensys.jl
code.estimate/
: Optimization, posterior sampling, and other functionality.- [
xxx/
]: Other model functionality, such as forecasts, impulse response functions, and shock decompositions. models/
-m990/
: Contains code to define and initialize version 990 of the FRBNY DSGE model. -m990.jl
: Constructs aModel990
object. -eqcond.jl
: ConstructsModel990
equilibrium condition matrices -measurement.jl
: ConstructsModel990
measurement equation matrices. -subspecs.jl
: Code for model sub-specifications is defined here. See Editing or Extending a Model for details on constructing model sub-specifications. -augment_states.jl
: Code for augmenting the state space system after model solution. - [m991/
]: Code for new models should be kept in directories at this level in the directory tree
test/
: Module test suite.
Generally, the user will want to reoptimize the parameter vector (and consequently, calculate the Hessian at this new mode) every time they conduct posterior sampling; that is, when:
- the input data are updated with a new quarter of observations or revised
- the model sub-specification is changed
- the model is derived from an existing model with different equilibrium conditions or measurement equation.
This behavior can be controlled more finely.
Reoptimize the model starting from the parameter values supplied in use in a specified file.
Ensure that you supply an HDF5 file with a variable named params
that is the correct
dimension and data type.
m = Model990()
params = load_parameters_from_file(m, "path/to/parameter/file.h5")
update!(m, params)
estimate(m)
You can provide a modal parameter vector and optionally a Hessian matrix calculated at that mode to skip the reoptimization entirely. These values are usually computed by the user previously.
You can skip reoptimization of the parameter vector entirely.
m = Model990()
specify_mode!(m, "path/to/parameter/mode/file.h5")
estimate(m)
The specify_mode!
function will update the parameter vector to the mode and skip
reoptimization by setting the reoptimize
model setting. Ensure that you supply an HDF5
file with a variable named params
that is the correct dimension and data type. (See also
the utility function load_parameters_from_file
.)
You can additionally skip calculation of the Hessian matrix entirely.
m = Model990()
specify_mode!(m, "path/to/parameter/mode/file.h5")
specify_hessian(m, "path/to/Hessian/matrix/file.h5")
estimate(m)
The specify_hessian
function will cause estimate
to read in the Hessian matrix rather
than calculating it directly. Ensure that you supply an HDF5 file with a variable named
hessian
that is the correct dimension and data type. Specifying the Hessian matrix but
not the parameter mode results in undefined behavior.
There are many computational settings that affect how the code runs without affecting the mathematical definition of the model.
Below, we describe several important settings for package usage.
For more details on implementation and usage of settings, see Settings.
See defaults.jl for the complete description of default settings.
dataroot
: The root directory for model input data.saveroot
: The root directory for model output.use_parallel_workers
: Use available parallel workers in computaitons.data_vintage
: Data vintage identifier, formattedyymmdd
. By default,data_vintage
is set to today's date. It is (currently) the only setting printed to output filenames by default.
date_presample_start
: Start date of pre-sample.date_mainsample_start
: Start date of main sample.date_zlbregime_start
: Start date of zero lower bound regime.date_mainsample_end
: End date of main sample.date_forecast_start
: Start date of forecast period.date_forecast_end
: End date of forecast period.
n_anticipated_shocks
: Number of anticipated policy shocks.n_anticipated_shocks_padding
: Padding for anticipated shocks.
reoptimize
: Whether to reoptimize the posterior mode. Iftrue
(the default),estimate()
begins reoptimizing from the model object's parameter vector.calculate_hessian
: Whether to compute the Hessian. Iftrue
(the default),estimate()
calculates the Hessian at the posterior mode.
n_mh_simulations
: Number of draws from the posterior distribution per block.n_mh_blocks
: Number of blocks to run Metropolis-Hastings.n_mh_burn
: Number of blocks to discard as burn-in for Metropolis-Hastings.mh_thin
: Metropolis-Hastings thinning step.
The function get_setting(m::AbstractModel, s::Symbol)
returns the value of the setting s
in m.settings
. Some settings also have explicit getter methods that take only the model
object m
as an argument. Note that not all are exported.
- I/O:
saveroot(m)
,dataroot(m)
,data_vintage(m)
,
- Parallelization:
use_parallel_workers(m)
- Estimation:
reoptimize(m)
,calculate_hessian(m)
,
- Metropolis-Hastings:
n_mh_blocks(m)
,n_mh_simulations(m)
,n_mh_burn(m)
,mh_thin(m)
To overwrite default settings added during model construction, a user must define a new
Setting
object and update the corresponding entry in the model's settings
dictionary
using the <=
syntax. If the print
, code
, and description
fields of the new Setting
object are not provided, the fields of the existing setting will be maintained. If new
values for print
, code
, and description
are specified, and if these new values are
distinct from the defaults for those fields, the fields of the existing setting will be
updated.
For example, overwriting use_parallel_workers
should look like this:
m = Model990()
m <= Setting(:use_parallel_workers, true)
Users may want to extend or edit Model990
in a number of different ways. The most common
changes are listed below, in decreasing order of complexity:
- Add new parameters
- Modify equilibrium conditions or measurement equations
- Change the values of various parameter fields (i.e. initial
value
,prior
,transform
, etc.) - Change the values of various computational settings (i.e.
reoptimize
,n_mh_blocks
)
Points 1 and 2 often go together (adding a new parameter guarantees a change in equilibrium
conditions), and are such fundamental changes that they increment the model specification
number and require the definition of a new subtype of AbstractModel
(for instance,
Model991
). See Model specification for more details.
Any changes to the initialization of preexisting parameters are defined as a new model sub-specification, or subspec. While less significant than a change to the model's equilibrium conditions, changing the values of some parameter fields (especially priors) can have economic significance over and above settings we use for computational purposes. Parameter definitions should not be modified in the model object's constructor. First, incrementing the model's sub-specification number when parameters are changed improves model-level (as opposed to code-level) version control. Second, it avoids potential output filename collisions, preventing the user from overwriting output from previous estimations with the original parameters. The protocol for defining new sub-specifications is described in Model sub-specifications.
Overriding default settings is described in the Settings section above.
A particular model, which corresponds to a subtype of AbstractModel
, is defined as a set
of parameters, equilibrium conditions (defined by the eqcond
function) and measurement
equations (defined by the measurement
function). Therefore, the addition of new
parameters, states, or observables, or any changes to the equilibrium conditions or
measurement equations necessitate the creation of a new subtype of AbstractModel.
To create a new model object, we recommend doing the following:
-
Duplicate the
m990
directory within the models directory. Name the new directorymXXX.jl
, whereXXX
is your chosen model specification number or string. Renamem990.jl
in this directory tomXXX.jl
. -
In the
mXXX/
directory, change all references toModel990
toModelXXX
. -
Edit the
m990.jl
,eqcond.jl
, andmeasurement.jl
files as you see fit. If adding new states, equilibrium conditions, shocks, or observables, be sure to add them to the appropriate list ininit_model_indices
. -
Open the module file,
src/DSGE.jl
. AddModelXXX
to the list of functions to export, and include each of the files insrc/model/mXXX
.
Model990
sub-specifications are initialized by overwriting initial parameter definitions
before the model object is fully constructed. This happens via a call to init_subspec
in
the Model990
constructor. (Clearly, an identical protocol should be followed for new model
types as well.)
To create a new sub-specification (e.g., subspec 1) of Model990
, edit the file
src/models/subspecs.jl
as follows (note that this example is not actually
sub-specification 1
of Model990
. In the source code, our sub-specification 5
is
provided as additional example.):
-
Define a new function,
ss1
, that takes an object of typeModel990
(notAbstractModel
!) as an argument. In this function, construct new parameter objects and overwrite existing model parameters using the<=
syntax. For example,function ss1(m::Model990) m <= parameter(:ι_w, 0.000, (0.0, .9999), (0.0,0.9999), DSGE.Untransformed(), Normal(0.0,1.0), fixed=false, description="ι_w: Some description.", tex_label="\\iota_w") m <= parameter(:ι_p, 0.0, fixed=true, description= "ι_p: Some description" tex_label="\\iota_p") end
-
Add an
elseif
condition toinit_subspec
:... elseif subspec(m) == "ss1" return ss1(m) ...
To construct an instance of Model990
, ss1
, call the constructor
for Model990
with ss1
as an argument. For example,
```julia
m = Model990("ss1")
```
Given all of the hard work put into specifying the model, one should be able to maintain the input data painlessly. To that extent, DSGE.jl provides facilities to download appropriate vintages of data series from FRED (Federal Reserve Economic Data).
Note that a sample input dataset for use with model m990
is provided; see Sample input
data for more details. To update this sample dataset for use with
model m990
, see Update sample input data.
To take advantage of the ability to automatically download data series from FRED via the FredData.jl package, set up your FRED API access by following the directions here.
At the most basic, loading data looks like this:
m = Model990()
df = load_data(m)
By default, load_data
will look on the disk first to see if an appropriate vintage of data
is already present. If data on disk are not present, or if the data are invalid for any
reason, a fresh vintage will be downloaded from FRED and merged with the other data sources
specified. See ?load_data
for more details.
The resulting DataFrame df
contains all the required data series for this model, fully
transformed. The first row is given by the Setting date_presample_start
and the last row
is given by date_mainsample_end
. The first n_presample_periods
rows of df
are the
presample.
Driver functions including estimate
accept this df
as an argument and convert it into a
Matrix
suitable for computations using df_to_matrix
, which sorts the data, ensures the
full sample is present, discards the date column, and sorts the observable columns according
to the observables
field of the model object.
Some data series may not be available from FRED or one may simply wish to use a different
data source, for whatever reason. The data sources and series are specified in the
data_series
field of the model object. For each data source that is not :fred
, a
well-formed CSV of the form <source>_<yymmdd>.csv
is expected in the directory indicated
by inpath(m, "data")
. For example, the following might be the contents of a data source
for two series :series1
and :series2
:
date,series1,series2
1959-06-30,1.0,NaN
1959-09-30,1.1,0.5
etc.
Note that quarters are represented by the date of the last day of the quarter and missing
values are specified by NaN
.
Let's consider an example dataset comprised of 10 macro series sourced from FRED and one survey-based series sourced from, say, the Philadelphia Fed's Survey of Professional Forecasters via Haver Analytics:
julia> m.data_series
Dict{Symbol,Array{Symbol,1}} with 2 entries:
:spf => [:ASACX10]
:fred => [:GDP, :PCE, ...] #etc
If the data vintage specified for the model is 151127
(Nov. 27, 2015), then the following
files are expected in inpath(m, "data")
:
spf_151127.csv
fred_151127.csv
The FRED series will be downloaded and the fred_151127.csv
file will be automatically
generated, but the spf_151127.csv
file must be manually compiled as shown above:
date,ASACX10
1991-12-31,4.0
etc.
Now, suppose that we set the data vintage to 151222
, to incorporate the BEA's third
estimate of GDP. The fred_151222.csv
file will be downloaded, but there are no updates to
the SPF dataset during this period. Regardless, the file spf_151222.csv
must be present to
match the data vintage. The solution in this case is to manually copy and rename the older
SPF dataset. Although this is not an elegant approach, it is consistent with the concept of a
vintage as the data available at a certain point in time --- in this example, it just so
happens that the SPF data available on Nov. 27 and Dec. 22 are the same.
Many variables enter the model in per-capita terms. To that extent, we use data on population levels to adjust aggregate variables into per-capita variables. Furthermore, we apply the Hodrick-Prescott filter ("H-P filter") to the population levels to smooth cyclical components.
The user will ultimately want to produce forecasts of key variables such as GDP and then represent these forecasts in standard terms. That is, one wants to report GDP forecasts in aggregate terms, which is standard, rather than per-capita terms. To do this, we either extrapolate from the last periods of population growth in the data, or use external population forecasts.
Note that if external population forecasts are provided, non-forecast procedures, such as model estimation, are also affected because the H-P filter smoothes back from the latest observation.
To incorporate population forecasts,
- Set the model setting
use_population_forecast
totrue
. - Provide a file
population_forecast_<yymmdd>.csv
toinpath(m, "data")
. Population forecasts should be in levels, and represent the same series as given by thepopulation_mnemonic
setting (defaults to:CNP16OV
, or "Civilian Noninstitutional Population, Thousands"). If your population forecast is in growth rates, convert it to levels yourself. The first row of data should correspond to the last period of the main sample, such that growth rates can be computed. As many additional rows of forecasts as desired can be provided.
The file should look like this:
date,POPULATION
2015-12-31,250000
2016-03-31,251000
etc.
Let's quickly walk through the steps DSGE.jl takes to create a suitable dataset.
First, a user provides a detailed specification of the data series and transformations used for their model.
- the user specifies
m.observables
; the keys of this dictionary name the series to be used in estimating the model. - the user specifies
m.data_series
; the keys of this dictionary name data sources, and the values of this dictionary are lists of mnemonics to be accessed from that data source. Note that these mnemonics do not correspond to observables one-to-one, but rather are usually series in levels that will be further transformed. - the user specifies
m.data_transforms
; the keys of this dictionary name the series to be constructed and match the keys ofm.observables
exactly; the values of this dictionary are functions that operate on a single argument (levels
) which is a DataFrame of the series specified inm.data_series
. These functions return a DataArray for a single series. These functions could do nothing (e.g. returnlevels[:, :SERIES1]
) or perform a more complex transformation, such as converting to one quarter percent changes or adjusting into per-capita terms. - the user adjusts data-related settings, such as
data_vintage
,dataroot
,date_presample_start
,date_mainsample_end
, anddate_zlbregime_start
, anduse_population_forecast
.
Second, DSGE.jl attempts to construct the dataset given this setup through a call to
load_data
. See ?load_data
for more details.
- Intermediate data in levels are loaded. See
?load_data_levels
for more details. - Transformations are applied to the data in levels. See
?transform_data
for more details. - The data are saved to disk. See
?save_data
for more details.
Given the complexity of the data download, you may find that the dataset generated by
load_data
is not exactly as you expect. Here are some common pitfalls to look out for:
- Ensure that the
data_vintage
model setting is as you expect. (Try checkingdata_vintage(m)
.) - Ensure that the
date_mainsample_end
model setting is as you expect, and that is not logically incompatible withdata_vintage
. - Ensure that the
data_series
field of the model object is set as expected. - Double check the transformations specified in the
data_transforms
field of the model object. - Ensure that the keys of the
observables
anddata_transforms
fields of the model object match. - Check the input files for non-FRED data sources. They should be
in the directory indicated by
inpath(m, "data")
, be named appropriately given the vintage of data expected, and be formatted appropriately. One may have to copy and rename files of non-FRED data sources to match the specified vintage, even if the contents of the files would be identical. - Look for any immediate issues in the final dataset saved (
data_<yymmdd>.csv
). If a data series in this file is allNaN
values, then likely a non-FRED data source was not provided correctly. - Ensure that the column names of the data CSV match the keys of the
observables
field of the model object. - You may receive a warning that an input data file "does not contain the entire date range
specified". This means that observations are not provided for some periods in which the
model requires data. This is perfectly okay if your data series starts after
date_presample_start
.
If you experience any problems using FredData.jl, ensure your API key is provided correctly and that there are no issues with your firewall, etc. Any issues with FredData.jl proper should be reported on that project's page.
For more details on the sample input data provided -- which is used to estimate the provided
model m990
, please see Data.
For more details on using market interest rate expectations to treat the zero lower bound, see Anticipated Policy Shocks. In particular, note that our model, as used to compute the forecasts referenced in Liberty Street Economics posts, is trained on data that includes six quarters of interest rate expectations. The user is responsible for procuring interest rate expectations and appending it to the provided sample data set, as discussed in the linked documentation here.
A sample dataset is provided for the 2015 Nov 27 vintage. To update this dataset:
-
See above to setup automatic data pulls using FredData.jl.
-
Specify the exact data vintage desired:
julia> m <= Setting(:data_vintage, "yymmdd")
-
Create data files for the non-FRED data sources (specified in
m.data_series
). For modelm990
, the required data files includespf_<yymmdd>.csv
(with columnASACX10
),longrate_<yymmdd>.csv
(with columnFYCCZA
), andfernald_<yymmdd>.csv
(with columnsTFPJQ
andTFPKQ
). To include data on expected interest rates, the fileois_<yymmdd>.csv
is also required. To include data on population forecasts, the filepopulation_forecst_<yymmdd>.csv
is also required. See Data for details on the series used and links to data sources. -
Run
load_data(m)
; series from FRED will be downloaded and merged with the series from non-FRED data sources that you have already created. See Common pitfalls for some potential issues.
This section describes important functions and implementation features in greater detail. If the user is interested only in running the default model and reproducing the estimation results, this section can be ignored. Additional documentation can also be found in function documentation or in-line.
This section focuses on what the code does and why, while the code itself (including comments) provides detailed information regarding how these basic procedures are implemented.
The AbstractModel
type provides a common interface for all model objects,
which greatly facilitates the implementation of new model specifications. Any
concrete subtype of AbstractModel
can be passed to any function defined for
AbstractModel
, provided that the concrete type has the fields that the
function expects to be available.
Model990
is one example of a concrete subtype of AbstractModel
that
implements a single specification of the FRBNY DSGE model.
See Editing or Extending a Model.
parameters::Vector{AbstractParameter}
: Vector of all time-invariant model parameters.steady_state::Vector
: Model steady-state values, computed as a function of elements ofparameters
.keys::Dict{Symbol,Int}
: Maps human-readable names for all model parameters and steady-states to their indices inparameters
andsteady_state
.
endogenous_states::Dict{Symbol,Int}
: Maps each state to a column in the measurement and equilibrium condition matrices.exogenous_shocks::Dict{Symbol,Int}
: Maps each shock to a column in the measurement and equilibrium condition matrices.expected_shocks::Dict{Symbol,Int}
: Maps each expected shock to a column in the measurement and equilibrium condition matrices.equilibrium_conditions::Dict{Symbol,Int}
: Maps each equilibrium condition to a row in the model's equilibrium condition matrices.endogenous_states_augmented::Dict{Symbol,Int}
: Maps lagged states to their columns in the measurement and equilibrium condition equations. These are added aftergensys
solves the model.observables::Dict{Symbol,Int}
: Maps each observable to a row in the model's measurement equation matrices.
spec::ASCIIString
: Model specification number (e.g."m990"
). Identifies a particular set of parameters, equilibrium conditions, and measurement equation (equivalently, a concrete model type - for example, models of typeModel990
would havespec = "m990"
.)subspec::ASCIIString
: Model sub-specification (e.g."ss0"
). Indicates any changes to parameter initialization fromspec
. See Editing or Extending a Model for more details.settings::Dict{Symbol,Setting}
: Settings/flags that affect computation without changing the economic or mathematical setup of the model.test_settings::Dict{Symbol,Setting}
: Settings/flags for testing mode
rng::MersenneTwister
: Random number generator. By default, it is seeded to ensure reproducibility in algorithms that involve randomness (such as Metropolis-Hastings).testing::Bool
: Indicates whether the model is in testing mode. Iftrue
, settings fromm.test_settings
are used in place of those inm.settings
.
The model's equilibrium conditions and observables are represented as fairly large matrices, and keeping track of which rows and columns correspond to which states, shocks, equations, etc. can be confusing. To improve clarity, we define several dictionaries that map variable names to indices in these matrices:
endogenous_states
: Indices of endogenous model statesexogenous_shocks
: Indices of exogenous shocksexpected_shocks
: Indices of expectation shocksequilibrium_conditions
: Indices of equilibrium condition equationsendogenous_states_augmented
: Indices of model states, after model solution and system augmentationobservables
: Indices of named observables
This approach has a number of advantages. Most importantly, it is robust to inadvertent typos or indexing errors. Since the actual index number doesn't matter to us, the user only needs to define the names of their equilibrium conditions, states, and other variables. Adding states is easy - we have only to add them to the appropriate list in the model constructor, and they will be assigned an index.
As an example, consider the model's equilibrium conditions. The canonical representation of the equilibrium conditions is
Γ0 s_t = Γ1 s_{t-1} + C + Ψ ε_t + Π η_t
where Γ0
, Γ1
, C
, Ψ
, and Π
are matrices of coefficients for s_t
(states at time t
), s_{t-1}
(lagged states), ε_t
(exogenous shocks) and
η_t
(expectational shocks). Each row of these matrices corresponds to an
equilibrium condition, which we define using a descriptive name (for example, we
name the consumption Euler equation :euler
). States (columns of Γ0
and
Γ1
), exogenous shocks (columns of Ψ
), and expectational shocks (columns
Π
) also have names.
The AbstractParameter
type implements our notion of a model parameter: a
time-invariant, unobserved value that has economic significance in the model's
equilibrium conditions. We estimate the model to find the values of these
parameters.
Though all parameters are time-invariant, each has different features. Some parameters are scaled for use in the model's equilibrium conditions and measurement equations. During optimization, parameters can be transformed from model space to the real line via one of three different transformations. These transformations are also defined as types, and require additional information for each parameter. Finally, steady-state parameters are not estimated directly, but are calculated as a function of other parameters.
These various requirements are nicely addressed using a parameterized type hierarchy.
AbstractParameter{T<:Number}
: The common abstract supertype for all parameters.Parameter{T<:Number, U<:Transform}
: The abstract supertype for parameters that are directly estimated.UnscaledParameter{T<:Number, U:<Transform}
: Concrete type for parameters that do not need to be scaled for equilibrium conditions.ScaledParameter{T<:Number, U:<Transform}
: Concrete type for parameters that are scaled for equilibrium conditions.
SteadyStateParameter{T<:Number}
: Concrete type for steady-state parameters.
All Parameter
s have the following fields:
key::Symbol
: Parameter name. For maximum clarity,key
should conform to the guidelines established in CONTRIBUTING.md.value::T
: Parameter value. Initialized in model space (guaranteed to be betweenvaluebounds
), but can be transformed between model space and the real line via calls totransform_to_real_line
andtransform_to_model_space
.valuebounds::Interval{T}
: Bounds for the parameter's value in model space.transform_parameterization::Interval{T}
: Parameters used to transformvalue
between model space and the real line.transform::U
: Transformation used to transformvalue
between model space and real line.prior::NullablePrior
: Prior distribution for parameter value.fixed::Bool
: Indicates whether the parameter's value is fixed rather than estimated.description::AbstractString
: A short description of the parameter's economic significance.tex_label::AbstractString
: String for printing the parameter name to LaTeX.
ScaledParameters
also have the following fields:
scaledvalue::T
: Parameter value scaled for use ineqcond.jl
scaling::Function
: Function used to scale parameter value for use in equilibrium conditions.
Note: Though not strictly necessary, defining a scaling with the parameter object allows for much a much cleaner definition of the equilibrium conditions.
Because the values of SteadyStateParameter
s are directly computed as a
function of ScaledParameter
s and UnscaledParameter
s, they only require 4
fields:
key::Symbol
value::T
description::AbstractString
tex_label::AbstractString
The Setting
type implements computational settings that affect how the code
runs without affecting the mathematical definition of the model. These include
flags (e.g. whether or not to recompute the Hessian), parameterization for the
Metropolis-Hastings algorithm (e.g. number of times to draw from the posterior
distribution), and the vintage of data being used (Setting
is a parametric
type - a Setting{T<:Any}
, so Booleans, Numbers, and Strings can all be turned
into Setting
s). They are stored centrally in the settings
dictionary within
the model object.
Why implement a Setting
type when we could put their values directly into the
source code or dictionary? The most obvious answer is that the parametric type
allows us to implement a single interface for all Setting
s (Booleans, Strings,
etc.), so that when we access a particular setting during the estimation and
forecast steps, we don't have to think about the setting's type.
Setting
s play an important role in addition to providing useful abstraction.
Estimating and forecasting the FRBNY DSGE model takes many hours of computation
time and creates a lot of output files. It is useful to be able to compare model
output from two different models whose settings differ slightly (for example,
consider two identical models that use different vintages of data as input). A
central feature of the Setting
type is a mechanism that generates unique,
meaningful filenames when code is executed with different settings.
Specifically, when a setting takes on a non-default value, a user-defined
setting code (along with the setting's value) are appended to all output files
generated during execution.
The Setting{T<:Any}
type has the following fields:
key::Symbol
: Name of settingvalue::T
: Value of settingprint::Bool
: Indicates whether to append this setting's code and value to output file names. If true, output file names will include a suffix of the form_key1=val1_key2=val2
etc. where codes are listed in alphabetical order.code::AbstractString
: short string (4 characters or less) to print to output file names whenprint=true
.description::AbstractString
: Short description of what the setting is used for.
Finds modal parameter values, calculate Hessian matrix at mode, and samples
from posterior distribution. See estimate
in
estimate.jl.
Main Steps:
-
Initialization: Read in and transform raw data from
save/input_data/
. -
Reoptimize parameter vector: The main program will call the
csminwel
optimization routine (located incsminwel.jl
) to find modal parameter estimates. -
Compute Hessian matrix: Computing the Hessian matrix to scale the proposal distribution in the Metropolis-Hastings algorithm.
-
Sample from Posterior: Posterior sampling is performed using the Metropolis-Hastings algorithm. A proposal distribution is constructed centered at the posterior mode and with proposal covariance scaled by the inverse of the Hessian matrix. Settings for the number of sampling blocks and the size of those blocks can be altered as described in Editing or Extending a Model.
Remark: In addition to saving each mh_thin
-th draw of the parameter
vector, the estimation program also saves the resulting posterior value and
transition equation matrices implied by each draw of the parameter vector. This
is to save time in the forecasting step since that code can avoid recomputing
those matrices.
Developers of this package at FRBNY include
Contributors to this package at QuantEcon include
The gensys
and csminwel
routines in gensys.jl and
csminwel.jl are based on routines originally
copyright Chris Sims. The files are released
here with permission of Chris Sims under the BSD-3 license.
The kalman_filter
routine is loosely based on a version of the
Kalman filter algorithm originally copyright Federal Reserve Bank of Atlanta
and written by Iskander Karibzhanov. The files are
released here with permission of the Federal Reserve Bank of Atlanta under the
BSD-3 license.