> ## Documentation Index
> Fetch the complete documentation index at: https://docs.oddspapi.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 核心概念 - ID、数据模型和最佳实践

> 了解OddsPapi数据模型：赛事ID、盘口ID、赔率标识符、实体关系、时间戳以及套利检测的实施最佳实践。

本页解释Odds API中使用的核心概念：ID如何构建、实体之间如何关联、时间戳如何使用，以及如何设计可靠的客户端存储。

***

## 语言前缀

所有端点都以语言代码为前缀：

* `/zh/...` (中文)
* `/en/...` (英文)
* `/de/...` (德文)

翻译字段（例如名称）在可用时遵循前缀语言。
标识符（`sportId`、`fixtureId`等）与语言无关。

***

## 体育、锦标赛、赛季

### sportId

* 整数。
* 嵌入到多个下游ID中的**前两位数字**。
* 通过`/sports`发现。

### tournamentId

* 整数。
* 属于恰好一项体育运动。
* 通过`/tournaments`发现。

### seasonId

* 整数。
* 属于恰好一个锦标赛。
* 通过`/seasons`发现。

> 完整的[运动覆盖](/zh/coverage)表列出了每个 `sportId` 及其是否以赛事、期货或两者形式提供。

***

## 盘口和选项

<Note>
  **摘要** —— 每个选项都遵循固定层级：`marketType → period → handicap → side`，全部编码进 `outcomeId` —— 因此同一个投注始终解析为相同的 `marketId` / `outcomeId`，通过坐标定位而非对博彩公司名称做字符串匹配。唯一不在 `outcomeId` 中的是球员，由 `playerId` 承载。同一盘口的所有选项共享其 `marketId`，即该盘口的首个 `outcomeId`。
</Note>

### 盘口如何分解（确定性层级）

每一个 `outcomeId` 都由单一、确定性的分解逻辑生成 —— OddsPapi 将每个盘口拆分为固定层级：

```
marketType（盘口类型）→ period（赛段）→ handicap（盘口线 / specifier）→ side（选项）   [+ 可选 player]
```

因此"全场总进球大于 2.5"在不同博彩公司和不同赛事中始终落在相同的 `marketId` / `outcomeId` 上。

| 层级 | 字段            | 含义                                                                                          |
| -- | ------------- | ------------------------------------------------------------------------------------------- |
| 1  | `marketType`  | 盘口种类（`1x2`、`totals`、`spreads`、`spreads-european`、`bothteamsscore`、`drawnobet`、`oddeven` 等）。 |
| 2  | `period`      | 盘口适用的赛段（`fulltime`、`p1`、`p2`）。                                                              |
| 3  | `handicap`    | 盘口线 / specifier（例如 `2.5`、`-0.25`）。**每个不同的盘口线都是独立的 `marketId`。** 无盘口线的盘口（如 `1x2`）为 `0.0`。    |
| 4  | `outcomeName` | 盘口的**选项方向**（`Over` / `Under`、`1` / `X` / `2`、`Yes` / `No` 等）。                               |

> **关键规则：** 由于每个盘口线都有**自己的** `marketId`，"大于 2.5" 和 "大于 3.5" 是*不同的*盘口 —— 而非同一盘口的两个选项。在单个 `marketId` 内，选项仅为该确切盘口线的**各个方向**（例如 `Over 2.5` + `Under 2.5`）。

