🧪 模拟盘下单与平仓策略

香港天气合约 · Polymarket ·

1. 系统概述

模拟盘与实盘做市系统共享同一套预测模型和市场数据,但在模拟账户中交易,不涉及真实资金。 策略分为两个阶段:建仓和半仓平仓。

📡
发现市场
Gamma API → 今日香港合约
🔮
检测边缘
公平价 − 市场中间价
📥
建仓
买入偏见 / 卖出偏见
📤
半仓平仓
90%公平价 × 一半仓位
模拟盘使用 10 倍仓位(基础单 100 股 vs 实盘 10 股),最大单边持仓 500 股。
特性模拟盘实盘
基础单大小100 股10 股
单边最大持仓500 股风控管理
边缘阈值(最低)实盘的 ½完整
自动交易开关simu_auto_trading.jsonauto_trading.json
下单方式模拟撮合真实 CLOB 挂单
退出策略半仓平仓做市 + 手动

2. 配置与开关

2.1 模拟盘主开关

data/mm/simulation.json
enabled: true
启用 10× 仓位、减半边缘阈值
data/mm/simu_auto_trading.json
enabled: true
控制模拟盘是否产生成交
simu_auto_trading.enabled 独立于 auto_trading.enabled(实盘开关)。实盘暂停时模拟盘仍可运行。

2.2 仓位倍数

SIM_SIZE_MULTIPLIER = 10 # 实盘的 10 倍 BASE_ORDER_SIZE = 10.0 # 实盘:每边 10 股 MIN_ORDER_SIZE = 6.0 # 实盘最小(5 股扣费后约 4.7 股) # 模拟盘实际大小: 模拟基础单 = 10 × 10 = 100 股 模拟最小单 = 6 × 10 = 60 股 单合约单边最大 = 500

2.3 交易时间

交易开始 = 9:00 北京时间 交易结束 = 17:30 北京时间 扫描间隔 = 60 秒(Cron:每分 8-17 点)
模拟盘不受交易时间限制。sim_mode = true 时, can_trade 始终为真。

3. 扫描周期(每 60 秒)

每次扫描执行以下流水线:

🔮
1. 获取预测
结算预测 + 概率分布 + 硬下界
🔍
2. 拉取市场
Gamma API → 按日期+硬下界过滤
💹
3. 获取价格
Gamma outcomePrices + CLOB 盘口
📐
4. 计算边缘
公平价 − 市场中间价 → 报价
5. 模拟成交
报价与盘口交叉 → 建仓成交
⚖️
6. 半仓平仓
遍历所有持仓 → 尝试平半仓

3.1 市场过滤

合约需通过三层过滤才可交易:

过滤条件规则示例
日期 仅当日合约(MM-DD 格式匹配) 5月14日 → target_date = 05-14
硬下界 温度低于午夜最高温 → 不可能,除非有持仓 午夜=29.2°C → T21-T28 被拒绝
活跃 市场必须在 Polymarket 上活跃且未关闭
例外:已被硬下界排除但有持仓的合约,保留以监控和退出,避免留下孤儿仓位。

4. 建仓逻辑

4.1 报价模式(Quote Mode)

每次扫描为每个活跃市场生成一份报价,报价模式决定操作方向:

模式触发条件操作买价卖价
buy_biased edge > 0,无持仓 买入 YES 买一 + 1tick 卖一(被动)
sell_biased edge < 0,无持仓 买入 NO(通过卖出 YES) 买一(被动) 卖一 − 1tick
buy_biased_holding edge > 0,持有 YES 加仓 + 赚价差 买一 + 1tick 卖一 − 1tick ≥ 成本+1¢
sell_biased_holding edge < 0,持有 NO 加仓 + 赚价差 买一 + 1tick ≤ 成本−1¢ 卖一 − 1tick
closing_long edge 消失,持有 YES 市场价卖出 YES 被动(远低于市价) 买一(激进)
closing_short edge 消失,持有 NO 市场价买入平空 卖一(激进) 被动(远高于市价)
none |edge| 低于阈值,无持仓 跳过 0 0

