
最近折腾 Polymarket 量化交易 Bot,踩了几个坑,这篇把核心架构问题说清楚。
当一个 Bot 开始真正做交易决策的时候,它就开始变得难以维护了。
拉取订单簿容易,发送订单也容易。真正的工程难题在于:市场状态、策略状态、订单状态、仓位、风险控制、执行行为这些模块全都在异步变化,你怎么保持它们之间的一致性?
这才是正经的 Polymarket Bot 架构要解决的问题。
作者:Bo$onaX
Polymarket 交易机器人 | 量化交易 | Rust | Web3 基础设施
GitHub:https://github.com/n9xdev/poly-alpha-lab
Telegram:https://t.me/bosonax
YouTube:https://youtube.com/@bosonax
X:https://x.com/xxniiinxx
Polymarket:https://polymarket.com/@bosona
用六个边界来思考:
Market Discovery ↓
Market Data ───────→ Strategy Engine ↓ ↓
State Store ←──────── Decision ↓ ↓
Risk Engine ───────→ Execution Engine ↓ Polymarket CLOB ↓ Order / Fill Events ↓ Reconciliation
关键细节是底部的这个反馈循环。
Bot 不应该假设 HTTP 响应成功就意味着交易状态正确。一笔订单可能保持挂起、部分成交、完全成交、被取消,或者与 Bot 本地假设不一致。因此执行层需要把事件反馈到状态系统和风险系统。
Polymarket 将市场信息与交易基础设施分开。
当前的开发者文档将市场发现和市场元数据与 CLOB 交易分开暴露,有专门的概念处理市场、事件、价格、订单簿、仓位、订单和结算。
一个有用的发现服务应该维护:
在策略循环之前完成这些。
策略应该接收一个已经标准化过的市场对象,而不是在每次交易决策时重复查询元数据。
轮询 REST 接口适合初始化、恢复和定期对账。但作为对延迟敏感的决策循环的基础,它就差点意思了。
Polymarket 通过 WebSocket 基础设施提供实时市场数据和认证订单更新。官方文档也暴露了专门的实时数据和订单更新部分。
所以 Rust 实现可以这样分开:
WebSocket task ↓
event parser ↓
normalized event ↓
single-writer market state ↓
strategy
策略永远不应该直接修改原始 WebSocket 表示。
将交易所事件转换为内部类型,比如:
struct BookUpdate { token_id: String, best_bid: f64, best_ask: f64, timestamp_ns: u64,
}
对于生产级交易,用定点数或 Decimal 运算比随手用浮点数处理价格和数量要好得多。
这是值得保护的核心架构边界之一。
不要这样做:
strategy → API → order
应该这样做:
strategy → OrderIntent → risk → execution
比如:
struct OrderIntent { token_id: String, side: Side, price: Decimal, size: Decimal, reason: StrategyReason,
}
策略回答的是:
"我想要这个敞口。"
风险引擎回答的是:
"这个敞口允许吗?"
执行引擎回答的是:
"这个意图应该怎么到达交易所?"
这种分离让纸交易、回放测试、策略实验和紧急关停都变得简单得多。
一个盈利的信号仍然可能产生一个搞砸的交易系统。
风险层至少应该检查:
Polymarket 自己也指出,当流动性不足时,期望的交易规模可能无法在不产生显著价格冲击的情况下执行。
这意味着仓位大小计算不能与实时订单簿分开。
一个有用的规则是:
signal strength ≠ permitted trade size
后者必须在风险和流动性约束应用后才能计算出来。
执行引擎应该负责:
当前 Polymarket 文档提供了认证、下单、订单管理和实时订单更新的专门工作流程。
不要把这些操作埋在策略代码里。
这样同一个策略可以使用不同的执行策略而不需要重写策略本身:
OrderIntent ├── passive limit execution ├── aggressive execution └── staged execution
执行策略也应该了解当前的费率规则。Polymarket 当前费率文档指出,费率根据市场类别而不同,在成交时收取,而做市商不收取交易费用。
这是没经验的 Bot 通常会漏掉的组件。
维护两个概念:
Desired State
Actual Exchange State
然后持续对比它们。
比如:
local: order ABC = OPEN, 100 shares
remote: order ABC = FILLED, 63 shares
Bot 必须让自己的内部状态向交易所状态收敛,而不是盲目假设之前的命令成功了。
在重连、进程重启、网络故障或交易所端事件之后,对账就变成了恢复机制。
一个实用的部署可以保持令人惊讶的小规模:
┌──────────────────────────────┐
│ Bot Process │
│ │
│ Discovery │
│ WebSocket Market Feed │
│ State Store │
│ Strategy │
│ Risk Engine │
│ Execution │
│ Reconciliation │
└──────────────┬───────────────┘ │ Polymarket APIs
不需要因为系统是交易 Bot 就搞二十个微服务。
只在故障隔离、扩展或运维归属真正需要的时候才拆分进程。
对于 Rust 实现,Tokio 提供了天然的异步运行时基础。保持热路径事件驱动,让状态转换显式,用关联 ID 记录每个决策,连接市场事件 → 策略决策 → 订单 → 成交。
在部署真实资金之前,刻意模拟:
只在网络完美时才工作的 Bot 不是生产就绪的。
最强的 Polymarket Bot 架构不是组件最多的那个。
而是每个重要状态转换都是显式的那个:
Market Event ↓
State Update ↓
Strategy Decision ↓
Risk Decision ↓
Execution ↓
Exchange Event ↓
Reconciliation
一旦这些边界清晰了,策略就变成了可替换的模块,而不是整个应用。
这就是好架构的真正优势:改变交易想法不应该需要重建周围的交易基础设施。
交易涉及执行、流动性、费率、模型和市场结算风险。本文中的例子描述的是工程架构,不代表预期的交易表现或盈利能力。