<Accordion title="示例 —— 足球（sportId 10）">
  真实足球盘口映射的代表性切片。每个 `marketId` 等于其首个 `outcomeId`，且每个盘口线都是独立的盘口：

  | marketId | marketName           | marketType         | period   | handicap | outcomeId | outcomeName |
  | -------: | -------------------- | ------------------ | -------- | -------: | --------: | ----------- |
  |      101 | Full Time Result     | `1x2`              | fulltime |      0.0 |       101 | 1           |
  |      101 | Full Time Result     | `1x2`              | fulltime |      0.0 |       102 | X           |
  |      101 | Full Time Result     | `1x2`              | fulltime |      0.0 |       103 | 2           |
  |      104 | Both Teams To Score  | `bothteamsscore`   | fulltime |      0.0 |       104 | Yes         |
  |      104 | Both Teams To Score  | `bothteamsscore`   | fulltime |      0.0 |       105 | No          |
  |      106 | Over Under Full Time | `totals`           | fulltime |      0.5 |       106 | Over        |
  |      106 | Over Under Full Time | `totals`           | fulltime |      0.5 |       107 | Under       |
  |     1010 | Over Under Full Time | `totals`           | fulltime |      2.5 |      1010 | Over        |
  |     1010 | Over Under Full Time | `totals`           | fulltime |      2.5 |      1011 | Under       |
  |     1068 | Asian Handicap       | `spreads`          | fulltime |     -0.5 |      1068 | 1           |
  |     1068 | Asian Handicap       | `spreads`          | fulltime |     -0.5 |      1069 | 2           |
  |    10122 | European Handicap    | `spreads-european` | fulltime |      6.0 |     10122 | 1           |
  |    10122 | European Handicap    | `spreads-european` | fulltime |      6.0 |     10123 | X           |
  |    10122 | European Handicap    | `spreads-european` | fulltime |      6.0 |     10124 | 2           |
  |    10208 | First Half Result    | `1x2`              | p1       |      0.0 |     10208 | 1           |
  |    10208 | First Half Result    | `1x2`              | p1       |      0.0 |     10209 | X           |
  |    10208 | First Half Result    | `1x2`              | p1       |      0.0 |     10210 | 2           |
  |    10214 | Draw No Bet          | `drawnobet`        | fulltime |      0.0 |     10214 | 1           |
  |    10214 | Draw No Bet          | `drawnobet`        | fulltime |      0.0 |     10215 | 2           |
  |    10222 | Odd Even Full Time   | `oddeven`          | fulltime |      0.0 |     10222 | Odd         |
  |    10222 | Odd Even Full Time   | `oddeven`          | fulltime |      0.0 |     10223 | Even        |
  |    10224 | Over Under Team 1    | `teamtotals-team1` | fulltime |      0.5 |     10224 | Over        |
  |    10224 | Over Under Team 1    | `teamtotals-team1` | fulltime |      0.5 |     10225 | Under       |

  读取第一行：`marketType=1x2`、`period=fulltime`、`handicap=0.0`、`outcomeName=1` —— 即全场比赛结果盘口的主队方向。
</Accordion>

### `marketId`和`outcomeId`之间的关系

盘口和选项在设计上紧密耦合：

* **盘口**代表一个完整的投注市场（例如独赢、1x2、大小球）。
* **盘口包含多个选项**。
* **属于同一盘口的所有选项共享相同的`marketId`。**
* **盘口的第一个`outcomeId`始终等于`marketId`。**

这使得可以**无需额外元数据**即可确定哪些选项属于哪个盘口。

### ID结构

`marketId`和`outcomeId`都是整数，构造如下：

```
{sportId（2位数字）} + {递增数字}
```

示例：

* `11xxxx` → 篮球盘口或选项
* `14xxxx` → 美式橄榄球盘口或选项

这种设计提供：

* 每项运动无限的盘口和选项
* 按运动快速分组
* 立即可见的盘口关系

### 为什么`marketId`很重要（套利和建模）

单个`marketId`下的所有选项共同代表一个**完整的概率空间**。

这使得`marketId`特别适用于：

* 套利检测
* 超出率/利润率计算
* 概率归一化
* 盘口完整性检查

**建议：**
如果您执行套利或定价逻辑，请始终按`marketId`对赔率分组。

***

## 参与者和球员

### participantId

* 整数。
* 代表赛事中的球队或参赛者。
* 通过`/participants`发现。

### playerId

