Model LP3#

Model LP3 extends Model LP2 from Chapter 5 of Godley and Lavoie [2006] by making government expenditures endogenous through a fiscal austerity rule. When the deficit-to-GDP ratio (\(PSBR/Y\)) exceeds a threshold, the government cuts spending. This introduces path-dependence — the long-run steady state depends on the history of shocks, not just the structural parameters.

Module Contents#

As with all MacroStat models, LP3 is divided into Variables, Parameters (fixed constants), Scenarios, and the Behavior (model initialization and steps). The module-level documentation can be seen in:

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#

Model LP3 inherits all 20 equations from Model LP2. The exogenous government spending \(G\) of LP2 is replaced by an endogenous fiscal rule, and a new equation tracks the public sector borrowing requirement.

New equation — Fiscal Austerity Rule (equations 5.28–5.31 of GL06):

(1)#\[z_3 = 1 \text{ iff } \frac{PSBR(t-1)}{Y(t-1)} > \text{threshold}, \quad z_4 = 1 \text{ iff } \frac{PSBR(t-1)}{Y(t-1)} < -\text{threshold}\]
(2)#\[G(t) = G(t-1) - (z_3 + z_4) \cdot \beta_g \cdot PSBR(t-1)\]

New equation — Public Sector Borrowing Requirement:

(3)#\[PSBR(t) = \left(G(t) + r_b(t) \cdot B_s(t-1) + BL_s(t-1)\right) - \left(T(t) + r_b(t) \cdot B_{CB}(t-1)\right)\]

The fiscal rule is symmetric: the government also loosens when the budget is in surplus beyond the threshold. Government spending adjusts gradually at speed \(\beta_g\), not instantaneously.

Transaction Flow Matrix#

Accounting Transaction Matrix for Model LP3#

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\)

Taxes

\(-T(t)\)

\(+T(t)\)

\(0\)

Interest On Bills Household

\(+r_b(t-1) \cdot B_h(t-1)\)

\(-r_b(t-1) \cdot B_h(t-1)\)

\(0\)

Bond Coupon Income Household

\(+BL_h(t-1)\)

\(-BL_h(t-1)\)

\(0\)

Central Bank Profits

\(+r_b(t-1) \cdot B_{CB}(t-1)\)

\(-r_b(t-1) \cdot B_{CB}(t-1)\)

\(0\)

Change in Bill Stock

\(+\Delta B_h(t)\)

\(-\Delta B_s(t)\)

\(+\Delta B_{CB}(t)\)

\(0\)

Change in Bond Stock

\(+\Delta BL_h(t)\)

\(0\)

Change in Bond Supply

\(-\Delta BL_s(t)\)

\(0\)

Change in Cash Stock

\(+\Delta H_h(t)\)

\(0\)

Change in Money Stock

\(-\Delta H_s(t)\)

\(0\)

Total

\(0\)

\(0\)

\(0\)

\(0\)

\(0\)

\(0\)

Balance Sheet Matrix#

Balance Sheet for Model LP3#

Household

Production

Government

CentralBank

Total

Current

Current

Current

Capital

Wealth

\(-V(t)\)

\(+V(t)\)

0

Bill Stock

\(+B_h(t)\)

\(-B_s(t)\)

\(+B_{CB}(t)\)

0

Bond Stock

\(+BL_h(t)\)

0

Bond Supply

\(-BL_s(t)\)

0

Cash Stock

\(+H_h(t)\)

0

Money Stock

\(-H_s(t)\)

0

Implementation in MacroStat#

Transposing these equations to the MacroStat framework, we consider that there are:

  1. Eighteen parameters (fixed constants): all sixteen from LP2, plus \(\beta_g\) (fiscal adjustment speed) and \(\phi\) (PSBR threshold) (see Parameters)

  2. One scenario variable: \(r_b(t)\) — both bond prices and government spending are now endogenous (see Scenarios)

  3. The remaining tracked series are variables — including the new PublicSectorBorrowingRequirement (see Variables)

Model Dynamics#

Preparatory Steps#

%load_ext autoreload
%autoreload 2

import importlib
import logging
import sys

# Import the necessary libraries for plotting
from matplotlib import pyplot as plt
from matplotlib.ticker import PercentFormatter

# Import the MacroStat model components
from macrostat.models.GL06LP import GL06LP, ParametersGL06LP, ScenariosGL06LP
from macrostat.models.GL06LP3 import GL06LP3, ParametersGL06LP3, ScenariosGL06LP3

