Backtest API & SDK
Run strategy backtests, browse the bot leaderboard, clone bots, and paper trade — all via API.
Quick Start
# Run a funding rate backtest
curl -X POST https://cymetica.com/api/v1/backtest-labs/run \
-H "Content-Type: application/json" \
-d '{"type":"funding_rate","params":{"coin":"BTC","days":30,"capital":10000,"leverage":1,"strategy":"cross_exchange"}}'
# Get bot leaderboard
curl https://cymetica.com/api/v1/backtest/leaderboard?sort=return&limit=10
from event_trader import EventTrader
client = EventTrader(api_key="evt_...")
# Run a backtest
result = await client.backtest.run_lab(
type="funding_rate",
params={"coin": "BTC", "days": 30, "capital": 10000}
)
# Get leaderboard
leaderboard = await client.backtest.leaderboard(sort="return", limit=10)
# Get bot profile
bot = await client.backtest.bot("cryptobull-blue")
The JS/TS SDK's npm release is in progress — these snippets show the interface it ships with. The Python SDK and the REST API are live today.
import { EventTrader } from "cymetica-eventtrader";
const client = new EventTrader({ apiKey: "evt_..." });
// Run a backtest
const result = await client.backtest.runLab({
type: "funding_rate",
params: { coin: "BTC", days: 30, capital: 10000 }
});
// Get leaderboard
const leaderboard = await client.backtest.leaderboard({ sort: "return", limit: 10 });
// Get bot profile
const bot = await client.backtest.bot("cryptobull-blue");
# Python
pip install cymetica-eventtrader
# TypeScript / Node.js — npm release in progress; Python SDK is live today
# npm install cymetica-eventtrader
Authentication
Most read endpoints are public (no auth required). Write operations use one of two methods depending on the endpoint family: Backtest Labs runs (/api/v1/backtest-labs/*) authenticate with an API key (X-API-Key header), while bot management (clone, trigger run, update settings) requires a Bearer token from a logged-in session.
# Authenticated request
curl -H "Authorization: Bearer $EVENTTRADER_API_KEY" \
https://cymetica.com/api/v1/backtest/bots/your-bot-slug/run-backtest
Get your API key from /account → API Keys.
Which backtest surface?
Five surfaces, all current, each answering a different question. None supersedes another. The machine-readable spec of record is openapi-public.json, llms.txt and the MCP tools list_backtest_surfaces / get_backtest_models; this page mirrors them.
| Page | Answers | API | Auth |
|---|---|---|---|
| /backtest | How has a specific clone-able bot performed, and can I clone it? | GET /api/v1/backtest/bots/{slug}, /equity, /trades, GET /api/v1/backtest/leaderboard, POST /api/v1/backtest/clone | reads: none · writes: Bearer |
| /backtest (your clones) | Run my clone's equipped model on a catalog instrument and keep the immutable record. | GET /api/v1/cloned-bots/backtest/catalog-assets (items carry asset_id = crypto:BTC / stock:AAPL), GET /api/v1/cloned-bots/backtest/quote, POST /api/v1/cloned-bots/{instance_id}/backtest, GET /api/v1/cloned-bots/backtest/runs/{run_id}, GET /api/v1/backtests/leaderboard (/backtest-results: Sharpe-ranked under ranking floors) | catalog + leaderboard: none · quote / run record: Bearer session JWT or X-API-Key (owner) · start: Bearer session JWT (owner) |
| /backtest-labs | What would one parameterised strategy have returned on one asset? | POST /api/v1/backtest-labs/run — type: cross_exchange_arb | funding_rate | dex_replay | X-API-Key |
| /backtest-rally | Which theme basket beat BTC under a research model, then launch it as a rally card? | GET /api/v1/research-backtest/models, /themes, POST /api/v1/research-backtest/run, GET /runs, /runs/{run_id}, /queue, /leaderboard | none (30/min per IP; 2 in-flight runs per anonymous IP, 5 per account) |
| /events/builder | Would an event-card idea have paid off historically? | POST /api/v1/event-cards/backtest-idea | none (30/min per IP) |
| /macromarket | Build an AI Index Basket from a macro theme, backtest and paper-trade it. | POST /api/v1/macromarket/build, /backtest, /paper-trade | Bearer |
Backtest Labs
Run one parameterised strategy on one asset. Accepted type values: cross_exchange_arb (cross-exchange arbitrage), funding_rate (perp funding-rate capture) and dex_replay (DEX trade replay with params.strategy = mean_reversion | momentum | lp_vs_hold). Any other type returns {"error":"Unknown backtest type. Supported: cross_exchange_arb, funding_rate, dex_replay"}. Max 20 concurrent backtests via Redis queue.
Request Body
cross_exchange_arb, funding_rate, dex_replay (for dex_replay, params.strategy = mean_reversion | momentum | lp_vs_hold)cross_exchange)curl -X POST https://cymetica.com/api/v1/backtest-labs/run \
-H "Content-Type: application/json" \
-d '{
"type": "funding_rate",
"params": {
"coin": "BTC",
"days": 30,
"capital": 10000,
"leverage": 1,
"strategy": "cross_exchange"
}
}'
result = await client.backtest.run_lab(
type="funding_rate",
params={"coin": "BTC", "days": 30, "capital": 10000, "leverage": 1, "strategy": "cross_exchange"}
)
const result = await client.backtest.runLab({
type: "funding_rate",
params: { coin: "BTC", days: 30, capital: 10000, leverage: 1, strategy: "cross_exchange" }
});
Multi-Leg Backtesting — Baskets, AIBs and Themes
Backtest Labs above runs a strategy against one asset. To backtest a
basket — several legs with weights — use the endpoints below.
Basket symbols (AIB-, TREND-, EVCDX-,
RALLY-, MM-) are not accepted by the single-asset endpoints.
All results describe the past and are never a predicted return.
The API behind the “Backtest this idea” widget on /events/builder. Read-only: no card is created, no ledger or on-chain write happens, no money moves. Runs the same engine as the saved-card backtest, so an idea and a published card holding the same basket return identical numbers.
Request Body
{symbol, weight} (optional coingecko_id). Weights are relative and renormalized, so 50/30/20 equals 5/3/2.7, 30, 90, 180, 365. Anything else returns 422.Response
ending_capital_usdcnumberSimulated capital at each end of the window.
n_points).dropped_symbolsarrayLegs that could not be priced and were excluded. Always check these before trusting the numbers.
curl -X POST https://cymetica.com/api/v1/event-cards/backtest-idea \
-H "Content-Type: application/json" \
-d '{
"constituents": [
{"symbol": "BTC", "weight": 50},
{"symbol": "ETH", "weight": 30},
{"symbol": "SOL", "weight": 20}
],
"period_days": 90,
"starting_capital": 10000
}'
result = await client.basket_backtest.run(
constituents=[("BTC", 50), ("ETH", 30), ("SOL", 20)],
period_days=90,
starting_capital=10_000,
)
print(result["total_return_pct"], result["sharpe_ratio"])
print(result["unresolved_symbols"], result["dropped_symbols"])
const result = await client.basketBacktest.run({
constituents: [
{ symbol: "BTC", weight: 50 },
{ symbol: "ETH", weight: 30 },
{ symbol: "SOL", weight: 20 },
],
periodDays: 90,
startingCapital: 10000,
});
console.log(result.total_return_pct, result.sharpe_ratio);
et_basket_backtest(
constituents=[
{"symbol": "BTC", "weight": 50},
{"symbol": "ETH", "weight": 30},
{"symbol": "SOL", "weight": 20}
],
period_days=90,
starting_capital=10000
)
Give it a plain-language theme and the Tuatara semantic model selects real
trading vehicles (stocks, ETFs, crypto) and weights them by relevance. The basket
gets an MM-… symbol and a /macromarket page.
Request Body
"AI infrastructure".tuatara (relevance-weighted) or equal.crypto, equity. Omit for both.curl -X POST https://cymetica.com/api/v1/macromarket/build \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $EVENTTRADER_API_KEY" \
-d '{"theme": "AI infrastructure", "num_assets": 8, "weighting": "tuatara"}'
aib = await client.macromarket.build("AI infrastructure", num_assets=8)
print(aib["symbol"]) # e.g. "MM-AIINFRA"
const aib = await client.macromarket.build({
theme: "AI infrastructure",
numAssets: 8,
});
et_aib_build(theme="AI infrastructure", num_assets=8, weighting="tuatara")
Pass aib_symbol for an existing basket, or theme to build
and persist one first. Quota-gated: an over-quota caller gets 402
before any expensive work runs. Results are simulated, not investment advice.
Request Body
MM-… symbol. Required unless theme is given.curl -X POST https://cymetica.com/api/v1/macromarket/backtest \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $EVENTTRADER_API_KEY" \
-d '{"aib_symbol": "MM-AIINFRA", "period_days": 365}'
bt = await client.macromarket.backtest(aib_symbol="MM-AIINFRA", period_days=365)
# Or skip the build entirely and backtest a theme directly:
bt = await client.macromarket.backtest(theme="uranium miners")
const bt = await client.macromarket.backtest({
aibSymbol: "MM-AIINFRA",
periodDays: 365,
});
et_aib_backtest(aib_symbol="MM-AIINFRA", period_days=365)
et_aib_backtest(theme="uranium miners")
The headline-basket research engine behind /backtest-rally. Races strategies against BTC and can launch the winner as a Rally Card. Signal/backtest only — no orders are placed. Full reference: /backtest-rally/api.
curl -X POST https://cymetica.com/api/v1/research-backtest/run \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $EVENTTRADER_API_KEY" \
-d '{"themes": ["ai tokens"], "algo_model": "aib_sentiment", "hold_days": 2}'
run = await client.research_backtest.run(
themes=["defi tokens", "ai tokens"], hold_days=2)
result = await client.research_backtest.get(run["run_id"])
await client.research_backtest.launch_rally_card(run["run_id"], basket_index=0)
const run = await client.researchBacktest.run({
themes: ["defi tokens", "ai tokens"],
params: { hold_days: 2 },
});
const result = await client.researchBacktest.get(run.run_id);
et_research_backtest_run(params={"themes": ["ai tokens"], "hold_days": 2})
et_research_backtest_launch_rally_card(run_id="<run_id>", basket_index=0)
Bot Leaderboard
Browse ranked AI trading bots with sorting and filtering. 60-second cache on leaderboard results.
Query Parameters
return (headline return, desc), clones, win_rate, drawdown (asc). Default: return. Unknown values fall back to return.all, crypto, politics, sports, finance (default: all)all, red, blue30d, 6m, 24h (default: all). headline_return_pct is measured over each bot's own window and is NOT comparable across periods, so rank within one. Every row carries headline_period and headline_period_hours.false: only bots whose figures rest on a stored historical record (a backtest trade log or recorded paper trades on real prices) are ranked. true also lists bots whose trade list is synthetic (generated from summary metrics, trades marked is_synthetic, prices 90–110) or who have no record.Row fields (every bot card, profile and trade list)
historical (backtest or paper record on real prices), synthetic (generated from summary metrics — NOT a backtest of the named assets), none (no trades) or unknown (record lookup failed). Only historical rows are in the default ranking.backtest | paper | synthetic | none | unknown — the finer-grained source behind data_kind.true when data_kind is synthetic (also synthetic, the older name).curl "https://cymetica.com/api/v1/backtest/leaderboard?sort=return&team=red&period=30d&limit=10"
leaderboard = await client.backtest.leaderboard(sort="return", team="red", limit=10)
const lb = await client.backtest.leaderboard({ sort: "return", team: "red", limit: 10 });
Bot Profiles
Get bot details, equity curves, and trade history. 60-second cache on profile data.
Path Parameters
curl https://cymetica.com/api/v1/backtest/bots/cryptobull-blue
Returns time-series equity data for charting the bot's portfolio value over time.
curl https://cymetica.com/api/v1/backtest/bots/cryptobull-blue/equity
Query Parameters
curl "https://cymetica.com/api/v1/backtest/bots/cryptobull-blue/trades?limit=10"
Bot Operations
Run backtests and update settings. These endpoints require authentication.
Triggers a new backtest run for the specified bot (Bearer token; no request body — the bot's own strategy, top-10 asset universe and a 30-day window are used). Responds immediately with {"run_id", "status": "queued", "slug"}; the refreshed results appear on the bot's profile and the leaderboard when the async run completes. Also callable as the MCP tool et_backtest_bot_run.
curl -X POST https://cymetica.com/api/v1/backtest/bots/your-bot-slug/run-backtest \
-H "Authorization: Bearer $EVENTTRADER_API_KEY"
# 200 → {"run_id": "…", "status": "queued", "slug": "your-bot-slug"}
# then stream progress on wss://cymetica.com/ws/backtest/{instance_id}?run_id={run_id}
Request Body
curl -X PATCH https://cymetica.com/api/v1/backtest/bots/your-bot-slug/settings \
-H "Authorization: Bearer $EVENTTRADER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Alpha Bot v3"}'
Bot Cloning
Clone bot species into new instances. Cloned bots get dedicated HD wallets (account 4 derivation path). Requires authentication.
Request Body
curl -X POST https://cymetica.com/api/v1/backtest/clone \
-H "Authorization: Bearer $EVENTTRADER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"species_slug": "cryptobull-blue", "name": "My Momentum Clone"}'
Returns deposit address and minimum deposit required to activate a cloned bot.
curl https://cymetica.com/api/v1/backtest/clone/deposit-info/cryptobull-blue \
-H "Authorization: Bearer $EVENTTRADER_API_KEY"
Paper Trading
View paper trades and paper trading status for bots running in simulation mode.
Query Parameters
open, closed, allcurl "https://cymetica.com/api/v1/backtest/bots/cryptobull-blue/paper-trades?status=open&limit=20"
Returns current paper trading status including P&L, open positions, and account balance.
curl https://cymetica.com/api/v1/backtest/bots/cryptobull-blue/paper-status
WebSocket — Real-Time Backtest Updates
Stream real-time backtest progress and results via WebSocket.
Message Types
// JavaScript WebSocket example
const ws = new WebSocket("wss://cymetica.com/ws/backtest/bot-123?run_id=run-456");
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case "progress":
console.log(`${msg.percent}% — ${msg.step}`);
break;
case "completed":
console.log("Results:", msg.results);
break;
case "failed":
console.error("Error:", msg.error);
break;
}
};
Methodology — bars, time, missing data, universe
Stated from the engine as implemented (the MCP tool list_backtest_surfaces returns the same methodology object).
SYMBOLUSDT), paged forward from the window start, up to 60 pages of 1,000 bars. Stocks: Yahoo Finance 1-hour bars for a window of 30 days or less, daily bars for longer. Catalog fallback when the live feed is empty: stored daily equity OHLCV; for crypto, stored price points surfaced as bars with open = high = low = close. Annualised metrics use the median spacing of the bars actually present.period_days; the run reports the start, end and days actually covered (data_window, executed_config.period_fully_covered).No candle data found and the run credit is refunded (free slot restored).asset_id (crypto:BTC vs stock:BTC, the Grayscale ETF).not_modelled.Rate Limits
MCP Tools
Two MCP surfaces. The hosted server at https://cymetica.com/mcp/v1 (JSON-RPC tools/list / tools/call, no install) exposes list_backtest_surfaces, get_backtest_models and get_strategy_returns (accepts cryptobull-blue, cryptobull_blue or bt-cryptobull-blue; returns the latest run's metrics with its measurement period plus the leaderboard headline figure and its window). The installable server (pip install cymetica-eventtrader-mcp) ships the full endpoint mirror below — these et_backtest_* names come from that package.