fix: portfolio.py OverflowError on out-of-range endDate; add Chinese docs; utf-8 dashboard write

- live/portfolio.py:336 — catch OverflowError alongside ValueError in _end_ts
  (mktime on out-of-range dates raises OverflowError on Windows; previously
  crashed the whole window_bets() on certain operator-resolved markets)
- live/dashboard.py — open(out,'w',encoding='utf-8') so dashboard.html
  renders Chinese titles correctly on Windows
- .gitignore — ignore session-local artifacts (PolymarketDocumentation-main/,
  wwf_state.json, live/portfolio.json, etc.)
- Add Chinese-language docs: USAGE.md (项目使用手册), live/仪表盘说明.md,
  live/钱包筛选完整流程.md, 实盘配置.md
This commit is contained in:
2026-07-17 17:37:06 +08:00
parent 45af082633
commit b916633528
8 changed files with 1374 additions and 4 deletions
+8
View File
@@ -42,3 +42,11 @@ live/watch_prejune*.json
live/history/
live/slug_cache.json
archive/local/
# session-local artifacts not part of the upstream repo
PolymarketDocumentation-main/
wwf_state.json
live/portfolio.json
polymarket_connectivity_test.py
python portfolio.py.md
新建 Markdown 文件.md
+555
View File
@@ -0,0 +1,555 @@
# 项目使用手册(USAGE.md
> Polymarket 跟单系统 — 找会赢的钱包 → 纸盘回测 → 实盘跟单
>
> 写于 2026-07-17。配合 `README.md`、`live/README.md`、`live/仪表盘说明.md`、`HANDOFF.md`、`实盘配置.md` 一起看。
---
## 0. 三句话理解项目
1. **找钱包**:从 Polymarket 全网数据里,按 5 道统计门(z-score、FDR、样本外一致性等)筛出 ~3% 真有 edge 的钱包,落到 `live/watch_skilled.json`;再用"信念门槛(每钱包自己下注前 20% 才叫信念单)"筛出 12 个可复制的,落到 `live/watch_sharps.json`
2. **回测**:拿这些钱包的历史已结算单,按 4% 资金 + 真实手续费 + 0.5% 滑点重放一遍,写出 $1000 的纸盘账户进出,落到 `live/portfolio.json`(纸盘与实盘共用同一个回放引擎,确保一致)。
3. **跟单**:跟单 bot`copybot.py`)24/7 监听这些钱包的新交易,paper 模式只记账,live 模式(`--live` + `LIVE_CONFIRM`)才真下单。
整个系统分三层:**离线找钱包 + 纸盘验证 → 跟单 bot 跑 paper → 跟单 bot 切 live**。
---
## 1. 仓库结构
```
winning-wallet-finder/
├── README.md ← 项目总览(论文背景、findings)
├── HANDOFF.md ← 接手文档(关键决策与已知坑)
├── FINDINGS.md ← 研究发现汇总
├── 实盘配置.md ← 实盘钱包/私钥配置(已写过)
├── ETHERSCAN_MIGRATION.md ← Etherscan 数据迁移说明
├── requirements.txt ← 依赖(核心用 Python 3 stdlib
├── smart_money.py ← Polymarket data-api 客户端(共用)
├── insider.py ← 内部人/聪明钱检测
├── copybot.py ← ★ 跟单 botpaper/live 两用)
├── copytrade.py ← 实盘下单引擎(py-clob-client 封装)
├── redeem.py ← 兑现中奖单
├── preflight_live.py ← live 前的只读凭证检查
├── polymarket_connectivity_test.py ← API 连通性测试
├── config.example.json ← 通用配置示例(无密钥)
├── config.live.example.json ← ★ 实盘配置模板(无密钥)
├── copybot_state.json ← bot 状态(持续累积)
├── copybot_fills.jsonl ← bot 成交记录(每行一笔)
├── fly.Dockerfile ← 云端部署镜像
├── fly.toml / fly.live.toml ← Fly.io 部署配置
├── live/ ← ★ 离线分析与纸盘回测
│ ├── README.md ← live/ 子模块说明
│ ├── 仪表盘说明.md ← dashboard.html 字段说明
│ ├── wallet筛选完整流程.md ← 钱包筛选细节
│ │
│ ├── enumerate.py ← 候选池发现(top traders
│ ├── collect.py ← 把候选钱包历史拉进 cache.duckdb
│ ├── cache.py ← DuckDB 缓存层(所有离线分析的"事实来源")
│ ├── skill.py ← 5 道门打分 → watch_skilled.json
│ ├── conviction_scan.py ← 信念钱包扫描
│ ├── validate_timing.py ← 复制可行性 + 费后 Copy P&L → watch_sharps.json
│ ├── sync_floors.py ← 把每钱包 p80 信念门槛同步进 copybot 配置
│ ├── payouts.py ← 链上 payout 校验(防幸存者偏差)
│ ├── trust.py ← 信任度评估(哪些 cache 行可信)
│ ├── slug_cache.py ← market → event 关联缓存
│ │
│ ├── portfolio.py ← ★ $1k 纸盘回测引擎
│ ├── portfolio.json ← portfolio.py 产物(仪表盘读这个)
│ ├── backtest.json ← ★ 纸盘配置(天数、钱包列表、class_pct)
│ ├── copybot.paper.json ← ★ paper 模式 bot 配置(bot 直接读)
│ ├── copybot_live.json ← paper bot 实时输出的 feed(仪表盘读)
│ ├── copybot_live_real.json ← live bot 的 feed
│ ├── watch_skilled.json ← skill.py 产物
│ ├── watch_sharps.json ← validate_timing.py 产物(仪表盘主表)
│ ├── conviction_wallets.json ← conviction_scan.py 产物
│ │
│ ├── dashboard.py ← 生成 dashboard.html
│ ├── dashboard.html ← ★ 自包含 HTML 仪表盘(双击打开)
│ ├── daily.sh ← ★ 一键每日流水线(见 §7)
│ ├── discord_daily.py ← Discord 每日摘要推送
│ │
│ └── tests/, archive/ ← 测试与归档研究代码
├── host/ ← 云端/部署辅助(Fly、桥接、钱包 bootstrap
├── wide/ ← 历史归档的 subgraph 扫描(frozen
├── tests/ ← 单元测试
└── PolymarketDocumentation-main/ ← 官方文档(参考用)
```
---
## 2. 数据流(一张图)
```
Polymarket data-api ──► enumerate.py ──► candidates.json
collect.py + cache.py ──► cache.duckdb (本地事实来源)
┌─────────────┼─────────────┐
▼ ▼ ▼
skill.py conviction_scan.py payouts.py/trust.py
│ │
▼ ▼
watch_skilled.json watch_sharps.json ──┐
│ │
▼ │
sync_floors.py │
│ │
▼ ▼
copybot.paper.json dashboard.html
│ ▲
▼ │
copybot.py (paper) ────┘
copybot_live.json ──► dashboard / GitHub Pages
cloud run (每日定时)
copybot.py --live ──► CLOB API ──► 真实成交
```
---
## 3. 常用命令速查
### 3.1 一次性:建库
```bash
pip install duckdb py-clob-client web3 # 实盘需要后两个
cd live
python enumerate.py 180 # 发现候选(过去 180 天)
python collect.py # 拉取所有候选钱包历史(首次慢,可断点续传)
python skill.py # 5 道门打分 → watch_skilled.json
python conviction_scan.py # 信念钱包扫描
python validate_timing.py # 复制可行性 → watch_sharps.json
python sync_floors.py # 把每钱包 p80 门槛写进 copybot 配置
python dashboard.py # 生成 dashboard.html
python portfolio.py --days 30 # 跑 30 天纸盘回测 → portfolio.json
```
### 3.2 日常:每日流水线
```bash
cd live
./daily.sh # 一键:发现 → 刷新 → 打分 → 仪表盘 → 提交推送 → Discord 摘要
```
按顺序跑:discover → freshen → re-score → sharps → floors → portfolio → dashboard → publish。
### 3.3 跑纸盘 bot24/7
```bash
python copybot.py --config copybot.paper.json --poll 60
```
- `--poll 60`:每 60 秒轮询一次(无 Alchemy webhook 时用这个)
- 默认用 WebSocket 订阅 RTDS(实时数据流),延迟最低
- `copybot_state.json` 自动持久化,重启可恢复
### 3.4 切实盘(**真钱**
```bash
export LIVE_PRIVATE_KEY=0x...
export LIVE_FUNDER_ADDRESS=0x...
export LIVE_SIGNATURE_TYPE=1 # 邮箱登录 = 1
export LIVE_CONFIRM="I understand real money is at risk"
python copybot.py --config config.live.json --live --poll 60
```
详见 `实盘配置.md`
### 3.5 其它常用命令
| 用途 | 命令 | 说明 |
| --- | --- | --- |
| 测试 API 连通性 | `python polymarket_connectivity_test.py` | 7 个端点一次测完 |
| 实盘前凭证检查 | `python preflight_live.py` | 只读,不会下单 |
| 单钱包 dry-run | `python copybot.py --test-wallet 0xabc...` | 走一遍流程但不持久化 |
| 强制刷特定钱包 | `python -c "import cache; cache.invalidate(['0xabc...'])"` | 跳过 14 天缓存 |
| 手动跑纸盘 | `python portfolio.py --wallets 0xabc,0xdef:whale --days 30 --out /tmp/p.json` | 临时换钱包跑 |
| 只跑一次回测 | `python copybot.py --poll-once --config copybot.paper.json` | 给 cron 用 |
| 把 paper feed 跟 model 比对 | 跑完 `portfolio.py` 后看 `history/calibration.csv` | 校准核心指标 |
| 重算某钱包门槛 | `python sync_floors.py` | 把 p80 写回 copybot 配置 |
---
## 4. 关键配置文件
### 4.1 `live/backtest.json`(纸盘回测用)
```json
{
"days": 30, // 回看窗口长度(滚动)
"class_pct": { // 每类钱包的下注比例(占总资金)
"volume": 0.04, // 普通钱包:每笔 4% 当前权益
"whale": 0.12 // whale12%
},
"wallets": [ // 要回测的钱包列表
{
"wallet": "0x...",
"name": "Kruto2027",
"class": "volume", // 或 "whale"
"floor": 80.0 // 信念门槛(可选;缺省从历史 p80 算)
}
]
}
```
跑:`python portfolio.py --days 7` 覆盖 days,或 `--wallets 0xabc,0xdef:whale` 临时换列表。
### 4.2 `live/copybot.paper.json`(纸盘 bot 配置)
```json
{
"mode": "paper",
"bankroll_usd": 1000.0, // 起始资金
"bankroll_pct": 0.04, // 每笔占当前权益的比例
"wallets": [...], // 跟哪些钱包
"follow": {
"buy_only": true, // 只跟买,不跟卖(卖通常意味着平仓)
"min_their_usd": 25.0, // 信号钱包下注 ≥ $25 才跟
"min_entry": 0.0,
"max_entry": 0.95, // 不买 > ¢95 的热门(赔率薄)
"class_pct": { "volume": 0.04, "whale": 0.12 }
},
"risk": {
"min_order_usd": 5.0, // Polymarket 单笔最小 $5
"max_per_event": 0 // 同事件并发数(0 = 不限)
},
"price_guard_abs": 0.05 // 价格偏离保护(信号价 vs 我看到的)
}
```
### 4.3 `config.live.example.json`(实盘模板)
```json
{
"mode": "live",
"bankroll_usd": 22.28, // ★ 必须等于你实盘钱包的真实余额(启动时和链上比对)
"bankroll_pct": 0.04,
"price_guard_abs": 0.05,
"taker_fee_rate": 0.03, // Polymarket V2 taker fee (2026-03-30 起)
"discord_webhook": "",
"follow": { ... },
"risk": { ... },
"live": {
"private_key": "", // ← 留空,靠 env 注入
"funder_address": "", // ← 留空,靠 env 注入
"signature_type": 1, // ← 邮箱登录固定 = 1
"order_type": "FAK", // Fill-And-Kill:能成交多少成交多少
"auto_redeem": false, // 兑现交给 redeem.py
"rpc_url": ""
},
"wallets": [...], // 实盘跟的钱包(必须跟 paper 一致)
"feed_path": "live/copybot_live_real.json",
"fill_log": "copybot_fills.live.jsonl",
"shadow_log": "rtds_shadow.live.jsonl"
}
```
> **强烈建议**:不要碰 `live.private_key`/`live.funder_address`,用环境变量注入(`LIVE_PRIVATE_KEY` 等),见 §3.4 与 `copybot.py:2303-2308`。
### 4.4 `live/watch_skilled.json`、`live/watch_sharps.json`
- 这两个是 **`skill.py` / `validate_timing.py` 的输出****不要手改**
- `daily.sh` 会每天重算并 commit/push
- 仪表盘(`dashboard.html`)直接读这两个文件
### 4.5 `config.example.json`(通用配置)
只放了 Discord webhook + Alchemy key + 一个 `watch` 列表,是 `config.live.example.json` 的简化版——**实盘请用后者**,别用这个。
---
## 5. 核心变量怎么改
### 5.1 改跟单金额
| 变量 | 在哪个文件 | 含义 | 推荐范围 |
| --- | --- | --- | --- |
| `bankroll_usd` | `copybot.paper.json``config.live.json` | 起始资金(实盘要等于链上余额) | 看你预算 |
| `bankroll_pct` | 同上 | 每笔占**当前权益**的比例(凯利式复利) | 2–8% |
| `class_pct.volume` / `.whale` | `backtest.json` + `copybot.paper.json``follow.class_pct` | 按钱包类型区分下注比例 | volume 26%, whale 815% |
| `min_order_usd` | `copybot.paper.json``risk.min_order_usd` | Polymarket 单笔最小 | ≥ $5 |
| `max_per_event` | `copybot.paper.json``risk.max_per_event` | 同事件最多几笔并发 | 1–3(避免一输全输) |
| `max_entry` | `copybot.paper.json``follow.max_entry` | 不买价格高于 ¢? 的票 | 0.850.95 |
**改纸盘 → 实盘要做的一致性检查**`daily.sh` 里已经写好自动警告):
```bash
python -c "import json; print(json.load(open('copybot.paper.json'))['follow']['class_pct'])"
python -c "import json; print(json.load(open('backtest.json'))['class_pct'])"
# 两个必须完全一致,否则纸盘和实盘会用不同金额
```
### 5.2 改跟哪些钱包
方法 A**推荐**:用 paper 配置)
1. 编辑 `live/copybot.paper.json``wallets` 数组
2. 同时编辑 `live/backtest.json``wallets`(纸盘回测跟同一个集)
3. 重启 bot
方法 B(**调试用**:CLI 临时覆盖)
```bash
python portfolio.py --wallets 0xabc,0xdef:whale,0x123:volume --days 7
python copybot.py --test-wallet 0xabc... # 只跑这一个
```
每钱包字段:
- `wallet`Polygon 地址(小写)
- `name`:仪表盘上显示的标签
- `class``volume`(只跟信念单)或 `whale`(跟所有单)
- `floor`:**信念门槛**(美元)。钱包下一笔 ≥ 这个金额才跟进;缺省时 `sync_floors.py` 自动从历史 p80 算
- `floor_pin``floor` 是否钉死(`sync_floors.py` 不覆盖钉死的值)
**建议流程**:先用 `dashboard.html` 挑出几个高 z / 高 OOS z 的 → 加进 `copybot.paper.json` → 跑一周 paper → 看 `portfolio.json` 的 equity 曲线 → 表现好的保留进 `config.live.json`
### 5.3 改回测窗口
```bash
python portfolio.py --days 7 # 7 天滚动窗口
python portfolio.py --days 30 # 默认
python portfolio.py --days 0 # 全历史(极慢,慎用)
```
`backtest.json` 里的 `"days"` 是默认值;CLI 参数优先。
### 5.4 改信念门槛
```bash
python sync_floors.py # 自动算每钱包 p80,写回 copybot.paper.json
python copybot.py --conviction-from-sharps live/watch_sharps.json # 用 sharps 表的 avg_bet
```
或者手改某钱包的 `floor` 字段(建议同时设 `floor_pin: true` 防止被自动覆盖)。
---
## 6. 完整流程(从零跑通)
### 6.1 第一次跑(建库)
```bash
# 0. 环境
pip install duckdb # 本地分析最低依赖
# 实盘才需要:pip install py-clob-client==0.34.6 web3==7.16.0
# 1. 找候选钱包(一次,约 2–5 分钟)
cd live
python enumerate.py 180 # 取过去 180 天最活跃的市场 → top traders → candidates.json
# 2. 拉所有候选钱包的历史(首次 30+ 分钟,断点续传)
python collect.py
# 3. 打分(秒级,全在本地 cache)
python skill.py # → watch_skilled.json71 个左右)
python conviction_scan.py # → conviction_wallets.json
python validate_timing.py # → watch_sharps.json12 个左右)
# 4. 同步门槛 + 生成仪表盘
python sync_floors.py # → copybot.paper.json 的 floor 字段
python dashboard.py # → dashboard.html
python portfolio.py --days 30 # → portfolio.json
# 5. 看结果
start dashboard.html # WindowsMac 用 `open dashboard.html`
cat portfolio.json | python -m json.tool | head -50
```
### 6.2 每日维护
```bash
cd live
./daily.sh # 一键全自动:发现 → 刷新 → 打分 → 仪表盘 → commit → push → Discord
```
`daily.sh` 内部做的事(按顺序):
1. `enumerate.py 14`:最近 14 天新市场的 top traders,合并进 `candidates.json`
2. `cache.invalidate(...)` + `collect.py`:watchlist 钱包强制重拉,新钱包补齐
3. `skill.py`5 道门打分(秒级)
4. `conviction_scan.py` + `validate_timing.py`:信念 + 复制可行性
5. `sync_floors.py`:把每钱包 p80 写回 bot 配置
6. `portfolio.py`:纸盘回测
7. 校准行:把 paper bot 当前 equity 跟 model 比对,写入 `history/calibration.csv`
8. `dashboard.py` + 快照到 `history/`
9. `git commit && git push`:把 JSON 推到 GitHub
10. `discord_daily.py`:推送每日摘要到 Discord
部署建议:cron 或 launchd 每天 10:00 跑一次。
### 6.3 跑纸盘 bot
```bash
cd live
python copybot.py --config copybot.paper.json --poll 60
```
观察:
- `copybot_live.json`:bot 实时写的 feed(仪表盘读这个)
- `copybot_state.json`:bot 内部状态(持仓、现金、追踪进度)
- `copybot_fills.jsonl`:每笔成交(追加写)
随时看:双击 `dashboard.html`,里面"Live"那一栏就是 bot 当前的书。
### 6.4 切到实盘
`实盘配置.md` 走完整流程:
1. 拿到私钥(邮箱登录走 https://reveal.magic.link/polymarket
2. 拿到 funder addresspolymarket.com/settings
3. 设环境变量(`LIVE_PRIVATE_KEY` 等)
4. `cp config.live.example.json config.live.json`,改 `bankroll_usd` 为真实余额
5. **先 dry-run**:不传 `--live`,只设 `LIVE_PRIVATE_KEY` 让 bot 跑 dry-run 验证下单链路
6. **正式开火**`python copybot.py --config config.live.json --live --poll 60`
7. 建议先小金额跑几天(`bankroll_usd: 20`),验证无误再加大
---
## 7. 核心算法一图流
### 7.1 找钱包(5 道门)
```
所有钱包 → 1) n ≥ 15 已结算
→ 2) z = (赢 Σp) / √Σp(1p) > 阈值
→ 3) BH-FDR @ 5% (控制假阳性)
→ 4) 时间切两半:z_oos > 0 OOS 验证)
→ 5) 总笔数 ≤ 2500 (剔除做市机器人)
= watch_skilled.json71 个)
```
详见 `live/skill.py`
### 7.2 找可复制的(信念 + Copy P&L
```
watch_skilled → 取每钱包自己下注的前 20%(p80 信念门槛)
→ 实时模拟:跟这些单,扣 0.5% 滑点 + 3% taker fee
→ 只留:copy_pnl > 0 且 active in 30d
= watch_sharps.json12 个)
```
**Copy P&L 是唯一可信的筛选指标**——胜率(win%)对"只卖赚钱"的 scalper 会骗你。详见 `live/validate_timing.py`
### 7.3 纸盘回测
```
信号(watch_sharps 或 backtest.json 里的 wallets
每个 in-window 的信念单:
- stake = bankroll_pct × 当前权益(或 floor 取大)
- entry_p = signal_p × 1.005+0.5% 滑点)
- fee = stake × 0.03 × (1p)
- settle: 跟到 resolution 或跟信号平仓
- capital 在 res_t 时释放(不是 endDatecache 存了真实结算时间)
- DD < 20% 时 stake 减半
- EVENT_CAP > 0 时同事件限笔数
portfolio.jsondashboard 直接读)
```
详见 `live/portfolio.py`
---
## 8. 仪表盘怎么读
打开 `live/dashboard.html`,**顶部 5 个卡片**给出池子总览(z 中位数、OOS z 中位数、各类型数量),**表格**列出每个通过 5 道门的钱包。
挑钱包的硬指标(按重要性):
1. **OOS z > 0**(必须):前半后半都赢
2. **z ≥ 5**(强烈推荐):显著超过市场
3. **alpha 0.20.4 高**:在长镜头冷门区有 edge(不是骑热门)
4. **type = Value**(推荐):真正可复制的 alpha 区
5. **总笔数 ≥ 30**:样本足够
避坑:
- **高 z 但 OOS z 负** = 运气
- **高胜率但 avg entry > 0.85** = Favorite-rider,赔率薄、深度差,复利赚不到
- **总笔数 < 15** = 样本不足
详细字段含义见 `live/仪表盘说明.md`
---
## 9. 故障排查
| 症状 | 原因 / 解决 |
| --- | --- |
| 跑 `portfolio.py``OverflowError: mktime argument out of range` | Polymarket 某些市场的 `endDate` 是 0001-01-01 这种 sentinel,超出 mktime 范围。已修过:`live/portfolio.py:336` 同时捕 `ValueError``OverflowError`。 |
| `urllib` 直连超时 | 国内被墙。本机设 `$env:HTTPS_PROXY = "http://127.0.0.1:7890"`;云服务器直连没问题(已测)。 |
| `daily.sh` 跑完但 portfolio.json 不更新 | 多半是 `collect.py` 还在拉(首次很慢)。等下一次或手动跑。 |
| dashboard.html 不显示某些钱包 | `skill.py` 的 5 道门把它们刷掉了。改 `skill.py` 顶部的阈值(min_resolved、z 阈值等)。 |
| `sync_floors.py` 把我的 `floor` 改了 | 是因为没设 `floor_pin: true`。钉死的字段不会被覆盖。 |
| `preflight_live.py` 报 INVALID_SIGNATURE | 私钥错了。邮箱登录走 https://reveal.magic.link/polymarket 重导。 |
| `preflight_live.py` 报 Invalid Funder Address | funder_address 错了。打开 polymarket.com/settings 看你的 Polygon profile address。 |
| paper bot 跑了但 `copybot_live.json` 没更新 | 检查磁盘权限;或 bot 启动时报错(看 stderr)。 |
---
## 10. 进阶
### 10.1 跑更宽的回测
`wide/` 里有冻结的 subgraph 扫描器,能挖 2024 之前的历史。如果你换了一批钱包想看更久:
```bash
cd wide
python ingest.py --subgraph ...
python score.py --min-bets 30
```
(参考 `wide/README.md`,这是历史归档,新研究都在 `live/`
### 10.2 改入场价上限
`live/portfolio.py` 顶部:
```python
MAX_ENTRY = float(os.environ.get("MAX_ENTRY", 0.95))
```
或 CLI
```bash
MAX_ENTRY=0.85 python portfolio.py --days 30
```
`copybot.paper.json``follow.max_entry` 同理。两者保持一致。
### 10.3 加新钱包
```bash
# 1. 把钱包加进 candidates
python -c "import json,os; c=set(json.load(open('candidates.json'))); c.add('0xNEW...'); json.dump(sorted(c), open('candidates.json','w'))"
# 2. 拉它的历史
python -c "import cache; cache.invalidate(['0xNEW...'])"
python collect.py
# 或单拉:
python -c "import cache; cache.get_bets('0xNEW...')"
# 3. 重新打分
python skill.py
python validate_timing.py
# 4. 看仪表盘,决定要不要加进 follow set
python dashboard.py
```
---
## 11. 一句话总结
- **找**:用 `live/skill.py` 找 71 个有 edge 的,`live/validate_timing.py` 留 12 个可复制的
- **测**:用 `live/portfolio.py` 跑纸盘回测,结果看 `live/portfolio.json``live/dashboard.html`
- **跑**:用 `python copybot.py --config live/copybot.paper.json --poll 60` 跑 paper
- **上**`cp config.live.example.json config.live.json` + 设 env + 加 `--live`
每天 `./daily.sh` 一次维护。
+1 -1
View File
@@ -36,7 +36,7 @@ td.name{font-weight:600}a{color:var(--blue);text-decoration:none}a:hover{text-de
.b{color:var(--green)}.s{color:var(--red)}
</style></head><body><div class="wrap">
<h1>Skilled Wallets <span style="color:var(--dim);font-weight:400;font-size:18px">· Polymarket</span></h1>
<p class="sub">71 wallets that beat their own entry prices and held up out-of-sample · generated 2026-07-15 00:59</p>
<p class="sub">71 wallets that beat their own entry prices and held up out-of-sample · generated 2026-07-15 20:19</p>
<div class="cards">
<div class="card"><div class="k">Validated wallets</div><div class="v">71</div></div>
<div class="card"><div class="k">Median z</div><div class="v">3.8</div></div>
+2 -2
View File
@@ -42,7 +42,7 @@ def main():
.replace("{{MEDOOS}}", f"{med_oos:.1f}").replace("{{NVAL}}", str(n_val)) \
.replace("{{NFAV}}", str(n_fav))
out = os.path.join(HERE, "dashboard.html")
open(out, "w").write(html)
open(out, "w", encoding="utf-8").write(html)
print(f"wrote {out} ({len(w)} wallets)")
@@ -170,4 +170,4 @@ render();
if __name__ == "__main__":
main()
main()
+1 -1
View File
@@ -333,7 +333,7 @@ def _end_ts(s):
for fmt in ("%Y-%m-%dT%H:%M:%S", "%Y-%m-%d"):
try:
return int(time.mktime(time.strptime(s, fmt)))
except ValueError:
except (ValueError, OverflowError):
continue
return 0
+207
View File
@@ -0,0 +1,207 @@
# 仪表盘(dashboard.html)说明文档
本文档详细说明 Polymarket Skilled Wallets 仪表盘的每一个字段、标签、颜色、操作方法。
---
## 一、这是什么
这是一个**自包含的 HTML 仪表盘(单文件,包含所有逻辑)。每次运行 `python dashboard.py` 时,它读取 `watch_skilled.json`skill.py 输出的 71 个通过统计筛选的钱包清单,重写 dashboard.html。
打开 `dashboard.html`
---
## 二、顶部卡片(五个关键指标)
| 卡片 | 数值含义 | 怎么看 |
|------|---------|--------|
| **Validated wallets** | 钱包总数(当前 71 个) | 通过 z-score + FDR + OOS > 0(这是候选池大小。数字越大,这套筛选越宽松;越小越严格。
| **Median z** | 所有钱包 z-score 的中位数 | z > 3 说明整体水平不错;< 0 说明平均不认真。<b>解读:> 3 表示中位数越高说明大多数钱包的 z 平均水平在"显著优于"大多数钱包 能够打败他们"的入场价。
| **Median OOS z** | 所有钱包的样本外 z 的中位数 | 绿色/红色表示该钱包的前半/后半数据分算出来。**这个数值为正还是为负。**绿色=正((前半和后半一致都赢是独立都正。**最关键的筛选门。
| **Value / longshot** | (长镜头价值型钱包数量 | 价值型=偏好入场价 0.5 以下的冷门/长镜头(长期赔率区,这是 alpha 真正集中的地方。
| **Favorite-rider** | 偏好骑乘型钱包数量 | 偏好偏好买入场价 0.85 以上的热门胜率高但是(入场价>0.85,看起来胜率高,**但赔率薄,难复制(深度不够。
---
## 三、表格字段详解
### 表头列含义(从左到右
| 列 | 标签 | 说明 |
|-----|------|------|
| # | 序号 | | 在 watch_skilled.json 中的排名(按 z 排序) |
| **Wallet** | 钱包名称 / 地址 | 名字是 Polymarket profile 链接 |
| **z** | 主要技能 z-score | **核心指标。衡量该钱包真正打败自己买入价战胜市场价格定价能力。 z = (实际胜利数 − Σ 入场价) / σ |
| **OOS z** | out-of-sample z | 前半/后半时间窗口 z-score 样本外验证防过拟合。 |
| **bets** | 胜利/总笔数 | 胜场 / 总场数 |
| **win%** | 胜率 | 胜利数 / 总笔数百分比(容易被"只买热门骗你"的钱包骗,不是主要参考 |
| **avg entry** | 平均入场价 | 平均入场价格 |
| **alpha 0.20.4** | 价值带占比 | 入场价 0.2–0.4 的长镜头 bet 占总笔数的百分比条图表示 |
| **type** | 类型标签 | Value/balanced/favorite
---
## 四、颜色编码详解
### 4.1 z 列颜色
| 颜色 | z 值 | 说明 |
|------|------|
| 🔴 默认 | z < 6 | 一般水平
| 🟢 绿色 | 6 ≤ z < 10 | 显著优秀
| 🟣 紫色 | z ≥ 10 | 极优秀(10 |
**解读:** z ≈ 3 约为 1/740 概率的随机事件;z ≈ 5 约为 1/350万。z=3 是一个常见的统计显著性边界。
### 4.2 OOS z 列颜色
| 颜色 | OOS z 值 |
|------|-----------|
| 🟢 绿色 | z ≥ 5 |
| 🟡 黄色 | 3 ≤ z < 5 |
| 🔴 红色 | z < 3 |
**为什么 OOS 比 z 更重要:** z 高可能是过拟合的结果;OOS 高才是"同一个技巧持续存在的信号。
### 4.3 钱包类型标签颜色
| 标签 | 颜色 | 判断条件 | 说明 |
|--------|------|---------|------|
| **Value**(价值型) | 🟢 绿色 | 平均入场价 < 0.5 | 买长镜头冷门,真正的 alpha 区 |
| **Balanced**(均衡型) | 🔵 蓝色 | 平均入场价 0.5–0.85 | 均衡分布 |
| **Favorite-rider**(骑乘型) | 🟡 黄色 | 平均入场价 ≥ 0.85 | 几乎只买热门,胜率高但赔率薄 |
---
## 五、操作指南
### 5.1 生成最新仪表盘
```powershell
cd C:\Users\Administrator\Desktop\winning-wallet-finder\live
python dashboard.py
```
输出类似:
```
wrote C:\Users\Administrator\Desktop\winning-wallet-finder\live\dashboard.html (71 wallets)
```
### 5.2 打开仪表盘
在资源管理器中双击 `dashboard.html`,或:
```powershell
start C:\Users\Administrator\Desktop\winning-wallet-finder\live\dashboard.html
```
### 5.3 搜索钱包
顶部搜索框输入钱包名称或地址片段。
### 5.4 筛选类型
下拉框:
- **All archetypes** — 显示全部
- **Value / longshot** — 只看价值型
- **Balanced** — 只看均衡型
- **Favorite-rider** — 只看骑乘型
### 5.5 排序方式
**Sort 下拉:
| 排序选项 | 含义 |
|---------|------|
| **z (skill)** | 按 z-score 排序(默认,降序) |
| **out-of-sample z** | 按样本外 z 排序 |
| **alpha-zone %** | 按 0.20.4 带占比排序 |
| **# bets** | 按总下注次数排序 |
| **win rate** | 按胜率排序 |
点击表头也可以直接排序(再次点击同一列切换升/降序)。
### 5.6 查看某个钱包的最近交易
**点击任意一行** → 会在该行下方展开一个详情行,显示:**
- 该钱包最近 8 笔交易(BUY/SELL + 市场 + 价格 + 实时拉取(来自 data-api.polymarket.com
- "open profile ↗" 链接跳转到 Polymarket 官方 profile 页面
这是手动检查某个钱包"到底在玩什么"的最快方式。
---
## 六、核心指标详解
### 6.1 z-score(技能评分)
**公式:** z = (实际获胜数 − Σ 入场价) / σ
**含义:** 用"预期获胜数 = Σ 入场价(入场价本身就是市场对该事件胜率的估计)。如果一个钱包总是以 0.30 价格买 YES,买了 100 次,赢了 50 次,那预期赢 30 次,实际赢 50 次,z-score = (50 30) / sqrt(100 × 0.3 × 0.7) ≈ 4.36 → 显著高于市场预期。
**为什么不用胜率:胜率会被"只买 ¢90 热门项"钱包骗,胜率高并不是优势,但 z-score 是稳健的。
### 6.2 OOS z(样本外 z
把时间切成前后两半,分别算 z-score,看是否一致。如果前半高、后半也高,说明不是过拟合。这是**最关键的筛选门。
### 6.3 alpha 0.20.4
入场价在 0.200.40(即 ¢20–¢40)的 bet 占总 bet 的占比百分比。这是长镜头冷门区,research 发现 research 研究发现真正的 alpha 真的集中在这里。越高说明越偏好好挖掘长镜头挖掘能力越强。
---
## 七、怎么用它挑钱包
### 7.1 找"好钱包的特征
| 条件 | 推荐 |
|------|------|
| z ≥ 5 | (显著优于市场 |
| OOS z ≥ 3 | (前半/后半一致 |
| alpha 0.2–0.4 高 | (买长镜头冷门的能力强 |
| Value 类型 | (真正有 alpha 的 |
### 7.2 需要避开的陷阱
| 现象 | 原因 |
|------|------|
| z 高但 OOS z 负 | 前半段表现好但后半段不行,可能是运气 |
| 胜率高但 avg entry 高 | Favorite-rider 骑乘热门型 — 高胜率但赔率薄,难复制 |
| z 高但总笔数少 | 样本量小,统计不显著 |
### 7.3 copybot 与仪表盘的关系
copybot 当前跟踪的 5 个钱包(Kruto2027 / 0xbadaf319 / gkmgkldfmg,都是从这 71 个里面**挑选出来的。它们的信念门槛(conviction floor)也是从这些历史数据算出来的。
换句话说:**仪表盘展示的是整个候选池的全貌;copybot 只抄"这 71 个里面"他们自己真正下重注的那几笔。
---
## 八、已知限制
1. **历史数据 ≠ 未来表现。这是样本内表现,真实复制会有滑点、fee、深度限制。
2. **封禁风险。优势钱包可能被 Polymarket 平台限制。
3. **OOS z 只是时间切分验证,不考虑市场风格切换后是否继续赢。
4. **实时交易拉取失败。点击行时,如果 data-api 可能返回空(网络问题或该钱包没有近期活动)。
---
## 九、快速对照表
| 你看到的 | 实际含义 |
|-----------|---------|
| z = 8.3 | 该钱包显著优于市场预期 |
| OOS z = 5.1 | 前半/后半都表现出一致,非过拟合 |
| bets 47/82 | 47 胜 / 82 笔已结算交易 |
| win% 57 | 57% 胜率 |
| avg entry 0.33 | 平均入场价 ¢33(长镜头冷门) |
| alpha 0.2-0.4 = 68% | 68% 的 bets 在长镜头冷门价值区(真正 alpha 区 |
| type Value | 价值型 |
---
*生成于 2026-07-15
+492
View File
@@ -0,0 +1,492 @@
# 钱包筛选 / 回测 / 实时跟踪 — 完整流程说明
本文档基于 `live/` 下各 Python 脚本(enumerate.py、collect.py、cache.py、skill.py、conviction_scan.py、validate_timing.py、portfolio.py、sync_floors.py、daily.sh)和 `copybot.py` 的真实代码逻辑,描述"从 Polymarket 全体交易者 → 71 个 z-score 合格钱包 → copybot 跟踪的钱包集合"是如何一步步产生的。
---
## 一、"71 个钱包"是什么?从哪来的?
### 1.1 它们是 `watch_skilled.json` 的内容
`dashboard.py` 第 28 行:
```python
w = json.load(open(os.path.join(HERE, "watch_skilled.json")))
```
`watch_skilled.json``skill.py` 的输出(skill.py 第 147 行),记录了通过 **5 门统计漏斗** 的候选钱包列表。当前你看到的 71 个,就是这个文件的条数。
### 1.2 71 个 ≠ copybot 跟踪的钱包
copybotpaper 模式)读的是 `live/copybot.paper.json`(也就是你 bot 实际用的 follow 配置),它只跟踪 **5 个** 钱包。这 5 个是从 `watch_skilled.json``validate_timing.py``watch_sharps.json``sync_floors.py` 这 4 层里最终挑出的"fee-aware 复制复现后仍盈利"的子集。
所以关系是:
```
candidates.json (~1-3 万候选)
↓ skill.py 的 5 门
watch_skilled.json (~71 个 z 合格)
↓ conviction_scan.py(信念画像)
conviction_wallets.json (~20-40 个)
↓ validate_timing.pyfee-aware 复制复现)
watch_sharps.json (~10-20 个可复制)
↓ sync_floors.py(写 p80 门槛到 bot 配置)
copybot.paper.json / backtest.json (copybot 跟踪的 5-10 个)
```
### 1.3 它们是什么时候筛选出来的?
`watch_skilled.json` 里的 71 个钱包 **不是固定时间** 的快照。看 daily.sh
```bash
# step 2 — 强制刷新 watchlist
python3 -c "import json,os,cache
wl=[]
for f in ('watch_skilled.json','watch_sharps.json'):
if os.path.exists(f):
wl += [w['wallet'] for w in json.load(open(f))]
if wl:
cache.invalidate(wl)" # ← 强制让当前 watchlist 的钱包下一次 get_bets 时重新拉数据
python3 collect.py
# step 3 — 重新评分(从缓存瞬间完成)
python3 skill.py
```
所以 `watch_skilled.json` 每跑一次 daily.sh 就会被覆盖一次。里面的每条数据都是 **基于该钱包过去 180 天窗口内已结算的所有 bets** 实时计算的 z-score。
**这意味着你现在看到的 71 个钱包,就是最近一次运行 skill.py 时,用当时的 bets 缓存数据算出来的结果。**
---
## 二、完整的"7 层"筛选流程
下面每一步都有明确的代码位置。
---
### 第 1 层:枚举候选池(enumerate.py → candidates.json
**问题**Polymarket 上有数万个活跃钱包,我们不可能全拉。
**做法**enumerate.py 第 113-156 行 main 函数):
1. **找到最近 N 天内已结算且流动性高的市场**
- `WINDOW_DAYS = 180`(默认),可通过命令行参数覆盖(daily.sh 传 14
-`https://gamma-api.polymarket.com/markets` 拉取
- `MIN_VOLUME = 20000`$20,000 以上才算)
- 每 100 个一页,并发 4 波,最多扫描 MAX_SCAN=25000 个市场,保留 MAX_MARKETS=1200 个
2. **对每个市场取 Top 30 名义金额交易者**
- 调用 `insider.market_traders(conditionId, top=30)`enumerate.py 第 123 行)
- 去重,统计每个钱包"出现在多少个不同市场"(`markets_seen` 计数)
3. **累积到 candidates.json**
- 每次运行不是覆盖,而是与旧的 `candidates.json` **合并**(第 138-151 行)
- 新钱包追加;已存在的钱包取更大的 markets_seen
- 最终按 markets_seen 降序写入 candidates.json
**输出**candidates.json(当前约 1-3 万个钱包,取决于累积运行次数)
**为什么按 markets_seen 排序**:在 many 个不同市场都下注的钱包更可能是认真研究的,不是只碰了一个市场就走运的。
---
### 第 2 层:收集钱包的历史 betscollect.py → cache.duckdb
**问题**:enumerate 只给出了候选钱包名单,没有他们的下注历史。每算一次 z-score 都要从 Polymarket 拉一次 API,非常慢(单钱包 5-30 秒)。
**做法**collect.py 第 31-55 行 + cache.py 全文件):
1.`candidates.json`,按 markets_seen 倒序
2. 区分 "全新钱包"(从未拉过)和 "过期钱包"(最后一次拉取已经超过 14 天)
- `MAX_AGE_DAYS = 14` 在 cache.py 第 44 行
3. 每轮只拉新的 + 最近过期的 `STALE_CAP = 2500` 个(第 28 行)
- 这个 cap 很重要:没有它,当整个候选池第一次同时跨过 14 天阈值时,一次 daily 会变成 40 小时的重拉
4.`cache.get_bets(wallet)` 拉取并写入 DuckDB(第 179 行 INSERT
**缓存机制**cache.py 核心逻辑,第 135-183 行):
- DuckDB 表 schema`bets(wallet, cond, asset, won, p, res_t, size, src, ts, resolved)`
- 对每个钱包,如果 `pulled_at` 距今 < 14 天(`MAX_AGE_DAYS`),直接读缓存返回,不碰 API
- 否则调用 `insider.resolved_bets(wallet, ...)` 拉取 180 天窗口的已结算 bets
- 写入时是 upsert 而非 overwrite:旧的历史数据会累积(永久归档)
- `get_bets` 读回时会对 p 值 clamp 到 [0.001, 0.999](第 18 行注释 + 130 行)
- 如果 API 拉取失败,**不会**把空结果写进 pulled 表,下次会重试(第 149 行 return []
**输出**cache.duckdbSQLite-like,当前约 1-3 万钱包 × 数十到数百条 bets)
---
### 第 3 层:可信数据过滤(trust.py,被 conviction_scan/validate_timing/portfolio 间接调用)
**问题**cache 里的某些 `res_t` 字段不是链上结算时间,而是钱包自己的"卖出时间"(当市场还未结算时,钱包已经卖了)。做市商钱包的 `won` 被标记为卖出方向价格 -> 结果看起来胜率极高("99% 赢"),这是虚高。
**做法**trust.py,被 conviction_scan.py 第 69 行的 `f"WITH {trust.cte(now)}"` 和 validate_timing.py 调用):
- `trust.cte(now)` 返回一个 SQL CTE,只保留:
- 有共识 `res_t`(跨钱包一致、非 fallback 的结算时间戳)
- `resolved IS NOT False`
- `pulled_at` 足够新
- 所有需要"真实结算结果"的统计(信念画像、复制复现、回测)都用这个 CTE 过滤后的行
这一步非常重要——**如果跳过 trust,整个 pipeline 会把高频做市商识别为"顶级 alpha 钱包"**。
---
### 第 4 层:5 门 z-score 统计漏斗(skill.py → watch_skilled.json
**核心公式**skill.py 第 50-57 行 zstats):
```
z = (实际获胜数 − Σ入场价) / √Σ(入场价 × (1 − 入场价))
```
Σ入场价 = 如果该钱包所有 bet 都以"市场估计的胜率"随机开奖,预期赢几场。z > 0 意味着它实际赢的比市场预期多。
**5 门门槛**skill.py 第 60-161 行 score_wallet + main 的选择逻辑):
| 编号 | 门 | 源码位置 | 含义 |
|-----|-----|---------|------|
| 1 | `MIN_N = 15` 已结算 bets | 第 37 行 + 第 70 行 | 样本量门槛 |
| 2 | `MAX_N = 2500` 排除超高频 | 第 39 行 + 第 70 行 | 排除做市机器人(单钱包几千条 bets 是做市不是 alpha |
| 3 | z > 0 | 隐式(z 排序后配合 FDR 阈值) | 实际胜率 > 市场预期 |
| 4 | Benjamini-Hochberg FDR @ 5% | 第 94-103 行 bh_threshold + 128 行 thresh | 控制"同时检验几千个钱包"的多重比较错误率 |
| 5 | 前半/后半样本外持续性(z_is/z_oos| 第 73-78 行 + 第 131-137 行 | z_oos > 0 才算 tier=validated(真正决定性的门) |
额外统计并写入:
- `avg_entry`(平均入场价,用于区分 favorite-rider vs value
- `band_0204`0.2-0.4 带 bets 占比,alpha 高地带)
**并行化**ThreadPoolExecutorWORKERS=10(第 42 行 + 114-123 行)
**输出**
- `watch_skilled.json`(当前 ~71 个钱包 —— 你 dashboard 看到的就是它)
- `watch_skilled_scored.json`(完整中间结果,含未通过门槛的钱包)
**关键细节**skilled set 的 tier 分级:
- tier=`validated`:同时有前后两半数据 AND z_oos > 0(真正可信)
- tier=`candidate`:只有半边数据或 z_oos ≤ 0(数据不够,暂存观察)
**为什么不是胜率**:胜率会被"只买 0.90 热门"的钱包欺骗(看起来胜率高,但是赔率薄且无复制价值)。z-score 对标入场价本身的信息含量,稳健得多。
---
### 第 5 层:信念画像扫描(conviction_scan.py → conviction_wallets.json
**问题**z-score 统计的是钱包**所有** bets 的表现,但复制 bot 只能复制"他认真下注"的那些 bet。他用 $5 小钱玩的 bet 你去 $50 抄,盈亏没意义。
**定义**"信念 bet" = 该钱包下注金额 Top 20%p80)的那些 bets。
- `CONV_PCTILE = 0.80`conviction_scan.py 第 41 行)
- p80 阈值仅在 TRAIN 窗口(res_t < 2026-06-01)上计算,**避免测试数据泄漏**(第 8-9 行注释 + 第 71 行 SQL
**扫描逻辑**conviction_scan.py 第 62-144 行 main):
1. 从 cache.duckdb 读所有钱包的 TRUSTED rows(经过 trust.py 过滤)
2. 对每个钱包,计算 TRAIN 窗口的信念门槛 p80
3. 只保留 TRAIN 窗口信念 bets ≥ 12 条的钱包
4. 进一步过滤:
- 胜率 ≥ 65%`WIN_MIN = 0.65`,第 43 行)
- 平均入场价在 [0.30, 0.75](排除 0.90+ favorite-rider,第 44 行)
- 信念 copy-ROI > 0(第 96 行)
- **whole-book z_all > 2.0**(第 47 行 Z_ALL_MIN)—— 这是 2026-07-03 加入的最强门:该钱包整个交易簿都有正 z,不只是信念 bet
- 中位信念 stake ≥ $50(第 48 行 `MIN_MED_STAKE = 50`)—— 排除只玩 $2-$6 clips 的 dust 钱包
5. BH-FDR 控制(第 103-110 行)
6. 计算 6 月 1 日后的前瞻表现(forward validation
**输出**conviction_wallets.json(约 20-40 个,每个带 train/forward 两套统计)
**重要注释**conviction_scan.py 第 26-32 行):50/50 REFUNDS 市场把两方都标记为 won,这会让统计数字虚高。本层有意"过度生成候选",真正去毒在 validate_timing.py 里通过链上 payout 核对。
---
### 第 6 层:fee-aware 复制复现(validate_timing.py → watch_sharps.json
**这是从 71 个到 10 个可复制钱包的最关键门**
**问题**conviction_wallets.json 里有些钱包其实是"卖得好(scalper)" — 他们高入场后低出赚利差,但他们 buy 的时候价格还没动。你(copier)跟在后面买,滑点吃掉全部利润。
**做法**validate_timing.py):
1. 对 conviction_wallets.json 里的每个候选钱包
2. 用与真实 copybot 完全一致的逻辑做"模拟复制":
- `STAKE = 50.0`(每笔 $50 固定)
- `FEE_RATE = 0.03`sports 类费率,`fee = shares · rate · price · (1-price)`,双边收)
- 持有到结算(`_clob_winner` 通过 CLOB `winner` 字段判断胜负)
3. 得到:
- `copy_pnl`:复现的净收益(扣除两边 fee 后的实际收入)
- `held_pnl`:持有到结算的收益(延迟鲁棒的那条腿)
- 中位 lead timeentry → resolution 的小时数)
4. 通过门:
- `copy_pnl > 0`(复制它**真实赚钱**
- `held_pnl > 0`(hold 到结算的收益也是正的 —— 说明不是只靠卖的时机精准)
- `median_lead >= COPYABLE_MED_LEAD = 24h`(第 45 行,排除 sub-hour snipers
- 最近 30 天有活动
**输出**watch_sharps.json~10-20 个真正可复制的 sharp 钱包)
这一步与 copybot 的资金管理、fee 模型完全一致,所以 `watch_sharps.json` 里钱包的数字就是"如果你真的在这段时间复制他们,你会赚多少钱"的最诚实估计。
---
### 第 7 层:同步信念门槛到 bot 配置(sync_floors.py → copybot.paper.json
**问题**copybot 的 follow 配置里每个钱包都有"信念门槛"(低于这个金额的 bet 不抄)。这个阈值从哪来?
**做法**sync_floors.py 从 cache 里每个钱包的 TRAIN 数据计算 p80 信念 stake,写回 `copybot.paper.json``floor` 字段。
同时 `backtest.json` 也用同样的阈值,确保回测和 live bot 判断同一笔 bet 时的结论一致(daily.sh 第 55-62 行还做了 class_pct 的一致性校验,如果两个文件这个参数不同会报错)。
**输出**:更新后的 `copybot.paper.json`(每个钱包加了 floor 数字,如 Kruto2027 的门槛约 $2000xbadaf319 约 $44
---
### 第 8 层:滚动回测(portfolio.py → portfolio.json
**最后一步**$1000 起步资金,把 watch_sharps.json(或 backtest.json 指定的钱包集合)过去 30 天的信念 bets 按时间轴复现一遍。
- 每笔 stake = 当时权益 × class_pct(通常 4%
- 上限 ≤ 信号钱包自己下注的金额
- 扣除 CLOB 双边 fee(费率按 sports 0.03 计算)
- 链上赎回免费
- 结算用 CLOB winner 字段 + 链上 payout 双保险
**输出**portfolio.json,含:
- `equity`:最终权益(核心指标,如果你问"回测赚多少"就是这个)
- `realized`:已结算盈亏
- `deployed`:当前持仓成本
- `cash`:剩余现金
---
### 第 9 层:daily.sh 一键跑
daily.sh 第 37-100 行按顺序执行:
```
python3 enumerate.py 14 # 发现最近 14 天的新候选
cache.invalidate(watch_skilled + watch_sharps) # 强制刷新 watchlist 的数据
python3 collect.py # 从 Polymarket 拉新钱包 + 刷新过期钱包
python3 skill.py # 重新 5 门评分 → watch_skilled.json
python3 conviction_scan.py # 信念画像 → conviction_wallets.json
python3 validate_timing.py # fee-aware 复现 → watch_sharps.json
python3 sync_floors.py # 写 p80 门槛 → copybot.paper.json
python3 portfolio.py # $1000 回测 → portfolio.json
python3 dashboard.py # → dashboard.html
cp watch_skilled.json history/watch_YYYYMMDD.json # 可审计快照
git add + commit + push # bot 下次重启会拉最新配置
```
**注意 publish 步骤**daily.sh 第 102-115 行):bot(不论是 paper 还是 live)在每次重启时 git pull 最新的 copybot.paper.json。所以 daily.sh 的输出实际上是"更新 bot 配置"的唯一入口。
---
## 三、你现在看到的 dashboard 71 个 —— 怎么更新?
### 3.1 如果你想让 dashboard 数字是最新的
只需要单独跑:
```powershell
cd live
python3 dashboard.py
```
它只读 `watch_skilled.json`,生成 `dashboard.html`。**不会碰网络**dashboard.py 本身不碰 Polymarket API)。
### 3.2 如果你想让 watch_skilled.json 的 z-score 用最新 bets 重算
```powershell
cd live
python3 collect.py # 拉新钱包 + 刷新过期钱包的 bets 到 cache.duckdb
python3 skill.py # 从 cache 重新 5 门评分 → 覆盖 watch_skilled.json
python3 dashboard.py # 刷新仪表盘
```
`collect.py` 每轮只拉新钱包 + 最近 14 天未拉过的钱包中的 `STALE_CAP=2500` 个,不会全量重拉(全量可能几小时到十几小时)。
如果你只想刷新"当前 watchlist 里的那几个钱包",可以先手动 invalidate
```powershell
# 先强制 watchlist 的钱包过期
python3 -c "import cache; cache.invalidate(['0x...','0x...','0x...'])"
# 然后收集
python3 collect.py
# 重算
python3 skill.py
python3 dashboard.py
```
### 3.3 全流程跑一次(等同于 daily.sh 的简化版)
```powershell
cd live
python3 enumerate.py 14
python3 collect.py
python3 skill.py
python3 conviction_scan.py
python3 validate_timing.py
python3 portfolio.py
python3 dashboard.py
```
首次跑会比较慢(主要是 collect.py 拉新钱包),之后每次很快。
---
## 四、copybot.py 与筛选 pipeline 的关系
### 4.1 copybot 的 follow 列表从哪来?
copybot 启动时读取 `live/copybot.paper.json`paper 模式)或 `live/copybot.paper.json`live 模式)。这个文件里的 `follow` 数组,正是 sync_floors.py 从 watch_sharps.json 里挑出的最终子集,加上每个钱包的 `floor`p80 信念金额)。
### 4.2 bot 实时触发的过滤规则
对每个 RTDS 或 5 分钟轮询捕获到的新 bet,copybot 检查:
1. **钱包在 follow 集合里**?(只跟踪 watch_sharps.json 最后挑出的那几个)
2. **方向是 BUY**?(只抄入场;SELL 作为 exit 触发器,但"跟卖"不支持)
3. **下注金额 ≥ 该钱包的 floor**(信念门槛,由 sync_floors.py 写入)
4. **入场价在 [0.00, 0.95] 区间**(排除 near-certain 热门)
5. **价格漂移门**:如果 bot 下单价比信号钱包成交价高 > 0.05 绝对点位,跳过(滑点保护)
6. **深度门**:价差 > 0.08 或 5c ask 深度 < $50 的市场跳过(薄盘不可靠)
7. **资金管理**:stake = 当前权益 × 4%,上限 ≤ 信号钱包自己下注的金额,下限 $1
### 4.3 paper bot 的账本状态保存在哪?
paper 模式下,bot 的 `state`(持有的 positions、现金、已结算盈亏)保存在命令行 `--state` 指定的 JSON 文件里(你用的 `wwf_state.json`)。
live 模式类似,文件是 `copybot_live.json`,由 bot 自己 commit/push 回 GitHub 以便每日校准(daily.sh 第 71-96 行做 `live_equity vs model_equity` 比较,结果写入 `history/calibration.csv`)。
---
## 五、关键数字速查(代码里的硬常量)
| 常量 | 值 | 位置 | 含义 |
|-----|-----|------|------|
| WINDOW_DAYS | 180 | enumerate.py:31 / cache.py:43 | 拉取 bet 的时间窗口(最近 180 天) |
| MIN_VOLUME | 20000 | enumerate.py:35 | 只看 $20k+ 流动性的市场 |
| TOP_TRADERS | 30 | enumerate.py:34 | 每个市场取 Top 30 名义金额交易者 |
| MAX_MARKETS | 1200 | enumerate.py:32 | 最多枚举市场数 |
| MIN_N | 15 | skill.py:37 | z-score 至少 15 条已结算 bet |
| MAX_N | 2500 | skill.py:39 | 超过这个数可能是做市机器人 |
| FDR_Q | 0.05 | skill.py:40 | BH 多重检验控制率 |
| CONV_PCTILE | 0.80 | conviction_scan.py:41 / cache.py:51 | 信念 bet 的 p80 阈值 |
| JUN1 | 2026-06-01 | conviction_scan.py:40 | TRAIN/TEST 切分点 |
| WIN_MIN | 0.65 | conviction_scan.py:43 | 信念 bet 胜率门槛 |
| ENTRY_LO/HI | 0.30/0.75 | conviction_scan.py:44 | 信念 bet 的入场价区间 |
| Z_ALL_MIN | 2.0 | conviction_scan.py:47 | whole-book z-score 门 |
| MIN_MED_STAKE | $50 | conviction_scan.py:48 | 信念 bet 中位金额门(dust 过滤) |
| FEE_RATE | 0.03 | validate_timing.py:53 | CLOB sports 类 taker 费率(fee = shares·rate·price·(1-price) |
| COPYABLE_MED_LEAD | 24h | validate_timing.py:45 | 可复制判定的中位 lead time |
| STAKE | $50 | validate_timing.py:47 | 复现时每笔固定模拟金额 |
| MAX_AGE_DAYS | 14 | cache.py:44-45 | 钱包缓存新鲜度阈值(超过则下次重新拉取) |
| STALE_CAP | 2500 | collect.py:28 | 单轮 collect.py 强制刷新的钱包数上限(避免一次跑几小时) |
---
## 六、流程总结图
```
Polymarket API (gamma / clob / data-api)
enumerate.py ──→ candidates.json ( ~3 万钱包, 按 markets_seen 排序 )
collect.py / cache.py ──→ cache.duckdb ( 每钱包的 resolved bets, 14 天自动刷新 + 累计归档,
upsert 而非 overwrite, 含 asset/token_id 去重避免 double-count,
trust.cte() 过滤做市商 poison 数据)
┌──────────── skill.py ────────────┐
│ 1. MIN_N=15 已结算 bets │
│ 2. z = (wins - Σp)/√Σp(1-p) │
│ 3. BH-FDR @ 5% │
│ 4. split-half: z_is/z_oos 前后半 │
│ 5. MAX_N=2500 排除做市 │
│ 6. avg_entry 分 value/favorite │
└───────────────────────────────────┘
watch_skilled.json ( ~71 个, 你 dashboard 看到的 )
┌──────── conviction_scan.py ───────┐
│ 只取每个钱包 Top 20% stake (p80) │
│ TRAIN: res_t < 2026-06-01 │
│ TEST: res_t >= 2026-06-01 │
│ 门槛: ≥12 信念 bet + win% ≥65% │
│ + entry 0.30-0.75 + ROI>0 │
│ + z_all>2 + med stake≥$50 │
└───────────────────────────────────┘
conviction_wallets.json ( ~20-40 个 )
┌────── validate_timing.py ─────────┐
│ flat-$50/stake fee-aware 复现 │
│ 真实 CLOB winner 判定胜负 │
│ 门: copy_pnl>0 AND held_pnl>0 │
│ AND med_lead ≥ 24h │
│ AND 30 天内有活动 │
└───────────────────────────────────┘
watch_sharps.json ( ~10-20 个可复制 )
┌────── sync_floors.py ─────────────┐
│ 每个钱包计算 p80 信念金额 │
│ → copybot.paper.json 的 floor │
│ → backtest.json 的 floor (一致) │
└───────────────────────────────────┘
copybot.paper.json / backtest.json ( copybot 实际跟踪的 5-10 个钱包 + 各自 floor )
┌────── copybot.py ─────────────────┐
│ RTDS WebSocket + 5 分钟轮询兜底 │
│ 过滤: 钱包 in follow set + BUY │
│ + stake ≥ floor │
│ + entry 价 [0, 0.95] │
│ + 价格漂移保护 (+0.05 abs) │
│ + 深度门 (spread + 5c ask) │
│ + 4% 权益 stake, 上限信号 │
└───────────────────────────────────┘
wwf_state.json ( paper 账本 ) / copybot_live.json ( 实盘账本 )
portfolio.json ( 回测结果, $1000 起步的权益曲线 )
dashboard.html ( 当前 71 个钱包的 z / z_oos / win% / entry 概览 )
history/calibration.csv ( daily.sh: live_equity vs model_equity 的每日校准行 )
history/watch_YYYYMMDD.json ( watch_skilled.json 每日快照 —— 可审计 )
```
---
## 七、你需要多久重新拉一次?
**如果你只是跑 paper bot 玩**
- **不用刻意重新拉**。bot 启动时会 baseline 校准(copybot.py baseline 逻辑),RTDS 实时推送新 bet。
- 每 2-4 周跑一次 `python3 collect.py && python3 skill.py && python3 dashboard.py` 刷新 z-score 即可。
**如果你认真做回测**
- 每周跑一次 daily.sh 全流程(enumerate → collect → skill → conviction_scan → validate_timing → sync_floors → portfolio → dashboard
- 确保 `watch_sharps.json` 始终用最新 bets 复现
**如果你跑 live 实盘**(你当前没这么做,但记录一下):
- 每次 `python3 sync_floors.py` 更新后 `git commit && fly apps restart copybot-live`
- bot 启动时的 geo-gategeocheck.py)验证必须通过(斯德哥尔摩区域可交易)
---
## 八、已知的方法论限制(来自代码注释)
1. **回测上偏**:回测是"事后挑出表现好的钱包然后说'如果我过去跟着他们会怎样'",真实复制需要预先选出那些钱包。这是所有筛选型回测的固有问题。
2. **复制滞后**:RTDS 已经比信号钱包晚几百毫秒到几秒,加上 bot 处理、下单、CLOB 排队,实际入场价总会比信号钱包差。滑点吃掉部分 alpha 是不可避免的。
3. **深度限制**:市场上某 outcome 只有 $X 的 5c ask 深度,你不能复制一个下注 $10X 的钱包——他能拿到的价你拿不到。
4. **ban 风险**:被识别为"总是跟着别人下大单"可能被 Polymarket 限制。
5. **refund 机器**:有些钱包 z-score 高其实是因为他们高频参与 50/50 refund 市场(两边都算赢)。conviction_scan.py 第 26-32 行专门注释说本层会过度生成,由 validate_timing.py 的链上 payout 去毒。
6. **CLOB winner=False 语义陷阱**:未结算市场的每个 token winner 都为 False。把 False 当"输"会把所有未结算仓位记为亏损。代码里 `_clob_winner()` 显式处理:只有 `winner is True` 才算赢;其他都是 unresolved。(validate_timing.py 第 58-78 行注释)
以上所有步骤每一行代码都可以在 `live/` 目录下的同名 .py 文件里找到对应的实现,欢迎直接看源码验证。
+108
View File
@@ -0,0 +1,108 @@
## 邮箱登录钱包的实盘配置
对照官方 `PolymarketDocumentation-main/docs/api-reference/authentication.md` 第 349-361 行,签名类型是固定的:
| Sig Type | 值 | 适用 |
| -------------- | ----- | ----------------------------------------- |
| EOA | 0 | MetaMask 等浏览器钱包(你自己持有私钥) |
| **POLY_PROXY** | **1** | **Magic Link 邮箱/Google 登录** ← 你这种 |
| GNOSIS_SAFE | 2 | 老 Safe 用户 |
| POLY_1271 | 3 | 新 API 用户推荐(存款钱包,私钥完全自管) |
> **官方 Tip**`New API users should use deposit wallets with POLY_1271.` 新用户建议用 deposit wallet (`signature_type=3`)。如果你已经有邮箱钱包、不想动现有余额,走路径 A;想干净开始或风险更可控,走路径 B。
---
### 路径 A:保留现有邮箱钱包(signature_type=1
**1) 拿到私钥**——官方 `docs/polymarket-learn/FAQ/how-to-export-private-key.md` 写得很清楚:
> Exporting your private key on Magic.Link. **This process is applicable if you've signed up via email.**
> Access the Export Link while signed into Polymarket: **https://reveal.magic.link/polymarket**
> Sign-in on Magic.Link → Export Private Key. Once revealed, you should store it somewhere safe.
操作步骤:
1. 浏览器登录 polymarket.com(已登录状态)
2. 访问 **https://reveal.magic.link/polymarket**
3. 用你的邮箱走 Magic.link 登录
4. 点 "Export Private Key"
5. 看到一个 `0x...` 的 64 位十六进制私钥,**离线保存**(密码管理器 / 加密文件,**不要 commit**
**2) 拿到 funder address**——访问 https://polymarket.com/settings 看你的 Polygon profile address。这个就是你 `LIVE_FUNDER_ADDRESS`(USDC 实际存放的代理合约地址)。
**3) 填环境变量****不要**写进 JSON):
```bash
export LIVE_PRIVATE_KEY=0x... # 上一步导出的私钥
export LIVE_FUNDER_ADDRESS=0x... # polymarket.com/settings 显示的地址
export LIVE_SIGNATURE_TYPE=1 # POLY_PROXY,邮箱登录固定值
export LIVE_CONFIRM="I understand real money is at risk"
```
**4) `config.live.json` 改两处**
```diff
"mode": "live",
"bankroll_usd": 22.28, ← 改成你邮箱钱包里实际 USDC.e 余额(启动时 copybot 会和链上比对)
"live": {
- "private_key": "",
- "funder_address": "",
- "signature_type": 1,
+ "private_key": "", ← 留空,靠 env 注入
+ "funder_address": "", ← 留空,靠 env 注入
+ "signature_type": 1, ← 邮箱登录 = 1
"order_type": "FAK",
...
},
```
### 路径 B:新建 deposit walletsignature_type=3,更推荐)
`PolymarketDocumentation-main/docs/api-reference/authentication.md` 第 354 行 + Tip
> `POLY_1271` | `3` | Deposit wallet flow for new API users. **The funder is the deposit wallet address and orders are validated through ERC-1271.**
意思是:你创建一个 Polygon 钱包(MetaMask 私钥),用 deposit flow 把 USDC 存进去,下单时用 ERC-1271 签名校验。具体步骤官方文档的 [Deposit Wallet Guide](/trading/deposit-wallets) 有,HANDOFF.md 第 239 行 `SecureClient.create(private_key) — deposit wallet auto-resolves` 也是同一个意思。
这种路径私钥你自己完全控制(不像邮箱路径依赖 Magic.link),跟 MetaMask 的 EOA 体验一样,**风险更低**。
---
### 区分两种类型的关键(先确认你是哪一种)
打开 https://polymarket.com/settings 看你自己的 profile address
- `0x` 开头 + 一串 hex
- 在 Polymarket 里**是真实的 EOA 钱包**(不是合约)→ signature_type = 1POLY_PROXY,邮箱登录)
- 或者是 Gnosis Safe 地址 → signature_type = 2(如果你曾用过 Safe 登录)
**最简单的判定方式**:如果你的钱包余额页面显示 "Email" 登录、是普通地址(不是 Safe),就是 `signature_type=1`
---
### 启动
```bash
python copybot.py --config config.live.json --live --poll 60
```
> `copytrade.py:92` 注释里写的是 `0 EOA · 1 email/magic proxy · 2 browser proxy` —— 这个注释**漏了 type 3**POLY_1271,新 API 用户的 deposit wallet 模式)。但 `signature_type` 字段本身是直接透传给 `py_clob_client.ClobClient(signature_type=...)` 的(`copytrade.py:256`),传 1 或 3 都支持,无需改代码。
建议先**只读模拟跑一周**`python copybot.py --config config.live.json --poll 60`(不加 `--live`,不设 `LIVE_CONFIRM`,看实盘钱包的对账逻辑和链上 cash-anchor 是否对得上)。对得上再开 `--live`
确认了:**项目里没有 `.env` 文件,也没有 `.env.example` 模板**。我上一条消息里把 `.env` 当作 "方式 2" 提了一句,那是泛指行业惯例,不是说项目里有。抱歉让你去找。
项目实际只有两个相关的模板/示例:
| 文件 | 用途 |
| -------------------------- | ------------------------------------------------------------ |
| `config.example.json` | 通用配置示例(应该是 paper 模式或空模板) |
| `config.live.example.json` | **实盘模板**,私钥字段已留空——按上一条说的复制成 `config.live.json` 即可 |
```bash
cp config.live.example.json config.live.json
```
然后私钥/funder/signature_type 通过环境变量(`LIVE_PRIVATE_KEY` 等)注入,`copybot.py:2303-2308` 会自动从 env 读,不用碰 JSON。