Prediction market strategy research, backtesting, and paper trading toolkit
Project description
agenttrader
agenttrader is a toolkit that lets AI agents autonomously research, write, backtest, and deploy prediction market trading strategies — without human intervention.
Give an agent a goal. It handles everything: discovering markets on Polymarket and Kalshi, analyzing price history, writing strategy code, running the backtesting loop, and starting a live paper trading daemon. Running strategies hot-reload automatically when the strategy file changes — agents can iterate on live deployments without restarting.
All market data flows through pmxt, unified across Polymarket and Kalshi.
Primary users: AI coding agents (Claude Code, Codex, OpenCode, Cursor).
Secondary users: Developers who want to write and test strategies manually.
How It Works
Agent receives goal
│
▼
agenttrader dataset download ← one-time parquet history setup for backtesting
│
▼
agenttrader markets list ← discover opportunities
│
▼
[agent writes strategy.py]
│
▼
agenttrader validate ← catch errors before they waste time
│
▼
agenttrader backtest ← test against real historical data
│
├─── metrics look bad? agent edits strategy.py, loops back
│
▼
agenttrader paper start ← deploy as background daemon
│
▼
[agent edits strategy.py] ← daemon hot-reloads, no restart needed
All state lives locally in ~/.agenttrader/. No cloud backend. No app account required. PMXT market data does not require an API key.
Quickstart
Prerequisites
python --version # 3.12+ required
pip --version
Install PMXT dependencies (Python package + Node.js sidecar):
pip install pmxt
npm install -g pmxtjs
Install
pip install agenttrader
agenttrader init
Verify it works
agenttrader sync --platform polymarket --days 2 --limit 5 --json
agenttrader markets list --json
If you see markets in the output, you're ready.
Optional but recommended for full-history backtests:
agenttrader dataset verify
agenttrader dataset download
Using agenttrader With Your Agent
There are two ways to connect your agent to agenttrader. MCP is recommended — it's the most natural interface for agents. CLI mode works as a fallback if MCP isn't available.
Option A: MCP (Recommended)
MCP lets your agent call agenttrader tools as native function calls — no shell commands, no output parsing. The agent calls get_markets(), run_backtest(), start_paper_trade() directly inside its reasoning loop.
Setup for Claude Code
Add this to .claude/mcp.json in your project directory:
{
"mcpServers": {
"agenttrader": {
"command": "agenttrader",
"args": ["mcp"]
}
}
}
Restart Claude Code. The MCP server starts automatically — you never run agenttrader mcp manually.
Setup for Cursor
In Cursor settings → MCP → add server:
{
"name": "agenttrader",
"command": "agenttrader",
"args": ["mcp"],
"transport": "stdio"
}
Setup for any other MCP-compatible agent
{
"mcpServers": {
"agenttrader": {
"command": "agenttrader",
"args": ["mcp"]
}
}
}
The transport is stdio. The agent spawns agenttrader mcp as a subprocess and communicates over stdin/stdout. No port, no server to keep running.
Available MCP tools
| Category | Tools |
|---|---|
| Markets | get_markets, get_price, get_history, match_markets, research_markets |
| Strategy | validate_strategy, run_backtest, validate_and_backtest, get_backtest, list_backtests |
| Paper trading | start_paper_trade, get_portfolio, stop_paper_trade, list_paper_trades |
| Data | sync_data |
MCP response behavior
- Errors now include a machine-actionable
fixfield when possible. get_historyreturns analytics by default (current price, 7d avg delta, 24h change, trend, volatility).get_historyraw points are optional viainclude_raw=true(default isfalseto reduce token usage).research_marketscomposessync_data -> get_markets -> get_historyinto one call.validate_and_backtestcomposesvalidate_strategy -> run_backtestinto one call.
Example agent prompts (MCP mode)
Once connected, give your agent natural language goals:
Use agenttrader to find the 10 most liquid Polymarket politics markets.
Build a mean reversion strategy in ./strategy.py. Backtest it from
2024-06-01 to 2024-12-31 with $10,000 starting cash. Iterate until
Sharpe ratio > 1.0 and max drawdown better than -20%. Then start
paper trading and report the portfolio_id.
Check my current paper trading portfolio. If any position has been
open for more than 7 days with negative PnL, update the strategy
to add a stop-loss and let me know what changed.
Find prediction markets on both Polymarket and Kalshi covering the
same event. Write an arbitrage strategy that trades the price
discrepancy when it exceeds 3 cents. Backtest it and paper trade it.
The agent will chain tool calls autonomously — setting up data, writing and editing strategy.py, running backtests, reading metrics, iterating, and deploying — without you touching a terminal.
Option B: CLI Mode
If your agent can run shell commands (most can), it can use agenttrader without any MCP setup. Every command supports --json for clean, parseable output.
Setup
No configuration needed beyond the initial install. Your agent runs commands like:
agenttrader sync --platform polymarket --days 7 --limit 20 --json
agenttrader markets list --platform polymarket --json
agenttrader validate ./strategy.py --json
agenttrader backtest ./strategy.py --from 2024-01-01 --to 2024-06-01 --json
agenttrader paper start ./strategy.py --cash 5000 --json
agenttrader paper status <portfolio_id> --json
Example agent prompts (CLI mode)
For Claude Code without MCP configured:
Use the agenttrader CLI to research Polymarket crypto markets.
Run: agenttrader markets list --platform polymarket --category crypto --json
Then build a strategy in ./strategy.py and iterate using:
agenttrader validate ./strategy.py --json
agenttrader backtest ./strategy.py --from 2024-01-01 --to 2024-06-01 --json
Keep iterating until Sharpe > 1.0, then run:
agenttrader paper start ./strategy.py --json
Report the portfolio_id when done.
The JSON contract
Every --json response follows this shape:
{ "ok": true, "...": "..." }
{ "ok": false, "error": "ErrorType", "message": "Human-readable details", "fix": "Next action for the agent" }
Strategy errors include line numbers and tracebacks so agents can self-heal:
{
"ok": false,
"error": "StrategyError",
"message": "name 'undefined_var' is not defined",
"file": "./strategy.py",
"line": 14,
"traceback": "Traceback (most recent call last):\n ..."
}
The Agent Iteration Loop
Whether using MCP or CLI, the core loop looks like this:
1. ensure backtest data is available (`agenttrader dataset verify`, then `agenttrader dataset download`)
2. inspect markets + history
3. write strategy.py
4. validate --json → fix any errors
5. backtest --json → read Sharpe, drawdown, win rate
6. edit strategy.py → improve based on metrics
7. repeat 4–6 until satisfied
8. paper start → deploy daemon
9. paper status --json → monitor
10. edit strategy.py live → daemon hot-reloads automatically
Steps 3–7 are where agents spend most of their time. A well-prompted agent will iterate 5–10 times before deploying, each time reading the structured backtest metrics and making targeted improvements to the strategy logic.
Strategy Authoring
Strategies are plain Python files that subclass BaseStrategy. Write them in any editor — or let your agent write them.
from agenttrader import BaseStrategy
class MyStrategy(BaseStrategy):
def on_start(self):
# Subscribe to markets — called once at startup
self.subscribe(platform="polymarket", category="politics")
def on_market_data(self, market, price, orderbook):
# Called on every price update for subscribed markets
history = self.get_history(market.id, lookback_hours=48)
if len(history) < 2:
return
avg = sum(h.yes_price for h in history) / len(history)
if price < avg - 0.08 and self.get_position(market.id) is None:
self.buy(market.id, contracts=20)
self.log(f"BUY {market.title[:40]} @ {price:.3f}")
elif price > avg + 0.05 and self.get_position(market.id):
self.sell(market.id)
self.log(f"SELL {market.title[:40]} @ {price:.3f}")
def on_schedule(self, now, market):
# Called every 15 minutes — use for time-based logic
hours_left = (market.close_time - now.timestamp()) / 3600
if hours_left < 3 and self.get_position(market.id):
self.sell(market.id)
self.log(f"Pre-expiry close: {hours_left:.1f}h left")
def on_resolution(self, market, outcome, pnl):
# Called when a market resolves
self.log(f"Resolved: {outcome}, PnL: ${pnl:.2f}")
Available methods inside a strategy:
# Subscribe + discover
self.subscribe(platform, category, tags, market_ids)
self.search_markets(query, platform)
# Price + data (time-bounded in backtesting — no look-ahead bias)
self.get_price(market_id) # current YES price (0.0–1.0)
self.get_orderbook(market_id) # bids, asks, best_bid, best_ask, mid
self.get_history(market_id, lookback_hours) # list of PricePoint objects
# Portfolio
self.get_position(market_id) # open Position or None
self.get_cash() # available cash
self.get_portfolio_value() # cash + mark-to-market positions
# Orders
self.buy(market_id, contracts, side="yes", order_type="market")
self.sell(market_id, contracts=None) # None = sell entire position
# Utilities
self.log(message) # appears in dashboard + paper status
self.set_state(key, value) # persist state across calls
self.get_state(key, default)
Do not call PMXT directly from a strategy. Do not import requests, httpx, pmxt, or any networking library. All data access goes through self.* methods.
All Commands
Setup
agenttrader init
agenttrader config show
agenttrader dataset verify
agenttrader dataset download
Data Sync
agenttrader sync # live sync for paper trading
agenttrader sync --platform polymarket --days 7
agenttrader sync --platform kalshi --days 7
agenttrader sync --resolved --days 365 --platform polymarket --json # resolved/expired markets
agenttrader sync --markets <id1> --markets <id2> # specific markets
agenttrader sync --json
sync is still used for paper trading and live cache updates. Backtesting automatically prefers the parquet dataset when available.
Markets
agenttrader markets list
agenttrader markets list --platform polymarket --limit 20 --json
agenttrader markets list --category politics --min-volume 50000
agenttrader markets price <market_id> --json
agenttrader markets history <market_id> --days 30 --json
agenttrader markets screen --condition "current_price < 0.30" --json
agenttrader markets screen --condition "price_vs_7d_avg < -0.10" --platform polymarket --limit 20 --json
agenttrader markets match --polymarket-slug "<slug>"
agenttrader markets match --kalshi-ticker "<ticker>"
markets screen runs only on local cache data (SQLite) and supports fixed metric expressions:
price_vs_7d_avg, current_price, volume, days_until_close, price_change_24h with operators <, >, <=, >=, ==.
Validate
agenttrader validate ./strategy.py
agenttrader validate ./strategy.py --json
Backtest
agenttrader backtest ./strategy.py --from 2024-01-01 --to 2024-06-01
agenttrader backtest ./strategy.py --from 2024-01-01 --to 2024-06-01 --cash 10000
agenttrader backtest ./strategy.py --from 2024-01-01 --to 2024-06-01 --json
agenttrader backtest list --json
agenttrader backtest show <run_id> --json
Backtest results include:
{
"metrics": {
"total_return_pct": 32.4,
"sharpe_ratio": 1.84,
"sortino_ratio": 2.31,
"max_drawdown_pct": -12.3,
"win_rate": 0.61,
"profit_factor": 2.14,
"calmar_ratio": 2.63,
"avg_slippage": 0.008,
"total_trades": 47
},
"resolution_accuracy": {
"bought_yes_resolved_yes_pct": 0.64,
"bought_no_resolved_no_pct": 0.58,
"sample_size": 47
},
"by_category": {
"politics": { "trades": 23, "win_rate": 0.65, "return_pct": 18.2 },
"sports": { "trades": 14, "win_rate": 0.43, "return_pct": -4.1 }
},
"equity_curve": [...],
"trades": [...]
}
Paper Trading
agenttrader paper start ./strategy.py --json
agenttrader paper start ./strategy.py --cash 5000 --json
agenttrader paper start ./strategy.py --no-daemon --json # blocking, for testing
agenttrader paper list --json
agenttrader paper status <portfolio_id> --json
agenttrader paper compare <portfolio_id_1> <portfolio_id_2> --json
agenttrader paper compare --all --json
agenttrader paper stop <portfolio_id> --json
agenttrader paper stop --all --json
Hot-reload: edit strategy.py while a paper trade is running — the daemon detects the change and reloads automatically. Portfolio state is preserved. No restart needed. paper status shows reload_count to confirm it happened.
paper compare computes side-by-side stats per portfolio from existing SQLite tables (paper_portfolios, positions, trades): portfolio value, unrealized PnL, win rate, avg PnL per sell trade, open positions, and reload count.
Experiments
agenttrader experiments log <backtest_run_id> --note "baseline" --tags "politics,mean-reversion" --json
agenttrader experiments log --portfolio <portfolio_id> --note "live run snapshot" --json
agenttrader experiments list --json
agenttrader experiments note <experiment_id> "updated note" --json
agenttrader experiments show <experiment_id> --json
agenttrader experiments compare <exp_id_1> <exp_id_2> --json
Experiments are persisted in ~/.agenttrader/experiments.json and are sorted newest-first in experiments list.
Dashboard
agenttrader dashboard # http://localhost:8080
agenttrader dashboard --port 9090
Read-only local dashboard showing active paper trades, positions, trade history, strategy logs, and backtest results with equity curves.
Dashboard routes include:
#/overview#/paperpaper portfolios#/compareside-by-side stats for all running paper portfolios#/backtestsbacktest runs#/marketscached markets
Maintenance
agenttrader prune --older-than 90d --dry-run --json
agenttrader prune --older-than 90d --json
Local Storage
~/.agenttrader/
├── config.yaml # scheduler and sync preferences
├── db.sqlite # markets, backtests, positions, trade ledger
├── data/ # parquet dataset (polymarket + kalshi history)
├── experiments.json # experiment memory/log for strategy iterations
├── logs/
│ └── performance.jsonl # call timing logs (CLI + MCP)
└── orderbooks/ # compressed orderbook snapshots by market/day
db.sqlite is a standard SQLite file. Agents can query it directly with SQL for custom analysis — for example, SELECT * FROM markets WHERE volume > 10000 ORDER BY volume DESC.
PMXT market-data calls are unauthenticated; no API key is required for sync and paper-trading workflows.
Performance logging is enabled by default. Every CLI command and MCP tool call appends a JSON line to ~/.agenttrader/logs/performance.jsonl with operation, status, duration_ms, session_id, and timestamps.
Troubleshooting
| Problem | Fix |
|---|---|
agenttrader: command not found |
Activate your virtual environment: source .venv/bin/activate |
| Not initialized | agenttrader init |
| Market not in cache | agenttrader sync ... |
| Strategy validation errors | agenttrader validate ./strategy.py --json — read errors array |
| Backtest has no trades | Date range has no price movement — try wider range or sync more markets |
| Dashboard shows blank | Ensure agenttrader dashboard is running and open http://127.0.0.1:8080; check command logs for startup errors |
| MCP not connecting | Verify agenttrader mcp runs without error; check .claude/mcp.json path |
Development
git clone https://github.com/yourname/agenttrader
cd agenttrader
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
ruff check agenttrader tests
python -m compileall agenttrader tests
# Integration tests
python tests/integration/test_full_workflow.py
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agenttrader-0.3.3.tar.gz.
File metadata
- Download URL: agenttrader-0.3.3.tar.gz
- Upload date:
- Size: 72.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6288c1219321b442fd4c013d734ad6459ad9755d15f455119725eac2a9353a52
|
|
| MD5 |
7ac51e142d3f21641bfe41df68eeb8ca
|
|
| BLAKE2b-256 |
34efbc7783f9e2141cbc19a65eb853d04b06f05977a45cbe7bbaa9dbe32e1f70
|
Provenance
The following attestation bundles were made for agenttrader-0.3.3.tar.gz:
Publisher:
publish.yml on finnfujimura/agenttrader
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agenttrader-0.3.3.tar.gz -
Subject digest:
6288c1219321b442fd4c013d734ad6459ad9755d15f455119725eac2a9353a52 - Sigstore transparency entry: 1002822600
- Sigstore integration time:
-
Permalink:
finnfujimura/agenttrader@c0f96599263f42cbe2c42c801d5f405a1ab54050 -
Branch / Tag:
refs/tags/v0.3.3 - Owner: https://github.com/finnfujimura
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c0f96599263f42cbe2c42c801d5f405a1ab54050 -
Trigger Event:
push
-
Statement type:
File details
Details for the file agenttrader-0.3.3-py3-none-any.whl.
File metadata
- Download URL: agenttrader-0.3.3-py3-none-any.whl
- Upload date:
- Size: 80.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee8f1889129ec16a8dfccf784a57dfc3fc15695605122f222abc26f6e3a28355
|
|
| MD5 |
e3dbc30201513431a801349a9cec1513
|
|
| BLAKE2b-256 |
2833d69aacf534a251c0f0a773823db3ddaa9e38895f8fa195347803cb0fab72
|
Provenance
The following attestation bundles were made for agenttrader-0.3.3-py3-none-any.whl:
Publisher:
publish.yml on finnfujimura/agenttrader
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agenttrader-0.3.3-py3-none-any.whl -
Subject digest:
ee8f1889129ec16a8dfccf784a57dfc3fc15695605122f222abc26f6e3a28355 - Sigstore transparency entry: 1002822602
- Sigstore integration time:
-
Permalink:
finnfujimura/agenttrader@c0f96599263f42cbe2c42c801d5f405a1ab54050 -
Branch / Tag:
refs/tags/v0.3.3 - Owner: https://github.com/finnfujimura
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c0f96599263f42cbe2c42c801d5f405a1ab54050 -
Trigger Event:
push
-
Statement type: