openthomas
v0.1.0
Published
Open-source Bayesian trading worker for prediction markets. Named after Thomas Bayes. v0: 2026 World Cup moneyline forecasts on Polymarket, auditable belief ledger, WorkPnP worker client, dry-run edge/Kelly intents.
Downloads
175
Readme
OpenThomas
An open-source Bayesian trading worker for prediction markets, named after Thomas Bayes.
v0 scope: forecast 2026 World Cup match moneyline markets on Polymarket with statistical priors (Elo → Poisson), de-vigged market prices, and a transparent blend — then keep every belief on an append-only ledger and work as a forecaster on a WorkPnP pool. Trade decisions are computed dry-run only.
Hard limits in v0 (by design, see CONTRACT_v0.md §6):
- No LLM calls. Priors are pure statistics: Elo win expectancy → expected goals → independent Poisson → outcome probabilities.
- No private keys, no real orders.
openthomas edgeprints intended trades (edge / fractional Kelly) and stops there. - No invented numbers. If a team has no Elo rating, the model leg is skipped and the forecast degrades to a market-only prior.
The belief ledger philosophy
Most trading bots are black boxes that remember only their PnL. OpenThomas treats the
belief as the primary artifact: every forecast it ever emits is appended to
~/.openthomas/forecasts.jsonl as a self-contained JSON record — probability, prior,
method, and evidence chain (which Elo ratings, which market prices, which blend λ).
Outcomes are back-filled into a separate settlements.jsonl; the original belief is never
edited. That makes calibration honest and auditable: openthomas calib computes Brier
score and a 10-bucket calibration curve from the raw record, and anyone can recompute it.
The same Forecast JSON (openthomas/[email protected], frozen in CONTRACT_v0.md §1.1) is
simultaneously the ledger entry and the deliverable WorkPnP scores — one format, three uses.
The model (v0)
- Elo prior — national-team ratings from eloratings.net
(live TSV fetch with a bundled snapshot fallback in
data/elo-snapshot.json). Win expectancyWe = 1/(1+10^(−d/400)); WC 2026 venues are neutral except when a host (USA/CAN/MEX) plays, which earns +100 Elo. - Poisson —
Wemaps to an expected goal difference (We=0.76 ≈ +1 goal), split around a 2.6 total-goals baseline; independent Poisson goals (truncated at 10) give P(home/draw/away). - Market prior — the three Polymarket moneyline mids, de-vigged by normalization (negRisk books already sum to ≈1).
- Blend —
p = (1−λ)·model + λ·market,λ = MARKET_BLEND(default 0.3). See the comment insrc/core/blend.tsfor why copying the market is safe but worthless.
Quickstart
npm install
npm run build
npm link # optional: puts `openthomas` on your PATH
# What's playing, and what does the market think?
openthomas scan
# Full pipeline for one match → three Forecast JSONs + ledger write
openthomas forecast fifwc-usa-par-2026-06-12
# Dry-run trade intents from your ledgered forecasts vs current prices
openthomas edge
# Calibration: first backfill resolutions from Gamma, then report
openthomas calib settle-sync
openthomas calib(Or run from source without building: npm run dev -- scan.)
Connecting to WorkPnP
WorkPnP is the research-syndicate side: sponsors fund pools of forecast work; workers submit probabilities and get paid wages per accepted submission plus a profit-linked bonus.
export WORKPNP_URL=http://localhost:8787
# 1. Register (the apiKey is shown exactly once — the server stores a hash)
openthomas register --name thomas --wallet 0xYourPayoutAddress
export WORKPNP_API_KEY=<the printed key>
# 2. Work: poll open forecast work, forecast each market, submit, ledger
openthomas work # one pass
openthomas work --loop --interval 600 # keep pollingAlready-submitted work items are tracked in ~/.openthomas/state.json and skipped.
Environment variables
| Variable | Default | Meaning |
|---|---|---|
| WORKPNP_URL | http://localhost:8787 | WorkPnP server |
| WORKPNP_API_KEY | — | worker key from openthomas register |
| MARKET_BLEND | 0.3 | λ: weight of the market prior in the blend |
| BANKROLL_USD | 100 | dry-run bankroll for Kelly sizing |
| KELLY_FRACTION | 0.25 | fraction of full Kelly |
| EDGE_THRESHOLD | 0.03 | minimum net edge to emit a TradeIntent |
| LEDGER_DIR | ~/.openthomas | belief-ledger directory |
Agent-only (the agent command):
| Variable | Default | Meaning |
|---|---|---|
| ANTHROPIC_API_KEY | — | Claude API key (or run ant auth login) — required by agent |
| OPENTHOMAS_MODEL | claude-opus-4-8 | model for the agent loop |
| OPENTHOMAS_ARMED | unset | set to 1 and provide a key to allow real orders; otherwise dry-run |
| POLYMARKET_PRIVATE_KEY | — | EOA key for signing CLOB orders (only read when arming) |
| POLYMARKET_FUNDER_ADDRESS | — | proxy/Safe address holding funds, if the key is a proxy signer |
| POLYMARKET_SIGNATURE_TYPE | 0 | 0 EOA · 1 POLY_PROXY · 2 POLY_GNOSIS_SAFE |
| POLYMARKET_CLOB_HOST | https://clob.polymarket.com | CLOB REST host |
| POLYGON_RPC_URL | viem default | Polygon RPC |
| MAX_ORDER_USD | 25 | hard cap on one order's notional |
| MAX_TOTAL_EXPOSURE_USD | 100 | hard cap on total open exposure per session |
The autonomous agent (v0.2)
Beyond the deterministic pipeline, openthomas agent runs a specialized prediction-market
trading agent — a Claude tool-use loop that orchestrates the v0 math instead of replacing it.
It is vertically scoped on purpose: no shell, no browser, no arbitrary file access — only
trading-domain tools.
export ANTHROPIC_API_KEY=...
openthomas agent "Forecast today's World Cup matches, recall my track record, and show any edge — dry run"Three ideas make it a forecaster's agent rather than a generic one:
- Memory = your track record.
ledger_recallreturns prior beliefs joined with how they resolved (calibration-aware recall), so the agent always decides while looking at its own scored history — not a pile of un-graded notes. - Tools are the audited pipeline.
market_scan/market_get(Gamma),model_forecast(Elo→Poisson + blend),edge_kelly,ledger_recall/ledger_append,calibration_report,skill_view, and the gatedplace_order. - Skills are the constitution. Markdown methodology (
skills/) — de-vig method choice, evidence discipline, Kelly & risk — loaded on demand viaskill_view.
Execution safety. place_order is dry-run by default: it prints the intended order and
stops. Real orders require OPENTHOMAS_ARMED=1 plus a key, must clear the pre-trade risk gate
(per-order / per-market / total-exposure caps), and must carry confirm: true. Real signing uses
the official @polymarket/clob-client. Every intent and fill is appended to orders.jsonl.
Repository layout
src/core/gamma.ts Gamma API client (discovery method documented in comments)
src/core/elo.ts Elo ratings: live fetch → snapshot fallback; 48-team slug mapping
src/core/poisson.ts Elo → expected goals → Poisson → outcome probabilities
src/core/devig.ts market mids → implied probabilities
src/core/blend.ts model/market blend (λ)
src/core/pipeline.ts match → three contract-valid Forecast JSONs
src/core/ledger.ts append-only forecasts.jsonl / settlements.jsonl + calibration
src/core/decision.ts edge, taker-fee model, fractional Kelly (DRY RUN ONLY)
src/workpnp/client.ts WorkPnP worker API client (contract §2)
src/config/config.ts agent config: Kelly knobs, risk caps, the execution gate
src/agent/ tool registry, model client, agentic loop, system prompt, skills loader
src/tools/ agent tools: market, quant, ledger, risk, execute (gated orders)
skills/ human-authored methodology (SKILL.md), loaded on demand
src/cli.ts commander CLI (`openthomas`, incl. `agent`)
data/elo-snapshot.json frozen eloratings.net snapshot (see its sourcedAt field)Development
npm test # vitest (54 tests)
npm run typecheck # tsc --noEmit
npm run build # tsc → dist/
bun run src/cli.ts agent "..." # run the agent from sourceMIT © 2026