* 整数。
* 用于球员投注盘口。
* `playerId = 0`通常代表非球员盘口。
* 通过`/players`发现。

***

## 赛事ID

### fixtureId结构

`fixtureId`是一个编码多条信息的**字符串**：

```
{providerSlug}{sportId}{tournamentId}{providerFixtureId}
```

概念示例：

```
id1100013262926199
```

其中：

* `id` → 提供商标识符/简短slug
* `11` → sportId（2位数字）
* `000132` → tournamentId（6位数字）
* `62926199` → 提供商的原生赛事ID

仅从 `fixtureId` 即可推断出运动、锦标赛和上游提供商 —— 便于日志记录和跨系统关联。

***

## 期货ID

### futureId结构

`futureId`遵循类似的原则：

```
{providerSlug}{sportId}{seasonId}{marketId}
```

这编码了提供商、运动、赛季和盘口 —— 全局唯一且自描述，与 `fixtureId` 一样。

***

## 赔率标识符

<Note>
  **摘要** —— 单个价格由 `{fixtureId}:{bookmaker}:{outcomeId}:{playerId}`（赛事）或 `{futureId}:{bookmaker}:{participantId}`（期货）唯一标识。请将这些 `oddsId` 字符串用作存储中的**主键**，以实现干净的去重、更新与对账。
</Note>

### 赛事赔率键

对于赛事，单个价格由以下唯一标识：

```
{fixtureId}:{bookmaker}:{outcomeId}:{playerId}
```

示例：

```
id1400003160574217:bet365:141:0
```

此组合唯一定义**一个价格**。

去掉 `bookmaker`，剩下的就是**选项**本身 —— `{fixtureId}:{outcomeId}:{playerId}` —— 这是与博彩公司无关的评级 / 结算键，因为一个选项在任何博彩公司处的输赢都相同。无需 `marketId`；`outcomeId` 已编码了盘口。

### 期货赔率ID

对于期货，赔率由以下唯一标识：

```
{futureId}:{bookmaker}:{participantId}
```

***

## 时间戳（秒与毫秒）

此API有意使用**纪元秒和纪元毫秒**，具体取决于上下文。

### 纪元秒（UTC）

用于计划或粗粒度时间值：

* `startTime`
* `startTimeFrom`
* `startTimeTo`

### 纪元毫秒（UTC）

用于高频价格更新：

* `changedAt`
* 赛事的赔率`since`过滤器
* 期货赔率`createdAt`

### 建议

* 将时间戳存储为整数。
* 不要在文档记录为秒的地方假设毫秒（反之亦然）。
* 仅在需要时进行内部归一化。

***

## 运营提示

### 服务器位置很重要

要实现实时更新的最低延迟：

