Methodology

One model per market (hybrid-1.1.0 · keystone-1.0.0 · empire-1.0.0 · minuteman-1.0.0), each calibrated on its market's real settled load. A call never publishes without its inputs. Here is every input and every coefficient.

What we predict

For each eligible day we estimate P(this day ends up a coincident peak): the month's single peak for a monthly program like ERCOT 4CP, a slot in the season's ranked peaks for the rest. A day can only count if its load clears the bar it is chasing, the month-to-date maximum or the standing season ladder. We publish two numbers everywhere: p_raw (grid forecast vs the bar, before any demand response) and p_final (net of our estimated demand response). The gap between them is what we expect the market to do to itself.

The market models

The ERCOT model

The model in math

A calibrated logistic regression over 6 features. z = intercept + Σ wᵢ·featureᵢ, P = 1/(1+e⁻ᶻ).

FeatureWeightMeaning
intercept-2.102917bias
margin_gw0.391734(effective_forecast_max_mw - month_max_mw) / 1000
load_gw-0.03674effective_forecast_max_mw / 1000 - load_center_gw
temp_anomaly_f0.280635heat above seasonal normal, degF (fit: ERA5 daily-max for the four load centers, 2022-2025, open-meteo archive; live: ERCOT zone vintages vs baseline)
dow_factor1.249877day-of-week load factor in [0,1]
days_remaining_norm-2.830513days_remaining_in_month / 30
survival_p2.67127(1 - q)^days_remaining, q = logistic(horizon params over margin_gw, load_gw); 0 when margin_gw <= 0

Mercury's features plus a fitted temperature anomaly and a structural survival term: the probability that no remaining day in the month beats today's expected max, from a 3-parameter exposure model. Fit on REAL ERCOT settled load, seasons 2022-2025. loso_folds hold per-season leave-one-out weights; the backtest grades each season with the fold that never saw it.

How it scored

Out-of-fold on real settled load, at the WARNING threshold: recall 93.8% (15 of 16 peaks) at 18.3% precision, with 134 false-alarm curtailment hours vs the blanket baseline's 178.

The baseline's recall of 100% is a ceiling it only reaches by flagging every candidate day (105 flagged days to our 82). Every call and every miss is on the ERCOT track record.

hybrid-1.0.0 succeeded mercury-1.0.0 (a five-feature calibrated logistic without the survival term) on July 5, 2026, before any month of the live season had closed. Mercury continues to run in shadow on every recompute, logged but never served, so the two models remain comparable on live data.

Program config

ERCOT 4CP · 2026

{
  "peaks": {
    "period": "month",
    "count": 1
  },
  "eligible_months": [
    6,
    7,
    8,
    9
  ],
  "interval_minutes": 15,
  "eligible_window_ct": [
    "13:00",
    "20:00"
  ],
  "levels": {
    "watch": 0.25,
    "warning": 0.5,
    "action_interval": 0.2
  },
  "timezone": "America/Chicago"
}
The PJM model

The model in math

A calibrated logistic regression over 4 features. z = intercept + Σ wᵢ·featureᵢ, P = 1/(1+e⁻ᶻ).

FeatureWeightMeaning
intercept-2.422519bias
margin_gw0.169736(forecast_max_mw - cp_k_bar_mw) / 1000
load_gw0.293142forecast_max_mw / 1000 - load_center_gw
days_remaining_norm-1.946431business_days_remaining / 30
survival_p1.007511P(Bin(n_remaining, q) <= k - entry_rank); q = logistic(horizon params); 0 when margin_gw <= 0

