Model PC#
Model PC is introduced in chapter 4 of Godley and Lavoie [2006] “Monetary Economics: An Integrated Approach to Credit, Money, Income, Production and Wealth”.
Module Contents#
As with all MacroStat models, PC is divided into Variables, Parameters (fixed constants), Scenarios, and the Behavior (model initialization and steps). The module-level documentation, such as all variables/parameters/scenarios and their notation or the behavioral equations associated with each function of BehaviorPC.py can be seen in:
Note
The remainder of this page gives an introduction to the model, notes on how it is implemented in MacroStat and then shows some of the model dynamics by replicating the relevant graphs of Godley and Lavoie (2006).
Model Overview#
Behavioral Equations#
Similarly to model SIM, model PC is built on the idea of perfect foresight, that is, we assume that producers will sell whatever is demanded and that the households are correct in their expectations of disposable income. The PC model as introduced in Chapter 4 consists of the following 11 equations and 11 unknowns:
National Income
Disposable income is national income and interest earnings minus taxes
Taxes are a fixed share of income
Wealth increases by savings
Consumption is partially out of disposable income and wealth
Household cash holdings are the difference between wealth and bill holdings
The share of bills in wealth
The share of cash in wealth (6A)
The change in the stock of outstanding government bills (also known as the government’s budget constraint). The first part represents government outlays (direct purchases and interest payments) while the second represents government revenues (taxes and central bank profits)
The change in money circulating
Bills held by the central bank, the central bank purchases all of the bills issued by the government that the households are not willing to buy given the current interest rate. Combined with (10) it implies the CB provides cash money on demand. Therefore, the amount of cash in the system is endogeneous and demand-led while the rate on bills is exogenous.
The interest rate is fixed
With the redundant equation being
Transaction Flow Matrix#
Household |
Production |
Government |
CentralBank |
CentralBank |
Total |
|
|---|---|---|---|---|---|---|
Current |
Current |
Current |
Current |
Capital |
||
Consumption Household |
\(-C(t)\) |
\(+C(t)\) |
\(0\) |
|||
Consumption Government |
\(+G(t)\) |
\(-G(t)\) |
\(0\) |
|||
National Income |
\(+Y(t)\) |
\(-Y(t)\) |
\(0\) |
|||
Interest Earned On Bills Household |
\(+r(t-1)\cdot B_h(t-1)\) |
\(-r(t-1)\cdot B_h(t-1)\) |
\(0\) |
|||
Interest Earned On Bills Central Bank |
\(-r(t-1)\cdot B_{CB}(t-1)\) |
\(+r(t-1)\cdot B_{CB}(t-1)\) |
\(0\) |
|||
Central Bank Profits |
\(+r(t-1)\cdot B_{CB}(t-1)\) |
\(-r(t-1)\cdot B_{CB}(t-1)\) |
\(0\) |
|||
Taxes |
\(-T(t)\) |
\(+T(t)\) |
\(0\) |
|||
Change in Money Stock |
\(+H_h(t)\) |
\(-H_{s}(t)\) |
\(0\) |
|||
Change in Bill Stock |
\(+B_h(t)\) |
\(-B_s(t)\) |
\(+B_{CB}(t)\) |
\(0\) |
||
Total |
\(0\) |
\(0\) |
\(0\) |
\(0\) |
\(0\) |
\(0\) |
Balance Sheet Matrix#
Household |
Production |
Government |
CentralBank |
Total |
|
|---|---|---|---|---|---|
Current |
Current |
Current |
Capital |
||
Money Stock |
\(+H_h(t)\) |
\(-H_{s}(t)\) |
0 |
||
Bill Stock |
\(+B_h(t)\) |
\(-B_s(t)\) |
\(+B_{CB}(t)\) |
0 |
|
Wealth |
\(-V(t)\) |
\(+V(t)\) |
0 |
Implementation in MacroStat#
Transposing these eleven equations to the MacroStat framework, we consider that there are:
Three parameters (fixed constants): \(\alpha_1\), \(\alpha_2\), and \(\theta\) (see Parameters)
Two scenario variables : \(G_d(t)\) and \(W(t)\) (see Scenarios)
The remaining 14 tracked series are variables (see Variables)
Behavioral Modeling#
The model PC imposes that the assumptions of the household on income are correct and firms supply all goods. For the implementation of the behavioral equations (see Behavior), most prior implementations have made use of some form of linear solver or iteration until the system is solved. To simplify the implementation in Macrostat, we can note that the system can be solved analytically for a given timestep as follows:
Substitute Eq. (5) into Eq. (1) to obtain
where \(G(t)\) is exogenous and \(V(t-1)\) is determined. Substituting further equation Eq. (2) and Eq. (3) we can obtain
which can be rearranged to yield a solution for \(Y(t)\)
Therefore, for a given period \(t\) we can solve the system by solving, in order:
Eq. (14) for national income \(Y(t)\)
Eq. (3) for taxes \(T(t)\)
Eq. (2) for disposable income \(YD(t)\)
Eq. (5) for consumption \(C(t)\)
Eq. (4) for wealth \(V(t)\)
Eq. (7) for household bill holdings \(B_h(t)\)
Eq. (6) for household depositis (residual) \(H_h(t)\)
Eq. (9) for the government budget resulting in bill issuance \(B_s(t)\)
Eq. (11) for the Central Bank holding of bills \(B_{CB}(t)\)
Eq. (10) for the level of cash money \(H_s(t)\)
This is implemented as such in the Behavior class.
Model Dynamics#
Preparatory Steps#
%load_ext autoreload
%autoreload 2
import importlib
import logging
import sys
from matplotlib import pyplot as plt
from matplotlib.ticker import PercentFormatter
from macrostat.models import get_model
import numpy as np
plt.style.use("../../macrostat.mplstyle")
importlib.reload(logging)
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
The autoreload extension is already loaded. To reload it, use:
%reload_ext autoreload
Running the Simulation#
First, we can run the model without any shocks to see the convergence to the steady state.
GL06PC = get_model("GL06PC")
model = GL06PC()
model.simulate()
output = model.variables.to_pandas()
INFO:root:Starting simulation. Scenario: 0
Here we can also check that the variables are healthy, which means that the redundant equations hold and that all the assets and liabilities are positive. For model PC, the redundant equation is that the household money stock equals the central bank money stock.
Note
In numerical implementations, due to floating point precision it is unlikely that the redundant equation will hold exactly. Therefore, we check that the absolute percentage error is less than a given tolerance, in this case 1e-5. We use the absolute percentage error to appropriately scale the error for different magnitudes of the variables.
model.variables.check_health(tolerance=1e-5)
True
Convergence to the Steady State#
In Godley and Lavoie [2006] models are initialized with almost all of the variables set to zero. For model PC the only non-zero item is that in period 0 the government demand is 20, i.e. the government creates 20 monetary units of demand.
Steady State solutions#
To aid in our graphing, we can calculate the steady state solutions for the variables following the derivations in Section 4.5 of Godley and Lavoie Godley and Lavoie [2006].
First, we set some helper variables, using r for the interest rate and g for the government demand. Then defining
we can solve for the steady state values of the variables.
steadystate = {}
p = model.parameters
a3 = (1-p["PropensityToConsumeIncome"]) / p["PropensityToConsumeSavings"]
r = model.scenarios[(0,"InterestRate")][0]
g = model.scenarios[(0,"GovernmentDemand")][0]
The steady state disposable income is given by
steadystate["DisposableIncome"] = g / (
(p["TaxRate"] / (1-p["TaxRate"]))
- r * ((p["WealthShareBills_Constant"] + p["WealthShareBills_InterestRate"] * r) * a3 - p["WealthShareBills_Income"])
)
The steady state household bill holdings are given by
and the steady state wealth is given by
steadystate["HouseholdBillStock"] = (
a3 * (p["WealthShareBills_Constant"] + p["WealthShareBills_InterestRate"] * r) - p["WealthShareBills_Income"]
) * steadystate["DisposableIncome"]
steadystate["Wealth"] = steadystate["DisposableIncome"] / a3
Finally, the steady state national income is given by
since
steadystate["NationalIncome"] = steadystate["DisposableIncome"] + g
steadystate["Consumption"] = steadystate["DisposableIncome"]
Convergence to the Steady State#
fig, axs = plt.subplots(ncols=2, nrows=1, figsize=(7, 3))
axs[0].plot(output.index, output['NationalIncome'], color='k', label=r'National Income $Y$')
axs[0].axhline(y=steadystate["NationalIncome"], color='r', linestyle='--', label=r'Steady State Income $Y^\star$')
axs[0].legend(loc='lower right', frameon=False)
axs[0].set_xlim(0,40)
axs[0].set_title('National Income')
axs[1].plot(output.index, output['ConsumptionHousehold'], color='k', label=r'Consumption $C$')
axs[1].plot(output.index, output['DisposableIncome'], color='g', linestyle='-.', label=r'Disposable Income $YD$')
axs[1].axhline(y=steadystate["Consumption"], color='r', linestyle='--', label=r'Steady State Consumption $C^\star$')
axs[1].legend(loc='lower right', frameon=False)
axs[1].set_xlim(0,40)
axs[1].set_title('Consumption')
fig.suptitle('Figure PC.1: Convergence to the Steady State')
plt.tight_layout()
plt.show()
fig, axs = plt.subplots(ncols=2, nrows=1, figsize=(7, 3))
# Left panel - Savings
line1 = axs[0].plot(output.index, output['Wealth'].diff(), color='k', linestyle='-', label='Savings, $\Delta V$')
line2 = axs[0].axhline(y=0, color='r', linestyle='--', label='Target Savings $\Delta V^\star=0$')
axs[0].legend(loc='center right', frameon=False)
axs[0].set_xlim(0,40)
axs[0].set_title('Savings')
# Right panel - Wealth
line3 = axs[1].plot(output.index, output['Wealth'], color='k', linestyle='-', label='Household Wealth $V$')
line4 = axs[1].axhline(y=steadystate["Wealth"], color='r', linestyle='--', label='Target Wealth, $V^\star$')
axs[1].legend(loc='center right', frameon=False)
axs[1].set_xlim(0,40)
axs[1].set_title('Household Wealth')
fig.suptitle('Figure PC.2: Savings and Household Wealth')
plt.tight_layout()
plt.show()
fig, axs = plt.subplots(ncols=2, nrows=1, figsize=(7, 3))
share_bills = steadystate['HouseholdBillStock'] / steadystate['Wealth']
# Left panel - Money share
money_share = output['HouseholdMoneyStock'] / output['Wealth']
line1 = axs[0].plot(output.index, money_share, color='k', linestyle='-', label='Money Share $H_h/V$')
axs[0].axhline(y=1-share_bills, color='r', linestyle='--', label='Steady State Share')
axs[0].legend(loc='center right', frameon=False)
axs[0].set_xlim(0,40)
axs[0].set_title('Money Share')
axs[0].yaxis.set_major_formatter(PercentFormatter(1))
# Right panel - Bills share
bills_share = output['HouseholdBillStock'] / output['Wealth']
line3 = axs[1].plot(output.index, bills_share, color='k', linestyle='-', label='Bills Share $B_h/V$')
axs[1].axhline(y=share_bills, color='r', linestyle='--', label='Steady State Share')
axs[1].legend(loc='center right', frameon=False)
axs[1].set_xlim(0,40)
axs[1].set_title('Bills Share')
axs[1].yaxis.set_major_formatter(PercentFormatter(1))
fig.suptitle('Figure PC.3: Portfolio Allocation')
plt.tight_layout()
plt.show()
Perturbations in the Steady State: An increase in the interest rate#
Following the convergence to the steady state, we can introduce a shock in the interest rate, increasing it by 100 basis points from 2.5% to 3.5%.
This kind of scenario can easily be implemented in the MacroStat version by adding a scenario:
Noting that we have convergence to the steady state at period 40, let us set this as the scenario trigger
We then need a new timeseries for the scenario, where \(r=0.035\) from the trigger period onwards
model.parameters["scenario_trigger"] = 60
model.scenarios.add_scenario(
name="InterestRateIncrease",
timeseries={"InterestRate":0.035}
)
model.simulate(scenario="InterestRateIncrease")
output_interest_rate_increase = model.variables.to_pandas()
INFO:root:Starting simulation. Scenario: 1
We then need to compute the new steady state values for the variables, but with r=0.035 instead of r=0.025.
steadystate_interest_rate_increase = {}
p = model.parameters
a3 = (1-p["PropensityToConsumeIncome"]) / p["PropensityToConsumeSavings"]
r = model.scenarios[(1,"InterestRate")][-1]
g = model.scenarios[(1,"GovernmentDemand")][-1]
steadystate_interest_rate_increase["DisposableIncome"] = g / (
(p["TaxRate"] / (1-p["TaxRate"]))
- r * ((p["WealthShareBills_Constant"] + p["WealthShareBills_InterestRate"] * r) * a3 - p["WealthShareBills_Income"])
)
steadystate_interest_rate_increase["HouseholdBillStock"] = (
a3 * (p["WealthShareBills_Constant"] + p["WealthShareBills_InterestRate"] * r) - p["WealthShareBills_Income"]
) * steadystate_interest_rate_increase["DisposableIncome"]
steadystate_interest_rate_increase["Wealth"] = steadystate_interest_rate_increase["DisposableIncome"] / a3
steadystate_interest_rate_increase["NationalIncome"] = steadystate_interest_rate_increase["DisposableIncome"] + g
steadystate_interest_rate_increase["Consumption"] = steadystate_interest_rate_increase["DisposableIncome"]
Now we can see how the model reacts to the shock.
fig, axs = plt.subplots(ncols=2, nrows=1, figsize=(7, 3))
df = output_interest_rate_increase.loc[model.parameters["scenario_trigger"]-2:]
axs[0].plot(df.index, df['NationalIncome'], color='k', label=r'National Income $Y$')
axs[0].axhline(y=steadystate_interest_rate_increase["NationalIncome"], color='r', linestyle='--', label=r'Steady State Income $Y^\star$')
axs[0].legend(loc='lower right', frameon=False)
axs[0].set_title('National Income')
axs[1].plot(df.index, df['ConsumptionHousehold'], color='k', label=r'Consumption $C$')
axs[1].plot(df.index, df['DisposableIncome'], color='g', linestyle='-.', label=r'Disposable Income $YD$')
axs[1].axhline(y=steadystate_interest_rate_increase["Consumption"], color='r', linestyle='--', label=r'Steady State Consumption $C^\star$')
axs[1].legend(loc='lower right', frameon=False)
axs[1].set_title('Consumption')
fig.suptitle('Figure PC.3: Convergence to the Steady State')
plt.tight_layout()
plt.show()
fig, axs = plt.subplots(ncols=2, nrows=1, figsize=(7, 3))
df = output_interest_rate_increase.loc[model.parameters["scenario_trigger"]-2:]
# Left panel - Savings
line1 = axs[0].plot(df.index, df['Wealth'].diff(), color='k', linestyle='-', label='Savings, $\Delta V$')
line2 = axs[0].axhline(y=0, color='r', linestyle='--', label='Target Savings $\Delta V^\star=0$')
axs[0].legend(frameon=False)
axs[0].set_title('Savings')
# Right panel - Wealth
line3 = axs[1].plot(df.index, df['Wealth'], color='k', linestyle='-', label='Household Wealth $V$')
line4 = axs[1].axhline(y=steadystate_interest_rate_increase["Wealth"], color='r', linestyle='--', label='Target Wealth, $V^\star$')
axs[1].legend(frameon=False)
axs[1].set_title('Household Wealth')
fig.suptitle('Figure PC.5: Savings and Household Wealth')
plt.tight_layout()
plt.show()
fig, axs = plt.subplots(ncols=2, nrows=1, figsize=(7, 3))
share_bills = steadystate_interest_rate_increase['HouseholdBillStock'] / steadystate_interest_rate_increase['Wealth']
df = output_interest_rate_increase.loc[model.parameters["scenario_trigger"]-2:]
# Left panel - Money share
money_share = df['HouseholdMoneyStock'] / df['Wealth']
line1 = axs[0].plot(df.index, money_share, color='k', linestyle='-', label='Money Share $H_h/V$')
axs[0].axhline(y=1-share_bills, color='r', linestyle='--', label='Steady State Share')
axs[0].legend(loc='center right', frameon=False)
axs[0].set_title('Money Share')
axs[0].yaxis.set_major_formatter(PercentFormatter(1))
# Right panel - Bills share
bills_share = df['HouseholdBillStock'] / df['Wealth']
line3 = axs[1].plot(df.index, bills_share, color='k', linestyle='-', label='Bills Share $B_h/V$')
axs[1].axhline(y=share_bills, color='r', linestyle='--', label='Steady State Share')
axs[1].legend(loc='center right', frameon=False)
axs[1].set_title('Bills Share')
axs[1].yaxis.set_major_formatter(PercentFormatter(1))
fig.suptitle('Figure PC.6: Portfolio Allocation')
plt.tight_layout()
plt.show()