# Custom matplotlib style for the documentation
plt.style.use("../../macrostat.mplstyle")
# We show the logging output in the notebook
importlib.reload(logging)
logging.basicConfig(stream=sys.stdout, level=logging.INFO)

Running the Simulation#

In LP3, government spending adjusts endogenously after the first period. The initial value \(G_0 = 20\) is used in period 1, then the fiscal rule takes over. The PSBR-to-GDP ratio converges toward zero as the government adjusts spending to balance the budget.

params = ParametersGL06LP3()
model = GL06LP3(parameters=params)
model.simulate()
output = model.variables.to_pandas()

We can check that the variables are healthy, meaning the redundant equation \(H_h = H_s\) holds and all stocks are positive.

Note

Due to floating point precision, the redundant equation will not hold exactly. We check that the absolute percentage error is below a tolerance.

model.variables.check_health(tolerance=1e-3)
True

An overview of the first 10 steps of the model:

df = model.variables.to_pandas()
df.head(10).T
time 0 1 2 3 4 5 6 7 8 9
ConsumptionHousehold Household 0.0 0.0 0.000000 16.124001 28.993177 39.686977 48.558765 55.919319 62.026039 67.092545
ConsumptionGovernment Government 0.0 0.0 20.000000 19.838760 19.710068 19.603130 19.514412 19.440807 19.379740 19.329075
NationalIncome Macroeconomy 0.0 0.0 20.000000 35.962761 48.703247 59.290108 68.073181 75.360123 81.405777 86.421616
Taxes Household 0.0 0.0 3.876000 6.969583 9.540234 11.672894 13.442276 14.910254 16.128176 17.138641
InterestOnBillsHousehold Household 0.0 0.0 0.000000 0.000000 0.189307 0.341071 0.467330 0.572079 0.658957 0.731008
BondCouponIncomeHousehold Household 0.0 0.0 0.000000 0.000000 0.334654 0.600475 0.821078 1.004089 1.155981 1.282050
CentralBankProfits CentralBank 0.0 0.0 0.000000 0.483720 0.487647 0.503520 0.516294 0.526903 0.535701 0.542999
CapitalGains Household 0.0 0.0 -0.000000 -0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
ExpectedCapitalGains Household 0.0 0.0 0.000000 0.009906 0.008887 0.006076 0.003715 0.002138 0.001186 0.000641
Wealth Household 0.0 0.0 16.124001 28.993179 39.686981 48.558765 55.919315 62.026031 67.092537 71.296036
HouseholdBillStock Household 0.0 0.0 0.000000 6.310246 11.369034 15.577663 19.069304 21.965221 24.366936 26.358936
GovernmentBillStock Government 0.0 0.0 16.124001 22.565136 28.153038 32.787479 36.632748 39.821934 42.466904 44.660660
CentralBankBillStock CentralBank 0.0 0.0 16.124001 16.254890 16.784004 17.209816 17.563444 17.856712 18.099968 18.301723
HouseholdBondStock Household 0.0 0.0 0.000000 0.334654 0.600475 0.821078 1.004089 1.155981 1.282050 1.386680
GovernmentBondSupply Government 0.0 0.0 0.000000 0.334654 0.600475 0.821078 1.004089 1.155981 1.282050 1.386680
HouseholdCashStock Household 0.0 0.0 16.124001 16.254892 16.784016 17.209831 17.563463 17.856722 18.099981 18.301744
CentralBankMoneyStock CentralBank 0.0 0.0 16.124001 16.254890 16.784004 17.209816 17.563446 17.856714 18.099968 18.301723
DisposableIncome Household 0.0 0.0 16.124001 28.993177 39.686977 48.558762 55.919315 62.026035 67.092545 71.296036
ExpectedDisposableIncome Household 0.0 0.0 0.000000 16.124001 28.993177 39.686977 48.558762 55.919315 62.026035 67.092545
ExpectedWealth Household 0.0 0.0 0.000000 16.124001 28.993181 39.686985 48.558758 55.919312 62.026024 67.092545
HouseholdBillDemand Household 0.0 0.0 0.000000 6.310246 11.369034 15.577663 19.069304 21.965221 24.366936 26.358936
HouseholdBondDemand Household 0.0 0.0 0.000000 0.334654 0.600475 0.821078 1.004089 1.155981 1.282050 1.386680
HouseholdCashDemand Household 0.0 0.0 0.000000 3.385713 6.090216 8.338050 10.202904 11.750002 13.033468 14.098253
InterestRateBills Macroeconomy 0.0 0.0 0.030000 0.030000 0.030000 0.030000 0.030000 0.030000 0.030000 0.030000
BondPrice Macroeconomy 20.0 20.0 19.600000 19.208000 19.208000 19.208000 19.208000 19.208000 19.208000 19.208000
BondYield Macroeconomy 0.0 0.0 0.051020 0.052062 0.052062 0.052062 0.052062 0.052062 0.052062 0.052062
ExpectedBondPrice Household 20.0 20.0 19.799999 19.504000 19.355999 19.282000 19.244999 19.226500 19.217251 19.212626
ExpectedReturnOnBonds Household 0.0 0.0 0.052041 0.053603 0.052832 0.052447 0.052254 0.052158 0.052110 0.052086
TargetProportion Government 0.0 0.0 0.000000 0.000000 0.504624 0.503600 0.503088 0.502832 0.502704 0.502640
PublicSectorBorrowingRequirement Government 0.0 0.0 16.124001 12.869179 10.693795 8.871782 7.360544 6.106721 5.066502 4.203491