PJM 5CP season-top-k model, fit on REAL settled hourly RTO load and PJM's own ARCHIVED forecast vintages (load_frcstd_hist), seasons 2022-2025, business days only (NERC holidays out). Decision convention: the newest vintage evaluated by 10:00 ET supplies the day's forecast max. month_max_mw carries the season-to-date cp-k bar; bars_mw carries the ladder for entry-rank estimation; survival_p is the top-k binomial tail (the hybrid (1-q)^n is the k=1 case). No temperature feature in 1.0.0 (the forecast embeds weather); a fitted PJM temp anomaly is the 1.1.0 roadmap, mirroring hybrid's history. First fit-week days per season (fewer than k prior business days) are excluded. loso_folds grade each season with weights that never saw it.

How it scored

Out-of-fold on real settled load, at the WARNING threshold: recall 90.0% (18 of 20 peaks) at 50.0% precision, with 36 false-alarm curtailment hours vs the blanket baseline's 132.

The baseline's recall of 100% is a ceiling it only reaches by flagging every candidate day (86 flagged days to our 36). Every call and every miss is on the PJM track record.

The fit has no stand-in conventions: PJM archives its load forecast vintages, so all 320 training days use the newest forecast actually evaluated by 10:00 AM ET that morning. No temperature feature yet, since the forecast embeds weather. A fitted PJM temperature anomaly is the 1.1.0 roadmap, the same path hybrid took. PJM's official October determination uses unrestricted peaks, observed load plus estimated demand response added back. Our live standings read observed load and say so wherever they appear.

Program config

PJM 5CP · 2026

{
  "peaks": {
    "period": "season",
    "count": 5
  },
  "eligible_months": [
    6,
    7,
    8,
    9
  ],
  "interval_minutes": 60,
  "eligible_window_ct": [
    "12:00",
    "20:00"
  ],
  "levels": {
    "watch": 0.25,
    "warning": 0.5,
    "action_interval": 0.2
  },
  "timezone": "America/New_York"
}
The NYISO model

The model in math

A calibrated logistic regression over 4 features. z = intercept + Σ wᵢ·featureᵢ, P = 1/(1+e⁻ᶻ).

FeatureWeightMeaning
intercept-0.783323bias
margin_gw1.089542(forecast_max_mw - cp_k_bar_mw) / 1000
load_gw1.30181forecast_max_mw / 1000 - load_center_gw
days_remaining_norm-0.186612business_days_remaining / 30
survival_p-0.311782P(Bin(n_remaining, q) <= k - entry_rank); q = logistic(horizon params); 0 when margin_gw <= 0

NYCA season-top-1 model, fit on REAL settled hourly load and NYISO's archived daily isolf files (one true vintage per day), seasons 2022-2025, business days only (NERC holidays out). Decision convention: the newest vintage evaluated by 10:00 ET supplies the day's forecast max. month_max_mw carries the season-to-date cp-k bar; bars_mw carries the ladder for entry-rank estimation; survival_p is the top-k binomial tail (the hybrid (1-q)^n is the k=1 case). No temperature feature in 1.0.0 (the forecast embeds weather); a fitted temp anomaly is the 1.1.0 roadmap, mirroring hybrid's history. First fit-week days per season (fewer than k prior business days) are excluded. loso_folds grade each season with weights that never saw it.

How it scored

Out-of-fold on real settled load, at the WARNING threshold: recall 75.0% (3 of 4 peaks) (95% range 30%95%) at 13.6% precision, with 38 false-alarm curtailment hours vs the blanket baseline's 46.

The baseline's recall of 100% is a ceiling it only reaches by flagging every candidate day (25 flagged days to our 22). Every call and every miss is on the NYISO track record.

Program config

NYISO ICAP · 2026

{
  "peaks": {
    "period": "season",
    "count": 1
  },
  "eligible_months": [
    6,
    7,
    8,
    9
  ],
  "interval_minutes": 60,
  "eligible_window_ct": [
    "12:00",
    "20:00"
  ],
  "levels": {
    "watch": 0.25,
    "warning": 0.5,
    "action_interval": 0.2
  },
  "timezone": "America/New_York"
}
The ISO-NE model

The model in math

