A 股实时行情 API 怎么接?一个 ticker 和 kline 不会各自为政的实时行情数据源

摘要:接入行情 API 时,常见风险是不同端点的鉴权、时间字段、返回结构和错误分支需要分别确认——你以为同一个数据源的不同端点应该一致,但实际上可能 ticker 正常返回、kline 鉴权失败,或者两边都返回成功码,timestamp 却是不同精度。本文以 TickDB 的 REST API 作为可复核示例,展示一个 ticker 和 kline 在鉴权和字段类型上保持一致性的数据源长什么样。如果你还没接过行情 API,这篇直接带你用最少代码跑通首次调用;如果你已经在用别的数据源,这篇给你一个可对照的契约一致性检查清单。关键词:A股行情API、ticker、kline、REST API、TickDB、契约一致性。


一、需求边界

在开始写代码之前,先把本文要解决和不解决的问题说清楚。

本文要解决的:用最少代码完成一次 A 股 ticker 快照查询和一次 kline 历史数据查询,并验证两个端点在鉴权、返回结构和关键字段类型上的一致性。跑通这两步,你就有了一个可复用的校验骨架。

本文不解决的:数据库写入、定时调度、断线回补、WebSocket 推送、多市场并行查询、生产级错误重试策略。这些是另一个工程层级的问题,不在本文范围内。

适用读者:第一次接行情 API 的 Python 开发者、需要验证 ticker 和 kline 端点契约一致性的后端工程师、想用最少代码跑通首次调用的量化初学者。


二、配置与环境变量

开始之前,确认三件事:

  • Python 3.8+ 已安装
  • requests 库已安装:pip install requests
  • 环境变量 TICKDB_API_KEY 已设置为你的 API Key
export TICKDB_API_KEY="your_api_key_here"

三、两个端点,可复用的校验骨架

接入行情 API 时,维护成本往往不在第一行代码,而在后续的扩展和排查。ticker 和 kline 如果鉴权方式不同、业务码语义不同、时间戳精度不同,你每多接一个端点就要多维护一套解析逻辑。等到几十个品种的监控脚本跑起来、两边的数据需要合到一起做分析时,设计不一致的代价就会集中爆发。

TickDB 的做法是把两个端点放在同一套规则下。你可以复用同一套鉴权、HTTP/JSON/业务码/空数据、Decimal 和时间字段校验骨架;但 ticker 与 kline 的字段路径和语义必须分别核对——ticker 返回 data[] 中的 last_price,kline 返回 data.klines[] 中的 OHLC,两者不能混用。业务码也需各自检查,不可假设两个端点的错误码语义一致。

下面这张表,既是功能对照,也是契约一致性检查的起点。你可以拿它去对照你正在评估的数据源。

维度 ticker 历史 kline
端点 /v1/market/ticker /v1/market/kline
鉴权 X-API-Key X-API-Key(同一个 Header)
核心参数 symbols symbol + interval
返回路径 data[] data.klines[]
价格字段 last_price open, high, low, close
时间字段 timestamp time(均为毫秒整数)
用途 当前快照 已结束周期的历史数据
成功码 code=0 code=0(需分别检查)

ticker 的 last_price 不是 kline 的 close,两者不能混用。它们的鉴权方式和时间字段精度一致,但业务码必须各自验证,不假设跨端点统一。


四、TickerClient:一个短函数,查 A 股快照

import os
import sys
import requests
from decimal import Decimal, InvalidOperation

API_KEY = os.environ.get("TICKDB_API_KEY")
BASE_URL = "https://api.tickdb.ai/v1"
TIMEOUT = 10

# 教学示例,非生产级代码
# 价格字段值为接口返回值占位说明,不构成实时报价展示


