AEMO SDP-Teacher Decision Transformer (planner-distilled)
The shipped / preferred model of the energydecision benchmark. A standalone Decision Transformer distilled from an honest (non-clairvoyant) SDP-planning teacher β no solver at inference β that beats the PPO reference on all four identity surfaces and passes the market-impact gate, with bootstrap 95% CIs excluding zero on every comparison.
Files in this repo
| File | Model | Status |
|---|---|---|
aemo_dt_sdp_jtsoc_fullcorpus.pt |
Stage C β J_t(soc) RTG, combined corpus (640 eps) | β Shipped β use this one |
aemo_dt_fcas_model.pt |
Stage B β conservative teacher (320 eps), discounted RTG | Historical (superseded; kept for the ablation) |
aemo_dt_fcas_best_checkpoint.pt |
Stage B β best checkpoint of the same model | Historical |
Shipped model results (aemo_dt_sdp_jtsoc_fullcorpus.pt, rtg_mode="auto")
| Surface | This model | PPO | Ratio |
|---|---|---|---|
| Standard Oct | $11,573/ep | $2,353 | 4.9Γ |
| Dispatch-matched | $35,320/ep | $22,530 | 1.57Γ |
| Expanded broad-2024 | $34,761/ep | $19,504 | 1.78Γ |
| 2025 OOD | $25,862/ep | $6,498 | 3.98Γ |
| Impact gate (merit-order) | +$63,064/cell vs PPO, 95% CI [+$33k, +$101k], win 9/9 | β | 2.5β3.1Γ |
All six paired-difference bootstrap 95% CIs exclude zero; expanded broad-2024 additionally significant under paired Wilcoxon (p = 0.0002, n = 27). Methodology and full experiment ladder: report.md Β§8.2.10.
What is new vs the Stage B files in this repo
- State-dependent RTG prompting (J_t(soc)). Stage B used a hand-tuned constant return-to-go prompt; Stage C prompts with the teacher's cost-to-go table (
-J_t(s_t)β "how much value remains from this battery level"), recovering the energy-arbitrage gap on the broad surface ($12.0k β $34.8k on expanded broad-2024). - Combined conservative + aggressive teachers. Trained on the concatenated corpus (640 eps; deg=$50/MWh + deg=$20/MWh) instead of conservative-only, diversifying energy-arbitrage behaviour.
- Wins all four identity surfaces (Stage B lost expanded broad-2024 to PPO), and ships with surface-aware
rtg_mode="auto":j_t_socprompting on price-taking surfaces, conservative constant-RTG fallback under market impact. Do not forcej_t_socunder merit-order impact β it self-suppresses on 194β250 MWh batteries.
Architecture (both models share the modern v2 backbone)
| Parameter | Value |
|---|---|
| Blocks / hidden dim | 8 / 768 |
| Attention | GQA (12 Q / 6 KV), QK-Norm |
| FFN | SwiGLU, dropout 0.15 |
| Weight tying | Enabled |
| State / action dim | 18 / 9 (full_fcas) |
| Action head | Mixed (Tanh energy, Sigmoid FCAS) |
Action space: dim 0 energy dispatch in [-1, 1]; dims 1β8 FCAS bids in [0, 1] (RAISEREG, LOWERREG, RAISE6SEC, LOWER6SEC, RAISE60SEC, LOWER60SEC, RAISE5MIN, LOWER5MIN). State space: 18-dim normalized market observations (time features, RRP, demand, 8 FCAS prices, generation mix, SOC).
Training Data
- Stage C (shipped):
dt_trajectories_jtsoc_combined.parquetβ 640 episodes, 6.2M rows, conservative + aggressive SDP teachers concatenated, per-episode J_t(soc) cost-to-go RTG. - Stage B (historical):
dt_trajectories_full.parquetβ 320 episodes, conservative teacher only. - Both generated by
scripts/generate_sdp_dt_trajectories.pyin the source repo; method in report.md Β§4.3/Β§5.4.
Usage
Load with the DecisionTransformer class from the energydecision repo and run through AEMOAgent with rtg_mode="auto" (recommended):
import torch
from huggingface_hub import hf_hub_download
from decision_transformer import DecisionTransformer
from decision import AEMOAgent
import json
repo = "mrvictoru/energydecision-dt-v2-sdp"
ckpt = hf_hub_download(repo, "aemo_dt_sdp_jtsoc_fullcorpus.pt")
meta = json.loads(hf_hub_download(repo, "aemo_dt_sdp_jtsoc_fullcorpus.pt.meta.json"))
model_kwargs = dict(
state_dim=meta["model"]["state_dim"], act_dim=meta["model"]["act_dim"],
h_dim=meta["model"]["h_dim"], context_len=meta["model"]["context_len"],
n_block=8, n_heads=12, n_kv_heads=6, drop_p=0.15,
max_timestep=2016, rope_enabled=True, qk_norm=True, tie_weights=True,
action_head_mode="mixed",
)
model = DecisionTransformer(**model_kwargs)
model.load_from_checkpoint(ckpt) # raw state_dict
model.return_scale = meta["return_scale"] # J_t(soc) prompt scaling (~25,988)
model.eval()
agent = AEMOAgent(env, algorithm="dt", model=model, rtg_mode="auto")
Intended Use
Research into offline RL / sequence modeling for energy markets; simulation of multi-market BESS dispatch in Australia's NEM; baseline for planner-distilled offline RL. Not intended for live trading without further validation (sim-to-real is open), risk management, and regulatory compliance.
Citation
@misc{energydecision-dt-v2-sdp,
title = {AEMO SDP-Teacher Decision Transformer (planner-distilled)},
author = {mrvictoru},
year = {2026},
url = {https://huggingface.co/mrvictoru/energydecision-dt-v2-sdp}
}
Related
- Dataset:
mrvictoru/AEMO_simulated_trade - Modern v2 (behaviour-cloning era, superseded): energydecision-dt-v2
- Impact-aware variant: energydecision-dt-v2-impact
- Source repo: mrvictoru/energydecision