Files
gavindiaz 9259325d8a
deploy GitHub Pages / deploy (push) Has been cancelled
tests / test (push) Has been cancelled
first commit
2026-07-22 18:53:44 +08:00

562 lines
15 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 套利机器人 — 中文使用手册
> 适用版本:v0.1.0 · Python 3.12+ · Windows / Linux / macOS
>
> 本文从环境准备到日常运维,按真实使用顺序逐步讲解。
---
## 目录
1. [项目简介](#1-项目简介)
2. [系统要求](#2-系统要求)
3. [快速安装](#3-快速安装)
4. [首次配置](#4-首次配置)
5. [首次运行](#5-首次运行)
6. [日常使用(Paper 模式)](#6-日常使用paper-模式)
7. [Web 仪表板](#7-web-仪表板)
8. [切换到 Live 模式](#8-切换到-live-模式)
9. [监控与运维](#9-监控与运维)
10. [故障排查](#10-故障排查)
11. [安全建议](#11-安全建议)
12. [附录:环境变量参考](#附录环境变量参考)
---
## 1. 项目简介
**Polymarket NegRisk 多结果套利机器人**。Polymarket 的某些分类事件(例如"2028 大选谁赢")每个结果独立挂牌交易。当**所有结果的卖一价加总 < $1** 时,买入一整套结果,结算时一定收回 $1 → **无风险套利**
本机器人扫描所有活跃 negRisk 事件,实时计算套利空间,提供两种模式:
| 模式 | 资金风险 | 用途 |
|------|---------|------|
| **Paper** | $0 | 模拟成交,验证策略,观察 PnL |
| **Live** | 实盘资金 | 真实下单,需提供钱包私钥 |
---
## 2. 系统要求
| 项目 | 要求 |
|------|------|
| **Python** | 3.12 或更高 |
| **操作系统** | Windows 10+ / Linux / macOS |
| **内存** | ≥ 512 MB |
| **磁盘** | ≥ 1 GB(含 SQLite 与日志) |
| **网络** | **必须**能访问 `polymarket.com`(国内需配置代理) |
| **钱包**(仅 Live | Polygon 上的专用 EOA,持有 USDC.e |
**国内用户注意**Polymarket 服务器在国内无法直连,必须配置 HTTP 代理(详见 [§4.2 网络代理](#42-网络代理))。
---
## 3. 快速安装
### 3.1 克隆项目
```bash
git clone https://github.com/matthewnyc2/arbitrage
cd arbitrage
```
### 3.2 创建虚拟环境
**Windows (PowerShell)**
```powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
```
**Linux / macOS**
```bash
python3 -m venv .venv
source .venv/bin/activate
```
### 3.3 安装依赖
```bash
pip install -e ".[dev]"
```
> **⚠️ 重要**:默认会装 `websockets>=13.0`pip 可能会拉到 **15.x** 版本,但该版本与项目不兼容(参见 [§10.3 websockets 版本问题](#103-websockets-版本问题))。**显式锁版本**:
> ```bash
> pip install "websockets==14.2"
> ```
### 3.4 验证安装
```bash
arb --help
```
应显示:`init / discover / scan / web / resolve` 五个子命令。
---
## 4. 首次配置
### 4.1 复制环境变量模板
```bash
cp .env.example .env
```
`.env` 关键字段(Paper 模式默认值即可运行):
```ini
ARB_MODE=paper # paper = 模拟盘;live = 实盘
ARB_CLOB_HOST=https://clob.polymarket.com
ARB_GAMMA_HOST=https://gamma-api.polymarket.com
ARB_PROXY= # 国内用户填代理,见下文
```
### 4.2 网络代理
**国内 / 防火墙环境下必须配置**,否则 `arb discover` 会报 `httpx.ConnectTimeout`
**PowerShell 临时设置**(仅当前会话):
```powershell
$env:HTTPS_PROXY="http://127.0.0.1:7890" # 改成你的代理地址
$env:HTTP_PROXY="http://127.0.0.1:7890"
```
**永久设置**(写入 `.env`):
```ini
ARB_PROXY=http://127.0.0.1:7890
```
> **常见代理端口**Clash 默认 7890V2Ray 默认 10809SSR 默认 1080。
>
> 配置完成后 httpxREST)和 websocketsWS)都会通过代理连接。
### 4.3 验证代理可用
```bash
arb discover
```
预期输出:
```
seen=2000 negRisk=928 upserted=894 malformed=34
```
如果仍超时,回到 [§10.1 网络连通性](#101-网络连通性)。
---
## 5. 首次运行
### 5.1 初始化数据库
```bash
arb init
```
创建 SQLite 表结构(含 events / outcomes / opportunities / baskets / fills / live_orders / resolutions / denylist / daily_pnl)。
### 5.2 发现事件
```bash
arb discover
```
从 Polymarket Gamma API 拉取所有活跃 negRisk 事件。需要约 1020 秒。
**持续发现**(生产部署推荐,每 180 秒刷新):
```bash
arb discover --loop --interval 180
```
### 5.3 启动扫描
```bash
arb scan
```
开始监听 CLOB WebSocket,实时检测套利机会,写入 paper baskets。
预期日志:
```
scan loop started (885 events hydrated)
ws subscribed to N tokens
```
### 5.4 启动 Web 仪表板(另一终端)
```bash
arb web
```
打开浏览器访问 <http://127.0.0.1:8000>。
---
## 6. 日常使用(Paper 模式)
### 6.1 标准三进程部署
| 终端 | 命令 | 作用 |
|------|------|------|
| 终端 1 | `arb discover --loop --interval 180` | 每 3 分钟刷新事件列表 |
| 终端 2 | `arb scan` | 实时扫描 + 模拟成交 |
| 终端 3 | `arb web` | Web 仪表板 |
**Linux/macOS 后台运行**
```bash
nohup arb discover --loop --interval 180 > logs/discover.log 2>&1 &
nohup arb scan > logs/scan.log 2>&1 &
```
### 6.2 CLI 命令速查
| 命令 | 功能 |
|------|------|
| `arb init` | 创建 SQLite schema |
| `arb discover` | 单次拉取事件 |
| `arb discover --loop` | 持续拉取 |
| `arb scan` | 启动扫描 |
| `arb web` | 启动仪表板(默认 127.0.0.1:8000 |
| `arb resolve <event_id> --winner <token_id>` | 手动标记事件结算结果 |
| `arb resolve <event_id>` | 标记为 invalid |
### 6.3 停止服务
**正常停止**:在运行终端按 `Ctrl+C`
**强制清理残留进程**Windows):
```powershell
Get-Process python | Where-Object { $_.StartTime -gt (Get-Date).AddHours(-1) } | Stop-Process -Force
```
---
## 7. Web 仪表板
仪表板每 2–3 秒自动刷新(HTMX 轮询),无需手动操作。
### 7.1 主要面板
| 面板 | 显示内容 |
|------|---------|
| **Paper PnL** | 已实现盈亏、各状态组合数、Kill Switch 按钮 |
| **Baskets** | 最近 25 个组合:ID、事件、份数、成本、状态、PnL |
| **Recent Opportunities** | 最近 25 个检测到的机会:Σ asks、净边际、最大组合、预期利润 |
### 7.2 关键指标解读
**Σ asks < $1** → 存在套利空间。典型值:
- `0.98` → 净边际 ~2%(扣费前)
- `0.95` → 净边际 ~5%(扣费前)
- `< 0.90` → 非常罕见的深度套利
**net edge bps**:扣除手续费 + 分摊 gas 后的净边际。低于 50 bps 会被 `ARB_MIN_NET_EDGE_BPS` 过滤。
**status**
| 状态 | 含义 |
|------|------|
| `pending_resolution` | 等待事件结算 |
| `redeemed` | 已结算,PnL 入账 |
| `failed` | 部分腿未成交(paper 模拟时深度消失) |
| `invalid` | 事件被标记为无效 |
### 7.3 Kill Switch
仪表板右下角红色 **kill** 按钮,点击后立即创建 `./KILL` 文件,执行器会拒绝所有新单。再次点击 **unkill** 删除文件即可恢复。
也可在终端手动:
```bash
# Windows
New-Item -Path .\KILL -ItemType File
# Linux/macOS
touch ./KILL
```
---
## 8. 切换到 Live 模式
### 8.1 准备工作
1. **专用钱包**:在 Polygon 上创建一个新的 EOA,**不要复用个人钱包**
2. **充值**:向钱包转入 USDC.e(建议 ≥ $200,含 gas
3. **批准授权**:按 `docs/api/order-signing.md` §8 的脚本,对 CTF Exchange + NegRisk Exchange + NegRisk Adapter 三个合约授权
### 8.2 派生 L2 API 凭证
`docs/api/order-signing.md` §2 的 `bootstrap_clob_creds.py` 脚本执行一次,生成:
- `CLOB_API_KEY`
- `CLOB_SECRET`
- `CLOB_PASSPHRASE`
**这三个凭证无法恢复,丢失需重新派生。**
### 8.3 修改 `.env`
```ini
ARB_MODE=live
ARB_PRIVATE_KEY=0x... # 钱包私钥(0x 前缀)
ARB_FUNDER_ADDRESS=0x... # 资金地址(EOA 模式 = 私钥对应地址)
ARB_SIGNATURE_TYPE=0 # 0=EOA, 1=Polymarket proxy, 2=Gnosis Safe
ARB_API_KEY=...
ARB_API_SECRET=...
ARB_API_PASSPHRASE=...
# 风险上限(强烈建议保持默认值)
ARB_MAX_BASKET_USD=50
ARB_MAX_OPEN_BASKETS=3
ARB_DAILY_LOSS_STOP_USD=100
```
### 8.4 首次实盘(强烈建议 dry_run 验证)
**第 1 步**:先用 dry_run 验证签名链路:
```python
# 在 Python REPL 中手动测试
from arbitrage.engine.live_executor import LiveExecutor, RiskLimits
from arbitrage.book.l2 import BookRegistry
books = BookRegistry()
ex = LiveExecutor(books=books, dry_run=True) # 只签名不提交
```
**第 2 步**:把 `cli.py:87``dry_run=False` 保持不变(默认就是 False),但**先把 `ARB_MAX_BASKET_USD` 设为 `5`**
```ini
ARB_MAX_BASKET_USD=5
ARB_MAX_OPEN_BASKETS=1
```
**第 3 步**:观察 1–2 天无异常后,再逐步放大到默认值。
---
## 9. 监控与运维
### 9.1 日志
日志写在 `logs/arbitrage.jsonl`(结构化 JSON,每天 0 点轮转,保留 7 天)。
**实时查看**
```bash
# Linux/macOS
tail -f logs/arbitrage.jsonl | jq .
# Windows PowerShell
Get-Content logs\arbitrage.jsonl -Wait
```
**按事件过滤**
```bash
grep "new_market" logs/arbitrage.jsonl | tail -20
```
### 9.2 数据库
位置:`./arbitrage.db`SQLite + WAL 模式)
**直接查询**
```bash
sqlite3 arbitrage.db "SELECT status, COUNT(*) FROM baskets GROUP BY status;"
sqlite3 arbitrage.db "SELECT * FROM opportunities ORDER BY detected_at DESC LIMIT 10;"
```
**备份**
```bash
cp arbitrage.db arbitrage.db.bak-$(date +%Y%m%d)
```
**重置**(清空所有 paper 数据):
```bash
rm arbitrage.db
arb init
arb discover
```
### 9.3 进程监控
**检查运行中的 arb 进程**
```powershell
# Windows
Get-Process | Where-Object { $_.Name -eq "python" -and $_.Path -like "*Python312*" }
# Linux
ps aux | grep arb
```
**查看资源占用**
```bash
# 内存占用(正常 100-300 MB
Get-Process python | Select-Object Id, @{n='Mem(MB)';e={[int]$_.WorkingSet64/1MB}}
```
### 9.4 升级
```bash
cd arbitrage
git pull
pip install -e ".[dev]"
# 重启 arb scan / arb web
```
---
## 10. 故障排查
### 10.1 网络连通性
**症状**`httpx.ConnectTimeout``arb discover` 卡住。
**排查**
```powershell
Test-NetConnection -ComputerName gamma-api.polymarket.com -Port 443
```
如果失败,确认 `ARB_PROXY` 设置正确,或切换代理节点。
### 10.2 WebSocket 反复重连
**症状**`ws disconnect (TimeoutError); reconnecting in 1.0s` 大量重复。
**根因**WebSocket 没有走代理。
**修复**:确认 `.env` 中设置了 `ARB_PROXY=http://...`,代码会自动通过 HTTP CONNECT 隧道建立 WS 连接。
### 10.3 websockets 版本问题
**症状**`AttributeError: 'ClientConnection' object has no attribute 'recv_messages'`,或 `got an unexpected keyword argument 'proxy'`
**根因**`websockets 15.0` 与项目不兼容。
**修复**
```bash
pip install "websockets==14.2"
```
### 10.4 日志 PermissionError
**症状**`PermissionError: [WinError 32] 另一个程序正在使用此文件`
**根因**loguru 在 Windows 上的 50MB 轮转触发 `close() → os.rename()` 竞态。
**修复**(已默认配置):`rotation="1 day"` + `retention="7 days"`,避免高并发期轮转。
如果仍出现:
```powershell
# 清理残留进程
Get-Process python | Where-Object { $_.Path -like "*Python312*" } | Stop-Process -Force
```
### 10.5 残留进程占用文件
**症状**:杀掉 `arb scan` 后,新进程仍报 PermissionError。
**排查**
```powershell
Get-Process | Where-Object { $_.Name -eq "python" }
```
**清理**
```powershell
Get-Process python -ErrorAction SilentlyContinue | Stop-Process -Force
```
### 10.6 仪表板 404 / 端口冲突
**症状**:浏览器访问 `http://127.0.0.1:8000` 返回 404 或连接拒绝。
**排查**
```powershell
Test-NetConnection -ComputerName 127.0.0.1 -Port 8000
Get-NetTCPConnection -LocalPort 8000 -State Listen
```
**修复**:更换端口启动:
```bash
arb web --port 8888
```
### 10.7 没有任何套利机会
**症状**:仪表板显示 `recent opportunities` 为空或 net edge bps 全部 < 50。
**原因**
1. 当前 Polymarket 没有负空间(正常 — 套利机会稀少)
2. WS 数据未刷新(检查 `ws subscribed` 日志)
3. `ARB_MIN_NET_EDGE_BPS` 设得太高(默认值 50 合理)
---
## 11. 安全建议
### 11.1 私钥保护
- **永远不要把 `.env` 提交到 Git**(已在 `.gitignore` 中)
- 实盘私钥使用**专用钱包**,与其他资产隔离
- 服务器上 `.env` 文件权限设为 `chmod 600`Linux
- 考虑使用硬件钱包或多签
### 11.2 Web 仪表板
- 默认绑定 `127.0.0.1`**不要**改为 `0.0.0.0` 后暴露到公网
- `kill` / `unkill` 端点**无身份认证**,暴露即等于把钱包控制权交给攻击者
- 如果需要远程访问,使用 SSH 隧道:
```bash
ssh -L 8000:127.0.0.1:8000 user@server
```
### 11.3 依赖管理
- 锁定关键依赖版本,避免供应链风险:
```bash
pip install "websockets==14.2" "py-clob-client==0.34.6"
```
- 定期升级时检查 changelog
### 11.4 Live 模式额外建议
- 先用 ≥ $10 的小资金跑 1 周
- 每日检查 realized PnL,确认与 paper 结果趋势一致
- 设置价格告警(Discord webhook / Telegram bot 等)
- 监控 `daily_pnl` 表,日亏损接近 `ARB_DAILY_LOSS_STOP_USD` 时手动 kill
---
## 附录:环境变量参考
完整 `.env` 配置项:
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `ARB_MODE` | `paper` | `paper` / `live` |
| `ARB_CLOB_HOST` | `https://clob.polymarket.com` | CLOB API |
| `ARB_GAMMA_HOST` | `https://gamma-api.polymarket.com` | Gamma REST |
| `ARB_DATA_HOST` | `https://data-api.polymarket.com` | Data API |
| `ARB_WS_URL` | `wss://ws-subscriptions-clob.polymarket.com/ws/market` | WS 端点 |
| `ARB_POLYGON_RPC` | `https://polygon-rpc.com` | Polygon RPC |
| `ARB_PROXY` | _空_ | HTTP 代理(REST + WS 共享) |
| `ARB_PRIVATE_KEY` | _空_ | 钱包私钥(Live 必填) |
| `ARB_FUNDER_ADDRESS` | _空_ | 资金地址(Live 必填) |
| `ARB_SIGNATURE_TYPE` | `0` | 0=EOA / 1=proxy / 2=Safe |
| `ARB_API_KEY` | _空_ | CLOB L2 API key |
| `ARB_API_SECRET` | _空_ | CLOB L2 API secret |
| `ARB_API_PASSPHRASE` | _空_ | CLOB L2 API passphrase |
| `ARB_MIN_NET_EDGE_BPS` | `50` | 最低净边际(基点) |
| `ARB_MAX_BASKET_USD` | `50` | 单笔组合上限 USD |
| `ARB_MAX_OPEN_BASKETS` | `3` | 全局未结算组合上限 |
| `ARB_DAILY_LOSS_STOP_USD` | `100` | 日亏损止损 USD |
| `ARB_KILL_SWITCH_FILE` | `./KILL` | Kill switch 文件路径 |
| `ARB_PAPER_LATENCY_MS` | `250` | Paper 模拟延迟(毫秒) |
| `ARB_DB_PATH` | `./arbitrage.db` | SQLite 路径 |
| `ARB_WEB_HOST` | `127.0.0.1` | Web 监听地址 |
| `ARB_WEB_PORT` | `8000` | Web 端口 |
| `ARB_LOG_LEVEL` | `INFO` | 控制台日志级别 |
---
## 联系与反馈
- 项目主页:<https://matthewnyc2.github.io/arbitrage/>
- GitHub Issues<https://github.com/matthewnyc2/arbitrage/issues>
- API 参考:`docs/api/order-signing.md`、`docs/api/negrisk.md`
- 设计文档:`DESIGN.md`