def fetch_ticker(symbols: str):
    """获取 ticker 快照并校验关键字段。"""
    if not API_KEY:
        print("错误: 环境变量 TICKDB_API_KEY 未设置", file=sys.stderr)
        sys.exit(1)

    try:
        resp = requests.get(
            f"{BASE_URL}/market/ticker",
            headers={"X-API-Key": API_KEY},
            params={"symbols": symbols},
            timeout=TIMEOUT,
        )
        resp.raise_for_status()
    except requests.exceptions.ConnectionError as e:
        sys.exit(f"连接失败: {e}")
    except requests.exceptions.Timeout as e:
        sys.exit(f"请求超时: {e}")
    except requests.exceptions.HTTPError as e:
        sys.exit(f"HTTP 错误: {e}")
    except requests.exceptions.RequestException as e:
        sys.exit(f"请求异常: {e}")

    try:
        data = resp.json()
    except ValueError as e:
        sys.exit(f"JSON 解析失败: {e}")

    if data.get("code") != 0:
        sys.exit(f"业务失败: code={data.get('code')}")

    ticker_list = data.get("data", [])
    if not isinstance(ticker_list, list) or not ticker_list:
        sys.exit("ticker data 为空")

    for item in ticker_list:
        sym = item.get("symbol")
        if sym != symbols:
            sys.exit(f"symbol 不匹配: 期望 {symbols}, 返回 {sym}")

        raw_price = item.get("last_price")
        if not isinstance(raw_price, str) or not raw_price:
            sys.exit("last_price 缺失或为空")
        try:
            price = Decimal(raw_price)
        except InvalidOperation:
            sys.exit(f"last_price 无法解析: {raw_price}")
        if not price.is_finite():
            sys.exit(f"last_price 非有限数: {raw_price}")

        ts = item.get("timestamp")
        if isinstance(ts, bool) or not isinstance(ts, int) or ts <= 0:
            sys.exit(f"timestamp 无效: {ts}")

        return {"symbol": sym, "last_price": price, "timestamp": ts}


# 示例:查询 A 股快照
result = fetch_ticker("600519.SH")
print(f"ticker: {result}")

核心逻辑:发请求 → 检查 HTTP → 检查业务码 → 检查 data 非空 → 核对 symbol → 校验 last_price(Decimal 解析,非 NaN/Infinity)→ 校验 timestamp(正整数,排除 bool)。任何一步失败都非零退出,不补默认值。


五、KlineClient:一个短函数,查 A 股日线

def fetch_kline(symbol: str, interval: str, limit: int = 5):
    """获取历史 K 线并校验关键字段。"""
    if not API_KEY:
        print("错误: 环境变量 TICKDB_API_KEY 未设置", file=sys.stderr)
        sys.exit(1)

    try:
        resp = requests.get(
            f"{BASE_URL}/market/kline",
            headers={"X-API-Key": API_KEY},
            params={
                "symbol": symbol,
                "interval": interval,
                "limit": limit,
            },
            timeout=TIMEOUT,
        )
        resp.raise_for_status()
    except requests.exceptions.ConnectionError as e:
        sys.exit(f"连接失败: {e}")
    except requests.exceptions.Timeout as e:
        sys.exit(f"请求超时: {e}")
    except requests.exceptions.HTTPError as e:
        sys.exit(f"HTTP 错误: {e}")
    except requests.exceptions.RequestException as e:
        sys.exit(f"请求异常: {e}")

    try:
        data = resp.json()
    except ValueError as e:
        sys.exit(f"JSON 解析失败: {e}")

    if data.get("code") != 0:
        sys.exit(f"业务失败: code={data.get('code')}")

    kline_data = data.get("data", {})
    if not isinstance(kline_data, dict):
        sys.exit("kline data 格式异常")

    klines = kline_data.get("klines", [])
    if not isinstance(klines, list) or not klines:
        sys.exit("kline klines 为空")

    for bar in klines:
        raw_time = bar.get("time")
        if isinstance(raw_time, bool) or not isinstance(raw_time, int) or raw_time <= 0:
            sys.exit(f"kline time 无效: {raw_time}")

        for field in ("open", "high", "low", "close", "volume", "quote_volume"):
            raw_val = bar.get(field)
            if not isinstance(raw_val, str) or not raw_val:
                sys.exit(f"kline {field} 缺失或为空")
            try:
                val = Decimal(raw_val)
            except InvalidOperation:
                sys.exit(f"kline {field} 无法解析: {raw_val}")
            if not val.is_finite():
                sys.exit(f"kline {field} 非有限数: {raw_val}")

    return {"symbol": symbol, "interval": interval, "bars": len(klines)}


