Mid-fiscal-year, someone asks: where does this obligation curve land at year-end? In the flow simulator, there are two honest answers, computed on identical footing — and the postmortem on how they were picked is the point of this post.
The setup
The series is cumulative obligated by fiscal period for each agency — P2 through P10 reported at the freeze (the API doesn’t report P1; October just isn’t there, and the module says so). Two methods:
- Linear BAU. Actuals through the last reported period, then the observed average period-to-period increment continued — capped at budget authority, because an obligation curve cannot exceed its appropriation.
- Damped Holt-Winters (additive). Seasonality m=4, damping φ=0.98 fixed, smoothing parameters (α, β, γ) chosen from a 5×5×5 grid by AICc.
Both produce P11 and P12 for the in-progress year. Both are always shown.
The backtest
Walk-forward over FY2022–FY2025: 36 held-out periods — each year, hold out P10, P11, and P12 in turn, fit on everything before, score the hold-out, no lookahead. The table is baked in as data, never recomputed at view time:
| Method | Overall MAE ($bn) |
|---|---|
| Damped Holt-Winters | 20.161 |
| Linear BAU | 22.611 |
The pick rule, stated before looking at the numbers: lower overall MAE wins; ties within a $0.5bn band default to linear. Holt-Winters wins, 20.161 to 22.611, so the page’s picked curve is the damped one.
The honest part
A 2.45 billion margin on cumulative obligation series is thin, and it is convention-sensitive: under a different standard library’s seasonal decomposition convention (statsmodels’ baseline-residual approach), the winner flips to linear (23.705 MAE). And for VA specifically, Holt-Winters is worse — the aggregate win is carried by the other agencies.
So the page does three things instead of hiding that:
- shows both methods’ P11/P12 projections, not just the pick;
- states the pick and the rule that made it;
- carries a note that the win is thin and convention-dependent.
This is the general lesson I’d apply to any short-series forecast: with a handful of years of monthly data, the method choice can be worth billions in apparent accuracy for reasons that are partly bookkeeping. Document the convention, set a tie band, show both curves. A forecast that can’t say that about itself is a number, not an analysis.
The no-fabrication plumbing
The baked table has a re-derivation gate: a Python script recomputes it from
the frozen JSON with the same formulas and diffs it against what ships. One
footgun worth recording: Python 3.12’s sum() uses compensated
accumulation and does not bit-for-bit match a naive browser loop — the
re-deriver uses naive accumulation on purpose, because the contract is
“reproduce the browser,” not “be more precise than the browser.”
The next post checks the biggest baseline in federal finance: did CBO’s age well?
— Joanna