金融数据实时行情API使用教程:如何跨市场查询多品种的实时行情数据

在量化交易中,获取准确、及时的行情数据是策略执行的基础。本文将从概念到实操,详细介绍如何接入实时行情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也在独立线程运行,避免阻塞主线程。

相关推荐
让学习成为一种生活方式2 小时前
如何根据过滤的pep序列进一步过滤gff3文件--python015
开发语言·人工智能·python
belldeep2 小时前
金融科技在风险管理中的运用
科技·金融·风险·金融科技
qijiabao41132 小时前
深度学习|可变形卷积DCNv3编译安装
人工智能·python·深度学习·机器学习·cuda
m5655bj2 小时前
通过 Python 提取 PDF 表格数据
服务器·python·pdf
玄同7652 小时前
面向对象编程 vs 其他编程范式:LLM 开发该选哪种?
大数据·开发语言·前端·人工智能·python·自然语言处理·知识图谱
南_山无梅落2 小时前
PyCharm 安装了库却无法 Alt + Enter 导入?(简洁排查版)
ide·python·pycharm·虚拟环境·alt·enter·.venv
ID_180079054732 小时前
Python采集闲鱼商品详情API:JSON数据解析与应用实践
数据库·python·json
APIshop2 小时前
API 接口文档测试:从“能跑”到“敢上线”的完整闭环
爬虫·python
AndrewHZ2 小时前
【复杂网络分析】如何入门Louvain算法?
python·算法·复杂网络·社区发现·community det·louvain算法·图挖掘