Skip to main content
跨预测市场做市意味着同时维护多个场馆的一致视图 —— 价格、深度、将订单送回场馆所需的标识符,以及对「这条报价还作数吗」的诚实回答。OddsPapi 将这一切归一化为单一数据流,因此新增一个场馆只是改一个过滤条件,而不是一个集成项目。 本指南讲的是建立在该数据流之上的交易决策:如何导出可交易视图、如何按真实深度定量、如何判断某条价格已不可执行,以及连接中断时的正确行为。负载结构与线上示例请见 odds 频道页 —— 本页假定您已读过它。

每条价格包含什么

每个 outcome 都在同一个对象中携带做市所需的要素 —— 无需二次查询: 四段时间戳链(bookmakerChangedAtchangedAt → 信封 ts → 您的接收时间)让您可以自行测量各场馆的延迟,而不必只凭信任。各博彩商已公布的数值参见延迟与新鲜度

导出可交易视图

meta.backmeta.lay 在所有交易所与预测市场中形态一致:{ price, size } 数组,最优价在前,且各场馆语义相同。back 的首条即盘口顶部,与该 outcome 的 price 一致。确切结构参见高级元数据 正因为形态不随场馆变化,跨场馆比较可以直接进行 —— 无需按场馆分支解析,也无需您自建一层归一化逻辑并随各场馆接口变动不断维护。 跨场馆的最优买价与最优卖价。 将所有报价按选项分组,然后在您有权限的场馆中取最高 back 价与最低 lay 价。即便您只会吃单边,两边也都重要:最窄的价差告诉您共识实际落在哪里,而报在其外的场馆要么携带了信息,要么携带了一条陈旧价格 —— 究竟是哪一种,由时间戳而非价格来判断。 交叉与锁定盘口是信号,而不是机会。 若某场馆的最优 back 高于另一场馆的最优 lay,在把它当作套利之前,先核对双方的 changedAt。真实的交叉会在毫秒级内消失;持续存在的交叉几乎总是意味着其中一方已停止更新。先通过下文的状态门控,再考虑定量。 深度是按场馆计的,不能汇总。 把各场馆的阶梯加总,得到的是一个您无法据以交易的数字 —— 每个场馆的队列各自独立,您无法用一个场馆的订单去吃另一个场馆的挂单。请按场馆建模可执行规模,再在选项层面汇总,而不是在盘口层面。 成交量以场馆自身的币种计价。 size 以场馆的基础货币表示。加密货币场馆请先通过currencies 频道换算,再去比较深度或设定仓位上限,否则一个场馆上的大数字与另一个场馆上的小数字可能代表同样的敞口。

定量:limit 与深度各司其职

同一条赔率行上传来两个彼此独立的规模信号,混淆二者是过度定量的常见来源:
  • limit 是场馆在当前价格上可接受的最大投注额。它在各家博彩商间已归一化,且几乎每家 sharp 博彩商都携带 —— 包括完全不提供阶梯的传统博彩商。参见投注限额
  • 阶梯是盘口顶部之后的挂单流动性。它告诉您在推动价格之前可以清掉多少量,以及平均成本是多少。
在交易所与预测市场上您两者兼有:limit 约束单笔订单,阶梯约束成交结果。在传统博彩商上您通常只有 limit,而那就是全部答案。请沿阶梯走到目标规模,并以成交量加权均价而非盘口顶部价来给这笔成交定价 —— 只要超出第一档,触及价就不是您实际拿到的价。 限额变化同样是价格信息。 它们以普通赔率更新的形式、在同一个 oddsId 上到达。一家博彩商在维持价格的同时下调限额,传达的信息是价格本身没有的,而且它往往是两者中更早出现的那个信号。若想看到这一点,请把限额历史与价格历史一并记录。 这些都不是交易所独有的 —— 同一区分也支配着在传统 sharp 博彩商上的定量,只不过那里 limit 是您能拿到的唯一规模信号。参见 sharp 线路交易与 CLV

判断一条报价是否可交易

