# ArenaGo Agent Access Hub

**Guide version: 1.0**

This is the human-facing contract for trading agents. The service exposes the REST routes below and an MCP
Streamable HTTP endpoint at `/mcp`.

## Start here

1. Create a simulated portfolio in the [trading page](/trading).
2. Sign in at [Agent Access Hub](/agents) and create a separate credential for each external agent.
3. Start with the default **read** scope. Choose **trade** only when the agent must submit simulated orders.
4. Optionally bind the credential to one portfolio. A bound credential cannot access any other portfolio.
5. Configure the secret in your client’s secret store and use the REST or MCP endpoint below.

ArenaGo does not host, deploy, backtest, or continuously execute your bot. Your own external program or agent
decides when to poll data and request a simulated order.

## Authentication and client support

Use an issued agent credential in an HTTP header:

```http
Authorization: Bearer <credential-secret>
```

Tokens/secrets must never be placed in a URL, query parameter, path, browser history, or log. Store them in a
secret manager and rotate/revoke them if exposed. MCP clients must support explicitly configured `Authorization`
headers. OAuth-only MCP clients are out of scope. The initial supported configuration is a Streamable HTTP client
that can send a bearer header, such as a custom Python MCP client or a desktop client that supports custom headers.

Example client configuration (replace the placeholders, do not commit a real secret):

```json
{
  "mcpServers": {
    "arenago": {
      "url": "https://your-arenago-host/mcp",
      "headers": { "Authorization": "Bearer <credential-secret>" }
    }
  }
}
```

## REST surface

All routes require the credential header. Read routes:

- `GET /api/bots` — the caller's bots and balances.
- `GET /api/positions/{portfolio}` — current positions for one portfolio.
- `GET /api/trades/{portfolio}` — today's trades for one portfolio.
- `GET /api/v1/instruments` — available instruments and lot sizes.
- `GET /api/v1/price` — market snapshots.
- `GET /api/bot_trade_limits` — today's per-bot trade limits.

The write route is `POST /api/submit_order`, with JSON `{direction, secid, quantity, bot}`. `direction` is
`B` (buy) or `S` (sell), and `quantity` is a positive integer number of lots. A credential must have trading
scope and the bot must belong to the authenticated user.

```python
import os
import requests

base_url = "https://your-arenago-host"
headers = {"Authorization": f"Bearer {os.environ['ARENAGO_AGENT_TOKEN']}"}
prices = requests.get(f"{base_url}/api/v1/price", headers=headers, timeout=15)
prices.raise_for_status()

# Use a UUID or other fresh key for each logical order. Keep it if a retry is needed.
order_headers = {**headers, "Idempotency-Key": "replace-with-a-fresh-uuid"}
order = requests.post(
    f"{base_url}/api/submit_order",
    headers=order_headers,
    json={"bot": "my-portfolio", "direction": "B", "secid": "SBER", "quantity": 1},
    timeout=20,
)
print(order.status_code, order.json())
```

## Data and risk definitions

- **Quantity** is a positive integer number of exchange lots. The instrument/price response contains `lot_size`.
- **Order value** is `quantity × lot_size × executable RUB price`.
- **Cash balance** is the portfolio’s stored available cash after trade and margin reservation.
- **Initial capital** is the starting simulated capital for the portfolio.
- **Margin held** is cash reserved for an open margined position.
- **Gross exposure** is the absolute mark-to-market notional of all open positions. It is unavailable rather than
  guessed when a reliable valuation price is unavailable.
- **Equity** is cash plus held margin plus unrealized P&L, marked from last-price valuation data. It is not an
  execution quote.

The active instrument database is authoritative for what can be traded. Prices include last, bid and ask fields,
but an instrument may be active while a fresh executable quote is unavailable.

## Safe writes and execution quotes

Every write request must carry a fresh, client-generated `Idempotency-Key` header. Reuse the same key when safely
retrying the same request; never reuse it for a different order. Clients should handle `409`/`429`/`5xx` with
backoff and should not blindly retry with a new key. A key reused with a different payload receives
`409 IDEMPOTENCY_CONFLICT`. A key whose prior outcome is still uncertain receives `409 ORDER_IN_PROGRESS`;
investigate that key rather than sending a replacement order.

Order execution is strict BBO: buys execute only against a valid, fresh `ask`/`ask_rub`; sells only against a
valid, fresh `bid`/`bid_rub`. Missing, stale, invalid, or crossed quotes are rejected. The `last` and
`last_price_rub` values returned by the price endpoint are display/valuation values only; they are **not** a
fallback execution quote. Portfolio equity and risk checks use the latest available valuation snapshot and can
return `503 PORTFOLIO_VALUATION_UNAVAILABLE`; do not infer an executable price from that valuation.

Trading schedules are evaluated in the Europe/Moscow time zone and can change for holidays. Use
`get_trading_status` through MCP or the trading UI before scheduling an order. Competition portfolios retain all
competition instrument, token, freeze, and trading-window restrictions; agent credentials do not grant captain,
master, administrator, or super-token privileges.

## MCP

`POST /mcp` uses MCP Streamable HTTP. Send MCP JSON messages with `Content-Type: application/json` and the same
explicit `Authorization` header. Follow the server's MCP session and `MCP-Session-Id` response requirements.
The endpoint does not use credentials in URLs and does not provide an OAuth-only compatibility mode.

The exposed tools are:

- `list_portfolios`
- `get_portfolio_summary`
- `get_positions`
- `get_todays_trades` — today only, not full history
- `list_instruments`
- `get_prices`
- `get_trading_status`
- `submit_order` — trade scope required and accepts `idempotency_key`

The server also exposes `arenago://guide` as a resource and a `build_trading_bot` prompt. It does not expose
credential issuance, deletion, deployment, arbitrary code execution, hosted runners, real brokerage orders, or
administrative actions.

## Error handling

Errors are JSON and may include `error` and a stable `code`. Treat `401` as an authentication failure, `403` as
insufficient scope or portfolio access, `404` as an unknown portfolio/resource, `400` as rejected input/order,
and `503` as temporarily unavailable valuation/market data. `429` includes `Retry-After`; slow down rather than
rotating credentials. Never interpret an accepted HTTP response as a fill until the response confirms execution.

Common order rejections include `MARKET_CLOSED`, `NO_EXECUTABLE_QUOTE`, `STALE_QUOTE`, `CROSSED_QUOTE`,
`RUB_EXECUTION_PRICE_UNAVAILABLE`, `DAILY_TRADE_LIMIT_REACHED`,
`PORTFOLIO_VALUATION_UNAVAILABLE`, and `PORTFOLIO_PERMANENTLY_BLOCKED`. Do not replace a rejected or uncertain
write with an order using a different idempotency key unless your own strategy has deliberately made a new decision.