Skip to content

feat: live-formula Excel export (toolkit + canonical translator + verification) - #13

Merged
JeffBrines merged 12 commits into
mainfrom
feat/excel-live-model
Jun 23, 2026
Merged

JeffBrines merged 12 commits into
mainfrom
feat/excel-live-model

Conversation

@JeffBrines

Copy link
Copy Markdown
Owner

What

The most-requested finance artifact, made native: a workbook where changing an assumption recalculates the model. Built per the spec as a toolbelt capability, not a bolt-on exporter.

How it works

  • pyfpa/excel/toolkit.py: composable openpyxl helpers (named assumption cells/rows, formula filling, formats). What agent-generated exporters compose.
  • pyfpa/excel/model_workbook.py: the canonical translator. model_to_excel(cfg, path) compiles EntityConfig to an Assumptions sheet (every driver a named, editable cell) plus a Model sheet where the full monthly P&L and cash flow are real formulas: per-instrument debt balance rows with interest on the pre-payment balance, NOL opening/used/closing rows feeding tax, calendar-correct seasonality references, first-month working-capital deltas against opening balances. Formula vocabulary restricted to arithmetic, ^, SUM, MIN, MAX, IF (enforced by test).
  • pyfpa/excel/verify.py: verify_workbook evaluates the workbook with the formulas library (dev-only dependency) and compares every line, every month, to cashflow_from_config at 1e-6. NaN, unevaluated, and Excel-error cells are failures, never crashes.
  • model-export CLI command, public API exports, and the fpa-excel-model skill: triggers on how users actually ask ("export this to Excel", "a workbook that recalculates"), encodes the workflow, and carries the core guardrail: no workbook ships unverified. Cadence variants (quarterly, weekly, per-segment) are agent work composed from the toolkit and registered as entrypoints, never kernel variants.

Proof

  • CI equivalence: the Ridgeline workbook and a deliberately nasty edge config (term loan exhausting mid-horizon, NOL consumption, April start, negative growth, 24 months) reproduce the engine line by line at 1e-6. These run in CI via the dev extra.
  • Live recalc verified end to end: change dio_days by 30 in the saved workbook, re-evaluate, compare to the engine on the modified config: ending cash month 12 matches at a relative deviation of 1.1e-16, with the answer moving by -$205,667 (so the change genuinely propagated).
  • 269 tests passing; independent whole-branch review with the one Important finding (Excel error sentinels crashing verify) fixed and regression-tested.

Notes

  • Runtime dependencies unchanged (openpyxl was already shipped); formulas>=1.2 confined to the dev extra.
  • Ridgeline demo now emits model.xlsx (gitignored like the other xlsx outputs).
  • Spec and plan: docs/superpowers/specs/2026-06-10-excel-live-model-design.md, docs/superpowers/plans/2026-06-10-excel-live-model.md.

🤖 Generated with Claude Code

Adds pyfpa/excel/verify.py with VerifyReport and verify_workbook that
lazily loads the formulas library, evaluates every Model-sheet cell, and
compares to cashflow_from_config output at rel_tol=1e-6. NaN/unevaluated
cells are hard failures. Adds formulas>=1.2 to the dev extra in
pyproject.toml. Three tests cover pass, corruption detection, and the
missing-dependency error message.

Confirmed formulas 1.3.4 key format: "'[<filename>]MODEL'!<COL><ROW>"
(filename preserves original casing, sheet name is uppercase, no dollar signs).
Value extraction via sol[key].value[0][0].
Adds test_excel_equivalence.py with two cases: Ridgeline config (standard
regression) and a nasty edge config (term loan exhausting mid-horizon at
month 10 via MIN branch, NOL consumption across months, April start with
off-January calendar seasonality indexing, negative growth on channel B,
24-month horizon exercising year-offset exponent > 0). Both pass at
rel_tol=1e-6 with no translator fixes required.

@JeffBrines JeffBrines left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good

@JeffBrines
JeffBrines merged commit ddac4e9 into main Jun 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant