Skip to content

Commit dd21944

Browse files
authored
Merge pull request #1037 from jdebacker/debt_gdp
Merging
2 parents 8cf609a + bc9dc7a commit dd21944

23 files changed

Lines changed: 1048950 additions & 1050160 deletions

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.15.2] - 2025-01-22 12:00:00
9+
10+
### Added
11+
12+
- A new parameters, `r_gov_DY` and `r_gov_DY2`, that allow the government interest rate to be a function of the debt-to-GDP ratio. See PR [#1037](https://github.com/PSLmodels/OG-Core/pull/1037)
13+
814
## [0.15.1] - 2026-01-19 12:00:00
915

1016
### Added
@@ -493,6 +499,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
493499
- Any earlier versions of OG-USA can be found in the [`OG-Core`](https://github.com/PSLmodels/OG-Core) repository [release history](https://github.com/PSLmodels/OG-Core/releases) from [v.0.6.4](https://github.com/PSLmodels/OG-Core/releases/tag/v0.6.4) (Jul. 20, 2021) or earlier.
494500

495501

502+
[0.15.2]: https://github.com/PSLmodels/OG-Core/compare/v0.15.1...v0.15.2
503+
[0.15.1]: https://github.com/PSLmodels/OG-Core/compare/v0.15.0...v0.15.1
496504
[0.15.0]: https://github.com/PSLmodels/OG-Core/compare/v0.14.14...v0.15.0
497505
[0.14.14]: https://github.com/PSLmodels/OG-Core/compare/v0.14.13...v0.14.14
498506
[0.14.13]: https://github.com/PSLmodels/OG-Core/compare/v0.14.12...v0.14.13

docs/book/content/theory/government.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -667,14 +667,14 @@ Note that the budget closure rule (described in Section ref{`SecUnbalGBCcloseRul
667667
(SecRateWedge)=
668668
## Interest Rate on Government Debt and Household Savings
669669

670-
Despite the model having no aggregate risk, it may be helpful to build in an interest rate differential between the rate of return on private capital and the interest rate on government debt. Doing so helps to add realism by including a risk premium. `OG-Core` allows users to set an exogenous wedge between these two rates. The interest rate on government debt,
670+
Despite the model having no aggregate risk, it may be helpful to build in an interest rate differential between the rate of return on private capital and the interest rate on government debt. Doing so helps to add realism by including a risk premium. `OG-Core` allows users to set an exogenous wedge between these two rates. `OG-Core` also allows for the risk premium on debt to be a quadratic function of the debt-to-GDP ratio. The interest rate on government debt is given by:
671671

672672
```{math}
673673
:label: EqUnbalGBC_rate_wedge
674-
r_{gov, t} = (1 - \tau_{d, t})r_{t} - \mu_{d}
674+
r_{gov, t} = (1 - \tau_{d, t})r_{t} - \mu_{d} + \beta_{1}\frac{D_t}{Y_t} + \beta_{2}\left(\frac{D_t}{Y_t}\right)^2
675675
```
676676

677-
where $r_t$ is the marginal product of capital faced by firms. The two parameters, $\tau_{d,t}$ and $\mu_{d,t}$ can be used to allow for a government interest rate that is a percentage hair cut from the market rate or a government interest rate with a constant risk premium.
677+
where $r_t$ is the marginal product of capital faced by firms. The two parameters, $\tau_{d,t}$ and $\mu_{d,t}$ can be used to allow for a government interest rate that is a percentage hair cut from the market rate or a government interest rate with a constant risk premium. The parameters $\beta_1$ and $\beta_2$ can be used to allow for a quadratic risk premium on government debt that is a function of the debt-to-GDP ratio.
678678

679679

680680
(SecUnbalGBCcloseRule)=

ogcore/SS.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ def inner_loop(outer_loop_vars, p, client):
444444
B = aggr.get_B(bssmat, p, "SS", False)
445445

446446
# Find gov't debt
447-
r_gov = fiscal.get_r_gov(r, p, "SS")
447+
r_gov = fiscal.get_r_gov(r, p.debt_ratio_ss, p, "scalar", t=-1)
448448
D, D_d, D_f, new_borrowing, _, new_borrowing_f = fiscal.get_D_ss(
449449
r_gov, Y, p
450450
)
@@ -495,7 +495,7 @@ def inner_loop(outer_loop_vars, p, client):
495495
new_r = firm.get_r(Y_vec[-1], K_vec[-1], p_m, p, "SS", -1)
496496
new_w = firm.get_w(Y_vec[-1], L_vec[-1], p_m, p, "SS")
497497

498-
new_r_gov = fiscal.get_r_gov(new_r, p, "SS")
498+
new_r_gov = fiscal.get_r_gov(new_r, p.debt_ratio_ss, p, "scalar", t=-1)
499499
# now get accurate measure of debt service cost
500500
(
501501
D,
@@ -829,7 +829,7 @@ def SS_solver(
829829
K_vec_ss = new_K_vec
830830
L_vec_ss = new_L_vec
831831
Y_vec_ss = new_Y_vec
832-
r_gov_ss = fiscal.get_r_gov(rss, p, "SS")
832+
r_gov_ss = fiscal.get_r_gov(rss, p.debt_ratio_ss, p, "scalar", t=-1)
833833
p_m_ss = new_p_m
834834
p_i_ss = np.dot(p.io_matrix, p_m_ss)
835835
p_tilde_ss = aggr.get_ptilde(p_i_ss, p.tau_c[-1, :], p.alpha_c)

ogcore/TPI.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,8 @@ def run_TPI(p, client=None):
715715
total_tax_revenue = np.ones(p.T + p.S) * ss_vars["total_tax_revenue"]
716716

717717
# Compute other interest rates
718-
r_gov = fiscal.get_r_gov(r, p, "TPI")
718+
r_gov = np.ones_like(r) * ss_vars["r_gov"]
719+
r_gov[: p.T] = fiscal.get_r_gov(r[: p.T], D[: p.T] / Y[: p.T], p, "TPI")
719720
r_p = np.ones_like(r) * ss_vars["r_p"]
720721
MPKg = np.zeros((p.T, p.M))
721722
for m in range(p.M):
@@ -1082,6 +1083,11 @@ def run_TPI(p, client=None):
10821083
"TPI",
10831084
)
10841085
total_tax_revenue[: p.T] = total_tax_rev
1086+
if not p.baseline_spending:
1087+
I_g = fiscal.get_I_g(Y[: p.T], None, p, "TPI")
1088+
if p.baseline:
1089+
K_g0 = p.initial_Kg_ratio * Y[0]
1090+
K_g = fiscal.get_K_g(K_g0, I_g, p, "TPI")
10851091
dg_fixed_values = (
10861092
Y,
10871093
total_tax_revenue,
@@ -1097,18 +1103,12 @@ def run_TPI(p, client=None):
10971103
G[: p.T],
10981104
D_d[: p.T],
10991105
D_f[: p.T],
1106+
r_gov_new,
11001107
new_borrowing,
11011108
debt_service,
11021109
new_borrowing_f,
1103-
) = fiscal.D_G_path(r_gov, dg_fixed_values, p)
1104-
K[: p.T], K_d[: p.T], K_f[: p.T] = aggr.get_K_splits(
1105-
B[: p.T], K_demand_open_vec.sum(-1), D_d[: p.T], p.zeta_K[: p.T]
1106-
)
1107-
if not p.baseline_spending:
1108-
I_g = fiscal.get_I_g(Y[: p.T], None, p, "TPI")
1109-
if p.baseline:
1110-
K_g0 = p.initial_Kg_ratio * Y[0]
1111-
K_g = fiscal.get_K_g(K_g0, I_g, p, "TPI")
1110+
) = fiscal.D_G_path(r, dg_fixed_values, p)
1111+
11121112
rnew = r.copy()
11131113
rnew[: p.T] = np.squeeze(
11141114
firm.get_r(
@@ -1117,14 +1117,20 @@ def run_TPI(p, client=None):
11171117
)
11181118
# For case where economy is small open econ
11191119
rnew[p.zeta_K == 1] = p.world_int_rate[p.zeta_K == 1]
1120-
r_gov_new = fiscal.get_r_gov(rnew, p, "TPI")
11211120
MPKg_vec = np.zeros((p.T, p.M))
11221121
for m in range(p.M):
11231122
MPKg_vec[:, m] = np.squeeze(
11241123
firm.get_MPx(
11251124
Y_vec[: p.T, m], K_g[: p.T], p.gamma_g[m], p, "TPI", m
11261125
)
11271126
)
1127+
1128+
K[: p.T], K_d[: p.T], K_f[: p.T] = aggr.get_K_splits(
1129+
B[: p.T], K_demand_open_vec.sum(-1), D_d[: p.T], p.zeta_K[: p.T]
1130+
)
1131+
r_gov_new = fiscal.get_r_gov(
1132+
rnew[: p.T], Dnew[: p.T] / Y[: p.T], p, "TPI"
1133+
)
11281134
r_p_new = aggr.get_r_p(
11291135
rnew[: p.T],
11301136
r_gov_new[: p.T],

ogcore/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@
2020
from ogcore.txfunc import *
2121
from ogcore.utils import *
2222

23-
__version__ = "0.15.1"
23+
__version__ = "0.15.2"

ogcore/default_parameters.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,42 @@
10101010
}
10111011
}
10121012
},
1013+
"r_gov_DY": {
1014+
"title": "Linear effect of debt to GDP ratio on government interest rate",
1015+
"section_1": "Fiscal Policy Parameters",
1016+
"description": "Parameter summarizing the linear effect of the debt to GDP ratio on the government interest rate.",
1017+
"notes": "",
1018+
"type": "float",
1019+
"value": [
1020+
{
1021+
"value": 0.0
1022+
}
1023+
],
1024+
"validators": {
1025+
"range": {
1026+
"min": -0.3,
1027+
"max": 0.3
1028+
}
1029+
}
1030+
},
1031+
"r_gov_DY2": {
1032+
"title": "Quadratic effect of debt to GDP ratio on government interest rate",
1033+
"section_1": "Fiscal Policy Parameters",
1034+
"description": "Parameter summarizing the quadratic effect of the debt to GDP ratio on the government interest rate.",
1035+
"notes": "",
1036+
"type": "float",
1037+
"value": [
1038+
{
1039+
"value": 0.0
1040+
}
1041+
],
1042+
"validators": {
1043+
"range": {
1044+
"min": -0.3,
1045+
"max": 0.3
1046+
}
1047+
}
1048+
},
10131049
"cit_rate": {
10141050
"title": "Corporate income tax rate",
10151051
"description": "Corporate income tax rate. Set value for base year, click '+' to add value for next year. All future years not specified are set to last value entered.",

ogcore/fiscal.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"""
1818

1919

20-
def D_G_path(r_gov, dg_fixed_values, p):
20+
def D_G_path(r, dg_fixed_values, p):
2121
r"""
2222
Calculate the time paths of debt and government spending
2323
@@ -87,11 +87,14 @@ def D_G_path(r_gov, dg_fixed_values, p):
8787
G = p.alpha_G[: p.T] * Y[: p.T]
8888
D_f = np.zeros(p.T)
8989
D_d = np.zeros(p.T)
90+
r_gov = get_r_gov(r[: p.T], np.zeros(p.T), p, method="TPI")
9091
new_borrowing = np.zeros(p.T)
9192
debt_service = np.zeros(p.T)
9293
new_borrowing_f = np.zeros(p.T)
9394
else:
9495
t = 1
96+
r_gov = np.zeros_like(r)
97+
r_gov[0] = get_r_gov(r[0], D[0] / Y[0], p, method="scalar", t=0)
9598
while t < p.T - 1:
9699
D[t] = (1 / growth[t]) * (
97100
(1 + r_gov[t - 1]) * D[t - 1]
@@ -102,6 +105,7 @@ def D_G_path(r_gov, dg_fixed_values, p):
102105
+ agg_pension_outlays[t - 1]
103106
- total_tax_revenue[t - 1]
104107
)
108+
r_gov[t] = get_r_gov(r[t], D[t] / Y[t], p, method="scalar", t=t)
105109
if (t >= p.tG1) and (t < p.tG2):
106110
G[t] = (
107111
growth[t + 1]
@@ -137,6 +141,7 @@ def D_G_path(r_gov, dg_fixed_values, p):
137141
+ agg_pension_outlays[t - 1]
138142
- total_tax_revenue[t - 1]
139143
)
144+
r_gov[t] = get_r_gov(r[t], D[t] / Y[t], p, method="scalar", t=t)
140145
G[t] = (
141146
growth[t] * (p.debt_ratio_ss * Y[t])
142147
- (1 + r_gov[t]) * D[t]
@@ -182,6 +187,7 @@ def D_G_path(r_gov, dg_fixed_values, p):
182187
G,
183188
D_d,
184189
D_f[: p.T],
190+
r_gov[: p.T],
185191
new_borrowing,
186192
debt_service,
187193
new_borrowing_f,
@@ -350,27 +356,42 @@ def get_TR(
350356
return new_TR
351357

352358

353-
def get_r_gov(r, p, method):
359+
def get_r_gov(r, DY_ratio, p, method, t=0):
354360
r"""
355361
Determine the interest rate on government debt
356362
357363
.. math::
358-
r_{gov,t} = \max\{(1-\tau_{d,t}r_{t} - \mu_d, 0.0\}
364+
r_{gov,t} = \max\{(1-\tau_{d,t}r_{t} - \mu_d + \beta_1 \frac{D_t}{Y_t} + \beta_2 \left(\frac{D_t}{Y_t}\right)^2, 0.0\}
359365
360366
Args:
361367
r (array_like): interest rate on private capital debt over the
362368
time path or in the steady state
369+
DY_ratio (array_like): ratio of government debt to GDP
363370
p (OG-Core Specifications object): model parameters
371+
method (str): either 'scalar' for one period or 'TPI' for transition path
372+
t (int): time period index, used only if method is 'scalar'
364373
365374
Returns:
366375
r_gov (array_like): interest rate on government debt over the
367376
time path or in the steady-state
368377
369378
"""
370-
if method == "SS":
371-
r_gov = np.maximum(p.r_gov_scale[-1] * r - p.r_gov_shift[-1], 0.00)
379+
if method == "scalar":
380+
r_gov = np.maximum(
381+
p.r_gov_scale[t] * r
382+
- p.r_gov_shift[t]
383+
+ p.r_gov_DY * DY_ratio
384+
+ p.r_gov_DY2 * DY_ratio**2,
385+
0.00,
386+
)
372387
else:
373-
r_gov = np.maximum(p.r_gov_scale * r - p.r_gov_shift, 0.00)
388+
r_gov = np.maximum(
389+
p.r_gov_scale[: p.T] * r[: p.T]
390+
- p.r_gov_shift[: p.T]
391+
+ p.r_gov_DY * DY_ratio[: p.T]
392+
+ p.r_gov_DY2 * DY_ratio[: p.T] ** 2,
393+
0.00,
394+
)
374395

375396
return r_gov
376397

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setuptools.setup(
77
name="ogcore",
8-
version="0.15.1",
8+
version="0.15.2",
99
author="Jason DeBacker and Richard W. Evans",
1010
license="CC0 1.0 Universal (CC0 1.0) Public Domain Dedication",
1111
description="A general equilibrium overlapping generations model for fiscal policy analysis",

0 commit comments

Comments
 (0)