Synced from github/main: aebcfa2d copybot: live paper feed [skip ci]
Our additions: serve_dashboard.py, bot_dashboard.html, mihomo-healthcheck.sh,
中文文档, 我们的钱包配置
22 KiB
DEPLOY.md - 手动部署 paper + live copybot 到新服务器
一步一步来,可以一边看一边敲。不要一次性复制粘贴大段命令--每一步做完、看到预期输出,再进下一步。
适用:把 paper bot + live bot 部署到一台新的 Linux 服务器(Ubuntu/Debian),从你自己的 Gitea 拉代码。如果服务器在 Polymarket 禁单地区(美国/英国/法国等),用 mihomo 代理绕开 geo-block。
完成时间参考:60-90 分钟(含每步等待 + live 配置)。
完成部署后提供:
- paper bot:24/7 模拟跟单(直连,不走代理)
- live bot:24/7 真钱跟单(走代理绕 geo-block)
- bot 实时账本(中文 web dashboard):
http://服务器IP:18080/- 原始 JSON:
https://<GITEA>/<USERNAME>/winning-wallet-finder/raw/branch/main/live/copybot_live.json
目录
- Part A: Paper Bot 部署 (§0-§13)
- Part B: 代理配置(geo-block 绕行) (§14-§16)
- Part C: Live Bot 部署 (§17-§23)
- 常见坑
- 日常命令
Part A: Paper Bot 部署
0. 前置条件
- 一台 Linux VPS(Ubuntu 20.04+ / Debian 11+)
- 你有 root SSH 权限
- 你的 Gitea 上有这个仓库的镜像
- Gitea PAT(权限
repository: Write)
测 Polymarket 直连:
python3 -c "
import urllib.request, time
for url in [
'https://data-api.polymarket.com/v1/leaderboard?limit=1',
'https://gamma-api.polymarket.com/markets?limit=1',
'https://clob.polymarket.com/markets?next_cursor=',
]:
t = time.time()
try:
urllib.request.urlopen(urllib.request.Request(url, headers={'User-Agent':'Mozilla/5.0'}), timeout=10)
print(f'OK {(time.time()-t)*1000:6.0f}ms {url}')
except Exception as e:
print(f'FAIL {type(e).__name__}: {url}')
"
三行 OK = 能读 API(paper 够用)。
1. 装基础依赖
ssh root@你的服务器IP
apt-get update
apt-get install -y git python3 python3-pip ca-certificates
2. 从 Gitea 拉代码
cd /opt
git clone https://<USERNAME>:<TOKEN>@<GITEA>/<USERNAME>/winning-wallet-finder.git
cd winning-wallet-finder/live
3. 装 duckdb + 首次回测
pip3 install duckdb --break-system-packages
python3 portfolio.py --days 30
预期:portfolio[30d rolling]: equity $... | ... | -> portfolio.json
4. 前台测试 bot
python3 ../copybot.py --config copybot.paper.json --state copybot_state.json --poll 60
看到 watching N wallets + 2-3 条心跳后 Ctrl+C。
5. 配 Gitea 凭证
5.1 生成 PAT
Gitea -> 用户设置 -> Applications -> Generate Token(权限 repository: Write)
5.2 写 .netrc
nano ~/.netrc
machine <GITEA域名>
login <USERNAME>
password <TOKEN>
chmod 600 ~/.netrc
cd /opt/winning-wallet-finder
git push origin main --dry-run
5.3 root git 白名单
git config --global --add safe.directory /opt/winning-wallet-finder
6. 建专用用户 wwf
useradd --system --shell /usr/sbin/nologin --home /opt/winning-wallet-finder wwf
chown -R wwf:wwf /opt/winning-wallet-finder
chmod -R u+rwX,g+rX,o-rwx /opt/winning-wallet-finder
sudo cp /root/.netrc /opt/winning-wallet-finder/.netrc
sudo chown wwf:wwf /opt/winning-wallet-finder/.netrc
sudo chmod 600 /opt/winning-wallet-finder/.netrc
sudo -u wwf git config --global user.name "copybot[bot]"
sudo -u wwf git config --global user.email "copybot@wwf.local"
坑:wwf 没配 git identity 会导致 commit 报
Author identity unknown。上面最后两行不能漏。
7. 写 paper systemd unit
cat > /etc/systemd/system/copybot-paper.service <<'EOF'
[Unit]
Description=Polymarket Paper Copybot
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=wwf
Group=wwf
WorkingDirectory=/opt/winning-wallet-finder/live
ExecStart=/usr/bin/python3 /opt/winning-wallet-finder/copybot.py \
--config /opt/winning-wallet-finder/live/copybot.paper.json \
--state /opt/winning-wallet-finder/copybot_state.json \
--poll 60
Restart=always
RestartSec=10
TimeoutStopSec=30
Nice=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=copybot-paper
EnvironmentFile=-/etc/copybot/paper.env
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
RestrictSUIDSGID=true
RestrictNamespaces=true
LockPersonality=true
RestrictRealtime=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
SystemCallArchitectures=native
ReadWritePaths=/opt/winning-wallet-finder
[Install]
WantedBy=multi-user.target
EOF
8. 启动 paper bot
systemctl daemon-reload
systemctl enable --now copybot-paper
sleep 5
systemctl status copybot-paper
9. 验证 Gitea 推送
cd /opt/winning-wallet-finder
sudo -u wwf git add -A
sudo -u wwf git commit -m "manual sync"
sudo -u wwf git push
10. 加 Alchemy RPC
sudo install -d -m 750 -o wwf -g wwf /etc/copybot
sudo tee /etc/copybot/paper.env > /dev/null <<EOF
ALCHEMY_RPC_URL=https://polygon-mainnet.g.alchemy.com/v2/你的KEY
EOF
sudo chown wwf:wwf /etc/copybot/paper.env
sudo chmod 600 /etc/copybot/paper.env
sudo systemctl restart copybot-paper
11. 加每日 cron
timedatectl | grep "Time zone"
# UTC 服务器 -> 北京 10:00 = cron 0 2
(crontab -l 2>/dev/null; echo "0 2 * * * cd /opt/winning-wallet-finder/live && bash daily.sh >> daily.log 2>&1") | crontab -
12. 部署 web dashboard
cat > /etc/systemd/system/copybot-dashboard.service <<'EOF'
[Unit]
Description=Polymarket Copybot Live Dashboard
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=wwf
Group=wwf
WorkingDirectory=/opt/winning-wallet-finder/live
ExecStart=/usr/bin/python3 /opt/winning-wallet-finder/live/serve_dashboard.py
Restart=always
RestartSec=5
Nice=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=copybot-dashboard
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
RestrictNamespaces=true
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now copybot-dashboard
sleep 3
curl -s http://localhost:18080/api/feed | python3 -m json.tool | head -10
浏览器访问 http://服务器IP:18080/。如果打不开,放行防火墙 TCP 18080。
13. Paper 完成检查
- paper bot systemd 24/7 跑
- web dashboard @ :18080
- Alchemy RPC ON
- 状态推 Gitea
- 每日 cron
Part B: 代理配置(geo-block 绕行)
为什么要代理:Polymarket 禁止美国/英国/法国/德国/意大利/荷兰/波兰/新加坡/澳大利亚/加拿大安大略/巴西 等地区的 IP 下单。如果你的服务器在这些地区,live bot 需要走代理。
paper bot 不受影响(只读 API 全球开放)。
14. 检查 geo-gate
cd /opt/winning-wallet-finder
python3 host/geocheck.py
VERDICT: TRADABLE-> 跳过 Part B,直接去 Part CVERDICT: BLOCKED-> 继续往下配代理
15. 装 mihomo(Clash Meta 内核)
15.1 下载 mihomo
mkdir -p /etc/mihomo
cd /tmp
wget -q https://github.com/MetaCubeX/mihomo/releases/download/v1.19.0/mihomo-linux-amd64-v1.19.0.gz
gzip -d mihomo-linux-amd64-v1.19.0.gz
mv mihomo-linux-amd64-v1.19.0 /usr/local/bin/mihomo
chmod +x /usr/local/bin/mihomo
mihomo -v
15.2 拉订阅配置
坑:机场会检测 User-Agent,wget 默认 UA 会被返回假节点。必须伪装 UA。
wget -O /etc/mihomo/config.yaml \
--user-agent="clash-verge/v1.7.7" \
"你的订阅链接&flag=meta"
验证拿到的是真实节点(不是"当前Clash客户端不支持本机场协议"):
grep "name:" /etc/mihomo/config.yaml | grep -i "日本\|香港\|新加坡" | head -10
15.3 改全局模式 + 启动
sed -i 's/^mode: rule/mode: global/' /etc/mihomo/config.yaml
nohup mihomo -d /etc/mihomo > /var/log/mihomo.log 2>&1 &
sleep 5
# 选一个非美国节点(以日本为例)
curl -s -X PUT http://127.0.0.1:9090/proxies/GLOBAL \
-H "Content-Type: application/json" \
-d '{"name":"节点名"}'
# 验证
HTTPS_PROXY=http://127.0.0.1:7890 python3 host/geocheck.py
期望 VERDICT: TRADABLE。
15.4 做 systemd 服务
kill $(pgrep mihomo)
cat > /etc/systemd/system/mihomo.service <<'EOF'
[Unit]
Description=Mihomo (Clash Meta) Proxy
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/mihomo -d /etc/mihomo
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=mihomo
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now mihomo
sleep 5
# 重选节点(mihomo 重启会记住,但第一次要手动选)
curl -s -X PUT http://127.0.0.1:9090/proxies/GLOBAL \
-H "Content-Type: application/json" \
-d '{"name":"节点名"}'
# 验证代理 + geo-gate
HTTPS_PROXY=http://127.0.0.1:7890 python3 host/geocheck.py | tail -1
mihomo 会记住节点选择:
cache.db持久化,重启不丢。
15.5 自动健康检查(节点挂了自动换)
cat > /opt/mihomo-healthcheck.sh <<'SCRIPT'
#!/bin/bash
API="http://127.0.0.1:9090"
PROXY="http://127.0.0.1:7890"
code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 -x $PROXY \
"https://clob.polymarket.com/markets?next_cursor=" 2>/dev/null)
if [ "$code" = "200" ]; then exit 0; fi
nodes=$(curl -s "$API/proxies" | python3 -c "
import sys, json
d = json.load(sys.stdin)
skip = {'DIRECT','REJECT','自动选择','故障转移','良心云','GLOBAL'}
for name, info in d.get('proxies', {}).items():
if name in skip: continue
if info.get('type') in ('Selector','URLTest','Fallback','LoadBalance'): continue
if any(k in name for k in ['美国','US','United States']): continue
print(name)
")
for node in $nodes; do
curl -s -X PUT "$API/proxies/GLOBAL" -H "Content-Type: application/json" -d "{\"name\":\"$node\"}" > /dev/null
sleep 2
code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 -x $PROXY \
"https://clob.polymarket.com/markets?next_cursor=" 2>/dev/null)
if [ "$code" = "200" ]; then
logger "mihomo-healthcheck: switched to $node"
exit 0
fi
done
logger "mihomo-healthcheck: ALL nodes failed"
exit 1
SCRIPT
chmod +x /opt/mihomo-healthcheck.sh
(crontab -l 2>/dev/null | grep -v mihomo-healthcheck; echo "*/5 * * * * /opt/mihomo-healthcheck.sh") | crontab -
代理只影响 live bot:mihomo 监听
127.0.0.1:7890,不是透明代理。只有设了HTTPS_PROXY的程序才走代理。paper bot / dashboard / Gitea 全部直连,不受影响。不要把
HTTPS_PROXY写进/etc/environment或~/.bashrc(会全局生效),只放在/etc/copybot/live.env里。
Part C: Live Bot 部署
真钱! 每一步验证完再进下一步。建议先 $3-5 测试。
17. 装 live 依赖
pip3 install py-clob-client==0.34.6 web3==7.16.0 polymarket-client==0.1.0b16 websocket-client --break-system-packages
坑:
preflight_live.py需要polymarket-client(不是py-clob-client),漏装会报ModuleNotFoundError: No module named 'polymarket'。
18. 准备钱包
18.1 钱包架构说明
Polymarket 2026 新架构有两套钱包系统:
你的私钥(MetaMask 导出)
|
+-- EOA 地址(MetaMask 本身)
+-- 代理钱包(邮箱登录自动创建,POLY_PROXY type=1)
+-- 存款钱包(API 自动创建,POLY_1271 type=3)<- bot 用这个
bot 只能用存款钱包。pUSD 在 EOA 或代理钱包里 bot 看不到。
18.2 用 MetaMask 登录(推荐,比邮箱简单)
- 浏览器装 MetaMask,创建新钱包
- 用 MetaMask 登录 polymarket.com
- MetaMask -> 账户详情 -> 导出私钥
18.3 创建存款钱包
cd /opt/winning-wallet-finder
export HTTPS_PROXY=http://127.0.0.1:7890
export LIVE_PRIVATE_KEY=0x你的MetaMask私钥
python3 -c "
from polymarket import SecureClient
client = SecureClient.create(private_key='$LIVE_PRIVATE_KEY')
print('存款钱包:', client.wallet)
b = client.get_balance_allowance(asset_type='COLLATERAL')
print('pUSD 余额: \$' + str(b.balance / 1e6))
client.close()
"
坑(邮箱登录用户):如果用邮箱登录的私钥(Magic.link 导出),
SecureClient.create()会报Gasless transactions require a Builder API Key。需要用 Builder API 凭证创建存款钱包:bk = BuilderApiKey(key=..., secret=..., passphrase=...) client = SecureClient.create(private_key=pk, api_key=bk)Builder 凭证在 polymarket.com -> Settings -> API Keys 获取。创建完存款钱包后不再需要 Builder 凭证。
MetaMask 登录用户不受此影响,存款钱包自动创建。
18.4 充值 USDC -> pUSD
关键:必须充到存款钱包地址,不是 MetaMask 地址。
- 从交易所提 USDC 到 Polygon 链
- 接收地址 = 步骤 18.3 打印的存款钱包地址
- 到账后 USDC 自动转成 pUSD
坑:充到 Polymarket 网站显示的充值地址(代理钱包)是错的,bot 在存款钱包里看不到。必须充到存款钱包地址。
18.5 验证余额
export HTTPS_PROXY=http://127.0.0.1:7890
export LIVE_PRIVATE_KEY=0x你的私钥
python3 -c "
from polymarket import SecureClient
client = SecureClient.create(private_key='$LIVE_PRIVATE_KEY')
b = client.get_balance_allowance(asset_type='COLLATERAL')
print('pUSD: \$' + str(b.balance / 1e6))
client.close()
"
19. 创建 config.live.json
cd /opt/winning-wallet-finder
cp config.live.example.json config.live.json
# 改 bankroll_usd 为实际 pUSD 余额
sed -i 's/"bankroll_usd": 22.28/"bankroll_usd": 你的余额/' config.live.json
chown wwf:wwf config.live.json
chmod 640 config.live.json
坑:config.live.json 是 root 创建的,wwf 用户读不了会报
PermissionError。必须chown wwf:wwf。
20. 创建 live.env
cat > /etc/copybot/live.env <<'EOF'
LIVE_PRIVATE_KEY=0x你的MetaMask私钥
LIVE_FUNDER_ADDRESS=0x你的MetaMask地址
LIVE_SIGNATURE_TYPE=1
LIVE_CONFIRM=TRADE LIVE
HTTPS_PROXY=http://127.0.0.1:7890
EOF
chown wwf:wwf /etc/copybot/live.env
chmod 600 /etc/copybot/live.env
坑 1:
LIVE_CONFIRM的值必须是TRADE LIVE(copytrade.py:48 的CONFIRM_PHRASE),不是I understand real money is at risk。填错会报Aborted - LIVE_CONFIRM is set but does not match the phrase。坑 2:systemd EnvironmentFile 里值不要加引号。
LIVE_CONFIRM=TRADE LIVE是对的,LIVE_CONFIRM="TRADE LIVE"会让值带引号导致不匹配。坑 3:
HTTPS_PROXY只在 live.env 里设,不要放 paper.env(paper bot 不需要代理)。
21. 跑 preflight
cd /opt/winning-wallet-finder
export LIVE_PRIVATE_KEY=$(grep LIVE_PRIVATE_KEY /etc/copybot/live.env | cut -d= -f2-)
export LIVE_FUNDER_ADDRESS=$(grep LIVE_FUNDER_ADDRESS /etc/copybot/live.env | cut -d= -f2-)
export HTTPS_PROXY=http://127.0.0.1:7890
python3 preflight_live.py
全绿才能继续。常见失败:
ModuleNotFoundError: No module named 'websocket'->pip3 install websocket-clientGasless transactions require a Builder API Key-> 见 §18.3 邮箱登录的坑Invalid private_key: 'ascii' codec can't encode-> 私钥里有非 ASCII 字符(中文占位符没替换)
22. 写 live systemd unit
cat > /etc/systemd/system/copybot-live.service <<'EOF'
[Unit]
Description=Polymarket LIVE Copybot (real money)
After=network-online.target mihomo.service
Wants=network-online.target
Requires=mihomo.service
[Service]
Type=simple
User=wwf
Group=wwf
WorkingDirectory=/opt/winning-wallet-finder/live
ExecStart=/usr/bin/python3 /opt/winning-wallet-finder/copybot.py \
--config /opt/winning-wallet-finder/config.live.json \
--state /opt/winning-wallet-finder/copybot_state.live.json \
--poll 60 --live
Restart=always
RestartSec=10
TimeoutStopSec=30
Nice=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=copybot-live
EnvironmentFile=/etc/copybot/live.env
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
RestrictSUIDSGID=true
RestrictNamespaces=true
LockPersonality=true
RestrictRealtime=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
SystemCallArchitectures=native
ReadWritePaths=/opt/winning-wallet-finder
[Install]
WantedBy=multi-user.target
EOF
Requires=mihomo.service+After=mihomo.service:代理先启动,live bot 才启动。
23. 启动 live bot
systemctl daemon-reload
systemctl enable --now copybot-live
sleep 10
journalctl -u copybot-live --since "15 seconds ago" --no-pager
预期看到:
geo-gate: TRADABLE - order endpoint reachable
LIVE MODE - this will place REAL orders with REAL money.
confirmed via LIVE_CONFIRM env - armed.
mode: LIVE - REAL MONEY
watching 5 wallets
清空 live state(如果出现 LEDGER DRIFT)
systemctl stop copybot-live
rm -f /opt/winning-wallet-finder/copybot_state.live.json
rm -f /opt/winning-wallet-finder/copybot_fills.live.jsonl
rm -f /opt/winning-wallet-finder/live/copybot_live_real.json
systemctl start copybot-live
监控第一笔成交
journalctl -u copybot-live -f
看到 FOLLOW + [LIVE] order placed = 第一笔真钱成交。然后去 polymarket.com 用 MetaMask 登录确认仓位。
完整部署架构
服务器
|
+-- mihomo.service 代理(日本节点,127.0.0.1:7890)
| +-- healthcheck cron 每 5 分钟自动换节点
|
+-- copybot-paper.service paper bot(直连,不走代理)
| +-- EnvironmentFile: paper.env(ALCHEMY_RPC_URL)
| +-- state: copybot_state.json
| +-- feed: live/copybot_live.json
|
+-- copybot-live.service live bot(走代理绕 geo-block)
| +-- EnvironmentFile: live.env(LIVE_PRIVATE_KEY + HTTPS_PROXY + TRADE LIVE)
| +-- state: copybot_state.live.json
| +-- feed: live/copybot_live_real.json
|
+-- copybot-dashboard.service web dashboard(:18080)
|
+-- cron 每日 02:00 UTC daily.sh
常见坑
| 坑 | 症状 | 解法 |
|---|---|---|
| 没装 duckdb | ModuleNotFoundError: No module named 'duckdb' |
pip3 install duckdb --break-system-packages |
| 没装 polymarket-client | ModuleNotFoundError: No module named 'polymarket' |
pip3 install polymarket-client==0.1.0b16 |
| 没装 websocket-client | preflight 报 No module named 'websocket' / 日志 rtds off |
pip3 install websocket-client |
| wwf 没配 git identity | commit 报 Author identity unknown |
§6 最后两行 |
| root 看不到 wwf 仓库 | dubious ownership |
git config --global --add safe.directory ... |
| config.live.json 权限 | PermissionError: [Errno 13] |
chown wwf:wwf config.live.json |
| LIVE_CONFIRM 值错 | Aborted - LIVE_CONFIRM is set but does not match |
值必须是 TRADE LIVE,不加引号 |
| 私钥有非 ASCII 字符 | UnicodeEncodeError: 'ascii' codec can't encode |
env 文件里私钥必须是纯 hex(0x + 64 位),不能有中文占位符 |
| 邮箱登录建存款钱包失败 | Gasless transactions require a Builder API Key |
用 Builder API 凭证创建(§18.3),或改用 MetaMask 登录 |
| 充错钱包 | bot 余额 $0 | 必须充到存款钱包地址,不是 MetaMask 地址或网站充值地址 |
| geo-gate BLOCKED | VERDICT: BLOCKED |
服务器在禁单区,需配代理(Part B) |
| 机场返回假节点 | 节点名是"当前Clash客户端不支持" | wget 加 --user-agent="clash-verge/v1.7.7" + &flag=meta |
| mihomo 重启丢节点 | 出口 IP 变回美国 | mihomo cache.db 自动记忆;第一次需手动选节点 |
| LEDGER DRIFT | check_book could not attribute it |
清空 live state 重来(§23) |
| bot 不自动 commit | dashboard 数据陈旧 | commit-on-change 节流,无新交易不写;手动 commit 见 §9 |
| 浏览器 dashboard 不更新 | 数据没变 | Ctrl+Shift+R 强制刷新 |
| cron 时区错 | daily.sh 时间不对 | 服务器 UTC,北京 10:00 = cron 0 2 |
| 防火墙挡 18080 | 浏览器打不开 dashboard | 放行 TCP 18080 |
日常用的命令
# === 看日志 ===
journalctl -u copybot-paper -f # paper bot
journalctl -u copybot-live -f # live bot
journalctl -u copybot-dashboard -f # dashboard
journalctl -u mihomo -f # 代理
# === 看 dashboard ===
# 浏览器:http://服务器IP:18080/
# === 改配置 ===
nano /opt/winning-wallet-finder/live/copybot.paper.json # paper 跟单钱包/金额
sudo systemctl restart copybot-paper
nano /opt/winning-wallet-finder/config.live.json # live 跟单钱包/金额
sudo systemctl restart copybot-live
# === 代理管理 ===
# 看当前节点
curl -s http://127.0.0.1:9090/proxies/GLOBAL | python3 -c "import sys,json;print(json.load(sys.stdin).get('now'))"
# 换节点
curl -s -X PUT http://127.0.0.1:9090/proxies/GLOBAL -H "Content-Type: application/json" -d '{"name":"节点名"}'
# 手动健康检查
/opt/mihomo-healthcheck.sh
# === 停/启动 ===
sudo systemctl stop copybot-live # 停 live(真钱!)
sudo systemctl start copybot-live
sudo systemctl stop copybot-paper # 停 paper
sudo systemctl stop mihomo # 停代理(live 也会停)
# === 完全卸载 ===
sudo systemctl disable --now copybot-paper copybot-live copybot-dashboard mihomo
sudo rm /etc/systemd/system/copybot-*.service /etc/systemd/system/mihomo.service
sudo userdel wwf
rm -rf /opt/winning-wallet-finder