debtkit

Lifecycle: stable License: MIT

Analyse whether a government’s debt is on a sustainable path, using the same frameworks that the IMF and European Commission use in their country assessments.

Installation

# install.packages("devtools")
devtools::install_github("charlescoverdale/debtkit")
library(debtkit)

# Project debt forward 10 years
proj <- dk_project(debt = 0.90, interest_rate = 0.04,
                   gdp_growth = 0.03, primary_balance = 0.01, horizon = 10)
proj
#> -- Debt Sustainability Projection --
#> * Horizon: 10 years
#> * Initial debt/GDP: 90%
#> * Terminal debt/GDP: 82.6%
#> * Debt-stabilising primary balance: 0.8%

Why debt sustainability analysis?

Every government borrows money. The central question in public finance is whether a government can keep servicing that debt, or whether the debt ratio will spiral upward until something breaks. Debt sustainability analysis (DSA) is the standard framework for answering this question. The IMF, the European Commission, finance ministries, and central banks all use it.

The core idea is simple. Next year’s debt-to-GDP ratio depends on four things: this year’s debt, the interest rate on that debt, how fast the economy is growing, and whether the government is running a surplus or deficit (excluding interest payments). When interest rates exceed GDP growth, debt snowballs. When growth exceeds interest rates, debt stabilises more easily.

In practice, a full DSA combines several analyses: deterministic projections under baseline assumptions, historical decompositions of what drove debt changes in the past, stress tests under adverse scenarios, stochastic simulations that generate thousands of possible paths, tests of whether the government historically responds to rising debt by tightening policy, and calculations of how much fiscal adjustment is needed to hit a target. All of these are well-established methods, but tedious to code from scratch. debtkit packages them into clean R functions with plotting methods, so you can go from raw fiscal data to a complete assessment in a few lines of code.

Examples

Where is the debt ratio heading?

Project debt forward under constant assumptions. The output tells you the terminal debt ratio and the primary balance that would stabilise debt at its current level.

library(debtkit)

proj <- dk_project(debt = 0.90, interest_rate = 0.04,
                   gdp_growth = 0.03, primary_balance = 0.01, horizon = 10)
proj
#> -- Debt Sustainability Projection --
#> * Horizon: 10 years
#> * Initial debt/GDP: 90%
#> * Terminal debt/GDP: 82.6%
#> * Debt-stabilising primary balance: 0.8%

plot(proj)

What drove debt changes in the past?

Decompose historical debt movements into four components: interest costs pushing debt up, GDP growth pulling it down, the primary balance, and a residual (stock-flow adjustment).

d <- dk_sample_data()
decomp <- dk_decompose(d$debt, d$interest_rate, d$gdp_growth,
                        d$primary_balance, years = d$years)
decomp
#> -- Debt Decomposition --
#> * Periods: 19 (2005-2023)
#> * Cumulative change: 24 pp
#> *   Interest effect: 52.5 pp
#> *   Growth effect: -58.2 pp
#> *   Primary balance: 14.4 pp
#> *   Stock-flow adj.: 15.3 pp

plot(decomp)  # Stacked bar chart of contributions

How wide is the range of possible outcomes?

Estimate the joint distribution of macro shocks from historical data, then simulate 1,000 debt paths. The fan chart shows the 10th to 90th percentile bands.

d <- dk_sample_data()
shocks <- dk_estimate_shocks(d$gdp_growth, d$interest_rate, d$primary_balance)
fan <- dk_fan_chart(debt = 0.90, interest_rate = 0.035, gdp_growth = 0.03,
                    primary_balance = -0.01, shocks = shocks,
                    n_sim = 1000, horizon = 10, seed = 42)
fan
#> -- Stochastic Debt Fan Chart --
#> * Simulations: 1000
#> * Horizon: 10 years
#> * Initial debt: 90% of GDP
#> * Baseline terminal debt: 103.2% of GDP

plot(fan)  # Fan chart with percentile bands

What if things go wrong?

Run six standardised IMF stress-test scenarios: growth shock, interest rate shock, exchange rate shock, primary balance shock, a combined shock, and contingent liabilities materialising.

stress <- dk_stress_test(debt = 0.90, interest_rate = 0.04,
                         gdp_growth = 0.03, primary_balance = 0.01,
                         fx_share = 0.20)
stress
#> -- IMF Stress Test Scenarios --
#> * Horizon: 5 years
#> * Initial debt/GDP: 90%
#>
#> Terminal debt/GDP by scenario:
#>   Baseline                 85.5%
#>   Growth shock             87.3%
#>   Interest rate shock      93.9%
#>   Exchange rate shock      88.2%
#>   Primary balance shock    87.3%
#>   Combined shock           89.8%
#>   Contingent liabilities   94.8%

plot(stress)

Does the government respond to rising debt?

Estimate Bohn’s (1998) fiscal reaction function. A positive, statistically significant coefficient means the government systematically raises the primary surplus when debt rises, satisfying a sufficient condition for sustainability.

d <- dk_sample_data()
bohn <- dk_bohn_test(d$primary_balance, d$debt, robust_se = TRUE)
bohn
#> -- Bohn Fiscal Reaction Function --
#> * Method: ols (HAC)
#> * Observations: 20
#> * rho = 0.15 (SE = 0.06, p = 0.02)
#> v Sustainable: rho > 0 and significant at 5% level.