* **最快的交付区域**是中欧（推荐）和美国东部。
* 为获得最佳性能，请将后端部署在**我们使用的相同数据中心**，例如：
  * [Netcup](https://www.netcup.com/en/?ref=294017)
  * [Hetzner](https://www.hetzner.com/cloud)
* 如果您正在构建**对延迟敏感的预测市场或模型**，请考虑：
  * 通过Netcup部署在**奥地利(AT)**，或
  * 使用**AWS eu-west-1**（爱尔兰）

> 与我们流媒体基础设施位于同一位置的服务器可以更快地接收更新，跳数更少。

### 快照 + 实时模式

可靠的集成模式：

<Steps>
  <Step title="获取 HTTP 快照">
    通过 REST 拉取当前状态（赛事、赔率或期货）。
  </Step>

  <Step title="订阅 WebSocket">
    在快照之上流式接收实时更新。
  </Step>

  <Step title="按信号重新获取快照">
    如果 WebSocket 发出 `snapshot_required` 信号，请重新获取 HTTP 快照并恢复实时流。
  </Step>
</Steps>

### 高效回填

* 在可用时使用`since`参数
* 除非需要，否则避免完整的历史获取
* 按赔率标识符存储赔率以进行去重

### 速率限制

有关请求头、各端点限制以及 429 退避处理，请参阅[速率限制](/zh/api-reference/rate-limits)。

***

## 历史赔率与 CLV

实时 WebSocket 的 `odds` 和 `oddsFutures` 频道传输的是**最新状态** —— 它们针对低延迟交易进行了优化，在负载下会合并或丢弃中间更新，并非逐笔账本。

> 使用实时数据流进行**交易**，使用 REST 历史端点进行**衡量** —— CLV 模型、成交审计和回测。

当您需要某个结果的**完整价格变动**，或其**开盘价与收盘价**时，请使用 REST 历史端点。它们与实时 `odds` 频道共享相同的 `oddsId` 格式（`{fixtureId}:{bookmaker}:{outcomeId}:{playerId}`），因此您可以将实时成交直接关联到其历史记录和收盘价记录。

| 用途               | 赛事（Fixtures）                    | 期货（Futures）                    |
| ---------------- | ------------------------------- | ------------------------------ |
| 完整价格时间线          | `GET /fixtures/odds/historical` | `GET /futures/odds/historical` |
| 开盘价与收盘价（OLV/CLV） | `GET /fixtures/odds/clv`        | `GET /futures/odds/clv`        |

**交易用例：**

* **CLV 建模** —— 将您的成交价与收盘价进行比较，以衡量优势。
* **成交审计** —— 将成交价格与已记录的时间线进行核对，以检测滑点。
* **回测** —— 重放完整的历史时间线，以根据真实的价格变动测试策略。

完整的请求参数、响应结构以及每个端点的交互式演练，请参阅 **API → 参考** 部分。

***

## 术语表

API 中常用核心术语的快速定义。

**标识符与数据模型**

* **sportId** —— 运动数字标识符；下游 `marketId`、`outcomeId` 和 `fixtureId` 的前两位数字。
* **fixtureId** —— 单场比赛/事件的字符串键，编码提供商、运动、赛事和提供商赛事 ID。
* **futureId** —— 期货 / 冠军盘口的字符串键，编码提供商、运动、赛季和盘口。
* **marketType** —— 盘口种类（`1x2`、`totals`、`spreads`、`bothteamsscore` 等）。
* **period** —— 盘口适用的赛段（`fulltime`、`p1`、`p2`）。
* **handicap** —— 盘口线 / specifier；每个不同的盘口线都是独立的 `marketId`。
* **marketId** —— 将一个盘口所有选项分组的整数；等于盘口的首个 `outcomeId`。
* **outcomeId** —— 标识一个完全分解后的选择的整数：`marketType` + `period` + `handicap`（盘口线）+ 方向。它编码了除球员以外的一切 —— 球员由 `playerId` 补充。
* **playerId** —— 在球员盘口中将选项关联到某个球员；`playerId = 0` 为非球员盘口。
* **participantId** —— 标识赛事中球队或参赛者的整数。
* **oddsId** —— 价格复合键。赛事：`{fixtureId}:{bookmaker}:{outcomeId}:{playerId}`。期货：`{futureId}:{bookmaker}:{participantId}`。

**价格与交易**

* **OLV** —— 开盘线价值：选项的首个记录价格。
* **CLV** —— 收盘线价值：选项结算前的最后价格；评估执行质量的基准。
* **staleOdds** —— 表示某博彩公司连接降级、其赔率可能并非最新的标志。
* **settlement（结算）** —— 赛后端点，返回逐结果的结果（赢 / 输）、最终比分和盈亏幅度。

**流式传输与交付**

* **serverEpoch + entryId** —— 每频道游标；`serverEpoch` 变化表示客户端必须重新获取快照。
* **resume / replay（恢复 / 重放）** —— 在 `resumeWindowMs`（默认 `60000`）内重连并重放丢失的消息。
* **snapshot\_required** —— 表示游标已超出恢复窗口、需重新获取 REST 快照的信号。
