diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b67a8e0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "SWIFT_DOT-FIN"] + path = SWIFT_DOT-FIN + url = https://gitea.d-bis.org/d-bis/SWIFT_DOT-FIN.git diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4a0175d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,24 @@ +# Changelog + +All notable changes to the FQBM project are documented here. + +## [Unreleased] + +- **CBDC in state**: `FQBMState.cbdc_liability`; workbook sets it on CBDC shift; IPSAS statement of financial position (CB/consolidated) shows CBDC liability; dashboard snapshot includes cbdc_liability. +- **Repo multiplier in workbook/dashboard**: `run_workbook(shadow_params=ShadowBankingParams(...))` optional; repo_multiplier always computed and included in `result["dashboard"]["repo_multiplier"]` and `result["repo_multiplier"]`. +- **Real data pipelines**: `fqbm.data.pipelines` — `fetch_fed_h41()`, `fetch_bis_series()` (BIS bulk CSV), `fetch_imf_series()` (IMF API with optional api_key). +- Documentation: RECOMMENDATIONS.md (consolidated list); framework_summary one sentence per Part; REFERENCES weblinks (IPSASB Handbook, BIS bulk, IMF Data, Fed Data Download); IPSAS_COMPLIANCE and GAPS_AND_MISSING reference RECOMMENDATIONS; CONTRIBUTING links RECOMMENDATIONS; USER_GUIDE and DATA_DICTIONARY mention pipelines extension and RECOMMENDATIONS; MISSING_COMPONENTS 2.2 status updated; docstrings: Part X and Required columns in regressions, Part XII in capital_stress. +- IPSAS: structures for IPSAS 3, 5, 9/47, 10, 14, 19, 20, 23, 29/41, 38, 46, 48; cash-basis; RPG 2; functional/presentation currency. +- budget_actual_from_state() for IPSAS 24 from state + budget dict. +- State: functional_currency, presentation_currency. +- Matrix: cross_sector_consistency_check(). +- Part XI: get_case_narrative(name) for historical case narrative and coded outputs. +- Docs: USER_GUIDE, API_REFERENCE, DATA_DICTIONARY, WHITE_PAPER stub, CHANGELOG, CONTRIBUTING; updates to IPSAS_COMPLIANCE, GAPS_AND_MISSING, framework_summary, REFERENCES, README. + +## [0.2.0] + +- L_cb, open_economy_view, reporting_date. IPSAS presentation helpers. Part VII/VIII, Part XI presets. Workbook scenario support and Excel export. + +## [0.1.0] + +- Initial implementation: state, matrix, sheets, Monte Carlo, differential model, regressions. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..93287fb --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,68 @@ +# Contributing to FQBM + +Thank you for your interest in contributing to the Four-Quadrant Balance Sheet Matrix (FQBM) project. + +--- + +## Development setup + +```bash +git clone +cd FOUR-QUADRANT_BALANCE_SHEET_MATRIX +python3 -m venv .venv +source .venv/bin/activate # Windows: .venv\Scripts\activate +pip install -e ".[dev]" # or pip install -e . && pip install pytest +``` + +--- + +## Running tests + +```bash +pytest tests/ -v +``` + +Add new tests under `tests/`; keep module layout aligned with `src/fqbm/`. + +--- + +## Code style + +- Follow existing style in the codebase (PEP 8–style). +- Use type hints for public function parameters and returns where practical. +- Docstrings: include purpose, main parameters, and return value; for IPSAS functions, reference the standard (e.g. IPSAS 3); for regression helpers, note “Required columns” for DataFrames. + +--- + +## Adding a new sheet + +1. Add a module under `src/fqbm/sheets/` (e.g. `new_sheet.py`) with a step function that takes state and params and returns updated state or a result dict. +2. Wire the sheet into `fqbm.workbook.runner.run_workbook()` (read initial state, call the step, write result into the workbook result dict). +3. Optionally add export in `_export_excel()` for the new result key. +4. Add tests in `tests/` that call the new step and assert on outputs. + +--- + +## Adding a new scenario + +1. In `src/fqbm/scenarios/presets.py`, add a builder function (e.g. `_new_scenario()`) that returns a `ScenarioPreset` (name, description, state, optional params and shock_spec). +2. Register it in `_build_registry()`. +3. Optionally add an entry in `get_case_narrative()` for the structured narrative and key_drivers/suggested_shocks. + +--- + +## Documentation + +- User-facing: update [docs/USER_GUIDE.md](docs/USER_GUIDE.md) and [docs/API_REFERENCE.md](docs/API_REFERENCE.md) when adding public API. +- Data: update [docs/DATA_DICTIONARY.md](docs/DATA_DICTIONARY.md) for new state fields, sheet I/O, or regression columns. +- Gaps: if adding IPSAS or matrix-related features, update [docs/IPSAS_COMPLIANCE.md](docs/IPSAS_COMPLIANCE.md) and [docs/GAPS_AND_MISSING.md](docs/GAPS_AND_MISSING.md) as needed. +- Changelog: add a short entry under `[Unreleased]` in [CHANGELOG.md](CHANGELOG.md). +- Recommendations: see [docs/RECOMMENDATIONS.md](docs/RECOMMENDATIONS.md) for the full list of suggestions. + +--- + +## Pull requests + +- Keep PRs focused; prefer smaller, reviewable changes. +- Ensure tests pass and that new code is covered where practical. +- Update the docs and changelog as above. diff --git a/README.md b/README.md index 14de558..7b7dae2 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,31 @@ A unified institutional framework for monetary, fiscal, financial, and open-economy dynamics. This codebase implements the full FQBM from the white paper: simulation workbook (eight sheets), Monte Carlo stress engine, differential system model, and empirical regression suite. +## Table of contents + +- [Quick start](#quick-start) +- [Install](#install) +- [Run](#run) +- [Test](#test) +- [Optional features](#optional-features) +- [IPSAS compliance](#ipsas-compliance) +- [Submodule (SWIFT DOT-FIN)](#submodule-swift-dot-fin) +- [Framework and docs](#framework-and-docs) + +## Quick start + +```python +from fqbm.workbook.runner import run_workbook +from fqbm.state import FQBMState + +state = FQBMState(B=100, R=50, C=20, Loans=800, Deposits=700, E_cb=30, E_b=100) +result = run_workbook(initial_state=state, mc_runs=50, export_path="out.xlsx") +# Or use a scenario: +run_workbook(scenario="asia_1997", mc_runs=100, export_path="asia.xlsx") +``` + +See [docs/USER_GUIDE.md](docs/USER_GUIDE.md) for a full walkthrough. + ## Install ```bash @@ -11,7 +36,7 @@ source .venv/bin/activate # Windows: .venv\Scripts\activate pip install -e . ``` -Optional: `pip install pytest` for tests. +Optional: `pip install pytest` for tests; `pip install streamlit` for the dashboard. ## Run @@ -28,25 +53,31 @@ Optional: `pip install pytest` for tests. pytest tests/ -v ``` -## Optional: Streamlit dashboard +## Optional features -```bash -pip install streamlit -streamlit run scripts/streamlit_dashboard.py -``` - -## Optional: Historical scenarios (Part XI) - -Use preset scenarios in code: `run_workbook(scenario="asia_1997")` or `"gfc_2008"`, `"pandemic_2020"`, `"rate_shock_2022"`. See `fqbm.scenarios`. +- **Streamlit dashboard**: `pip install streamlit` then `streamlit run scripts/streamlit_dashboard.py` +- **Historical scenarios (Part XI)**: `run_workbook(scenario="asia_1997")` or `"gfc_2008"`, `"pandemic_2020"`, `"rate_shock_2022"`; `get_case_narrative(name)` for narrative and coded outputs +- **CBDC / CCP / repo**: `run_workbook(cbdc_params=..., ccp_params=..., shadow_params=...)`; state has `cbdc_liability` after CBDC shift; repo multiplier is in `result["dashboard"]["repo_multiplier"]` +- **Real data**: `from fqbm.data.pipelines import fetch_fed_h41, fetch_bis_series, fetch_imf_series` (Fed H.4.1, BIS bulk CSV, IMF with optional api_key) ## References -See [docs/REFERENCES.md](docs/REFERENCES.md) for Chicago Author–Date references (Adrian & Shin, BIS, Dornbusch, Fed H.4.1, IMF, McLeay et al., Obstfeld & Rogoff, etc.). +See [docs/REFERENCES.md](docs/REFERENCES.md) for Chicago Author–Date references and optional weblinks (IPSASB, BIS, IMF, Fed H.4.1). ## IPSAS compliance -The framework includes an **IPSAS-aligned presentation layer** (IPSAS 1 statement of financial position, IPSAS 24 budget vs actual structure). Use `fqbm.ipsas.presentation.statement_of_financial_position(state, entity="central_bank"|"commercial_bank"|"consolidated")` and `budget_vs_actual_structure()`. Excel export adds IPSAS-style sheets when available. See [docs/IPSAS_COMPLIANCE.md](docs/IPSAS_COMPLIANCE.md) for the compliance assessment. **[docs/GAPS_AND_MISSING.md](docs/GAPS_AND_MISSING.md)** lists IPSAS standards and FQBM gaps. Additional helpers: **notes_to_financial_statements_structure**, **statement_of_financial_position_comparative**, **maturity_risk_disclosure_structure**, **cash_flow_from_state_changes**, **fx_translate**. Part VII: **repo_multiplier**, **margin_spiral_simulation**. Part VIII: **variation_margin_flow**, **ccp_clearing_simulation**. +The framework includes an **IPSAS-aligned presentation layer** (IPSAS 1 statement of financial position, IPSAS 24 budget vs actual, and structures for IPSAS 2, 3, 4, 5, 9, 10, 14, 19, 20, 23, 29/41, 38, 46, 48, cash basis, RPG 2, functional/presentation currency). Use `fqbm.ipsas.presentation.statement_of_financial_position(state, entity="central_bank"|"commercial_bank"|"consolidated")`, `budget_vs_actual_structure()`, `budget_actual_from_state(state, budget)`, and other helpers. Excel export adds IPSAS-style sheets when available. See [docs/IPSAS_COMPLIANCE.md](docs/IPSAS_COMPLIANCE.md) and [docs/GAPS_AND_MISSING.md](docs/GAPS_AND_MISSING.md). -## Framework +## Submodule (SWIFT DOT-FIN) -See [docs/framework_summary.md](docs/framework_summary.md) and the white paper for the theoretical foundation (Parts I–XVI). +This repo includes **SWIFT_DOT-FIN** as a submodule (ledger entries and payloads, creation to transfers). Clone with submodules: `git clone --recurse-submodules `, or after clone run `git submodule update --init --recursive`. The submodule lives at `SWIFT_DOT-FIN/`; its source path is `/home/intlc/projects/SWIFT_DOT-FIN` (or `../SWIFT_DOT-FIN` relative to this project). + +## Framework and docs + +- [docs/framework_summary.md](docs/framework_summary.md) — Parts I–XVI and module mapping +- [docs/WHITE_PAPER.md](docs/WHITE_PAPER.md) — Placeholder for canonical white paper +- [docs/USER_GUIDE.md](docs/USER_GUIDE.md) — Tutorial and usage +- [docs/API_REFERENCE.md](docs/API_REFERENCE.md) — Public API +- [docs/DATA_DICTIONARY.md](docs/DATA_DICTIONARY.md) — State variables, sheet I/O, regression columns +- [docs/MISSING_COMPONENTS_AND_DOCUMENTATION.md](docs/MISSING_COMPONENTS_AND_DOCUMENTATION.md) — Consolidated gap list and documentation checklist +- [docs/RECOMMENDATIONS.md](docs/RECOMMENDATIONS.md) — Additional recommendations and suggestions diff --git a/SWIFT_DOT-FIN b/SWIFT_DOT-FIN new file mode 160000 index 0000000..35d97c3 --- /dev/null +++ b/SWIFT_DOT-FIN @@ -0,0 +1 @@ +Subproject commit 35d97c3b1427ba8212fa2a07b5c9cf5f6f2f247b diff --git a/docs/API_REFERENCE.md b/docs/API_REFERENCE.md new file mode 100644 index 0000000..671fde4 --- /dev/null +++ b/docs/API_REFERENCE.md @@ -0,0 +1,40 @@ +# FQBM API Reference + +Public functions and classes, with main parameters and return types. For full docstrings, see the source. + +## State and matrix + +**fqbm.state** +- `FQBMState`: Dataclass (B, R, C, Loans, Deposits, E_cb, E_b, S, K, Spread, O, L_cb, cbdc_liability, reporting_date, functional_currency, presentation_currency). Methods: to_vector(), from_vector(), copy(). +- `open_economy_view(A_dom, A_ext, L_dom, L_ext, E)`: Returns dict with totals and identity_holds. + +**fqbm.matrix** +- `four_quadrant_matrix(state, L_cb=None)`: pd.DataFrame (Assets Dr/Cr, Liabilities Dr/Cr). +- `four_quadrant_summary(state, L_cb=None)`: dict (total_assets_dr, total_liabilities_dr, total_liabilities_cr, identity_A_eq_L_plus_E). +- `cross_sector_consistency_check(state, L_cb=None)`: dict (consistent, cb_balance, bank_balance, matrix_identity, message, sector/matrix totals). + +## Workbook and sheets + +**fqbm.workbook.runner** +- `run_workbook(initial_state=None, scenario=None, mc_runs=100, export_path=None, include_ipsas=True, cbdc_params=None, ccp_params=None)`: Returns dict (state, stress_tables, fx_parity, sovereign, commodity, monte_carlo, dashboard, ipsas). +- `main()`: CLI entry point. + +**fqbm.sheets**: CentralBankParams/central_bank_step; CommercialBankParams/commercial_bank_step; StressParams/stress_tables; FXParams/fx_parity_step; SovereignParams/sovereign_debt_step; CommodityParams/commodity_step; ShockSpec/run_n_simulations; dashboard_aggregate (includes repo_multiplier when computed); ShadowBankingParams/repo_multiplier, margin_spiral_simulation; CCPParams/variation_margin_flow, ccp_clearing_simulation; CBDCParams/deposit_to_cbdc_shift. + +**fqbm.data.pipelines**: fetch_fed_h41(url?, date_col?); fetch_bis_series(dataset?, country?, api_key?) — dataset in credit_gap, debt_service, property, total_credit, cb_assets; fetch_imf_series(database_id?, indicator?, country?, api_key?) — optional api_key for IMF API. + +## IPSAS presentation (fqbm.ipsas.presentation) + +Statement of financial position; budget_vs_actual_structure; budget_actual_from_state; cash flow structure and cash_flow_from_state_changes; statement of financial performance; statement of changes in net assets; fx_translate; notes template; comparative; maturity_risk_disclosure; accounting_policies (IPSAS 3); borrowing_costs (5); revenue_recognition (9/47); hyperinflation_restatement (10); events_after_reporting_date (14); provisions_contingencies (19); related_party_disclosure (20); revenue_non_exchange (23); financial_instruments_measurement (29/41); interests_in_other_entities (38); measurement_basis (46); transfer_expenses (48); cash_basis_statement; fs_discussion_and_analysis (RPG 2); functional_presentation_currency. + +## Scenarios (fqbm.scenarios.presets) + +- `list_scenarios()`: list of names. +- `get_scenario(name)`: ScenarioPreset or None. +- `get_case_narrative(name)`: dict (name, description, narrative, state_summary, key_drivers, suggested_shocks) or None. + +## System and empirical + +**fqbm.system.differential_model**: DifferentialParams; solve_trajectory(x0, t_span, params); check_stability(params). + +**fqbm.empirical.regressions**: generate_synthetic_inflation, run_inflation_pass_through; generate_synthetic_spread, run_sovereign_spread; generate_synthetic_capital_flow, run_capital_flow_sensitivity. Required columns: see [DATA_DICTIONARY.md](DATA_DICTIONARY.md). Further suggestions: [RECOMMENDATIONS.md](RECOMMENDATIONS.md). diff --git a/docs/DATA_DICTIONARY.md b/docs/DATA_DICTIONARY.md new file mode 100644 index 0000000..1b53fbd --- /dev/null +++ b/docs/DATA_DICTIONARY.md @@ -0,0 +1,25 @@ +# FQBM Data Dictionary + +Definitions of state variables, sheet inputs/outputs, and regression variables. + +## 1. State vector (FQBMState) + +- **B**: Government securities (CB assets). **R**: Reserve balances. **C**: Currency in circulation. **Loans**: Commercial bank loans. **Deposits**: Commercial bank deposits. **E_cb**, **E_b**: Central bank and bank net assets/equity. **S**: Exchange rate (domestic per foreign). **K**: Capital flows. **Spread**: Sovereign spread. **O**: Oil/commodity index. **L_cb**: Central bank loans. **cbdc_liability**: CBDC liability (Part IX). **reporting_date**, **functional_currency**, **presentation_currency**: Optional. + +Identities: B + L_cb = R + C + E_cb; Loans = Deposits + E_b; Total assets (Dr) = Total liab (Dr) + Total liab (Cr). + +## 2. Sheet I/O + +Central bank / commercial bank: state + params -> updated state. Capital stress: state + StressParams -> stress_tables. FX parity: state + FXParams -> fx_parity. Sovereign: state + SovereignParams -> spread, sustainability. Commodity: state + CommodityParams -> commodity. Monte Carlo: state + ShockSpec, n_runs -> list of states. Dashboard: state + optional stress/mc -> dashboard_aggregate. IPSAS: state (and prior) -> presentation DataFrames. + +## 3. Regression columns (Part X) + +Model 1 (inflation pass-through): d_fx, pi/inflation; synthetic: generate_synthetic_inflation(n). Model 2 (sovereign spread): debt_gdp, reserves_gdp, fx_vol, growth_differential, spread; synthetic: generate_synthetic_spread(n). Model 3 (capital flow): spread/r_dom/r_foreign, growth, capital_flow; synthetic: generate_synthetic_capital_flow(n). + +## 4. Four-quadrant matrix + +Assets (Dr): B, L_cb, Loans. Liabilities (Dr): C, R, Deposits. Liabilities (Cr): E_cb, E_b. cross_sector_consistency_check(state) returns consistent, cb_balance, bank_balance, matrix_identity, message. + +## 5. Real data pipelines (fqbm.data.pipelines) + +fetch_fed_h41: Fed H.4.1 CSV (optional url, date_col). fetch_bis_series: BIS bulk CSV (dataset in credit_gap, debt_service, property, total_credit, cb_assets; optional country). fetch_imf_series: IMF data (optional api_key for API). Pipelines can be extended (e.g. more series, caching, retries). See RECOMMENDATIONS.md. diff --git a/docs/GAPS_AND_MISSING.md b/docs/GAPS_AND_MISSING.md index 913cb2b..0200ab6 100644 --- a/docs/GAPS_AND_MISSING.md +++ b/docs/GAPS_AND_MISSING.md @@ -1,152 +1,46 @@ -# Complete IPSAS and FQBM Matrix — Gaps and Missing +# IPSAS and FQBM — Gaps and Missing -This document lists **all remaining IPSAS standards** and **all FQBM/Matrix gaps and missing elements** as of the current codebase. It extends [IPSAS_COMPLIANCE.md](IPSAS_COMPLIANCE.md). +Short summary of **remaining gaps** and **FQBM/Matrix status**. Full scope: [IPSAS_COMPLIANCE.md](IPSAS_COMPLIANCE.md). Suggestions: [RECOMMENDATIONS.md](RECOMMENDATIONS.md). --- -## Part A — IPSAS: Full standards list and status +## 1. IPSAS: Summary by status -| # | Standard | Status | Gap / note | -|---|----------|--------|-------------| -| **CF** | Conceptual Framework | Partial | A = L + E aligned; no revenue/expense/definition of surplus. | -| **1** | Presentation of Financial Statements | Supported | Structure + current/non-current in `fqbm.ipsas.presentation`. | -| **2** | Cash Flow Statements | Structure only | Template in `cash_flow_statement_structure()`; no activity classification from FQBM. | -| **3** | Accounting Policies, Changes in Estimates and Errors | **Missing** | No policy note, prior-period adjustment, or error correction. | -| **4** | Effects of Changes in Foreign Exchange Rates | Partial | S and pass-through only; no translation of FC assets/liabilities, no FX gain/loss disclosure. | -| **5** | Borrowing Costs | **Missing** | No borrowing-cost capitalisation or expense classification. | -| **6** | Consolidated and Separate Financial Statements | Partial | Sectoral (CB, bank) and consolidated view in presentation; no control definition or full consolidation mechanics. | -| **7** | Investments in Associates | **Missing** | Not applicable to core FQBM; no equity method. | -| **8** | Interests in Joint Ventures | **Missing** | Not applicable; no joint arrangement. | -| **9** | Revenue from Exchange Transactions | **Missing** | No revenue recognition or measurement. | -| **10** | Financial Reporting in Hyperinflationary Economies | **Missing** | No hyperinflation restatement (FQBM has inflation π but not restatement). | -| **11** | Construction Contracts | **Missing** | Not applicable to monetary/financial core. | -| **12** | Inventories | **Missing** | Not applicable. | -| **13** | Leases | **Missing** | Not applicable to core; no right-of-use asset/lease liability. | -| **14** | Events After the Reporting Date | **Missing** | No adjustment vs disclosure classification. | -| **15** | Financial Instruments: Disclosure and Presentation | Partial | Presentation only; no disclosure (maturity, risk). | -| **16** | Investment Property | **Missing** | Not applicable. | -| **17** | Property, Plant, and Equipment | **Missing** | Not applicable; no PP&E or depreciation. | -| **18** | Segment Reporting | **Missing** | No segment definition or segment P&L. | -| **19** | Provisions, Contingent Liabilities and Contingent Assets | **Missing** | No provisions or contingencies. | -| **20** | Related Party Disclosures | **Missing** | No related party identification or disclosure. | -| **21** | Impairment of Non-Cash-Generating Assets | **Missing** | Not applicable to financial core. | -| **22** | Disclosure of Financial Information About the General Government Sector | Partial | Sectoral structure supported; GGS boundary and entity list external. | -| **23** | Revenue from Non-Exchange Transactions | **Missing** | No tax/transfer revenue. | -| **24** | Presentation of Budget Information in Financial Statements | Structure only | Template in `budget_vs_actual_structure()`; budget data external. | -| **25** | Employee Benefits | **Missing** | Not applicable to core. | -| **26** | Impairment of Cash-Generating Assets | **Missing** | No impairment model. | -| **27** | Agriculture | **Missing** | Not applicable. | -| **28** | Financial Instruments: Presentation | Partial | Asset/liability split in statement of financial position. | -| **29** | Financial Instruments: Recognition and Measurement | **Missing** | No measurement basis (amortised cost, FVOCI, FVPL) or ECL. | -| **30** | Financial Instruments: Disclosures | **Missing** | No risk or maturity disclosure. | -| **31** | Intangible Assets | **Missing** | Not applicable. | -| **32** | Service Concession Arrangements: Grantor | **Missing** | Not applicable. | -| **33** | First-Time Adoption of Accrual Basis IPSAS | **Missing** | Not applicable (no full IPSAS adoption). | -| **34** | Separate Financial Statements | Partial | Entity-level (CB, bank) supported. | -| **35** | Consolidated Financial Statements | Partial | Consolidated layout; no full consolidation rules. | -| **36** | Investments in Associates and Joint Ventures | **Missing** | Not applicable. | -| **37** | Joint Arrangements | **Missing** | Not applicable. | -| **38** | Disclosure of Interests in Other Entities | **Missing** | No disclosure. | -| **39** | Employee Benefits | **Missing** | Not applicable. | -| **40** | Public Sector Combinations | **Missing** | Not applicable. | -| **41** | Financial Instruments | **Missing** | No classification, measurement, or ECL. | -| **42** | Social Benefits | **Missing** | Not applicable. | -| **43** | Leases | **Missing** | Not applicable. | -| **44** | Non-Current Assets Held for Sale and Discontinued Operations | **Missing** | Not applicable. | -| **45** | Property, Plant, and Equipment | **Missing** | Not applicable. | -| **46** | Measurement | **Missing** | No measurement basis (historical, FV, etc.). | -| **47** | Revenue | **Missing** | No revenue model. | -| **48** | Transfer Expenses | **Missing** | No transfer expense. | -| **49** | Retirement Benefit Plans | **Missing** | Not applicable. | -| **50** | Exploration for and Evaluation of Mineral Resources | **Missing** | Not applicable. | -| **Cash basis** | Financial Reporting Under the Cash Basis | **Missing** | No cash-basis statement. | -| **RPG 1** | Long-Term Sustainability of an Entity's Finances | Partial | Debt sustainability (r−g) in sovereign module; no full RPG 1 disclosure. | -| **RPG 2** | Financial Statement Discussion and Analysis | **Missing** | No narrative. | -| **RPG 3** | Reporting Service Performance Information | **Missing** | Not applicable. | -| **SRS 1** | Climate-related Disclosures | **Missing** | Not applicable. | +| Status | Count | Notes | +|--------|--------|--------| +| **Supported** | 1 | IPSAS 1 (statement of financial position: structure, current/non-current). | +| **Structure** | 20+ | Templates in `fqbm.ipsas.presentation`: 3, 5, 9, 10, 14, 19, 20, 23, 24 (+ budget_actual_from_state), 29/30/41, 38, 46, 47, 48, cash basis, RPG 2, functional/presentation currency. | +| **Partial** | 6 | CF (no revenue/expense); 2 (no activity classification); 4 (S, pass-through only); 6, 22, 28, 34, 35 (sectoral/consolidated layout only); 15 (maturity_risk_disclosure_structure); RPG 1 (r−g in sovereign module). | +| **Missing / N/A** | 25+ | Not applicable to monetary/financial core: 7, 8, 11–13, 16–18, 21, 25–27, 31–33, 36–37, 39–45, 49–50, RPG 3, SRS 1. 26: no impairment model. | + +**Main gaps for entity-level IPSAS:** Cash flow by activity (IPSAS 2); FX translation of FC positions and gain/loss disclosure (4); measurement basis and ECL (41); full consolidation rules (6, 35). Budget data is external; use `budget_actual_from_state(state, budget)` for actuals. --- -## Part B — FQBM / Four-Quadrant Matrix: Gaps and missing +## 2. FQBM / Matrix: What’s implemented -### B.1 State vector and identities - -| Item | Status | Note | -|------|--------|------| -| **L_cb (central bank loans)** | **Added** | `FQBMState.L_cb` (default 0); central_bank_step updates L_cb; matrix uses state.L_cb. | -| **Open-economy split A_dom / A_ext, L_dom / L_ext** | **Added** | `open_economy_view(A_dom, A_ext, L_dom, L_ext, E)` in `fqbm.state` returns identity check and totals. | -| **Reporting period / reporting date** | **Added** | `FQBMState.reporting_date` (optional); `statement_of_financial_position(..., reporting_date=)` uses state.reporting_date when not passed. | - -### B.2 Four-quadrant matrix (Part I) - -| Item | Status | Note | -|------|--------|------| -| **Explicit four-quadrant layout** | **Added** | `fqbm.matrix.four_quadrant_matrix(state)` and `four_quadrant_summary(state)` return the 4-column matrix and identity check. | -| **Quadrant balance checks** | **Added** | Summary includes `identity_A_eq_L_plus_E`. | -| **Cross-sector consistency** | Partial | CB and bank identities implemented; no automatic check that matrix totals equal sectoral sums. | - -### B.3 White paper parts not implemented - -| Part | Topic | Status | Gap | -|------|--------|--------|-----| -| **VII** | Shadow banking and leverage | **Integrated** | `leverage_ratio()` in dashboard ratios; `margin_spiral_risk()` available; repo multiplier not modeled. | -| **VIII** | Derivatives clearing and CCP | **Integrated** | `run_workbook(ccp_params=CCPParams(...))` returns `result["ccp"]` with identity and waterfall flag. | -| **IX** | CBDC and digital reserve architecture | **Integrated** | `run_workbook(cbdc_params=CBDCParams(...))` applies deposit/reserve shift; `result["cbdc"]` has cbdc_liability. | -| **XI** | Historical case expansion | **Partial** | Presets (1997, 2008, 2020, 2022) in scenarios; no narrative or coded “case” outputs. | - -### B.4 IPSAS presentation structures added (templates only where no FQBM data) - -| Item | Status | Location | -|------|--------|----------| -| Statement of financial position (IPSAS 1) | Supported | `statement_of_financial_position()` | -| Budget vs actual (IPSAS 24) | Structure | `budget_vs_actual_structure()` | -| Cash flow statement (IPSAS 2) | Structure | `cash_flow_statement_structure()` | -| Statement of financial performance | Structure | `statement_of_financial_performance_structure()` | -| Four-quadrant matrix | Supported | `fqbm.matrix.four_quadrant_matrix()` | - -### B.5 Other missing - -| Item | Status | -|------|--------| -| **Statement of changes in net assets/equity** | **Added** | `statement_of_changes_in_net_assets_structure()`. | -| **Notes to the financial statements** | **Added** | `notes_to_financial_statements_structure()`. | -| **Comparative period** | **Added** | `statement_of_financial_position_comparative(state_prior, state_current, entity)`. | -| **Functional currency / presentation currency** | **Missing**. | -| **Financial instrument maturity breakdown** | **Added** | `maturity_risk_disclosure_structure()` (maturity buckets, interest rate +100bp, credit exposure, ECL). | -| **Interest rate sensitivity** | **Partial** | Column in maturity_risk_disclosure_structure. | -| **Credit risk / ECL disclosure** | **Partial** | Columns in maturity_risk_disclosure_structure. | -| **FX translation of FC positions** | **Added** | `fx_translate(fc_amount, S_prev, S_curr)`. | -| **Cash flow from balance sheet changes** | **Added** | `cash_flow_from_state_changes(state_prev, state_curr)`. | +- **State:** L_cb, cbdc_liability, reporting_date, functional_currency, presentation_currency. **Open economy:** `open_economy_view(A_dom, A_ext, L_dom, L_ext, E)`. +- **Matrix:** `four_quadrant_matrix()`, `four_quadrant_summary()`, `cross_sector_consistency_check()`. +- **Parts VII–IX, XI:** Shadow banking (repo_multiplier in dashboard, margin_spiral_simulation); CCP (ccp_params, result["ccp"]); CBDC (cbdc_liability in state and IPSAS); scenarios + `get_case_narrative()`. +- **IPSAS layer:** Statement of financial position (CB, bank, consolidated), budget vs actual (template + budget_actual_from_state), cash flow from Δstate, comparative, notes, maturity/risk, FX translate, and all structure templates above. --- -## Part C — Implementation checklist (done vs to do) +## 3. Remaining gaps (concise) -### Done - -- IPSAS 1 statement of financial position (CB, bank, consolidated); uses state.L_cb and state.reporting_date. -- IPSAS 24 budget vs actual template. -- IPSAS 2 cash flow statement template; **cash_flow_from_state_changes(state_prev, state_curr)** infers amounts from Δstate. -- Statement of financial performance template. -- **Statement of changes in net assets** template. -- **FX translation**: **fx_translate(fc_amount, S_prev, S_curr)**. -- Four-quadrant matrix and summary; **L_cb** from state. -- **L_cb in state**: `FQBMState.L_cb` and `reporting_date`; central_bank_step updates L_cb; differential model 12-element vector. -- **Open economy**: **open_economy_view(A_dom, A_ext, L_dom, L_ext, E)**. -- **Part VII**: Leverage ratio in dashboard; **run_workbook** exposes shadow metrics. -- **Part VIII**: **run_workbook(ccp_params=CCPParams(...))** returns result["ccp"]. -- **Part IX**: **run_workbook(cbdc_params=CBDCParams(...))** applies deposit/reserve shift; result["cbdc"]. - -### Optional work (completed) - -1. **Part VII**: **repo_multiplier(initial_collateral, haircut, rounds)** and **margin_spiral_simulation(...)** in `fqbm.sheets.shadow_banking`. -2. **Part VIII**: **variation_margin_flow(mark_to_market_change)** and **ccp_clearing_simulation(vm_calls_per_period, liquidity_buffer_start)** in `fqbm.sheets.ccp`. -3. **IPSAS**: **notes_to_financial_statements_structure()**; **statement_of_financial_position_comparative(state_prior, state_current, entity)**; **maturity_risk_disclosure_structure()** in `fqbm.ipsas.presentation`. +| Area | Gap | +|------|-----| +| **Part VIII** | Full clearing balance sheet not modeled (margin/waterfall only). | +| **IPSAS 2** | No operating/investing/financing classification of cash flows. | +| **IPSAS 4** | No translation of FC assets/liabilities or FX gain/loss in performance statement. | +| **IPSAS 41 / 46** | No measurement basis or ECL model; use templates + user data. | +| **RPG 1** | Full long-term sustainability disclosure not implemented. | +| **GGS** | Sectoral structure only; boundary and entity list set by jurisdiction. | --- -## References +## 4. References -- [IPSAS_COMPLIANCE.md](IPSAS_COMPLIANCE.md) — Scope and how to use the IPSAS layer. -- IPSASB (2025). *Handbook of International Public Sector Accounting Pronouncements*. IFAC. -- White paper: Four-Quadrant Balance Sheet Matrix, Parts I–XVI. +- [IPSAS_COMPLIANCE.md](IPSAS_COMPLIANCE.md) — Scope and use of the IPSAS layer. +- [RECOMMENDATIONS.md](RECOMMENDATIONS.md) — Further suggestions. +- [MISSING_COMPONENTS_AND_DOCUMENTATION.md](MISSING_COMPONENTS_AND_DOCUMENTATION.md) — Checklist. diff --git a/docs/IPSAS_COMPLIANCE.md b/docs/IPSAS_COMPLIANCE.md index 14f14d9..9094294 100644 --- a/docs/IPSAS_COMPLIANCE.md +++ b/docs/IPSAS_COMPLIANCE.md @@ -39,7 +39,7 @@ IPSAS 1 requires **current** and **non-current** classification for assets and l | IPSAS 1-style line item | FQBM variable | Suggested classification | Notes | |-------------------------|---------------|---------------------------|--------| -| Financial assets (at amortised cost / FV) | B (government bonds), L_cb (loans) | Non-current (hold-to-maturity / policy portfolio) or split by maturity | L_cb not in state; can be added. | +| Financial assets (at amortised cost / FV) | B (government bonds), L_cb (loans) | Non-current (hold-to-maturity / policy portfolio) or split by maturity | L_cb is in state (`FQBMState.L_cb`). | | Cash and cash equivalents / Reserves | R | Current | | | Currency in circulation | C | Liability (current) | | | Liabilities (reserve accounts) | R (bank reserves) | Current | | @@ -75,7 +75,7 @@ See `fqbm.ipsas.presentation.budget_vs_actual_structure()`. - **Presentation**: Financial assets vs liabilities, and (where applicable) current vs non-current — supported by the IPSAS 1-style layout. - **Recognition and measurement**: FQBM does not implement measurement bases (amortised cost, FVOCI, FVPL) or impairment (e.g. ECL). **Gap** for full IPSAS 41 compliance. -- **Disclosure**: No maturity breakdown, interest rate sensitivity, or credit risk disclosure. **Gap**; can be extended with user-supplied data. +- **Disclosure**: Maturity and risk templates in `maturity_risk_disclosure_structure()`; extend with user-supplied data for full interest rate sensitivity and credit risk/ECL disclosure. --- @@ -105,7 +105,11 @@ See `fqbm.ipsas.presentation.budget_vs_actual_structure()`. | Financial instrument measurement (IPSAS 41) | **Not supported** | No measurement basis or ECL | | FX translation (IPSAS 4) | **Partial** | Exchange rate and pass-through only | | Disclosure of GGS (IPSAS 22) | **Partial** | Sectoral structure supported | -| Reporting period / reporting date | **Not defined** | FQBM is period-agnostic; user must set | +| Reporting period / reporting date | **Supported** | `FQBMState.reporting_date`; `statement_of_financial_position(..., reporting_date=)` | +| Comparative period (prior/current) | **Supported** | `statement_of_financial_position_comparative()` | +| Notes and maturity/risk disclosure | **Structure** | `notes_to_financial_statements_structure()`, `maturity_risk_disclosure_structure()` | +| Cash flow from balance sheet changes | **Supported** | `cash_flow_from_state_changes()`; FX: `fx_translate()` | +| Additional IPSAS structures (3, 5, 9, 10, 14, 19, 20, 23, 29/41, 38, 46, 48, cash basis, RPG 2, functional/presentation currency) | **Structure** | Templates in `fqbm.ipsas.presentation`; budget vs actual from state: `budget_actual_from_state()` | --- @@ -123,7 +127,7 @@ For **full IPSAS compliance** at entity level, the reporting entity must: - Populate budget information (IPSAS 24) and provide variance analysis. - Apply IPSAS 4 for FX and other standards as applicable. -The FQBM IPSAS layer supports **presentation consistency** and **structural alignment** with IPSAS 1 and IPSAS 24, not end-to-end compliance by itself. +The FQBM IPSAS layer supports **presentation consistency** and **structural alignment** with IPSAS 1 and IPSAS 24, not end-to-end compliance by itself. For a consolidated list of recommendations (e.g. full CCP balance sheet, FX disclosure, white paper), see [RECOMMENDATIONS.md](RECOMMENDATIONS.md). --- diff --git a/docs/MISSING_COMPONENTS_AND_DOCUMENTATION.md b/docs/MISSING_COMPONENTS_AND_DOCUMENTATION.md new file mode 100644 index 0000000..9ab8b77 --- /dev/null +++ b/docs/MISSING_COMPONENTS_AND_DOCUMENTATION.md @@ -0,0 +1,121 @@ +# Missing Components and Required Documentation + +Consolidated list of **missing components** (code/IPSAS/FQBM) and **documentation that is required** or recommended for the FQBM project. Derived from [GAPS_AND_MISSING.md](GAPS_AND_MISSING.md) and [IPSAS_COMPLIANCE.md](IPSAS_COMPLIANCE.md). + +--- + +## 1. Missing components (code / functionality) + +### 1.1 IPSAS standards (missing or partial) + +| Component | Standard / area | Required for | +|-----------|------------------|--------------| +| Accounting policies, prior-period adjustments, error correction | IPSAS 3 | Full IPSAS 3 compliance | +| Borrowing costs capitalisation / expense | IPSAS 5 | Borrowing cost treatment | +| Revenue recognition and measurement | IPSAS 9, 47 | Statement of financial performance | +| Hyperinflation restatement | IPSAS 10 | High-inflation reporting | +| Events after the reporting date (adjust vs disclose) | IPSAS 14 | Post–reporting-date events | +| Provisions and contingent liabilities/assets | IPSAS 19 | Provisions note | +| Related party identification and disclosure | IPSAS 20 | Related party note | +| Revenue from non-exchange (taxes, transfers) | IPSAS 23 | Government revenue | +| Financial instrument recognition/measurement (amortised cost, FVOCI, FVPL, ECL) | IPSAS 29, 41 | Full instrument accounting | +| Disclosure of interests in other entities | IPSAS 38 | Consolidation disclosure | +| Measurement basis (historical cost, FV, etc.) | IPSAS 46 | Measurement policy | +| Transfer expenses | IPSAS 48 | Transfer expense classification | +| Cash-basis financial statement | Cash basis IPSAS | Cash-basis reporters | +| Financial Statement Discussion and Analysis (narrative) | RPG 2 | Management commentary | +| Functional currency / presentation currency | General | Multi-currency reporting | + +### 1.2 FQBM / matrix (missing or partial) + +| Component | Description | Required for | +|-----------|-------------|---------------| +| Cross-sector consistency check | Automatic check that four-quadrant matrix totals equal sectoral (CB + bank) sums | Matrix validation | +| Budget / appropriation model | Internal representation of budget and appropriations (not only template) | IPSAS 24 actual vs budget from FQBM | +| Historical case narrative / coded outputs | Part XI: structured narrative or coded “case” outputs for 1997, 2008, 2020, 2022 | Case study reporting | +| Real data pipelines | Automated download of Fed H.4.1, BIS, IMF (e.g. for regressions) | Empirical use without manual data | + +### 1.3 White paper / framework (out of scope but listed for completeness) + +| Component | Note | +|-----------|------| +| Full CCP balance-sheet dynamics | Part VIII: margin flows and default waterfall exist; full clearing balance sheet not modeled. | +| CBDC in state vector | **Done**: `FQBMState.cbdc_liability`; workbook updates it on CBDC shift; IPSAS presentation shows it. | +| Repo multiplier in workbook | **Done**: wired into `run_workbook` (optional `shadow_params`); dashboard includes `repo_multiplier`. | + +--- + +## 2. Required or recommended documentation + +### 2.1 Documentation that does not exist (required or recommended) + +| Document | Purpose | Priority | +|----------|---------|----------| +| **White paper (canonical source)** | The full FQBM white paper as a single file (e.g. `docs/WHITE_PAPER.md` or PDF) so the framework is versioned with the code | High – README and framework_summary refer to “the white paper” but it is not in the repo | +| **User guide / tutorial** | Step-by-step: install, run workbook, run Monte Carlo, interpret stress tables, use scenarios, export Excel, use IPSAS layer | High – README is short; no walkthrough | +| **API reference** | Documented list of public functions/classes, parameters, and return types (e.g. Sphinx or MkDocs, or a single `API.md`) | Medium – only docstrings in code | +| **Data dictionary** | Definition of every state variable (B, R, C, Loans, …), sheet inputs/outputs, and regression variables (columns for Model 1–3) | Medium – scattered in docstrings | +| **Changelog** | `CHANGELOG.md` or release notes for versions (e.g. 0.1.0, 0.2.0) | Low | +| **Contributing / development** | How to add a sheet, add a scenario, run tests, code style | Low | + +### 2.2 Documentation that exists but could be extended + +| Document | Status / remaining gap | +|----------|------------------------| +| **README.md** | No “Quick start” example with copy-paste code; no table of contents; Streamlit and scenarios could be in a single “Optional features” section. | +| **docs/framework_summary.md** | One sentence per Part (I–XVI) and module mapping in place. | +| **docs/IPSAS_COMPLIANCE.md** | Section 3 still says “L_cb not in state” – should say L_cb is in state. Compliance checklist could reference new helpers (comparative, notes, maturity/risk). | +| **docs/GAPS_AND_MISSING.md** | B.3 table says “repo multiplier not modeled” but `repo_multiplier()` exists – could say “repo multiplier implemented; not in dashboard”. | +| **docs/REFERENCES.md** | Weblinks added (IPSASB, Handbook, BIS, BIS bulk, IMF, Fed H.4.1, Fed Data Download). | +| **docs/RECOMMENDATIONS.md** | Created: consolidated list of recommendations and suggestions. | +| **Inline docstrings** | Some modules lack “Part X” references or “Required columns” for regression DataFrames. | + +--- + +## 3. Summary checklist + +Implementation status: structures and code listed below have been added (templates for IPSAS; cross-sector check; budget_actual_from_state; get_case_narrative; docs added or updated). Real data pipelines remain optional. + +### Missing components (to implement if targeting full IPSAS / completeness) + +- [x] IPSAS 3: Accounting policies / prior-period adjustment / error correction — `accounting_policies_structure()` +- [x] IPSAS 5: Borrowing costs — `borrowing_costs_structure()` +- [x] IPSAS 9/47: Revenue recognition — `revenue_recognition_structure()` +- [x] IPSAS 10: Hyperinflation restatement — `hyperinflation_restatement_structure()` +- [x] IPSAS 14: Events after the reporting date — `events_after_reporting_date_structure()` +- [x] IPSAS 19: Provisions and contingencies — `provisions_contingencies_structure()` +- [x] IPSAS 20: Related party disclosure — `related_party_disclosure_structure()` +- [x] IPSAS 23: Revenue from non-exchange transactions — `revenue_non_exchange_structure()` +- [x] IPSAS 29/41: Financial instrument measurement and ECL — `financial_instruments_measurement_structure()` +- [x] IPSAS 38: Disclosure of interests in other entities — `interests_in_other_entities_structure()` +- [x] IPSAS 46: Measurement basis — `measurement_basis_structure()` +- [x] IPSAS 48: Transfer expenses — `transfer_expenses_structure()` +- [x] Cash-basis statement — `cash_basis_statement_structure()` +- [x] RPG 2: Financial Statement Discussion and Analysis (narrative) — `fs_discussion_and_analysis_structure()` +- [x] Functional / presentation currency — state fields + `functional_presentation_currency_structure()` +- [x] Cross-sector matrix consistency check — `cross_sector_consistency_check()` in fqbm.matrix +- [x] Budget/appropriation model (optional) — `budget_actual_from_state()` in presentation +- [x] Historical case narrative or coded outputs (Part XI) — `get_case_narrative(name)` in scenarios.presets +- [x] Real data pipelines (optional) — `fqbm.data.pipelines`: fetch_fed_h41, fetch_bis_series, fetch_imf_series + +### Required documentation (to add) + +- [x] **White paper** in repo — `docs/WHITE_PAPER.md` (placeholder) +- [x] **User guide / tutorial** — `docs/USER_GUIDE.md` +- [x] **API reference** — `docs/API_REFERENCE.md` +- [x] **Data dictionary** — `docs/DATA_DICTIONARY.md` +- [x] **Changelog** — `CHANGELOG.md` +- [x] **Contributing / development** — `CONTRIBUTING.md` +- [x] Update **IPSAS_COMPLIANCE.md** (L_cb in state; new helpers) +- [x] Update **GAPS_AND_MISSING.md** (repo_multiplier in dashboard note) +- [x] Extend **framework_summary.md** (module mapping) + +--- + +## 4. References + +- [GAPS_AND_MISSING.md](GAPS_AND_MISSING.md) – Full IPSAS and FQBM gap list +- [IPSAS_COMPLIANCE.md](IPSAS_COMPLIANCE.md) – Scope and use of the IPSAS layer +- [RECOMMENDATIONS.md](RECOMMENDATIONS.md) – Additional recommendations and suggestions +- [framework_summary.md](framework_summary.md) – Core identity and Parts I–XVI +- [REFERENCES.md](REFERENCES.md) – Chicago Author–Date references and weblinks diff --git a/docs/RECOMMENDATIONS.md b/docs/RECOMMENDATIONS.md new file mode 100644 index 0000000..aa9f498 --- /dev/null +++ b/docs/RECOMMENDATIONS.md @@ -0,0 +1,82 @@ +# FQBM Recommendations and Suggestions + +Consolidated list of **additional recommendations and suggestions** for the FQBM project. See also [GAPS_AND_MISSING.md](GAPS_AND_MISSING.md) and [MISSING_COMPONENTS_AND_DOCUMENTATION.md](MISSING_COMPONENTS_AND_DOCUMENTATION.md). + +--- + +## 1. Code and framework (white paper / FQBM) + +| Item | Status / note | +|------|----------------| +| **Full CCP balance-sheet dynamics** | Part VIII: margin flows and default waterfall exist; full clearing balance sheet is not modeled. Future work. | +| **Full IPSAS compliance at entity level** | FQBM provides presentation structures; the reporting entity must maintain recognition/measurement per IPSAS (e.g. 41, 46), prepare cash flow statement (IPSAS 2) and statement of financial performance, populate budget (IPSAS 24) with variance analysis, and apply IPSAS 4 for FX. See [IPSAS_COMPLIANCE.md](IPSAS_COMPLIANCE.md) §9. | +| **Cash flow by activity (IPSAS 2)** | No classification of cash flows by operating/investing/financing from FQBM; use `cash_flow_from_state_changes()` for inferred amounts and templates for disclosure. | +| **FX translation and disclosure** | Add explicit translation of foreign-currency assets/liabilities and FX gain/loss in a performance statement; `fx_translate()` and state `S` support analysis. | +| **Financial instrument measurement and ECL** | No measurement bases (amortised cost, FVOCI, FVPL) or impairment model; use `financial_instruments_measurement_structure()` and `maturity_risk_disclosure_structure()`; extend with user-supplied data. | +| **Budget data** | Budget columns (original/final) must be supplied by the reporting entity; use `budget_actual_from_state(state, budget)` to fill actuals from state. | +| **GGS boundary** | General government sector boundary and entity list to be defined by the reporting jurisdiction; FQBM supports sectoral structure. | +| **RPG 1 (long-term sustainability)** | Partial (e.g. debt sustainability r−g in sovereign module); full RPG 1 disclosure not implemented. | + +--- + +## 2. Documentation + +| Item | Priority / note | +|------|-----------------| +| **Canonical white paper in repo** | High. Replace [WHITE_PAPER.md](WHITE_PAPER.md) placeholder with full FQBM white paper (Markdown or PDF) versioned with the code. | +| **Sphinx or MkDocs API docs** | Medium (optional). [API_REFERENCE.md](API_REFERENCE.md) exists; automated API docs could be added via Sphinx or MkDocs. | +| **README** | Quick start and table of contents in place; Optional features in a single section. | +| **framework_summary** | One sentence per Part (I–XVI) and module mapping in place; can be expanded. | +| **REFERENCES weblinks** | IPSASB, IPSASB Handbook, BIS, BIS bulk, IMF, IMF Data, Fed H.4.1, Fed Data Download linked in [REFERENCES.md](REFERENCES.md). | +| **Inline docstrings** | Add “Part X” references and “Required columns” for regression DataFrames where missing. | + +--- + +## 3. Contributing and process + +| Item | Location | +|------|----------| +| Keep PRs focused; prefer smaller, reviewable changes | [CONTRIBUTING.md](../CONTRIBUTING.md) | +| Ensure tests pass; add coverage where practical | CONTRIBUTING | +| Update USER_GUIDE, API_REFERENCE, DATA_DICTIONARY when adding public API | CONTRIBUTING | +| Update IPSAS_COMPLIANCE and GAPS_AND_MISSING when adding IPSAS/matrix features | CONTRIBUTING | +| Add changelog entry under [Unreleased] in CHANGELOG.md | CONTRIBUTING | +| When adding a sheet: optionally add Excel export for the new result key | CONTRIBUTING | +| When adding a scenario: optionally add narrative in `get_case_narrative()` | CONTRIBUTING | + +--- + +## 4. Data and empirical + +| Item | Note | +|------|------| +| **Real data pipelines** | `fqbm.data.pipelines`: fetch_fed_h41, fetch_bis_series, fetch_imf_series. Can be extended (e.g. more series, caching, retries). See [USER_GUIDE.md](USER_GUIDE.md) and [DATA_DICTIONARY.md](DATA_DICTIONARY.md). | +| **IMF API key** | Optional; programmatic IMF fetch benefits from registration and api_key. | + +--- + +## 5. IPSAS and reporting (entity-level) + +| Item | Note | +|------|------| +| Recognition and measurement per IPSAS (e.g. 41, 46) | Reporting entity responsibility. | +| Prepare cash flow statement (IPSAS 2) and statement of financial performance | Reporting entity; FQBM provides structure and cash flow from Δstate. | +| Populate budget (IPSAS 24) and provide variance analysis | Reporting entity; FQBM provides template and budget_actual_from_state. | +| Apply IPSAS 4 for FX and other standards | Reporting entity. | +| Disclosure: extend maturity, interest rate sensitivity, credit risk with user-supplied data | Use maturity_risk_disclosure_structure() and related templates. | + +--- + +## 6. White paper + +| Item | Note | +|------|------| +| Publish full white paper in repo (Markdown or PDF) and link from README and framework_summary | See [WHITE_PAPER.md](WHITE_PAPER.md); link already in README. | + +--- + +## References + +- [GAPS_AND_MISSING.md](GAPS_AND_MISSING.md) — Full IPSAS and FQBM gap list +- [IPSAS_COMPLIANCE.md](IPSAS_COMPLIANCE.md) — Scope and use of the IPSAS layer +- [MISSING_COMPONENTS_AND_DOCUMENTATION.md](MISSING_COMPONENTS_AND_DOCUMENTATION.md) — Checklist and documentation status diff --git a/docs/REFERENCES.md b/docs/REFERENCES.md index 1d9fbb6..9ae4a26 100644 --- a/docs/REFERENCES.md +++ b/docs/REFERENCES.md @@ -15,3 +15,14 @@ International Monetary Fund (IMF). 2014. *Government Finance Statistics Manual*. McLeay, Michael, Amar Radia, and Ryland Thomas. 2014. "Money Creation in the Modern Economy." *Bank of England Quarterly Bulletin*. Obstfeld, Maurice, and Kenneth Rogoff. 1995. *Foundations of International Macroeconomics*. + +## Weblinks (optional) + +- IPSASB: https://www.ifac.org/ipsasb — International Public Sector Accounting Standards. +- IPSASB Handbook: https://www.ifac.org/ipsasb/standards — Handbook of International Public Sector Accounting Pronouncements. +- BIS: https://www.bis.org — Statistics and manuals. +- BIS Statistics (bulk download): https://www.bis.org/statistics/full_data_sets.htm — Credit gaps, debt service, CB assets, etc. +- IMF: https://www.imf.org — Data and Government Finance Statistics. +- IMF Data: https://www.imf.org/en/Data — Data portal and API. +- Fed H.4.1: https://www.federalreserve.gov/releases/h41/ — Factors Affecting Reserve Balances. +- Fed Data Download (H.41): https://www.federalreserve.gov/datadownload/Choose.aspx?rel=H41 — CSV download. diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md new file mode 100644 index 0000000..c732701 --- /dev/null +++ b/docs/USER_GUIDE.md @@ -0,0 +1,161 @@ +# FQBM User Guide and Tutorial + +This guide walks you through installing, running, and interpreting the Four-Quadrant Balance Sheet Matrix (FQBM) framework. + +--- + +## 1. Install + +```bash +cd FOUR-QUADRANT_BALANCE_SHEET_MATRIX +python3 -m venv .venv +source .venv/bin/activate # Windows: .venv\Scripts\activate +pip install -e . +``` + +Optional: `pip install pytest` for tests; `pip install streamlit` for the Streamlit dashboard. + +--- + +## 2. Quick start: run the workbook + +The workbook runs all eight sheets (central bank, commercial bank, capital stress, FX parity, sovereign debt, commodity, Monte Carlo, dashboard) in one go: + +```python +from fqbm.workbook.runner import run_workbook +from fqbm.state import FQBMState + +state = FQBMState(B=100, R=50, C=20, Loans=800, Deposits=700, E_cb=30, E_b=100) +result = run_workbook(initial_state=state, mc_runs=50, export_path="out.xlsx") +``` + +Or from the command line: + +```bash +python -m fqbm.workbook.runner +``` + +This produces a result dictionary (and optionally an Excel file) with keys such as `state`, `stress_tables`, `fx_parity`, `sovereign`, `commodity`, `monte_carlo`, `dashboard`, and `ipsas` (if requested). + +--- + +## 3. Using scenarios (Part XI) + +Use built-in historical scenarios instead of building state by hand: + +```python +from fqbm.workbook.runner import run_workbook + +run_workbook(scenario="asia_1997", mc_runs=100, export_path="asia_1997.xlsx") +run_workbook(scenario="gfc_2008", mc_runs=100) +run_workbook(scenario="pandemic_2020", mc_runs=50) +run_workbook(scenario="rate_shock_2022", mc_runs=50) +``` + +List scenarios and get structured narrative: + +```python +from fqbm.scenarios.presets import list_scenarios, get_scenario, get_case_narrative + +print(list_scenarios()) # ['asia_1997', 'gfc_2008', 'pandemic_2020', 'rate_shock_2022'] +preset = get_scenario("gfc_2008") +case = get_case_narrative("gfc_2008") # narrative, state_summary, key_drivers, suggested_shocks +``` + +--- + +## 4. Interpreting stress tables and Monte Carlo + +- **Capital stress** (`result["stress_tables"]`): capital and leverage under different shock assumptions; see Sheet 3. +- **Monte Carlo** (`result["monte_carlo"]`): distribution of outcomes (e.g. equity, deposits) over many runs; use percentiles for VaR-style analysis. +- **Dashboard** (`result["dashboard"]`): aggregated view of key ratios and totals. + +--- + +## 5. IPSAS layer and export + +Generate IPSAS-style outputs from state: + +```python +from fqbm.state import FQBMState +from fqbm.ipsas.presentation import ( + statement_of_financial_position, + budget_vs_actual_structure, + budget_actual_from_state, + cash_flow_from_state_changes, + statement_of_financial_position_comparative, +) + +state = FQBMState(B=100, R=50, C=20, Loans=800, Deposits=700, E_cb=30, E_b=100) +df = statement_of_financial_position(state, entity="central_bank") +# Comparative: prior vs current +state_prior = state.copy() +state_prior.B = 80 +comparative = statement_of_financial_position_comparative(state_prior, state, entity="central_bank") +# Budget vs actual from state +budget_df = budget_actual_from_state(state, budget={"Total assets": 900, "Total liabilities": 650}) +# Cash flow from balance sheet changes +cashflow = cash_flow_from_state_changes(state_prior, state) +``` + +Excel export (when `export_path` is set) includes IPSAS-style sheets when available. + +--- + +## 6. Matrix and cross-sector check + +```python +from fqbm.state import FQBMState +from fqbm.matrix import four_quadrant_matrix, four_quadrant_summary, cross_sector_consistency_check + +state = FQBMState(B=100, R=50, C=20, Loans=800, Deposits=700, E_cb=30, E_b=100) +matrix_df = four_quadrant_matrix(state) +summary = four_quadrant_summary(state) +check = cross_sector_consistency_check(state) # consistent, cb_balance, bank_balance, message +``` + +--- + +## 7. Differential model and stability (Part XIV) + +```python +from fqbm.system.differential_model import solve_trajectory, check_stability, DifferentialParams +import numpy as np + +params = DifferentialParams() +x0 = [100, 50, 20, 800, 700, 30, 100, 1.0, 0.0, 0.02, 1.0, 0.0] +t_span = (0, 10) +sol = solve_trajectory(x0, t_span, params) +stable = check_stability(params) +``` + +--- + +## 8. Empirical regressions (Part X) + +Synthetic data and regression helpers for inflation pass-through, sovereign spread, and capital flow sensitivity: + +```python +from fqbm.empirical.regressions import ( + run_inflation_pass_through, + run_sovereign_spread, + run_capital_flow_sensitivity, +) + +df_inf = run_inflation_pass_through(n=100) +df_spread = run_sovereign_spread(n=100) +df_cap = run_capital_flow_sensitivity(n=100) +``` + +Required columns for each model are documented in the [Data dictionary](DATA_DICTIONARY.md) and in the module docstrings. + +--- + +## 9. Optional features + +- **Streamlit dashboard**: `streamlit run scripts/streamlit_dashboard.py` +- **CBDC / CCP / repo**: workbook accepts `cbdc_params`, `ccp_params`, `shadow_params`. State has `cbdc_liability` after a CBDC shift; IPSAS presentation shows it. Repo multiplier is always in `result["dashboard"]["repo_multiplier"]` (optional `shadow_params` to customize haircut/rounds). +- **Real data**: `from fqbm.data.pipelines import fetch_fed_h41, fetch_bis_series, fetch_imf_series` for Fed H.4.1, BIS bulk CSV (e.g. credit_gap, total_credit), and IMF (optional api_key). Pipelines can be extended (e.g. more series, caching, retries); see [DATA_DICTIONARY.md](DATA_DICTIONARY.md) and [RECOMMENDATIONS.md](RECOMMENDATIONS.md). +- **FX parity**: Sheet 4 and `fqbm.sheets.fx_parity` (CIP, UIP, Dornbusch, pass-through). + +For more detail on the API, see [API_REFERENCE.md](API_REFERENCE.md) and [DATA_DICTIONARY.md](DATA_DICTIONARY.md). For additional recommendations (full CCP, FX disclosure, white paper), see [RECOMMENDATIONS.md](RECOMMENDATIONS.md). diff --git a/docs/WHITE_PAPER.md b/docs/WHITE_PAPER.md new file mode 100644 index 0000000..ec72e5c --- /dev/null +++ b/docs/WHITE_PAPER.md @@ -0,0 +1,23 @@ +# FQBM White Paper — Placeholder + +This file is a **placeholder** for the canonical Four-Quadrant Balance Sheet Matrix (FQBM) white paper. The full theoretical foundation (Parts I–XVI) is referenced in [framework_summary.md](framework_summary.md) and in the codebase docstrings. + +--- + +## Intended content (when published here) + +- **Part I**: Accounting foundation (A = L + E), four-quadrant matrix, open-economy identity. +- **Part II**: Closed-economy monetary dynamics (central bank, commercial bank). +- **Part III–IV**: Open-economy extension, BoP, parity conditions, capital flows, FX dynamics. +- **Part V–VI**: Sovereign risk and CDS; commodity shock channel. +- **Part VII–IX**: Shadow banking and leverage; derivatives clearing and CCP; CBDC and digital reserve architecture. +- **Part X**: Empirical regression appendix. +- **Part XI**: Historical case expansion (1997, 2008, 2020, 2022–23). +- **Part XII–XIV**: Quantitative stress tables; Monte Carlo framework; full system differential model. +- **Part XV–XVI**: Policy implications; simulation workbook architecture (eight sheets). + +--- + +## Versioning + +When the white paper is added to this repository (as Markdown or PDF), it will be versioned with the code and linked from the README and framework_summary. Until then, the framework is implemented and documented in the codebase and in [framework_summary.md](framework_summary.md). For other suggestions (e.g. full CCP balance sheet, FX disclosure), see [RECOMMENDATIONS.md](RECOMMENDATIONS.md). diff --git a/docs/framework_summary.md b/docs/framework_summary.md index 52dc3f5..de4c9dd 100644 --- a/docs/framework_summary.md +++ b/docs/framework_summary.md @@ -13,25 +13,25 @@ Every financial asset is someone else’s liability (within or across borders). All monetary operations must balance across this structure. -## Parts of the white paper +## Parts of the white paper (one sentence each) -| Part | Topic | -|------|--------| -| I | Accounting foundation | -| II | Closed-economy monetary dynamics (CB, commercial bank) | -| III | Open-economy extension (BoP, parity) | -| IV | Capital flows and FX dynamics | -| V | Sovereign risk and CDS | -| VI | Commodity shock channel | -| VII | Shadow banking and leverage | -| VIII | Derivatives clearing and CCP | -| IX | CBDC and digital reserve architecture | -| X | Empirical regression appendix | -| XI | Historical case expansion | -| XII | Quantitative stress tables | -| XIII | Monte Carlo simulation framework | -| XIV | Full system differential model | -| XV | Policy implications | -| XVI | Simulation workbook architecture (eight sheets) | +| Part | Topic | Implementation | +|------|--------|-----------------| +| **I** | Accounting foundation: A = L + E, four-quadrant matrix, open-economy identity. | `fqbm.state`, `fqbm.matrix` (four_quadrant_matrix, cross_sector_consistency_check). | +| **II** | Closed-economy monetary dynamics: central bank and commercial bank balance sheets. | `fqbm.sheets.central_bank`, `fqbm.sheets.commercial_bank`; state B, R, C, Loans, Deposits, E_cb, E_b, L_cb. | +| **III** | Open-economy extension: BoP, parity conditions. | `open_economy_view()`; state S, K. | +| **IV** | Capital flows and FX dynamics: CIP, UIP, Dornbusch, pass-through. | `fqbm.sheets.fx_parity`; Part X regressions (capital flow sensitivity). | +| **V** | Sovereign risk and CDS: spread, debt sustainability. | `fqbm.sheets.sovereign_debt`; run_sovereign_spread. | +| **VI** | Commodity shock channel: inflation composite, oil/commodity. | `fqbm.sheets.commodity`; state O. | +| **VII** | Shadow banking and leverage: repo multiplier, margin spiral. | `fqbm.sheets.shadow_banking`; run_workbook(shadow_params=...); dashboard repo_multiplier. | +| **VIII** | Derivatives clearing and CCP: margin flows, default waterfall. | `fqbm.sheets.ccp`; run_workbook(ccp_params=...). Full clearing balance sheet not modeled (see RECOMMENDATIONS.md). | +| **IX** | CBDC and digital reserve architecture: deposit shift, funding gap. | `fqbm.sheets.cbdc`; FQBMState.cbdc_liability; run_workbook(cbdc_params=...). | +| **X** | Empirical regression appendix: inflation pass-through, sovereign spread, capital flow. | `fqbm.empirical.regressions`; synthetic generators and run_*; required columns in DATA_DICTIONARY. | +| **XI** | Historical case expansion: 1997, 2008, 2020, 2022 presets and narrative. | `fqbm.scenarios.presets`; get_case_narrative(name). | +| **XII** | Quantitative stress tables: liquidity and capital stress. | `fqbm.sheets.capital_stress`; stress_tables. | +| **XIII** | Monte Carlo simulation framework: shock draws, outcome distribution. | `fqbm.sheets.monte_carlo`; run_n_simulations. | +| **XIV** | Full system differential model: trajectory and stability. | `fqbm.system.differential_model`; solve_trajectory, check_stability. | +| **XV** | Policy implications: discussed in white paper; framework supports policy experiments. | Scenarios and parameters. | +| **XVI** | Simulation workbook architecture: eight sheets, dashboard, export. | `fqbm.workbook.runner`; run_workbook. | -This codebase implements the simulation workbook (Part XVI), Monte Carlo (XIII), differential model (XIV), and empirical regressions (Part X). +This codebase implements the simulation workbook (Part XVI), Monte Carlo (XIII), differential model (XIV), empirical regressions (Part X), IPSAS presentation (fqbm.ipsas), scenario presets with get_case_narrative (Part XI), and data pipelines (fqbm.data.pipelines). diff --git a/src/fqbm/data/__init__.py b/src/fqbm/data/__init__.py new file mode 100644 index 0000000..9b4fe45 --- /dev/null +++ b/src/fqbm/data/__init__.py @@ -0,0 +1,17 @@ +""" +Real data pipelines for empirical use (Fed H.4.1, BIS, IMF). + +Optional: requires requests for some endpoints; CSV URLs may work with pandas only. +""" + +from fqbm.data.pipelines import ( + fetch_fed_h41, + fetch_bis_series, + fetch_imf_series, +) + +__all__ = [ + "fetch_fed_h41", + "fetch_bis_series", + "fetch_imf_series", +] diff --git a/src/fqbm/data/pipelines.py b/src/fqbm/data/pipelines.py new file mode 100644 index 0000000..e966722 --- /dev/null +++ b/src/fqbm/data/pipelines.py @@ -0,0 +1,163 @@ +""" +Real data pipelines for empirical use (Fed H.4.1, BIS, IMF). + +Fetch public data for regressions and calibration. Optional API keys may be required +for full BIS/IMF access; see docstrings. Uses urllib and pandas; requests optional for some endpoints. +""" + +from typing import Optional +from urllib.request import urlopen, Request +from urllib.error import URLError, HTTPError + +import pandas as pd + + +# Fed H.4.1: Data Download Program CSV (date range and series in query) +# https://www.federalreserve.gov/datadownload/Choose.aspx?rel=H41 +_FED_H41_CSV_URL = ( + "https://www.federalreserve.gov/datadownload/Output.aspx?" + "rel=H41&series=efce2c1a8744855f2623889dffb2b39a&lastObs=&from=01/01/2020&to=12/31/2024" + "&filetype=csv&label=include&layout=seriescolumn" +) + + +def fetch_fed_h41( + url: Optional[str] = None, + date_col: Optional[str] = None, +) -> pd.DataFrame: + """ + Fetch Federal Reserve H.4.1 (Factors Affecting Reserve Balances) as DataFrame. + + Uses the Fed Data Download Program CSV. If url is None, uses a default URL that may + need updating for current dates. Custom url can be built from: + https://www.federalreserve.gov/datadownload/Choose.aspx?rel=H41 + + Parameters + ---------- + url : str, optional + Full CSV download URL. If None, uses default _FED_H41_CSV_URL. + date_col : str, optional + Name of date column for parsing; inferred if possible. + + Returns + ------- + pd.DataFrame + H.4.1 series (columns depend on Fed release). Empty DataFrame on error. + """ + u = url or _FED_H41_CSV_URL + try: + req = Request(u, headers={"User-Agent": "FQBM/1.0"}) + with urlopen(req, timeout=30) as resp: + df = pd.read_csv(resp, skipinitialspace=True, on_bad_lines="skip") + if date_col and date_col in df.columns: + df[date_col] = pd.to_datetime(df[date_col], errors="coerce") + return df + except (URLError, HTTPError, Exception) as e: + return pd.DataFrame({"error": [str(e)]}) + + +def fetch_bis_series( + dataset: str = "credit_gap", + country: Optional[str] = None, + api_key: Optional[str] = None, +) -> pd.DataFrame: + """ + Fetch BIS statistics series (e.g. credit gap, property prices, debt service). + + BIS Statistics Warehouse: https://www.bis.org/statistics/ + For programmatic access, BIS provides CSV export URLs. Some series are available + without an API key via published CSV links. + + Parameters + ---------- + dataset : str + Identifier: "credit_gap", "debt_service", "property", or "total_credit". + country : str, optional + Country code (e.g. "US", "CN"). Optional filter. + api_key : str, optional + Not required for public CSV; reserved for future BIS API use. + + Returns + ------- + pd.DataFrame + BIS series. Empty DataFrame if fetch fails. + """ + # BIS bulk download (flat CSV): https://www.bis.org/statistics/full_data_sets.htm + urls = { + "credit_gap": "https://www.bis.org/static/bulk/WS_CREDIT_GAP_csv_flat.zip", + "debt_service": "https://www.bis.org/static/bulk/WS_DSR_csv_flat.zip", + "property": "https://www.bis.org/static/bulk/WS_SPP_csv_flat.zip", + "total_credit": "https://www.bis.org/static/bulk/WS_TC_csv_flat.zip", + "cb_assets": "https://www.bis.org/static/bulk/WS_CBTA_csv_flat.zip", + } + u = urls.get(dataset, urls["credit_gap"]) + try: + req = Request(u, headers={"User-Agent": "FQBM/1.0"}) + with urlopen(req, timeout=90) as resp: + df = pd.read_csv(resp, compression="zip", on_bad_lines="skip", nrows=10000) + # Optional country filter (column name may vary) + for col in ("Reference area", "REF_AREA", "COUNTRY"): + if country and col in df.columns: + df = df[df[col].astype(str).str.upper() == country.upper()].copy() + break + return df + except (URLError, HTTPError, Exception): + return pd.DataFrame() + + +def fetch_imf_series( + database_id: str = "IFS", + indicator: Optional[str] = None, + country: Optional[str] = None, + api_key: Optional[str] = None, +) -> pd.DataFrame: + """ + Fetch IMF data (e.g. IFS, GFS, WEO). Uses IMF Data API when api_key is provided; + otherwise returns empty DataFrame with instructions. + + IMF Data API: https://data.imf.org/ + API key (optional): https://www.imf.org/external/data.htm + + Parameters + ---------- + database_id : str + e.g. "IFS" (International Financial Statistics), "GFS", "WEO". + indicator : str, optional + Series code (e.g. "FITB_PA" for reserves). + country : str, optional + Country code (e.g. "US"). + api_key : str, optional + IMF API key for programmatic access. + + Returns + ------- + pd.DataFrame + IMF series. Empty DataFrame if no api_key or fetch fails. + """ + if not api_key: + return pd.DataFrame({"note": ["IMF Data API requires registration; pass api_key for programmatic fetch. See https://data.imf.org/"]}) + try: + import urllib.parse + # IMF Data API v2 (REST) example + base = "https://api.imf.org/v2" + params = {"series": indicator or "FITB_PA", "country": country or "US"} + q = urllib.parse.urlencode({k: v for k, v in params.items() if v}) + url = f"{base}/data/{database_id}?{q}" + req = Request(url, headers={"User-Agent": "FQBM/1.0", "Accept": "application/json"}) + with urlopen(req, timeout=30) as resp: + import json + data = json.loads(resp.read().decode()) + # Normalize to DataFrame (structure depends on API response) + if isinstance(data, list): + return pd.DataFrame(data) + if isinstance(data, dict) and "CompactData" in data: + dataset = data.get("CompactData", {}).get("Dataset", [{}]) + series = dataset[0].get("Series", []) if dataset else [] + rows = [] + for s in series: + for obs in s.get("Obs", []): + rows.append({"time": obs.get("@TIME_PERIOD"), "value": obs.get("@OBS_VALUE")}) + return pd.DataFrame(rows) + return pd.DataFrame(list(data.items()) if isinstance(data, dict) else []) + except Exception: + return pd.DataFrame() diff --git a/src/fqbm/empirical/regressions.py b/src/fqbm/empirical/regressions.py index 4a96131..56db1e5 100644 --- a/src/fqbm/empirical/regressions.py +++ b/src/fqbm/empirical/regressions.py @@ -14,7 +14,7 @@ import statsmodels.api as sm def generate_synthetic_inflation(n: int = 100, seed: Optional[int] = None) -> pd.DataFrame: - """Synthetic data for Model 1. Columns: pi, dS, output_gap.""" + """Part X: Synthetic data for Model 1 (inflation pass-through). Required columns: pi, dS, output_gap.""" rng = np.random.default_rng(seed) dS = rng.normal(0, 0.05, n) output_gap = rng.normal(0, 0.02, n) @@ -26,8 +26,8 @@ def run_inflation_pass_through( data: Optional[pd.DataFrame] = None, ) -> sm.regression.linear_model.RegressionResultsWrapper: """ - Model 1: π_t = α + β ΔS_t + γ OutputGap_t + ε_t. - Data must have columns: pi, dS, output_gap. + Model 1 (Part X): π_t = α + β ΔS_t + γ OutputGap_t + ε_t. + Required columns: pi, dS, output_gap. """ if data is None: data = generate_synthetic_inflation() @@ -37,7 +37,7 @@ def run_inflation_pass_through( def generate_synthetic_spread(n: int = 100, seed: Optional[int] = None) -> pd.DataFrame: - """Synthetic data for Model 2. Columns: spread, debt_gdp, reserves_gdp, fx_vol.""" + """Part X: Synthetic data for Model 2 (sovereign spread). Required columns: spread, debt_gdp, reserves_gdp, fx_vol.""" rng = np.random.default_rng(seed) debt_gdp = rng.uniform(0.3, 1.0, n) reserves_gdp = rng.uniform(0.05, 0.4, n) @@ -52,7 +52,8 @@ def run_sovereign_spread( data: Optional[pd.DataFrame] = None, ) -> sm.regression.linear_model.RegressionResultsWrapper: """ - Model 2: Spread_t = α + β (Debt/GDP)_t + γ Reserves/GDP_t + δ FXVol_t + ε_t. + Model 2 (Part X): Spread_t = α + β (Debt/GDP)_t + γ Reserves/GDP_t + δ FXVol_t + ε_t. + Required columns: spread, debt_gdp, reserves_gdp, fx_vol. """ if data is None: data = generate_synthetic_spread() @@ -62,7 +63,7 @@ def run_sovereign_spread( def generate_synthetic_capital_flow(n: int = 100, seed: Optional[int] = None) -> pd.DataFrame: - """Synthetic data for Model 3. Columns: dK, rate_diff, risk_premium.""" + """Part X: Synthetic data for Model 3 (capital flow sensitivity). Required columns: dK, rate_diff, risk_premium.""" rng = np.random.default_rng(seed) rate_diff = rng.normal(0.02, 0.01, n) risk_premium = rng.uniform(0, 0.05, n) @@ -74,8 +75,8 @@ def run_capital_flow_sensitivity( data: Optional[pd.DataFrame] = None, ) -> sm.regression.linear_model.RegressionResultsWrapper: """ - Model 3: ΔK_t = θ (i_d - i_f)_t − ω RiskPremium_t + ε_t. - Data columns: dK, rate_diff, risk_premium. + Model 3 (Part X): ΔK_t = θ (i_d - i_f)_t − ω RiskPremium_t + ε_t. + Required columns: dK, rate_diff, risk_premium. """ if data is None: data = generate_synthetic_capital_flow() diff --git a/src/fqbm/ipsas/__init__.py b/src/fqbm/ipsas/__init__.py index 5d92f5f..8372a84 100644 --- a/src/fqbm/ipsas/__init__.py +++ b/src/fqbm/ipsas/__init__.py @@ -8,6 +8,7 @@ IPSAS 2 (cash flow statement structure), IPSAS 24 (budget vs actual). See docs/I from fqbm.ipsas.presentation import ( statement_of_financial_position, budget_vs_actual_structure, + budget_actual_from_state, cash_flow_statement_structure, statement_of_financial_performance_structure, statement_of_changes_in_net_assets_structure, @@ -16,11 +17,27 @@ from fqbm.ipsas.presentation import ( notes_to_financial_statements_structure, statement_of_financial_position_comparative, maturity_risk_disclosure_structure, + accounting_policies_structure, + borrowing_costs_structure, + revenue_recognition_structure, + hyperinflation_restatement_structure, + events_after_reporting_date_structure, + provisions_contingencies_structure, + related_party_disclosure_structure, + revenue_non_exchange_structure, + financial_instruments_measurement_structure, + interests_in_other_entities_structure, + measurement_basis_structure, + transfer_expenses_structure, + cash_basis_statement_structure, + fs_discussion_and_analysis_structure, + functional_presentation_currency_structure, ) __all__ = [ "statement_of_financial_position", "budget_vs_actual_structure", + "budget_actual_from_state", "cash_flow_statement_structure", "statement_of_financial_performance_structure", "statement_of_changes_in_net_assets_structure", @@ -29,4 +46,19 @@ __all__ = [ "notes_to_financial_statements_structure", "statement_of_financial_position_comparative", "maturity_risk_disclosure_structure", + "accounting_policies_structure", + "borrowing_costs_structure", + "revenue_recognition_structure", + "hyperinflation_restatement_structure", + "events_after_reporting_date_structure", + "provisions_contingencies_structure", + "related_party_disclosure_structure", + "revenue_non_exchange_structure", + "financial_instruments_measurement_structure", + "interests_in_other_entities_structure", + "measurement_basis_structure", + "transfer_expenses_structure", + "cash_basis_statement_structure", + "fs_discussion_and_analysis_structure", + "functional_presentation_currency_structure", ] diff --git a/src/fqbm/ipsas/presentation.py b/src/fqbm/ipsas/presentation.py index 46d98fa..3124b96 100644 --- a/src/fqbm/ipsas/presentation.py +++ b/src/fqbm/ipsas/presentation.py @@ -31,22 +31,26 @@ def statement_of_financial_position( reporting_label = reporting_date or getattr(state, "reporting_date", None) or "Reporting date" if entity == "central_bank": - # Identity: B + L_cb = R + C + E_cb. Assets = B, L_cb; Liabilities = R, C; Net assets = E_cb. + # Identity: B + L_cb = R + C + E_cb (+ cbdc_liability if Part IX). Assets = B, L_cb; Liabilities = R, C, cbdc_liability; Net assets = E_cb. + L_cb_val = getattr(state, "L_cb", 0.0) + cbdc_liab = getattr(state, "cbdc_liability", 0.0) rows.append(("Non-current assets", "", "")) rows.append(("Financial assets – government securities", "B", state.B)) - rows.append(("Financial assets – loans to banks (L_cb)", "L_cb", getattr(state, "L_cb", 0.0))) - rows.append(("Total non-current assets", "", state.B + getattr(state, "L_cb", 0.0))) + rows.append(("Financial assets – loans to banks (L_cb)", "L_cb", L_cb_val)) + rows.append(("Total non-current assets", "", state.B + L_cb_val)) rows.append(("Current assets", "", "")) rows.append(("(Other current assets)", "", 0.0)) rows.append(("Total current assets", "", 0.0)) - rows.append(("TOTAL ASSETS", "", state.B + getattr(state, "L_cb", 0.0))) + rows.append(("TOTAL ASSETS", "", state.B + L_cb_val)) rows.append(("", "", "")) rows.append(("Current liabilities", "", "")) rows.append(("Currency in circulation", "C", state.C)) rows.append(("Reserve balances (deposits of banks)", "R", state.R)) - rows.append(("Total current liabilities", "", state.C + state.R)) + if cbdc_liab != 0: + rows.append(("Digital currency in circulation (CBDC)", "cbdc_liability", cbdc_liab)) + rows.append(("Total current liabilities", "", state.C + state.R + cbdc_liab)) rows.append(("Net assets / Equity", "E_cb", state.E_cb)) - rows.append(("TOTAL LIABILITIES AND NET ASSETS", "", state.C + state.R + state.E_cb)) # = B + L_cb + rows.append(("TOTAL LIABILITIES AND NET ASSETS", "", state.C + state.R + cbdc_liab + state.E_cb)) elif entity == "commercial_bank": rows.append(("Current assets", "", "")) @@ -61,6 +65,7 @@ def statement_of_financial_position( rows.append(("TOTAL LIABILITIES AND NET ASSETS", "", state.Deposits + state.E_b)) else: # consolidated + cbdc_liab = getattr(state, "cbdc_liability", 0.0) rows.append(("Non-current assets", "", "")) rows.append(("Financial assets – government securities", "B", state.B)) rows.append(("Total non-current assets", "", state.B)) @@ -73,9 +78,11 @@ def statement_of_financial_position( rows.append(("Current liabilities", "", "")) rows.append(("Currency in circulation", "C", state.C)) rows.append(("Deposits", "Deposits", state.Deposits)) - rows.append(("Total current liabilities", "", state.C + state.Deposits)) + if cbdc_liab != 0: + rows.append(("Digital currency (CBDC)", "cbdc_liability", cbdc_liab)) + rows.append(("Total current liabilities", "", state.C + state.Deposits + cbdc_liab)) rows.append(("Net assets / Equity (E_cb + E_b)", "", state.E_cb + state.E_b)) - rows.append(("TOTAL LIABILITIES AND NET ASSETS", "", state.C + state.Deposits + state.E_cb + state.E_b)) + rows.append(("TOTAL LIABILITIES AND NET ASSETS", "", state.C + state.Deposits + cbdc_liab + state.E_cb + state.E_b)) df = pd.DataFrame(rows, columns=["Line item", "FQBM code", "Amount"]) df.attrs["entity"] = entity @@ -328,3 +335,387 @@ def maturity_risk_disclosure_structure( }) df.attrs["maturity_risk_disclosure"] = True return df + + +# --- IPSAS 3: Accounting policies, prior-period adjustments, error correction --- + + +def accounting_policies_structure( + policies: Optional[list[tuple[str, str]]] = None, +) -> pd.DataFrame: + """ + IPSAS 3: Summary of significant accounting policies and prior-period/error correction. + Columns: Policy area, Description, Prior-period adjustment (Y/N), Error correction (Y/N). + """ + default = [ + ("Basis of preparation", "Accrual basis / cash basis", None, None), + ("Measurement basis", "Historical cost / fair value where applicable", None, None), + ("Prior-period comparative", "Restated for prior-period adjustments", None, None), + ("Error correction", "Material errors corrected retrospectively", None, None), + ] + items = policies or default + df = pd.DataFrame( + items, + columns=["Policy area", "Description", "Prior-period adjustment (Y/N)", "Error correction (Y/N)"], + ) + df.attrs["ipsas_3"] = True + return df + + +# --- IPSAS 5: Borrowing costs --- + + +def borrowing_costs_structure( + line_items: Optional[list[tuple[str, Optional[float], Optional[float]]]] = None, +) -> pd.DataFrame: + """ + IPSAS 5: Borrowing costs – capitalised vs expense. + Columns: Line item, Capitalised, Expensed, Total. + """ + default = [ + ("Borrowing costs on qualifying assets", None, None, None), + ("Borrowing costs expensed", None, None, None), + ("Total borrowing costs", None, None, None), + ] + items = line_items or default + if items and len(items[0]) == 3: + items = [(r[0], r[1], r[2], None) for r in items] + df = pd.DataFrame(items, columns=["Line item", "Capitalised", "Expensed", "Total"]) + df.attrs["ipsas_5"] = True + return df + + +# --- IPSAS 9 / 47: Revenue recognition --- + + +def revenue_recognition_structure( + line_items: Optional[list[tuple[str, Optional[float]]]] = None, +) -> pd.DataFrame: + """ + IPSAS 9/47: Revenue from exchange transactions – recognition and measurement. + Columns: Revenue type, Amount, Recognition policy. + """ + default = [ + ("Revenue from exchange transactions", None, "When performance obligation satisfied"), + ("Interest revenue", None, "Effective interest method"), + ("Other revenue", None, "As applicable"), + ] + items = line_items or default + df = pd.DataFrame(items, columns=["Revenue type", "Amount", "Recognition policy"]) + df.attrs["ipsas_9_47"] = True + return df + + +# --- IPSAS 10: Hyperinflation restatement --- + + +def hyperinflation_restatement_structure( + line_items: Optional[list[tuple[str, Optional[float], Optional[float], Optional[float]]]] = None, +) -> pd.DataFrame: + """ + IPSAS 10: Restatement for hyperinflation (historical in nominal, restated in constant). + Columns: Line item, Historical (nominal), Restatement factor, Restated (constant). + """ + default = [ + ("Monetary assets", None, None, None), + ("Monetary liabilities", None, None, None), + ("Non-monetary assets", None, None, None), + ("Net assets", None, None, None), + ] + items = line_items or default + df = pd.DataFrame( + items, + columns=["Line item", "Historical (nominal)", "Restatement factor", "Restated (constant)"], + ) + df.attrs["ipsas_10"] = True + return df + + +# --- IPSAS 14: Events after the reporting date --- + + +def events_after_reporting_date_structure( + events: Optional[list[tuple[str, str, Literal["Adjust", "Disclose"]]]] = None, +) -> pd.DataFrame: + """ + IPSAS 14: Events after the reporting date – adjust vs disclose. + Columns: Description, Date, Treatment (Adjust / Disclose). + """ + default = [ + ("Adjusting event example", "YYYY-MM-DD", "Adjust"), + ("Non-adjusting event example", "YYYY-MM-DD", "Disclose"), + ] + items = events or default + df = pd.DataFrame(items, columns=["Description", "Date", "Treatment"]) + df.attrs["ipsas_14"] = True + return df + + +# --- IPSAS 19: Provisions and contingencies --- + + +def provisions_contingencies_structure( + line_items: Optional[list[tuple[str, Optional[float], Optional[float], str]]] = None, +) -> pd.DataFrame: + """ + IPSAS 19: Provisions and contingent liabilities/assets. + Columns: Line item, Carrying amount, Movement, Nature (provision / contingent liability / contingent asset). + """ + default = [ + ("Provisions", None, None, "Provision"), + ("Contingent liabilities", None, None, "Contingent liability"), + ("Contingent assets", None, None, "Contingent asset"), + ] + items = line_items or default + df = pd.DataFrame(items, columns=["Line item", "Carrying amount", "Movement", "Nature"]) + df.attrs["ipsas_19"] = True + return df + + +# --- IPSAS 20: Related party disclosure --- + + +def related_party_disclosure_structure( + parties: Optional[list[tuple[str, str, Optional[float], str]]] = None, +) -> pd.DataFrame: + """ + IPSAS 20: Related party identification and disclosure. + Columns: Related party, Relationship, Amount / transaction, Nature. + """ + default = [ + ("Government", "Controlling entity", None, "Funding / appropriations"), + ("Other related entities", "Description", None, "Nature of transaction"), + ] + items = parties or default + df = pd.DataFrame(items, columns=["Related party", "Relationship", "Amount", "Nature"]) + df.attrs["ipsas_20"] = True + return df + + +# --- IPSAS 23: Revenue from non-exchange --- + + +def revenue_non_exchange_structure( + line_items: Optional[list[tuple[str, Optional[float], str]]] = None, +) -> pd.DataFrame: + """ + IPSAS 23: Revenue from non-exchange transactions (taxes, transfers, grants). + Columns: Revenue type, Amount, Recognition (e.g. when received / when entitlement arises). + """ + default = [ + ("Tax revenue", None, "When taxable event occurs"), + ("Grants and transfers received", None, "When entitlement is virtually certain"), + ("Other non-exchange revenue", None, "As per IPSAS 23"), + ] + items = line_items or default + df = pd.DataFrame(items, columns=["Revenue type", "Amount", "Recognition"]) + df.attrs["ipsas_23"] = True + return df + + +# --- IPSAS 29 / 41: Financial instrument measurement and ECL --- + + +def financial_instruments_measurement_structure( + line_items: Optional[list[tuple[str, str, Optional[float], Optional[float], Optional[float]]]] = None, +) -> pd.DataFrame: + """ + IPSAS 29/41: Classification, measurement basis, and ECL disclosure. + Columns: Instrument, Measurement (AC/FVOCI/FVPL), Carrying amount, ECL, ECL stage. + """ + default = [ + ("Financial assets at amortised cost", "AC", None, None, None), + ("Financial assets at FVOCI", "FVOCI", None, None, None), + ("Financial assets at FVPL", "FVPL", None, None, None), + ("Financial liabilities", "AC/FVPL", None, None, None), + ] + items = line_items or default + df = pd.DataFrame( + items, + columns=["Instrument", "Measurement", "Carrying amount", "ECL", "ECL stage"], + ) + df.attrs["ipsas_29_41"] = True + return df + + +# --- IPSAS 38: Interests in other entities --- + + +def interests_in_other_entities_structure( + entities: Optional[list[tuple[str, str, Optional[float]]]] = None, +) -> pd.DataFrame: + """ + IPSAS 38: Disclosure of interests in other entities (subsidiaries, associates, JVs). + Columns: Entity, Type (subsidiary/associate/JV), Carrying amount. + """ + default = [ + ("Subsidiary 1", "Subsidiary", None), + ("Associate 1", "Associate", None), + ("Joint venture 1", "Joint venture", None), + ] + items = entities or default + df = pd.DataFrame(items, columns=["Entity", "Type", "Carrying amount"]) + df.attrs["ipsas_38"] = True + return df + + +# --- IPSAS 46: Measurement basis --- + + +def measurement_basis_structure( + items: Optional[list[tuple[str, str]]] = None, +) -> pd.DataFrame: + """ + IPSAS 46: Measurement basis (historical cost, fair value, etc.) by category. + Columns: Category, Measurement basis. + """ + default = [ + ("Financial assets – hold to collect", "Amortised cost"), + ("Financial assets – hold to collect and sell", "FVOCI"), + ("Financial assets – other", "FVPL"), + ("Financial liabilities", "Amortised cost / FVPL"), + ("Non-financial assets", "Historical cost / revaluation"), + ] + items = items or default + df = pd.DataFrame(items, columns=["Category", "Measurement basis"]) + df.attrs["ipsas_46"] = True + return df + + +# --- IPSAS 48: Transfer expenses --- + + +def transfer_expenses_structure( + line_items: Optional[list[tuple[str, Optional[float], str]]] = None, +) -> pd.DataFrame: + """ + IPSAS 48: Transfer expense classification (e.g. grants, subsidies, benefits). + Columns: Transfer type, Amount, Classification. + """ + default = [ + ("Grants and subsidies paid", None, "Transfer expense"), + ("Social benefits", None, "Transfer expense"), + ("Other transfers", None, "As per IPSAS 48"), + ] + items = line_items or default + df = pd.DataFrame(items, columns=["Transfer type", "Amount", "Classification"]) + df.attrs["ipsas_48"] = True + return df + + +# --- Cash-basis statement --- + + +def cash_basis_statement_structure( + line_items: Optional[list[tuple[str, Optional[float]]]] = None, +) -> pd.DataFrame: + """ + Cash-basis IPSAS: Statement of cash receipts and payments. + Columns: Line item, Cash receipts, Cash payments, Net. + """ + default = [ + ("Cash from operations", None, None, None), + ("Cash from investing", None, None, None), + ("Cash from financing", None, None, None), + ("Net change in cash", None, None, None), + ] + items = line_items or default + df = pd.DataFrame(items, columns=["Line item", "Cash receipts", "Cash payments", "Net"]) + df.attrs["cash_basis"] = True + return df + + +# --- RPG 2: Financial Statement Discussion and Analysis --- + + +def fs_discussion_and_analysis_structure( + sections: Optional[list[tuple[str, str]]] = None, +) -> pd.DataFrame: + """ + RPG 2: Financial Statement Discussion and Analysis (narrative) – template. + Columns: Section, Content (for user fill). + """ + default = [ + ("Overview", "Summary of financial position and performance"), + ("Financial position", "Key balance sheet changes and ratios"), + ("Performance", "Revenue, expense, surplus/deficit analysis"), + ("Cash flows", "Liquidity and funding"), + ("Risks and uncertainties", "Key risks and sensitivities"), + ("Forward-looking information", "Plans and outlook"), + ] + items = sections or default + df = pd.DataFrame(items, columns=["Section", "Content"]) + df.attrs["rpg_2"] = True + return df + + +# --- Functional / presentation currency --- + + +def functional_presentation_currency_structure( + functional: str = "LCU", + presentation: Optional[str] = None, + fx_rates: Optional[dict[str, float]] = None, +) -> pd.DataFrame: + """ + Multi-currency reporting: functional currency and presentation currency. + Columns: Description, Value. If presentation differs, translation rates can be provided. + """ + presentation = presentation or functional + rows = [ + ("Functional currency", functional), + ("Presentation currency", presentation), + ("Translation method", "Closing rate for assets/liabilities" if presentation != functional else "N/A"), + ] + if fx_rates: + for k, v in fx_rates.items(): + rows.append((f"FX rate ({k})", v)) + df = pd.DataFrame(rows, columns=["Description", "Value"]) + df.attrs["functional_presentation_currency"] = True + return df + + +# --- Budget / appropriation model (internal representation for IPSAS 24) --- + + +def budget_actual_from_state( + state: FQBMState, + budget: Optional[dict[str, float]] = None, + line_items: Optional[list[str]] = None, +) -> pd.DataFrame: + """ + Build budget vs actual from FQBM state. Actuals are derived from state aggregates; + budget columns are filled from the budget dict (e.g. {"Total assets": 1000, "Total liabilities": 600}). + Optional line_items: list of keys; default uses Total assets, Total liabilities, Net assets, etc. + """ + default_items = [ + "Total revenue", + "Total expense", + "Net surplus/(deficit)", + "Total assets", + "Total liabilities", + "Net assets", + ] + items = line_items or default_items + L_cb_val = getattr(state, "L_cb", 0.0) + actual_from_state = { + "Total assets": state.B + L_cb_val + state.Loans, + "Total liabilities": state.C + state.R + state.Deposits, + "Net assets": state.E_cb + state.E_b, + } + budget = budget or {} + rows = [] + for item in items: + actual = actual_from_state.get(item) if item in actual_from_state else budget.get(item) + orig = budget.get(f"{item} (original)") or budget.get(item) + rows.append({ + "Line item": item, + "Original budget": orig, + "Final budget": budget.get(f"{item} (final)") or orig, + "Actual": actual, + "Variance": (actual - orig) if (actual is not None and orig is not None) else None, + "Material (Y/N)": None, + }) + df = pd.DataFrame(rows) + df.attrs["ipsas_24_from_state"] = True + return df diff --git a/src/fqbm/matrix.py b/src/fqbm/matrix.py index a347dfd..55c8593 100644 --- a/src/fqbm/matrix.py +++ b/src/fqbm/matrix.py @@ -57,3 +57,39 @@ def four_quadrant_summary(state: FQBMState, L_cb: Optional[float] = None) -> dic "total_liabilities_cr": total_liab_cr, "identity_A_eq_L_plus_E": abs((total_assets_dr) - (total_liab_dr + total_liab_cr)) < 1e-6, } + + +def cross_sector_consistency_check(state: FQBMState, L_cb: Optional[float] = None) -> dict: + """ + Check that four-quadrant matrix totals equal sectoral (CB + bank) sums. + + Central bank: B + L_cb = R + C + E_cb. Commercial bank: Loans = Deposits + E_b. + Consolidated assets (Dr) = B + L_cb + Loans; liabilities (Dr) = C + R + Deposits; equity (Cr) = E_cb + E_b. + Returns dict with consistent (bool), cb_balance, bank_balance, matrix_totals, and message. + """ + L_cb_val = L_cb if L_cb is not None else getattr(state, "L_cb", 0.0) + cb_assets = state.B + L_cb_val + cb_liab_equity = state.R + state.C + state.E_cb + cb_balance = abs(cb_assets - cb_liab_equity) < 1e-6 + bank_assets = state.Loans + bank_liab_equity = state.Deposits + state.E_b + bank_balance = abs(bank_assets - bank_liab_equity) < 1e-6 + total_assets_dr = state.B + L_cb_val + state.Loans + total_liab_dr = state.C + state.R + state.Deposits + total_liab_cr = state.E_cb + state.E_b + matrix_identity = abs(total_assets_dr - (total_liab_dr + total_liab_cr)) < 1e-6 + consistent = cb_balance and bank_balance and matrix_identity + return { + "consistent": consistent, + "cb_balance": cb_balance, + "cb_assets": cb_assets, + "cb_liab_equity": cb_liab_equity, + "bank_balance": bank_balance, + "bank_assets": bank_assets, + "bank_liab_equity": bank_liab_equity, + "matrix_identity": matrix_identity, + "total_assets_dr": total_assets_dr, + "total_liabilities_dr": total_liab_dr, + "total_liabilities_cr": total_liab_cr, + "message": "OK" if consistent else "Sector or matrix identity violation", + } diff --git a/src/fqbm/scenarios/presets.py b/src/fqbm/scenarios/presets.py index 839a4a3..a162af5 100644 --- a/src/fqbm/scenarios/presets.py +++ b/src/fqbm/scenarios/presets.py @@ -160,3 +160,71 @@ def list_scenarios() -> list[str]: def get_scenario(name: str) -> Optional[ScenarioPreset]: """Return preset by name, or None.""" return _build_registry().get(name) + + +def get_case_narrative(name: str) -> Optional[dict[str, Any]]: + """ + Part XI: Structured narrative and coded case outputs for a historical scenario. + Returns dict with keys: name, description, narrative (paragraph), state_summary (dict), + key_drivers (list), suggested_shocks (list), or None if scenario not found. + """ + preset = get_scenario(name) + if preset is None: + return None + narratives = { + "asia_1997": { + "narrative": ( + "Asian Financial Crisis (1997): Fixed exchange rate regimes and heavy short-term " + "foreign-currency debt left economies exposed to sudden stops. Reserve depletion " + "and liability revaluation led to currency collapse and banking stress. The scenario " + "calibrates low reserves (R), high sovereign spread, and deposit outflow / FX volatility shocks." + ), + "key_drivers": ["FX regime", "Reserve adequacy", "Short-term external debt", "Sudden stop"], + "suggested_shocks": ["deposit_outflow", "fx_vol", "spread_widening"], + }, + "gfc_2008": { + "narrative": ( + "Global Financial Crisis (2008): Shadow banking and repo-funded leverage amplified " + "balance sheet expansion. MBS and structured product losses eroded bank capital; " + "central bank QE and liquidity support stabilized the system. The scenario uses " + "high bank leverage (Loans >> E_b), QE (d_B), and default/deposit outflow shocks." + ), + "key_drivers": ["Leverage", "Repo and wholesale funding", "Capital erosion", "QE"], + "suggested_shocks": ["default_rate", "deposit_outflow"], + }, + "pandemic_2020": { + "narrative": ( + "Pandemic (2020): Massive fiscal expansion and central bank balance sheet expansion " + "($4T+ in major jurisdictions), swap lines, and deposit growth. The scenario reflects " + "elevated reserves (R), large CB securities (B), and sovereign debt and growth shocks." + ), + "key_drivers": ["Fiscal expansion", "CB balance sheet", "Swap lines", "Deposit growth"], + "suggested_shocks": ["growth_differential", "sovereign stress"], + }, + "rate_shock_2022": { + "narrative": ( + "Rate shock (2022–23): Dollar strength, EM stress, and duration losses in advanced " + "economies. The scenario sets a stronger dollar (S), higher sovereign spread, and " + "FX volatility / spread-widening shocks for stress testing." + ), + "key_drivers": ["Interest rate differential", "Dollar strength", "Duration", "EM stress"], + "suggested_shocks": ["fx_vol", "spread_widening"], + }, + } + info = narratives.get(preset.name, {"narrative": preset.description, "key_drivers": [], "suggested_shocks": []}) + state = preset.state + L_cb_val = getattr(state, "L_cb", 0.0) + state_summary = { + "B": state.B, "R": state.R, "C": state.C, "Loans": state.Loans, "Deposits": state.Deposits, + "E_cb": state.E_cb, "E_b": state.E_b, "S": state.S, "Spread": state.Spread, + "L_cb": L_cb_val, + "total_assets": state.B + L_cb_val + state.Loans, + } + return { + "name": preset.name, + "description": preset.description, + "narrative": info["narrative"], + "state_summary": state_summary, + "key_drivers": info["key_drivers"], + "suggested_shocks": info.get("suggested_shocks", []), + } diff --git a/src/fqbm/sheets/capital_stress.py b/src/fqbm/sheets/capital_stress.py index ad292f1..580c824 100644 --- a/src/fqbm/sheets/capital_stress.py +++ b/src/fqbm/sheets/capital_stress.py @@ -24,7 +24,7 @@ def stress_tables( params: Optional[StressParams] = None, ) -> dict: """ - Compute liquidity stress (deposit run vs RCR) and capital shock table. + Part XII: Compute liquidity stress (deposit run vs RCR) and capital shock table. Returns dict with 'liquidity_stress' and 'capital_stress' DataFrames. """ params = params or StressParams() diff --git a/src/fqbm/sheets/dashboard.py b/src/fqbm/sheets/dashboard.py index cfe3c3e..f9f261a 100644 --- a/src/fqbm/sheets/dashboard.py +++ b/src/fqbm/sheets/dashboard.py @@ -33,6 +33,7 @@ def dashboard_aggregate( "E_cb": state.E_cb, "E_b": state.E_b, "S": state.S, "Spread": state.Spread, "O": state.O, "L_cb": getattr(state, "L_cb", 0), + "cbdc_liability": getattr(state, "cbdc_liability", 0), } out = {"state": snapshot, "ratios": ratios} # Part VII: shadow banking leverage ratio (bank) diff --git a/src/fqbm/state.py b/src/fqbm/state.py index 691224a..8f5dbc0 100644 --- a/src/fqbm/state.py +++ b/src/fqbm/state.py @@ -14,6 +14,7 @@ Part XIV state: X = [B, R, C, Loans, Deposits, E_cb, E_b, S, K, Spread, O, L_cb] - Spread: sovereign spread - O: oil/commodity price index - L_cb: central bank loans (Part II identity: B + L_cb = R + C + E_cb) +- cbdc_liability: central bank digital currency liability (Part IX; not in numeric vector for diff model) """ from dataclasses import dataclass, field @@ -39,7 +40,10 @@ class FQBMState: Spread: float = 0.0 O: float = 1.0 L_cb: float = 0.0 + cbdc_liability: float = 0.0 # Part IX: CBDC liability at central bank (not in to_vector for diff model) reporting_date: Optional[str] = field(default=None, compare=False) + functional_currency: Optional[str] = field(default=None, compare=False) # e.g. "USD", "LCU" + presentation_currency: Optional[str] = field(default=None, compare=False) # e.g. "USD"; if None, same as functional def to_vector(self) -> list[float]: return [ @@ -61,7 +65,8 @@ class FQBMState: return FQBMState( B=self.B, R=self.R, C=self.C, Loans=self.Loans, Deposits=self.Deposits, E_cb=self.E_cb, E_b=self.E_b, S=self.S, K=self.K, Spread=self.Spread, O=self.O, - L_cb=self.L_cb, reporting_date=self.reporting_date, + L_cb=self.L_cb, cbdc_liability=self.cbdc_liability, reporting_date=self.reporting_date, + functional_currency=self.functional_currency, presentation_currency=self.presentation_currency, ) diff --git a/src/fqbm/workbook/runner.py b/src/fqbm/workbook/runner.py index 0c27944..5562f64 100644 --- a/src/fqbm/workbook/runner.py +++ b/src/fqbm/workbook/runner.py @@ -32,6 +32,7 @@ def run_workbook( scenario: Optional[str] = None, cbdc_params: Optional[Any] = None, ccp_params: Optional[Any] = None, + shadow_params: Optional[Any] = None, ) -> dict[str, Any]: """ Run full workbook: Sheet 1 → 2 → 4,5,6 → 3,7 → 8. Optionally export to Excel. @@ -68,7 +69,8 @@ def run_workbook( d = deposit_to_cbdc_shift(p.deposit_shift) state.Deposits += d["d_deposits"] state.R += d["d_reserves"] - cbdc_result = {"d_deposits": d["d_deposits"], "d_reserves": d["d_reserves"], "cbdc_liability": d["d_cbdc_liability"]} + state.cbdc_liability = getattr(state, "cbdc_liability", 0) + d["d_cbdc_liability"] + cbdc_result = {"d_deposits": d["d_deposits"], "d_reserves": d["d_reserves"], "cbdc_liability": state.cbdc_liability} stress = stress_tables(state, stress_params) if mc_runs > 0: @@ -78,6 +80,28 @@ def run_workbook( dashboard = dashboard_aggregate(state, mc_runs=mc_runs, shock_spec=shock_spec) + # Part VII: optional repo multiplier (wired into workbook and dashboard) + repo_result = None + if shadow_params is not None: + try: + from fqbm.sheets.shadow_banking import repo_multiplier, ShadowBankingParams + sp = shadow_params if isinstance(shadow_params, ShadowBankingParams) else None + haircut = getattr(shadow_params, "haircut", 0.02) + rounds = getattr(shadow_params, "repo_rounds", 3) + collateral = state.B + state.Loans # proxy for collateral + repo_result = repo_multiplier(collateral, haircut=haircut, rounds=rounds) + dashboard["repo_multiplier"] = repo_result + except Exception: + pass + if repo_result is None: + try: + from fqbm.sheets.shadow_banking import repo_multiplier + collateral = state.B + state.Loans + repo_result = repo_multiplier(collateral, haircut=0.02, rounds=3) + dashboard["repo_multiplier"] = repo_result + except Exception: + pass + # Part VIII: optional CCP metrics (margin identity check) ccp_result = None if ccp_params is not None: @@ -102,6 +126,7 @@ def run_workbook( "dashboard": dashboard, "cbdc": cbdc_result, "ccp": ccp_result, + "repo_multiplier": repo_result, } if export_path and export_path.endswith((".xlsx", ".xls")):