9.9 KiB
项目全流程
Polymarket Copy Trader — 一个自动跟单系统:监控 Polymarket 上最赚钱的钱包,找出多个钱包同时下注的"共识信号",然后发送 Telegram 通知(可选自动执行)。
启动命令
python -m src.main run # 主程序:跑整个循环(6 个 asyncio 任务)
python -m src.main dashboard # Web 看板(FastAPI,端口 8518)
python -m src.main stats # CLI 查看命中率 / PnL 统计
python -m src.main backfill # 手动回填已 resolve 市场的信号结果
python -m src.main pool # 立即重建钱包池并退出
python -m src.main test-stream # 测试交易流轮询
1. 钱包池构建(Pool Build)
启动时,从 Polymarket 公开 API 拉取 24h 交易量最高的 200 个活跃市场,对每个市场拉取前 30 名持仓者,聚合后筛选出约 100 个最赚钱的钱包(PnL 最高、交易笔数多、覆盖品类多)。这 100 个钱包就是"被跟单目标"。
PnL 计算的数据源:对每个候选钱包并行调用
/positions(当前持仓 cashPnl)+/trades(近 30 天成交)+/closed-positions(近 30 天已平仓 realizedPnl)。/closed-positions用sortBy=TIMESTAMP+offset翻页拉满整个 30 天窗口,避免服务端 50 条上限导致漏数据。详见data_api.py:get_closed_positions_since。
2. 交易流监控(Trade Stream)
每隔 30 秒,对这 100 个钱包轮询 Polymarket Data API,抓取他们最新的交易记录。每笔交易触发 on_trade 回调,交给信号聚合器处理。
3. 共识信号聚合(Signal Aggregator)
对每笔交易做 五层过滤:
- 交易金额 ≥ 500 USD(
MIN_TRADE_SIZE_USD,可在 .env 调整) - 价格在 0.1 ~ 0.9 之间(
MIN_PRICE/MAX_PRICE,过滤已决议或极端价格市场) - 钱包信誉分 ≥ 0.3(
MIN_CREDIBILITY) - 同一钱包对同一市场 10 分钟内只算一次(
WALLET_DEBOUNCE_SECONDS) - 通过则进入"市场累加器"
共识规则:在 10 分钟窗口内(CONSENSUS_WINDOW_SECONDS),有 ≥ 2 个钱包 朝同一方向(都买或都卖)下注同一市场,且正反面力量差 ≥ 0.4(CONSENSUS_STRENGTH_THRESHOLD),就发出信号。
信号包含:市场名称、方向、入场价、聚合强度、参与钱包数、Kelly 公式建议仓位。
4. 信号输出
- 存库:写入 SQLite(
data/copytrader.db),供 Dashboard 展示 - Telegram 通知:发送格式化消息(市场名、方向、建议仓位、来源钱包数)
- Dashboard:Web 页面实时展示 Top 钱包排名 + 最近信号(每 30 秒自动刷新)
5. 贝叶斯信誉更新(Bayesian Updater)
每 60 分钟(CREDIBILITY_UPDATE_MINUTES)运行一次:根据钱包历史交易的实际表现(赢/亏),用贝叶斯方法更新每个钱包的信誉分,形成 先验→后验 的正反馈。表现好的钱包权重上升,表现差的下降。
数据源:调用
/closed-positions(sortBy=TIMESTAMP+ 翻页)拉取近BAYESIAN_DECAY_DAYS(默认 14 天)的全部平仓记录,累加realizedPnl作为更新依据。API 返回的已经全部在窗口内,无需在 Python 端二次过滤。
6. 钱包池定期刷新
每 24 小时(WALLET_REFRESH_HOURS)重建一次钱包池,淘汰表现差的,引入新晋赚钱钱包。
7. 信号结果回填(Outcome Resolver)
第 6 个 asyncio 循环,每 15 分钟(OUTCOME_RESOLVE_MINUTES)扫描 copy_signals 表中 resolved_at IS NULL 的信号,调用 Gamma /markets API 拉取市场结算结果,回填以下字段:
| 字段 | 含义 |
|---|---|
exit_price |
1.0(赢) / 0.0(输) |
pnl_usd |
理论 PnL(win = S*(1-p)/p,loss = -S,S=suggested_size,p=entry_price) |
outcome_correct |
1(赢) / 0(输) / -1(市场取消) |
resolved_at |
市场 closedTime |
PnL 模型说明:理论"全仓按 entry_price 成交" PnL,不建模滑点。用于策略评估,非真实账户 PnL。
手动 backfill:python -m src.main backfill --batch 500 一次性回填历史信号。
解析逻辑严格遵循 Gamma
/markets文档:closed=true+outcomePrices中价格 ≥ 0.99 的 outcome 即为赢家。
8. 策略分析看板(Analytics Dashboard)
访问 http://localhost:8518/analytics,展示 6 个维度的聚合统计,每个维度都用于调参:
| 维度 | 调参目标 |
|---|---|
| 强度分桶命中率 | CONSENSUS_STRENGTH_THRESHOLD — 理想下强度越高命中率越高 |
| 共识钱包数分桶 | CONSENSUS_MIN_WALLETS — 理想下钱包数越多命中率越高 |
| 各时段命中率 | 优化 pool_refresh 调度时间 |
| 品类表现 | 发现跟单有效的品类 |
| 钱包排行榜 | 反向验证信誉分公式 — 高信誉钱包应排前列 |
| 总体表现 | 总命中率 / 总 PnL / 平均 PnL |
API 端点:/api/outcomes, /api/stats/by-strength, /by-wallet-count, /by-wallet, /by-category, /by-hour, /api/credibility-history?address=...
调参闭环:看 Analytics → 发现某分桶表现差 → 调 .env → 重启 → 等待新数据 → 再看 Analytics 验证。
.env 关键配置
| 分组 | 配置项 | 说明 |
|---|---|---|
| 资金 | INITIAL_CAPITAL_USD |
模拟本金(默认 $10000) |
KELLY_FRACTION |
Kelly 仓位比例(0.5 = 半 Kelly) | |
| 价格防护 | MIN_PRICE / MAX_PRICE |
只跟单价格在 0.1~0.9 的市场 |
| 交易规则 | MIN_TRADE_SIZE_USD |
忽略低于此金额的交易(默认 500) |
ENABLE_EXECUTION |
设为 true 才真执行(默认 false 只通知) |
|
| 钱包池 | WALLET_POOL_SIZE |
监控钱包数(默认 100) |
WALLET_PNL_MIN_USD |
钱包最低 PnL 门槛(默认 $5000) | |
WALLET_REFRESH_HOURS |
池刷新间隔(默认 24h) | |
| 共识 | CONSENSUS_MIN_WALLETS |
触发信号最少钱包数(默认 2) |
CONSENSUS_WINDOW_SECONDS |
共识窗口(默认 600s) | |
CONSENSUS_STRENGTH_THRESHOLD |
正反面差值门槛(默认 0.4) | |
MIN_CREDIBILITY |
钱包最低信誉分(默认 0.3) | |
WALLET_DEBOUNCE_SECONDS |
去重窗口(默认 600s) | |
| 信誉分 | BAYESIAN_PRIOR_SKILL |
初始信誉分(默认 0.5) |
BAYESIAN_DECAY_DAYS |
PnL 累计窗口(默认 14 天) | |
BAYESIAN_STEP |
贝叶斯步长(默认 0.05) | |
CREDIBILITY_UPDATE_MINUTES |
更新间隔(默认 60min) | |
| 信号回填 | OUTCOME_RESOLVE_MINUTES |
回填循环间隔(默认 15min) |
OUTCOME_RESOLVE_BATCH_SIZE |
每批最多扫描信号数(默认 50) | |
OUTCOME_RESOLVE_TIMEOUT |
单次 /markets 请求超时(默认 5s) | |
| Telegram | TELEGRAM_ENABLED |
是否启用(默认 false) |
TELEGRAM_BOT_TOKEN |
BotFather 给的 token(必填) | |
TELEGRAM_CHAT_ID |
发送目标的 chat ID(必填) | |
| 代理 | HTTP_PROXY |
需要翻墙时填写(如 http://127.0.0.1:7890) |
| 轮询 | USER_POLL_INTERVAL_SECONDS |
钱包轮询间隔(默认 30s) |
STREAM_MAX_TRADES_PER_WALLET |
每次拉取最多交易数(默认 20) | |
| 数据库 | DB_PATH |
SQLite 路径(默认 data/copytrader.db) |
| CLOB | POLY_API_KEY 等 |
仅启用实盘执行时填 |
数据库并发模型
SQLite 由四个异步协程并发写入:pool_refresh(写钱包池)、trade_stream(写信号)、credibility(写信誉分 + 信誉历史)、outcome_resolve(写信号回填),均通过 asyncio.to_thread 在线程池里执行。CopyTraderDatabase 的并发治理策略(见 db/database.py):
- 单一长连接 +
check_same_thread=False(不再每次操作开关连接) threading.RLock串行化所有方法,进程内永远只有一个 writer- WAL 模式 +
synchronous=NORMAL:多读不阻塞写 busy_timeout=10000ms:跨进程写冲突(如 Dashboard 进程)等待 10s 而非立即抛database is locked
数据库表结构
| 表 | 用途 | 关键字段 |
|---|---|---|
wallet_targets |
跟单目标钱包池 | address, credibility, health_score, pnl_30d_usd |
copy_signals |
发出的共识信号 | condition_id, side, entry_price, aggregated_strength, n_contributors, outcome_correct, pnl_usd, resolved_at |
credibility_history |
信誉分演化历史(append-only) | address, credibility, realized_pnl_window, n_closed, updated_at |
trade_executions |
实盘执行记录 | signal_id, order_id, status, executed_at |
pool_state |
KV 持久化(重启恢复) | key, value(JSON) |
pool_state 存储的 key:last_pool_refresh、last_credibility_update、open_accumulators(共识累加器)、stream_last_seen(交易去重)、wallet_debounce(钱包级去重)。