Weather MM 挂单策略
天气做市系统的挂单机制、智能定价算法、以及 UI 操作指南。
1. 智能挂单定价算法
核心原则:始终排在己方最优位置,不跨越价差,避免不必要的撤单重挂。
1.1 算法流程
输入: side (BUY/SELL), bids[], asks[], existingOrderPrice (己方已有挂单价格, nullable)
Step 1: 计算目标价格 p
BUY 方:
p = 本方 1 档(best_bid) + 1 tick
如果 p >= 对方 1 档(best_ask) → p = 本方 1 档 (不跨越价差)
SELL 方:
p = 本方 1 档(best_ask) - 1 tick
如果 p <= 对方 1 档(best_bid) → p = 本方 1 档 (不跨越价差)
Step 2: 检查己方是否已存在挂单 x
┌─ x = null (无挂单) → 直接挂 p
├─ x < p (挂价比目标差) → 撤 x,挂 p
├─ x = p (挂价=目标) → 不重复挂 p。触发 2 档检查
└─ x > p (挂价已优于目标) → 不做任何操作
Step 3: 2 档检查 (仅当 x = p 时触发)
y = 本方 2 档(second_best) + 1 tick
如果 y < x (2 档远落后于当前挂单位置)
→ 撤 x,p = y,重新挂 p
否则
→ 不做任何操作,保持当前位置
1.2 图解示例
BUY 挂单
盘口:
Ask: 0.026 ← 对方 1 档
─────────
Bid: 0.022 ← 本方 1 档
0.021 ← 本方 2 档
1) p = ceilTick(0.022 + 0.0001) = 0.023
2) p (0.023) < ask (0.026) → 不跨越,p = 0.023 ✓
3) 无己方挂单 → 直接挂 p = 0.023
己方已有挂单,价格差于目标
己方挂单 x = 0.021 (BUY)
目标 p = 0.023
x (0.021) < p (0.023) → 撤 x,挂 p = 0.023
己方已挂 p,2 档远落后
己方挂单 x = 0.023 (BUY) = p
本方 2 档 = 0.005
y = ceilTick(0.005 + 0.0001) = 0.006
y (0.006) < x (0.023) → 2 档远落后 → 撤 x,挂 p = 0.006
此时己方已经是唯一的最优买价,不需要支付过高溢价,撤回靠近 2 档。
SELL 挂单
盘口:
Ask: 0.026 ← 本方 1 档 (SELL 方)
0.030 ← 本方 2 档
─────────
Bid: 0.022 ← 对方 1 档
1) p = floorTick(0.026 - 0.0001) = 0.025
2) p (0.025) > bid (0.022) → 不跨越,p = 0.025 ✓
3) 无己方挂单 → 直接挂 p = 0.025
2. 价格步长(Tick Size)
Tick size 由 CLOB 官方 API 返回,每个 token 固定,不是按价格分段:
GET https://clob.polymarket.com/tick-size?token_id=<token_id>
→ {"minimum_tick_size": 0.01} 或 {"minimum_tick_size": 0.001}
实测 4/28 香港天气合约:
| 温度 | Tick |
|---|---|
| 26°C | 0.001 |
| 27°C | 0.01 |
| 28°C | 0.01 |
| 29°C | 0.01 |
| 30°C | 0.001 |
⚠️ 同一天不同温度合约的 tick 可能不同,必须按 token 逐次查询。前端和后端在下单前都会调用此 API 获取正确 tick。
3. 挂单方式
3.1 快捷按钮(Quote Card)
每个温度卡片上有 4 个快捷按钮,点击后自动应用智能定价算法:
| 按钮 | 含义 | 算法计算的 Token |
|---|---|---|
| 🟢 BUY YES | 做多该温度 | YES token, BUY 方向 |
| 🔴 BUY NO | 做空该温度(买 NO) | NO token, BUY 方向 |
| 🔴 SELL YES | 平多 / 做空 | YES token, SELL 方向 |
| 🔴 SELL NO | 平空 / 做多 | NO token, SELL 方向 |
🔄 更新 表示存在同方向旧单将被撤销重挂3.2 Quick 挂单(手动)
卡片展开后底部的手动按钮,同样使用智能定价算法:
手动 Buy YES 手动 Buy NO
3.3 API 直连
POST /api/mm/weather/place-order
{
"token_id": "0x...",
"price": 0.49,
"size": 5,
"side": "BUY",
"temp": 28,
"comp_type": "exact"
}
POST /api/mm/weather/quick-order
{
"token_id": "0x...",
"side": "BUY",
"size": 5,
"price": 0.067 // 可选:前端已计算的价格
}
后端 quick-order 同样实现了完整的智能定价算法。
4. 安全机制
4.1 冷却时间(Cooldown)
同一 token + 同一方向的下单有 30 秒冷却:
⏳ BUY 冷却 Xs429 拒绝4.2 自成交防护(Self-Trade Prevention)
单向挂单检查
下单前检查同一 token 是否存在反向挂单:
双边挂单检查(Quote Engine)
系统生成双边报价时,额外检查 BUY/SELL 是否交叉:
如果 ask ≤ bid(卖价 ≤ 买价):
→ 自成交危险!
→ 以中价为准,bid/ask 各退 1 tick
→ 如果退 1 tick 后仍然交叉:
buy_biased 方向 → 放弃 SELL,只保留 BUY
sell_biased 方向 → 放弃 BUY,只保留 SELL
symmetric → 放弃 SELL
原则:双向报价时 bid < ask 且至少相差 1 tick,绝不自己吃自己。
4.3 重复单处理
同一 token + 同一方向已存在挂单时:
1. 自动撤销旧挂单
2. 等待撤单确认
3. 再挂新单
智能定价算法会自动判断是否需要撤旧重挂(仅当 x < p 或 2 档落后时)。
4.4 最大订单额
4.5 最低数量
5. 盘口深度面板
点击卡片标题行展开深度面板:
YES 卖盘(Asks)
YES 买盘(Bids)
NO 侧买卖盘
6. 状态栏标签
卡片底部状态栏显示:
| 标签 | 说明 |
|---|---|
| 🟢 BUY 2.2¢ | 持有 YES 买单(可点击撤单 ×) |
| 🔴 SELL 2.6¢ | 持有 YES 卖单(可点击撤单 ×) |
| 📦 YES 5.0 | 当前持仓(YES 方向) |
| 📦 NO 3.0 | 当前持仓(NO 方向) |
7. 不可交易温度(Hard Bound)
系统根据当前实际温度设定 硬下限:
⛔ impossible示例:当前香港气温 26°C → 26°C 及以下的所有合约被判定为"已不可能",无需再交易。
8. NO Token 操作逻辑
天气市场为每个温度创建 YES 和 NO 两个 token,互为对手:
| 操作 | Token | 方向 | 经济含义 |
|---|---|---|---|
| BUY YES | YES | BUY | 做多该温度 |
| SELL YES | YES | SELL | 平多 / 做空该温度 |
| BUY NO | NO | BUY | 做空该温度(买 NO = 买不会) |
| SELL NO | NO | SELL | 平空 / 做多该温度(卖 NO = 卖不会 = 买会) |
NO 价格 = 1 - YES 价格(在有效市场中)。
9. 实现位置
| 组件 | 文件 | 说明 |
|---|---|---|
| 前端算法 | mm_weather.html → computeOrderPlacementPrice() | 所有下单按钮(快捷/手动)的定价入口 |
| 前端按钮 | mm_weather.html → confirmOrder(), quickOrder() | 拉取 CLOB 盘口后调用算法 |
| 后端算法 | app.py → api_mm_weather_quick_order() | quick-order 接口的 CLOB fallback 路径 |
| 后端自成交 | app.py → place-order | 单向挂单时检查反向挂单冲突 |
| 双向自成交 | weather_quotes.py → _calculate_quote() | Quote Engine 生成双边报价时防交叉 |
| 后端取整 | app.py → place-order, quick-order | 所有入口均做 token 级 tick 取整 |