---------------------------------------------------------------------- This is the API documentation for the tiny_dsa library. ---------------------------------------------------------------------- ## Functions Public functions compute_all(ctx: 'EvalContext | None' = None, *, inputs: 'dict[str, object] | None' = None) -> 'dict[str, object]' Compute all target cells and return results. make_context(inputs: 'dict[str, object] | None' = None) -> 'EvalContext' Create an EvalContext with merged inputs. list_setters() -> 'list[str]' Return generated series-binding setter function names. list_computes() -> 'list[str]' Return generated series-binding compute function names. set_country_initial_debt(ctx: 'EvalContext', records: 'SeriesInput', *, strict: 'bool' = True, empty_measure: 'EmptyMeasure' = 'write') -> 'None' Set the initial debt-to-GDP ratios for countries in the profile table. Updates the initial debt-to-GDP values for the selected countries in the Tiny-DSA country profile lookup table. Each record matches a row in the country profile table by the COUNTRY field; the OBS_VALUE is written to the corresponding initial debt cell. Args: records (SeriesInput): A list of records, a single record dict, a tidy pandas/polars DataFrame, or a 1-D iterable of measure values in key order. empty_measure (EmptyMeasure): How to treat rows with missing measure values (`None` or float NaN after DataFrame coercion). "write" (default) passes values through; "skip" drops them; "error" raises. Empty key fields always raise. Required record fields: - COUNTRY: Country name as listed in the country profile table. - OBS_VALUE: The initial debt-to-GDP ratio, expressed as a percentage of GDP. Optional record fields: - INDICATOR: The economic indicator that the observation value represents. If supplied, expected value: "initial_debt_to_gdp". - UNIT_MEASURE: Unit of measurement for the debt-to-GDP ratio. If supplied, expected value: "PC_GDP". Returns: None: Applies the input updates to ctx. Source binding: Workbook range: Inputs!B10:B12 Layout: series Value type: float Examples: set_country_initial_debt(ctx, [ {'COUNTRY': 'Borvelia', 'OBS_VALUE': 60.0}, {'COUNTRY': 'Litellia', 'OBS_VALUE': 80.0}, ]) set_country_initial_debt(ctx, [60.0, 80.0]) set_country_name(ctx: 'EvalContext', records: 'Records | Record | Scalar', *, strict: 'bool' = True) -> 'None' Set the country name for the debt sustainability analysis. Updates the selected country name in the Inputs sheet, which drives the initial debt-to-GDP ratio lookup. Input is normalized to a record with PARAMETER 'country_name' and OBS_VALUE; the setter checks record structure and key fields but does not validate the country name against the profile table. Args: records (Scalar | Record | Records): A bare scalar value, a single record dict, or a list of records. Required record fields: - OBS_VALUE: The name of the country to set. Optional record fields: - PARAMETER: The parameter being set, identifying the workbook input cell. If supplied, expected value: "country_name". Returns: None: Applies the input updates to ctx. Source binding: Workbook range: Inputs!B5 Layout: scalar Value type: string Examples: set_country_name(ctx, 'Borvelia') set_growth_baseline(ctx: 'EvalContext', records: 'SeriesInput', *, strict: 'bool' = True, empty_measure: 'EmptyMeasure' = 'write') -> 'None' Set baseline real GDP growth rates for projection years 1 through 5. Updates the growth_baseline named range (Inputs!C16:G16) with the provided growth values. Each record’s OBS_VALUE is assigned to the cell in the growth_baseline range corresponding to its TIME_PERIOD. Args: records (SeriesInput): A list of records, a single record dict, a tidy pandas/polars DataFrame, or a 1-D iterable of measure values in key order. empty_measure (EmptyMeasure): How to treat rows with missing measure values (`None` or float NaN after DataFrame coercion). "write" (default) passes values through; "skip" drops them; "error" raises. Empty key fields always raise. Required record fields: - TIME_PERIOD: Projection year. - OBS_VALUE: Real GDP growth rate in percent per annum. Optional record fields: - INDICATOR: Economic indicator for the series. If supplied, expected value: "real_gdp_growth". - UNIT_MEASURE: Unit of measure for the observation value. If supplied, expected value: "PERCENT_PER_ANNUM". Returns: None: Applies the input updates to ctx. Source binding: Workbook range: Inputs!C16:G16 Layout: series Value type: float Examples: set_growth_baseline(ctx, [ {'TIME_PERIOD': 1, 'OBS_VALUE': 3.5}, {'TIME_PERIOD': 2, 'OBS_VALUE': 3.5}, ]) set_growth_baseline(ctx, [3.5, 3.5]) set_interest_baseline(ctx: 'EvalContext', records: 'SeriesInput', *, strict: 'bool' = True, empty_measure: 'EmptyMeasure' = 'write') -> 'None' Set the baseline real interest rates for projection years 1 through 5. Sets the annual real interest rate baseline values in the Inputs sheet. Each record's `TIME_PERIOD` maps to a projection year header, and `OBS_VALUE` sets the corresponding cell in the Inputs!C17:G17 range. Args: records (SeriesInput): A list of records, a single record dict, a tidy pandas/polars DataFrame, or a 1-D iterable of measure values in key order. empty_measure (EmptyMeasure): How to treat rows with missing measure values (`None` or float NaN after DataFrame coercion). "write" (default) passes values through; "skip" drops them; "error" raises. Empty key fields always raise. Required record fields: - TIME_PERIOD: The projection year identifying the column in the baseline range. - OBS_VALUE: The real interest rate value for the projection year. Optional record fields: - INDICATOR: A constant identifying the series as real interest rate data. If supplied, expected value: "real_interest_rate". - UNIT_MEASURE: The unit of measure for the interest rate values. If supplied, expected value: "PERCENT_PER_ANNUM". Returns: None: Applies the input updates to ctx. Source binding: Workbook range: Inputs!C17:G17 Layout: series Value type: float Examples: set_interest_baseline(ctx, [ {'TIME_PERIOD': 1, 'OBS_VALUE': 4.0}, {'TIME_PERIOD': 2, 'OBS_VALUE': 4.0}, ]) set_interest_baseline(ctx, [4.0, 4.0]) set_primary_balance_baseline(ctx: 'EvalContext', records: 'SeriesInput', *, strict: 'bool' = True, empty_measure: 'EmptyMeasure' = 'write') -> 'None' Set the baseline primary balance path for projection years 1 through 5. Updates the primary balance baseline series with the provided records. Each record supplies a projection year (TIME_PERIOD) and its corresponding primary balance value (OBS_VALUE) for the baseline path. Args: records (SeriesInput): A list of records, a single record dict, a tidy pandas/polars DataFrame, or a 1-D iterable of measure values in key order. empty_measure (EmptyMeasure): How to treat rows with missing measure values (`None` or float NaN after DataFrame coercion). "write" (default) passes values through; "skip" drops them; "error" raises. Empty key fields always raise. Required record fields: - TIME_PERIOD: Projection year index, from 1 to 5. - OBS_VALUE: Primary balance as a percent of GDP; positive values denote a surplus. Optional record fields: - INDICATOR: Series indicator identifier. If supplied, expected value: "primary_balance". - UNIT_MEASURE: Unit of measure for the observation values. If supplied, expected value: "PC_GDP". Returns: None: Applies the input updates to ctx. Source binding: Workbook range: Inputs!C18:G18 Layout: series Value type: float Examples: set_primary_balance_baseline(ctx, [ {'TIME_PERIOD': 1, 'OBS_VALUE': -1.0}, {'TIME_PERIOD': 2, 'OBS_VALUE': -0.5}, ]) set_primary_balance_baseline(ctx, [-1.0, -0.5]) set_shock_magnitudes(ctx: 'EvalContext', records: 'SeriesInput', *, strict: 'bool' = True, empty_measure: 'EmptyMeasure' = 'write') -> 'None' Set the shock magnitudes for each shock parameter (growth, interest, primary balance). Defines the magnitude of the shock for each of the three possible affected parameters. Each record represents one shock magnitude cell in the shock table (Inputs!B26:D26); the SHOCK_PARAMETER field maps to the column header. Args: records (SeriesInput): A list of records, a single record dict, a tidy pandas/polars DataFrame, or a 1-D iterable of measure values in key order. empty_measure (EmptyMeasure): How to treat rows with missing measure values (`None` or float NaN after DataFrame coercion). "write" (default) passes values through; "skip" drops them; "error" raises. Empty key fields always raise. Required record fields: - SHOCK_PARAMETER: The parameter affected by the shock (e.g., 'Growth', 'Interest', or 'Primary Balance'). - OBS_VALUE: The shock magnitude applied to the parameter. Optional record fields: - PARAMETER: Constant field identifying the data as shock magnitude entries. If supplied, expected value: "shock_magnitude". - UNIT_MEASURE: Unit of measure for the shock magnitude. If supplied, expected value: "PP". Returns: None: Applies the input updates to ctx. Source binding: Workbook range: Inputs!B26:D26 Layout: series Value type: float Examples: set_shock_magnitudes(ctx, [ {'SHOCK_PARAMETER': 'Growth', 'OBS_VALUE': -2.0}, {'SHOCK_PARAMETER': 'Interest', 'OBS_VALUE': 2.0}, ]) set_shock_magnitudes(ctx, [-2.0, 2.0]) set_shock_type(ctx: 'EvalContext', records: 'Records | Record | Scalar', *, strict: 'bool' = True) -> 'None' Set the shock type for the scenario configuration. Updates the shock type in the Inputs sheet to specify which macroeconomic or fiscal parameter the shock affects. The OBS_VALUE field of the record is written to the scalar cell Inputs!B22. Args: records (Scalar | Record | Records): A bare scalar value, a single record dict, or a list of records. Required record fields: - OBS_VALUE: The shock type code: 1 for real GDP growth, 2 for real interest rate, 3 for primary balance. Optional record fields: - PARAMETER: Identifies the conceptual parameter being configured. If supplied, expected value: "shock_type". Returns: None: Applies the input updates to ctx. Source binding: Workbook range: Inputs!B22 Layout: scalar Value type: int Examples: set_shock_type(ctx, 1) set_shock_year(ctx: 'EvalContext', records: 'Records | Record | Scalar', *, strict: 'bool' = True) -> 'None' Set the shock year in the Tiny-DSA Inputs sheet. Updates the value of the shock year, an integer between 1 and 5 that determines the first projection year in which the selected shock takes effect. Each record corresponds to the scalar shock year value; the OBS_VALUE field holds the integer year. Args: records (Scalar | Record | Records): A bare scalar value, a single record dict, or a list of records. Required record fields: - OBS_VALUE: The integer year, from 1 to 5, in which the shock begins. Optional record fields: - PARAMETER: Identifies the record as belonging to the shock_year parameter. If supplied, expected value: "shock_year". Returns: None: Applies the input updates to ctx. Source binding: Workbook range: Inputs!B21 Layout: scalar Value type: int Examples: set_shock_year(ctx, 2) compute_output_baseline(ctx=None, *, inputs=None) -> 'Records' Compute the baseline debt-to-GDP trajectory for projection years 1 through 5. Return the baseline debt-to-GDP path as a list of records. Each record maps to a cell in Outputs!B12:F12, with TIME_PERIOD as the year and OBS_VALUE as the debt-to-GDP ratio. Args: ctx (EvalContext | None): Existing evaluation context, if available. inputs (dict[str, object] | None): Optional input map when ctx is omitted. Returns: Records: Computed output records. Required record fields: - TIME_PERIOD: Projection year (1 through 5). - OBS_VALUE: Debt-to-GDP ratio as a percentage of GDP. Optional record fields: - SCENARIO: Scenario identifier distinguishing baseline from shocked output. If supplied, expected value: "baseline". - UNIT_MEASURE: Unit of measure for the OBS_VALUE. If supplied, expected value: "PC_GDP". Source binding: Workbook range: Outputs!B12:F12 Layout: series Value type: float Examples: compute_output_baseline(ctx=ctx) compute_output_delta(ctx=None, *, inputs=None) -> 'Records' Compute the difference between the shocked and baseline debt-to-GDP paths over the projection horizon. Return a list of records, each containing a projection year and the corresponding difference, in percentage points, between the shocked and baseline debt-to-GDP ratios. Each record corresponds to one cell in the Outputs!B14:F14 range, providing the delta value for a single projection year. Args: ctx (EvalContext | None): Existing evaluation context, if available. inputs (dict[str, object] | None): Optional input map when ctx is omitted. Returns: Records: Computed output records. Required record fields: - TIME_PERIOD: Projection year identifier, ranging from 1 to 5. - OBS_VALUE: Difference between the shocked and baseline debt-to-GDP ratios, expressed in percentage points of GDP. Optional record fields: - SCENARIO: Scenario classification for the delta series. If supplied, expected value: "shocked_minus_baseline". - UNIT_MEASURE: Unit of measurement for the debt ratio difference. If supplied, expected value: "PP". Source binding: Workbook range: Outputs!B14:F14 Layout: series Value type: float Examples: compute_output_delta(ctx=ctx) compute_output_shocked(ctx=None, *, inputs=None) -> 'Records' Returns the shocked debt-to-GDP path as a series of records. Provides the projected debt-to-GDP trajectory under the shocked scenario for projection years 1 through 5. Each record represents one projection year, with the observation value in percent of GDP. Args: ctx (EvalContext | None): Existing evaluation context, if available. inputs (dict[str, object] | None): Optional input map when ctx is omitted. Returns: Records: Computed output records. Required record fields: - TIME_PERIOD: Projection year (1–5). - OBS_VALUE: Debt-to-GDP ratio under the shocked scenario. Optional record fields: - SCENARIO: Scenario identifier. If supplied, expected value: "shocked". - UNIT_MEASURE: Unit of measure for the observation value. If supplied, expected value: "PC_GDP". Source binding: Workbook range: Outputs!B13:F13 Layout: series Value type: float Examples: compute_output_shocked(ctx=ctx) ---------------------------------------------------------------------- This is the User Guide documentation for the package. ---------------------------------------------------------------------- ### Introduction Tiny DSA is a stylized debt-sustainability tool that projects a country's general-government debt-to-GDP ratio over a five-year horizon under a baseline macroeconomic and fiscal path and one configurable shock.[^1] It is intentionally minimal. The model implements a single public-debt accounting identity, recursed annually for five periods. It does not reproduce the indicator-and-threshold structure of operational debt-sustainability frameworks. Install the Python package directly from GitHub with: `uv add "tiny-dsa @ git+https://github.com/Teal-Insights/py-tiny-dsa"` This package is a Python reimplementation of the source Excel workbook. It was produced using programmatic extraction, machine translation, and AI-assisted documentation rewriting. The purpose of the package documentation is to make the workbook logic easier to inspect, run, and reproduce from Python while preserving the source model's intent and limitations. The intended reader is a domain economist familiar with the standard public-debt accounting identity, who needs to understand what the tool computes, what its inputs and outputs mean, and where its simplifications depart from operational practice. The debt ratio and the primary balance are expressed as percentages of GDP. Real GDP growth and the real interest rate are expressed in percent per annum. The tool projects in real terms throughout. Tiny DSA deliberately omits several features that are important in operational frameworks: the contribution of inflation to the debt ratio; exchange-rate dynamics and the currency composition of debt; residual financing terms such as stock-flow adjustments, contingent-liability calls, and other below-the-line operations; and any endogenous response of the primary balance to the macroeconomic environment.[^2] The fiscal stance is taken as exogenous, the snowball factor is computed in real terms, and shocks are step changes to a single parameter from a configurable shock year onward. These omissions keep the model small enough to be traced end to end. The rest of this documentation explains how to use the Python package and interpret its outputs. It covers the model interface, the baseline and shock inputs, illustrative scenarios using the fictional country Borvelia, and the methodology behind the debt-dynamics identity, shock specification, realism checks, and modeling limitations. [^1]: Tiny DSA is a methodological artifact prepared by Teal Insights. It is not intended for policy use, is not validated against country-specific data, and is not endorsed by the IMF, the World Bank, or any other institution. The modeling choices follow the standard public-debt accounting literature; the documentation conventions follow the IMF/World Bank LIC-DSF and IMF FAD Q-CRAFT guidance documents. [^2]: For the corresponding treatment in operational frameworks, see International Monetary Fund and World Bank (2018), Sections II.B–II.D and Appendix III, and the discussion in Escolano (2010). For correctness evidence, see [Excel parity validation](03-excel-parity-validation.qmd), which summarizes the exported-library differential test against the source workbook. ### Functional overview Tiny DSA is organized around a small generated Python API rather than direct workbook navigation. The workflow has three layers: 1. Create an evaluation context with `make_context()`. 2. Configure the scenario with `set_*` functions. 3. Read the public output series with `compute_*` functions. The same conceptual separation is preserved from the workbook design: inputs feed the calculation layer, and downstream consumers should read only the stable output functions. Internal baseline and shocked paths are calculation surfaces; production code should use `compute_output_baseline`, `compute_output_shocked`, and `compute_output_delta`. ### A. API management rules Tiny DSA's Python interface replaces workbook formatting conventions with explicit function boundaries: - User-editable inputs are set through `set_*` functions. - Country profile initial-debt values are loaded by default and can be updated for existing profile countries with `set_country_initial_debt` when required. - Computed outputs are read-only from the caller's perspective. Do not write output values into the context; call the relevant `compute_*` function instead. - Intermediate calculation ranges are not part of the stable public API. The workbook's number formats are not applied by the API. Computed records return numeric values, and presentation code should format ratios, signed deltas, and shock magnitudes as needed. Use the documented input domains when configuring scenarios: - `set_country_name` selects the country profile. The setter checks record structure but does not, by itself, validate the country name against the profile table. - `set_shock_year` is intended for projection years 1 through 5. - `set_shock_type` uses 1 for real GDP growth, 2 for the real interest rate, and 3 for the primary balance. The stable Python contract is summarized below. | Workbook concept | Python API | Role | |---|---|---| | Selected country | `set_country_name` | Sets the country used to resolve initial debt-to-GDP from the profile data | | Country initial debt profile | `set_country_initial_debt` | Updates initial debt-to-GDP values for keyed country records when a profile override is needed | | Baseline real GDP growth | `set_growth_baseline` | Sets the five-year growth path | | Baseline real interest rate | `set_interest_baseline` | Sets the five-year interest-rate path | | Baseline primary balance | `set_primary_balance_baseline` | Sets the five-year primary-balance path | | Shock year | `set_shock_year` | Sets the first projection year in which the shock applies | | Shock type | `set_shock_type` | Selects the affected parameter: growth, interest, or primary balance | | Shock magnitudes | `set_shock_magnitudes` | Sets the available shock magnitudes by shock parameter | | Baseline output path | `compute_output_baseline` | Returns the public baseline debt-to-GDP series | | Shocked output path | `compute_output_shocked` | Returns the public shocked debt-to-GDP series | | Shock impact | `compute_output_delta` | Returns shocked minus baseline, in percentage points | ### B. Selecting a country The first scenario input is the country name. In Python, create a context and set the selected country with `set_country_name(ctx, "Borvelia")` or another supported profile name. Tiny DSA ships with a small profile table containing Borvelia, Litellia, and Aurelium. Their default initial debt-to-GDP ratios are 60.0, 80.0, and 40.0 percent of GDP, respectively. The selected country drives the initial-debt lookup used in the debt path calculations. In normal use, treat the profile data as reference data. If a scenario requires an override to an existing country profile, use `set_country_initial_debt` with keyed records containing `COUNTRY` and `OBS_VALUE`. ### C. Setting the baseline parameters The baseline scenario is defined by three five-period series: - `set_growth_baseline` sets real GDP growth, in percent per annum. - `set_interest_baseline` sets the effective real interest rate on outstanding government debt, in percent per annum. - `set_primary_balance_baseline` sets the primary balance, in percent of GDP; positive values denote a surplus. Series setters accept either a one-dimensional sequence in projection-year order or keyed records with `TIME_PERIOD` and `OBS_VALUE`. For example, `[3.5, 3.5, 3.5, 3.5, 3.5]` sets years 1 through 5 in order. The default baseline is a flat 3.5 percent growth path, a flat 4.0 percent real interest-rate path, and a fiscal-consolidation path from a primary deficit of 1.0 percent of GDP in year 1 to a surplus of 1.0 percent of GDP in year 5. ### D. Configuring the shock The shock is defined by three inputs: - `set_shock_year` sets the first year in which the shock takes effect. The shock applies from that year through the end of the five-year horizon. - `set_shock_type` selects the affected parameter: 1 for growth, 2 for interest, and 3 for primary balance. - `set_shock_magnitudes` sets the available magnitude for each shock parameter. The default configuration is a growth shock beginning in year 2. The default magnitudes are -2.0 percentage points for growth, +2.0 percentage points for interest, and -1.0 percentage point for the primary balance. Only the magnitude associated with the selected shock type is applied in a given run. ### E. Reading the outputs The output API returns three public time series for years 1 through 5: - `compute_output_baseline(ctx=ctx)` returns the baseline debt-to-GDP path. - `compute_output_shocked(ctx=ctx)` returns the shocked debt-to-GDP path. - `compute_output_delta(ctx=ctx)` returns shocked minus baseline, in percentage points. Each compute function returns a list of records with `TIME_PERIOD`, `OBS_VALUE`, and output metadata fields such as `SCENARIO` and `UNIT_MEASURE`. Convert these records to a Polars DataFrame for reporting or downstream processing. ```{python} import polars as pl from tiny_dsa.api import ( make_context, set_country_name, 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 path_frame(records: list[dict[str, object]], alias: str) -> pl.DataFrame: return ( pl.DataFrame(records) .sort("TIME_PERIOD") .select( "TIME_PERIOD", pl.col("OBS_VALUE").alias(alias), ) ) ctx = make_context() set_country_name(ctx, "Borvelia") 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_type(ctx, 1) 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}, ], ) baseline = path_frame(compute_output_baseline(ctx=ctx), "Baseline debt (% GDP)") shocked = path_frame(compute_output_shocked(ctx=ctx), "Shocked debt (% GDP)") delta = path_frame(compute_output_delta(ctx=ctx), "Delta (pp)") output = baseline.join(shocked, on="TIME_PERIOD").join(delta, on="TIME_PERIOD") output ``` This table is the Python equivalent of the workbook's public Outputs sheet. Keep scenario context such as country name, shock year, and shock type in the calling code or adjacent metadata, and use the compute functions as the stable read-only interface for trajectory values. ### 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: - a 2-percentage-point reduction in real GDP growth; - a 2-percentage-point increase in the real interest rate; - a 1-percentage-point reduction in the primary balance. ```{python} 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. ```{python} 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. ```{python} 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), ) ``` 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`. ```{python} 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 ``` 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`. ```{python} 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 ``` 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. ```{python} 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 ``` 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. ```{python} 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 ``` 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. ### Excel parity validation Tiny DSA includes an exported validation bundle that checks the generated `tiny_dsa` package against the source Excel workbook. The test drives the workbook with Microsoft Excel through `xlwings`, applies the same inputs through the package's public `set_*` functions, and compares the public `compute_*` outputs cell by cell. ## Current reference result The current reference run is **PASS**: **1,770 / 1,770** cell-level comparisons passed at `atol = 1e-06`. - Result: **PASS** - Generated: `2026-07-12T14:18:16+00:00` - Passed: **1,770** - Failed: **0** - Pass rate: **100.00%** - Acceptance bar: **100.00%** The sweep covers **1,770** cell-level comparisons against Excel. ## What Was Tested The validation checks the exported standalone library, not just the extraction graph. It imports `tiny_dsa.api`, creates a fresh context for each scenario, sets inputs through the records-shaped public setters, computes the exported output series, and compares those values against the workbook's calculated output cells. ## Inspect Or Re-run The validation bundle is shipped in the source repository under `tests/`. Because the golden-master oracle uses Microsoft Excel through COM automation, reruns require Windows with Microsoft Excel installed. - Reference parity report: `tests/results/reference/parity_report.txt` - Validation bundle README: `tests/README.md` - Differential test harness: `tests/differential/differential_test_exported_library.py` To re-run the validation from the exported project: ```pwsh uv run --project . --group validation python -m tests.differential.differential_test_exported_library --layout exported ```