Replication code for a DFO/ONE Data analysis of sovereign borrowing costs for low- and lower-middle-income countries. The analysis underpins the paper Priced Out: The rising cost of borrowing for low- and lower-middle-income countries quantifying how much more these countries pay for external debt compared to concessional alternatives — and how that burden has shifted as more countries access, or are shut out of, international bond markets.
The pipeline has four analytical components, corresponding to sections of the paper:
Sections 1-2 — Average borrowing rates by creditor and country group Commitment-weighted average interest rates, maturities, and grace periods from the World Bank International Debt Statistics (IDS), broken down by creditor type (Paris Club, China, bondholders, IBRD, IDA, all creditors) and by IDA borrowing classification (IDA-only, Blend, IBRD-only).
Section 3 — Composition-adjusted market rate A sample-selection-corrected measure of what the bond market charges, accounting for selection bias by assigning implied rates to non-issuers with outstanding bond debt.
Section 4 — Present Value Repayment (PVR) analysis A comparison of the true cost of different lending sources on a like-for-like basis using PVR and excess cost calculations.
See PVR_methodology.md for detailed methodology.
- Python 3.13+
- uv (recommended)
uv syncCreate a .env file in the project root with your FRED API key:
FRED_API_KEY=your_key_here
Get a free key at https://fred.stlouisfed.org/docs/api/api_key.html
The pipeline will fall back to cached Treasury yields if the key is missing, but will raise a clear error if no cached data is available either.
The full analysis uses secondary market bond yield data from Cbonds. This data is proprietary and not included in the repository. To use it, place the exported CSV file at raw_data/cbonds_yearly.csv.
Without this file, the pipeline will continue using Damodaran rating-implied spreads only and will print a warning.
Run the full pipeline:
uv run python -m src.pipelineThis downloads fresh IDS data, then runs Sections 1-2, 3, and 4 in sequence for Blend and IBRD-only country groups. To skip the data download:
uv run python -m src.pipeline --skip-fetchRun tests:
uv run pytest tests/DFO-cost-of-borrowing/
├── src/
│ ├── pipeline.py # Single entry point — runs all four components
│ ├── config.py # Paths, indicator codes, counterpart group definitions
│ ├── pv.py # Present value math (amortising and bullet structures)
│ ├── data/ # Data loading and API access
│ ├── borrowing_rates/ # Sections 1-2: average rates by creditor
│ ├── market_rates/ # Section 3: composition-adjusted market rate
│ └── pvr/ # Section 4: PVR and excess cost analysis
├── raw_data/ # Input data files
├── output/ # Generated results
└── tests/ # pytest test suite
| Source | What it provides | How it is accessed |
|---|---|---|
| World Bank IDS API | Borrowing terms, commitments, debt outstanding | src/data/fetch_ids.py downloads to raw_data/ids_data.parquet |
| Cbonds | Secondary market sovereign bond yields | Proprietary — manual export to raw_data/cbonds_yearly.csv |
| Damodaran (NYU) | Sovereign default spreads by credit rating | Downloaded automatically by src/data/damodaran.py |
| FRED (St. Louis Fed) | US Treasury constant-maturity yields | src/data/treasury.py via API; requires FRED_API_KEY in .env |
uv run ruff check src/ # Lint
uv run ruff format src/ # Format
uv run ty check # Type checkPre-commit hooks are configured to run automatically:
pre-commit install
pre-commit run --all-filesThis project is licensed under the MIT License — see the LICENSE file for details.