Convergence to the Steady State#

In the steady state of Model LP3, the fiscal rule drives \(PSBR/Y \to 0\). Government spending adjusts slowly (governed by \(\beta_g\)) until the budget is balanced. The long-run outcome is similar to LP2 in terms of portfolio composition, but the level of government spending (and hence income) is path-dependent.

dfo = output.loc[:100]

fig, axs = plt.subplots(ncols=2, nrows=3, figsize=(8, 8))

# National Income
axs[0, 0].plot(dfo.index, dfo['NationalIncome'], color='k', label=r'National Income $Y$')
axs[0, 0].legend(loc='upper center', bbox_to_anchor=(0.5, -0.1), frameon=False)
axs[0, 0].set_title('National Income')

# Consumption and Government Spending
axs[0, 1].plot(dfo.index, dfo['ConsumptionHousehold'], color='k', label=r'Consumption $C$')
axs[0, 1].plot(dfo.index, dfo['ConsumptionGovernment'], color='g', linestyle='-.', label=r'Government $G$')
axs[0, 1].legend(loc='upper center', bbox_to_anchor=(0.5, -0.1), frameon=False)
axs[0, 1].set_title('Consumption and Government Spending')

# Wealth
axs[1, 0].plot(dfo.index, dfo['Wealth'], color='k', label=r'Wealth $V$')
axs[1, 0].plot(dfo.index, dfo['ExpectedWealth'], color='g', linestyle='-.', label=r'Expected Wealth $V^e$')
axs[1, 0].legend(loc='upper center', bbox_to_anchor=(0.5, -0.1), frameon=False)
axs[1, 0].set_title('Wealth')

# PSBR / Y ratio
psbr_ratio = output['PublicSectorBorrowingRequirement'].div(output['NationalIncome']['Macroeconomy'], axis=0)
axs[1, 1].plot(dfo.index, psbr_ratio.loc[:100], color='k', label=r'$PSBR/Y$')
axs[1, 1].axhline(y=0, color='r', linestyle='--', label=r'Target $PSBR/Y^\star=0$')
axs[1, 1].legend(loc='upper center', bbox_to_anchor=(0.5, -0.1), frameon=False)
axs[1, 1].set_title('PSBR to GDP Ratio')
axs[1, 1].yaxis.set_major_formatter(PercentFormatter(1))

# Portfolio Shares
cash_share = output['HouseholdCashStock'] / output['Wealth']
bill_share = output['HouseholdBillStock'] / output['Wealth']
bond_share = (output['HouseholdBondStock'].mul(output[('BondPrice','Macroeconomy')], axis=0)) / output['Wealth']

axs[2, 0].plot(output.index, cash_share, color='b', label='Cash $H_h/V$')
axs[2, 0].plot(output.index, bill_share, color='k', label='Bills $B_h/V$')
axs[2, 0].plot(output.index, bond_share, color='r', label='Bonds $p_{bl} BL_h/V$')
axs[2, 0].legend(loc='center right', frameon=False)
axs[2, 0].set_xlim(0, 100)
axs[2, 0].set_title('Portfolio Shares')
axs[2, 0].yaxis.set_major_formatter(PercentFormatter(1))

# Bond Price
axs[2, 1].plot(dfo.index, dfo[('BondPrice', 'Macroeconomy')], color='b', label=r'Bond Price $p_{bl}$')
axs[2, 1].plot(dfo.index, dfo['ExpectedBondPrice'], color='r', linestyle='--', label=r'Expected $p_{bl}^e$')
axs[2, 1].legend(loc='center right', frameon=False)
axs[2, 1].set_title('Bond Price')

