Illustrative example

This example reproduces the Borvelia scenarios using the canonical Tiny DSA inputs. Borvelia is a stylized small open economy with general-government debt of 60 percent of GDP at the end of year 0. The authorities assume flat real GDP growth of 3.5 percent, flat real interest rates of 4.0 percent, and a fiscal-consolidation path that moves the primary balance from a deficit of 1.0 percent of GDP in year 1 to a surplus of 1.0 percent of GDP by year 5.

Three shocks begin in year 2 and persist through the end of the five-year horizon:

import polars as pl

from tiny_dsa.api import (
    make_context,
    set_country_name,
    set_country_initial_debt,
    set_growth_baseline,
    set_interest_baseline,
    set_primary_balance_baseline,
    set_shock_year,
    set_shock_type,
    set_shock_magnitudes,
    compute_output_baseline,
    compute_output_shocked,
    compute_output_delta,
)


def projection_frame(records: list[dict[str, object]], value_name: str) -> pl.DataFrame:
    return (
        pl.DataFrame(records)
        .sort('TIME_PERIOD')
        .select(
            'TIME_PERIOD',
            pl.col('OBS_VALUE').alias(value_name),
        )
    )


def debt_frame(
    records: list[dict[str, object]],
    scenario: str,
    initial_debt: float,
) -> pl.DataFrame:
    projection = (
        pl.DataFrame(records)
        .sort('TIME_PERIOD')
        .select(
            pl.lit(scenario).alias('Scenario'),
            'TIME_PERIOD',
            pl.col('OBS_VALUE').alias('Debt (% of GDP)'),
        )
    )
    initial = pl.DataFrame({
        'Scenario': [scenario],
        'TIME_PERIOD': [0],
        'Debt (% of GDP)': [initial_debt],
    })
    return pl.concat([initial, projection])


ctx = make_context()

Configure the shared Borvelia inputs once. Scalar setters take bare scalar values; the time-series setters below use one-dimensional sequences in projection-year order; keyed series use records.

initial_debt = 60.0

set_country_name(ctx, "Borvelia")
set_country_initial_debt(ctx, [
    {"COUNTRY": "Borvelia", "OBS_VALUE": initial_debt},
])

set_growth_baseline(ctx, [3.5, 3.5, 3.5, 3.5, 3.5])
set_interest_baseline(ctx, [4.0, 4.0, 4.0, 4.0, 4.0])
set_primary_balance_baseline(ctx, [-1.0, -0.5, 0.0, 0.5, 1.0])

set_shock_year(ctx, 2)
set_shock_magnitudes(ctx, [
    {"SHOCK_PARAMETER": "Growth", "OBS_VALUE": -2.0},
    {"SHOCK_PARAMETER": "Interest", "OBS_VALUE": 2.0},
    {"SHOCK_PARAMETER": "Primary balance", "OBS_VALUE": -1.0},
])

The baseline trajectory

Under baseline assumptions, the snowball factor is (1 + 0.04) / (1 + 0.035) = 1.00483. Applied recursively to the initial debt stock and net of the consolidating primary balance, the debt ratio rises modestly during the deficit years before declining as the primary balance turns positive.

baseline_records = compute_output_baseline(ctx=ctx)
baseline_path = debt_frame(baseline_records, 'Baseline', initial_debt)

baseline_path.with_columns(
    pl.col('Debt (% of GDP)').round(2),
)
shape: (6, 3)
Scenario TIME_PERIOD Debt (% of GDP)
str i64 f64
"Baseline" 0 60.0
"Baseline" 1 61.29
"Baseline" 2 62.09
"Baseline" 3 62.39
"Baseline" 4 62.19
"Baseline" 5 61.49

Year-1 debt is 61.29 percent of GDP, and year-5 debt rounds to 61.49 percent of GDP. The fiscal consolidation roughly offsets the modestly positive snowball factor.

The growth-shock scenario

For the growth shock, set shock_type to 1. The configured growth-shock magnitude is -2.0 percentage points, so real growth falls from 3.5 percent to 1.5 percent from year 2 onward. The snowball factor in years 2 through 5 rises to (1 + 0.04) / (1 + 0.015) = 1.02463.

set_shock_type(ctx, 1)

growth_records = compute_output_shocked(ctx=ctx)
growth_delta_records = compute_output_delta(ctx=ctx)
growth_shock_path = debt_frame(growth_records, 'Growth shock', initial_debt)

growth_summary = (
    growth_shock_path
    .filter(pl.col('TIME_PERIOD') > 0)
    .join(
        projection_frame(growth_delta_records, 'Delta vs baseline (pp)'),
        on='TIME_PERIOD',
    )
    .with_columns(
        pl.col('Debt (% of GDP)').round(2),
        pl.col('Delta vs baseline (pp)').round(2),
    )
)

