Climate Commodity Risk — Interactive Dashboard

Drive the scenario engine, the alarm threshold and the procurement backtest from cotton and rubber’s real model artifacts, entirely in the browser.
Published

July 26, 2026

Five panels over ten years of cotton and natural rubber prices, the climate that moved them, and a spike alarm you can push until it breaks. Every one runs on the same artifacts as the write-up — no server, no database, no data API. The whole payload is 400 KB of static CSV, loaded once and recomputed in your browser as you move the controls.

Each panel carries a collapsed “how to read this” note. Open them: several of these charts have a caveat that changes what the numbers mean.

The short version. Cotton and natural rubber both respond to climate, in opposite directions — a strong El Niño lifts rubber’s 12-month median price +36.9% while pushing cotton’s −10.9% — so they get separate models throughout. Climate leads price by about two quarters.

A classifier then asks a narrower question each month: what is the probability this price rises more than 10% within the next h months? It has real skill for rubber (AUC 0.775 at nine months) and little for cotton beyond three, which traces to cotton’s climate coverage being Xinjiang only, ~20% of world production.

The panel worth your time is the alarm explorer. The operating rule fires at k × the historical base rate, and that rule breaks in a way that is easy to miss and easy to reproduce here: where the base rate is already high, the threshold outruns anything a calibrated model can emit, and a skillful model goes silent. The full write-up has the method, the limitations and the numbers.

Models are fitted through 2025-12, the last month of the joined panel, so every forecast starts there. Observed prices run to 2026-06 because the World Bank Pink Sheet publishes further than the slowest input — so the first six months of every forecast band already have actuals drawn over them. That is a free out-of-sample look, not fitted values, and the charts distinguish the two.

Prices are World Bank Pink Sheet levels in US\(/kg** (Brent in US\)/bbl), CC-BY 4.0. Disaster counts are EM-DAT monthly aggregates** — national counts by type, never event records. Alarm probabilities are walk-forward out-of-sample from 2016-01.

Observed prices

The dashed rule marks the forecast origin, read from meta.json rather than hardcoded. Observed price continues past it: the models anchor at while the price series runs to .

Two series, one axis, both in US$/kg — which is only fair because cotton and rubber happen to trade in the same units and the same order of magnitude. Brent is loaded but not drawn here; it enters the models as a driver, since oil price raises the cost of synthetic rubber and pulls natural rubber with it.

The interesting feature is how differently the two series move. Rubber’s 2010–11 spike and cotton’s are close in time but not in cause, and the scenario explorer below shows why: the same climate shock pushes them in opposite directions.

Scenario explorer

The forecast origin is — the last month in the modelling panel. The solid line is observed price up to that point; the dotted line past the rule is real price the model never saw, because the Pink Sheet runs six months further than the panel. Treat that overlap as a free out-of-sample look, not as fitted values.

Percentiles come from 10,000 bootstrapped-residual paths per cell. Severity scales every driver shock in the scenario, so severity 0 collapses all four scenarios onto the same baseline.

On the probability tile: it reads from this dial’s own simulation, which re-seeds per cell so the severity slider moves smoothly rather than jittering. That is a different random stream from the pipeline run the write-up quotes, so the two are not bit-identical — measured across all 24 scenario × horizon cells, they agree to within 0.9 percentage points (mean 0.3). Medians and planning percentiles agree to within half a point.

Alarm explorer

The alarm fires when the predicted probability clears k × the historical base rate — a relative rule, not a fixed cutoff, because the models are calibrated and rarely emit high absolute probabilities. Move k and watch what it costs.

This panel is where the study’s most transferable finding lives, and you can drive it into failure yourself.

Select rubber, XGBoost, h=9 — the study’s most skillful cell — and push k to 1.50. The alarm count drops to zero, and it stays there. The base rate is 56%, so the bar sits at 0.84, while the highest probability this model ever emitted across ten years is 0.821. Past k ≈ 1.47 the alarm is not merely conservative, it is arithmetically incapable of firing. No amount of signal helps.

