连接流程
1. 连接
打开WebSocket连接。连接时无需身份验证。
2. 登录
在连接后 30秒 内发送登录消息:
{
"type": "login",
"apiKey": "YOUR_API_KEY",
"channels": []
}
空的 channels 数组订阅所有可用频道。要订阅特定频道:
{
"type": "login",
"apiKey": "YOUR_API_KEY",
"channels": ["orders", "bets", "settlements"]
}
可选启用可靠传递和消息确认:
{
"type": "login",
"apiKey": "YOUR_API_KEY",
"channels": ["orders", "bets"],
"reliableDelivery": true
}
3. 登录确认
成功后,服务器响应:
{
"type": "login_ok",
"clientName": "your-client",
"channels": ["orders", "bets", "settlements"],
"subscriptionId": 1,
"access": {
"clientFiltered": ["orders", "bets", "settlements", "accounts", "balance", "betslip"],
"global": ["fixtures", "currencies", "status", "emergency"]
},
"reliableDelivery": false,
"features": {
"messageOrdering": true,
"acknowledgments": false,
"batchAck": false
}
}
4. 接收更新
数据消息格式如下:
{
"type": "data",
"channel": "orders",
"event": "INSERT",
"payload": { ... },
"ts": 1771183909789,
"seq": 1
}
| 字段 | 描述 |
|---|
type | 数据消息始终为 "data" |
channel | 此消息所属频道 |
event | 事件类型:INSERT、UPDATE、DELETE、SETTLED、STATUS |
payload | 完整的更新对象(见下方各频道负载格式) |
ts | 服务器时间戳(自纪元以来的毫秒数) |
seq | 每订阅的单调递增序列号 |
requireAck | 仅在启用可靠传递时存在且为 true |
5. 保持活跃
服务器每 30秒 发送一次 ping:
在 120秒 内回复 pong,否则连接将被关闭:
您也可以从客户端发送 ping — 服务器会回复 pong。
客户端过滤频道
这些频道仅传递属于您 clientName 的数据:
| 频道 | 事件 | 描述 |
|---|
orders | INSERT, UPDATE, DELETE | 订单下达、成交、状态变更 |
bets | INSERT, UPDATE, DELETE | 投注下达、确认和移除 |
settlements | SETTLED | 结算状态更新(负载与 bets 相同) |
accounts | INSERT, UPDATE, DELETE | 账户创建、更新和删除 |
balance | UPDATE | 余额变动通知 |
betslip | UPDATE | 活跃投注单订阅的实时赔率更新(每次 GET /betslip 调用后 60 秒窗口) |
全局频道
所有订阅者都会收到:
| 频道 | 事件 | 描述 |
|---|
fixtures | UPDATE | 赛事元数据和比分变更 |
currencies | UPDATE | 货币汇率更新 |
status | STATUS | 系统状态变更 |
emergency | STATUS | 紧急模式激活/停用 |
负载格式
订单负载
orders 表的完整数据行:
{
"orderId": 327,
"requestUuid": "eb45b192-317b-42d5-9f65-af497b9fa8c1",
"client": "demo",
"clientName": "demo",
"fixtureId": "id1000004461512432",
"outcomeId": 103,
"playerId": 0,
"orderPrice": 1.95,
"orderStake": 10.0,
"filledStake": 10.0,
"remainingStake": 0.0,
"orderStatus": "FILLED",
"statusReason": null,
"userRef": "bettor1234",
"testOrder": false,
"acceptBetterOdds": true,
"acceptPartialStake": true,
"orderCurrency": "USD",
"back": true,
"allowedBookmakers": "*",
"oddsInfo": null,
"meta": {},
"expiresAt": "2026-02-07T17:29:37+00:00",
"filledAt": "2026-02-07T17:29:32+00:00",
"createdAt": "2026-02-07T17:29:32+00:00",
"updatedAt": "2026-02-07T17:29:32+00:00"
}
投注 / 结算负载
bets 表的完整数据行:
{
"betId": 73,
"orderId": 327,
"bookmaker": "pinnacle",
"bookmakerBetId": "3332684214",
"betStatus": "CONFIRMED",
"settlementStatus": "UNSETTLED",
"placedStake": 10.0,
"placedPrice": 1.98,
"placedCurrency": "USD",
"account": "pinnacle_main",
"requestUuid": "eb45b192-317b-42d5-9f65-af497b9fa8c1",
"userRef": "bettor1234",
"testBet": false,
"client": "demo",
"clientName": "demo",
"sentData": { "stake": 10.0, "price": 1.95 },
"receivedData": { "betId": "3332684214", "status": "accepted", "price": 1.98 },
"settlementAmount": null,
"settlementReason": null,
"settledAt": null,
"declineReason": null,
"betRequestId": null,
"oddsInfo": null,
"meta": {},
"placedAt": "2026-02-07T17:29:32+00:00",
"createdAt": "2026-02-07T17:29:32+00:00",
"updatedAt": "2026-02-07T17:29:32+00:00"
}
余额负载
{
"clientName": "demo",
"bookmaker": "pinnacle",
"username": "pinnacle_main",
"balance": 4990.0,
"currency": "USD",
"ts": 1771183910123
}
紧急负载
{
"active": true,
"reason": "Upstream provider maintenance",
"ts": 1771183910123
}
投注单负载
当投注单订阅处于活跃状态时推送实时赔率更新。调用 GET /betslip 注册一个 60 秒的滑动窗口 — 在此窗口内,价格变化以与投注单 REST 响应相同的格式进行广播。每次轮询重置计时器。
这适用于赛事和期货投注单订阅。对于期货,负载包含 futureId 和 participantId 而非 fixtureId。
赛事投注单示例:
{
"fixtureId": "id1000004461512432",
"outcomeId": 103,
"playerId": 0,
"client": "demo",
"userRef": null,
"odds": {
"pinnacle": {
"odds_12345": {
"price": 1.98,
"limit": 500.0,
"limitMin": 1.0,
"limitCurrency": "USD",
"limitUsd": 500.0,
"limitMinUsd": 1.0,
"active": true,
"account": "pinnacle_main"
}
}
}
}
期货投注单示例(即将推出):
{
"futureId": "fut_123456",
"outcomeId": 0,
"playerId": 0,
"participantId": 5,
"client": "demo",
"userRef": null,
"odds": {
"pinnacle": {
"fut_123456:pinnacle:0:0:5": {
"price": 3.50,
"limit": 1000.0,
"limitMin": 5.0,
"limitCurrency": "USD",
"limitUsd": 1000.0,
"limitMinUsd": 5.0,
"active": true,
"account": "pinnacle_main"
}
}
}
}
期货投注单 WebSocket 广播尚未启用。订阅基础设施已就绪,将在即将发布的版本中激活。
连接限制
| 设置 | 值 |
|---|
| 每API密钥最大连接数 | 5 |
| 认证超时 | 30秒 |
| 服务器 ping 间隔 | 30秒 |
| Pong 超时(断开连接) | 120秒 |
| 消息缓冲区(可靠传递) | 100条消息 |
| 每客户端输出队列 | 2000条消息 |
示例:Python 客户端
import asyncio
import json
import websockets
async def connect():
uri = "wss://v2.55-tech.com/ws"
async with websockets.connect(uri) as ws:
# 登录
await ws.send(json.dumps({
"type": "login",
"apiKey": "YOUR_API_KEY",
"channels": ["orders", "bets", "settlements"]
}))
# 等待登录确认
login_resp = json.loads(await ws.recv())
print(f"已登录为 {login_resp.get('clientName')}")
# 监听更新(自动回复 ping)
async for message in ws:
data = json.loads(message)
if data["type"] == "ping":
await ws.send(json.dumps({"type": "pong"}))
elif data["type"] == "data":
print(f"[{data['channel']}:{data['event']}] {data['payload']}")
asyncio.run(connect())
示例:JavaScript 客户端
const WebSocket = require('ws');
const ws = new WebSocket('wss://v2.55-tech.com/ws');
ws.on('open', () => {
ws.send(JSON.stringify({
type: 'login',
apiKey: 'YOUR_API_KEY',
channels: ['orders', 'bets', 'settlements']
}));
});
ws.on('message', (raw) => {
const msg = JSON.parse(raw);
if (msg.type === 'login_ok') {
console.log(`已登录为 ${msg.clientName}`);
}
if (msg.type === 'ping') {
ws.send(JSON.stringify({ type: 'pong' }));
}
if (msg.type === 'data') {
console.log(`[${msg.channel}:${msg.event}]`, msg.payload);
}
});