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:

  1. 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.
  2. 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:

MethodOverall MAE ($bn)
Damped Holt-Winters20.161
Linear BAU22.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