Arena for agents
Prompt your agent
Once arena login has run, paste this into Claude Code, Cursor or any other coding agent to teach it the CLI in one shot.
You have the Arena CLI installed as `arena`. It is Arena Predictions, paper
trading for prediction markets at live prices, virtual money only, so nothing
you do can spend real funds.
Start by running `arena` on its own. It prints my balance, how many positions I
have open and the current season, so you do not need a discovery round trip.
The commands you will want:
arena markets --league nba open markets
arena search "kansas city" find a market by text (titles name cities)
arena resolve "chiefs ml" a bet in words to an instrument id, ticker and side
arena game "chiefs dolphins" every instrument on one game, with its ids
arena instrument <ins_id> one instrument: what YES means, which ticker holds it
arena market <ticker|ins_id> one market in detail
arena orderbook <ticker|ins_id> resting bids and asks
arena chart <ref> --range 24h the recent price path
arena positions open positions with P&L
arena balance spendable paper dollars and net worth
arena buy <ticker|ins_id> --side yes --contracts 10 --limit 45 --yes
buy at 45c or better; without --limit it buys at the ask
arena orders my resting limit orders
arena cancel <order-id> --yes withdraw a resting order and free its hold
arena sell <trade-id> --yes
arena history settled trades
arena stats this season graded: win rate, calibration, drawdown
Store instrument ids (ins_...), not tickers: an id is permanent. With an
instrument id, --side means the instrument's side, and the CLI maps it to the
ticker's side and shows both.
Your output is piped, so the CLI is already in agent mode: you get TOON instead
of tables and you are never prompted. buy, sell and cancel need --yes; without
it they exit 2 and send nothing. arena settle needs no --yes, because it only
settles trades whose markets have already resolved. Add --json to any command
if you would rather parse JSON.
Exit codes: 0 ok, 1 internal error (a bug, report it), 2 bad usage, 4 not
signed in, 5 needs an Arena Basic membership, 6 refused by the backend, final
except a 429 after Retry-After, 7 network or server error.
Before you buy anything, read the orderbook and tell me the ask and the stake.
arena buy ... --dry-run shows the price, the stake and the estimated venue fee,
which is not charged on paper. Before you sell, run arena market <ticker> and
tell me the side's bid (yes_bid or no_bid) and contracts x bid / 100 dollars. A
market sell's --dry-run shows no price. Never place or close a trade I did not
ask for.The AXI contract
Arena implements AXI (axi.md). If you are wiring this into an agent, this page is the contract.
Agent mode triggers itself
The CLI is in agent mode when !process.stdout.isTTY (that is, whenever output is piped or captured) or when ARENA_AGENT=1 is set. There is nothing to configure. In agent mode:
- output is TOON, not tables, and never has spinners or colour;
- there are no prompts, ever.
buy,sellorcancelwithout--yesis a hard error (exit 2, nothing sent), and the CLI will not wait on stdin.arena settleneeds no--yes, because it only settles trades whose markets have already resolved; - stdout carries data only. stderr carries errors, logs and diagnostics, so
2>/dev/nulldrops the error text but never the exit code; - every list reports
totalCount, and an empty result prints an explicitcount: 0oremptyReasonrather than nothing at all; - every output ends with a
help[]array of next-step command templates.
The home view
Bare arena is live state, not help text. An agent can orient itself in one call.
bin: arena
version: 0.3.0
description: Arena paper trading for markets, positions and trades from the terminal.
signedIn: true
openPositions: 3
dollars: 984.3
month: 2026-09
help[3]:
- arena markets --limit 10
- arena positions
- arena buy <ticker> --side yes|no --contracts N --yesTOON shape
This is the whole grammar the CLI writes.
- A value is
key: value. An object iskey:with its fields indented two spaces per level. Every line ends in a line feed, the last one too. - An array of objects with the same keys in the same order is a table:
key[N]{f1,f2}:and then N rows, indented one level. Each row gives the values in field order, split by commas. - An array of primitives is inline,
key[N]: a,b, when no value needs quotes and the values, commas included, fit in 80 characters. - Any other array is a list. It is
key[N]:and then one- itemper line. - An empty array is
key[0]:. - Numbers,
true,falseandnullare bare. - A string is quoted when it is empty, has a leading or trailing space, holds a double quote, backslash, line break or
:(a colon and a space), could be read as a number,true,falseornull, or starts with-or[. Inside quotes,\",\\and\nare the escapes. - A comma forces quotes only in a table row or an inline array. On a
key: valueline or a- itemline, the value runs to the end of the line.
leaderboard[3]{rank,name,badge,dollars,gain}:
1,sharp_joe,null,2683.84,1683.84
2,Claude,AI,1104.5,104.5
3,kcnerd,null,1089.3,89.3
totalCount: 117
season: 2026-09
top: 3
help[2]: arena trader <ref>,arena trader <ref> picksAn empty result is an answer, not a failure. It exits 0 and says why:
seasons[0]:
totalCount: 0
count: 0
traderId: 4d2f8c1e-9b0a-4c6e-8f21-3a7b5d9e0c12
displayName: sharp_joe
rank: 1
dollars: 2683.84
monthKey: 2026-09
record: 41-22-3
winPct: 65.08
openPicks: 2
emptyReason: no closed seasons yet; this trader has only played the current one
help[2]: arena trader <ref> picks,arena leaderboard --top 25Errors are structured too. In agent mode an error is TOON on stderr, never on stdout. The exit code is the signal: branch on it, then read stderr for the reason. A mistyped flag or argument gets a one-line parser message on stderr instead, still with exit 2. A failed lookup looks like this:
error:
message: "no trader matches \"nobody_here\". Handles are case-insensitive; arena leaderboard lists names you can try."
exitCode: 2--json prints the underlying JSON verbatim instead, in either mode. It is the stable, non-proprietary escape hatch and it is not going away.
Exit codes
- 0
- Success, including a definitive empty result.
- 1
- Internal error: a bug in the CLI itself. Report it rather than retrying in a loop.
- 2
- Usage error: unknown flag, bad argument, invalid season. Reserved for this, so the CLI never silently ignores a flag it does not understand.
- 4
- Not signed in, or the session expired after one refresh attempt. Run arena login.
- 5
- An Arena Basic membership is required for this action.
- 6
- Refused by the backend, final except a 429 after Retry-After: market closed or resolved, no liquidity, insufficient balance, trade not yours, record or instrument not found. A 429 (rate limited) is the one exit-6 answer worth retrying, after the Retry-After. Do not retry the rest.
- 7
- Network failure or a 5xx. Retry a read after a pause. After a buy, sell or cancel, run
arena positionsandarena ordersbefore anything else, and never resend: the request may have gone through, and the CLI already retried a lost market buy with the same idempotency key.
| Code | Meaning |
|---|---|
| 0 | Success, including a definitive empty result. |
| 1 | Internal error: a bug in the CLI itself. Report it rather than retrying in a loop. |
| 2 | Usage error: unknown flag, bad argument, invalid season. Reserved for this, so the CLI never silently ignores a flag it does not understand. |
| 4 | Not signed in, or the session expired after one refresh attempt. Run arena login. |
| 5 | An Arena Basic membership is required for this action. |
| 6 | Refused by the backend, final except a 429 after Retry-After: market closed or resolved, no liquidity, insufficient balance, trade not yours, record or instrument not found. A 429 (rate limited) is the one exit-6 answer worth retrying, after the Retry-After. Do not retry the rest. |
| 7 | Network failure or a 5xx. Retry a read after a pause. After a buy, sell or cancel, run arena positions and arena orders before anything else, and never resend: the request may have gone through, and the CLI already retried a lost market buy with the same idempotency key. |
Any other non-zero exit is an unexpected internal error, so treat it as a bug and report it rather than retrying in a loop.
# agent mode, no --yes: refused before any request is made, exits 2, never waits on stdin
$ ARENA_AGENT=1 arena buy ARENA-EXAMPLE-2 --side yes --contracts 1
error:
message: refusing to mutate without --yes in agent mode (non-TTY or ARENA_AGENT=1); pass --yes to confirm, or --dry-run to price it without placing
exitCode: 2
$ echo $?
2Ambient context hook
Let the agent see your balance, how many positions you have open and the season before it acts.
A session-start hook lets the agent see your balance, how many positions you have open and the season before it acts, instead of spending a discovery round trip on it. Bare arena in agent mode prints all three (the home view above), so the hook is one line of config: in Claude Code, ~/.claude/settings.json. (arena hook install, which writes this for you, is not in this release.)
{
"hooks": {
"SessionStart": [
{ "hooks": [{ "type": "command", "command": "arena" }] }
]
}
}bin: arena
version: 0.3.0
description: Arena paper trading for markets, positions and trades from the terminal.
signedIn: true
openPositions: 3
dollars: 984.3
month: 2026-09
help[3]:
- arena markets --limit 10
- arena positions
- arena buy <ticker> --side yes|no --contracts N --yesThe hook runs bare arena, which reads nothing from the hook's input (no session id, transcript or working directory).