fig.suptitle('Figure LP3.1: Model convergence to the steady state')
plt.tight_layout()
plt.show()
../../_images/9d5f5fe9af2d257c2cddf8970e78fc0699c18661f047fe9d3a9bb616266cd8cd.png

Perturbation 1: Drop in Propensity to Consume (Section 5.9, Figure 5.10 vs 5.11)#

Following Section 5.9 of Godley and Lavoie [2006], we reduce the propensity to consume out of current income (\(\alpha_1\)) by 0.1. This induces a recession that raises the government deficit (lower taxes, same spending). In LP (with exogenous G), the economy recovers above its initial level. In LP3, the fiscal austerity rule kicks in: once \(PSBR/Y\) exceeds the threshold, government spending is cut, deepening and prolonging the recession.

We run both LP3 and LP (for comparison) with the same \(\alpha_1\) shock:

# Run LP3 with the drop in alpha1
scenarios_lp3 = ScenariosGL06LP3(parameters=params)
sc1 = scenarios_lp3.get_scenario_index("Scenario.1: Drop in alpha1")
model1 = GL06LP3(parameters=params, scenarios=scenarios_lp3)
model1.simulate(scenario=sc1)
output_sc1 = model1.variables.to_pandas()

# Run LP (exogenous G) for comparison — same alpha1 shock
params_lp = ParametersGL06LP()
scenarios_lp = ScenariosGL06LP(parameters=params_lp)
sc_lp = scenarios_lp.get_scenario_index("Scenario.2: Drop in alpha1")
model_lp_base = GL06LP(parameters=params_lp)
model_lp_base.simulate()
output_lp1_base = model_lp_base.variables.to_pandas()
model_lp1 = GL06LP(parameters=params_lp, scenarios=scenarios_lp)
model_lp1.simulate(scenario=sc_lp)
output_lp1_shock = model_lp1.variables.to_pandas()
trigger = params["scenario_trigger"]

fig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)

# Figure 5.10 (LP — exogenous G)
ax = axes[0]
ax.plot(output_lp1_base.loc[trigger - 2:].index,
        output_lp1_base.loc[trigger - 2:, 'NationalIncome'], 'k--', label='Baseline')
ax.plot(output_lp1_shock.loc[trigger - 2:].index,
        output_lp1_shock.loc[trigger - 2:, 'NationalIncome'], 'k-', label='Shock')
ax.axvline(x=trigger, color='grey', linestyle=':', alpha=0.5)
ax.set_title('Figure 5.10: $Y$ in Model LP (exogenous $G$)')
ax.set_xlabel('Period')
ax.legend(frameon=False)

# Figure 5.11 (LP3 — fiscal rule)
ax = axes[1]
ax.plot(output.loc[trigger - 2:].index,
        output.loc[trigger - 2:, 'NationalIncome'], 'k--', label='Baseline (LP3)')
ax.plot(output_sc1.loc[trigger - 2:].index,
        output_sc1.loc[trigger - 2:, 'NationalIncome'], 'k-', label='Shock (LP3)')
ax.axvline(x=trigger, color='grey', linestyle=':', alpha=0.5)
ax.set_title('Figure 5.11: $Y$ in Model LP3 (fiscal rule)')
ax.set_xlabel('Period')
ax.legend(frameon=False)

fig.suptitle('Figures 5.10 & 5.11: Drop in $\\alpha_1$ - LP vs LP3 (fiscal rule)')
plt.tight_layout()
plt.show()
../../_images/790b8e2c013ecdb10b751960f8892964197354885f1be1655aaa5ef2bf31098a.png

Figure 5.12: Government expenditures and the PSBR-to-GDP ratio. Once the threshold is breached, government spending is cut each period until the ratio returns within bounds.

dfo = output_sc1.loc[trigger - 2:]
dfo_base = output.loc[trigger - 2:]

fig, axes = plt.subplots(1, 2, figsize=(12, 4))

# Government spending
ax = axes[0]
ax.plot(dfo_base.index, dfo_base['ConsumptionGovernment'], 'k--', label='$G$ (base)')
ax.plot(dfo.index, dfo['ConsumptionGovernment'], 'k-', label='$G$ (shock)')
ax.axvline(x=trigger, color='grey', linestyle=':', alpha=0.5)
ax.set_title('Figure 5.12a: Government Spending $G$')
ax.set_xlabel('Period')
ax.legend(frameon=False)

