12. Satellite models
A forecast round publishes far more than the core model carries. The structural model gives output, inflation and the policy rate; the round also has to say something about consumption, unemployment, a savings identity, sector detail. Those come from a satellite layer: small single equations, each fitted on its own, run after the core model and conditional on it.
rise.satellite is that layer — the third family of model object,
alongside the DSGE and the VAR.
m = rise.satellite([
"diff_log(consumption) = a1 + a2*diff_log(gdp)"
"unemployment = b1 + b2*unemployment{-1} + b3*diff_log(gdp)"
"savings === gdp - consumption"
]);
12.1. How it connects to the core model
Through the data, not through an object. A satellite system holds
equations and parameters and nothing else: it has no handle on a
dsge_model, it is not estimated jointly with one, and nothing feeds back
from it into the core.
The workflow is:
core = perfect_foresight(m_dsge, 'simul_historical_data', plan); % or simulate(...)
detail = simulate(m_sat, core, periods);
gdp in the equations above is simply a name the system reads out of the
databank you hand it. Where that databank came from is your business — the
core model, the published data, or a mixture. That is what makes the layer a
satellite: it orbits the core model, and it is the round that puts them
together.
12.2. Write the transform, get the level
The point of the object is the left-hand side. Modellers write these equations in growth rates; everyone downstream needs levels.
diff_log(consumption) = ... is fitted and simulated in log differences,
and simulate returns the level of consumption. You never write the
accumulation. The transforms are the same seven shared with simulation plans
(see Conditioning on transforms), so diff_log means the same thing
in both places.
=== marks an accounting identity. It is never estimated and never gets a
residual.
12.3. Estimation, equation by equation
A name in an equation is a variable until it is declared a parameter. Declaring the coefficients is what tells the system which names to fit:
m.Parameters.a1 = 0; m.Parameters.a2 = 0;
info = estimate(m, history, 2:n);
The system is recursive by construction, so ordinary least squares equation
by equation is consistent and there is nothing to gain from estimating them
jointly. info carries the estimates, their standard errors and the
R-squared of each equation.
Estimating a system whose coefficients were never declared fits nothing, and says so rather than reporting a clean result with no estimates — that is how somebody comes to believe a model was estimated when it was not.
12.4. Residual recovery, and why it has to be exact
The two operations a forecast round actually runs:
recovered = residuals(m, history, 2:n); % what residuals does the data imply?
back = simulate(m, recovered, 2:n); % put them back
These must be exact inverses of each other, through the transform inversion.
They are, to about 1e-13. If they were not, every add-factor a
forecaster applies would be quietly wrong, and nobody would find out,
because the path would still look plausible. That property is the reason to
use this object rather than hand-rolling the accumulation per equation.
12.5. The horizon belongs to the caller
simulate(m, data, (n+1):(n+12)) runs past the end of the data. A forecast
is by definition periods the data do not reach, so the working array is
extended to match. Missing residuals are zero — no judgment is the sensible
default.
12.6. What it refuses
The order of solution is worked out from the equations, never guessed at. A system that cannot be made recursive is reported by name:
RISE:satellite:notRecursive the simultaneous block involves: alpha, beta
Other refusals carry the same prefix: notAnEquation,
badLeftHandSide, noExpansion, tooManyTransforms, noInverse,
noData.
12.7. See also
Conditioning on transforms — the shared transform rules