How much fiscal adjustment is needed?

Compute the European Commission’s S1 and S2 sustainability gap indicators. S1 measures the adjustment needed to reach 60% debt in 20 years. S2 measures the adjustment needed to stabilise debt over an infinite horizon, accounting for ageing costs.

dk_sustainability_gap(
  debt = 0.90, structural_balance = -0.01,
  gdp_growth = 0.015, interest_rate = 0.025, ageing_costs = 0.02
)
#> -- Sustainability Gap Indicators --
#> * Current debt/GDP: 90%
#> * Current structural PB: -1%
#>
#> -- S1 Indicator --
#> * Required PB adjustment: 2.8 pp
#> * Target debt/GDP: 60% in 20 years
#>
#> -- S2 Indicator --
#> * Required PB adjustment: 1.9 pp

What data do I need?

Every function in debtkit takes the same four inputs, all as decimals (0.90 = 90% of GDP):

Input What it is
debt Debt-to-GDP ratio (0.90 = 90%)
interest_rate Interest rate on government debt (0.04 = 4%)
gdp_growth Nominal GDP growth (0.03 = 3%)
primary_balance Revenue minus non-interest spending (0.01 = 1% surplus, negative = deficit)

These are standard fiscal variables published by every major data provider. You can type them in directly, use the built-in sample data, or pull them from a data source.

Option 1: Type values in directly

If you know the numbers (from a budget document, a news article, or a textbook exercise), just type them:

library(debtkit)

# Italy-like scenario: high debt, low growth, small deficit
dk_project(debt = 1.40, interest_rate = 0.035, gdp_growth = 0.02,
           primary_balance = 0.015, horizon = 10)

Option 2: Use the built-in sample data

The package includes sample datasets so you can try everything immediately:

d <- dk_sample_data()
str(d)
#> List of 5
#>  $ years          : int [1:20] 2004 2005 2006 ... 2023
#>  $ debt           : num [1:20] 0.45 0.44 0.42 ... 0.69
#>  $ interest_rate  : num [1:20] 0.045 0.043 0.042 ... 0.038
#>  $ gdp_growth     : num [1:20] 0.055 0.050 0.060 ... 0.040
#>  $ primary_balance: num [1:20] 0.010 0.012 0.015 ... -0.005

Option 3: Pull data from a source

Here is a complete example using OECD data for any of 38 member countries:

# 1. Install the data package (one time)
install.packages("readoecd")

# 2. Download fiscal data for France
library(readoecd)
debt <- oecd_series("GGDEBT", "FRA")          # General government debt (% of GDP)
growth <- oecd_series("NGDP_RPCH", "FRA")      # Real GDP growth (%)
balance <- oecd_series("GGXONLB_NGDP", "FRA")  # Primary balance (% of GDP)

# 3. Convert from percent to decimal and feed into debtkit
library(debtkit)
dk_project(
  debt = tail(debt$value, 1) / 100,
  interest_rate = 0.03,                         # Use latest effective rate estimate
  gdp_growth = tail(growth$value, 1) / 100,
  primary_balance = tail(balance$value, 1) / 100,
  horizon = 10
)

Where to find fiscal data

Source Coverage Series you need How to get into R
OECD 38 countries GGDEBT, NGDP_RPCH, GGXONLB_NGDP readoecd
IMF WEO 190+ countries GGXWDG_NGDP, NGDP_RPCH, GGXONLB_NGDP Download CSV from imf.org
FRED (US) United States GFDEGDQ188S, GDPC1, FYFSGDA188S fred
Eurostat EU members gov_10dd_edpt1, nama_10_gdp eurostat package
World Bank 200+ countries GC.DOD.TOTL.GD.ZS, NY.GDP.MKTP.KD.ZG WDI package

Functions

Function Description
dk_project() Project debt-to-GDP paths forward
dk_decompose() Decompose historical debt changes into interest, growth, primary balance, and stock-flow effects
dk_rg() Interest rate-growth differential and debt-stabilising primary balance
dk_bohn_test() Bohn fiscal reaction function (OLS, rolling, quadratic; optional HAC standard errors)
dk_estimate_shocks() Estimate joint shock distributions (VAR, bootstrap, normal)
dk_fan_chart() Stochastic debt fan charts via Monte Carlo simulation
dk_stress_test() Six IMF standardised stress tests (fixed or data-driven calibration)
dk_heat_map() IMF-style risk heat map with colour-coded ratings
dk_gfn() Gross financing needs projection
dk_sustainability_gap() European Commission S1/S2 sustainability gap indicators
dk_compare() Side-by-side comparison of multiple projection scenarios
dk_sample_data() Built-in sample fiscal datasets

All objects returned by dk_project(), dk_decompose(), dk_fan_chart(), dk_stress_test(), dk_bohn_test(), and dk_compare() have print(), summary(), and plot() methods.

Academic references

Package Description
yieldcurves Yield curve fitting and analysis
inflationkit Inflation analysis and core measures
fred Federal Reserve Economic Data
readoecd OECD data access

Issues

Found a bug or have a feature request? Please open an issue on GitHub.

Keywords

r, r-package, debt-sustainability, fiscal-policy, public-debt, macroeconomics, imf, sovereign-risk, fan-chart, stress-test