Files

164 lines
9.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 项目全流程
**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
对每笔交易做 **五层过滤**
1. 交易金额 ≥ 500 USD`MIN_TRADE_SIZE_USD`,可在 .env 调整)
2. 价格在 0.1 ~ 0.9 之间(`MIN_PRICE` / `MAX_PRICE`,过滤已决议或极端价格市场)
3. 钱包信誉分 ≥ 0.3`MIN_CREDIBILITY`
4. 同一钱包对同一市场 10 分钟内只算一次(`WALLET_DEBOUNCE_SECONDS`
5. 通过则进入"市场累加器"
**共识规则**:在 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` | 理论 PnLwin = `S*(1-p)/p`loss = `-S`S=suggested_sizep=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, valueJSON|
`pool_state` 存储的 key`last_pool_refresh``last_credibility_update``open_accumulators`(共识累加器)、`stream_last_seen`(交易去重)、`wallet_debounce`(钱包级去重)。