价格新鲜度与价格价值是两个独立的问题,而对自动化交易而言,判断错的时候真正付出代价的是前者。四道彼此独立的关卡,任何一道都可以否决: staleOdds 是最容易被漏掉的一个,因为您收到的最后一条价格看上去毫无异常。您手上的价格可能已在场馆侧变动,而我们无从告知。在它变回 false 之前,请将该场馆的所有数据视为未经验证,并撤回报价而不是重新定价。详见自动化交易关键字段 再加一道属于您自己的关卡:对照公布上界做时效检查。 每家博彩商都在 GET /bookmakers 上公布 maxDelayLiveInSec / maxDelayPregameInSec。若 now − changedAt 明显超出该博彩商的上界,即便没有任何标志位触发,这条报价也已超出约定 —— 在最初几秒里,一个安静的场馆和一个故障的场馆看起来是一样的。参见状态信号 把这五项接入同一个「此刻我能否据此报价」的判定函数,并在每次更新时求值,而不是靠定时器。一个判定,一处可改。 对于赛事盘口,bookmakers 频道上的 participantsRotated 是性质不同的第六道检查:为 true 时,该场馆的主客场分配与我们相反,这会颠倒基于方向的 outcome 含义,并翻转让分的符号。它影响的是正确性而非新鲜度;而如果您按位置而不是按 ID 映射 outcome,它的影响是无声的。

延迟预算

您的反应时间由四段构成,其中您能掌控的恰好只有一段:
  1. 场馆 → OddsPapi 观测(changedAt − bookmakerChangedAt)—— 按博彩商公布,在场馆提供可用时间戳时为实测值。
  2. 合并 —— 固定约 10 ms 的批处理窗口;最新值一定会送达,因此它约束的是陈旧度上界,而不是丢弃数据。
  3. 网关 → 您(您的接收时间 − ts)—— 传输跳,也是您唯一能缩短的一段。参见服务器位置
  4. 您自身的决策,以及订单回到场馆的往返时间。
请用自己的时钟持续测量第 3 段;它是最先劣化的一段,也是您的基础设施决策真正能改变的一段。第 1 段与第 4 段则应对照场馆的更新节奏来做预算:若某场馆重新定价的速度快于您闭环的速度,您就不是在该场馆的盘口顶部上竞争,此时应当报在其后或减小规模,而不是去追。 按场馆计的延迟也正是「对所有场馆采用统一报价策略」为什么是错的原因。同一标的上,一个 0.05 秒的场馆与一个约 1 秒的场馆理应对应不同的价差,而用于参数化这一点的数值是公布出来的,不需要您去推测。同样的推理应用在吃线上,参见 sharp 线路交易与 CLV

熬过数据空档

做市方最糟糕的状态,是以为某个盘口是最新的、并据此报价,而事实并非如此。因此断连时的正确行为是有顺序的:先撤报价,再做对账。
  • 服务端发起的重连。 在发布或重新均衡之前,我们会推送一个 reconnect 控制帧,并在一小段宽限期内继续推送数据。请对这个帧作出反应,而不是等待随后的套接字错误 —— 这样得到的是干净的交接,而不是空档。
  • 在重放窗口内。 携带每频道游标(serverEpoch + entryId)重连,遗漏的更新会被无缝重放。
  • 超出窗口。 网关发出 snapshot_required;用一次 REST 快照恢复。这是最坏情况有界的既定路径,而不是错误。
  • serverEpoch 发生变化时,旧游标已失去意义 —— 请重新快照,而不是恢复。
完整机制见恢复和重放。真正重要的纪律是:您账本中按场馆记录的「最后已知良好」时间戳,应当独立于套接字是否连着来门控报价 —— 因为套接字活着、背后场馆却已冻结,才是真正会造成损失的那种故障。

将订单路由回场馆

bookmakerMarketIdbookmakerOutcomeId 随每条价格在实时数据流上传输。当策略选定某个选项时,您手中已经握有场馆自身的标识符 —— 无需维护映射表,也无需在下单前再调用一次接口。这一点恰恰在最困难的时刻最有价值:您最想动手的那一刻,也是您最不希望链路中还夹着一次查询的时刻。 在赛事层面,GET /fixtures/mapping 支持双向解析 —— 传入 fixtureIds 获取场馆 ID,或传入 bookmakerFixtureIds 从场馆 ID 反查 OddsPapi 赛事。bookmakers 频道同样按场馆、按赛事携带 bookmakerFixtureIdfixturePath,因此人工交易台也能直接打开场馆自己的页面查看同一盘口。

