- 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
24 KiB
钱包筛选 / 回测 / 实时跟踪 — 完整流程说明
本文档基于 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 行:
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 跟踪的钱包
copybot(paper 模式)读的是 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.py(fee-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:
# 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 函数):
-
找到最近 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 个
-
对每个市场取 Top 30 名义金额交易者
- 调用
insider.market_traders(conditionId, top=30)(enumerate.py 第 123 行) - 去重,统计每个钱包"出现在多少个不同市场"(
markets_seen计数)
- 调用
-
累积到 candidates.json
- 每次运行不是覆盖,而是与旧的
candidates.json合并(第 138-151 行) - 新钱包追加;已存在的钱包取更大的 markets_seen
- 最终按 markets_seen 降序写入 candidates.json
- 每次运行不是覆盖,而是与旧的
输出:candidates.json(当前约 1-3 万个钱包,取决于累积运行次数)
为什么按 markets_seen 排序:在 many 个不同市场都下注的钱包更可能是认真研究的,不是只碰了一个市场就走运的。
第 2 层:收集钱包的历史 bets(collect.py → cache.duckdb)
问题:enumerate 只给出了候选钱包名单,没有他们的下注历史。每算一次 z-score 都要从 Polymarket 拉一次 API,非常慢(单钱包 5-30 秒)。
做法(collect.py 第 31-55 行 + cache.py 全文件):
- 读
candidates.json,按 markets_seen 倒序 - 区分 "全新钱包"(从未拉过)和 "过期钱包"(最后一次拉取已经超过 14 天)
MAX_AGE_DAYS = 14在 cache.py 第 44 行
- 每轮只拉新的 + 最近过期的
STALE_CAP = 2500个(第 28 行)- 这个 cap 很重要:没有它,当整个候选池第一次同时跨过 14 天阈值时,一次 daily 会变成 40 小时的重拉
- 用
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.duckdb(SQLite-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 Falsepulled_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 高地带)
并行化:ThreadPoolExecutor,WORKERS=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):
- 从 cache.duckdb 读所有钱包的 TRUSTED rows(经过 trust.py 过滤)
- 对每个钱包,计算 TRAIN 窗口的信念门槛 p80
- 只保留 TRAIN 窗口信念 bets ≥ 12 条的钱包
- 进一步过滤:
- 胜率 ≥ 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 钱包
- 胜率 ≥ 65%(
- BH-FDR 控制(第 103-110 行)
- 计算 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):
- 对 conviction_wallets.json 里的每个候选钱包
- 用与真实 copybot 完全一致的逻辑做"模拟复制":
STAKE = 50.0(每笔 $50 固定)FEE_RATE = 0.03(sports 类费率,fee = shares · rate · price · (1-price),双边收)- 持有到结算(
_clob_winner通过 CLOBwinner字段判断胜负)
- 得到:
copy_pnl:复现的净收益(扣除两边 fee 后的实际收入)held_pnl:持有到结算的收益(延迟鲁棒的那条腿)- 中位 lead time(entry → resolution 的小时数)
- 通过门:
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 的门槛约 $200,0xbadaf319 约 $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 数字是最新的
只需要单独跑:
cd live
python3 dashboard.py
它只读 watch_skilled.json,生成 dashboard.html。不会碰网络(dashboard.py 本身不碰 Polymarket API)。
3.2 如果你想让 watch_skilled.json 的 z-score 用最新 bets 重算
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:
# 先强制 watchlist 的钱包过期
python3 -c "import cache; cache.invalidate(['0x...','0x...','0x...'])"
# 然后收集
python3 collect.py
# 重算
python3 skill.py
python3 dashboard.py
3.3 全流程跑一次(等同于 daily.sh 的简化版)
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 检查:
- 钱包在 follow 集合里?(只跟踪 watch_sharps.json 最后挑出的那几个)
- 方向是 BUY?(只抄入场;SELL 作为 exit 触发器,但"跟卖"不支持)
- 下注金额 ≥ 该钱包的 floor(信念门槛,由 sync_floors.py 写入)
- 入场价在 [0.00, 0.95] 区间(排除 near-certain 热门)
- 价格漂移门:如果 bot 下单价比信号钱包成交价高 > 0.05 绝对点位,跳过(滑点保护)
- 深度门:价差 > 0.08 或 5c ask 深度 < $50 的市场跳过(薄盘不可靠)
- 资金管理: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-gate(geocheck.py)验证必须通过(斯德哥尔摩区域可交易)
八、已知的方法论限制(来自代码注释)
- 回测上偏:回测是"事后挑出表现好的钱包然后说'如果我过去跟着他们会怎样'",真实复制需要预先选出那些钱包。这是所有筛选型回测的固有问题。
- 复制滞后:RTDS 已经比信号钱包晚几百毫秒到几秒,加上 bot 处理、下单、CLOB 排队,实际入场价总会比信号钱包差。滑点吃掉部分 alpha 是不可避免的。
- 深度限制:市场上某 outcome 只有 $X 的 5c ask 深度,你不能复制一个下注 $10X 的钱包——他能拿到的价你拿不到。
- ban 风险:被识别为"总是跟着别人下大单"可能被 Polymarket 限制。
- refund 机器:有些钱包 z-score 高其实是因为他们高频参与 50/50 refund 市场(两边都算赢)。conviction_scan.py 第 26-32 行专门注释说本层会过度生成,由 validate_timing.py 的链上 payout 去毒。
- CLOB winner=False 语义陷阱:未结算市场的每个 token winner 都为 False。把 False 当"输"会把所有未结算仓位记为亏损。代码里
_clob_winner()显式处理:只有winner is True才算赢;其他都是 unresolved。(validate_timing.py 第 58-78 行注释)
以上所有步骤每一行代码都可以在 live/ 目录下的同名 .py 文件里找到对应的实现,欢迎直接看源码验证。