在量化交易中,获取准确、及时的行情数据是策略执行的基础。本文将从概念到实操,详细介绍如何接入实时行情API,并给出具体代码示例。 如果您有查询股票、外汇、期货等多市场的需求,这篇内容你一定不能错过。

一、实时行情与延迟行情的区别
在交易系统中,行情数据通常分为两类:实时行情 和延迟行情。
实时行情:
-
数据尽可能接近市场发生的价格变动,延迟通常在毫秒级或秒级。
-
对高频交易和短线策略尤为重要,因为价格的微小波动可能直接影响交易决策。
-
优点:及时、精准;缺点:通常需要付费,并对系统性能有较高要求。
延迟行情:
-
数据通常延迟几分钟到几十分钟,比如免费行情或部分财经网站提供的延迟数据。
-
优点:可免费获取,系统压力小;
-
缺点:对高频或精细策略几乎无用。
二、综合行情接口介绍
在量化交易中,我们可能需要跨市场、跨品种获取行情。传统接口往往局限于单市场或单品种,而综合行情接口可以一次性查询多个市场、多种资产类型的行情数据,包括:
-
A股、港股、美股
-
外汇、加密货币
-
期货等
例如,Infoway API提供的综合行情接口,可以让你在一次请求中获取多市场行情,大大简化数据管理和策略实现。
三、接入实时行情API
以 Infoway API 的批量K线查询接口为例,介绍如何获取实时K线数据。
3.1 HTTP接口请求说明
请求地址模板:
https://data.infoway.io/stock/v2/batch_kline/{klineType}/{klineNum}/{codes}
参数说明:
| 参数 | 类型 | 描述 |
|---|---|---|
klineType |
int | K线周期:1=1分钟, 2=5分钟, 3=15分钟, 4=30分钟, 5=1小时, 6=2小时, 7=4小时, 8=日K, 9=周K, 10=月K, 11=季K, 12=年K |
klineNum |
int | 查询K线数量,单产品最大500根 |
codes |
string | 逗号分隔的股票代码,如 002594.SZ,00285.HK,TSLA.US |
下面是请求示例,我们一次性查询A股、港股和美股的最近10根1分钟K线:
import requests
# 接口URL
api_url = 'https://data.infoway.io/stock/batch_kline/1/10/002594.SZ%2C00285.HK%2CTSLA.US'
# 设置请求头
headers = {
'User-Agent': 'Mozilla/5.0',
'Accept': 'application/json',
'apiKey': 'yourApikey' # API Key申请:www.infoway.io
}
# 发送GET请求
response = requests.get(api_url, headers=headers)
# 输出结果
print(f"HTTP code: {response.status_code}")
print(f"message: {response.text}")
3.2 HTTP返回结果解析
返回示例中,每个s对应一个交易品种,respList为对应的K线数组。你可以通过遍历 respList 获取每根K线的开盘价、收盘价、最高价、最低价及成交量。
{
"ret": 200,
"msg": "success",
"traceId": "19814db2-42f7-4788-9b51-b2001bf17953",
"data": [
{
"s": "TSLA.US",
"respList": [
{
"t": "1751372340",
"h": "298.620",
"o": "298.439",
"l": "298.100",
"c": "298.310",
"v": "24329",
"vw": "7259092.235",
"pc": "-0.02%",
"pca": "-0.070"
},
{
"t": "1751372280",
"h": "298.450",
"o": "298.090",
"l": "298.000",
"c": "298.380",
"v": "32214",
"vw": "9607344.900",
"pc": "0.10%",
"pca": "0.290"
}
]
},
{
"s": "01810.HK",
"respList": [
{
"t": "1751270400",
"h": "59.950",
"o": "59.950",
"l": "59.950",
"c": "59.950",
"v": "23669600",
"vw": "1418992520.000",
"pc": "0.50%",
"pca": "0.300"
},
{
"t": "1751270340",
"h": "59.700",
"o": "59.650",
"l": "59.650",
"c": "59.650",
"v": "829002",
"vw": "49466778.300",
"pc": "-0.08%",
"pca": "-0.050"
}
]
}
]
}
返回字段说明:
| 字段名 | 类型 | 描述 | 示例 |
|---|---|---|---|
t |
string | 成交时间(秒时间戳) | 1751270340 |
h |
string | 最高价 | 18.01 |
o |
string | 开盘价 | 18.01 |
l |
string | 最低价 | 18.01 |
c |
string | 收盘价 | 18.01 |
v |
string | 成交量 | 18000 |
vm |
string | 成交额 | 20000 |
pc |
string | 涨跌幅 | 0.12% |
pca |
string | 涨跌额 | 0.11 |
四、使用WebSocket获取实时行情
在量化交易中,WebSocket是获取高频、低延迟行情的另一种重要方式,相比HTTP轮询有明显优势。
4.1 WebSocket的优势
实时性更强
WebSocket是长连接协议,一旦建立连接,服务器可以主动推送数据,无需客户端频繁请求。 这对高频交易和策略回测实时性要求高的场景尤其重要。
而且WS能减少请求开销,如果使用HTTP请求(一般是轮询发送),需要频繁发送请求,占用带宽并增加延迟。
反观WebSocket只需建立一次连接,即可持续接收数据,节省网络和计算资源。
WS对于多路订阅也方便,在同一连接上可以同时订阅多个品种的成交明细、盘口数据和K线数据,便于统一管理。
4.2 Python示例解析 (WebSocket)
下面的代码示例展示了如何使用WebSocket订阅实时行情数据,包括成交明细、盘口和K线数据。
import json
import time
import schedule
import threading
import websocket
from loguru import logger
class WebsocketExample:
def __init__(self):
self.session = None
self.ws_url = "wss://data.infoway.io/ws?business=crypto&apikey=yourApikey"
self.reconnecting = False
self.is_ws_connected = False # 添加连接状态标志
def connect_all(self):
"""建立WebSocket连接并启动自动重连机制"""
try:
self.connect(self.ws_url)
self.start_reconnection(self.ws_url)
except Exception as e:
logger.error(f"Failed to connect to {self.ws_url}: {str(e)}")
def start_reconnection(self, url):
"""启动定时重连检查"""
def check_connection():
if not self.is_connected():
logger.debug("Reconnection attempt...")
self.connect(url)
# 使用线程定期检查连接状态
schedule.every(10).seconds.do(check_connection)
def run_scheduler():
while True:
schedule.run_pending()
time.sleep(1)
threading.Thread(target=run_scheduler, daemon=True).start()
def is_connected(self):
"""检查WebSocket连接状态"""
return self.session and self.is_ws_connected
def connect(self, url):
"""建立WebSocket连接"""
try:
if self.is_connected():
self.session.close()
self.session = websocket.WebSocketApp(
url,
on_open=self.on_open,
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close
)
# 启动WebSocket连接(非阻塞模式)
threading.Thread(target=self.session.run_forever, daemon=True).start()
except Exception as e:
logger.error(f"Failed to connect to the server: {str(e)}")
def on_open(self, ws):
"""WebSocket连接建立成功后的回调"""
logger.info(f"Connection opened")
self.is_ws_connected = True # 设置连接状态为True
try:
# 发送实时成交明细订阅请求
trade_send_obj = {
"code": 10000,
"trace": "01213e9d-90a0-426e-a380-ebed633cba7a",
"data": {"codes": "BTCUSDT"}
}
self.send_message(trade_send_obj)
# 不同请求之间间隔一段时间
time.sleep(5)
# 发送实时盘口数据订阅请求
depth_send_obj = {
"code": 10003,
"trace": "01213e9d-90a0-426e-a380-ebed633cba7a",
"data": {"codes": "BTCUSDT"}
}
self.send_message(depth_send_obj)
# 不同请求之间间隔一段时间
time.sleep(5)
# 发送实时K线数据订阅请求
kline_data = {
"arr": [
{
"type": 1,
"codes": "BTCUSDT"
}
]
}
kline_send_obj = {
"code": 10006,
"trace": "01213e9d-90a0-426e-a380-ebed633cba7a",
"data": kline_data
}
self.send_message(kline_send_obj)
# 启动定时心跳任务
schedule.every(30).seconds.do(self.ping)
except Exception as e:
logger.error(f"Error sending initial messages: {str(e)}")
def on_message(self, ws, message):
"""接收消息的回调"""
try:
logger.info(f"Message received: {message}")
except Exception as e:
logger.error(f"Error processing message: {str(e)}")
def on_close(self, ws, close_status_code, close_msg):
"""连接关闭的回调"""
logger.info(f"Connection closed: {close_status_code} - {close_msg}")
self.is_ws_connected = False # 设置连接状态为False
def on_error(self, ws, error):
"""错误处理的回调"""
logger.error(f"WebSocket error: {str(error)}")
self.is_ws_connected = False # 发生错误时设置连接状态为False
def send_message(self, message_obj):
"""发送消息到WebSocket服务器"""
if self.is_connected():
try:
self.session.send(json.dumps(message_obj))
except Exception as e:
logger.error(f"Error sending message: {str(e)}")
else:
logger.warning("Cannot send message: Not connected")
def ping(self):
"""发送心跳包"""
ping_obj = {
"code": 10010,
"trace": "01213e9d-90a0-426e-a380-ebed633cba7a"
}
self.send_message(ping_obj)
# 使用示例
if __name__ == "__main__":
ws_client = WebsocketExample()
ws_client.connect_all()
# 保持主线程运行
try:
while True:
schedule.run_pending()
time.sleep(1)
except KeyboardInterrupt:
logger.info("Exiting...")
if ws_client.is_connected():
ws_client.session.close()
核心类 WebsocketExample 的功能包括:
1. 建立连接
connect_all():建立WebSocket连接,并启动定时重连机制。
connect(url):创建 WebSocketApp,绑定回调函数,非阻塞运行。
2. 订阅数据
on_open(ws):连接建立成功后发送订阅请求:
- 实时成交明细
- 实时盘口数据
- 实时K线数据
不同请求之间用 time.sleep() 控制间隔,防止请求过快。
3. 接收数据
on_message(ws, message):回调函数接收服务器推送的消息,并打印日志。
4. 连接管理
is_connected():检查连接状态。
start_reconnection(url):定时检查连接状态,如断开自动重连。
on_error / on_close:处理连接错误或关闭事件,更新状态标志。
5. 心跳机制
ping():定时发送心跳包,防止服务器断开空闲连接。
6. 发送消息
send_message(message_obj):封装JSON消息发送,并处理异常。
4.3 WebSocket使用中的常见问题及解决方案
1. 连接不稳定 / 自动断开
原因:网络波动或服务器空闲断开。
解决:增加心跳机制(如每30秒发送一次ping),并定时检查连接状态,必要时重连。
2. 消息处理阻塞
原因:回调函数执行耗时操作。
解决:在回调中尽量只做消息解析和入队处理,后续数据处理放到单独线程或队列。
3. 订阅请求过快导致失败
原因:一次性发送大量订阅消息。
解决:订阅请求之间加延迟(如 time.sleep(1~5秒)),或分批发送。
4. 网络异常导致发送失败
原因:连接断开但未检测。
解决:在发送前使用 is_connected() 检查状态,如果未连接则先重连。
5. 多线程和调度冲突
原因:同时使用WebSocket线程和定时任务调度器。
解决:保证调度器在单独守护线程中运行,WebSocket也在独立线程运行,避免阻塞主线程。