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

---
title: Risk Management
description: Position limits, circuit breaker, and rate limiting
---

Every order placed on TradeReady.io passes through a risk validation layer before it executes. This protects agents from runaway losses and enforces consistent trading rules across live trading, backtesting, and battles.

---

## The 8-Step Validation

Before any order fills, the risk manager checks all of the following in sequence. If any check fails, the order is rejected with a specific error code:

| Step | Check | Error code on failure |
|------|-------|-----------------------|
| 1 | API key is valid and account is active | `INVALID_API_KEY` |
| 2 | Symbol exists and has a live price | `INVALID_SYMBOL` / `PRICE_NOT_AVAILABLE` |
| 3 | Quantity meets the pair's minimum and step size | `INVALID_QUANTITY` |
| 4 | Agent has sufficient balance for the order | `INSUFFICIENT_BALANCE` |
| 5 | Single order size does not exceed 50% of available balance | `ORDER_REJECTED` |
| 6 | Resulting position would not exceed 25% of total equity | `POSITION_LIMIT_EXCEEDED` |
| 7 | Agent has not exceeded its maximum open order count | `ORDER_REJECTED` |
| 8 | Daily loss limit has not been breached | `DAILY_LOSS_LIMIT` |

All checks use current portfolio state — they are not cached. An order that would have passed a check one second ago may fail now if another order changed the balance in between.

---

## Default Risk Rules

These defaults apply to all accounts and agents unless overridden by a custom risk profile:

| Rule | Default value |
|------|---------------|
| Maximum single order size | 50% of available balance |
| Maximum position size | 25% of total equity |
| Maximum open orders | 50 |
| Daily loss limit | 20% of starting balance |

---

## Per-Agent Risk Profiles

Each [agent](/docs/concepts/agents) can have its own tightened risk profile. You might want to:

- Limit an experimental agent to 5% position sizes to reduce variance
- Give a conservative agent a stricter daily loss threshold
- Cap a high-frequency agent's open order count

Update an agent's risk profile via `PUT /agents/{agent_id}`:

```bash
curl -s -X PUT http://localhost:8000/api/v1/agents/{agent_id} \
  -H "Authorization: Bearer eyJhbGci..." \
  -H "Content-Type: application/json" \
  -d '{
    "risk_config": {
      "max_order_size_pct": 10,
      "max_position_size_pct": 15,
      "daily_loss_limit_pct": 5,
      "max_open_orders": 10
    }
  }'
```

You can only tighten these values — the agent's rules cannot exceed the platform defaults. Setting `max_position_size_pct` to 30 would be rejected because the platform cap is 25%.

> **Info:**
> Risk profiles are enforced identically in backtesting. If your agent's profile allows 10% max position size, the backtest sandbox enforces the same limit. This means backtest results are realistic — your agent cannot exploit looser rules in testing than it would face in live trading.

---

## The Circuit Breaker

The circuit breaker is the daily loss limit in action. It operates per agent, per calendar day (UTC):

- The circuit breaker tracks cumulative realized and unrealized P&L from the start of the UTC day
- When losses exceed the configured threshold (default: 20% of starting balance), the circuit trips
- Once tripped, order placement is blocked with `DAILY_LOSS_LIMIT` (HTTP 403)
- Market data and read-only endpoints remain accessible — the agent can still observe the market
- The circuit resets automatically at 00:00 UTC

```json
{
  "error": {
    "code": "DAILY_LOSS_LIMIT",
    "message": "Daily loss limit reached. Trading resumes at 00:00 UTC."
  }
}
```

In historical backtests, the circuit breaker simulates across virtual days — it resets whenever the virtual clock crosses midnight UTC, not in real time.

---

## Rate Limiting

Rate limits are applied per API key across four endpoint groups:

| Endpoint group | Limit | Window |
|----------------|-------|--------|
| Market data (`GET /market/*`) | 1,200 requests | per minute |
| Trading (`POST` / `DELETE /trade/*`) | 100 requests | per minute |
| Account (`GET /account/*`) | 600 requests | per minute |
| Analytics (`GET /analytics/*`) | 120 requests | per minute |

Every response includes the current rate limit state in headers:

```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 67
X-RateLimit-Reset: 1708000060
```

When the limit is exceeded, the API returns HTTP 429 with a `RATE_LIMIT_EXCEEDED` error. The `X-RateLimit-Reset` header contains the Unix timestamp when the window resets. Wait until that timestamp before retrying.

```json
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests. Retry after the reset timestamp."
  }
}
```

> **Info:**
> The trading endpoint limit (100 per minute) is intentionally lower than market data. If your agent is approaching this limit, consider using the order's `stop_loss` and `take_profit` types to reduce the number of manual order placements needed.

---

## Reading Account Risk Info

To see your agent's current risk configuration and live circuit breaker state:

```bash
curl -s http://localhost:8000/api/v1/account/info \
  -H "X-API-Key: $AGENT_KEY"
```

This returns the active risk profile (`max_position_size_pct`, `daily_loss_limit_pct`, `max_open_orders`), the current session's cumulative P&L, and whether the circuit breaker is currently active.

---

## Handling Risk Errors

Your agent should handle risk-related errors gracefully rather than treating them as fatal:

```python
from agentexchange.exceptions import (
    InsufficientBalanceError,
    RateLimitError,
    AgentExchangeError,
)
import time

try:
    order = client.place_market_order("BTCUSDT", "buy", quantity)

except InsufficientBalanceError as e:
    # Check balance before next order
    balance = client.get_balance()
    quantity = recalculate_quantity(balance.available_usdt)

except RateLimitError as e:
    # Wait for the rate limit window to reset
    time.sleep(e.retry_after or 5)

except AgentExchangeError as e:
    if e.code == "DAILY_LOSS_LIMIT":
        # Stop trading for the day, log the outcome
        log_daily_result()
    elif e.code == "POSITION_LIMIT_EXCEEDED":
        # Reduce position size and retry
        quantity = quantity * 0.5
    else:
        raise
```

---

## Next Steps

- [Trading Rules](/docs/concepts/trading-rules) — Fees, slippage, and order types
- [Agents](/docs/concepts/agents) — Configure per-agent risk profiles
- [REST API Reference](/docs/api) — Full error code reference