growth_summary
shape: (5, 4)
Scenario TIME_PERIOD Debt (% of GDP) Delta vs baseline (pp)
str i64 f64 f64
"Growth shock" 1 61.29 0.0
"Growth shock" 2 63.3 1.21
"Growth shock" 3 64.86 2.47
"Growth shock" 4 65.96 3.77
"Growth shock" 5 66.58 5.09

Year 1 is unchanged because the shock begins in year 2. By year 5, debt reaches 66.58 percent of GDP, 5.09 percentage points above the baseline. The consolidation path is preserved, but the higher snowball factor prevents the debt ratio from reversing within the five-year horizon.

The interest-rate-shock scenario

For the interest-rate shock, set shock_type to 2. The configured interest-rate shock is +2.0 percentage points, so the real interest rate rises from 4.0 percent to 6.0 percent from year 2 onward. The snowball factor becomes (1 + 0.06) / (1 + 0.035) = 1.02415.

set_shock_type(ctx, 2)

interest_records = compute_output_shocked(ctx=ctx)
interest_delta_records = compute_output_delta(ctx=ctx)
interest_shock_path = debt_frame(interest_records, 'Interest shock', initial_debt)

interest_summary = (
    interest_shock_path
    .filter(pl.col('TIME_PERIOD') > 0)
    .join(
        projection_frame(interest_delta_records, 'Delta vs baseline (pp)'),
        on='TIME_PERIOD',
    )
    .with_columns(
        pl.col('Debt (% of GDP)').round(2),
        pl.col('Delta vs baseline (pp)').round(2),
    )
)

interest_summary
shape: (5, 4)
Scenario TIME_PERIOD Debt (% of GDP) Delta vs baseline (pp)
str i64 f64 f64
"Interest shock" 1 61.29 0.0
"Interest shock" 2 63.27 1.18
"Interest shock" 3 64.8 2.41
"Interest shock" 4 65.86 3.68
"Interest shock" 5 66.45 4.97

The trajectory closely tracks the growth-shock path because the two snowball factors are nearly identical. Year-5 debt is 66.45 percent of GDP, 4.97 percentage points above the baseline. The small difference from the growth shock reflects the convexity of the (1 + r) / (1 + g) ratio.

The primary-balance-shock scenario

For the primary-balance shock, set shock_type to 3. The configured primary-balance shock is -1.0 percentage point, so the primary balance is one percentage point of GDP lower from year 2 onward. The snowball factor remains at the baseline value of 1.00483.

set_shock_type(ctx, 3)

primary_balance_records = compute_output_shocked(ctx=ctx)
primary_balance_delta_records = compute_output_delta(ctx=ctx)
primary_balance_shock_path = debt_frame(
    primary_balance_records,
    'Primary balance shock',
    initial_debt,
)

primary_balance_summary = (
    primary_balance_shock_path
    .filter(pl.col('TIME_PERIOD') > 0)
    .join(
        projection_frame(primary_balance_delta_records, 'Delta vs baseline (pp)'),
        on='TIME_PERIOD',
    )
    .with_columns(
        pl.col('Debt (% of GDP)').round(2),
        pl.col('Delta vs baseline (pp)').round(2),
    )
)

primary_balance_summary
shape: (5, 4)
Scenario TIME_PERIOD Debt (% of GDP) Delta vs baseline (pp)
str i64 f64 f64
"Primary balance shock" 1 61.29 0.0
"Primary balance shock" 2 63.09 1.0
"Primary balance shock" 3 64.39 2.0
"Primary balance shock" 4 65.2 3.01
"Primary balance shock" 5 65.52 4.03

Year-5 debt is 65.52 percent of GDP, 4.03 percentage points above the baseline. The shock is smaller than the growth and interest shocks, and its effect is direct rather than amplified through the snowball factor, so the cumulative debt impact is approximately linear in the cumulative shock dose.

Full trajectories

The compute functions return projection years 1 through 5. The final table prepends the known year-0 debt stock and reports all trajectories in percent of GDP.

trajectory_table = (
    pl.concat([
        baseline_path,
        growth_shock_path,
        interest_shock_path,
        primary_balance_shock_path,
    ])
    .with_columns(
        pl.col('Debt (% of GDP)').round(2),
    )
    .pivot(
        on='TIME_PERIOD',
        index='Scenario',
        values='Debt (% of GDP)',
    )
    .select('Scenario', '0', '1', '2', '3', '4', '5')
)

trajectory_table
shape: (4, 7)
Scenario 0 1 2 3 4 5
str f64 f64 f64 f64 f64 f64
"Baseline" 60.0 61.29 62.09 62.39 62.19 61.49
"Growth shock" 60.0 61.29 63.3 64.86 65.96 66.58
"Interest shock" 60.0 61.29 63.27 64.8 65.86 66.45
"Primary balance shock" 60.0 61.29 63.09 64.39 65.2 65.52

The resulting table reproduces the Borvelia paths: baseline debt ends at 61.49 percent of GDP, while the growth, interest-rate, and primary-balance shocks end at 66.58, 66.45, and 65.52 percent of GDP, respectively.