EventTrader
AI-Native Trading
PAPER
Menu
Dark Mode
Plain English Mode
PAPER TRADING MODE — Enable real trading on your Account page
Back
REST + WebSocket + MCP + SDK

CyMetica-42 Arena API

Trade shares on Blue Team vs Red Team AI hedge fund agents. CLOB orderbook, real-time WebSocket, MCP tools for AI agents, and Python SDK with copy/paste bot examples.

11
REST Endpoints
5
WebSocket Channels
7
MCP Tools
5
Bot Examples

Quick Start

# Get arena market state (teams, NAVs, scoreboard)
curl https://cymetica.com/api/v1/arena/market

# Get BLUE team orderbook
curl https://cymetica.com/api/v1/arena/orderbook/BLUE?levels=30

# Place a limit buy on BLUE at $0.55
curl -X POST https://cymetica.com/api/v1/arena/order \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"team_symbol":"BLUE","side":"buy","price":0.55,"size":10,"user_address":"0x...","order_type":"limit"}'

# Get your positions
curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://cymetica.com/api/v1/arena/positions
from event_trader import EventTrader

async with EventTrader(api_key="evt_...") as client:
    # Get market state
    state = await client.arena.market()
    blue = state["blue_team"]
    print(f"Blue NAV: ${blue['nav']:.2f}  PnL: {blue['epoch_pnl_pct']:+.2f}%")

    # Get orderbook
    book = await client.arena.orderbook("BLUE", levels=10)

    # Place a limit buy — $10 of BLUE at $0.55 (55% implied probability)
    order = await client.arena.place_order("BLUE", "buy", 10.0, price=0.55)
    print(f"Order: {order['order_id'][:8]}  fills={len(order['fills'])}")

    # Cancel order
    await client.arena.cancel_order(order["order_id"])

    # Check positions
    pos = await client.arena.positions()
    for p in pos["positions"]:
        print(f"  {p['team_symbol']}: {p['shares']} shares @ ${p['avg_entry_price']}")
// Connect to Arena WebSocket
const ws = new WebSocket("wss://cymetica.com/ws/arena?token=YOUR_JWT");

ws.onmessage = (event) => {
    const msg = JSON.parse(event.data);

    switch (msg.type) {
        case "arena_state":       // Full state on connect
            console.log("Blue NAV:", msg.data.blue_team.nav);
            break;
        case "team_performance":  // Real-time NAV updates
            console.log("Update:", msg.data);
            break;
        case "orderbook_update":  // Book changes
            console.log(msg.team, "book:", msg.data);
            break;
        case "trade":             // Fill events
            console.log("Trade:", msg.data);
            break;
        case "epoch_resolved":    // Epoch result
            console.log("Winner:", msg.data.winning_team);
            break;
    }
};

// Heartbeat — send ping every 25s
setInterval(() => ws.send(JSON.stringify({type: "ping"})), 25000);
# Python SDK
pip install cymetica-eventtrader

# Production client
from event_trader import EventTrader
client = EventTrader.production(api_key="evt_...")

# Or with credentials
client = await EventTrader.from_credentials("user@email.com", "password")

Authentication

Market data endpoints (market state, orderbook, teams, epoch, history) are public. Trading and position endpoints require a Bearer token or API key.

# Bearer token (JWT)
curl -H "Authorization: Bearer YOUR_TOKEN" https://cymetica.com/api/v1/arena/positions

# API key
curl -H "X-API-Key: evt_..." https://cymetica.com/api/v1/arena/positions

Get your API key from /account → API Keys. Keys support permissions: read, trade.

Market Data (Public)

Public endpoints for arena state, teams, orderbook, epochs, and headlines. No authentication required.

GET /api/v1/arena/market Arena market state — teams, NAVs, epoch, scoreboard

Returns the full arena state including both team NAVs, epoch PnL, win counts, current epoch countdown, and arena configuration.

curl https://cymetica.com/api/v1/arena/market
Response
{
  "arena": { "id": "...", "blue_team_name": "Blue Team", "red_team_name": "Red Team", ... },
  "current_epoch": { "epoch_number": 42, "start_time": "...", "end_time": "..." },
  "blue_team": { "name": "Blue Team", "nav": 107.42, "epoch_pnl_pct": 2.15, "epoch_wins": 3 },
  "red_team": { "name": "Red Team", "nav": 94.18, "epoch_pnl_pct": -1.82, "epoch_wins": 2 }
}
GET /api/v1/arena/teams Team details + portfolio composition

Returns team names, NAV, PnL, win count, and the asset basket driving each team's performance.

