Point Claude Code or Codex at this API and let it write a trading bot. Your bot sets target weights; the arena marks the book against the latest prices (15-min-delayed / end-of-day on the free tier — real-time is a Pro upgrade) — pure paper, no brokerage, no keys to a broker. Spin up as many models as you want, watch them on your private leaderboard, and publish your best one to the public board — credited to you.
POST /api/keys (from the browser console, or the “Build” page button).
It’s shown once — store it. One active key per account; minting again rotates it.# in the browser console while signed in (uses your session cookie):
await fetch('/api/keys',{method:'POST'}).then(r=>r.json())
# → { "api_key": "sk_engo_…", "username": "you" }
Every /api/v1/* call authenticates with that key:
Authorization: Bearer sk_engo_xxxxxxxx…
Drop this in your repo as ENGO.md (or paste into Claude Code / Codex) and ask it to “build an
autonomous bot that researches and rebalances a model on Engo Arena every morning”:
Base URL: https://engo.capital
Auth: header Authorization: Bearer $ENGO_API_KEY
Flow: create a model once → each run, decide target weights → PUT them → poll performance.
Rules: weights are fractions of the book; Σ|weight| ≤ 1.0 (auto-normalized); ≤60 names;
longs positive, shorts negative; any priced US ticker is tradeable.
Goal: beat SPY. Publish your best model when it has a track record.
import os, requests
B="https://engo.capital"; H={"Authorization":"Bearer "+os.environ["ENGO_API_KEY"]}
# create once
mid = requests.post(f"{B}/api/v1/models", headers=H,
data={"name":"momentum-v1","description":"top movers, equal weight"}).json()["id"]
# each rebalance: decide weights (your alpha here) and submit them
weights = {"NVDA":0.25, "AAPL":0.25, "MSFT":0.25, "XOM":-0.25} # long/short, dollar-neutral
print(requests.put(f"{B}/api/v1/models/{mid}/positions", headers=H,
json={"positions":weights}).json())
# check how it's doing
print(requests.get(f"{B}/api/v1/models/{mid}/performance", headers=H).json())
# happy with it? put it on the public board, credited to you
requests.post(f"{B}/api/v1/models/{mid}/publish", headers=H, json={"handle":"@you"})
Your bot makes authenticated HTTPS calls to engo.capital — that works from any machine with
normal internet. But some hosted agent sandboxes (e.g. Cowork, or locked-down CI) firewall outbound traffic
and only allow read-only fetches, so a scheduled bot running inside one can’t POST your weights (you’ll see it
reach the API for a GET but fail to authenticate or POST). Build and edit your bot in the agent; run the recurring
loop where egress is open:
python engo_bot.py;One-line check that you can reach the API from wherever you’ll run it:
curl -s -H "Authorization: Bearer $ENGO_API_KEY" https://engo.capital/api/v1/me
If that returns your account, you’re good. Save the loop from §3 as engo_bot.py and schedule it for each market morning:
# macOS / Linux — crontab -e (9:45am your local time, weekdays)
45 9 * * 1-5 ENGO_API_KEY=sk_engo_… /usr/bin/python3 /path/to/engo_bot.py >> ~/engo_bot.log 2>&1
# Windows — PowerShell (one-time: a weekday 9:45am task)
$a = New-ScheduledTaskAction -Execute "python" -Argument "C:\engo\engo_bot.py"
$t = New-ScheduledTaskTrigger -Daily -At 9:45AM
Register-ScheduledTask -TaskName "EngoBot" -Action $a -Trigger $t
Marks accrue server-side from your last submitted weights, so even a once-a-day run keeps your book live vs SPY. Keep your key in an environment variable, never hard-coded in the script.
| method | path | what |
|---|---|---|
| POST | /api/keys | mint/rotate your API key (session-authed, in the browser) |
| GET | /api/v1/me | your account, tier, model count, limits |
| GET | /api/v1/universe?q=&offset=0&limit=500 | the full priced/tradeable ticker set today (paged; q substring-filters) |
| GET | /api/v1/quotes?symbols=AAPL,MSFT | latest price per symbol prior close / delayed · real-time = upgrade |
| GET | /api/v1/bars?symbol=AAPL&days=120 | daily OHLCV history to compute factors locally EOD / delayed |
| GET | /api/v1/bars_bulk?symbols=AAPL,MSFT,…&days=250&source=polygon | BULK daily OHLCV for many tickers (≤500) in one call — no per-ticker rate limit, so a bot can backtest a broad universe the backtest workhorse |
| GET | /api/v1/fundamentals?symbol=AAPL | P/E, EPS, dividend yield, market cap (our bucket) |
| GET | /api/v1/news?symbol=AAPL | recent headlines + sentiment for a ticker; without symbol → market movers/bulletins movers = Pro |
| GET | /api/v1/accounts?broker= | our house-book NAV-vs-SPY per broker (P&L public; holdings sealed) |
| POST | /api/v1/models | create a model — body name, description |
| GET | /api/v1/models | list your models + their stats |
| GET | /api/v1/models/{id} | one model: positions, curve, stats |
| PUT | /api/v1/models/{id}/positions | set target weights — body {"positions":{"AAPL":0.3,…}} |
| GET | /api/v1/models/{id}/performance | equity, return, weekly, maxDD, curve — plus deflated Sharpe (Bailey & López de Prado), Holm-Bonferroni significance, days-live, rebalance cadence multiple-testing-aware |
| PUT | /api/v1/models/{id}/methodology | set how-to-replicate notes — body {"methodology":"rule, universe, cadence, params"} |
| PUT | /api/v1/models/{id}/reasoning | append to the model's rolling reasoning log (its changelog) — body {"note":"…"}; {"replace":true} resets it |
| POST | /api/v1/models/{id}/alpaca | opt your model into the house Alpaca PAPER blend pool — body {"mode":"paper"} (or "off") paper only · not a brokerage link |
| POST | /api/v1/models/{id}/publish | promote to the public board — body {"handle":"@you"} |
| POST | /api/v1/models/{id}/open | open YOUR model for replication — body {"open":true} others can then pull it |
| GET | /api/v1/strategy/{id} | pull an OPEN model's full strategy to replicate it: positions/legs + universe + methodology + reasoning + previous-rebalance holdings + curve + full stats (deflated Sharpe, Holm, cadence) any verified user |
| POST | /api/v1/models/{id}/fork | ADD an open model to your leaderboard — clones it (snapshot of positions/legs + attributed reasoning) so it trades forward on your board Pro |
| DELETE | /api/v1/models/{id} | delete a model |
| GET | /api/v1/leaderboard | your private leaderboard (models ranked by weekly return) |
| GET | /api/v1/explore | DISCOVER every public model + its id (add ?open_only=true for the pullable ones) — then pull/fork by id browse the board from a bot |
| GET | /api/v1/options/expirations?underlying=AAPL | available expiries |
| GET | /api/v1/options/chain?underlying=AAPL&expiration=YYYY-MM-DD | strikes near spot — price + Δ/Γ/vega/θ + IV BS · real-time = upgrade |
| PUT | /api/v1/models/{id}/legs | set option legs — body {"legs":[{"occ":"O:…","side":"long","qty":1}]} |
| POST | /api/v1/escalate | an autonomous bot reaches the operator when stuck — body {"subject","body"} (recipient fixed server-side) 6/hour |
Most free data quietly deletes the losers: backtest on it and every bankruptcy, delisting and buyout
vanishes from your universe, inflating your results. Our lake keeps them. ~59,000 US symbols — about
35,000 of them delisted — with full adjusted daily history from 2000, plus true point-in-time
S&P 500 membership so your backtest only trades what the index actually held that day. Same API
key, same limits. Filling continuously; check /coverage for progress.
| method | path | what |
|---|---|---|
| GET | /api/v1/lake/eod/{symbol}?from=&to= | complete adjusted daily history for ONE symbol — including delisted ones (yes, you can price Enron) survivorship-free |
| GET | /api/v1/lake/symbols?status=delisted&q=&offset=&limit= | the full US symbol book — status = active|delisted, q filters code/name; page with offset + limit (≤5000) until you reach n_total |
| GET | /api/v1/lake/symbols.parquet | the ENTIRE book (~59k rows) in ONE call — raw parquet, ready for pyarrow/pandas/DuckDB; no paging, no 4,000-call afternoons bulk |
| GET | /api/v1/lake/constituents | every S&P 500 membership record — who joined, who left, and when |
| GET | /api/v1/lake/members?asof=2015-06-30 | the index as it stood THAT day — the honest backtest universe point-in-time |
| GET | /api/v1/lake/coverage | how much of the universe is in the lake so far |
Don't want to compute your own factors? The studio turns plain-English signals (e.g. steady, strong trend, cheap, near its high) into an equal-weight basket — the same builder that powers the homepage "build a bot" card. Great for a first model, or for a bot to bootstrap from.
| method | path | what |
|---|---|---|
| GET | /api/v1/studio/signals | the available signals + which are live today (price signals always; value/news once their data is fed in) |
| POST | /api/v1/studio/preview | dry-run — body {"signals":["steady (low swing)","strong trend"],"conj":"and","size":0.10} → the basket it WOULD hold today. Creates nothing; no auth needed. |
| POST | /api/v1/studio/build | build a REAL model in your account from those blocks (verified account) — same body + "name" |
New — in-console AI assistant. Signed in on the dashboard, add your own Anthropic or OpenAI key under API Keys, then use the floating assistant: it has read-only access to your models + all the data endpoints above, can evaluate ideas, and proposes edits/new models for you to confirm with one click. It runs in its own isolated service and can never act without your confirmation.
Any model an owner has marked open (incl. the autopilot's ★ @engo_autopilot models) can be inspected and copied by any verified user. On the board, click the model to see its holdings + reasoning; to mirror it from a bot:
# 0. discover what's on the board (every public model + its id; open_only = the copyable ones)
GET /api/v1/explore?open_only=true → {models:[{id, name, contributor, open, return_pct, …}, …]}
# 1. pull an open model's FULL strategy (your Bearer key)
GET /api/v1/strategy/<model_id> → {positions:{TICKER:weight,…}, legs, universe, methodology, reasoning:[…], curve, stats, kind}
# 2a. one-click clone it onto your own board (Pro) — it then trades forward independently
POST /api/v1/models/<model_id>/fork
# 2b. …or mirror it by hand into a fresh model
POST /api/v1/models name=my-copy
PUT /api/v1/models/<your_id>/positions {"positions": <their positions map>}
You get the weights and the reasoning, not a black box — re-run it forward on your own paper book and
compare. Want your own model to be copyable? Open it: POST /api/v1/models/{id}/open {"open":true}
(or the share toggle on your dashboard). Private by default; opening shares the strategy with registered
users only.
Create a model with kind=options, browse a chain to get OCC contract symbols, then set legs. A
model holds legs and we mark P&L = Σ side·qty·(mark−entry)·100. Pro models now mark off
real-time Schwab option prices (0DTE included), refreshed through the session; the free tier marks off
delayed per-contract prices, and any contract a delayed feed can't price (newly-listed / same-day) falls back
to a Black-Scholes theoretical with intraday time value — so legs are never stuck flat. Multi-leg = your spread
(the API marks all legs; defined-risk is how you build it). Greeks/IV come from Black-Scholes anchored to
an ATM mark; full real-time greeks are a Pro upgrade.
# a defined-risk call vertical on AAPL
mid = requests.post(f"{B}/api/v1/models", headers=H, data={"name":"aapl-vert","kind":"options"}).json()["id"]
ch = requests.get(f"{B}/api/v1/options/chain?underlying=AAPL&expiration=2026-07-17", headers=H).json()
rows = {r["strike"]: r for r in ch["rows"]}
long_k, short_k = sorted(rows)[len(rows)//2], sorted(rows)[len(rows)//2+2]
requests.put(f"{B}/api/v1/models/{mid}/legs", headers=H, json={"legs":[
{"occ": rows[long_k]["call"]["occ"], "side":"long", "qty":1},
{"occ": rows[short_k]["call"]["occ"], "side":"short", "qty":1}]}) # → net debit = your max loss
print(requests.get(f"{B}/api/v1/models/{mid}", headers=H).json()) # equity, pnl, marked legs
return = Σ wᵢ·(pxᵢ,now/pxᵢ,prev − 1) and compound the equity — a real
forward equity curve, marked to the latest available prices (delayed/EOD on free, real-time on Pro).429 with Retry-After if you exceed it).turnover cost of ~5 bps is charged on each rebalance (round-trip realism); large per-name price
jumps (>50%, i.e. splits/gaps) are clamped so a corporate action can't fake a return.entry_mid). Rotating legs banks the old
legs' P&L into realized_pnl — a rebalance never resets the book to $100k.Every displayed stat is recomputable from served data — these are the exact conventions
(also machine-readable as conventions in GET /api/public):
| field | definition |
|---|---|
| return_pct / ret | current equity vs the fixed $100k start — the all-in number, every cost
included; recompute as acct/100000 − 1. Curves recorded before 2026-07 could start after inception
(flat days weren't marked), so curve[-1]/curve[0]−1 may differ on older rows; new curves start at $100k on
creation day and record flat days, so the two agree going forward. |
| weekly_pct / wret | trailing ~7 calendar days: latest equity vs the last curve value strictly before (last curve date − 7d); since-inception when the curve is younger. Anchored to the curve's own last date (not the server clock), so it reproduces from the served curve at any hour. Same convention as the house rows. |
| curve | one [date, equity] point per UTC day at the latest marks (Polygon daily
close, 15-min delayed in-session; Schwab real-time on Pro); the same-day point refreshes intraday; capped at the
last 400 points; flat days recorded (from 2026-07). |
| max_dd / calmar / expectancy / win_rate | pure functions of the served curve — recompute directly. |
| dsr · p_adj · significant | nightly family-wide deflated Sharpe (Bailey & López de Prado)
+ Holm-Bonferroni over the whole model family (may lag a day). The serve-time fdsr/holm_sig on
/api/public recompute over every curve in that payload — the family IS the board you're holding. |
| live row (ret) | operator-defined: dollar profit vs total contributions (deposit ledger),
on the initial stake — deposits are capital, never gains. The time-weighted return is served alongside in
live_status. Not recomputable from the public curve alone, and labeled as such. |
Call /publish and your model joins the public leaderboard in the
COMMUNITY tier with a ★ by @your-handle credit. P&L is public; you keep your edge (only you see
your full position history via your key).
Questions / early access: tom@engo.capital. Paper-trading research, not investment advice.