Moscow Transport Hackathon · 2026
See the delay
15 minutes
before it happens
The system listens to bus telematics over NDTP, matches every ping against the timetable and warns the dispatcher about a delay 10–15 minutes before the stop — with the cause and a recommendation for what to do.
Numbers come straight from api.mowtransit.ru/health
01 · Horizon
Every forecast looks at the (T+10, T+15] min window
Every 5 seconds, for every bus, we take the first scheduled stop that falls inside the window — and ML forecasts the delay at that stop. As time moves, the target rolls forward and the forecast updates. No after-the-fact forecasts: an alert is only raised while the stop is still 10–15 minutes away.
horizon_ok_share in /health02 · Incident card
Not just “it’ll be late”, but why and what to do
An alert is raised when the model puts the probability of being more than 2 minutes late at ≥ 0.7. The card shows the forecast with a range, the route segment, each cause’s contribution in seconds and a recommendation for the dispatcher. When the bus arrives, the alert is checked against the actual time.
- The forecast range is calibrated: the actual delay falls inside it 80% of the time
- Causes come from approximate SHAP, turned into plain language
- The recommendation works out the speed needed to get back on schedule
The card is rendered straight from the live system, which talks to Moscow dispatchers — so its text is in Russian.
03 · How it works
Stream → forecast → dashboard, end to end
Three independent services in Docker Compose. The backend decodes binary NDTP itself (no off-the-shelf parser); ML is a separate service that can be retrained and reloaded without stopping the backend.
NDTP terminals
The organisers’ emulator replaying real tracks. Binary TCP to ndtp.mowtransit.ru:9201
Backend · ingest
Our own NPL/NPH codec, CRC-16, handshake, G6CellNav00 cells. asyncio + uvloop
Backend · state
Timetable matching, GPS-detected arrivals, current deviation, segment speed, dwell time
ML · CatBoost ×5
An ensemble on 56 features + range and probability models, causes, what-if
Dashboard
MapLibre, risk traffic light, alert feed, what-if. WebSocket, updates every second
All history — telemetry, arrivals, every forecast and alert — is written to Postgres in the background
(write-behind, COPY), so the database can never slow down the live path.
04 · Accuracy
Off by 40 seconds where the baseline is off by 93
MAE of the delay forecast on a held-out set. The hackathon metric hits its ceiling: score = 1.0, both locally and on the platform.
6 of 6 points for accuracy
Shaped like validate
5 folds in 30-minute blocks: nearby moments of the same buses. Baseline: 88.4 s.
Honestly: an unseen bus
The whole bus is held out — the model has never seen it. Still beats the baseline (88.4 s).
AUC for “will it be late”
The late probability is calibrated: predicted ~0.89 → actually 0.95. It drives the risk traffic light.
Online = batch: the live service matches submission.csv on 151 of 151 points,
maximum difference 0.00 s. The same function computes the features in both.
05 · Reliability
Break something. The service won’t go down
Pick a failure and see how the system degrades and recovers. All of it was tested on live containers.
—
/health/predict/batch06 · Beyond the brief
Extra features
What-if
Release a reserve bus or a detour — forecasts for the route before and after, applied to the simulation.
ONNX
Model export: fp32 runs 6× faster than native CatBoost.
Ensemble
5 models on different seeds + separate quantile and early / on-time / late class models.
Conformal ranges
Not one number but a range with a guaranteed 80% coverage.
Route matching
GPS-detected arrivals: 23,664 arrivals match the training pipeline exactly.
Hot reload
POST /reload picks up a retrained model without restarting the container.
07 · Team
S
F
D
V
D