4.2 边缘阈值(Edge Threshold)

合约类型实盘最低实盘强信号模拟最低(½)模拟强信号(½)
exact(主导温度)0.020.080.010.04
exact(非主导温度)0.060.150.030.075
or_higher / or_lower0.030.100.0150.05
硬边界(结算确认)0.010.030.010.03

4.3 模拟撮合逻辑

# 买入 YES:我方买价 ≥ 市场卖一即成交 if side == 'BUY' and our_bid >= market_ask: fill_price = market_ask # 以卖一价成交 # 卖出 YES:我方卖价 ≤ 市场买一即成交 if side == 'SELL' and our_ask <= market_bid: fill_price = market_bid # 以买一价成交 # 买入 NO(sell_biased 转换为 NO 侧): # 由 YES 卖价推导:no_bid = 1.0 − ask_price if no_bid >= no_market_ask: fill_price = no_market_ask
高价合约调整:当 YES 超过 70¢ 时, no_bid = 1 − ask_price 换算出的 NO 买价会偏低。 系统自动将 no_bid 提升到实际 NO 盘口的卖一价,以确保成交。

4.4 仓位冲突处理

建仓前检查是否存在反向持仓:

场景处理方式
edge > 0(买入)+ 持有 NO(空头)先平空再买入
edge < 0(卖出)+ 持有 YES(多头)切换为 closing_long,优先平仓

5. 平仓逻辑

持仓可通过两种机制平仓:

5.1 信号驱动退出

当建仓时边缘较强,但后续边缘衰减到阈值以下,报价模式切换为 closing_longclosing_short, 以激进市价单退出。

模式退出价数量
closing_long 买一价 卖出 min(报价卖量, 持仓 YES 量)
closing_short 卖一价 买入 min(报价买量, 持仓 NO 量)

5.2 半仓平仓

独立于边缘信号,每次扫描遍历所有持仓尝试平半仓。 详见 §6

6. 半仓平仓策略

参考来源:实盘交易页面 mm_weather.html 中的 ⚖️ 公平价平仓半仓折价 按钮逻辑。

6.1 规则总表

规则YES(多头)NO(空头)
公平价 _fair_from_dist(temp, comp_type, prob_dist) 1.0 − fair_price
基础平仓价 max(fair, market_bid) max(no_fair, no_market_bid)
目标价(90%) 基础平仓价 × 0.9 基础平仓价 × 0.9
成交条件 market_bid ≥ 目标价 no_market_bid ≥ 目标价
平仓数量 max(5, floor(持仓 / 2)) max(5, floor(持仓 / 2))
最低触发持仓 ≥ 5 股 ≥ 5 股
成交价 买一价(卖在买盘) NO 买一价(卖在买盘)

6.2 从概率分布计算公平价

def _fair_from_dist(temp, comp_type, dist): if comp_type == 'exact': return dist[temp] # 例如 P(30°C) = 23.6% elif comp_type == 'or_higher': return Σ P(t) for t ≥ temp # 例如 P(≥31°C) = 4.4% elif comp_type == 'or_lower': return Σ P(t) for t ≤ temp # 例如 P(≤20°C)

6.3 实例演算

# 持仓:YES 500股 @ 30°C exact,成本=$26.81(均价 5.36¢) # 概率分布:P(30°C) = 23.62% 公平价 = 0.2362 # 23.62¢ 基础平仓价 = max(公平价, 买一价) # max(23.62¢, 9¢) = 23.62¢ 目标价 = 基础平仓价 × 0.9 # 23.62 × 0.9 = 21.26¢ # 成交判断:买一价(9¢) ≥ 目标价(21.26¢)? # → 9¢ < 21.26¢ → 不成交(市场价太低,继续持有) # 若市场涨到 22¢ 买一: # → 22¢ ≥ 21.26¢ → 成交!SELL 250 YES @ 22¢ # 盈亏 = (22¢ − 5.36¢) × 250 = $41.60
为什么折价 10%?在公平价基础上让利 10% 能显著提高成交概率, 在拿到好价格和实际成交之间取得平衡。参考实盘 半仓折价 按钮的设计。

