Files
winning-wallet-finder/问题排查与踩坑记录.md
gavindiaz 3141c392de docs: update 3 docs with critical findings from author's research
AGENT.md:
- Expanded dimension 4 (niche analysis): ITF tennis/tier-3 esports = real edge,
  5min BTC/scalping = never follow, long holds (>7d) kill compounding
- Added edge disappearance warning (ArbTraderRookie wiped mid-analysis)
- Added 5 new AI agent rules: don't use copy_pnl alone, niche awareness,
  edge fragility, win rate is survivorship-biased, small capital = concentrate

日常使用.md:
- Added emergency exit procedure (host/flatten_positions.py)
- Added edge disappearance detection and response procedure

问题排查与踩坑记录.md:
- Added gotcha 7.11: win rate is survivorship-biased (90.6% shown = 48.3% real)
- Added gotcha 7.12: edge can vanish (wallet wiped from API mid-analysis)
- Added gotcha 7.13: long holds kill compounding (leegunner + lifetime
  but - to copy due to 7.6-day holds)
- Added gotcha 7.14: emergency exit tool (flatten_positions.py)
2026-07-21 09:10:58 +08:00

526 lines
20 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.
# 问题排查与踩坑记录
> 本文档记录了从 paper 部署到 live 上线过程中遇到的所有问题和解决办法。任何第一次接触这个项目的 AI 代理或人类工程师都可以通过本文档快速了解已知坑和解决方案。
>
> 最后更新:2026-07-20
---
## 目录
1. [部署阶段](#1-部署阶段)
2. [代理阶段](#2-代理阶段)
3. [钱包与资金阶段](#3-钱包与资金阶段)
4. [Live Bot 运行阶段](#4-live-bot-运行阶段)
5. [Dashboard 阶段](#5-dashboard-阶段)
6. [代码修复记录](#6-代码修复记录)
7. [操作陷阱](#7-操作陷阱)
---
## 1. 部署阶段
### 1.1 portfolio.py OverflowError: mktime argument out of range
**症状**`python3 portfolio.py --days 7` 崩溃,报 `OverflowError: mktime argument out of range`
**原因**Polymarket 某些市场的 `endDate` 是 sentinel 值(如 0001-01-01),超出 Windows/Linux 的 `time.mktime` 范围。`_end_ts()` 只捕获了 `ValueError`,没捕获 `OverflowError`
**解决**`live/portfolio.py:336`,把 `except ValueError` 改成 `except (ValueError, OverflowError)`
### 1.2 ModuleNotFoundError: No module named 'duckdb'
**症状**`python3 portfolio.py` 报缺少 duckdb
**解决**`pip3 install duckdb --break-system-packages`Debian/Ubuntu 需要 `--break-system-packages`
### 1.3 wwf 用户 git commit 报 Author identity unknown
**症状**bot 以 wwf 用户运行时 git commit 失败:`Author identity unknown`
**原因**:建了 wwf 系统用户但没配 git 身份
**解决**
```bash
sudo -u wwf git config --global user.name "copybot[bot]"
sudo -u wwf git config --global user.email "copybot@wwf.local"
```
### 1.4 root 访问 wwf 的仓库报 dubious ownership
**症状**`git log``fatal: detected dubious ownership`
**原因**:仓库 chown 给了 wwf,root 访问被 git 安全机制拒绝
**解决**`git config --global --add safe.directory /opt/winning-wallet-finder`
### 1.5 config.live.json 权限错误
**症状**live bot 报 `PermissionError: [Errno 13] Permission denied: config.live.json`
**原因**config.live.json 是 root 创建的,wwf 用户读不了
**解决**`chown wwf:wwf config.live.json && chmod 640 config.live.json`
### 1.6 LIVE_CONFIRM 值不匹配
**症状**`Aborted - LIVE_CONFIRM is set but does not match the phrase`
**原因**:确认短语是 `TRADE LIVE``copytrade.py:48``CONFIRM_PHRASE`),不是 `I understand real money is at risk`
**解决**`/etc/copybot/live.env` 里设 `LIVE_CONFIRM=TRADE LIVE`(不加引号)
### 1.7 systemd EnvironmentFile 引号问题
**症状**LIVE_CONFIRM 的值带了引号导致不匹配
**原因**systemd EnvironmentFile 不需要引号,加了引号会变成值的一部分
**解决**`LIVE_CONFIRM=TRADE LIVE`(不要写 `LIVE_CONFIRM="TRADE LIVE"`
### 1.8 私钥含非 ASCII 字符
**症状**`UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5`
**原因**:重写 live.env 时占位符 `你的真实私钥` 没替换成真实私钥
**解决**:确保 live.env 里 `LIVE_PRIVATE_KEY=0x` + 64 位纯 hex
### 1.9 git push 被拒(remote 有 bot 自动 commit
**症状**`! [rejected] main -> main (fetch first)`
**原因**:服务器上的 bot 会自动 commit state/feed 文件到 Gitea,本地 push 时远程已有新 commit
**解决**`git pull --rebase --autostash origin main && git push origin main`
### 1.10 git reset --hard 恢复了腐败的 state
**症状**:清了 state 文件后 `git reset --hard`,bot 启动后又出现巨大的 LEDGER DRIFT
**原因**bot 已经把腐败的 state auto-commit 到 git`git reset --hard` 又把它拉回来了
**解决**`git reset --hard` 之后**再清一次 state**
```bash
git fetch origin && git reset --hard origin/main
rm -f copybot_state.live.json copybot_fills.live.jsonl live/copybot_live_real.json
```
---
## 2. 代理阶段
### 2.1 服务器在 Polymarket 禁单地区
**症状**`host/geocheck.py` 返回 `VERDICT: BLOCKED`
**原因**:服务器 IP 在美国/英国/法国等禁单地区
**解决**:部署 mihomo 代理,通过日本/香港节点绕开 geo-block。详见 `mihomo代理部署教程.md`
### 2.2 机场返回假节点
**症状**mihomo 配置里的节点名是"当前Clash客户端不支持本机场协议",连接地址是 127.0.0.1:65535
**原因**:机场检测 wget 的 User-Agent 不是 Clash 客户端
**解决**:加 UA 伪装 + flag 参数:
```bash
wget -O /etc/mihomo/config.yaml \
--user-agent="clash-verge/v1.7.7" \
"订阅链接&flag=meta"
```
### 2.3 选了英国节点导致 GEO-BLOCKED
**症状**live bot 启动报 `GEO-BLOCKED: this box cannot place Polymarket orders`
**原因**:健康检查脚本自动切换到了英国节点(英国也在禁单列表),但旧脚本只排除了美国
**解决**:见 2.4
### 2.4 健康检查脚本两个 bug
**bug 1**:只排除了美国,没排除英国/法国/德国等其它禁单地区
**bug 2**:测的是 `GET /markets`(不 geo-block),不是 `POST /order`(才 geo-block
**解决**:改用**白名单**(只选日本/香港)+ 测 `POST /order`401=通过,403=被 block)。脚本在 `host/mihomo-healthcheck.sh`
### 2.5 mihomo 重启后节点回到 DIRECT
**症状**:重启 mihomo 后代理不走节点了
**原因**:第一次启动后没手动选过节点,cache.db 里没记录
**解决**:启动后手动选一次节点,mihomo 会记住(cache.db 持久化)
### 2.6 代理影响其它服务
**担心**:配了代理会不会影响 paper bot / dashboard / Gitea
**结论**:不会。mihomo 不是透明代理(没开 TUN),只有设了 `HTTPS_PROXY` 的程序才走代理。`HTTPS_PROXY` 只在 `/etc/copybot/live.env` 里,只 live bot 读
### 2.7 Clash Verge 拦截 dashboard 访问
**症状**:本地浏览器打不开 `http://45.207.206.36:18080/`
**原因**:本地 Clash Verge 系统代理把直连 IP 也走了代理
**解决**:关掉 Clash 系统代理,或在 Clash 规则里加 `IP-CIDR,45.207.206.36/32,DIRECT`
---
## 3. 钱包与资金阶段
### 3.1 SecureClient.create 报 Builder API Key 错误
**症状**`Gasless transactions require a Builder API Key or Relayer API Key`
**原因**:邮箱登录(POLY_PROXY)的钱包没有存款钱包(Deposit Wallet),SecureClient 尝试自动创建但需要 Builder API key
**解决**:用 Builder API 凭证创建存款钱包(一次性):
```python
bk = BuilderApiKey(key=..., secret=..., passphrase=...)
client = SecureClient.create(private_key=pk, api_key=bk)
```
或改用 MetaMask 登录(自动创建存款钱包,不需要 Builder key
### 3.2 充错钱包
**症状**bot 余额 $0,但 Polymarket 网站显示有钱
**原因**:充到了 Polymarket 网站显示的充值地址(代理钱包),不是 bot 用的存款钱包
**解决**:必须充到 `SecureClient.create()` 返回的存款钱包地址
### 3.3 USDC vs pUSD
**问题**bot 需要的是 pUSDPolymarket 抵押代币),不是原始 USDC
**解决**:通过 Polymarket 网站充值会自动转换。或直接充 USDC 到存款钱包,Polymarket 自动转成 pUSD
### 3.4 wrap_via_bridge.py 硬编码了原作者的钱包地址
**症状**`host/wrap_via_bridge.py``DW = "0x455e..."` 是原作者的存款钱包,不是你的
**注意**:这个脚本的 `DW``EXPECTED_BRIDGE_EVM` 是硬编码的,直接跑会在第 145 行报错退出。如果需要用,改成你自己的存款钱包地址
### 3.5 bankroll_usd 必须精确匹配链上 pUSD 余额
**问题**bankroll 设成整数(如 $5),但链上实际是 $5.45,导致 CASH≠CHAIN 警告
**解决**:每次清 state 前先查链上实际余额,设精确值:
```bash
python3 -c "from polymarket import SecureClient; c=SecureClient.create(private_key='...'); b=c.get_balance_allowance(asset_type='COLLATERAL'); print('%.2f' % (b.balance/1e6)); c.close()"
```
---
## 4. Live Bot 运行阶段
### 4.1 CASH≠CHAIN / LEDGER DRIFT(核心问题)
**症状**bot 账本与链上实际余额不一致
**根因**:代理网络不稳 -> 订单验证调用(get_order / _shares_held)失败 -> 误判订单未成交 -> 账本漂移
**手动修复**(仍然有效):
```bash
systemctl stop copybot-live
# 查链上余额 -> 设 bankroll -> 清 state -> 重启
```
**自动修复**(已实现):`copybot.py``summary()` 心跳里,当 `chain_cash_gap > $1` 时自动把 book cash 设为链上 pUSD 余额
### 4.2 auto-absorb 反馈循环(已回滚)
**症状**LEDGER DRIFT 从 $1 指数增长到 $4,000,000+
**原因**auto-absorb 把 `-drift` 加到 adjustments,但 adjustments 是 ledger_drift 计算的一部分,导致每次心跳 drift 翻倍:$1 -> $2 -> $4 -> $8 -> ...
**解决**:删掉 auto-absorb 代码。LEDGER DRIFT 只警告不自动修(避免反馈循环)。CASH≠CHAIN 仍然自动修(cash anchor 不影响 ledger_drift 计算)
**教训**:不要用 adjustments 来"修复" ledger_drift -- adjustments 本身是 drift 计算的输入,改它只会让 drift 更大
### 4.3 in-play hold 导致订单"未成交"但实际成交
**症状**bot 日志显示 `pending expired unfilled`,但 Polymarket 网站上仓位实际存在
**原因**in-play 赛事(Dota 2 / LoL 等正在进行中的比赛)价格波动快,bot 保守挂单(in-play hold),超时后判定"未成交"并 cancel,但订单在 cancel 前刚好成交了
**修复**(已实现):`copybot.py:1512` 加了 late fill 检测 -- cancel 后再查一次 `get_order`,如果 `size_matched > 0` 就采纳为成交
### 4.4 RTDS / user-ws 频繁断线
**症状**:日志反复出现 `rtds: stream down - reconnect in 60s``user-ws: down - reconnect`
**原因**:代理网络不稳,WebSocket 连接频繁断开
**影响**:检测延迟从 ~3 秒变成 ~17-74 秒(fallback 到 60 秒轮询)
**解决**:重启 live bot 重连 WebSocket`systemctl restart copybot-live`
### 4.5 book unreliable spread too wide
**症状**`skip (book unreliable (spread 0.12 > 0.08))`
**原因**live bot 有 paper bot 没有的保护机制 -- 价差 > 8 分就跳过(避免在流动性差的市场吃大滑点)
**结论**:正常行为,保护真钱。不建议改阈值
### 4.6 paper 有持仓但 live 没有
**原因**
1. paper 有 $1,000(能持很多仓),live 只有 $5(最多 5 笔)
2. paper 直接记录交易(100% 成交),live 真实下单可能被 in-play hold / book unreliable 跳过
3. live 跟了卖单(平仓了),所以 open 0
4. 清过 state 丢了持仓追踪
**结论**:正常,两个 bot 执行环境不同
### 4.7 on-chain settle fallback: OFF
**症状**:日志显示 `on-chain settle fallback: OFF - set ALCHEMY_RPC_URL`
**原因**live.env 里没配 Alchemy RPC
**解决**`/etc/copybot/paper.env``ALCHEMY_RPC_URL=https://polygon-mainnet.g.alchemy.com/v2/你的KEY`paper bot 用的,live bot 也可以加到 live.env
### 4.8 feed 文件不更新(心跳显示几百分钟前)
**症状**dashboard 显示心跳 400+ 分钟前,但 bot 在跑
**原因**bot 用 commit-on-change 节流(`copybot.py:1328-1330`),feed 内容没变(没新交易/结算)就不重写文件
**结论**:正常。bot 在跑(`Live Bot: active`),只是没新事件。超过 1 小时可以重启确认
---
## 5. Dashboard 阶段
### 5.1 dashboard 打不开
**症状**:浏览器访问 `http://45.207.206.36:18080/` 超时
**排查**
1. 服务挂了?-> `systemctl restart copybot-dashboard`
2. 端口没监听?-> `ss -tlnp | grep 18080`
3. 本地 Clash 拦截?-> 关掉 Clash 系统代理
### 5.2 LIVE 模式显示 HTTP 404
**症状**:切到 LIVE 模式显示 `断开:HTTP 404`
**原因**`copybot_live_real.json` 文件不存在
**可能原因**
1. feed_path 路径错误(`self.here` 是 copybot.py 所在目录 = 项目根,不是 live/)
2. bot 刚启动还没写 feed(等 60 秒)
3. 清 state 时把 feed 文件也删了
**解决**feed_path 必须是 `live/copybot_live_real.json`(带 `live/` 前缀,因为 bot 的 `self.here` 是项目根目录)
### 5.3 dashboard 健康面板显示 CASH≠CHAIN 一百万美元
**症状**`CASH≠CHAIN: $1069795.15`
**原因**auto-absorb 反馈循环导致 adjustments 累积到 $4M(见 4.2
**解决**:停 bot -> 清 state -> 重启(auto-absorb 代码已删除)
### 5.4 dashboard 数据不更新
**症状**:dashboard 显示的数据是几小时前的
**原因**bot 用 commit-on-change 节流,没新交易就不重写 feed 文件。dashboard 读的是磁盘上的 feed 文件
**解决**:正常行为。或手动触发 bot 写 feed:`sudo -u wwf git -C /opt/winning-wallet-finder add -A && sudo -u wwf git commit -m "manual sync" && sudo -u wwf git push`
### 5.5 开放订单数字与表格不符
**症状**:卡片显示"开放订单 15"但表格只有 11 条
**原因**bot 的 `open_count``bets` 数组是两个独立的计数器,`open_count` 包含不在 `bets` 数组里的早期仓位
**解决**dashboard 已统一用 `bets.filter(!settled).length`(表格的数字),不用 `d.open_count`
---
## 6. 代码修复记录
| 日期 | 文件 | 修复内容 | commit |
|------|------|---------|--------|
| 07-17 | `live/portfolio.py:336` | `except (ValueError, OverflowError)` 捕获 mktime 越界 | b916633 |
| 07-17 | `live/dashboard.py` | `encoding="utf-8"` 修复 Windows 中文渲染 | b916633 |
| 07-18 | `copybot.py:1512` | late fill 检测:cancel 后再查一次 get_order | ea0faf6 |
| 07-20 | `copybot.py:1714` | cash anchorCASH≠CHAIN > $1 时自动对齐链上余额 | b689acb |
| 07-20 | `copybot.py:1714` | 删除 auto-absorb(反馈循环 bug | f4d1c2a |
| 07-20 | `host/mihomo-healthcheck.sh` | 白名单(JP/HK+ 测 POST /order | 83a385b |
| 07-20 | `live/serve_dashboard.py` | 新增 /api/health /api/daily /api/wallets | 03748c1 |
| 07-20 | `live/bot_dashboard.html` | 新增系统健康/每日流水线/钱包建议面板 | 03748c1 |
---
## 7. 操作陷阱
### 7.1 git reset --hard 顺序
**错误**:先清 state -> git reset --hard -> state 被恢复(腐败的 state 已被 bot commit 到 git
**正确**git reset --hard -> 再清 state
```bash
git fetch origin && git reset --hard origin/main
# 重要:reset 之后再清 statebot 可能已把腐败 state commit 到 git
rm -f copybot_state.live.json copybot_state.live.json copybot_fills.live.jsonl live/copybot_live_real.json
```
### 7.2 不要全局设 HTTPS_PROXY
**错误**`echo "HTTPS_PROXY=..." >> /etc/environment`(所有程序都走代理)
**正确**:只在 `/etc/copybot/live.env` 里设(只 live bot 走代理)
### 7.3 不要用 adjustments 修 ledger_drift
**错误**:把 `-drift` 加到 adjustments 试图清零 drift
**原因**adjustments 是 ledger_drift 计算的输入,改它会导致 drift 翻倍(反馈循环)
**正确**ledger_drift 只警告不自动修。如果影响使用,手动清 state
### 7.4 改完配置必须重启
**忘记**:改了 `config.live.json` / `copybot.paper.json` / `live.env` 但没重启 bot
**解决**:改完任何配置都要 `systemctl restart copybot-live`(或对应服务)
### 7.5 新钱包 floor 先设 $25
**注意**:换跟单钱包时,新钱包的 floor 先设 $25(最低门槛),`daily.sh``sync_floors.py` 第二天自动算真实 p80 门槛覆盖掉
### 7.6 两个配置文件必须一致
**注意**`config.live.json``copybot.paper.json``wallets` 数组必须一致(paper 和 live 跟同一批钱包,结果才可比)
**验证**
```bash
python3 -c "import json; [print(w['name']) for w in json.load(open('config.live.json'))['wallets']]"
python3 -c "import json; [print(w['name']) for w in json.load(open('live/copybot.paper.json'))['wallets']]"
```
### 7.7 paper 和 live 的 feed_path 不同
**注意**bot 的 `self.here` = `os.path.dirname(os.path.abspath(__file__))` = copybot.py 所在目录(项目根 `/opt/winning-wallet-finder/`),不是 systemd 的 WorkingDirectory
- paper feed_path`live/copybot_live.json` -> 写到 `/opt/winning-wallet-finder/live/copybot_live.json`
- live feed_path`live/copybot_live_real.json` -> 写到 `/opt/winning-wallet-finder/live/copybot_live_real.json`
如果 feed_path 不带 `live/` 前缀,文件会写到项目根目录,dashboard 读不到
### 7.8 bot 会自动 commit + push 到 Gitea
**注意**bot 每次有状态变化会自动 `git add + commit + push` state/feed/fills 文件到 Gitea
**影响**
- 本地 `git push` 经常被拒(remote 有 bot 的 auto-commit-> 用 `git pull --rebase`
- `git reset --hard` 会恢复 bot auto-commit 的 state(可能腐败)-> reset 后再清 state
### 7.9 健康检查脚本不在 git 仓库里
**历史**:健康检查脚本最初是用 heredoc 直接在服务器上创建的,不在 git 仓库
**已修复**:脚本已放入 `host/mihomo-healthcheck.sh`git pull 后 `cp host/mihomo-healthcheck.sh /opt/mihomo-healthcheck.sh`
### 7.10 Polymarket 禁单地区完整清单
以下地区的 IP 不能下单(CLOB /order 返回 403):
```
美国 (US) · 英国 (GB) · 法国 (FR) · 德国 (DE) · 意大利 (IT)
荷兰 (NL) · 波兰 (PL) · 新加坡 (SG) · 澳大利亚 (AU) · 加拿大安大略 · 巴西 (BR)
```
**允许的常见地区**:日本、香港、韩国、台湾、瑞典、南非、印度
> ⚠️ 新加坡很多人以为是允许的,实际在禁单列表里!
### 7.11 胜率是骗人的(作者核心研究发现)
**Polymarket 的胜率有幸存者偏差**:平台只兑现赢的份额,输的份额永远躺在 `/positions` 里不进 `/closed-positions`。一个钱包显示 90.6% 胜率,实际只有 48.3%。
**只有 Copy P&L(费后复制盈亏)才是真实指标**
- ArbTraderRookie 显示 99.5% 胜率,但实际复制后亏 $790
- 全市场第一的钱包(43% 胜率)90 天亏了 $3.8M
**教训**:永远不要按胜率选钱包。dashboard 的"信念胜率"只是辅助参考,决策看前瞻 realized。
### 7.12 edge 可能随时消失
作者的一个顶级钱包(ArbTraderRookie)在分析过程中突然从 Polymarket API 消失了(2026-07-03)。
**edge 的来源**可能是比赛操纵相关的内幕信息,这些信息源随时可能被封禁或改变行为。
**防范**
- 每周重新评估(daily.sh 自动重算,但人工 review 也很重要)
- 赚钱了定期取出来(不要假设 edge 永久存在)
- 如果某个钱包连续 1 周没有信号,考虑踢掉
### 7.13 长持仓杀死复利
leegunner 终身 P&L +$274k(看起来很厉害),但**复制后亏 $180**。原因是持仓 7.6 天才结算,资金被占用,复利效率极低。
**教训**:选钱包不只看盈亏,还要看**持仓时间**。持仓 > 7 天的钱包即使赚钱,复制后可能因为资金占用而亏。
### 7.14 紧急退出工具
如果需要一键清仓所有 live 仓位(bot 失控 / 紧急撤资):
```bash
systemctl stop copybot-live
cd /opt/winning-wallet-finder
export LIVE_PRIVATE_KEY=$(grep LIVE_PRIVATE_KEY /etc/copybot/live.env | cut -d= -f2-)
export HTTPS_PROXY=http://127.0.0.1:7890
python3 host/flatten_positions.py
```
> 这是 `host/flatten_positions.py`,市价卖出所有仓位。真钱操作,只在紧急情况用。
---
## 快速排查流程
遇到问题时的排查顺序:
```
1. dashboard 打不开?
-> systemctl restart copybot-dashboard
-> 关掉本地 Clash
2. live bot 报错?
-> journalctl -u copybot-live --since "30 minutes ago" | tail -30
-> 看是 GEO-BLOCKED / CASH≠CHAIN / RTDS down / 还是其它
3. GEO-BLOCKED
-> 代理节点切到禁单地区了
-> curl -s http://127.0.0.1:9090/proxies/GLOBAL | python3 -c "import sys,json; print(json.load(sys.stdin).get('now'))"
-> 手动切回日本节点
-> /opt/mihomo-healthcheck.sh 测试
4. CASH≠CHAIN / LEDGER DRIFT
-> 停 bot -> 查链上余额 -> 设 bankroll -> 清 state -> 重启
-> CASH≠CHAIN > $1 会自动修,LEDGER DRIFT 只能手动清)
5. RTDS down
-> systemctl restart copybot-live(重连 WebSocket
6. 代理异常?
-> systemctl status mihomo
-> /opt/mihomo-healthcheck.sh
-> HTTPS_PROXY=http://127.0.0.1:7890 python3 host/geocheck.py | tail -1
7. daily.sh 没跑?
-> tail -10 /opt/winning-wallet-finder/live/daily.log
-> 手动跑:cd /opt/winning-wallet-finder/live && bash daily.sh
```