# PSBR / Y
psbr_base = dfo_base['PublicSectorBorrowingRequirement'].div(dfo_base['NationalIncome']['Macroeconomy'], axis=0)
psbr_shock = dfo['PublicSectorBorrowingRequirement'].div(dfo['NationalIncome']['Macroeconomy'],axis=0)
ax = axes[1]
ax.plot(dfo_base.index, psbr_base, 'k--', label='$PSBR/Y$ (base)')
ax.plot(dfo.index, psbr_shock, 'k-', label='$PSBR/Y$ (shock)')
ax.axhline(y=0, color='r', linestyle=':', alpha=0.5)
ax.axvline(x=trigger, color='grey', linestyle=':', alpha=0.5)
ax.set_title('Figure 5.12b: PSBR to GDP Ratio')
ax.set_xlabel('Period')
ax.yaxis.set_major_formatter(PercentFormatter(1))
ax.legend(frameon=False)

fig.suptitle('Figure LP3.2: Fiscal response to drop in $\\alpha_1$')
plt.tight_layout()
plt.show()
../../_images/f421bbc65fc5f5c694f4e54706b43d73794ece9d2958599b0246e21d6efa9be4.png

Perturbation 2: Rise in Bill Rate (all mechanisms active)#

In this experiment, we raise the Treasury bill rate from 3% to 4% in a model where all three LP3 mechanisms are active: Tobin portfolio choice, endogenous bond prices, and the fiscal rule. The bill rate rise makes bills more attractive, reallocates the portfolio, pushes up the bond price endogenously, and increases government debt service costs, potentially triggering the fiscal rule.

scenarios_lp3_2 = ScenariosGL06LP3(parameters=params)
sc2 = scenarios_lp3_2.get_scenario_index("Scenario.2: Rise in bill rate")
model2 = GL06LP3(parameters=params, scenarios=scenarios_lp3_2)
model2.simulate(scenario=sc2)
output_sc2 = model2.variables.to_pandas()
dfo2 = output_sc2.loc[trigger - 2:]

fig, axes = plt.subplots(2, 2, figsize=(10, 8))
fig.suptitle('Figure LP3.3: Rise in bill rate with all mechanisms active', fontsize=14)

# National Income
axes[0, 0].plot(dfo_base.index, dfo_base['NationalIncome'], 'k--', label='Baseline')
axes[0, 0].plot(dfo2.index, dfo2['NationalIncome'], 'k-', label='Shock')
axes[0, 0].axvline(x=trigger, color='grey', linestyle=':', alpha=0.5)
axes[0, 0].set_title('National Income $Y$')
axes[0, 0].legend(frameon=False)

# Government Spending
axes[0, 1].plot(dfo_base.index, dfo_base['ConsumptionGovernment'], 'g--', label='$G$ (base)')
axes[0, 1].plot(dfo2.index, dfo2['ConsumptionGovernment'], 'g-', label='$G$ (shock)')
axes[0, 1].axvline(x=trigger, color='grey', linestyle=':', alpha=0.5)
axes[0, 1].set_title('Government Spending $G$')
axes[0, 1].legend(frameon=False)

# Bond Price
axes[1, 0].plot(dfo_base.index, dfo_base[('BondPrice','Macroeconomy')], 'b--', label='$p_{bl}$ (base)')
axes[1, 0].plot(dfo2.index, dfo2[('BondPrice','Macroeconomy')], 'b-', label='$p_{bl}$ (shock)')
axes[1, 0].axvline(x=trigger, color='grey', linestyle=':', alpha=0.5)
axes[1, 0].set_title('Bond Price $p_{bl}$')
axes[1, 0].legend(frameon=False)

# PSBR / Y
psbr_base2 = dfo_base['PublicSectorBorrowingRequirement'].div(dfo_base['NationalIncome']['Macroeconomy'], axis=0)
psbr_sc2 = dfo2['PublicSectorBorrowingRequirement'].div(dfo2['NationalIncome']['Macroeconomy'], axis=0)
axes[1, 1].plot(dfo_base.index, psbr_base2, 'k--', label='$PSBR/Y$ (base)')
axes[1, 1].plot(dfo2.index, psbr_sc2, 'k-', label='$PSBR/Y$ (shock)')
axes[1, 1].axhline(y=0, color='r', linestyle=':', alpha=0.5)
axes[1, 1].axvline(x=trigger, color='grey', linestyle=':', alpha=0.5)
axes[1, 1].set_title('PSBR-to-GDP Ratio')
axes[1, 1].yaxis.set_major_formatter(PercentFormatter(1))
axes[1, 1].legend(frameon=False)

plt.tight_layout()
plt.show()
../../_images/25a65c2cd16bbf190a514c2da20f259f74ebf915ece3d3f997652cc1f7874488.png