Arena CLI
Install the CLI
Your own login, the whole product, from a terminal. Node 20 or newer.
# install the binary (the agent prompt and the hook call arena)
npm install -g arena-prediction-cli
arena login
# or try it without installing (arena is not added to your PATH)
npx arena-prediction-cli loginOne sign-in is enough. Your agent and the MCP server act with that login. For an agent, install the binary, because the prompt and the hook call arena directly.
Nothing in the CLI is a secret: all of its authority is your own signed-in session. There is nothing else to install for agent use. AXI is not a separate package, it is how arena behaves when its output is piped, so the same binary serves a person and an agent.
Mac app
The same account, in an app on your Mac. Apple silicon, macOS 13 or newer.
- Download Arena-Mac.dmg, open it and drag Arena to Applications.
- Open Arena from Applications.
- If macOS says it cannot check the app, open System Settings, then Privacy & Security, choose Open Anyway, then Open.
The app shows a notice when a newer version is out. The latest release is always at https://downloads.arena-predictions.com/Arena-Mac.dmg.
Sign in
Arena has no password login. arena login hands the browser the interactive step and catches the redirect on a loopback port.
$ arena login
arena: opening your browser to sign in. If nothing opens, visit:
https://<auth host>/authorize?provider=google&redirect_to=http://127.0.0.1:53682/callback…
signed in as sharp_joe
signed in with Google
member yes, Arena Basic
balance $101,046.00
profile private
Next:
arena open
arena balanceProviders
arena login --provider apple signs in with Apple instead of Google. There is no guest or anonymous sign-in: the CLI acts for an account, so every trade and every record belongs to the person who signed in.
Headless machines
arena login --device, the flow for a machine with no browser, is not in this release. For a headless box, sign in on a machine that has a browser, straight into a file: ARENA_CREDENTIALS_FILE=./arena-creds.json arena login. Copy that file to the box, keep it readable only by you (chmod 600), and set ARENA_CREDENTIALS_FILE to its path there. One host per login: every refresh replaces the stored token, so two hosts sharing one file sign each other out. On a Mac the normal login lives in the keychain, so there is no ~/.config/arena/credentials.json to copy unless you signed in this way.
Where credentials live
In your OS keychain, falling back to ~/.config/arena/credentials.json with mode 0600. The access token lives about an hour and refreshes itself; arena logout deletes the stored pair.
Command reference
Reads work signed out where the data is public. Writes need a session.
- arena
- Home view. Live balance, open-position count and next steps, not help text. From CLI 0.6.0 it lists
recentlySettled, the bets that settled in the last 24 hours, and a person at a terminal sees what settled since they last looked. Signed out, a person gets what Arena is in two sentences and three commands that need no sign-in. - arena login [--provider google|apple]
- Sign in. Opens a browser and catches the redirect on 127.0.0.1.
--device, the headless flow, is not in this release. - arena logout
- Forget the stored credentials.
- arena whoami
- Who you are signed in as, membership status (
isMember), spendable balance (balanceDollars) and whether your account is private (private). From CLI 0.6.0 a person at a terminal reads it in words; TOON and--jsonkeep the fields. - arena markets [--sport …] [--league …] [--event …] [--limit 20]
- Browse open markets. Default limit 20.
- arena market <ticker|ins_id> [--contracts N] [--stake 100]
- One market in detail. From CLI 0.6.0
--stake(default $100) says what that many paper dollars buy on each side at the ask and what they pay if the side wins, the profit and the loss (payout; a sentence a side for a person), signed out too. Takes a ticker, an Arena instrument id (ins_…) or its slug; with an id, the output addsbuy_for_yesandbuy_for_no, the side of the ticker that holds each side of the instrument.--contracts Naddsvenue_fee_est_dollars, the estimated venue fee for that size, which is not charged on paper. Prices here are Kalshi’s in whole cents:yes_bid57 is 57%. From CLI 0.6.0 it works signed out for an Arena instrument (an id, a slug or a bet in words): Kalshi’s buy and sell prices for the instrument’s YES and NO, read the wayarena quotesreads them; a ticker Arena does not list still needsarena login. - arena search <query>
- Substring search over titles and tickers.
- arena resolve <text…> [--limit 10]
- Turn a bet in words, such as "chiefs ml", "kc -3.5", "canes over 5.5" or "arsenal leeds draw", into an Arena instrument id, the ticker and the side to buy: the next step is
arena buy <ticker> --side <buy_side>. Full-game moneylines, spreads and totals on half-point lines in the NFL, college football, MLB, the NHL, the NBA and the Premier League. It never guesses, so no rows means rephrase. Works signed out. - arena instrument <ins_id|slug>
- One Arena instrument: its label (always its YES side), type, line, start time and each listed ticker with
buy_for_yesandbuy_for_no;--jsonaddsyes_means, the YES side in a sentence. A ticker works too and finds the instrument listed on it. Works signed out. - arena game <game_id|slug|words…> [--type ml|spread|total]
- One game and every Arena instrument on it, with their ids: moneylines first, then spreads and totals by line. Spreads and totals are added closer to kickoff. Works signed out. Up to CLI 0.5 there are no prices; from 0.6.0 each row has
yes_pct, the chance of its YES (every row signed in, the moneylines signed out), and the game hasgame_state(pregame, live, final, postponed or cancelled). - arena games [--league nfl|all] [--date today|tomorrow|YYYY-MM-DD] [--week N] [--limit 20]
- From CLI 0.6.0, with no flags: today's games in every league, grouped by league, with each team's chance to win (
chances, andmoneylineswith eachyes_pctin--json: Kalshi's price) and the start in your own time.--dateis one calendar day in America/New_York (tonightis today, and today keeps last night's games still being played); a day with none says the next day that has games, and a league with none that day says what the others have, each as anarena games --datecommand.--leagueis one league id (nfl,ncaaf,mlb,nhl,nba,epl,ucl) orall; one league without--dateis its upcoming list, priced the same way, and--week Nits schedule for a week. Prices are read in batches of 20: up to 40 games signed in, and signed out up to 60 moneylines in whole games (about 30 games), with the answer saying how many of the day's games have chances. In agent mode and--jsonthe day is onegamestable withleagueon each row; a person reads a table per league, the chance cell sayinglive:orfinal, with team codes on a narrow terminal so the chance is never cut. A price read Arena rate-limits says so with the wait (pricesRetryAfterSeconds). Works signed out. CLI 0.5.0, on npm, lists the NFL's upcoming games (--leaguedefaults tonflthere) with their ids and instrument counts. - arena quotes <ins_id|ticker|words…>
- One instrument’s price on Kalshi and, while Arena shows them, on Novig and Polymarket for the same instrument:
venue,side,yes_pct,bid_pct,ask_pct,as_ofand anote. Every percentage is the chance of the instrument’s YES. With--jsonthe same prices are fractions of 1 under the namesyes_bid,yes_askandyes_mid(0.57 is 57%), the namesarena marketuses for whole cents (57), so read the unit from the command, not the name. The note says how that venue’s contract differs, whether a person has reviewed the match, and that Novig’s prices arrive over Novig’s live feed when it is connected and by polling otherwise, so a quiet market can sit unchanged for minutes; a price more than a minute old says how old it is (a Novig price can be shown up to 5 minutes old). From CLI 0.6.1 the table also lists ProphetX, whose prices come from its sandbox exchange until Arena’s production access is approved. Venues come in a fixed order, not ranked by price. Paper trades fill in Arena’s sandbox and are not sent to Kalshi, Novig or Polymarket. When Arena is not showing them, the output says so and lists Kalshi only. Works signed out. - arena gaps [ins_id|ticker|words…] [--league nfl] [--min-gap N] [--limit 20] [--size 100] [--all-venues]
- Price gaps for the same bet across Kalshi, Novig and Polymarket. With no argument: live instruments ranked, NFL, college football, MLB, NHL, Premier League, NBA and Champions League games for now. The list is ranked arbitrage first, then by the cheapest pair after fees, then by how far a venue is below Kalshi; each row names the game, every venue’s price per side and, in
where, which venue is below Kalshi or that Kalshi is lowest on both sides. Gaps are in cents, to two decimal places (gap_centscan be 0.8;gap_dollarsin--jsonis cents / 100). With one instrument: every pair that buys YES on one venue and NO on another, its cost before and after both venues’ taker fees for--sizecontracts, the edge in cents and why it is or is not labelled arbitrage, word for word. From CLI 0.6.0--topworks as--limit. Every leg names its venue and its age. A pair with Polymarket is listed either way, but by default only Kalshi and Novig can pair for that label;--all-venueslets a Polymarket pair take it too. Polymarket’s prices come from its international exchange, which does not accept US persons. See /docs/methodology. Works signed out. - arena perps [list|mark <ticker>|history <ticker>] [--status active|inactive|closed|all] [--limit N]
- Kalshi perpetual markets from the live perps book, with your login.
arena perps(andarena perps list) lists active tickers with their live settlement mark, bid and ask in contract dollars (mark,bid,ask;--jsonaddslast,liquidation_mark,reference,as_of,volume_24handopen_interest);--statuswidens the list to inactive, closed or all, and--limit(default 100) cuts it, sayingtruncatedwhen it does.arena perps mark <ticker>is one ticker’s live settlement mark, bid, ask and last trade; an unknown ticker exits 6not_found.arena perps history <ticker> [--limit N] [--since T] [--until T] [--source all|live|backfill]asks for one ticker’s recent stored snapshots, newest first (--limit1 to 50, default 20). Perp history is switched off on Arena’s server: until Arena switches it on, the command prints “Perp history is not available yet.” and exits 6not_available, which is about the feature, not your ticker. From CLI 0.6.0. - arena catalog
- The groups and leagues you can filter by. Not yet shipped
- arena screener [--league L] [--group G] [--min-volume N] [--max-spread C] [--price-min C] [--price-max C] [--closes-within 4h] [--min-move C] [--sort volume|move|spread|close_time] [--limit 20]
- Screen open markets by volume, price, spread, time to close and move over about 24 hours. Rows add
spread_centsand a signedmove_cents. Needsarena login. - arena orderbook <ticker|ins_id> [--depth 10]
- Resting bids and asks. Takes a ticker, an instrument id or its slug.
- arena chart <ref> [--range 1h|6h|24h|7d] [--points 12]
- A market's recent price path for a ticker, an instrument id or its slug: open, high, low, now, the change and a sparkline. For an instrument whose YES is the ticker's NO, the numbers price the instrument's YES, and
ticker_sidesays which side was charted. Needsarena login. - arena buy <ticker|ins_id|words> [--side yes|no] --contracts N|--stake DOLLARS [--venue best|kalshi] [--max-price CENTS] [--limit CENTS] [--tif gtc|ioc|fok] [--expires 4h] [--idempotency-key UUID] [--prob PCT] [--yes] [--dry-run]
- Open a position.
--stakesizes it in dollars: as many whole contracts as it buys at the worst price the fill may take (--max-pricewhen given, else the live ask), or at--limit, so it never costs more than the stake.--max-priceis the most you pay, enforced by the server.--idempotency-key(or--client-order-id) is your own UUID for the order: after a timeout, the retry command the receipt prints (the same contracts, cap and venue under the same key) answers the stored trade, markedreplayed, and the same key on a different order is refused (idempotency_key_reused). With--stakea keyed buy needs--max-priceor--limit, so the size cannot change between runs.--jsonanswers{ receipt, trade }.--probrecords your probability that the side wins, a whole percent from 1 to 99: it is saved with the trade, shown against the price as an edge in points (statedProbPct,edgePts), scored once the pick settles (on the eval board only if the pick is public), and never changes the fill. On one team’s win market--sidecan be left out: buying means that team wins, so it is yes, and the CLI says so; any other market asks for--side. Without --limit it quotes, confirms, places and reconciles the actual fill against the quote. With --limit it names your price: the order fills at that price or better, or rests until it does. With an instrument id or slug, --side is the instrument's side: the CLI maps it to the ticker's side, and the quote and the receipt show both (instrument_side,ticker_side).--venue best, the default, lets the fill take Novig's or Polymarket's price for the same instrument when it passes Arena's checks, and--venue kalshifills at Kalshi's price (/docs/methodology). The receipt names the venue (venue) with Kalshi's price beside it (kalshiPriceCents). --dry-run shows where it would fill now (venue,venuePriceCents,routeReason). From CLI 0.6.0 it shows no fee estimate and no book walk, because it is the server's preview of the paper fill:arena market <ref> --contracts Nshowsvenue_fee_est_dollars, the estimated venue fee, not charged on paper. From CLI 0.6.0: the ref can be a bet in words (arena buy steelers ml --stake 100 --dry-run), resolved first, where--side nobets against the words; several matches exit 6ambiguouswith each one's dry run inhelp[]. Every dry run and confirmation says what the bet pays if it wins and the most it can lose (payoutDollars,profitIfWinDollars,maxLossDollars, and apayssentence). A person at a terminal gets a dry run as a plain summary (the bet, the start, the contracts and price, the cost, thepayssentence and where it fills), and--verboseadds every field; TOON and--jsonkeep every field. For anins_id or a bet in words, the dry run'shelp[0]is exactly the order it priced: the same size, your--max-priceor else Kalshi's ask now (priceCapCents,priceCapIs),--prob, the key,--account, and--venueonly when you chose one, ending in--yesin agent mode and without it for a person (so running it asks the payout question); after a limit dry run it is the limit order, thenarena orders. After a Kalshi-ticker dry run it names the Arena instrument that ticker lists, with the instrument's side, because Arena never repeats a venue's ticker; a ticker Arena does not list leaves the line out ofhelp[]and puts it inplaceTemplate. When the preview comes back above that cap (a tick between two reads), the dry run prices the order at the cap, says it would be refused as priced (wouldPlace: false,overCap), andhelp[0]is a dry run at the preview's price. A person placing that--stakeorder at a terminal is not asked, since a yes would likely be refused: nothing is placed, it exits 6price_moved(error.details.wouldPlace: false), and the hint is the same order sized at the new price, which asks first. A limit order's summary says when an--expiresorder stops resting. A rate-limited dry run (about 20 previews a minute) exits 7rate_limitedwithretryAfterSeconds. A dry run that costs more than your cash exits 6insufficient_balancewitherror.details.wouldPlace: falseand the largest dry run that fits. A side nobody sells below 100% exits 6no_liquidityand says when the game is in progress or over; a game in progress fills at Kalshi's price only. Signed out it exits 4 and says what trading needs. With--liveor--poly-intlthe buy is a live order, sent with your own venue credentials, saved in Settings: real money at the venue, under the venue's rules and terms. Arena is not a broker and never holds your funds. A live buy needs a price you set yourself,--max-priceor--limit: in CLI 0.6.1 every live buy and its dry run exit 2 without one, and in 0.6.0 you must pass one yourself. - arena sell [trade-id] [--venue best|kalshi] [--min-price CENTS] [--limit CENTS] [--tif gtc|ioc|fok] [--expires 4h] [--idempotency-key UUID] [--yes] [--dry-run]
- Close a position at the live bid, or rest a limit exit with --limit.
--min-priceis the least a market sell takes; below it nothing is sold.--dry-runshows the proceeds and the P&L. Trade ids come from arena positions.--idempotency-keyon a sell is echoed in the receipt and becomes a limit exit'sclient_order_id; the server does not replay a market sell by key, so a market sell run again after it went through answersalready_closed(checkarena positions).--venue best, the default, lets the exit take another venue's bid when it passes Arena's checks. Today that only happens for a position filled on Novig or Polymarket, which can exit on that venue or on Kalshi.--venue kalshisells at Kalshi's bid. From CLI 0.6.0, with no trade id and exactly one open position, a dry run prices that one and a person is asked before it is sold; with--yesand no id, or several positions, it exits 2 with each position's dry run inhelp[]. The dry run also says what selling pays now next to what keeping it pays (pays). - arena orders [--status resting|executed|canceled|all] [--ticker T] [--limit 200]
- Your limit orders. Resting by default: the ones still holding money and still able to fill on their own.
--tickertakes the market's ticker. With--json, or--fieldsnaming them, rows also carryinstrument_id,instrument_labelandinstrument_side. From CLI 0.6.0 every row carries them andbet, the bet in words, which the default columns show in place of the ticker, and--topworks as--limit. - arena cancel <order-id> [--yes]
- Withdraw a resting order and get its hold back. Cancelling one that already filled is not an error: it answers reducedBy 0 and the trade id.
--jsonanswers{ order, reducedBy, releasedHoldDollars, canceled }. - arena settle
- Settle anything already resolved. A backstop: the server settles automatically within about five minutes.
- arena positions [--no-live]
- Open positions marked to live prices, with unrealised P&L. Prices are whole cents (
entry_price_cents,markPriceCents) and money is paper dollars (stake_dollars,unrealizedPnlDollars): the server’s snake_case columns sit next to the CLI’s camelCase marks.--no-liveskips the market reads and the P&L column. With--json, or--fieldsnaming them, rows also carryinstrument_id,instrument_labelandinstrument_side;--jsonleaves out internal columns unless--raw. From CLI 0.6.0 rows carrybet(the bet in words, such as "PHI ML (PHI at CHI)"),paid_pctandnow_pct, and the default columns show them in place of the ticker, which stays in--json. Read with an API key (--via api), rows carryid(the trade id) andbettoo. - arena balance
- Your balance (
balanceDollars), net worth, open stake, P&L since your last reset (pnlSinceResetDollars) and lifetime P&L (lifetimePnlDollars, the total since Sep 1, 2026 across every reset), with the run you are on. The short names (balance,netWorth,pnlSinceReset,lifetimePnl) are aliases with the same values. - arena history [--window 30d|90d|all] [--since 30d|YYYY-MM-DD] [--until YYYY-MM-DD] [--run current] [--limit n]
- Your settled trades, every one by default; from CLI 0.6.0
--limit n(or--top n) keeps the newest n, and the realized P&L still counts every one in the view.--sinceand--untiltake a day in America/New_York;--run currentkeeps what closed since your last reset. A position written off by a reset showsclose_reasonaccount_reset.resolved_price_centsis the price the trade closed at, for the side held: the sale price (1 to 99) for a sold trade, 100 or 0 for a win or a loss, and empty for a position written off by a reset. Instrument columns as forarena positions. From CLI 0.6.0 rows carrybetandresult(won, lost, push, sold early), newest close first. - arena stats [--window 30d|90d|all] [--since …] [--until …] [--run current] [--by league|band|hold]
- Your trading since Sep 1, 2026 graded, or a window, a date range or your current run: win rate against the prices you paid, calibration by price band, closing-line value, return on stake and drawdown.
--byadds a breakdown by league, entry price band or how the trade ended. With no closed trades it prints zero counts and anemptyReason, and when every closed trade was sold it says that only settled trades are graded.arena stats --helpdefines each field,acsamong them. From CLI 0.6.0:max_drawdown_dollarsbeside the percent, andsummary, a sentence on how your bets ended and how many settled bets the calibration score still needs. P&L here is before fees;fee_drag_est_dollarsis what Kalshi's taker fee would have cost, at entry on settled bets and (from CLI 0.6.0) at entry and at the sale on bets sold early. Needsarena login. - arena reset [--forfeit-open] [--reason TEXT] [--dry-run]
- Start a new run at $100,000. It shows the preview first, and in a terminal you type RESET. Turn resets on in Settings first; one reset every 30 days, shown on a public profile. Resting orders are cancelled; open positions stop the reset unless
--forfeit-openwrites them off as losses. Scripts and agents cannot reset unless a person setsARENA_ALLOW_RESET=1or runsarena config set allowReset truein a terminal. - arena config [get|set] [key] [value]
- Your CLI preferences:
defaultFormat,confirmTrades,allowReset, and from CLI 0.6.0webLinks(the "See it on the web" line) andopenBrowser(whetherarena openopens a browser). SettingallowResetto true works only in a terminal. - arena picks [--limit n]
- Today’s AI picks. From CLI 0.6.0 rows carry
bet, the pick in words, andprice_pct(the price of the side picked), it works signed out, and--limit nkeeps the first n. - arena leaderboard [--window 30d|90d|all] [--ai] [--kind people|ai] [--top 25]
- The leaderboards (from CLI 0.6.0,
--kind aiis--ai). 30d (the default, as on the website) and 90d rank trades that closed in the window; all ranks total profit since Sep 1, 2026 across every reset, for accounts with 20 closed trades and a first trade 30 or more days ago.--aishows the AI models. Rows carrypnl,tradesClosed,winPct(0 to 100),resetsandbusts. Works signed out. From CLI 0.6.0, when no person qualifies it shows the AI models' board for the window, withkind: ai,asked: peopleand anoteon how to appear, and--limitworks as--top. - arena trader <ref> [picks]
- A published trader record: the all-time standing and every reset. Lifetime P&L carries
lifetimePnlAsOf: the daily snapshot it is as of, orlivefor your own account. Accounts are private by default: a private account answers its name andprivate: trueand nothing else (exit 0; from CLI 0.6.0,traderIdis null), and your own account is always your full record. Works signed out. - arena privacy [status|on|off]
- Your account's privacy. Accounts are private by default: others see none of your trades, balance, rank or record until you make your profile public in Settings on the website.
arena privacyshows the state;arena privacy onmakes the account private (a y/N question in a terminal,--yesfor scripts and agents).arena privacy offchanges nothing: only a person can make an account public, in Settings, so it prints where (and exits 6,privacy_off_needs_person, in agent mode). - arena keys [list|create|rotate|revoke|check]
- Your API keys, with your own login (CLI 0.6.0 or newer; Settings on the website works now).
create --save FILEwrites a new key with mode 0600, never through a symlink, and checks the file before the key is made; it never replaces a file unless you add--force, which writes a new 0600 file in its place.rotate <prefix> --grace 10mreplaces one and keeps the old one working for the grace;revoke <prefix> --yes;checksays whose account a key reads and your plan's counts. A key reads unless made withcreate --trade, which adds trade:write (paper orders through POST /portfolio/orders) and asks first at a terminal. Keys made here expire in 90 days unless--expiressays otherwise. For an agent, create and rotate need--yesand--saveor--print-token, so a key never lands in its output unasked. Aftercreate --save FILEthe next steps name the file, never the key:arena keys check --key-file FILEandarena eval --key-file FILE.--jsongivesscopesas a list in every keys answer. - arena open [page] [ref], arena dashboard
- Run
arena opento see it on the web (CLI 0.6.0 or newer).arena openandarena dashboardare your portfolio;positions,orders,history,trade <id>,stats,eval,settings privacy|reset|keys,profile,market <ref>(anins_id, a slug, a Kalshi ticker or a bet in words),game <ref>,trader <name>,ai <model>,leaderboard [--window 30d] [--ai],docs [topic]andproopen those pages. It opens a browser only for a person at a terminal, not over SSH or in CI; an agent gets{open: {url, audience, opened: false}}and nothing opens, withresolvedIdandresolvedLabelwhen a bet, a game or a short trade id was looked up.audience: owneris a page only its owner sees, not a link to share.profileis your public profile page (audience: public), and its note says what others see there: your name and that the account is private, or your record once you make it public.--printprints the bare link; with--jsonit is the same{open}answer. It never takes a URL (exit 2);profilesigned out is exit 4; several matching bets are exit 6,ambiguous, with their ids inhelp[]. Answers that have a page carrywebUrl;shareUrlis the link that is safe to send to someone else, null unless the profile is public. - arena eval [--window 30d|90d|all] [--key-file FILE] [--picks] [--subject predictions|account] [--predictions FILE]
- Your own settled picks scored the way the eval board scores them, picks placed while private included (CLI 0.6.0 or newer): Brier score and log loss of your
--probwhere you gave one and of the entry price otherwise (forecastBasissays which), the same two over your stated picks alone, P&L after the fee Kalshi would charge (paper trades pay none), the largest fall of that P&L in dollars, and calibration bands;--pickslists the picks. It reads with a key given on purpose (--key-file,ARENA_API_KEYorARENA_API_KEY_FILE, throughGET /api/v1/me/eval), else with your login (the same rows), else with the key in~/.config/arena/arena.key(the filearena keys create --save ~/.config/arena/arena.keywrites, which the MCP server and the Python SDK read too); with none of them it exits 4 withkey_requiredandhelp[]isarena login.arena statsgrades your--probwith your login.--subject predictionswith--predictions FILE(or--subject account) saves a model or account eval throughPOST /api/v1/evalsand prints the run id. That run shows on the Design tab. - arena evalboard [--window 30d|90d|all] [--kind all|ai|human] [--cohort all|same_games] [--min-scored 20]
- The public eval board (CLI 0.6.0 or newer), signed out: people and AI models scored on their settled public picks, ranked inside their basis (log loss, closing-line value or edge), never by profit, with the market’s own Brier and log loss beside each row and 95% intervals clustered by game.
≈(tied_with_abovein JSON) marks a rank the sample does not separate from the one above. The same board as /eval and GET /eval. - arena record <trader> [--verify]
- A trader’s Ed25519-signed track record over the last 90 days (CLI 0.6.0 or newer), signed out.
--verifychecks the signature against the key Arena publishes at /.well-known/arena-track-record-keys.json, never a key the record names; a record that does not verify exits 6record_not_verifiedwith what failed. The record is exactly what Arena signed,*_pointskeys included, so a saved copy verifies on its own:--jsonhas it asrecord, and agent mode and--fields signed_recordcarry it as one JSON string. At a terminal its numbers read in words, the drawdown as a share of the peak it fell from. - arena backtest [--source holdings|series|kalshi] [--rule FILE|--predictions FILE|--csv FILE|--body FILE] [--name NAME] [--model MODEL]
- A paper replay, saved on your account when you are signed in or an API key is set. Prints the run id. The run shows on the Design tab (
arena open design). - arena runs [--kind backtest|eval] [--mode paper|live] [--source cli|mcp|api|web] [--limit N] [--cursor C] [show <id>]
- Saved backtests and evals, newest first.
arena runs show <id>prints one run, including its workflow. - arena watch <ticker…> [--orderbook]
- Live prices, repainted in place. Not yet shipped
- arena hook install
- Install the ambient-context hook into your agent runner. Not yet shipped
| Command | What it does |
|---|---|
| arena | Home view. Live balance, open-position count and next steps, not help text. From CLI 0.6.0 it lists recentlySettled, the bets that settled in the last 24 hours, and a person at a terminal sees what settled since they last looked. Signed out, a person gets what Arena is in two sentences and three commands that need no sign-in. |
| arena login [--provider google|apple] | Sign in. Opens a browser and catches the redirect on 127.0.0.1. --device, the headless flow, is not in this release. |
| arena logout | Forget the stored credentials. |
| arena whoami | Who you are signed in as, membership status (isMember), spendable balance (balanceDollars) and whether your account is private (private). From CLI 0.6.0 a person at a terminal reads it in words; TOON and --json keep the fields. |
| arena markets [--sport …] [--league …] [--event …] [--limit 20] | Browse open markets. Default limit 20. |
| arena market <ticker|ins_id> [--contracts N] [--stake 100] | One market in detail. From CLI 0.6.0 --stake (default $100) says what that many paper dollars buy on each side at the ask and what they pay if the side wins, the profit and the loss (payout; a sentence a side for a person), signed out too. Takes a ticker, an Arena instrument id (ins_…) or its slug; with an id, the output adds buy_for_yes and buy_for_no, the side of the ticker that holds each side of the instrument. --contracts N adds venue_fee_est_dollars, the estimated venue fee for that size, which is not charged on paper. Prices here are Kalshi’s in whole cents: yes_bid 57 is 57%. From CLI 0.6.0 it works signed out for an Arena instrument (an id, a slug or a bet in words): Kalshi’s buy and sell prices for the instrument’s YES and NO, read the way arena quotes reads them; a ticker Arena does not list still needs arena login. |
| arena search <query> | Substring search over titles and tickers. |
| arena resolve <text…> [--limit 10] | Turn a bet in words, such as "chiefs ml", "kc -3.5", "canes over 5.5" or "arsenal leeds draw", into an Arena instrument id, the ticker and the side to buy: the next step is arena buy <ticker> --side <buy_side>. Full-game moneylines, spreads and totals on half-point lines in the NFL, college football, MLB, the NHL, the NBA and the Premier League. It never guesses, so no rows means rephrase. Works signed out. |
| arena instrument <ins_id|slug> | One Arena instrument: its label (always its YES side), type, line, start time and each listed ticker with buy_for_yes and buy_for_no; --json adds yes_means, the YES side in a sentence. A ticker works too and finds the instrument listed on it. Works signed out. |
| arena game <game_id|slug|words…> [--type ml|spread|total] | One game and every Arena instrument on it, with their ids: moneylines first, then spreads and totals by line. Spreads and totals are added closer to kickoff. Works signed out. Up to CLI 0.5 there are no prices; from 0.6.0 each row has yes_pct, the chance of its YES (every row signed in, the moneylines signed out), and the game has game_state (pregame, live, final, postponed or cancelled). |
| arena games [--league nfl|all] [--date today|tomorrow|YYYY-MM-DD] [--week N] [--limit 20] | From CLI 0.6.0, with no flags: today's games in every league, grouped by league, with each team's chance to win (chances, and moneylines with each yes_pct in --json: Kalshi's price) and the start in your own time. --date is one calendar day in America/New_York (tonight is today, and today keeps last night's games still being played); a day with none says the next day that has games, and a league with none that day says what the others have, each as an arena games --date command. --league is one league id (nfl, ncaaf, mlb, nhl, nba, epl, ucl) or all; one league without --date is its upcoming list, priced the same way, and --week N its schedule for a week. Prices are read in batches of 20: up to 40 games signed in, and signed out up to 60 moneylines in whole games (about 30 games), with the answer saying how many of the day's games have chances. In agent mode and --json the day is one games table with league on each row; a person reads a table per league, the chance cell saying live: or final, with team codes on a narrow terminal so the chance is never cut. A price read Arena rate-limits says so with the wait (pricesRetryAfterSeconds). Works signed out. CLI 0.5.0, on npm, lists the NFL's upcoming games (--league defaults to nfl there) with their ids and instrument counts. |
| arena quotes <ins_id|ticker|words…> | One instrument’s price on Kalshi and, while Arena shows them, on Novig and Polymarket for the same instrument: venue, side, yes_pct, bid_pct, ask_pct, as_of and a note. Every percentage is the chance of the instrument’s YES. With --json the same prices are fractions of 1 under the names yes_bid, yes_ask and yes_mid (0.57 is 57%), the names arena market uses for whole cents (57), so read the unit from the command, not the name. The note says how that venue’s contract differs, whether a person has reviewed the match, and that Novig’s prices arrive over Novig’s live feed when it is connected and by polling otherwise, so a quiet market can sit unchanged for minutes; a price more than a minute old says how old it is (a Novig price can be shown up to 5 minutes old). From CLI 0.6.1 the table also lists ProphetX, whose prices come from its sandbox exchange until Arena’s production access is approved. Venues come in a fixed order, not ranked by price. Paper trades fill in Arena’s sandbox and are not sent to Kalshi, Novig or Polymarket. When Arena is not showing them, the output says so and lists Kalshi only. Works signed out. |
| arena gaps [ins_id|ticker|words…] [--league nfl] [--min-gap N] [--limit 20] [--size 100] [--all-venues] | Price gaps for the same bet across Kalshi, Novig and Polymarket. With no argument: live instruments ranked, NFL, college football, MLB, NHL, Premier League, NBA and Champions League games for now. The list is ranked arbitrage first, then by the cheapest pair after fees, then by how far a venue is below Kalshi; each row names the game, every venue’s price per side and, in where, which venue is below Kalshi or that Kalshi is lowest on both sides. Gaps are in cents, to two decimal places (gap_cents can be 0.8; gap_dollars in --json is cents / 100). With one instrument: every pair that buys YES on one venue and NO on another, its cost before and after both venues’ taker fees for --size contracts, the edge in cents and why it is or is not labelled arbitrage, word for word. From CLI 0.6.0 --top works as --limit. Every leg names its venue and its age. A pair with Polymarket is listed either way, but by default only Kalshi and Novig can pair for that label; --all-venues lets a Polymarket pair take it too. Polymarket’s prices come from its international exchange, which does not accept US persons. See /docs/methodology. Works signed out. |
| arena perps [list|mark <ticker>|history <ticker>] [--status active|inactive|closed|all] [--limit N] | Kalshi perpetual markets from the live perps book, with your login. arena perps (and arena perps list) lists active tickers with their live settlement mark, bid and ask in contract dollars (mark, bid, ask; --json adds last, liquidation_mark, reference, as_of, volume_24h and open_interest); --status widens the list to inactive, closed or all, and --limit (default 100) cuts it, saying truncated when it does. arena perps mark <ticker> is one ticker’s live settlement mark, bid, ask and last trade; an unknown ticker exits 6 not_found. arena perps history <ticker> [--limit N] [--since T] [--until T] [--source all|live|backfill] asks for one ticker’s recent stored snapshots, newest first (--limit 1 to 50, default 20). Perp history is switched off on Arena’s server: until Arena switches it on, the command prints “Perp history is not available yet.” and exits 6 not_available, which is about the feature, not your ticker. From CLI 0.6.0. |
| arena catalog | The groups and leagues you can filter by. Not yet shipped |
| arena screener [--league L] [--group G] [--min-volume N] [--max-spread C] [--price-min C] [--price-max C] [--closes-within 4h] [--min-move C] [--sort volume|move|spread|close_time] [--limit 20] | Screen open markets by volume, price, spread, time to close and move over about 24 hours. Rows add spread_cents and a signed move_cents. Needs arena login. |
| arena orderbook <ticker|ins_id> [--depth 10] | Resting bids and asks. Takes a ticker, an instrument id or its slug. |
| arena chart <ref> [--range 1h|6h|24h|7d] [--points 12] | A market's recent price path for a ticker, an instrument id or its slug: open, high, low, now, the change and a sparkline. For an instrument whose YES is the ticker's NO, the numbers price the instrument's YES, and ticker_side says which side was charted. Needs arena login. |
| arena buy <ticker|ins_id|words> [--side yes|no] --contracts N|--stake DOLLARS [--venue best|kalshi] [--max-price CENTS] [--limit CENTS] [--tif gtc|ioc|fok] [--expires 4h] [--idempotency-key UUID] [--prob PCT] [--yes] [--dry-run] | Open a position. --stake sizes it in dollars: as many whole contracts as it buys at the worst price the fill may take (--max-price when given, else the live ask), or at --limit, so it never costs more than the stake. --max-price is the most you pay, enforced by the server. --idempotency-key (or --client-order-id) is your own UUID for the order: after a timeout, the retry command the receipt prints (the same contracts, cap and venue under the same key) answers the stored trade, marked replayed, and the same key on a different order is refused (idempotency_key_reused). With --stake a keyed buy needs --max-price or --limit, so the size cannot change between runs. --json answers { receipt, trade }. --prob records your probability that the side wins, a whole percent from 1 to 99: it is saved with the trade, shown against the price as an edge in points (statedProbPct, edgePts), scored once the pick settles (on the eval board only if the pick is public), and never changes the fill. On one team’s win market --side can be left out: buying means that team wins, so it is yes, and the CLI says so; any other market asks for --side. Without --limit it quotes, confirms, places and reconciles the actual fill against the quote. With --limit it names your price: the order fills at that price or better, or rests until it does. With an instrument id or slug, --side is the instrument's side: the CLI maps it to the ticker's side, and the quote and the receipt show both (instrument_side, ticker_side). --venue best, the default, lets the fill take Novig's or Polymarket's price for the same instrument when it passes Arena's checks, and --venue kalshi fills at Kalshi's price (/docs/methodology). The receipt names the venue (venue) with Kalshi's price beside it (kalshiPriceCents). --dry-run shows where it would fill now (venue, venuePriceCents, routeReason). From CLI 0.6.0 it shows no fee estimate and no book walk, because it is the server's preview of the paper fill: arena market <ref> --contracts N shows venue_fee_est_dollars, the estimated venue fee, not charged on paper. From CLI 0.6.0: the ref can be a bet in words (arena buy steelers ml --stake 100 --dry-run), resolved first, where --side no bets against the words; several matches exit 6 ambiguous with each one's dry run in help[]. Every dry run and confirmation says what the bet pays if it wins and the most it can lose (payoutDollars, profitIfWinDollars, maxLossDollars, and a pays sentence). A person at a terminal gets a dry run as a plain summary (the bet, the start, the contracts and price, the cost, the pays sentence and where it fills), and --verbose adds every field; TOON and --json keep every field. For an ins_ id or a bet in words, the dry run's help[0] is exactly the order it priced: the same size, your --max-price or else Kalshi's ask now (priceCapCents, priceCapIs), --prob, the key, --account, and --venue only when you chose one, ending in --yes in agent mode and without it for a person (so running it asks the payout question); after a limit dry run it is the limit order, then arena orders. After a Kalshi-ticker dry run it names the Arena instrument that ticker lists, with the instrument's side, because Arena never repeats a venue's ticker; a ticker Arena does not list leaves the line out of help[] and puts it in placeTemplate. When the preview comes back above that cap (a tick between two reads), the dry run prices the order at the cap, says it would be refused as priced (wouldPlace: false, overCap), and help[0] is a dry run at the preview's price. A person placing that --stake order at a terminal is not asked, since a yes would likely be refused: nothing is placed, it exits 6 price_moved (error.details.wouldPlace: false), and the hint is the same order sized at the new price, which asks first. A limit order's summary says when an --expires order stops resting. A rate-limited dry run (about 20 previews a minute) exits 7 rate_limited with retryAfterSeconds. A dry run that costs more than your cash exits 6 insufficient_balance with error.details.wouldPlace: false and the largest dry run that fits. A side nobody sells below 100% exits 6 no_liquidity and says when the game is in progress or over; a game in progress fills at Kalshi's price only. Signed out it exits 4 and says what trading needs. With --live or --poly-intl the buy is a live order, sent with your own venue credentials, saved in Settings: real money at the venue, under the venue's rules and terms. Arena is not a broker and never holds your funds. A live buy needs a price you set yourself, --max-price or --limit: in CLI 0.6.1 every live buy and its dry run exit 2 without one, and in 0.6.0 you must pass one yourself. |
| arena sell [trade-id] [--venue best|kalshi] [--min-price CENTS] [--limit CENTS] [--tif gtc|ioc|fok] [--expires 4h] [--idempotency-key UUID] [--yes] [--dry-run] | Close a position at the live bid, or rest a limit exit with --limit. --min-price is the least a market sell takes; below it nothing is sold. --dry-run shows the proceeds and the P&L. Trade ids come from arena positions. --idempotency-key on a sell is echoed in the receipt and becomes a limit exit's client_order_id; the server does not replay a market sell by key, so a market sell run again after it went through answers already_closed (check arena positions). --venue best, the default, lets the exit take another venue's bid when it passes Arena's checks. Today that only happens for a position filled on Novig or Polymarket, which can exit on that venue or on Kalshi. --venue kalshi sells at Kalshi's bid. From CLI 0.6.0, with no trade id and exactly one open position, a dry run prices that one and a person is asked before it is sold; with --yes and no id, or several positions, it exits 2 with each position's dry run in help[]. The dry run also says what selling pays now next to what keeping it pays (pays). |
| arena orders [--status resting|executed|canceled|all] [--ticker T] [--limit 200] | Your limit orders. Resting by default: the ones still holding money and still able to fill on their own. --ticker takes the market's ticker. With --json, or --fields naming them, rows also carry instrument_id, instrument_label and instrument_side. From CLI 0.6.0 every row carries them and bet, the bet in words, which the default columns show in place of the ticker, and --top works as --limit. |
| arena cancel <order-id> [--yes] | Withdraw a resting order and get its hold back. Cancelling one that already filled is not an error: it answers reducedBy 0 and the trade id. --json answers { order, reducedBy, releasedHoldDollars, canceled }. |
| arena settle | Settle anything already resolved. A backstop: the server settles automatically within about five minutes. |
| arena positions [--no-live] | Open positions marked to live prices, with unrealised P&L. Prices are whole cents (entry_price_cents, markPriceCents) and money is paper dollars (stake_dollars, unrealizedPnlDollars): the server’s snake_case columns sit next to the CLI’s camelCase marks. --no-live skips the market reads and the P&L column. With --json, or --fields naming them, rows also carry instrument_id, instrument_label and instrument_side; --json leaves out internal columns unless --raw. From CLI 0.6.0 rows carry bet (the bet in words, such as "PHI ML (PHI at CHI)"), paid_pct and now_pct, and the default columns show them in place of the ticker, which stays in --json. Read with an API key (--via api), rows carry id (the trade id) and bet too. |
| arena balance | Your balance (balanceDollars), net worth, open stake, P&L since your last reset (pnlSinceResetDollars) and lifetime P&L (lifetimePnlDollars, the total since Sep 1, 2026 across every reset), with the run you are on. The short names (balance, netWorth, pnlSinceReset, lifetimePnl) are aliases with the same values. |
| arena history [--window 30d|90d|all] [--since 30d|YYYY-MM-DD] [--until YYYY-MM-DD] [--run current] [--limit n] | Your settled trades, every one by default; from CLI 0.6.0 --limit n (or --top n) keeps the newest n, and the realized P&L still counts every one in the view. --since and --until take a day in America/New_York; --run current keeps what closed since your last reset. A position written off by a reset shows close_reason account_reset. resolved_price_cents is the price the trade closed at, for the side held: the sale price (1 to 99) for a sold trade, 100 or 0 for a win or a loss, and empty for a position written off by a reset. Instrument columns as for arena positions. From CLI 0.6.0 rows carry bet and result (won, lost, push, sold early), newest close first. |
| arena stats [--window 30d|90d|all] [--since …] [--until …] [--run current] [--by league|band|hold] | Your trading since Sep 1, 2026 graded, or a window, a date range or your current run: win rate against the prices you paid, calibration by price band, closing-line value, return on stake and drawdown. --by adds a breakdown by league, entry price band or how the trade ended. With no closed trades it prints zero counts and an emptyReason, and when every closed trade was sold it says that only settled trades are graded. arena stats --help defines each field, acs among them. From CLI 0.6.0: max_drawdown_dollars beside the percent, and summary, a sentence on how your bets ended and how many settled bets the calibration score still needs. P&L here is before fees; fee_drag_est_dollars is what Kalshi's taker fee would have cost, at entry on settled bets and (from CLI 0.6.0) at entry and at the sale on bets sold early. Needs arena login. |
| arena reset [--forfeit-open] [--reason TEXT] [--dry-run] | Start a new run at $100,000. It shows the preview first, and in a terminal you type RESET. Turn resets on in Settings first; one reset every 30 days, shown on a public profile. Resting orders are cancelled; open positions stop the reset unless --forfeit-open writes them off as losses. Scripts and agents cannot reset unless a person sets ARENA_ALLOW_RESET=1 or runs arena config set allowReset true in a terminal. |
| arena config [get|set] [key] [value] | Your CLI preferences: defaultFormat, confirmTrades, allowReset, and from CLI 0.6.0 webLinks (the "See it on the web" line) and openBrowser (whether arena open opens a browser). Setting allowReset to true works only in a terminal. |
| arena picks [--limit n] | Today’s AI picks. From CLI 0.6.0 rows carry bet, the pick in words, and price_pct (the price of the side picked), it works signed out, and --limit n keeps the first n. |
| arena leaderboard [--window 30d|90d|all] [--ai] [--kind people|ai] [--top 25] | The leaderboards (from CLI 0.6.0, --kind ai is --ai). 30d (the default, as on the website) and 90d rank trades that closed in the window; all ranks total profit since Sep 1, 2026 across every reset, for accounts with 20 closed trades and a first trade 30 or more days ago. --ai shows the AI models. Rows carry pnl, tradesClosed, winPct (0 to 100), resets and busts. Works signed out. From CLI 0.6.0, when no person qualifies it shows the AI models' board for the window, with kind: ai, asked: people and a note on how to appear, and --limit works as --top. |
| arena trader <ref> [picks] | A published trader record: the all-time standing and every reset. Lifetime P&L carries lifetimePnlAsOf: the daily snapshot it is as of, or live for your own account. Accounts are private by default: a private account answers its name and private: true and nothing else (exit 0; from CLI 0.6.0, traderId is null), and your own account is always your full record. Works signed out. |
| arena privacy [status|on|off] | Your account's privacy. Accounts are private by default: others see none of your trades, balance, rank or record until you make your profile public in Settings on the website. arena privacy shows the state; arena privacy on makes the account private (a y/N question in a terminal, --yes for scripts and agents). arena privacy off changes nothing: only a person can make an account public, in Settings, so it prints where (and exits 6, privacy_off_needs_person, in agent mode). |
| arena keys [list|create|rotate|revoke|check] | Your API keys, with your own login (CLI 0.6.0 or newer; Settings on the website works now). create --save FILE writes a new key with mode 0600, never through a symlink, and checks the file before the key is made; it never replaces a file unless you add --force, which writes a new 0600 file in its place. rotate <prefix> --grace 10m replaces one and keeps the old one working for the grace; revoke <prefix> --yes; check says whose account a key reads and your plan's counts. A key reads unless made with create --trade, which adds trade:write (paper orders through POST /portfolio/orders) and asks first at a terminal. Keys made here expire in 90 days unless --expires says otherwise. For an agent, create and rotate need --yes and --save or --print-token, so a key never lands in its output unasked. After create --save FILE the next steps name the file, never the key: arena keys check --key-file FILE and arena eval --key-file FILE. --json gives scopes as a list in every keys answer. |
| arena open [page] [ref], arena dashboard | Run arena open to see it on the web (CLI 0.6.0 or newer). arena open and arena dashboard are your portfolio; positions, orders, history, trade <id>, stats, eval, settings privacy|reset|keys, profile, market <ref> (an ins_ id, a slug, a Kalshi ticker or a bet in words), game <ref>, trader <name>, ai <model>, leaderboard [--window 30d] [--ai], docs [topic] and pro open those pages. It opens a browser only for a person at a terminal, not over SSH or in CI; an agent gets {open: {url, audience, opened: false}} and nothing opens, with resolvedId and resolvedLabel when a bet, a game or a short trade id was looked up. audience: owner is a page only its owner sees, not a link to share. profile is your public profile page (audience: public), and its note says what others see there: your name and that the account is private, or your record once you make it public. --print prints the bare link; with --json it is the same {open} answer. It never takes a URL (exit 2); profile signed out is exit 4; several matching bets are exit 6, ambiguous, with their ids in help[]. Answers that have a page carry webUrl; shareUrl is the link that is safe to send to someone else, null unless the profile is public. |
| arena eval [--window 30d|90d|all] [--key-file FILE] [--picks] [--subject predictions|account] [--predictions FILE] | Your own settled picks scored the way the eval board scores them, picks placed while private included (CLI 0.6.0 or newer): Brier score and log loss of your --prob where you gave one and of the entry price otherwise (forecastBasis says which), the same two over your stated picks alone, P&L after the fee Kalshi would charge (paper trades pay none), the largest fall of that P&L in dollars, and calibration bands; --picks lists the picks. It reads with a key given on purpose (--key-file, ARENA_API_KEY or ARENA_API_KEY_FILE, through GET /api/v1/me/eval), else with your login (the same rows), else with the key in ~/.config/arena/arena.key (the file arena keys create --save ~/.config/arena/arena.key writes, which the MCP server and the Python SDK read too); with none of them it exits 4 with key_required and help[] is arena login. arena stats grades your --prob with your login. --subject predictions with --predictions FILE (or --subject account) saves a model or account eval through POST /api/v1/evals and prints the run id. That run shows on the Design tab. |
| arena evalboard [--window 30d|90d|all] [--kind all|ai|human] [--cohort all|same_games] [--min-scored 20] | The public eval board (CLI 0.6.0 or newer), signed out: people and AI models scored on their settled public picks, ranked inside their basis (log loss, closing-line value or edge), never by profit, with the market’s own Brier and log loss beside each row and 95% intervals clustered by game. ≈ (tied_with_above in JSON) marks a rank the sample does not separate from the one above. The same board as /eval and GET /eval. |
| arena record <trader> [--verify] | A trader’s Ed25519-signed track record over the last 90 days (CLI 0.6.0 or newer), signed out. --verify checks the signature against the key Arena publishes at /.well-known/arena-track-record-keys.json, never a key the record names; a record that does not verify exits 6 record_not_verified with what failed. The record is exactly what Arena signed, *_points keys included, so a saved copy verifies on its own: --json has it as record, and agent mode and --fields signed_record carry it as one JSON string. At a terminal its numbers read in words, the drawdown as a share of the peak it fell from. |
| arena backtest [--source holdings|series|kalshi] [--rule FILE|--predictions FILE|--csv FILE|--body FILE] [--name NAME] [--model MODEL] | A paper replay, saved on your account when you are signed in or an API key is set. Prints the run id. The run shows on the Design tab (arena open design). |
| arena runs [--kind backtest|eval] [--mode paper|live] [--source cli|mcp|api|web] [--limit N] [--cursor C] [show <id>] | Saved backtests and evals, newest first. arena runs show <id> prints one run, including its workflow. |
| arena watch <ticker…> [--orderbook] | Live prices, repainted in place. Not yet shipped |
| arena hook install | Install the ambient-context hook into your agent runner. Not yet shipped |
Flags that work everywhere
- --json
- JSON on stdout. Works in both modes, on every command. The stable escape hatch. An error is JSON on stdout too:
{"error": {"code", "message", "exitCode", "retryable", "help"}}, wherecodeuses the MCP server's words:invalid_request,confirmation_required,not_authenticated,subscription_required,insufficient_balance,not_found,market_resolved,market_not_open,no_liquidity,already_closed,price_moved,idempotency_key_reused,order_canceled,forbidden,bot_account,privacy_off_needs_person,not_available(the server lacks the feature yet, exit 6),not_implemented(a command this CLI does not have yet, exit 2),key_required(from 0.6.0:arena evalwith no API key, exit 4, which a login does not fix),rate_limited,upstream_unavailable,internal_error, and a reset's own refusal codes; the CLI addsambiguous(from 0.6.0: several bets or trades match, and their ids are inhelp). The list may grow: treat an unknown code by its exit code. - --fields a,b,c
- Widen a list beyond its default three or four columns, or, with
--json, print only those fields. With--json,--fieldsprints the flat document the agent format shows (the rows or the object under its key, then the scalars andhelp[]) instead of the command's own--jsonshape, such as{ receipt, trade }. - --full
- Stop truncating long text. Paths, URLs and ids are never cut.
- --raw
- On
buy,sell,positions,history,ordersandcancel, with--json: the rows as the server sent them, internal columns included. - --yes
- Skip the confirmation prompt. In agent mode
buy,sellandcancelneed it, and without it they exit 2 and send nothing.arena settledoes not, because it only settles trades whose markets have already resolved. - --dry-run
- On
buy: the price and the stake, and where the fill would be priced now under--venue best, with nothing sent. From CLI 0.6.0 it shows no fee estimate:arena market <ref> --contracts Nshows the estimated venue fee (not charged on paper). Itshelp[]names the command that places it, with the shown price as--max-price: the exact order for anins_id or a bet in words, and<ticker>in place of a Kalshi ticker, which Arena never repeats. On a marketsellunder--venue best: where the exit would be priced now (sellPriceCents,venue,kalshiPriceCents), the proceeds (proceedsDollars) and the P&L (pnlDollars). With--venue kalshi, or when that check is not available, it prices the exit at Kalshi's live bid (CLI 0.5.0); an older CLI shows only the position it would sell (ticker, side, contracts), with no price, so readyes_bidorno_bidwitharena market <ticker>for that.cancelhas no dry run.
| Flag | What it does |
|---|---|
| --json | JSON on stdout. Works in both modes, on every command. The stable escape hatch. An error is JSON on stdout too: {"error": {"code", "message", "exitCode", "retryable", "help"}}, where code uses the MCP server's words: invalid_request, confirmation_required, not_authenticated, subscription_required, insufficient_balance, not_found, market_resolved, market_not_open, no_liquidity, already_closed, price_moved, idempotency_key_reused, order_canceled, forbidden, bot_account, privacy_off_needs_person, not_available (the server lacks the feature yet, exit 6), not_implemented (a command this CLI does not have yet, exit 2), key_required (from 0.6.0: arena eval with no API key, exit 4, which a login does not fix), rate_limited, upstream_unavailable, internal_error, and a reset's own refusal codes; the CLI adds ambiguous (from 0.6.0: several bets or trades match, and their ids are in help). The list may grow: treat an unknown code by its exit code. |
| --fields a,b,c | Widen a list beyond its default three or four columns, or, with --json, print only those fields. With --json, --fields prints the flat document the agent format shows (the rows or the object under its key, then the scalars and help[]) instead of the command's own --json shape, such as { receipt, trade }. |
| --full | Stop truncating long text. Paths, URLs and ids are never cut. |
| --raw | On buy, sell, positions, history, orders and cancel, with --json: the rows as the server sent them, internal columns included. |
| --yes | Skip the confirmation prompt. In agent mode buy, sell and cancel need it, and without it they exit 2 and send nothing. arena settle does not, because it only settles trades whose markets have already resolved. |
| --dry-run | On buy: the price and the stake, and where the fill would be priced now under --venue best, with nothing sent. From CLI 0.6.0 it shows no fee estimate: arena market <ref> --contracts N shows the estimated venue fee (not charged on paper). Its help[] names the command that places it, with the shown price as --max-price: the exact order for an ins_ id or a bet in words, and <ticker> in place of a Kalshi ticker, which Arena never repeats. On a market sell under --venue best: where the exit would be priced now (sellPriceCents, venue, kalshiPriceCents), the proceeds (proceedsDollars) and the P&L (pnlDollars). With --venue kalshi, or when that check is not available, it prices the exit at Kalshi's live bid (CLI 0.5.0); an older CLI shows only the position it would sell (ticker, side, contracts), with no price, so read yes_bid or no_bid with arena market <ticker> for that. cancel has no dry run. |
A first session
Five commands to paste in order once `arena login` has run: today’s games with each team’s chance, a bet in words, its price on each venue and what $100 on it pays, what $100 on it would buy (`--dry-run` places nothing), and your portfolio on the website. Each contract pays $1 if its side wins. Steps 2 and 3 take the bet in words; step 4 takes the `ins_` id step 2 prints. The ids, prices and times below are made up (samples elsewhere on this page use the placeholder tickers ARENA-EXAMPLE-1 and ARENA-EXAMPLE-2).
# CLI 0.6.0 output. 0.5.0 lists the NFL's upcoming games and prints wider tables.
$ arena games
NFL (2)
AWAY HOME STARTS CHANCE TO WIN
Pittsburgh Steelers Cleveland Browns Thu Oct 1, 8:15 PM EDT PIT 57.5%, CLE 42.5%
Indianapolis Colts Washington Commanders Sun Oct 4, 9:30 AM EDT IND 48%, WAS 52%
(left out GAME ID to fit the terminal: widen it, or pick columns with --fields)
pricesFrom Kalshi
Next:
arena game game_0A1B2C3D4E
arena resolve "browns ml"
arena games --date 2026-10-02
$ arena resolve "steelers ml"
LABEL STARTS ID YOUR SIDE WHAT IT MEANS
PIT ML Thu Oct 1, 8:15 PM EDT ins_0123456789AB yes The Pittsburgh Steelers win against the Cleveland Browns. A tie settles at 50.
(left out TICKER, BUY SIDE to fit the terminal: widen it, or pick columns with --fields)
Next:
arena market ins_0123456789AB
arena buy ins_0123456789AB --side yes --stake 100 --dry-run
$ arena quotes "steelers ml"
VENUE SIDE CHANCE SELL YES AT BUY YES AT LAST NOTE
Kalshi PIT ML 57.5% 57% 58% 58%
Polymarket PIT ML 57.5% 57% 58% Arena has not yet compared Polymarket's rules on ties, postponements and settlement with Kalshi's. Matched by rule. Not yet reviewed by a person.
(left out AS OF to fit the terminal: widen it, or pick columns with --fields)
instrument_id ins_0123456789AB
label PIT ML
query_means The Pittsburgh Steelers win against the Cleveland Browns. A tie settles at 50.
starts_at Thu Oct 1, 8:15 PM EDT
pays $100.00 on PIT ML buys 172 contracts at 58¢ ($99.76). If PIT wins, they pay $172.00 (+$72.24); if not, you lose $99.76.
Next:
arena quotes ins_0123456789AB
arena instrument ins_0123456789AB
arena market ins_0123456789AB
$ arena buy ins_0123456789AB --side yes --stake 100 --dry-run
arena: $100.00 at the 58% Kalshi ask buys 172 contracts ($99.76). The fill is capped at 58%.
bet PIT ML (Pittsburgh Steelers at Cleveland Browns)
starts Thu, Oct 1, 8:15 PM EDT
you buy 172 contracts at 58% (58¢ a contract)
cost $99.76
pays If it wins, 172 contracts pay $172.00 (+$72.24). If it loses, you lose $99.76.
fills on Best price: Kalshi 58%
placed nothing yet (a dry run): the command under Next places it
note Paper trades pay no fee. The eval board's grades subtract the fee Kalshi would charge.
more every field: add --verbose
Next:
arena buy ins_0123456789AB --side yes --stake 100 --max-price 58
arena positions
# arena open: CLI 0.6.0. On 0.5.0, open https://arena-predictions.com/portfolio in your browser.
$ arena open
Opening https://arena-predictions.com/portfolio
url https://arena-predictions.com/portfolio
audience owner
opened true
note Only you can see this page. The website asks you to sign in once if this browser isn't signed in to Arena.Placing the buy
Without --dry-run the buy is a market order: you never set a price, and the fill happens server-side at the live ask, which is why the CLI reconciles the actual fill against the quote and tells you the slippage. From CLI 0.5.0, --venue best is the default: before the prompt the CLI shows where the fill would be priced now, which can be Novig’s or Polymarket’s price for the same bet when it passes Arena’s checks, and the receipt adds the venue (venue). --venue kalshi keeps Kalshi’s ask. --max-price is the most you will pay: the server fills at that price or better, or not at all, and with --stake the order is sized at that price, so it never costs more than the stake. And every buy carries an idempotency key, fresh unless you pass your own with --idempotency-key. If the request gets no answer, the CLI retries it with that same key, which can never fill twice. Do not resend the buy with a new key: that can open a second position. Check arena positions first, or run the same command again with the same --idempotency-key, which answers the trade already placed (marked replayed).
Naming your own price
Pass --limit to place a limit order instead. It fills only at that price or better, and otherwise rests until it fills, reaches --expires, or you cancel it. A resting buy holds the cost out of your balance and returns it in full on cancel; a resting sell holds nothing, because the position is the collateral. --tif ioc or fok never rests. See your resting orders with arena orders and withdraw one with arena cancel.
Packages
Four packages on npm, a Python SDK, one machine-readable spec, one file for language models.
- arena-prediction-cli 0.6.1
- The arena command: paper trading from a terminal, dual-mode for agents (AXI).
- arena-mcp-server 0.6.1
- The local stdio MCP server, run with npx -y arena-mcp-server. Reads the CLI’s stored login.
- arena-mcp-tools 0.6.1
- The tool catalog the server registers, for embedding in another MCP host.
- arena-core 0.5.1
- Auth, endpoints, credential storage and the typed API client the three share.
- arena-predictions 0.3.0 (Python)
- The Python SDK, from PyPI:
pip install arena-predictions. It only reads, and it places no trades: the resolver, instruments and games, quotes and price gaps across venues, the eval board, the public records and, with an API key, your own account, withto_frame()for pandas. A Python bot trades through the CLI or the MCP server.
| Package | What it is |
|---|---|
| arena-prediction-cli 0.6.1 | The arena command: paper trading from a terminal, dual-mode for agents (AXI). |
| arena-mcp-server 0.6.1 | The local stdio MCP server, run with npx -y arena-mcp-server. Reads the CLI’s stored login. |
| arena-mcp-tools 0.6.1 | The tool catalog the server registers, for embedding in another MCP host. |
| arena-core 0.5.1 | Auth, endpoints, credential storage and the typed API client the three share. |
| arena-predictions 0.3.0 (Python) | The Python SDK, from PyPI: pip install arena-predictions. It only reads, and it places no trades: the resolver, instruments and games, quotes and price gaps across venues, the eval board, the public records and, with an API key, your own account, with to_frame() for pandas. A Python bot trades through the CLI or the MCP server. |
Specifications
GET https://arena-predictions.com/api/v1/openapi.json is the OpenAPI 3.1 document for the live endpoints; /llms.txt is the short version for a language model. The CLI and the MCP server call the same backends the website does, through arena-core. The Python SDK, arena-predictions, reads the HTTP API and the public reads and places no trades.