Polymarket Bot 教程 · 第 32 章中的第 10 章

面向 bot 构建者的 Polymarket 订单类型详解:Fill-or-Kill (FOK)、Fill-and-Kill (FAK)、Good-til-Cancelled (GTC),以及限价 vs 市价的权衡。附生产级决策规则。

本章内容

订单类型混淆是新 bot 构建者最昂贵的一类 bug。该用 FOK 却发了 GTC,会导致错过入场;该用 GTC 却发了 FOK,会留下挂单,几个小时后在糟糕的价格成交。本章给出决策树,以及经过生产验证、在成千上万笔订单中都经受住考验的默认方案。

  • 快速决策树
  • FOK:必须成交否则跳过时使用
  • FAK:可接受部分成交时使用
  • GTC:希望挂在订单簿上等待时使用
  • 限价 vs 市价,以及 spread 税
  • 我们的生产默认值(FOK 买入,GTC 卖出]
  • 代码:下单各类订单类型

快速决策树

三个问题决定每一笔下单。

  1. 你是否需要立刻保证成交,而且如果现在不能成交就完全不要?→ FOK
  2. 你是否想尽可能多地立刻成交,接受部分成交,但不要挂单?→ FAK
  3. 你是否希望按你的价格挂在订单簿上,等待别人来成交你?→ GTC

就这么简单。大多数围绕订单类型的 bot bug,都是因为在你想要 #3 时选了 #1(“买入”变成了“因为 spread 太宽而没有仓位”),或者在你想要 #1 时选了 #3(“买入”变成了一个挂单,几个小时后在错误时点才成交)。

FOK:必须成交,否则跳过

Fill-or-Kill 会立即以请求的价格或更优价格撮合整笔订单。如果无法立刻把全部数量成交,这笔订单就会被拒绝,什么都不会发生。不会挂单,也不会部分成交。

FOK 适用于:新闻套利入场(你只想在新闻价格入场,不想在 30 秒后的市场价入场);在特定目标价位的止盈退出,部分成交会让记账变得混乱;任何策略假设原子执行的场景。

权衡:FOK 的拒单率比其他订单类型更高,尤其在流动性差的订单簿上。务必准备 fallback 路径-重新评估策略条件并在仍然有效时重试,或者直接跳过。

FAK:可接受部分成交时

Fill-and-Kill(也称 “immediate or cancel”)会尽可能立刻撮合,然后取消未成交的剩余部分。你可能得到全额成交、部分成交,或者零成交。

FAK 适用于:带特定价格上限的 market-buy(以不超过中间价上方 N cent 的价格抬升 ask);紧急减仓时扫单卖出;以及任何“有一点仓位总比没有好”的策略。

在操作上比 FOK 更棘手,因为 bot 必须先知道自己拿到了 100% 还是 30%,再决定下一步。成交响应里包含 filled_size 字段-一定要读取它。

GTC:希望挂在订单簿上等待时

Good-til-Cancelled 会按你的价格挂在订单簿上,直到成交或你主动取消。没有超时(v2 API 中其他订单类型包含带过期时间的 GTD)。

GTC 适用于:在入场价上方 +Nc 的止盈卖出;在入场价下方 -Nc 的止损卖出(有注意事项-见下文);双边报价的 market making;以及任何 bot 愿意等待更好价格的仓位。

硬性规则:GTC 要求 ≥ 5 shares。少于 5 shares 的订单会被 CLOB 拒绝,并返回 Size (X) lower than the minimum: 5。如果 bot 发出一个 4-share 的 GTC 卖单,会悄悄失败,退出单并未设置,仓位会一路持有到结算。发 GTC 前务必检查库存是否 ≥ 5;如果更小,则回退到 FAK 或持有到结算。

限价 vs 市价,以及 spread 税

严格来说,Polymarket 的每笔订单都是限价单-即使 bot 所谓的“market buy”也会指定一个价格上限。区别在于这个价格是最优 ask(实际上等同于市价单,会吃掉订单簿)还是低于最优 ask(会挂在订单簿上)。

这里就轮到 maker 和 taker 两个角色登场了。taker 发出的订单会立即与订单簿里已经挂着的订单成交;maker 则是挂出一个订单、停留在簿子里,等别人来跟它成交。taker 要付出 spread(以及 taker 手续费),maker 则收取 spread(并赚取返佣)。所谓 spread 税,就是 taker 为「跨价成交」付出的成本:当 bid 是 0.45、ask 是 0.47、mid 是 0.46 时,一次买 ask、卖 bid 的来回,每股要亏 2 美分。在一个胜率 60%、止盈 +3c / 止损 -4c 的策略里,这 2 美分的 spread 就是盈利和亏损之间的分界线。

