| Country | MA12 (pp) | OOS-STL (pp) | MA12+seasonal (pp) |
|---|---|---|---|
| Ukraine | 1.38 | 1.79 | 1.72 |
| Venezuela | 5.33 | 8.52 | 8.34 |
| Yemen | 8.05 | 12.16 | 12.06 |
| Mexico | 0.34 | 0.44 | 0.42 |
| Denmark | 0.71 | 0.95 | 0.93 |
Predicting Trade: The Upgrade That Wasn’t (Part 2)
Seasonal decomposition was supposed to fix the moving average. It didn’t — and finding out why was more useful than being right.
Part of an ongoing series on IMF International Merchandise Trade Statistics (IMTS) data, built by directing an AI research assistant: I set the questions, the evaluation design, and the interpretation; Claude wrote and ran the code. The analysis is what emerged from that back-and-forth — including the parts where my hypothesis turned out to be wrong.
Where we left off
Part 1 built a deliberately bad one-step-ahead forecaster — a 12-month moving average of partner export shares — and used its residuals to flag anomalies. It ended with a promise: Part 2 would bring in seasonal-trend decomposition (STL) to fix the moving average’s three failure modes (trend lag, calendar blindness, slow break absorption), and add a proper evaluation framework to measure whether any of this actually detects real-world events.
This post keeps that promise, with a twist: the seasonal upgrade loses to the naive baseline on every country, and the evaluation framework — the less glamorous half of the promise — turns out to be the thing that matters. By the end we’ll know why the obvious upgrade fails on this data, which “improvement” is just relabeling, and what genuinely works for the case every point-anomaly rule missed.
Setup
Same data and universe as Part 1: monthly goods-export shares to each country’s top 6 bilateral partners, for Ukraine, Venezuela, Yemen, Mexico, and Denmark, January 2017 through December 2025.
Four detectors
We compare four ways of turning each share series into anomaly flags. All four share the same flagging rule — \(|e_t| > 2\sigma\) on their residuals — so any difference comes from the model, not the threshold.
MA12 (the Part 1 baseline). Prediction = trailing 12-month mean, shifted one period. Honest one-step-ahead: it only ever sees the past.
Classic STL. Decompose the full series into trend + seasonal + remainder with STL, treat the remainder as the residual. This is the textbook approach — and it is not a forecast. The loess trend at month \(t\) is estimated using months after \(t\), so the remainder knows the future. It gets flattered by hindsight, and we include it precisely to measure how much.
Out-of-sample STL (OOS-STL). At each month \(t\), fit STL only to data through \(t-1\), then forecast \(\hat{s}_t\) = last trend value + the seasonal component from the same calendar month a year earlier. An expanding-window, honest version of the decomposition idea.
MA12 + seasonal. Deseasonalize with past-only STL, apply the trailing 12-month mean to the seasonally-adjusted series, re-add the seasonal. The hypothesis: keep the MA’s low-variance level estimate, fix only its calendar blindness.
Round 1: forecast accuracy
If the residuals are going to carry meaning, the forecasts need to be good. Here’s one-step-ahead MAE by country, averaged across each country’s partner series (classic STL is excluded — its “residuals” aren’t forecast errors).
The naive baseline wins everywhere — not by a little. OOS-STL is 25–60% worse; even the conservative MA12+seasonal hybrid loses on every country. Two seemingly obvious explanations fail quickly: it isn’t the warm-up window (both seasonal methods get the same one), and it isn’t robustness settings (robust STL is already being used).
Round 2: does anything detect real events?
Part 1 promised an evaluation framework, and this is where it earns its keep. For events with well-known dates, we check whether flags land inside the event window, and compare against the false-alarm rate everywhere else:
- Ukraine: Russian invasion — February–May 2022
- Venezuela: oil sanctions escalation — January–June 2019
- Mexico & Denmark: COVID shock — March–June 2020
- Yemen: no dated event (its disruption is chronic, not acute) — false alarms only
A 2σ rule flags ~4.6% of months by construction, so ~5% is the score a coin-flip detector gets. Hit rates need to clear that bar to mean anything.
| Country | Method | Hits | Hit % | False-alarm % |
|---|---|---|---|---|
| Ukraine | MA12 | 9/22 | 41% | 4.2% |
| Ukraine | STL (classic) | 13/22 | 59% | 4.8% |
| Ukraine | OOS-STL | 9/22 | 41% | 3.1% |
| Ukraine | MA12+seasonal | 10/22 | 45% | 3.6% |
| Venezuela | MA12 | 4/36 | 11% | 5.7% |
| Venezuela | STL (classic) | 3/36 | 8% | 7.7% |
| Venezuela | OOS-STL | 2/36 | 6% | 6.0% |
| Venezuela | MA12+seasonal | 2/36 | 6% | 6.0% |
| Yemen | MA12 | — | — | 5.1% |
| Yemen | STL (classic) | — | — | 8.4% |
| Yemen | OOS-STL | — | — | 5.1% |
| Yemen | MA12+seasonal | — | — | 5.1% |
| Mexico | MA12 | 6/24 | 25% | 2.7% |
| Mexico | STL (classic) | 8/24 | 33% | 4.2% |
| Mexico | OOS-STL | 4/24 | 17% | 4.4% |
| Mexico | MA12+seasonal | 5/24 | 21% | 3.8% |
| Denmark | MA12 | 2/24 | 8% | 4.2% |
| Denmark | STL (classic) | 3/24 | 12% | 8.2% |
| Denmark | OOS-STL | 3/24 | 12% | 4.8% |
| Denmark | MA12+seasonal | 6/24 | 25% | 4.6% |
Three things stand out:
Ukraine is detectable; Venezuela mostly isn’t. Every method beats the base rate handily on Ukraine (41–59% hit rates against a ~5% floor) — a violent overnight restructuring produces exactly the large one-month residuals that point rules catch. Venezuela’s 2019 collapse, which unfolded over months, stays near the base rate for every honest method. Same story as Part 1, now with numbers.
Classic STL’s edge is borrowed from the future. It posts the best Ukraine hit rate (59%) — and doubles Denmark’s false-alarm rate to 8.2%. The control country shouldn’t get more interesting under a better method. Hindsight decomposition sharpens real events and noise alike.
The honest seasonal methods buy nothing. OOS-STL and MA12+seasonal sit at or below MA12’s hit rates. Worse forecasts, no detection gain.
Round 3: is a robust threshold the fix?
Part 1 flagged a masking problem: σ is estimated from residuals including the anomalies, so disrupted series get inflated thresholds. The standard fix is a robust scale estimate — median absolute deviation (MAD) instead of the standard deviation. Swapping it in raises hit rates everywhere… and raises false alarms nearly as fast. That should make you suspicious that we’ve improved anything.
The clean way to compare two threshold rules is at matched false-alarm rates: sweep the multiplier \(z\) for both rules and trace hit rate against false-alarm rate.
At any given false-alarm budget, the two rules deliver roughly the same hit rate. Switching to MAD at the same \(z\) isn’t a better detector — it’s a lower threshold wearing a robustness costume. If you want more hits, you could have just lowered \(z\). (MAD retains a real advantage the pooled curve understates: in heavily contaminated individual series it keeps single catastrophic months from swallowing the threshold. But as a headline fix, it’s relabeling.)
Round 4: the right tool for slow collapses
The Venezuela problem — sanctions bite, exports bleed away over 18 months, no single month looks extreme — is not a point-anomaly problem. It’s a sustained shift problem, and the classical tool for it is CUSUM (cumulative sum control charting): accumulate standardized residuals, subtracting a slack \(k\) each month, and alarm when the cumulative drift exceeds a decision interval \(h\). Small persistent errors that a 2σ rule ignores add up until CUSUM fires.
We run a standard two-sided CUSUM (\(k = 0.5\sigma\), \(h = 4\sigma\), robust σ, reset after each alarm) on the same MA12 residuals:
The results on the headline cases are what the point rules couldn’t deliver:
- Ukraine → Russia alarms in March 2022, one month after the invasion. It also alarms in mid-2020 and mid-2021 — not noise, but the tail of the long post-2014 decoupling grinding Russia’s share lower, which is exactly the kind of sustained drift CUSUM is built to accumulate.
- Venezuela → USA alarms in March 2019, two months into the sanctions window that every 2σ rule scored near the base rate. Venezuela → India and → China alarm within the following two months, which is the rerouting story: barrels that stopped flowing to Houston went to Jamnagar instead.
The honest caveat: at these settings, an in-control Gaussian series should alarm roughly once every 14 years; our Denmark series alarm every 2–3 years. Trade-share residuals have heavier tails than the theory assumes (and the MA’s trend-lag feeds slow drifts into the accumulator), so the decision interval needs empirical calibration — the false-alarm arithmetic from Round 2 applies here too. CUSUM isn’t magic; it’s the right shape of detector for the right shape of disruption.
Scoreboard
| Approach | One-step accuracy | Event detection | Verdict |
|---|---|---|---|
| MA12 (naive baseline) | Best | Good on abrupt events | Still the champion, annoyingly |
| Classic STL | not a forecast | Best-looking, borrowed from the future | Use for description, never for detection claims |
| OOS-STL / MA12+seasonal | 25–60% worse | No gain | Fixed a failure mode the data doesn’t have |
| MAD threshold | n/a | Same ROC curve as sd | Relabeled threshold, not a better detector |
| CUSUM on MA12 residuals | n/a | Catches slow collapses point rules miss | The real upgrade — calibrate before trusting |
The methodological throughline: every “improvement” was evaluated against the boring baseline on out-of-sample accuracy and dated real-world events, and most of them failed. That’s not a disappointing result. The alternative — publishing the classic-STL flags because they looked sharpest — would have been a nicer-looking post describing a worse system, and nobody would have known until it was load-bearing.
What’s next
Three directions, in order of expected value:
- Model levels, not just shares. The seasonal signal that shares cancel out is alive in levels (strength 0.4–0.6). A level model with real seasonality, cross-checked against share-based flags, would separate “trade fell” from “trade reshuffled.”
- Calibrate CUSUM empirically. Set \(h\) per series from a false-alarm budget (via block bootstrap on pre-event residuals) rather than Gaussian theory, then re-score the event windows.
- Formal changepoint methods — PELT for retrospective segmentation, BOCPD for online detection — as the principled version of what CUSUM approximates, with the same honest evaluation harness ready to score them.
Data: IMF International Merchandise Trade Statistics, bulk flat file download. Events: Russian invasion of Ukraine (2022-02-24), US oil sanctions on Venezuela (2019-01-28), WHO COVID-19 pandemic declaration (2020-03-11).