7. 仓位大小与上限

7.1 仓位计算

# 仓位大小随边缘强度与合约类型动态调整 bid_size, ask_size = _calculate_sizes( edge, fair_price, comp_type, net_exposure, bid_price, ask_price, mode, simulation=True ) # 基础大小 × 10(模拟倍率),再按边缘和合约类型缩放

7.2 仓位上限

上限执行方式
单边最大500 股成交前截断
净持仓|YES − NO| ≤ 500缩减成交量至不超过上限
最低金额$1.00/单跳过低于最低金额的挂单
上限仅限制增加仓位的操作。平仓(减少 |净持仓|)永远不受上限约束——始终可以退出。

8. 盈亏计算

8.1 单笔盈亏

# 买入(开多或平空): if 已有_NO > 0: # 平空 平仓量 = min(买入量, 已有_NO) 盈亏 = (NO均价 − 成交价) × 平仓量 else: # 开多 盈亏 = 0.0 # 卖出(平多): if 已有_YES > 0: # 平多 平仓量 = min(卖出量, 已有_YES) 均价 = 总成本 / YES数量 盈亏 = (成交价 − 均价) × 平仓量 else: # 开空 盈亏 = 0.0

8.2 仓位追踪字段

字段含义
yes_tokens持有的 YES 数量(多头)
no_tokens持有的 NO 数量(空头)
total_cost累计成本(买入花费 − 卖出收入)
realized_pnl该仓位所有平仓盈亏之和
total_pnl全局所有仓位已实现盈亏总和

8.3 盈亏追踪示例

# T=1: BUY YES 180 @ 8.2¢ → 成本=$14.76, 盈亏=$0.00 # YES=180, 成本=$14.76, 均价=8.2¢ # T=2: BUY YES 180 @ 7.7¢ → 成本+=$13.86, 盈亏=$0.00 # YES=360, 成本=$28.62, 均价=7.95¢ # T=3: SELL YES 90 @ 12.0¢ → 平掉 360 股中的 90 股 # 盈亏 = (12.0¢ − 7.95¢) × 90 = $3.65 # YES=270, 成本=$21.47, 已实现盈亏=$3.65 # T=4: SELL YES 120 @ 9.0¢(半仓平仓) # 盈亏 = (9.0¢ − 7.95¢) × 120 = $1.26 # YES=150, 成本=$11.93, 已实现盈亏=$4.91

9. 边缘与公平价计算

9.1 公平价

def get_fair_price(temp, comp_type): dist = get_prob_distribution() if comp_type == 'exact': return dist[temp] # 原始概率 elif comp_type == 'or_higher': return Σ dist[t] for t ≥ temp # ≥ temp 的累计概率 elif comp_type == 'or_lower': return Σ dist[t] for t ≤ temp # ≤ temp 的累计概率

9.2 边缘

边缘 = 公平价 − 市场中间价 # 正值 = 被低估 → 买入 # 负值 = 被高估 → 卖出

9.3 今日概率分布

温度23°24°25°26°27°28°29°30°31°32°+
概率 0%0%0%0%0%0% 72% 23.6% 1.3%3.1%
硬下界:午夜最高温 = 29.2°C。所有低于 29°C 的合约公平价 = 0, 直接拒绝(除非已有持仓需要监控退出)。

10. 数据文件

文件路径用途
data/mm/simulation.json模拟模式开关(启用 10× 仓位)
data/mm/simu_auto_trading.json模拟盘成交开关
data/mm/auto_trading.json实盘交易开关
data/mm/weather_paper_trader_state.json持久化持仓 + 交易记录 + 盈亏
data/mm/weather_state.json当前扫描状态(报价、元数据)
data/mm/weather_trades.jsonl追加式交易日志
data/mm/weather_quotes.jsonl追加式报价日志
data/mm/pending_orders.json手动审批队列(仅实盘)
data/hko/hko_prediction.json预测模型输出
data/hko/current.jsonHKO 实时观测