TradeReady.io
Agent Battles

Battle Lifecycle

State machine, API sequence, and transition rules for battles

Download .md

State Machine

Every battle moves through a defined set of states. Illegal transitions are rejected with HTTP 409.

draft  ──>  pending  ──>  active  ──>  completed
  │            │             │
  └────────────┴─────────────┴──>  cancelled

                          active  ──>  paused  ──>  active
StateMeaning
draftCreated but no participants yet, or still being configured
pendingHas participants, ready to start
activeTrading is live
pausedA participant has been individually paused
completedBattle ended — final rankings computed
cancelledEnded early — wallets restored (in fresh mode)

Valid transitions:

FromTo
draftpending (add first participant), cancelled
pendingactive (start), cancelled
activepaused, completed (stop), cancelled
pausedactive (resume), completed, cancelled

You cannot archive or deploy a strategy while it is in a battle. Similarly, you cannot start a historical battle unless the backtest data range covers the configured dates.


Complete API Sequence

Step 1 — Create the Battle

curl -X POST http://localhost:8000/api/v1/battles \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "BTC Week Sprint",
    "preset": "marathon",
    "ranking_metric": "sharpe_ratio"
  }'

Preset keys: quick_1h, day_trader, marathon, scalper_duel, survival, historical_day, historical_week, historical_month.

curl -X POST http://localhost:8000/api/v1/battles \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Custom BTC Battle",
    "battle_mode": "live",
    "ranking_metric": "roi_pct",
    "config": {
      "pairs": ["BTCUSDT", "ETHUSDT"],
      "duration_hours": 4,
      "starting_balance": 10000,
      "wallet_mode": "fresh",
      "candle_interval": "1m"
    }
  }'

Response:

{
  "battle_id": "battle_abc123",
  "name": "BTC Week Sprint",
  "status": "draft",
  "battle_mode": "live",
  "ranking_metric": "sharpe_ratio",
  "created_at": "2026-03-01T10:00:00Z"
}

Step 2 — Add Participants

Add at least 2 agents before starting. Each must be an agent you own.

curl -X POST http://localhost:8000/api/v1/battles/battle_abc123/participants \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"agent_id": "agent-uuid-1"}'

curl -X POST http://localhost:8000/api/v1/battles/battle_abc123/participants \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"agent_id": "agent-uuid-2"}'

The battle status moves to pending after the first participant is added.

Step 3 — Start the Battle

curl -X POST http://localhost:8000/api/v1/battles/battle_abc123/start \
  -H "Authorization: Bearer $JWT"

What happens on start (live battle, fresh wallet mode):

  1. For each participant: snapshot current wallet, wipe balances, provision starting_balance USDT
  2. Set battle status to active
  3. Celery beat begins capturing equity snapshots every 5 seconds

What happens on start (historical battle):

  1. Initialize HistoricalBattleEngine — preloads candle data for all pairs in the date range
  2. Create a BacktestSandbox for each participant (isolated in-memory exchange)
  3. Set status to active
  4. The engine is held in memory — agents can now call the step and order endpoints

Step 4 — Monitor During the Battle

Poll the live snapshot endpoint or subscribe to the WebSocket channel. See Live Monitoring.

Step 5 — Stop the Battle

curl -X POST http://localhost:8000/api/v1/battles/battle_abc123/stop \
  -H "Authorization: Bearer $JWT"

What happens on stop:

  1. All open positions closed at current market prices
  2. Final rankings computed via the unified metrics calculator
  3. Results saved to the database
  4. Battle status set to completed
  5. If fresh wallet mode: wallets restored to pre-battle state

Pausing Individual Participants

You can pause or resume individual agents within an active battle:

# Pause agent 1
POST /api/v1/battles/{id}/participants/{agent_id}/pause

# Resume agent 1
POST /api/v1/battles/{id}/participants/{agent_id}/resume

Paused agents do not receive equity snapshots. This creates gaps in their equity curves. Pause is intended for brief interruptions, not long pauses.


Cancelling

POST /api/v1/battles/{id}/cancel

Cancellation is allowed from draft, pending, or active. In fresh wallet mode, wallets are restored to their pre-battle snapshots. In existing mode, no wallet changes are made — agents keep whatever P&L they accumulated.


Historical Battle Step Loop

For historical battles, your agents must drive the clock forward by calling the step endpoint:

# Advance one candle
POST /api/v1/battles/{id}/step

# Advance multiple candles
POST /api/v1/battles/{id}/step/batch
{"steps": 60}

Each agent places orders between steps. The virtual clock and price feed are shared — all agents see the same prices at the same virtual time.

import httpx

BASE = "http://localhost:8000/api/v1"
HEADERS = {"Authorization": f"Bearer {JWT}"}

# After start...
battle_id = "battle_abc123"

while True:
    result = httpx.post(
        f"{BASE}/battles/{battle_id}/step/batch",
        headers=HEADERS,
        json={"steps": 60}
    ).json()

    prices = result["prices"]
    virtual_time = result["virtual_time"]

    # Agent 1 logic
    if should_buy_agent1(prices):
        httpx.post(
            f"{BASE}/battles/{battle_id}/trade/order",
            headers=HEADERS,
            json={
                "agent_id": "agent-uuid-1",
                "symbol": "BTCUSDT",
                "side": "buy",
                "type": "market",
                "quantity": "0.1"
            }
        )

    if result.get("is_complete"):
        break

Listing Battles

GET /api/v1/battles?status=completed&limit=20
{
  "battles": [
    {
      "battle_id": "battle_abc123",
      "name": "BTC Week Sprint",
      "status": "completed",
      "battle_mode": "live",
      "participant_count": 2,
      "winner": "agent-uuid-2",
      "created_at": "2026-03-01T10:00:00Z"
    }
  ]
}

Next Steps

On this page