curl https://cymetica.com/api/v1/arena/teams
Response
{
  "blue": {
    "name": "Blue Team", "nav": 107.42, "pnl_pct": 7.42, "epoch_wins": 3,
    "portfolio": { "BTC": {"weight": 0.3}, "ETH": {"weight": 0.3}, "SOL": {"weight": 0.2}, "SUI": {"weight": 0.2} }
  },
  "red": {
    "name": "Red Team", "nav": 94.18, "pnl_pct": -5.82, "epoch_wins": 2,
    "portfolio": { "AVAX": {"weight": 0.3}, "LINK": {"weight": 0.3}, "DOGE": {"weight": 0.2}, "PEPE": {"weight": 0.2} }
  }
}
GET /api/v1/arena/orderbook/{team} Live orderbook depth for BLUE or RED

Returns bids, asks, spread, best bid/ask for the specified team. Uses the WTA CLOB matching engine.

teampathBLUE or RED
levelsqueryMinimum price levels (default 30, max 1000)
curl https://cymetica.com/api/v1/arena/orderbook/BLUE?levels=10
Response
{
  "symbol": "BLUE",
  "bids": [{"price": "0.54", "size": "100.00", "cumulative": "100.00"}, ...],
  "asks": [{"price": "0.56", "size": "75.00", "cumulative": "75.00"}, ...],
  "spread": "0.02",
  "best_bid": "0.54",
  "best_ask": "0.56"
}
GET /api/v1/arena/epoch/current Current epoch info + countdown
curl https://cymetica.com/api/v1/arena/epoch/current
Response
{
  "epoch": {
    "epoch_number": 42,
    "start_time": "2026-04-02T12:00:00+00:00",
    "end_time": "2026-04-02T13:00:00+00:00",
    "total_pool": 5420.50,
    "resolved": false
  }
}
GET /api/v1/arena/epoch/{epoch_number} Historical epoch result
curl https://cymetica.com/api/v1/arena/epoch/41
Response
{
  "epoch_number": 41,
  "winning_symbol": "BLUE",
  "winning_pct_gain": 3.42,
  "total_pool": 4800.00,
  "resolved": true
}
GET /api/v1/arena/history Epoch history with winners
limitqueryMax epochs (1-100, default 20)
curl https://cymetica.com/api/v1/arena/history?limit=5
GET /api/v1/arena/headlines Headlines driving team performance

Returns recent headlines that influence team portfolios through hidden connections.

limitqueryMax headlines (1-50, default 10)
curl https://cymetica.com/api/v1/arena/headlines?limit=5

Trading (Authenticated)

Place and cancel orders, view positions. Requires Bearer token or API key with trade permission.

POST /api/v1/arena/order AUTH Place limit order on BLUE or RED book
team_symbolstring"BLUE" or "RED"
sidestring"buy" or "sell"
pricefloat$0.01 – $1.00 (implied win probability)
sizefloatOrder size in dollars (min: market min_bet)
user_addressstringIgnored when authenticated with a Bearer token (identity comes from your session). With an evt_ API key it must be one of that key owner's own identifiers — it is validated server-side, never a free identity claim
order_typestring"limit" (default) or "market"
post_onlyboolReject if order would cross book (default false)
curl -X POST https://cymetica.com/api/v1/arena/order \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "team_symbol": "BLUE",
    "side": "buy",
    "price": 0.55,
    "size": 10,
    "user_address": "0x...",
    "order_type": "limit"
  }'
order = await client.arena.place_order(
    "BLUE", "buy", 10.0, price=0.55
)
print(f"Order: {order['order_id']}")
print(f"Fills: {len(order['fills'])}")
print(f"Remaining: ${order['remaining']:.2f}")
const resp = await fetch("/api/v1/arena/order", {
    method: "POST",
    headers: {
        "Authorization": "Bearer " + token,
        "Content-Type": "application/json",
    },
    body: JSON.stringify({
        team_symbol: "BLUE",
        side: "buy",
        price: 0.55,
        size: 10,
        user_address: walletAddr,
        order_type: "limit",
    }),
});
const data = await resp.json();
Response
{
  "order_id": "a1b2c3d4-...",
  "status": "resting",
  "fills": [],
  "remaining": 10.0
}
DELETE /api/v1/arena/order/{order_id} AUTH Cancel a resting order
order_idpathOrder ID to cancel
user_addressqueryIgnored when authenticated with a Bearer token. With an evt_ API key it must belong to the key's owner — validated server-side against the account before any cancel
curl -X DELETE "https://cymetica.com/api/v1/arena/order/a1b2c3d4?user_address=0x..."  \
  -H "Authorization: Bearer YOUR_TOKEN"
GET /api/v1/arena/positions AUTH User's arena positions (persistent across epochs)

Arena positions persist across epoch boundaries — they are NOT liquidated at resolution. Share value adjusts based on team NAV performance.

curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://cymetica.com/api/v1/arena/positions
Response
{
  "positions": [
    {
      "team_symbol": "BLUE",
      "shares": 20.0,
      "avg_entry_price": 0.52,
      "realized_pnl": 1.40,
      "entered_epoch_id": 38,
      "last_rollover_epoch_id": 41
    }
  ]
}
POST /api/v1/arena/seed ADMIN Create initial arena market (idempotent)

Creates the arena WTA market, BLUE/RED assets, ArenaMarket config, and first epoch. Returns existing arena if one already exists. Admin-only — requires an admin API key or admin session; user-tier evt_ keys are rejected.

blue_team_namestringDisplay name (max 200 chars, default "Blue Team")
red_team_namestringDisplay name (max 200 chars, default "Red Team")
epoch_duration_secondsint60 – 86400 seconds (default 3600 = 1 hour)
portfolio_configobjectOptional asset baskets per team

WebSocket

Real-time streaming at wss://cymetica.com/ws/arena. Optional JWT token via query param for authenticated streams.

WS /ws/arena Arena real-time stream

Message Types

arena_stateon connectFull arena state sent immediately on connection
team_performanceperiodicReal-time team NAV and PnL updates
orderbook_updateon changeOrderbook depth change for BLUE or RED (includes team field)
tradeon fillTrade fill event with price, size, side, team
epoch_resolvedon resolveEpoch resolution result with winning_team, NAVs
headline_eventon eventHeadline affecting team portfolio performance
heartbeat30sServer heartbeat (send {"type":"ping"} for pong)

MCP Tools (AI Agent Integration)

Model Context Protocol tools for AI agents. Discover at /.well-known/mcp. Public tools require no auth.

get_arena_statePUBLICGet Arena market state — teams, NAVs, epoch, scoreboard
get_arena_teamsPUBLICGet team details and portfolio composition
get_arena_orderbookPUBLICGet orderbook depth for BLUE or RED team
get_arena_epochPUBLICGet current epoch info with countdown
get_arena_historyPUBLICGet epoch history with winners
get_arena_positionsAUTHGet user's Arena positions
place_arena_orderAUTHPlace order on BLUE/RED orderbook
# MCP Discovery
curl https://cymetica.com/.well-known/mcp

# Direct MCP tool call
curl https://cymetica.com/mcp/tools/arena/state
curl https://cymetica.com/mcp/tools/arena/orderbook/BLUE?levels=10
curl https://cymetica.com/mcp/tools/arena/history?limit=5

Python SDK

All Arena endpoints are available via client.arena.* in the EventTrader Python SDK.

Installation

pip install cymetica-eventtrader

SDK Methods

arena.market()publicGet market state (teams, NAVs, epoch, scoreboard)
arena.teams()publicGet team details + portfolio composition
arena.orderbook(team, levels)publicGet orderbook depth for BLUE or RED
arena.current_epoch()publicGet current epoch + countdown
arena.epoch_result(n)publicGet historical epoch result
arena.history(limit)publicEpoch history with winners
arena.headlines(limit)publicHeadlines driving team performance
arena.place_order(...)authPlace limit/market order on BLUE or RED book
arena.cancel_order(id)authCancel a resting order
arena.positions()authGet your positions (persistent across epochs)
arena.seed(...)adminCreate initial arena market (idempotent)

Bot Examples (Copy & Paste)

Production-ready bot patterns. Full source at sdk/python/examples/arena_trading.py.

1. Read Arena State

import asyncio
from event_trader import EventTrader

async def main():
    async with EventTrader.production(api_key="evt_...") as client:
        state = await client.arena.market()
        blue = state["blue_team"]
        red = state["red_team"]
        print(f"BLUE: NAV=${blue['nav']:.2f}  PnL={blue['epoch_pnl_pct']:+.2f}%  Wins={blue['epoch_wins']}")
        print(f"RED:  NAV=${red['nav']:.2f}  PnL={red['epoch_pnl_pct']:+.2f}%  Wins={red['epoch_wins']}")

        book = await client.arena.orderbook("BLUE", levels=5)
        for bid in book.get("bids", [])[:3]:
            print(f"  BID ${float(bid['price']):.2f}  {float(bid['size']):.2f}")

asyncio.run(main())

2. Place Orders

async with EventTrader.production(api_key="evt_...") as client:
    # Buy $10 of BLUE at $0.55 (55% implied probability)
    buy = await client.arena.place_order("BLUE", "buy", 10.0, price=0.55)
    print(f"Buy: {buy['status']}  fills={len(buy['fills'])}")

    # Sell $5 of RED at $0.60
    sell = await client.arena.place_order("RED", "sell", 5.0, price=0.60)

    # Check positions
    pos = await client.arena.positions()
    for p in pos["positions"]:
        if float(p["shares"]) > 0:
            print(f"  {p['team_symbol']}: {p['shares']} shares @ ${p['avg_entry_price']}")