跨场馆定价

定价之前,先按 marketId 分组。同一 marketId 下的所有 outcome 构成完整的概率空间,计算水位、归一化与完整性检查都需要它 —— 而每个线值都是独立的 marketId,因此一个盘口绝不会悄悄混入不同线值。 在此之上是常规做法:先在盘口内对各场馆的报价做归一化,再跨场馆合并。有两种权重值得显式保留,因为数据本身支持它们:
  • 按规模加权 —— 展示出真实深度、limit 也较高的场馆,比在同一价格上只挂象征性数量的场馆构成更强的观测。
  • 按延迟加权 —— 实测上界在 100 毫秒以内的场馆与上界为数秒的场馆,其当前程度并不相等,而公布的逐家数值让您可以据此加权,而不是靠猜。
事后工作 —— 成交审计、回测、收盘线 —— 请使用 REST 历史赔率与 CLV 接口,它们与实时数据流共用同一 oddsId 键,因此实时决策与其审计轨迹无需任何转换即可关联。

非体育市场即期货

政治、选举、金融、加密货币与天气被建模为 sportId 69+ 的运动,且仅以期货形式存在 —— 每个冠军盘口对应一个期货:
使用 futuresoddsFutures 频道,二者遵循相同的交付语义并携带相同的订单簿 meta。上文关于门控、定量与深度的内容全部原样适用。模型参见期货如何运作,sportId 区间参见覆盖范围 某个期货问的是哪个问题futures 负载上的 market.marketId 给出 —— 即 futureId 末尾同样携带的 5 位 futureMarketId,按运动分配(69002 = 政治 Prediction)。 某条价格对应哪个选择项由键的最后两段给出,而由哪一段承载则取决于盘口的形态。以参与者为键的盘口(winnertopscorerrelegationmvp)不拥有 outcome 行:选择项就是您要投注的实体,futureOutcomeId 取哨兵值 0。可分解盘口(是 / 否、大 / 小)则按线值、按方向携带真实的 futureOutcomeId —— 单个期货盘口会跨越多条线值,且与赛事盘口不同,让分位于 outcome 上而非盘口上。请从赔率行的显式字段读取二者,而不要拆分 oddsId;参见期货赔率ID

接入步骤

1

快照

通过 REST(/fixtures/odds/futures/odds)拉取当前状态以初始化您的订单簿。
2

订阅数据流

连接 wss://v5.oddspapi.io/ws,并以您交易的频道、运动和场馆登录。请在订阅 odds 的同时订阅 bookmakers —— 没有它,您有价格但没有陈旧度信号。
3

应用更新

oddsId 为键就地覆盖。每条消息是状态,而非一次跳动。
4

门控

在每次更新时、按场馆求值报价判定函数,再让价格进入下游任何环节。
5

恢复

reconnectsnapshot_required 当作既定路径来处理。参见恢复和重放
在登录时过滤 —— sportIdstournamentIdsbookmakers 削减消息量的效果远好于客户端过滤 —— 并在规模化时于 odds 上优先使用 receiveType: "zstd"压缩)。登录报文结构与可运行的客户端示例见 odds 频道页与快速开始

您可以依赖的保证

  • 只有一种订单簿结构,而不是每个场馆一种。 meta.back / meta.lay 在所有交易所与预测市场上都是语义一致的 { price, size } 数组,因此您只需写一个解析器,而不是一整套按场馆定制的适配器。未知的 meta 键属于增量扩展;back / lay 核心不会被改作他用。
  • 场馆原生 ID 就挂在价格本身上。 bookmakerMarketIdbookmakerOutcomeId 位于实时赔率行内,因此将订单回送至场馆无需二次查询,也不需要您自建反向映射表。
  • 成交量是一等字段。 limit 在各家博彩商间已归一化,且几乎每家 sharp 博彩商都携带,您实际能成交的额度与价格在同一条载荷中。
  • 失联会被上报,而不是被掩盖。 staleOdds 会告诉您我们何时无法再为某场馆的价格背书 —— 这正是「撤回报价」与「在不知情的情况下对着一个冻结的盘口交易」之间的差别。