*This article is an unedited excerpt from my manuscript on stock-flow consistent (SFC) modelling in Python. It describes how the sfc_models framework determines an initial steady state for a model. For readers who are unfamiliar with the notion of the steady state within the context of SFC models, I have this primer on the concepts of steady state and equilibrium.*

Determining the initial steady state is particularly difficult if there are multiple types of financial assets within the model. All need to be set at levels that match asset allocation functions, and we must ensure that all balance sheets balance. (At present, the framework does not attempt to force balance sheets into balance; unbalanced initial conditions result in the creation of “ghost asset balances” that are implied but not explicitly held within the model.)

The EquationSolver class has the capacity to solve for an initial steady state (if it exists). By setting ParameterSolveInitialSteadyState to True, the solver will starts a new simulation, starting at k=-200 (by default), and then simulating from that point until k=0. The solver will then restart from k=0 from that steady state value normally. (The starting time is set by ParameterInitialSteadyStateMaxTime, which has a default of 200.)

Importantly, if this option is chosen, the initial conditions are applied to the starting state in negative time; the values at

*k=0*are just the calculated steady state values. By default, all financial stock variables start at zero, and therefore balance sheets balance. Therefore, the resulting steady state (if it exists) will obey accounting rules. (If the user hardcodes an initial condition, it is up to the user to validate that accounting consistency holds.) It is not possible to solve for a steady state and then specify different initial conditions for

*k=0.*That is, you would not be able to apply a shock to the steady state until

*k=1*.

It would be more elegant to solve for the steady state explicitly (as is done in the text

*Monetary Economics*by Godley and Lavoie). In principle, it looks straightforward: we just leave the equations unchanged, except for lag conditions: in a steady state,

*x[k-1] = x[k]*. That is, we replace lag relationships with an equality. However, when this was tested for simple models, there was no convergence to a solution. It is a future research project to re-examine this, and see whether it is possible to solve for the steady state in this fashion.

The framework currently supports the notion of a constant steady state: (almost) all variables are assumed to converge to constant values. (Exceptions are discussed below.) Presumably, we need exogenous variables to be constant as well. As a result, we use the value of exogenous variables at

*k=0*for all negative times. This technique will not work for variables that are viewed as “exogenous” from the point of view of economics, but are not explicitly defined as such. For example, we might think of government consumption as exogenous, and we want to see what happens if it increases linearly. If we specify spending as a function (for example, G = 20 * k) government spending will be calculated as such during negative time. As a result, we would not expect a steady state solution to exist. However, if we define

*G*as an exogenous variable, equal to a Python list ([0.0, 20.0, 40.0, …]), the government spending will be held flat at 0 for all negative time. If you want to avoid using a list, you could define the variable to be equal to a custom function (Section 7.3). You just need to ensure that the custom function returns a constant value for negative time values.

In this case, we could use:

def gov_spending(k):

"""

Government spending function that respects negative time.

"""

if k < 0:

# We are in the initial steady state calculation

return 0.0

else:

return 20.0*k

The calculated time series are associated with the ‘steadystate_0’ log, which is output to the “_steadystate.txt” log when RegisterStandardLogs (Section 3.3) is called. These series will need to be examined to see which ones do not reach steady state if the framework throws an error when trying to calculate the steady state.

The framework just looks at the last values, to see whether they are (roughly) constant. The criterion may be changed in the future; it presently just looks at the last two value, and checks whether the percentage change is less than ParameterInitialSteadyStateErrorToler (default is now 1e-4). The exception is when the last value is small in absolute terms (hardcoded threshold of 1e-4). In this case, the algorithm just validates that the previous value has a similarly low absolute value. This needs to be done, as series that are close to zero can have quite large percentage changes when solved numerically.

However, some variables are expected to change, even in a steady state. The most obvious example being the custom time axis

*t*. (Within

*sfc_models*, there are two variables associated with the time axis; the discrete time

*k*which always matches the iteration step number, and the custom time axis which can be a function of

*k*. This allows us to define a time axis that goes up by 0.25 increments to match quarterly data, while still having access to the current time step.) The parameter ParameterInitialSteadyStateExcludedVariables is a parameter that is a list of variables to exclude (by default, just '

*t*'). (The time axis

*k*is always excluded.) The user can add more variables to exclude in order to proceed to solving in positive time.

A planned extension will be to allow a “steady state” where variables are growing at a steady rate. Unfortunately, the growth rate of variables depends upon the variables’ classification. For example, nominal variables will grow at a nominal growth rate, real variables at a real growth rate, and price variables the rate of inflation. Meanwhile, parameters will still be constants, with a nominal growth rate of zero. The complexity of determining whether the solution is a legitimate steady state explains why this implementation has been pushed back.

(c) Brian Romanchuk 2017

## No comments:

## Post a Comment

Note: Posts are manually moderated, with a varying delay. Some disappear.

The comment section here is largely dead. My Substack or Twitter are better places to have a conversation.

Given that this is largely a backup way to reach me, I am going to reject posts that annoy me. Please post lengthy essays elsewhere.