Maker 模式(在 bid 或更低位置挂 GTC,等别人来吃)会收取 spread,而不是支付它。代价是不确定成交-你可能永远都不会被成交。对于高置信度交易,直接支付 spread;对于被动积累,去“工作”订单簿。

我们的生产默认值(FOK 买入,GTC 卖出]

我们大多数生产 bots 最终收敛到的模式:

  • 入场:在 ask 上方 0-2 cent 使用 FOK。如果 bot 决定买入,就应该现在买,或者跳过。通常不值得把入场单挂着-触发买入决策的情况变化,往往比订单挂单等待的时间更快。
  • 止盈退出:按目标价使用 GTC。在入场成交后立即挂出。我们让市场来找我们,不追着 bid 往下跑。前提是 ≥ 5 shares。
  • 止损:视情况而定。对于价格变化有界的慢速策略,GTC 可行。对于快速变动的市场,如果价格直接飞过止损价,GTC 止损不会成交;我们会采用 option-D 方式持有到结算(记忆提示:trader-gtc-sell.md)。

这种模式偏保守-成交更少,滑点更低。更激进的变体会使用 FAK 入场和 FAK 出场,接受部分成交。选一种并保持一致;按交易混用决策会带来混乱。

代码:下单各类订单类型

使用 py-clob-client (v0.34.6) 的 Python 下单参考。

from py_clob_client.client import ClobClient
from py_clob_client.clob_types import OrderArgs, OrderType
c = ClobClient(host="https://clob.polymarket.com", chain_id=137,
               key=PRIVATE_KEY, signature_type=2, funder=PROXY)
c.set_api_creds(creds)

# FOK buy: fill 10 shares at price 0.45 or skip
args = OrderArgs(token_id=TOKEN, price=0.45, size=10, side="BUY")
resp = c.create_and_post_order(args, OrderType.FOK)

# FAK buy: take as much as you can at 0.45 or below
resp = c.create_and_post_order(args, OrderType.FAK)

# GTC sell: rest a sell at 0.85 for 10 shares
sell_args = OrderArgs(token_id=TOKEN, price=0.85, size=10, side="SELL")
resp = c.create_and_post_order(sell_args, OrderType.GTC)

Node 中使用 @polymarket/clob-client-v2 的同样操作:将 OrderType.FOK 替换为 clob.OrderType.GTC 等;方法是 createAndPostOrder。对于多结果市场,必须在第二个参数中设置 negRisk 标志(第 11 章)-如果缺失,会路由到错误的 exchange contract。

常见问题

Polymarket 上的 FOK 是什么?
Fill-or-Kill。订单必须立即全部成交,否则就被取消-没有部分成交,也不会挂在订单簿上。我们在生产 trader 中默认把买入设为 FOK,因为它消除了“伪成交”歧义(订单要么完全成交,要么完全消失,绝不会半挂着)。
Polymarket 上的 FAK 是什么?
Fill-and-Kill(也叫 IOC,Immediate-or-Cancel)。订单会立即吃掉当前可用的流动性,然后取消未成交的剩余部分。适用于你接受部分成交,但绝不想挂单的情况。在碎片化订单簿中,比 FOK 更快。
Polymarket 上的 GTC 是什么?
Good-til-Cancelled。订单会挂在订单簿上,直到成交或你取消它。GTC 用于做 maker(提供流动性)、赚取返佣、避免 taker 费用。我们在生产配置中把卖出设为 GTC,这样在退出时可以吃到 spread。
我的 bot 应该用限价单还是市价单?
几乎总是限价单。市价单要支付 taker fee(0.75% 到 1.80%)以及 spread;限价单则能获得 maker rebate(taker fee 的 20-25%)。只有在新闻已经发生、而在你的限价单成交前价格即将穿过 spread 时,才有充分理由使用市价单。
Polymarket 是否原生支持止损单?
不支持。止损是客户端概念:你的 bot 监控价格,当触发条件满足时,发出市价或 FAK 卖单。交易所没有原生 stop primitive,所以你必须在 bot 里自行实现逻辑。
订单最小值是多少?
市价单:最低 1 USD 名义价值。限价单:最低 5 shares。某些流动性很薄的市场会拒绝很小的订单-SDK 会返回一个特定错误码,你可以据此检测并重新调整下单规模。