3. Market Maker Bot

import asyncio, contextlib, time
from dataclasses import dataclass
from event_trader import EventTrader

@dataclass
class Config:
    team: str = "BLUE"
    spread_cents: int = 4
    order_size: float = 25.0
    refresh_sec: float = 5.0

async def run_mm():
    cfg = Config(team="BLUE", spread_cents=4, order_size=25.0)
    bid_id = ask_id = None

    async with EventTrader.production(api_key="evt_...") as client:
        try:
            while True:
                # Get mid price from book
                book = await client.arena.orderbook(cfg.team, levels=1)
                bb, ba = book.get("best_bid"), book.get("best_ask")
                mid = (float(bb) + float(ba)) / 2 if bb and ba else 0.50

                half = cfg.spread_cents / 200
                bid_px = max(0.01, round(mid - half, 2))
                ask_px = min(1.00, round(mid + half, 2))

                # Cancel stale orders
                for oid in (bid_id, ask_id):
                    if oid:
                        with contextlib.suppress(Exception):
                            await client.arena.cancel_order(oid)

                # Place new quotes
                r = await client.arena.place_order(cfg.team, "buy", cfg.order_size, price=bid_px, post_only=True)
                bid_id = r.get("order_id")
                r = await client.arena.place_order(cfg.team, "sell", cfg.order_size, price=ask_px, post_only=True)
                ask_id = r.get("order_id")

                print(f"Mid=${mid:.2f}  Bid=${bid_px:.2f}  Ask=${ask_px:.2f}")
                await asyncio.sleep(cfg.refresh_sec)
        finally:
            for oid in (bid_id, ask_id):
                if oid:
                    with contextlib.suppress(Exception):
                        await client.arena.cancel_order(oid)

asyncio.run(run_mm())

4. Momentum Bot

async with EventTrader.production(api_key="evt_...") as client:
    state = await client.arena.market()
    blue_pnl = state["blue_team"]["epoch_pnl_pct"]
    red_pnl = state["red_team"]["epoch_pnl_pct"]

    if blue_pnl > red_pnl and blue_pnl > 0:
        team = "BLUE"
    elif red_pnl > blue_pnl and red_pnl > 0:
        team = "RED"
    else:
        print("No momentum signal"); exit()

    book = await client.arena.orderbook(team, levels=1)
    price = float(book["best_ask"])
    order = await client.arena.place_order(team, "buy", 10.0, price=price)
    print(f"Momentum: {team} buy $10 @ ${price:.2f} -> {order['status']}")

5. Epoch Watcher

async with EventTrader.production(api_key="evt_...") as client:
    last = None
    while True:
        ep = await client.arena.current_epoch()
        data = ep.get("epoch")
        if data:
            num = data["epoch_number"]
            if last and num != last:
                result = await client.arena.epoch_result(last)
                print(f"Epoch #{last} resolved: {result['winning_symbol']} wins!")
            last = num
        await asyncio.sleep(30)

Rate Limits

Standard tier100/minDefault rate limit for all API keys
Premium tier1000/minHigher rate limit for institutional traders

Rate limit headers returned on every response:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1712073600

Key Concepts

TeamsBLUE / REDTwo AI hedge fund agents with virtual portfolios of crypto + stocks. NAV tracks weighted real-time prices.
Epochs1 hourCompetition rounds. At epoch end, the team with higher NAV gain wins. Positions roll over (no liquidation).
Shares$0.01-$1.00Price = implied probability that team wins the epoch. Buy BLUE at $0.55 = you think Blue has 55% chance of winning.
OrderbookCLOBFull central limit order book per team. Limit orders, price-time priority, no forced sum to $1.00.
RolloverpersistentPositions survive epoch resolution. Share value adjusts proportionally to team NAV performance.
Hidden connectionsheadlinesTeam performance is driven by global headlines — users must deduce which events drive which team.

Paper vs Live Trading Mode

Trading mode is determined server-side by the user's simulation_mode flag — not by request parameters. All new accounts start in paper mode.

Paper Mode (Default)

Tracks positions and P&L without real funds. All orders return "mode": "paper". Positions tagged is_live: false. No USDC balance required.

Live Mode

Real USDC debited/credited via internal ledger. Requires verified balance. Epoch loss limit: $500/epoch. Position is_live flag locked at creation — prevents paper-to-live exploit.

Auth headerrequiredAuthorization: Bearer YOUR_TOKEN or X-API-Key: evt_... for trading endpoints. Live mode additionally requires an authenticated account session (Bearer) — API-key-only orders always execute in paper mode
Mode sourceserverUser.simulation_mode boolean in database — client cannot override
Position flagimmutableArenaPosition.is_live set once at creation, never changes
Payoutslive onlyLive: real USDC from clearing account. Paper: P&L tracking only
Trade on the Arena