WebSocket 实时推送
通过 WebSocket 订阅实时行情和市场深度推送
概述
TickFlow 提供两个 WebSocket 接口:| 接口 | 地址 | 说明 |
|---|---|---|
| 统一推送(推荐) | /v1/ws/stream | 按频道订阅,支持 quotes(行情)和 depth(五档盘口) |
| 行情推送 | /v1/ws/quotes | 仅推送行情数据,兼容旧版 |
WebSocket 为付费功能,需要订阅包含 WebSocket 实时行情的套餐(如 Expert)或单独开启。市场深度频道额外需要「市场深度」权限。
统一推送 /v1/ws/stream
连接地址
wss://api.tickflow.org/v1/ws/stream?api_key=YOUR_API_KEY
api_key 查询参数认证。认证失败返回 HTTP 401/403,客户端应停止重连。
所有消息使用 JSON 文本帧。
客户端命令
subscribe — 订阅频道
按频道(channel)+ 标的列表(symbols)订阅。可多次调用追加。
{"op": "subscribe", "channel": "quotes", "symbols": ["600000.SH", "000001.SZ"]}
{"op": "subscribe", "channel": "depth", "symbols": ["600000.SH"]}
subscribed 确认,并立即推送新增标的的缓存快照。
支持的频道:
| 频道 | 说明 | 所需权限 |
|---|---|---|
quotes | 实时行情 | WebSocket 实时行情 |
depth | 五档市场深度 | 市场深度 |
unsubscribe — 退订频道
{"op": "unsubscribe", "channel": "depth", "symbols": ["600000.SH"]}
服务端消息
subscribed — 频道订阅状态
{"op": "subscribed", "channel": "quotes", "symbols": ["600000.SH", "000001.SZ"], "total": 2}
quotes — 行情推送
{
"op": "quotes",
"data": [
{
"symbol": "600000.SH",
"region": "CN",
"last_price": 9.72,
"prev_close": 9.78,
"open": 9.78,
"high": 9.78,
"low": 9.68,
"volume": 426585,
"amount": 422430500,
"timestamp": 1776754802000,
"ext": {
"type": "cn_equity",
"name": "浦发银行",
"change_pct": -0.006135,
"change_amount": -0.06,
"amplitude": 0.010225,
"turnover_rate": 0.001281
}
}
]
}
depth — 市场深度推送
{
"op": "depth",
"data": [
{
"symbol": "600000.SH",
"region": "CN",
"timestamp": 1776754802000,
"bid_prices": [9.72, 9.71, 9.7, 9.69, 9.68],
"bid_volumes": [3192, 3870, 26168, 5849, 5480],
"ask_prices": [9.73, 9.74, 9.75, 9.76, 9.77],
"ask_volumes": [74, 1602, 1148, 1209, 1109]
}
]
}
| 字段 | 说明 |
|---|---|
bid_prices | 买入价(买1-买5,降序) |
bid_volumes | 买入量 |
ask_prices | 卖出价(卖1-卖5,升序) |
ask_volumes | 卖出量 |
error — 错误消息
{"op": "error", "message": "no permission for channel: depth"}
no permission for channel: ...— 无该频道权限unknown channel: ...— 未知频道名exceeded max N symbols— 标的数超出套餐上限invalid message: ...— JSON 格式不正确
命令总览
| 客户端命令 | 说明 | 服务端响应 |
|---|---|---|
subscribe | 按频道订阅 | subscribed + 对应频道的缓存快照 |
unsubscribe | 按频道退订 | subscribed |
| 服务端推送 | 说明 | 触发条件 |
|---|---|---|
subscribed | 频道订阅状态 | 每次 subscribe / unsubscribe 后 |
quotes | 实时行情数据 | 已订阅标的有行情更新时 |
depth | 五档市场深度 | 已订阅标的盘口变化时 |
error | 错误信息 | 操作失败时 |
行情推送 /v1/ws/quotes(旧版)
旧版接口仅推送行情数据,不支持市场深度。新接入建议使用
/v1/ws/stream。连接地址
wss://api.tickflow.org/v1/ws/quotes?api_key=YOUR_API_KEY
协议
与统一推送的quotes 频道行为一致,但不使用 channel 字段:
{"op": "subscribe", "symbols": ["600000.SH", "000001.SZ"]}
{"op": "unsubscribe", "symbols": ["600000.SH"]}
quotes、subscribed、error 消息,格式与统一推送相同。
连接保活
服务端每 30 秒发送一次 Ping 帧,客户端需回复 Pong 帧。大多数 WebSocket 库会自动处理。连接管理
- 断开清理:连接断开后该连接的所有订阅自动清除
- 重连恢复:客户端断线重连后需重新发送
subscribe恢复订阅 - 认证错误:收到 HTTP 401/403 时不应自动重连,请检查 API Key 和套餐权限
代码示例
- Python
- JavaScript / Node.js
- Python SDK
使用 websockets 库连接统一推送:
pip install websockets
import asyncio
import json
import websockets
API_KEY = "your-api-key"
URL = f"wss://api.tickflow.org/v1/ws/stream?api_key={API_KEY}"
async def main():
async with websockets.connect(URL) as ws:
# 订阅行情和盘口
await ws.send(json.dumps({"op": "subscribe", "channel": "quotes", "symbols": ["600000.SH", "000001.SZ"]}))
await ws.send(json.dumps({"op": "subscribe", "channel": "depth", "symbols": ["600000.SH"]}))
async for raw in ws:
msg = json.loads(raw)
if msg["op"] == "subscribed":
print(f"[{msg['channel']}] 已订阅 {msg['total']} 个标的")
elif msg["op"] == "quotes":
for q in msg["data"]:
print(f"{q['symbol']}: {q['last_price']}")
elif msg["op"] == "depth":
for d in msg["data"]:
print(f"[盘口] {d['symbol']} 买1:{d['bid_prices'][0]} 卖1:{d['ask_prices'][0]}")
elif msg["op"] == "error":
print(f"错误: {msg['message']}")
asyncio.run(main())
const API_KEY = "your-api-key";
const URL = `wss://api.tickflow.org/v1/ws/stream?api_key=${API_KEY}`;
const ws = new WebSocket(URL);
ws.onopen = () => {
ws.send(JSON.stringify({ op: "subscribe", channel: "quotes", symbols: ["600000.SH"] }));
ws.send(JSON.stringify({ op: "subscribe", channel: "depth", symbols: ["600000.SH"] }));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.op === "subscribed") {
console.log(`[${msg.channel}] 已订阅 ${msg.total} 个标的`);
} else if (msg.op === "quotes") {
for (const q of msg.data) {
console.log(`${q.symbol}: ${q.last_price}`);
}
} else if (msg.op === "depth") {
for (const d of msg.data) {
console.log(`[盘口] ${d.symbol} 买1:${d.bid_prices[0]} 卖1:${d.ask_prices[0]}`);
}
} else if (msg.op === "error") {
console.error("错误:", msg.message);
}
};
ws.onerror = (err) => console.error("WebSocket error:", err);
ws.onclose = (e) => console.log("连接关闭:", e.code, e.reason);
TickFlow Python SDK 封装了连接管理、自动重连和订阅恢复:非阻塞模式:
pip install "tickflow[all]" --upgrade
from tickflow import TickFlow
tf = TickFlow(api_key="your-api-key")
stream = tf.stream
@stream.on_quotes
def on_quotes(quotes):
for q in quotes:
print(f"{q['symbol']}: {q['last_price']}")
@stream.on_depth
def on_depth(depths):
for d in depths:
print(f"[盘口] {d['symbol']} 买1:{d['bid_prices'][0]}×{d['bid_volumes'][0]}")
@stream.on_error
def on_error(msg):
print(f"错误: {msg}")
stream.subscribe("quotes", ["600000.SH", "000001.SZ"])
stream.subscribe("depth", ["600000.SH"])
stream.connect() # 阻塞直到 close() 或 Ctrl+C
stream.connect(block=False) # 后台线程运行
stream.subscribe("quotes", ["AAPL.US"]) # 动态追加订阅
如果只需获取某一时刻的行情快照,使用 REST 接口更为简单。WebSocket 适合需要持续接收行情更新的场景。
AsyncAPI
stream
id: stream
title: Stream
description: |
统一推送接口,按频道(channel)订阅行情和市场深度。
支持频道: `quotes`(需 WebSocket 实时行情权限)、`depth`(需市场深度权限)。
servers:
- id: production
protocol: wss
host: api.tickflow.org
bindings: []
variables: []
address: /v1/ws/stream
parameters: []
bindings: []
operations:
- &ref_1
id: streamSubscribe
title: Stream subscribe
description: 客户端按频道订阅
type: receive
messages:
- &ref_7
id: subscribeRequest
contentType: application/json
payload:
- name: 订阅频道
description: 按频道和标的列表订阅,订阅成功后推送缓存快照
type: object
properties:
- name: op
type: string
description: 操作类型
enumValues:
- subscribe
required: true
- name: channel
type: string
description: 频道名称
enumValues:
- quotes
- depth
required: true
- name: symbols
type: array
description: 要订阅的标的代码列表
required: true
properties:
- name: item
type: string
required: false
headers: []
jsonPayloadSchema:
type: object
additionalProperties: false
properties:
op:
type: string
enum:
- subscribe
description: 操作类型
x-parser-schema-id: <anonymous-schema-2>
channel:
type: string
enum:
- quotes
- depth
description: 频道名称
x-parser-schema-id: <anonymous-schema-3>
symbols:
type: array
items:
type: string
x-parser-schema-id: <anonymous-schema-5>
description: 要订阅的标的代码列表
x-parser-schema-id: <anonymous-schema-4>
required:
- op
- channel
- symbols
examples:
- op: subscribe
channel: quotes
symbols:
- 600000.SH
- 000001.SZ
- op: subscribe
channel: depth
symbols:
- 600000.SH
x-parser-schema-id: <anonymous-schema-1>
title: 订阅频道
description: 按频道和标的列表订阅,订阅成功后推送缓存快照
example: |-
{
"op": "subscribe",
"channel": "quotes",
"symbols": [
"600000.SH",
"000001.SZ"
]
}
bindings: []
extensions:
- id: x-parser-unique-object-id
value: subscribeRequest
bindings: []
extensions: &ref_0
- id: x-parser-unique-object-id
value: stream
- &ref_2
id: streamUnsubscribe
title: Stream unsubscribe
description: 客户端按频道退订
type: receive
messages:
- &ref_8
id: unsubscribeRequest
contentType: application/json
payload:
- name: 退订频道
description: 按频道退订指定标的
type: object
properties:
- name: op
type: string
description: 操作类型
enumValues:
- unsubscribe
required: true
- name: channel
type: string
description: 频道名称
enumValues:
- quotes
- depth
required: true
- name: symbols
type: array
description: 要退订的标的代码列表
required: true
properties:
- name: item
type: string
required: false
headers: []
jsonPayloadSchema:
type: object
additionalProperties: false
properties:
op:
type: string
enum:
- unsubscribe
description: 操作类型
x-parser-schema-id: <anonymous-schema-7>
channel:
type: string
enum:
- quotes
- depth
description: 频道名称
x-parser-schema-id: <anonymous-schema-8>
symbols:
type: array
items:
type: string
x-parser-schema-id: <anonymous-schema-10>
description: 要退订的标的代码列表
x-parser-schema-id: <anonymous-schema-9>
required:
- op
- channel
- symbols
examples:
- op: unsubscribe
channel: depth
symbols:
- 600000.SH
x-parser-schema-id: <anonymous-schema-6>
title: 退订频道
description: 按频道退订指定标的
example: |-
{
"op": "unsubscribe",
"channel": "depth",
"symbols": [
"600000.SH"
]
}
bindings: []
extensions:
- id: x-parser-unique-object-id
value: unsubscribeRequest
bindings: []
extensions: *ref_0
- &ref_3
id: streamReceiveSubscribed
title: Stream receive subscribed
description: 服务端返回频道订阅状态
type: send
messages:
- &ref_9
id: subscribedResponse
contentType: application/json
payload:
- name: 频道订阅状态
description: subscribe / unsubscribe 操作后返回该频道的完整订阅列表
type: object
properties:
- name: op
type: string
description: 消息类型
enumValues:
- subscribed
required: true
- name: channel
type: string
description: 频道名称
enumValues:
- quotes
- depth
required: true
- name: symbols
type: array
description: 当前该频道已订阅的全部标的
required: true
properties:
- name: item
type: string
required: false
- name: total
type: integer
description: 当前该频道已订阅标的总数
required: true
headers: []
jsonPayloadSchema:
type: object
additionalProperties: false
properties:
op:
type: string
enum:
- subscribed
description: 消息类型
x-parser-schema-id: <anonymous-schema-12>
channel:
type: string
enum:
- quotes
- depth
description: 频道名称
x-parser-schema-id: <anonymous-schema-13>
symbols:
type: array
items:
type: string
x-parser-schema-id: <anonymous-schema-15>
description: 当前该频道已订阅的全部标的
x-parser-schema-id: <anonymous-schema-14>
total:
type: integer
description: 当前该频道已订阅标的总数
x-parser-schema-id: <anonymous-schema-16>
required:
- op
- channel
- symbols
- total
examples:
- op: subscribed
channel: quotes
symbols:
- 600000.SH
- 000001.SZ
total: 2
x-parser-schema-id: <anonymous-schema-11>
title: 频道订阅状态
description: subscribe / unsubscribe 操作后返回该频道的完整订阅列表
example: |-
{
"op": "subscribed",
"channel": "quotes",
"symbols": [
"600000.SH",
"000001.SZ"
],
"total": 2
}
bindings: []
extensions:
- id: x-parser-unique-object-id
value: subscribedResponse
bindings: []
extensions: *ref_0
- &ref_4
id: streamReceiveQuotes
title: Stream receive quotes
description: 服务端推送实时行情
type: send
messages:
- &ref_10
id: quotesData
contentType: application/json
payload:
- name: 行情推送
description: 实时行情数据,仅包含已订阅且有更新的标的
type: object
properties:
- name: op
type: string
description: 消息类型
enumValues:
- quotes
required: true
- name: data
type: array
description: 行情快照列表
required: true
properties:
- name: symbol
type: string
description: 标的代码
required: true
- name: region
type: string
description: 市场区域
enumValues:
- CN
- US
- HK
required: true
- name: last_price
type: number
description: 最新价
required: true
- name: prev_close
type: number
description: 昨收价
required: true
- name: open
type: number
description: 开盘价
required: true
- name: high
type: number
description: 最高价
required: true
- name: low
type: number
description: 最低价
required: true
- name: volume
type: integer
description: 成交量
required: true
- name: amount
type: number
description: 成交额
required: true
- name: timestamp
type: integer
description: 行情时间戳(毫秒)
required: true
- name: session
type: string
description: 交易时段(可选)
required: false
- name: ext
type: object
description: 扩展数据(名称、涨跌幅等,可选)
required: false
headers: []
jsonPayloadSchema:
type: object
additionalProperties: false
properties:
op:
type: string
enum:
- quotes
description: 消息类型
x-parser-schema-id: <anonymous-schema-18>
data:
type: array
items:
type: object
additionalProperties: true
description: 单条行情快照
properties:
symbol:
type: string
example: 600000.SH
description: 标的代码
x-parser-schema-id: <anonymous-schema-20>
region:
type: string
enum:
- CN
- US
- HK
example: CN
description: 市场区域
x-parser-schema-id: <anonymous-schema-21>
last_price:
type: number
example: 9.72
description: 最新价
x-parser-schema-id: <anonymous-schema-22>
prev_close:
type: number
example: 9.78
description: 昨收价
x-parser-schema-id: <anonymous-schema-23>
open:
type: number
example: 9.78
description: 开盘价
x-parser-schema-id: <anonymous-schema-24>
high:
type: number
example: 9.78
description: 最高价
x-parser-schema-id: <anonymous-schema-25>
low:
type: number
example: 9.68
description: 最低价
x-parser-schema-id: <anonymous-schema-26>
volume:
type: integer
example: 426585
description: 成交量
x-parser-schema-id: <anonymous-schema-27>
amount:
type: number
example: 422430500
description: 成交额
x-parser-schema-id: <anonymous-schema-28>
timestamp:
type: integer
example: 1776754802000
description: 行情时间戳(毫秒)
x-parser-schema-id: <anonymous-schema-29>
session:
type: string
example: trading
description: 交易时段(可选)
x-parser-schema-id: <anonymous-schema-30>
ext:
type: object
description: 扩展数据(名称、涨跌幅等,可选)
x-parser-schema-id: <anonymous-schema-31>
required:
- symbol
- region
- last_price
- prev_close
- open
- high
- low
- volume
- amount
- timestamp
x-parser-schema-id: Quote
description: 行情快照列表
x-parser-schema-id: <anonymous-schema-19>
required:
- op
- data
examples:
- op: quotes
data:
- symbol: 600000.SH
region: CN
last_price: 9.72
prev_close: 9.78
open: 9.78
high: 9.78
low: 9.68
volume: 426585
amount: 422430500
timestamp: 1776754802000
ext:
type: cn_equity
name: 浦发银行
change_pct: -0.006135
change_amount: -0.06
amplitude: 0.010225
turnover_rate: 0.001281
x-parser-schema-id: <anonymous-schema-17>
title: 行情推送
description: 实时行情数据,仅包含已订阅且有更新的标的
example: |-
{
"op": "quotes",
"data": [
{
"symbol": "600000.SH",
"region": "CN",
"last_price": 9.72,
"prev_close": 9.78,
"open": 9.78,
"high": 9.78,
"low": 9.68,
"volume": 426585,
"amount": 422430500,
"timestamp": 1776754802000,
"ext": {
"type": "cn_equity",
"name": "浦发银行",
"change_pct": -0.006135,
"change_amount": -0.06,
"amplitude": 0.010225,
"turnover_rate": 0.001281
}
}
]
}
bindings: []
extensions:
- id: x-parser-unique-object-id
value: quotesData
bindings: []
extensions: *ref_0
- &ref_5
id: streamReceiveDepth
title: Stream receive depth
description: 服务端推送市场深度
type: send
messages:
- &ref_11
id: depthData
contentType: application/json
payload:
- name: 市场深度推送
description: 五档盘口数据,仅包含已订阅 depth 频道且有更新的标的
type: object
properties:
- name: op
type: string
description: 消息类型
enumValues:
- depth
required: true
- name: data
type: array
description: 市场深度列表
required: true
properties:
- name: symbol
type: string
description: 标的代码
required: true
- name: region
type: string
description: 市场区域
enumValues:
- CN
- US
- HK
required: true
- name: timestamp
type: integer
description: 时间戳(毫秒)
required: true
- name: bid_prices
type: array
description: 买入价(买1-买5,降序)
required: true
properties:
- name: item
type: number
required: false
- name: bid_volumes
type: array
description: 买入量
required: true
properties:
- name: item
type: integer
required: false
- name: ask_prices
type: array
description: 卖出价(卖1-卖5,升序)
required: true
properties:
- name: item
type: number
required: false
- name: ask_volumes
type: array
description: 卖出量
required: true
properties:
- name: item
type: integer
required: false
headers: []
jsonPayloadSchema:
type: object
additionalProperties: false
properties:
op:
type: string
enum:
- depth
description: 消息类型
x-parser-schema-id: <anonymous-schema-33>
data:
type: array
items:
type: object
description: 单条五档市场深度
properties:
symbol:
type: string
example: 600000.SH
description: 标的代码
x-parser-schema-id: <anonymous-schema-35>
region:
type: string
enum:
- CN
- US
- HK
example: CN
description: 市场区域
x-parser-schema-id: <anonymous-schema-36>
timestamp:
type: integer
example: 1776754802000
description: 时间戳(毫秒)
x-parser-schema-id: <anonymous-schema-37>
bid_prices:
type: array
items:
type: number
x-parser-schema-id: <anonymous-schema-39>
example:
- 9.72
- 9.71
- 9.7
- 9.69
- 9.68
description: 买入价(买1-买5,降序)
x-parser-schema-id: <anonymous-schema-38>
bid_volumes:
type: array
items:
type: integer
x-parser-schema-id: <anonymous-schema-41>
example:
- 3192
- 3870
- 26168
- 5849
- 5480
description: 买入量
x-parser-schema-id: <anonymous-schema-40>
ask_prices:
type: array
items:
type: number
x-parser-schema-id: <anonymous-schema-43>
example:
- 9.73
- 9.74
- 9.75
- 9.76
- 9.77
description: 卖出价(卖1-卖5,升序)
x-parser-schema-id: <anonymous-schema-42>
ask_volumes:
type: array
items:
type: integer
x-parser-schema-id: <anonymous-schema-45>
example:
- 74
- 1602
- 1148
- 1209
- 1109
description: 卖出量
x-parser-schema-id: <anonymous-schema-44>
required:
- symbol
- region
- timestamp
- bid_prices
- bid_volumes
- ask_prices
- ask_volumes
x-parser-schema-id: MarketDepth
description: 市场深度列表
x-parser-schema-id: <anonymous-schema-34>
required:
- op
- data
examples:
- op: depth
data:
- symbol: 600000.SH
region: CN
timestamp: 1776754802000
bid_prices:
- 9.72
- 9.71
- 9.7
- 9.69
- 9.68
bid_volumes:
- 3192
- 3870
- 26168
- 5849
- 5480
ask_prices:
- 9.73
- 9.74
- 9.75
- 9.76
- 9.77
ask_volumes:
- 74
- 1602
- 1148
- 1209
- 1109
x-parser-schema-id: <anonymous-schema-32>
title: 市场深度推送
description: 五档盘口数据,仅包含已订阅 depth 频道且有更新的标的
example: |-
{
"op": "depth",
"data": [
{
"symbol": "600000.SH",
"region": "CN",
"timestamp": 1776754802000,
"bid_prices": [
9.72,
9.71,
9.7,
9.69,
9.68
],
"bid_volumes": [
3192,
3870,
26168,
5849,
5480
],
"ask_prices": [
9.73,
9.74,
9.75,
9.76,
9.77
],
"ask_volumes": [
74,
1602,
1148,
1209,
1109
]
}
]
}
bindings: []
extensions:
- id: x-parser-unique-object-id
value: depthData
bindings: []
extensions: *ref_0
- &ref_6
id: streamReceiveError
title: Stream receive error
description: 服务端返回错误
type: send
messages:
- &ref_12
id: errorResponse
contentType: application/json
payload:
- name: 错误消息
description: 操作失败时返回的错误信息
type: object
properties:
- name: op
type: string
description: 消息类型
enumValues:
- error
required: true
- name: message
type: string
description: 错误详情
required: true
headers: []
jsonPayloadSchema:
type: object
additionalProperties: false
properties:
op:
type: string
enum:
- error
description: 消息类型
x-parser-schema-id: <anonymous-schema-47>
message:
type: string
description: 错误详情
x-parser-schema-id: <anonymous-schema-48>
required:
- op
- message
examples:
- op: error
message: 'no permission for channel: depth'
- op: error
message: 'exceeded max 50 symbols (total unique: 53)'
- op: error
message: 'unknown channel: foo'
- op: error
message: 'invalid message: expected value at line 1 column 1'
x-parser-schema-id: <anonymous-schema-46>
title: 错误消息
description: 操作失败时返回的错误信息
example: |-
{
"op": "error",
"message": "no permission for channel: depth"
}
bindings: []
extensions:
- id: x-parser-unique-object-id
value: errorResponse
bindings: []
extensions: *ref_0
sendOperations:
- *ref_1
- *ref_2
receiveOperations:
- *ref_3
- *ref_4
- *ref_5
- *ref_6
sendMessages:
- *ref_7
- *ref_8
receiveMessages:
- *ref_9
- *ref_10
- *ref_11
- *ref_12
extensions:
- id: x-parser-unique-object-id
value: stream
securitySchemes:
- id: apiKey
name: api_key
type: httpApiKey
in: query
extensions: []