# 示例:查询 A 股日线,最近 5 根
result = fetch_kline("600519.SH", "1d", limit=5)
print(f"kline: {result}")

和 ticker 用的是同一个 BASE_URL、同一个 Header;业务码需分别检查,时间字段精度一致。 鉴权、HTTP/JSON/业务码/空数据、Decimal 和时间字段的校验骨架可以复用;但 ticker 读 data[] 中的 last_price,kline 读 data.klines[] 中的 OHLC——字段路径和语义必须分别核对。


六、返回契约对照

跑通上面两端代码后,你会得到两种不同的返回结构。它们的共同点和差异点如下:

共同点

  • 同一个 BASE_URL 和 X-API-Key Header
  • 业务成功码均为 code=0(但需各自检查)
  • 价格字段均为字符串,需要用 Decimal 解析
  • 时间字段均为毫秒整数

差异点

  • ticker 参数为 symbols(复数),kline 为 symbol(单数)
  • ticker 返回 data[] 数组,kline 返回 data.klines[] 数组
  • ticker 的价格字段是 last_price,kline 是 open/high/low/close
  • ticker 的时间字段叫 timestamp,kline 叫 time

两个端点的字段名和返回路径不同,不能交叉取数。契约一致性不等于字段名完全一样——一致的是鉴权方式、数据类型和校验规则。


七、最小异常策略

本文代码采用“失败即退出”的策略:任何校验步骤不通过,程序以非零状态码退出,不补默认值,不跳过错误行,不静默降级。具体处理以下六类异常:

异常类型 处理方式
网络错误(连接失败、超时) 打印错误信息,sys.exit(1)
HTTP 错误(4xx/5xx) 打印 HTTP 状态码,sys.exit(1)
JSON 解析失败 打印解析错误,sys.exit(1)
业务码非零 打印业务码,sys.exit(1)
字段缺失或类型错误 打印具体字段和实际类型,sys.exit(1)
数值解析失败或非有限数 打印原始值和错误原因,sys.exit(1)

这是一种“最小可行”的异常策略——适合首次接入和本地验证。生产环境中你可能需要更细粒度的错误分类(可重试 vs 不可重试)、日志记录和告警机制,但那是建立在本文校验骨架之上的第二层工作。


八、为什么本文不处理数据库、调度和回补

这三个问题是行情数据接入的常见延伸,但各自属于不同的工程层级:

  • 数据库写入:涉及 schema 设计、批量插入策略、主键冲突处理。本文只负责把数据校验到“可以写入”的程度——通过了文中校验的数据,写入数据库时不会再因为字段类型或空值问题报错。
  • 定时调度:涉及 cron 表达式、任务队列、失败重试策略。本文的 fetch_tickerfetch_kline 是纯函数,可以被任何调度器直接调用。
  • 断线回补:涉及 WebSocket 连接管理、空窗检测、REST 补数逻辑。本文只处理 REST 的请求-响应模式,不涉及长连接和流式数据。

把这三个问题拆开,不是因为它们不重要,而是因为一次只解决一个问题,代码才可测试、可复用、可排错。


九、本文没有证明什么

  • ticker 和 kline 各成功一次,不证明所有 A 股、所有端点、所有 interval 均可用。
  • 不证明低延迟、SLA、高频交易适用性、生产稳定性或持续推送能力。
  • 本文不验证 WebSocket、MCP、CLI、Skill 的接口行为。
  • 文中价格字段为接口返回值占位说明,不构成实时报价展示。
  • 本文不构成投资建议。

你现在的数据源,ticker 和 kline 的字段契约是否分别核对?有没有遇到过 ticker 正常、kline 鉴权失败,或者两边 timestamp 精度不一样的情况?评论区说说——一个端点、一个字段名、一个不一致的细节,就够了。

想用你自己的 symbol 跑一次 ticker 和 kline 的最小调用,可查看 TickDB 官方文档和 GitHub 示例,并使用你自己的 Key 或测试环境做一次契约一致性检查。


标签:A股行情API / ticker / kline / REST API / TickDB / 契约一致性

posted @ 2026-06-16 12:03  Srena量化员  阅读(17)  评论(0)    收藏  举报