MyNixOS website logo
Description

Debt Sustainability Analysis and Fiscal Risk Assessment.

Analyses government debt sustainability using the standard debt dynamics framework from Blanchard (1990) <doi:10.1787/budget-v2-art12-en> and the IMF Debt Sustainability Analysis methodology (IMF, 2013) and the Sovereign Risk and Debt Sustainability Framework (IMF, 2022). Projects debt-to-GDP paths, decomposes historical debt changes into interest, growth, and primary balance contributions, and estimates fiscal reaction functions following Bohn (1998) <doi:10.1162/003355398555793>. Produces stochastic fan charts via Monte Carlo simulation, standardised stress tests, and IMF- style heat map risk assessments. Computes S1/S2 sustainability gap indicators used by the European Commission. All methods are pure computation with no external dependencies beyond base R; works with fiscal data from any source.

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

InputWhat it is
debtDebt-to-GDP ratio (0.90 = 90%)
interest_rateInterest rate on government debt (0.04 = 4%)
gdp_growthNominal GDP growth (0.03 = 3%)
primary_balanceRevenue 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

SourceCoverageSeries you needHow to get into R
OECD38 countriesGGDEBT, NGDP_RPCH, GGXONLB_NGDPreadoecd
IMF WEO190+ countriesGGXWDG_NGDP, NGDP_RPCH, GGXONLB_NGDPDownload CSV from imf.org
FRED (US)United StatesGFDEGDQ188S, GDPC1, FYFSGDA188Sfred
EurostatEU membersgov_10dd_edpt1, nama_10_gdpeurostat package
World Bank200+ countriesGC.DOD.TOTL.GD.ZS, NY.GDP.MKTP.KD.ZGWDI package

Functions

FunctionDescription
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

  • Blanchard, O.J. (1990). "Suggestions for a New Set of Fiscal Indicators." OECD Economics Department Working Papers, No. 79. doi:10.1787/budget-v2-art12-en
  • Bohn, H. (1998). "The Behavior of U.S. Public Debt and Deficits." Quarterly Journal of Economics, 113(3), 949-963. doi:10.1162/003355398555793
  • Ghosh, A.R., Kim, J.I., Mendoza, E.G., Ostry, J.D. and Qureshi, M.S. (2013). "Fiscal Fatigue, Fiscal Space and Debt Sustainability in Advanced Economies." The Economic Journal, 123(566), F4-F30.
  • IMF (2013). Staff Guidance Note for Public Debt Sustainability Analysis in Market-Access Countries. IMF Policy Paper.
  • IMF (2022). Staff Guidance Note on the Sovereign Risk and Debt Sustainability Framework for Market Access Countries. IMF Policy Paper.
  • European Commission (2024). Fiscal Sustainability Report. Institutional Paper 291.

Related packages

PackageDescription
yieldcurvesYield curve fitting and analysis
inflationkitInflation analysis and core measures
fredFederal Reserve Economic Data
readoecdOECD 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.

Metadata

Version

0.1.2

License

Unknown

Platforms (80)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    uefi
    WASI
    Windows
Show all
  • aarch64-darwin
  • aarch64-freebsd
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64-uefi
  • aarch64-windows
  • aarch64_be-none
  • arc-linux
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-linux
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • sh4-linux
  • vc4-none
  • wasm32-wasi
  • wasm64-wasi
  • x86_64-cygwin
  • x86_64-darwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-uefi
  • x86_64-windows