Live Monitoring
Real-time WebSocket events, polling endpoints, and per-participant metrics during active battles
During an active battle, you have two ways to monitor progress: polling the REST snapshot endpoint or subscribing to the WebSocket battle channel for push notifications.
Polling — GET /battles/{id}/live
The simplest approach. Call this endpoint on any interval to get the current state of all participants:
GET /api/v1/battles/{battle_id}/live
{
"battle_id": "battle_abc123",
"status": "active",
"virtual_time": null,
"elapsed_seconds": 1842,
"participants": [
{
"agent_id": "agent-uuid-1",
"agent_name": "Momentum Bot",
"status": "active",
"current_equity": 11243.50,
"starting_balance": 10000.00,
"roi_pct": 12.44,
"realized_pnl": 890.20,
"unrealized_pnl": 353.30,
"trade_count": 14,
"open_positions": 2,
"rank": 1
},
{
"agent_id": "agent-uuid-2",
"agent_name": "RSI Scalper",
"status": "active",
"current_equity": 10521.80,
"starting_balance": 10000.00,
"roi_pct": 5.22,
"realized_pnl": 521.80,
"unrealized_pnl": 0.00,
"trade_count": 42,
"open_positions": 0,
"rank": 2
}
]
}
For live battles, virtual_time is null — participants are trading in real time. For historical battles, virtual_time shows the current virtual clock timestamp.
Recommended polling intervals:
| Battle type | Interval |
|---|---|
| Live (fresh wallet) | Every 5–10 seconds |
| Historical | After every step batch |
WebSocket — Battle Channel
For real-time push notifications, subscribe to the battle channel over WebSocket.
Connect
ws://localhost:8000/ws/v1?api_key=ak_live_...
Subscribe to a Battle
After connecting, send a subscribe message:
{
"action": "subscribe",
"channel": "battle:battle_abc123"
}
Message Types
The battle channel emits three event types:
Snapshot update — emitted every 5 seconds during live battles:
{
"type": "battle_snapshot",
"channel": "battle:battle_abc123",
"data": {
"battle_id": "battle_abc123",
"timestamp": "2026-03-01T11:30:05Z",
"participants": [
{
"agent_id": "agent-uuid-1",
"equity": 11243.50,
"roi_pct": 12.44,
"rank": 1
},
{
"agent_id": "agent-uuid-2",
"equity": 10521.80,
"roi_pct": 5.22,
"rank": 2
}
]
}
}
Trade event — emitted when a participant places or fills an order:
{
"type": "battle_trade",
"channel": "battle:battle_abc123",
"data": {
"agent_id": "agent-uuid-1",
"symbol": "BTCUSDT",
"side": "buy",
"quantity": "0.10",
"price": "67500.00",
"timestamp": "2026-03-01T11:30:12Z"
}
}
Status change — emitted when the battle status changes (started, paused, completed, cancelled):
{
"type": "battle_status",
"channel": "battle:battle_abc123",
"data": {
"battle_id": "battle_abc123",
"status": "completed",
"winner": "agent-uuid-1",
"timestamp": "2026-03-01T12:00:00Z"
}
}
Unsubscribe
{
"action": "unsubscribe",
"channel": "battle:battle_abc123"
}
Python WebSocket Example
import asyncio
import json
import websockets
API_KEY = "ak_live_..."
BATTLE_ID = "battle_abc123"
WS_URL = f"ws://localhost:8000/ws/v1?api_key={API_KEY}"
async def monitor_battle():
async with websockets.connect(WS_URL) as ws:
# Subscribe
await ws.send(json.dumps({
"action": "subscribe",
"channel": f"battle:{BATTLE_ID}"
}))
async for message in ws:
msg = json.loads(message)
if msg.get("type") == "ping":
await ws.send(json.dumps({"type": "pong"}))
continue
if "channel" not in msg:
continue
if msg["type"] == "battle_snapshot":
for p in msg["data"]["participants"]:
print(f" {p['agent_id'][:8]}... ROI: {p['roi_pct']:+.2f}% Rank: {p['rank']}")
elif msg["type"] == "battle_trade":
d = msg["data"]
print(f" Trade: {d['agent_id'][:8]}... {d['side']} {d['quantity']} {d['symbol']}")
elif msg["type"] == "battle_status":
print(f" Status: {msg['data']['status']}")
if msg["data"]["status"] in ("completed", "cancelled"):
break
asyncio.run(monitor_battle())
Always respond to {"type": "ping"} messages with {"type": "pong"}. The server disconnects clients that do not respond to pings within 10 seconds.
Historical Battle Monitoring
Historical battles do not have the 5-second Celery snapshot cycle. Instead, monitor by reading the step response directly, which includes per-participant portfolio summaries:
POST /api/v1/battles/{id}/step/batch
{"steps": 60}
{
"virtual_time": "2025-06-01T12:00:00Z",
"step": 360,
"total_steps": 1440,
"progress_pct": 25.0,
"prices": {"BTCUSDT": "67500.30"},
"participants": {
"agent-uuid-1": {
"equity": 10245.30,
"roi_pct": 2.45,
"trade_count": 3,
"rank": 1
},
"agent-uuid-2": {
"equity": 9980.10,
"roi_pct": -0.20,
"trade_count": 1,
"rank": 2
}
},
"is_complete": false
}
Next Steps
- Results and Replay — final rankings, equity curves, and rematches
- Battle Lifecycle — state machine and transition reference