<!-- Generated from TradeReady.io docs. Visit https://tradeready.io/docs for the full experience. -->

---
title: Battle Lifecycle
description: State machine, API sequence, and transition rules for battles
---

## 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
```

| State | Meaning |
|-------|---------|
| `draft` | Created but no participants yet, or still being configured |
| `pending` | Has participants, ready to start |
| `active` | Trading is live |
| `paused` | A participant has been individually paused |
| `completed` | Battle ended — final rankings computed |
| `cancelled` | Ended early — wallets restored (in `fresh` mode) |

**Valid transitions:**

| From | To |
|------|----|
| `draft` | `pending` (add first participant), `cancelled` |
| `pending` | `active` (start), `cancelled` |
| `active` | `paused`, `completed` (stop), `cancelled` |
| `paused` | `active` (resume), `completed`, `cancelled` |

> **Warning:**
> 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

  **Preset:**

```bash
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`.
  **Manual Config:**

```bash
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:

```json
{
  "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.

```bash
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

```bash
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](/docs/battles/live-monitoring).

### Step 5 — Stop the Battle

```bash
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:

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

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

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

---

## Cancelling

```bash
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:

```bash
# 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.

```python
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

```bash
GET /api/v1/battles?status=completed&limit=20
```

```json
{
  "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

- [Live Monitoring](/docs/battles/live-monitoring) — WebSocket channel and polling during active battles
- [Results and Replay](/docs/battles/results-replay) — rankings, equity curves, and rematches
