Tempo

Click anywhere to begin

About

A battery trader that shows its working.

Great Britain prices electricity in 48 half-hour blocks a day. Prices crash overnight when the wind blows and spike at 6pm when everyone gets home. A grid battery profits from that gap, but only if it buys and sells at the right moments.

Tempo makes that call every half hour, explains each one, and lets anyone check the timing afterwards.

48 prices a day

Every half-hour settles at its own price, sometimes below zero.

2 MWh · 1 MW · 90%

Real limits: capacity, power, and about 10% lost on every round trip.

Hold is a decision

When the spread doesn't cover the losses, Tempo waits and says why.

Built to be checked

No look-ahead, honest baselines, and every decision can be replayed.

one day · 48 half-hourly priceschargesellspread00:0006:0012:0018:0024:00profit = spread − round-trip loss − wear · illustrative day

Three jobs, every half hour

  • Forecast

    Predict the next 24 hours of prices as a range, not a single guess.

  • Decide

    Charge, sell or hold, only when the spread beats the losses. Every choice comes with a reason.

  • Prove

    Replay any decision earlier and later to show, in pounds, whether the timing was right.

66%

of perfect-foresight profit

2×

the fixed-hours baseline

100

unseen test weeks

30 min

between live decisions

How it works

Grid data in, a proven decision out, every 30 minutes.

01

Elexon

6 datasets, polled

02

Tiger Data

hypertables

03

Features

point-in-time

04

LightGBM

p10 · p50 · p90

05

Optimiser

charge · sell · hold

06

Audit

Tiger fork + Vultr

01

Live ingest: Elexon → Tiger Data

  • Six Elexon datasets: prices, wind and demand forecasts, generation mix
  • A metadata-driven poller fetches only datasets that changed
  • Every row stamped with available_at: when Tempo could first have seen it
  • Stored in Tiger Data hypertables, rolled up by continuous aggregates
Elexon Insights · poll every 60 sMIDWINDFORNDFINDOFUELHHDISEBSPTigerDatagrid_rawstart_time · value+ available_atsame normalise + upsert as the 2-year backfill · idempotent

02

Point-in-time features, zero leakage

  • Features use only rows with available_at ≤ decision_time
  • Price lags, rolling spreads, wind and demand forecasts, UK calendar
  • Each filter is tagged -- pit; a test removes each one and must fail
available_atdecision_timeusablethe future: blockedWHERE available_at <= decision_time -- pittest_pit_leak.py deletes each filter in turn: the suite must fail

03

Quantile forecast: p10 · p50 · p90

  • Three LightGBM quantile models predict the next 48 half-hourly prices
  • The band shows uncertainty, not just a best guess
  • Deterministic training: same rows, same model
  • Re-run every 30 min on a Vultr box in London
LightGBM × 3 · objective=quantile£/MWhp90p50p10now+6 h+12 h+18 h+24 h48 half-hours ahead · band widens with horizon · re-run every 30 min

04

Cost-gated battery optimiser

  • Greedy pairing: cheapest buy period with the dearest later sell
  • LP alternative (cvxpy) over all 48 periods with full battery physics
  • Trades only when the spread beats round-trip loss plus wear
  • Hold is a decision too: every row logs its reason
trade only if eff·sell − buy > eff·wearchargedischargehold2 MWh · 1 MW · 90% round trip · £20 per cycle wear

05

Timing audit: one fork, five replays

  • Click any decision; Tempo replays it 1 h early to 1 h late
  • One Tiger zero-copy fork, five timelines on five Vultr workers in parallel
  • Shows in pounds what rushing or hesitating would have cost
  • The fork is deleted afterwards; only the results are kept
1 Tiger fork → 5 Vultr workers → replay at actual pricesP&L−1 h−30 mon time+30 m+1 hΔ£ vs on time = the cost of rushing or hesitating

06

Walk-forward backtest: 66% of perfect

  • Retrained every week, tested only on weeks it never saw
  • £58,711 earned vs £29,265 for a fixed-hours rule: 2× the baseline
  • Scored against perfect foresight on the same weeks
% of perfect foresight · 100 unseen weeksPerfect foresight100%Tempo LP68.3%Tempo (live)66.4%Persistence52.9%Simple rule33.1%28-day train window, retrained each test week24 Oct 2024 – 24 Oct 2026