Arena for bots
Quick start
Section titled “Quick start”- Get your account’s own DEBYKO key from Account. Keep it in the server environment (for example
DEBYKO_ARENA_KEY), never a URL, source file or log. Requests go tohttps://arena.debyko.comwithAuthorization: Bearer …. - Set your participant profile first:
PUT /v1/profilewithhandle, optionalbio,declared_capital,capital_ccyandeffective_from(UTC). Declare capital before importing closed trades; see the conflict rule below. One account has one handle, shared with its Account public profile. - Report each position when it goes flat:
POST /v1/trades. Aggregate partial closes; keep hedge sides separate. Choose a deterministictrade_idand preserve it across retries. Include fees, signed funding, fill IDs/timestamps and the triggering Agent firing when known. - Keep open positions current:
POST /v1/positionsimmediately on every position change, plus every 60 seconds. Send{"account":"live","positions":[]}when flat. A snapshot stops being valued after 180 seconds without a replacement and shows as a gap. - See the record at
https://arena.debyko.com/<handle>. Trades appear under your participant, grouped by their per-tradelabel; use different labels for strategies, not separate handles. Public availability follows Arena’s launch setting; ingestion works while pages are restricted.
Contract and disclosure
Section titled “Contract and disclosure”This is a self-reported record of participants’ own trading, not execution, custody, investment advice or copy trading. Public figures are percentages only. Arena reads no balances and does not account for deposits or withdrawals. Capital is an explicit participant declaration, never an automatic balance measurement; changes rebase future returns only.
Arena OpenAPI 3.1 contract. The live user dbk_… key authenticates the full Arena API. A restricted key with arena:write permits only POST /v1/trades and POST /v1/positions; it cannot edit the profile, erase data, read paid market data or manage Agent rules. Issue it through the authenticated Account key API, POST /account/api/keys with {"label":"Runner Arena","scope":"arena:write"} and the normal session/CSRF protections; it is displayed once, owner-visible, revocable and keeps its scope on rotation. Omit scope for a normal user key. Agent/OAuth/Studio keys do not qualify.
Rate limit: 60 attempts/minute/key, shared with raw ingestion; 429 includes Retry-After: 60. Bodies are at most 64 KiB. Unknown fields are ignored. Decimal numbers accept JSON numbers or strings; strings preserve digits. Runnable curl/Python examples are below; no private repository access is required.
Profile and declared capital
Section titled “Profile and declared capital”GET /v1/profile returns the authenticated account’s handle, bio, declared_capital and capital_ccy. It has no side effects and is private, no-store. Capital is the latest declaration effective now, not a future scheduled value. Both capital fields are null when no declaration is active. A new account’s handle can be null. Like profile edits, this requires a full live user key, not an arena:write key.
On PUT, omitting declared_capital leaves its history untouched. Explicit null appends a clear event, effective at receipt or effective_from; it does not delete or update prior declarations. The same history-lock/conflict checks apply to amounts and clears. Repeat clears at receipt time do not add duplicate events. Historical periods retain their former basis; subsequent returns use trade ROE (or notional when margin/leverage is absent), with a mixed-basis label when applicable. Preserve the handle and bio returned by GET when changing only capital.
For example, {"handle":"my-bot","bio":"My own bot results","declared_capital":null} switches future results to no-capital mode. Merely removing a bot configuration value does not clear an existing declaration: the owner must request the explicit clear. Generated C# callers omit capital by leaving DeclaredCapital unset; to clear, set it to an explicit JSON-null JsonElement.
curl https://arena.debyko.com/v1/profile -X PUT \ -H "Authorization: Bearer $DEBYKO_ARENA_KEY" -H 'Content-Type: application/json' \ --data '{"handle":"my-bot","declared_capital":"20000","capital_ccy":"USD","effective_from":"2026-10-11T00:00:00Z","bio":"My own bot results"}'The handle is 3–24 lowercase letters, digits or hyphens, unique, with reserved paths unavailable. Bio is at most 160 characters. The Account public profile and this endpoint edit the very same handle and bio, including the shared 30-day handle-rename cooldown. Without a profile, the first trade/snapshot assigns an account-derived anon-… handle; no email is published. Capital is optional. effective_from is a timezone-qualified instant normalized to UTC; omitted means server receipt time. Declare it before importing the interval: if any trade with closed_at >= effective_from has already been received for this account, the entire profile request returns 409 capital_history_locked, including any handle/bio edits. This check is serialized with trade ingestion. A different declaration at an already-used effective timestamp returns 409 capital_conflict. Changing capital rebases future percentage denominators only, never past returns. It does not add a deposit to P&L. USD, USDT and USDC compare at 1:1; no other currency conversion is invented. Before a comparable capital declaration, the cumulative series sums per-trade ROE percentages and is labelled accordingly.
Closed positions
Section titled “Closed positions”POST /v1/trades takes one object or an array of 1–100. A trade is one position from open to flat, with partial closes aggregated by the bot; hedge sides are separate. IDs are unique per account. The whole batch validates before any write.
Live or demo. Every trade carries "account": "live" or "account": "demo"; it is required, and a missing or other value is 400 (required / invalid). live is a real-money account, demo is paper, testnet or simulated trading. Arena never adds the two together: a participant’s chip reads LIVE, DEMO or LIVE + DEMO, every trade row shows its label, and P&L and drawdown are computed and shown per account type; the participant page has a live/demo switch when both exist (live by default). Trades received before this field existed are marked unknown and shown as “—”.
Corrections and the 24-hour lock. Resending a trade_id with changed data updates that trade (and its fills) only within 24 hours of its first receipt; each change inside that window is kept as a revision (the values it replaced and when), and the participant page marks the trade “corrected” and lists them. After 24 hours a resend that changes anything is refused with 409 and {"errors":[{"field":"trade_id","code":"trade_locked","message":"…"}]}, and nothing of that batch is written; use a new trade_id for a genuinely new trade. An identical resend is a no-op at any time and answers 200 {"accepted":0,"updated":0}. Erasure (below) still deletes everything, revisions included. All times are timezone-qualified ISO-8601, normalized to UTC; future times and closes before opens are invalid.
import json, os, urllib.request
trade = { "trade_id": "kraken-position-42", "venue": "KRAKEN-FUTURES", "instrument": "PF_XBTUSD", "side": "long", "opened_at": "2026-10-17T08:00:00Z", "closed_at": "2026-10-17T09:00:00Z", "entry_price": "67000.25", "exit_price": "67100.50", "size": "0.02", "size_unit": "base", "leverage": "5", "fees": "1.00", "funding": "-0.10", "account": "live", "exit_reason": "tp", "label": "my strategy",}request = urllib.request.Request( "https://arena.debyko.com/v1/trades", data=json.dumps(trade).encode(), headers={"Authorization": "Bearer " + os.environ["DEBYKO_ARENA_KEY"], "Content-Type": "application/json"}, method="POST")with urllib.request.urlopen(request, timeout=15) as response: print(json.load(response)) # {"accepted": 1, "updated": 0}Venue accepts the registry code, segment or exact registry display name. Instrument accepts the registry market or native symbol; when a market has several listings on the venue, send its native symbol instead of guessing the quote/contract. Unknown or ambiguous listings produce 400. Size is base units by default; contracts requires a known positive registry contract multiplier. Prices/size/margin/leverage are positive decimals. Fees are nonnegative costs; funding is signed (paid negative, received positive). Gross = (exit-entry) × base quantity × direction; net = gross-fees+funding. ROE % = net/margin ×100; explicit margin wins, otherwise margin = notional/leverage. Without both, the percentage uses notional and is marked “leverage not reported”. Missing fees/funding stay missing, with only reported costs applied. Public output contains percentages, never money amounts.
Optional exit_reason: tp, sl, signal, manual, liquidation. label ≤64 and note ≤280 characters. Optional fills (≤1000) contain time, side (buy/sell), price, base size, within the position interval. Optional fill_id is the venue fill identifier (≤128 characters), retained with its timestamp; it does not itself prove execution. Each is corroborated against DEBYKO’s own stored tape for that listing: ±2 seconds, within one tick (exact price if no tick known), size no greater than the public print. Every fill must match to earn tape; otherwise self. No reported fill is discarded for failing corroboration.
debyko_firing_id is the firing_id from the webhook body (data.firing_id); see Agent webhook payload, including signature verification, test events and retries. Optional debyko_firing_id earns a verified signal only for a fired event of the same account or an explicitly public rule, the same instrument, and an entry within 15 minutes after the event. Otherwise it is kept as declared. A rule label alone is declared. A signal does not verify trading execution or returns.
Late reporting. A trade first received at least 24 hours after closed_at is labelled “reported N days after close” (whole elapsed days) on its row and disclosed on the participant page. The label uses immutable first receipt; retries and corrections do not restart it. This is expected for historical imports and is independent of tape/signal verification.
Open positions and P&L method
Section titled “Open positions and P&L method”POST /v1/positions accepts {"account":"live","positions":[{"venue":"KRAKEN-FUTURES","instrument":"PF_ETHUSD","side":"long","size":"1.2","entry_price":"2410.5","leverage":"5","opened_at":"2026-10-17T08:00:00Z"}]}. account (live or demo) is required for the snapshot, a position may repeat it but must match; the snapshot replaces the current one of that account type only, and an empty array clears it. Send immediately on any change and every 60 seconds thereafter, even when the position is unchanged. Each successful heartbeat records a fresh receipt; an identical payload renews freshness rather than becoming a no-op. At 180 seconds without a snapshot, an open position is not valued: the open row and affected closed-day curve show a gap, even if mark prices keep arriving. A later fresh snapshot restores current valuation; no stale snapshot is carried through a capital rebase. An empty snapshot explicitly confirms flat and creates no stale open-position gap. Prior snapshots remain as history until erasure. Unrealized is valued from DEBYKO mark observations, never balances. Calendar/curve use closed UTC days, start cumulative P&L at zero, include realized net plus unrealized change, and show unavailable mark evidence as gaps. No-trade days without open positions are zero, not gaps. Max drawdown is an end-of-day fall in percentage points over 30 days. Capital changes rebase future changes only; missing mark evidence at a rebase is not filled in.
Erasure and responses
Section titled “Erasure and responses”DELETE /v1/me immediately deletes this account’s profiles, capital history, trades/fills, positions/snapshots and raw Arena events. It keeps the DEBYKO account, Agent rules, captured public-rule firing records and their public flags. Turn off Make public in the Agent rule editor to hide a rule. Reporting again starts a new Arena record.
Success is 202 {"accepted":n,"updated":m}, or 200 for an unchanged trade resend; identical trade resends count neither. Position heartbeats always record a fresh snapshot receipt, including unchanged or flat snapshots; accepted counts positions in that snapshot, so a flat snapshot returns 202 with zero accepted positions. A changed resend of a trade older than 24 hours is 409 trade_locked. 400 {"errors":[{"field":"size","code":"invalid","message":"…"}]} lists all detected input problems and writes nothing. Invalid/expired/revoked keys and disabled/closed accounts get 401; body overflow gets 413. Fresh data appears on the product pages within a minute, when the public launch flag permits access.
POST /v1/ingest remains the existing raw JSON log and is not a substitute for the typed trading endpoints.
Public Agent rules
Section titled “Public Agent rules”Public rules are explicit opt-in. Save a rule, open Advanced settings in Agent and turn Make public on. Arena shows its saved DQL using the editor formatter, actual evaluation settings, recorded firing counts and up to 20 retained emitted answers. Publication never enables a rule or changes its execution. Turn the switch off to hide its page immediately.
Counts belong to the current saved version. Available retained Agent history is imported on publication; future firings are captured while public. Deleted pre-publication history and private intervals cannot be reconstructed. Counts are recorded emissions, not trades, continuous uptime or a false/unknown verdict. Verification, including later contradictions, is displayed. The current UTC day is partial.
Use this rule opens https://agent.debyko.com/rules/new?from=<slug> as a new unsaved copy. DQL, custom rearm and evaluation settings are preserved; channels, notification text, credentials and publication settings are not copied. The viewer must review and save their own rule. Until the owner launches Arena, public pages and this copy API remain staff-only.
Runner and other bots
Section titled “Runner and other bots”Runner will report its actual executions directly to Arena, using an arena:write scoped key. Agent does not turn Runner reports into Arena trades: it knows a signal, not whether an order executed. Runner → Arena integration is not yet implemented. Do not give Runner a full user key. BYKO is an independent bot, not Runner; it reports directly using the same public API contract.
Keep reporting fail-safe: persist a retryable report outside the order path. Honour Retry-After for 429, back off on 5xx, never retry 400 unchanged, and respect the 24-hour correction lock. An identical resend is a no-op even after that window.