内容
彩足球赔率 API
**简体 · [English]()
[](https://githubserf-Seed/SportteryAPI/actions/workflows/ci.yml) [](LICENSE)   
[](https://deploy.workers.cloudflare.com/?url=https://github.com/Johnserf-Seed/SportteryAPI)
一个 **Cloudflare Worker REST API + 本地 MCP 服务器**,实现了干净的、可调用的竞彩足球赔率服务。它提供**最新的赔率**,并**完全基于赔率推导** — 隐含概率、无水概率、返还率、公平赔率、凯利指数/价值对比,以及完整的单关/过关奖金计算。
```
┌── 最新赔率 ──────────────┐ ┌── 纯数学计算(无上游依赖) ──────────────┐
│ GET /api/matches │ │ POST /api/derive 赔率推导 │
│ GET /api/:id │ │ POST /api/value 凯利指数/价值└───────────────────────────┘ │ POST /api/parlay 单关/过关奖金 │
/api/parlay/types · GET /api/meta │
└──────────────────────────────────────────┘
```
- **以代理为中心。** JSON 输入/输出,开放 CORS,自我描述的 `/api/meta`,可选的 **API 密钥认证**,以及一个本地的 **MCP 服务器**,提供相同的功能。
- **纯粹、经过测试的数学计算。** 所有的推导/过关逻辑都生活在无依赖的模块中,包含 25 个单元测试 — 无需构建步骤(`npm test`)。
- **简单的数据路径。** 每个colo的 上游(可选地通过 `UPSTREAM_PROXY` 使用上游可达的中转服务器)。本地 MCP 服务器直接获取竞彩数据。
> ⚠️ **地理限制。** 上游服务器受到地理限制,拒绝来自数据中心 IP(包括 Cloudflare)的请求,返回 HTTP **567** 状态码。要从部署的 Worker 提供最新的赔率,请将 `UPSTREAM_PROXY` 设置为一个可以访问上游服务器的中转代理。当运行时可以直接访问上游服务器时(本地开发或 MCP 服务器),不需要代理。计算端点(`/`value`/`parlay`)不需要上游服务器,在任何地方都可以使用。
---
## 目录
- [快速开始](#快速开始)
- [项目布局](#项目布局)
- [架构和数据流](#架构和数据流)
- [认证](#认证)
- [REST 端点](#rest-端点)
- [数学计算(赔率推](#数学计算赔率推导)
- [赔率池和结果代码](#赔率池和结果代码)
- [过关规则](#过关规则)
- [MCP 服务器(本地标准输入)](#mcp-服务器本地标准输入)
- [部署](#部署)
- [配置](#配置)
- [缓存](#缓存)
- [未来:实时 WebSocket 流](#未来实时-websocket-流)
- [开发和测试](#开发和测试)
- [数据源](#数据源)
- [爬取和机器人策略](#爬机器人策略)
- [免责声明](#免责声明)
---
## 快速开始
需要 **Node ≥ 22.6**(用于 `--experimental-strip-types`;测试和脚本使用 TypeScript,无需构建步骤)。
```bash
npm install
npm test # 25 个单元测试(数学/解析/过关/标签),无需网络
npm run dev # wrangler dev → http://localhost:8787
curl http://localhost:8787/api/matches | jq '.data.matches[0]'
```
当 Worker 可以访问上游服务器时(例如本地开发),`/api/matches` 直接获取竞彩数据(`source: upstream`)。
---
## 项目布局
```
SportteryAPI/
├── src/ # Cloudflare Worker
│ ├── index.ts # 路由器 / HTTP 入口,所有 REST 端点
│ ├── auth.ts # API 密钥认证
│ ├── derive.ts ✅纯数学 # 赔率推导含概率/无水概率,返还率,公平赔率,凯利指数
│ ├── parlay.ts ✅纯数学 # 单关/过关 M 串 N 表 + 奖金计算器
│ ├── labels.ts ✅纯数学 # 解码 had/hhad/crs/ttg/hafu 结果代码
│ ├── parse.ts ✅纯数学 # 原始上游 JSON → 规范化 + 推导的 Match[]
│ ├── upstream.ts # Cache API → 上游获取(可选 UPSTREAM_PROXY)
│ ├── stream.ts # OddsStream Durable Object(未来的 WebSocket)
│ ├── env.ts / http.ts / types.ts
├── mcp/ # 本地标准输入 MCP 服务器
│ ├── server.ts # MCP 工具(get_matches, derive_odds, calc_parlay, …)
│ └── sporttery.ts # 节点端竞彩客户端(缓存 + 可选代理)
├── scripts/
│ ├── smoke.ts # REST 烟雾测试 (BASE=… node …)
│ ├── mcp-smoke.ts # MCP 标准输入烟雾测试 (npm run mcp:smoke)
│ └── refresh-sample.ts # 刷新 sample.json (npm run refresh-sample)
├── test/ # 25 个节点测试单元测试(无需网络)
├── sample.json # 捕获的上游数据(离线测试)
├── .env.example # 环境变量模板(MCP 代理 / 烟雾测试密钥)
├── wrangler.jsonc # Worker 配置(已提交;无密钥)
├── .mcp.json # Claude Code 项目 MCP 注册
├── .github/workflows/ # CI(类型检查+测试)+ 部署(CD 到 Cloudflare)
└── README.md / README.zh-CN.md
```
`✅纯数学` 模块**没有 Cloudflare 导入**,可以直接使用 Node 的类型剥离功能进行单元测试。它们被 Worker 和 MCP 服务器共享。
---
## 架构和数据流
```
代理 / 浏览器 ──HTTP(+ API 密钥)──▶ Worker(index.ts)
├─ /api/matches ─▶ 缓存 API ─▶ 上游 ─┐
├─ /api/derive|value|parlay ─▶ 纯数学计算 │
└─ 认证门(auth.ts) │
上游(可选 UPSTREAM_PROXY)▼
webapi.sporttery.cn
本地 MCP(mcp/server.ts) ─▶ 直接获取竞彩数据 ─▶ 纯数学计算
```
Worker 按需获取赔率(由每个colo的缓存去重)。当其运行时可以直接访问上游服务器时,它直接获取;从地理受限的全球边缘,需要 `UPSTREAM_PROXY`。
---
## 认证
可选的 API 密钥认证门控 `/api/*` 和 `/stream`。`/` 和 `/health` 保持开放。
- **默认禁用** — 如果未配置 ``,所有端点都是开放的(适用于本地开发)。
- **启用** 通过设置 `API_KEY` 密钥(单个密钥或多个逗号 ```bash
echo "key1,key2" | npx wrangler secret put API_KEY # 生产环境
# 本地开发:在 `.dev.vars` 中设置 API_KEY=...
```
- **呈现密钥** 通过以下任意一种方式:
- `Authorization: Bearer <key>`
- `X-API-Key: <key>`
- `?key=<key>`(适用于 WebSocket 客户端)
- 缺失/无效密钥 → `401`。密钥以恒定时间比较。
```bash
curl -H "Authorization: Bearer key1" http://localhost:8787/api/matches
```
---
## REST 端点
基准 URL:`http://localhost:8787`(开发)或 `https://sporttery-api.<your-subdomain>.workers.dev`(部署)。所有响应均为 `{ "success": true, "data": … }` 或 `{ "success": false, "error": "…" }`。CORS 已开放(`*`)。`/api/*` 需要 API 密钥(如果已配置)。
### `GET /api/matches` — 最新赔率 + 推导指标
查询参数(均可选):`pools`(`had,hhad,cttg,hafu` 的 CSV),`date`260619`),`league`(子字符串),`matchId`。
```bash
://localhost:8787/api/matches?pools=had,hhad" -H "X-API-Key: $KEY"
```
```jsonc
{
"success": true,
"data": {
"updatedAt": "…", "matchCount": 12, "pools": ["had","hhad"],
"source": "upstream", 边缘缓存 | 上游
"matches": [{
"matchId": 2040239, "matchNum "周五029home": { "abbName": "美国" }, "away": { "abbName": "澳大利亚" },
"league": { "abb "世界杯" },
markets": {
"had": {
"poolNameZh": "胜平负",
"overround": 1.1294, "returnRate": 0.8854, "margin": 0.1146,
"outcomes": [
{ "key":"home","labelZh":"主胜","odds":1.44,"trend":"down",
"impliedProb":0.6944,"noVigProb":0.6149,"fairOdds":1.6264 },
{ "key":"draw","labelZh":"平","odds":3.90, "…":"…" },
{ "key":"away","labelZh":"主负","odds":5.60,"…":"…" }
]
}
}
}]
}
}
```
### `GET /api/match/:matchId` — 通过上游 `matchId` 获取单个比赛(所有赔率池)
### `POST /api/derive` — 从原始赔率推导指标(无状态)
请求体:`{ "odds": [1.44, 3.90, 5.60] }` 或 `{ "h":1.44, "d":3.90, "a":5.60 }`。
适用于任何 N 结果市场。
### `POST /api/value` — 凯利指数 / 价值对比参考集
请求体:`{ "offered":[2.10,3.40,3], "reference":[2.00,34. "labels":["主胜","平","主负"] }`。
参考集被去水化”概率;每个提供的结果获取 `kelly`(= 赔率 × 参考概率)、`ev`、`kellyFraction`、`isValue`。
### `POST /api/parlay` — 单关 / 过关奖金计算器
```jsonc
{
"legs": [ { "odds": 1.44 }, { "odds": 2.10 }, { "odds": 3.30, "result": "void" } ],
"passType": "3串1", // "单关", 一个 "M串N" 标签,省略(= N串1),或 "folds":[2,3]
"multiplier": 2 // 倍数,默认 1
}
```
返回 `bets`(注数)、`totalStake`、`maxPayout`/`maxPayoutCapped`、`realizedPayout`(尊重每腿的 `result: win|lose|void`)、`byFold`、`capped`(500 万上限)。
### `GET /api/parlay/types` — 完整的 M串N 参考表(`?matches=4` 筛选)
### `GET /api/meta` — 赔率池标签、过关表、公式、认证 + 数据源说明
### `GET /health` · `GET /` — 存活 / 端点索引(无认证)
> 💡 在 Windows 上,从 **文件** 发送中文请求体(`--data-binary @body.json`),而不是内联 `-d`,以避免命令行对 `串` 等字符的破坏。
---
## 数学计算(赔率推导)具有十进制赔率 `o_i 结果市场:
| 指标 | 公式 |
|---|---|
隐含概率 | / o_i| 总概率S = Σ(1/o_i)` |
返还率** | `1 / S` |
| 庄家抽水 | `1 − 1/S` |
| 无概率 | `(1/o_i) / S` |
| 真实赔率1 / noVigProb = o_i × S` |
| 凯利指数 | `o_i × p_ref_i`(需要外部参考) |
| 预期价值 | `p_ref_i × o_i − 1` |
单个赔率集无法得出有意义的凯利指数(它退化为返还率 `/api/value` 需要一个**参考**赔率集。
---
## 赔率池和结果代码
| 赔率池 | 名称 | 结果 |
|---|---|---|
| `had` | 胜平负(无让球) | `h`/`d`/`a` → 主胜 / 平 / 主负 |
| `hhad` | 让球胜平负 | `h`/`d`/`a` 带有 `` 增加到 **主队** 比| `crs` | 比分 | `sHHsMM` = `home:away`; `s1sh`/`s1sd`/`s1sa` = 胜其它/平其它/负其它 |
| `ttg` | 总进球 | `s0`…`s6` 精确; **`s7` = 7+** |
| `hafu` | 半全场 | 9 个代码,第一个字符 = 半场,第二个字符 = 全场(例如 `ha` = 胜/负) |
**趋势** (`up`/`flatdown`) 源自竞彩的率变化标志。
---
##规则
- 单注金额 = **2 元**; 单关 = `2 × 倍数 × 赔率`; M串N 每注 = `2数 × Π(赔率) 注数(`bets`)对于一个“M串N” = `Σ C(M, size)` 在其盒装折大小上。
- 单张彩票上限 **5,000,000 元**; 混合过关最大关数 = 最小的每场比赛限制(木桶原理; had/hhad = 8, crs/ttg/hafu = 6).
| 比赛场次 M | 玩法(标签 → 注数) |
|---|---|
| 2 | 2串1 |
| 3 | 3串1, 3串3, 3串4 |
| 4 | 4串1, 4串4, 4串5, 4串6, 4串11 |
| 5 | 5串1, 5串5, 5串6, 5串10, 5串16, 5串20, 5串26 |
| 6 | 6串1, 6串6, 6串7, 6串15, 6串20, 6串22, 6串35, 6串42, 6串50, 6串57 |
| 7 | 7串1, 7串7, 7串8, 7串21, 7串35, 7串120 |
| 8 | 8串1, 8串8, 8串9, 8串28, 8串56, 8串708串247 |
---
## MCP 服务器(本地标准输入)
在本地作为工具向 MCP 主机(Claude Desktop / Claude Code)公开实时 推导 + 过关引擎。**本地运行**,因此它您的网络获取竞彩数据(无地理限制,无需代理),并重用测试的 `src/*` 模块。
**工具:** `get_matches`, `get_match`, `derive_odds`, `compare_value`, `calc_parlay`, `list_parlay_types`, `get_meta`.
```bash
npm run mcp:smoke # 启动服务器,列出工具,调用几个
```
aude Code** —项目范围的 `.mcp.json`;重新打开项目并批准 `sporttery-odds`。或全局注册:
```bash
claude mcp add sporttery-odds -- \
node --no-warnings --experimental-strip-types /abs/path/to/SportteryAPI/mcp/server.ts
```
**Claude Desktop** — 添加到 `%APPDATA%\Claude\claude_desktop_config.json`:
```jsonc
{
"mcpServers": {
"sporttery-odds": {
"command": "node",
"args": ["--no-warnings", "--experimental-strip-types",
"C:/abs/path/to/SportteryAPI/mcp/server.ts"]
}
}
}
```
在 `.env` 中设置 `SPORTTERY_PROXY`(如果主机需要代理访问竞彩)。
---
## 部署
**一键部署** — [部署到 Cloudflare](https://deploy.workers.cloudflare.com/?url=https://github.com/Johnserf-Seed/SportteryAPI) 按钮(此 README 文件顶部)分叉存储库,设置 Workers 构建(自动部署推送 = CD),并部署。计算端点立即工作;实时赔率需要 `UPSTREAM_PROXY`(如下),认证在您设置 `API_KEY` 后开启。
**CLI:**
```bash
npx wrangler login
echo "your-strong-key" | npx wrangler secret put API_KEY # 启用认证(推荐)
npm run deploy
```
对于全球边缘的实时赔率,设置一个可访问上游服务器的中转代理:
```bash
# 在 wrangler.jsonc → vars: "UPSTREAM_PROXY": "https://my-relay.example/"
npm run deploy
```
无需 `UPSTREAM_PROXY`,部署的 Worker 被地理限制(`/api/matches` → 502);计算端点仍然有效。`wrangler.jsonc` 已提交(无密钥);密钥存储在 `.dev.vars`(本地)/ `wrangler secret put`。
---
## 配置
**Worker** — `wrangler.jsonc` 中的 `vars`(+ `.dev.vars` / `wrangler secret put` 用于密钥):
| 变量 | 默认值 | 含义 |
|---|---|---|
| `DEFAULT_POOLS` | `hhad,had,crs,ttg,hafu` | `/api/matches` 返回的默认赔率池(Worker 始终获取所有赔率池) |
| `CACHE_TTL_SECONDS` | `30` | 每个colo的缓存 API TTL |
| `UPSTREAM_PROXY` | _(unset)_ | 通过可访问上游服务器的中转代理 URL 前缀,绕过地理限制 |
| `API_KEY` | _(secret)_ | 以逗号分隔的 API 密钥;未设置 = 认证禁用 |
**节点端**(MCP / 烟雾测试)— 环境变量,可从 `.env` 加载:
| 变量 | 默认值 | 含义 |
|---|---|---|
| `SPORTTERY_PROXY` | _(unset)_ | MCP 服务器的竞彩获取的 HTTP 代理(也支持 `HTTPS_PROXY`) |
| `API_KEY` | _(unset)_ | REST 烟雾测试呈现的密钥 |
| `BASE` | `http://localhost:8787` | 烟雾测试目标 |
**代理** — `SPORTTERY_PROXY` 是节点端获取的普通 HTTP 代理(通过 undici)。`UPSTREAM_PROXY` 是 **URL 前缀**,适用于 Worker(Workers 不能使用 CONNECT 代理)指向一个透明的、可访问上游服务器代理。
---
## 缓存api/matches` 在 `` 中报告它来自哪个层级:
1. **缓存 (`edge-cache`) — 每个colo,`CACHE_TTL(默认 30s)。在突发情况下去重以使上游每窗口最多命中一次。
2. **上游** (`upstream`) — 在缓存未命中时获取(可选地通过 `UPSTREAM_PROXY`)。
---
## 未来:实时 WebSocket 流
`src/stream.ts` 实现 `OddsStream`,一个可休眠的 Durable 对象,它将赔率差异扇出到 WebSocket 客户端。要启用:取消注释 `wrangler.jsonc` 中的 `durable_objects`、`migrations` 和 `triggers.crons` 块(DO 使用 `new_sqlite_classes`),然后部署。客户端连接到 `wss://<worker>/stream?pools=had,hhad&key=<API_KEY>`。
---
## 开发和测试
```bash
npm test # 25 个单元测试(node:test,无网络)
npm run typecheck # tsc --noEmit 在 src/ 上
npm run dev # 本地 Worker
npm run mcp:smoke # MCP测试
BASE=http://localhost:8787 API_KEY=$KEY node --experimental-strip-types scripts/smoke.ts
npm run refresh-sample # 从实时上游刷新 sample.json
```
无构建步骤:TypeScript --experimental-strip-types` 运行,Worker 由 Wrangler/esbuild 捆绑。纯模块使用 `.ts` 导入扩展名,以便在 `tsc`、esbuild 和 Node 类型剥离。
**CI:** `.github/workflows/ci.yml` 在每次推送和 PR 上运行 `typecheck` + `test`(Node 22)。**CD:** `.github/workflows/deploy.yml` 在 `main` 上的 CI 通过后部署到 Cloudflare Workers(需要 `CLOUDFLARE_API_TOKEN` 存储库密钥)。部署到 Cloudflare 的按钮(Workers 构建)是另一种选择。
---
## 数据源
本项目非官方**读取器。它不托管、生成或拥有数据。
- **上游端点:** `https://webapi.sporttery.cn/gateway/jc/football/getMatchCalculatorV1.qry`
- 这是**未公开的公共 Web API**,为官方竞彩(Jingcai)赔率计算器页面 `https://m.sporttery.cn/mjc/jsq/zqspf/` 提供支持。
- 所有比赛日程、赔率、结果和相关数据 —相关知识产权 — 归 **中国体育彩票 / China Sports Lottery (sporttery.cn)** 所有。数据由 **国家体育总局体育彩票管理中心 (State Sports Lottery Administration Center)** 发布;运营商为 **中体彩彩票运营管理有限公司**。站点备案:京ICP备090号。
- 本项目**仅读取上游 JSON 并将其转换为派生指标**。它不声称拥有底层数据的所有权官方认可,提供不保证准确性。分发底层数据的任何权利持有人所有,而不是该项目或其用户。
---
##取和机器人策略
---
免责声明
---
## 爬虫与 robots 策略
我们直接检查了 sporttery 的 `robots.txt` 文件(原始头部和主体)在相关主机上的内容。
- **`https://www.sporttery.cn/robots.txt`** — 存在(HTTP 200)。有一组 `User-agent: *`,包含五个基于扩展的 `Disallow` 规则:`/*.pdf$`、`/*.js$`、`/*.json$`、`/*.php$`、`/*.mp4$`。**没有** `Allow`、`Crawl-delay` 和 `Sitemap` 行。
- **`https://m.sporttery.cn/robots.txt`** — 未找到(HTTP 404)。没有指令。
- **`https://webapi.sporttery.cn/robots.txt`** (API 主机)— 没有提供 `robots.txt` 文件;请求仅返回一个腾讯云 EdgeOne WAF 阻止页面(HTTP 403 / 567)。
**这意味着:**
- 本项目调用一个 **单独的 JSON API 端点** (`/gateway/jc/football/getMatchCalculatorV1.qry`)。它 **不** 爬取 HTML 页面或爬虫网站。
- `robots.txt` 是 **每个主机** 独有的。已发布的唯一文件位于 `www.sporttery.cn`,这是一个与 API 主机 `webapi.sporttery.cn` **不同** 的主机,因此它不控制 API。即便如此,该文件的 `Disallow` 规则是锚定文件扩展名的模式(`.pdf/.js/.json/.php/.mp4`),而 `/gateway/jc/football/...` 这样的路径不会匹配。
- **没有 Disallow 不意味着许可**。API 主机受到 WAF 的保护,它主动挑战并阻止自动化请求 — 比 `robots.txt` 更强的访问控制信号。
**本项目采取的负责任使用措施:
- 短缓存 TTL,以便重复读取时从缓存中提供服务,而不是重新访问上游。
- 低请求频率;没有批量或并行抓取。
- 在端点前面使用 **单个代理/中继**,而不是使用一组爬虫。
- 遵守任何速率限制,并在出现错误时退后。
- **遇到 4xx/5xx 或 WAF 阻止响应时停止**,而不是积极重试。
如果您确定任何 sporttery `robots.txt` 规则(或网站条款)禁止您访问您打算访问的路径,**请尊重它**,并在继续之前获得 中国体育彩票 的授权。如果有疑问,请勿抓取。
---
## 免责声明
- 本项目 **不隶属于、不授权、不受 中国体育彩票 / China Sports Lottery (sporttery.cn)** 或其任何运营商的认可。所有商标和数据归各自所有者所有。
- 它仅用于 **信息和技术目的**。它 **不是投注建议**,不是预测服务,也不是任何结果的保证。
- 数据以 **“现状” 提供**,不保证准确性、完整性、及时性或可用性。上游可能会更改、中断或在任何时候返回错误。官方赛程、赔率、结果和奖品兑换仅由 中国体育彩票 的官方渠道确定。
- **赌博涉及财务风险**。任何彩票或投注活动仅适用于 **成年人(18岁及以上)**,并且仅在您的司法管辖区合法的情况下才适用。请理性购彩。
- **您负责** 遵守 sporttery 的条款(包括其 法律声明,其中限制复制/衍生使用并禁止通过 非正当手段 / 不当手段访问)和所有适用的当地法律。使用本项目风险自负。
- 这 **不是法律建议**。如果您是权利持有人,并希望更改或删除本项目(或其对您的端点的访问),请联系维护人员,**根据要求删除**。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器