Arena

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.

paste into your agent
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, sell or cancel without --yes is a hard error (exit 2, nothing sent), and the CLI will not wait on stdin. arena settle needs 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/null drops the error text but never the exit code;
  • every list reports totalCount, and an empty result prints an explicit count: 0 or emptyReason rather 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.

arena | cat
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 --yes

TOON shape

This is the whole grammar the CLI writes.

  • A value is key: value. An object is key: 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 - item per line.
  • An empty array is key[0]:.
  • Numbers, true, false and null are 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, false or null, or starts with - or [. Inside quotes, \", \\ and \n are the escapes.
  • A comma forces quotes only in a table row or an inline array. On a key: value line or a - item line, the value runs to the end of the line.
arena leaderboard --top 3 | cat
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> picks

An empty result is an answer, not a failure. It exits 0 and says why:

arena trader sharp_joe | cat
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 25

Errors 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:

arena trader nobody_here | cat
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 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 $?
2

Ambient 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.)

manual install
{
  "hooks": {
    "SessionStart": [
      { "hooks": [{ "type": "command", "command": "arena" }] }
    ]
  }
}
what it prints
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 --yes

The hook runs bare arena, which reads nothing from the hook's input (no session id, transcript or working directory).