A calibrated logistic regression over 4 features. z = intercept + Σ wᵢ·featureᵢ, P = 1/(1+e⁻ᶻ).

FeatureWeightMeaning
intercept-3.805343bias
margin_gw0.887247(forecast_max_mw - cp_k_bar_mw) / 1000
load_gw1.673357forecast_max_mw / 1000 - load_center_gw
days_remaining_norm-1.402993business_days_remaining / 30
survival_p1.90801P(Bin(n_remaining, q) <= k - entry_rank); q = logistic(horizon params); 0 when margin_gw <= 0

ISO-NE season-top-1 model, fit on REAL settled hourly load and the settled-actual decision convention (hybrid's; no archived vintages exist keyless), seasons 2023-2025 (first season partial: keyless window opens mid-July), business days only (NERC holidays out). Decision convention: the day's own settled daily max stands in for the decision forecast (the hybrid backtest convention, disclosed on /methodology; the live engine uses the real morning forecast). month_max_mw carries the season-to-date cp-k bar; bars_mw carries the ladder for entry-rank estimation; survival_p is the top-k binomial tail (the hybrid (1-q)^n is the k=1 case). No temperature feature in 1.0.0 (the forecast embeds weather); a fitted temp anomaly is the 1.1.0 roadmap, mirroring hybrid's history. First fit-week days per season (fewer than k prior business days) are excluded. loso_folds grade each season with weights that never saw it.

How it scored

Out-of-fold on real settled load, at the WARNING threshold: recall 100.0% (3 of 3 peaks) (95% range 44%100%) at 50.0% precision, with 6 false-alarm curtailment hours vs the blanket baseline's 30.

The baseline's recall of 100% is a ceiling it only reaches by flagging every candidate day (18 flagged days to our 6). Every call and every miss is on the ISO-NE track record.

Program config

ISO-NE capacity tag · 2026

{
  "peaks": {
    "period": "season",
    "count": 1
  },
  "eligible_months": [
    6,
    7,
    8,
    9
  ],
  "interval_minutes": 60,
  "eligible_window_ct": [
    "12:00",
    "20:00"
  ],
  "levels": {
    "watch": 0.25,
    "warning": 0.5,
    "action_interval": 0.2
  },
  "timezone": "America/New_York"
}

Validation

Every backtest number above is out-of-fold. Each model artifact carries leave-one-season-out weights (loso_folds): a season is graded by weights fit only on the other seasons in that model's corpus — four summers for ERCOT, PJM, and NYISO; three for ISO-NE, whose public feed opens mid-2023. The production weights are fit on all settled summers, but they never grade themselves. The published record comes exclusively from folds that had not seen the season being graded. The live season is the standing test in every market: calls are hash-chained the moment they publish and graded when the data settles.

The survival math generalizes across market shapes: entering PJM's ladder at rank r leaves 5−r survivable future exceedances, a binomial tail in which the 4CP formula is exactly the one-slot case. Both the TypeScript and Python implementations compute it, and shared test vectors hold them identical in CI.

Replays

Each replay page carries a fidelity label. "As it happened" days replay the predictions the engine actually published, tick by tick, next to the real forecast vintage that was newest at each moment. Nothing is recomputed, so a replay can never contradict what we served.

"Reconstructed" days predate the live engine. ERCOT does not archive its old load forecasts, so those days walk the settled hourly load forward under the backtest's own conventions (the highest load seen so far stands in for the forecast), while PJM's archived vintages let its reconstructions use the real forecasts. Either way the walk ends exactly on the graded call in the record, and the verdict shown is the record's, never the replay's.

Integrity

Every live call in every market is written to an append-only, SHA-256 hash-chained log before its interval closes. Nightly we publish the terminal hash at /v1/scorecard/attestations, so anyone can verify we never edited history.

WattMarkets is an information service, not a licensed advisor, and makes no guarantee of accuracy. Curtailment calls and their outcomes are yours.