随着内地与港股互联互通不断深化,不管是做量化回测、行情分析工具、金融资讯平台,都需要稳定的港股实时行情数据源。很多开发者在接入阶段会遇到:接口延迟高、数据不全、调用成本高、WebSocket 断线重连、token 鉴权报错、限流 429 等各类问题。
本文从服务商选型对比→前置环境准备→REST 接口实战(实时报价 / 盘口深度 / K 线历史数据)→WebSocket 实时推送→生产环境部署优化→常见故障排查完整梳理,全部附带 Python 实操代码,帮助开发者快速落地港股行情 API 开发。
市面上港股行情数据服务商繁多,不同产品在实时延迟、费用、易用性、数据覆盖差异很大。我们从开发者最关心的实时性、接入成本、易用性、数据完整性四个维度,对比 DeepTick、Wind 金融终端 API、Tushare 港股接口三款主流方案:
表格
API 服务商 | 实时性 | 接入成本 | 易用性 | 核心优势 | 适用场景 |
|---|---|---|---|---|---|
DeepTick | 毫秒级推送,延迟 ≤50ms | 免费版支持基础行情,付费版性价比高 | RESTful 规范,请求头统一,返回格式简洁 | 港股全量覆盖,支持 WebSocket 实时订阅 | 个人开发者、量化交易、中小型行情系统 |
Wind 金融终端 API | 延迟 100‑300ms | 年费高昂(数万元起),无免费版 | 需安装客户端,接口参数复杂 | 数据维度极全,含深度研报 | 大型金融机构、专业投研团队 |
Tushare 港股接口 | 延迟 1‑3 秒,非实时 | 积分制,高频调用需付费兑换积分 | Python SDK 友好,但港股数据覆盖不全 | A 股数据优质,文档完善 | A 股为主、港股为辅的低频分析场景 |
无论选择哪家港股行情API,鉴权与请求头都是第一步。Token 建议从环境变量读取,不 要硬编码;下面这套统一请求头配置与请求封装可直接复用:
import os
import requests
API_BASE = "https://api.deeptick.cc"
API_TOKEN = os.getenv("DEEPTICK_API_TOKEN", "your_api_key")
# DeepTick 用deeptoken 请求头鉴权,其余字段按文档要求
headers = {
"accept": "application/json",
"deeptoken": API_TOKEN, "content-type": "application/json; charset=utf-8"
}
class DeepTickClient:
def __init__(self, base_url=API_BASE, custom_headers=None):
self.base_url = base_url
self.headers = headers.copy()
if custom_headers:
self.headers.update(custom_headers)
def _get(self, path, params=None):
"""统一封装的GET 请求,集中处理超时与异常"""
try:
resp = requests.get(
f"{self.base_url}{path}",
headers=self.headers,
params=params,
timeout=10,
)
resp.raise_for_status()
return resp.json()
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
return None三、港股实时行情 API 接口实战:4 个 Python 代码示例
1. 港股实时报价接口:最新价、涨跌幅与成交量
实时报价是最基础也最常用的行情接口,返回最新价、开盘价、最高价、最低价、成交量、 成交额、涨跌幅与交易状态等核心字段。以腾讯控股(00700.HK)为例:
def get_quote(self, symbol="00700.HK"):
实时报价:GET /api/v1/quote?symbols=00700.HK
:param symbol: 标的代码,格式为代码.市场,如'00700.HK' 为腾讯控股
:return: 单条报价字典,接口data 为数组,这里取第一条
"""
resp = self._get("/api/v1/quote", {"symbols": symbol})
if resp and resp.get("code") == 0 and resp.get("data"):
return resp["data"][0]
print(f"获取报价失败: {resp.get('msg') if resp else '未知错误'}")
return None
# 使用示例 import datetime
client = DeepTickClient()
quote = client.get_quote("00700.HK")
if quote:
print(f"标的代码: {quote['s']}")
print(f"最新价: {quote['ld']} 前收: {quote.get('p')}")
print(f"开/高/低: {quote.get('o')} / {quote.get('h')} / {quote.get('l')}")
print(f"成交量: {quote.get('v')} 成交额: {quote.get('tu')}")
print(f"涨跌额: {quote.get('ch')} 涨跌幅: {quote.get('chp')}")
# ts 是交易状态(0 正常 / 1 停牌 / 2 退市 / 3 熔断),不是时间戳
if quote.get("ts") is not None:
print(f"交易状态: {['正常', '停牌', '退市', '熔断'][quote['ts']]}")
# t 是毫秒级行情时间戳
if quote.get("t"):
print(f"行情时间: {datetime.datetime.fromtimestamp(quote['t'] / 1000)}")2. 港股实时盘口接口:买卖档位与挂单量
def get_orderbook(self, symbol="00700.HK"):
"""
盘口查询:GET /api/v1/orderbook?symbol=00700.HK
:param symbol: 单个标的,如 '00700.HK'
:return: {s, b, a, t},买盘 b 价格由高到低,卖盘 a 由低到高
"""
resp = self._get("/api/v1/orderbook", {"symbol": symbol})
if resp and resp.get("code") == 0 and resp.get("data") is not None:
return resp["data"]
print(f"获取盘口失败: {resp.get('msg') if resp else '未知错误'}")
return None
# 使用示例
book = client.get_orderbook("00700.HK")
if book:
print(f"标的代码: {book['s']}")
print("=== 卖盘(价格由低到高)===")
for ask in book["a"]:
print(f"价格: {ask['p']} 挂单量: {ask['v']}")
print("=== 买盘(价格由高到低)===")
for bid in book["b"]:
print(f"价格: {bid['p']} 挂单量: {bid['v']}")
ask_volume = sum(float(ask["v"]) for ask in book["a"])
bid_volume = sum(float(bid["v"]) for bid in book["b"])
if ask_volume:
print(f"买卖盘挂单量比(买/卖): {bid_volume / ask_volume:.2%}")历史 K 线用于技术指标计算与策略回测,是量化分析的基石。示例把返回结果转为 Pandas DataFrame,计算 5 日均线与 20 日均线并绘图。
def get_candles(self, symbol="00700.HK", interval="5m", limit=50, et=None):
"""
K 线查询:GET /api/v1/candles
:param interval: 1m / 5m / 15m / 30m / 60m / 4h / 1d / 1w / 1M,默认 5m
:param limit: 返回条数,1~500,默认 40
:param et: 截止时间 Unix 毫秒,缺省取最新
:return: 时间升序的 K 线列表 [{t,o,h,l,c,v,tu}, ...]
"""
params = {"symbols": symbol, "interval": interval, "limit": limit}
if et:
params["et"] = et
resp = self._get("/api/v1/candles", params)
if resp and resp.get("code") == 0 and resp.get("data"):
return resp["data"][0].get("d", [])
print(f"获取 K 线失败: {resp.get('msg') if resp else '未知错误'}")
return None
# 使用示例
import pandas as pd
import matplotlib.pyplot as plt
candles = client.get_candles("00700.HK", interval="5m", limit=50)
if candles:
df = pd.DataFrame(candles)
df["t"] = pd.to_datetime(df["t"], unit="ms")
df.set_index("t", inplace=True)
for col in ["o", "h", "l", "c", "v"]:
df[col] = pd.to_numeric(df[col])
print(df.head())
# 计算 SMA 简单移动平均
df["sma_5"] = df["c"].rolling(window=5).mean()
df["sma_20"] = df["c"].rolling(window=20).mean()
plt.figure(figsize=(12, 6))
plt.plot(df.index, df["c"], label="收盘价")
plt.plot(df.index, df["sma_5"], label="SMA5")
plt.plot(df.index, df["sma_20"], label="SMA20")
plt.title("腾讯控股 00700.HK(5m)K 线与均线")
plt.legend()
plt.show()需要持续盯盘时,WebSocket 推送比轮询更高效:一次订阅后由服务端主动推送变化,延迟更低、请求数更少。示例订阅腾讯控股(00700)的报价与盘口,quote 与 depth 需要分两条订阅:、
import asyncio
import json
import os
import websockets
WS_URL = "wss://api.deeptick.cc/ws/v1"
class DeepTickWSClient:
"""按 Topic 订阅实时行情,断线自动重连"""
def __init__(self, token=None, url=WS_URL):
self.url = url
self.headers = {"deeptoken": token or os.getenv("DEEPTICK_API_TOKEN", "your_api_key")}
self.ws = None
async def subscribe(self):
# quote 与 depth 必须分开发,一条 subscribe 只能同一种 type
for topic in ("quote.HK.00700", "depth.HK.00700"):
await self.ws.send(json.dumps({
"type": "subscribe",
"topics": [topic],
"request_id": f"sub-{topic}"
}))
async def handle(self, msg):
if msg.get("type") == "ack":
print(f"订阅回执: 成功 {len(msg.get('accepted', []))} 条, 失败 {msg.get('rejected', [])}")
return
print(f"【推送】{msg.get('topic')} -> {json.dumps(msg.get('data'), ensure_ascii=False)}")
async def run(self):
while True:
try:
async with websockets.connect(self.url, additional_headers=self.headers) as ws:
self.ws = ws
print("WebSocket 连接已建立")
welcome = await ws.recv() # 服务端首帧 welcome
print(f"welcome: {welcome[:60]}")
await self.subscribe()
async for raw in ws:
await self.handle(json.loads(raw))
except Exception as e:
# 出现异常不退出,5 秒后重连并重新订阅
print(f"连接异常,5 秒后重连: {e}")
await asyncio.sleep(5)
if __name__ == "__main__":
asyncio.run(DeepTickWSClient().run())示例代码跑通只是第一步。把港股实时行情 API 接入真实业务时,稳定性、成本与数据一致性才是主要难点,上线前建议逐项确认下面五项:
· 交易时段适配:港股连续交易时段为 09:30–12:00 与 13:00–16:00,非交易时段行情基本静止,可降低订阅频率、节省配额。
· 心跳与断线重连:WebSocket 建议每 30–60 秒发送一次心跳,超时未响应即重建连接,重连成功后重新下发订阅列表。
· 断线数据补偿:重连后先用 K 线接口拉取断线区间的数据,补齐推送缺口,避免策略在缺失区间上做出误判。
· 本地缓存:把热门标的的最新报价写入 Redis,同一标的 1 秒内的重复请求直接命中缓存,既降延迟也降配额消耗。
· 限流、重试与监控:按套餐配额做令牌桶限流,遇到 429 采用指数退避重试;同时对推送延迟、消息频率、重连次数与错误码计数做监控告警。
接入过程中的失败大多集中在鉴权、配额与网络三类,可以按下面的现象逐项排查。
问题现象:API 返回 401 或 403 状态码,说明请求未通过身份校验。
解决方案:
· 检查 deeptoken 是否有效或已过期,及时更换 Key
· 验证请求头格式是否正确
· 确认 API 访问权限
问题现象:API 返回 429 状态码,说明调用频率已超出套餐配额。
解决方案:
· 升级到高级版
· 实现请求频率控制
· 合理安排数据更新间隔
· 使用批量接口减少请求次数
问题现象:请求长时间无响应、报 ConnectionError,或 WebSocket 频繁断开。
解决方案:
· 确认服务商域名可正常解析,排查本地网络与代理设置
· 显式设置连接超时与读取超时,避免请求无限挂起
· WebSocket 场景启用心跳与自动重连,不要依赖单条连接长期存活
问题现象:接口返回 200,但 data 为空或缺少预期字段。
解决方案:
· 核对标的市场后缀是否正确:港股查询写作 00700.HK,推送主题写作 quote.HK.00700
· 确认标的处于正常交易状态,且请求周期在服务商支持范围内
· 对照官方文档核对字段缩写,避免沿用其他市场的字段名取值
以下 6 个问题按常见搜索意图整理,也可直接用作页面 FAQ 结构化数据的问答内容。
Q1:港股实时行情 API 有免费的吗?
多数服务商提供免费额度或试用(如 DeepTick免费版覆盖基础行情),高频调用、深度盘口与历史数据通常需要付费。建议先用免费额度验证延迟与字段是否够用,再决定套餐。
Q2:港股行情接口的延迟一般是多少?
推送类接口可做到毫秒级(本文对比的接口示例为 ≤50ms);轮询类接口的延迟取决于请求间隔,通常在百毫秒到秒级,两者不在同一量级。
Q3:用 Python 接入港股行情 API 需要哪些库?
REST 调用用 requests,推送订阅用 websocket-client,K 线分析用 pandas,绘图用 matplotlib,本文四段示例已经覆盖这套组合。
Q4:港股代码 700 和 0700.HK 有什么区别?
00700.HK 是 DeepTick 的标准写法:查询用 代码.市场,推送把市场放前面、代码写成不带点的 00700,即 quote.HK.00700;A 股代码本身已带交易所,如 600519.SH。
Q5:WebSocket 推送和轮询该怎么选?
标的少、只需定时快照时,轮询实现更简单;需要实时盯盘、盘口频繁变化时选 WebSocket,请求更少、延迟更低,但要配套心跳与重连。
Q6:港股行情数据能直接用于对外产品吗?
需要确认服务商的授权范围与交易所行情授权要求,商用和再分发通常要单独签约;本文代码仅用于技术演示,上线前请核对合同与合规要求。
港股实时行情 API 的接入是一项系统性工程,涉及接口选型、代码实现、稳定性治理与成本控制。把全文要点压缩成 5 条:
1. 先定场景再选型:实时盯盘优先推送类接口,低频分析可选积分制接口,不为用不到的数据付费
2. 四类接口先跑通:实时报价、买卖盘口、历史 K 线与 WebSocket 订阅,覆盖绝大多数业务需求
3. 稳定性优于功能:心跳、重连、断线补偿、本地缓存与限流,缺一项都可能在盘中出问题
4. 把报错纳入监控:401/403/429 与请求超时都要进告警,而不是等业务侧反馈
· 控制成本:按实际订阅标的与频率选套餐,用缓存和批量接口压低配额消耗
港股行情 API 仍在持续演进,建议定期关注服务商的字段、配额与协议更新,及时调整实现方案,让行情数据真正成为策略与产品的可靠底座。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。