Now select cotton, XGBoost, h=3, base rate 19%. The same slider keeps producing alarms until k ≈ 1.87. The relative rule is fine where the event is genuinely rare and degrades exactly where it becomes common — which is where a buyer most needs the warning.

That is the mechanism that silenced the alarm through the 2010–11 spike: a 69% base rate put the bar at 0.858, the model peaked at 0.820, and it missed by 0.038 on an episode it had ranked at AUC 0.786. The fix is the rule, not the model.

What the alarm would have been worth

A crude procurement rule: buy one month of material every month; when the alarm fires, forward-buy the whole horizon at today’s price instead, then skip ahead. Savings are the fraction of spend avoided against buying monthly throughout.

These are directional figures, not a business case. The rule assumes a forward purchase costs the same per unit as a spot purchase: no inventory carrying cost, no storage, no liquidity limit, no counterparty constraint. Real procurement has all four, and carrying cost in particular scales with exactly the thing this rule does more of.

What the number does answer honestly is did the alarm point the right way — and it separates the two commodities the same way every other panel does. Rubber clears 2% of spend at the default operating point; cotton is under half a percent, which is noise against the assumptions above.

“Perfect detection” is a benchmark, not a ceiling, and the difference is instructive. It buys forward on every month a spike actually followed — a flawless detector. But this rule buys at the alarm month’s own price, and the month a spike begins is not necessarily a cheap month to buy in. A detector with worse recall that happens to fire at local price minima can beat it, and here one does: rubber XGBoost at h=9 clears 9.4% at k=1.20 against perfect detection’s 7.5%. That is not the model outperforming omniscience; it is the metric rewarding when you buy as much as whether you saw the spike coming — one more reason to read the whole panel as directional. (Verified against the Python implementation, which reproduces the same crossover: the port is faithful.)

Skill against the null

Rows are grouped by commodity and horizon, and ranked by AUC inside each group, with the climatology null marked by a leading dot. That ordering is doing work: an AUC means nothing on its own — the null runs 0.47–0.55 throughout — so any model sorted below its own climatology row failed to beat chance. Cotton logistic at h=9 is exactly that case, at 0.496 against a null of 0.553. AUC − null is the column that says whether a model learned anything, and Brier skill is scored against the same null, so a negative value means you would have done better quoting the base rate. Both cotton logistic cells at h=6 (−0.039) and h=9 (−0.100) are negative.

Base rate climbs with the horizon — cotton 19% → 41%, rubber 28% → 56% — which is why rubber’s best AUC (0.775 at h=9) is worth less than it looks: a +10% move within nine months is close to rubber’s normal state. That is the same observation the alarm explorer turns into a slider.

Two things about the last two columns, because they will otherwise mislead:

  • They are computed at a fixed 0.50 probability cutoff, not at the relative k × base rate rule the alarm explorer uses. The two panels answer different questions and their hit rates are not comparable.
  • Blank means undefined, not zero. In five cells the model never once crossed 0.50, so the false-alarm rate is 0 ÷ 0. Cotton’s h=3 XGBoost is the clearest case: its highest probability across ten years is 0.352, so at a 0.50 cutoff it is silent — which is precisely why the operating rule is relative.

Disasters and price

This panel is a faithful port of the team’s Streamlit page, including one behaviour worth stating plainly: the three averages are computed only over the months from the start date to start date + impact window — four months at the defaults, not the whole series. That is why the “after a quiet month” tile often reads n/a: in a 12-country scope, every month in the window had at least one recorded disaster.

Widen the picture by moving the start date, or read the full-series comparison instead: over 1995–2026 with all rubber-sourcing countries, price is +2.6% on average three months after a disaster month against −3.9% after a quiet one. The bubbles show every event regardless of the window.

Disaster counts are EM-DAT aggregates — monthly national counts by type, never event records. Bubble area scales with the count, capped so a bad month doesn’t swallow the chart.


That is the whole dashboard. The reasoning behind it — why the two commodities need separate models, what the Granger tests do and don’t establish, why the 2010–11 replay is the most useful failure in the project, and where the analysis breaks down — is in the full write-up, along with the source and reproduction steps.