Risk Management
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 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}:
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%.
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
{
"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.
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Retry after the reset timestamp."
}
}
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:
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:
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 — Fees, slippage, and order types
- Agents — Configure per-agent risk profiles
- REST API Reference — Full error code reference