股票数据接口返回字段逐项解析:从在线验证到 Python 数据校验

股票数据接口返回字段逐项解析:从在线验证到 Python 数据校验

一、问题:拿到返回之后,第一件事不是画图

做行情数据处理时,很多人第一步卡在「接口返回了,但不敢直接用」。常见三类情况:

  • 把数组当对象,data["o"] 直接报错;
  • 短名字段对不上含义,把前收盘价 pc 当成收盘价 c 写进同一列;
  • 某些字段整批为零,用它做除法或聚合,结果全是 0,却以为是自己代码写错了。

本文用一条可以直接点开的历史分时接口返回,把 9 个字段逐项拆开,再给一段可运行的校验代码。重点只在数据获取与解析,不涉及任何行情判断。

二、先看结果:一条可以在线验证的返回

下面这条地址可以直接点开,浏览器会显示接口实际返回的 JSON:

api.mairuiapi.com/hsstock/his...

紧跟一句说明它是什么:hsstock/history 是历史分时路径;000001.SZ 是「股票代码.市场」;d 是日线级别,n 是不复权口径;最后一段是演示证书;查询参数 st、et 是起止时间(格式 YYYYMMDD),lt 指定取最新的条数。本次 lt=3,返回 3 条,按交易时间升序排列。

完整返回如下(原样截取):

json 复制代码
[
  {"t":"2025-11-27","o":11.66,"h":11.71,"l":11.58,"c":11.71,"v":1011847.0,"a":1178308953.0,"pc":11.69,"sf":0.0},
  {"t":"2025-11-28","o":11.67,"h":11.68,"l":11.58,"c":11.61,"v":884358.0,"a":1027933463.0,"pc":11.71,"sf":0.0},
  {"t":"2025-12-01","o":11.6,"h":11.7,"l":11.53,"c":11.69,"v":1037323.0,"a":1205480322.0,"pc":11.61,"sf":0.0}
]

先记住两件事:顶层是 [ 开头的数组,不是单个对象;每条记录是一个交易周期,字段名全是短名。把这个形态看清之后,再逐项拆字段,比拿到数据就写遍历要省很多返工。

三、返回字段逐项说明

本次返回每条记录 9 个字段。下表按接口文档的字段定义列出,实测值取上面返回的首条记录:

字段 实测类型 含义(接口文档定义) 实测首条值 工程处理说明
t str 交易时间 "2025-11-27" 日线级别为纯日期,分钟级别带时分秒;按字符串接收,不要直接当时间戳
o float 开盘价 11.66 浮点,保留 float / DECIMAL
h float 最高价 11.71 浮点
l float 最低价 11.58 浮点
c float 收盘价 11.71 浮点
v float 成交量 1011847.0 实测为带 .0 的浮点;单位口径以正式数据的接口文档为准,不从样例反推
a float 成交额 1178308953.0 十位数量级,保留双精度,避免单精度丢位
pc float 前收盘价 11.69 与 c 是不同语义,落库时分开命名,不要覆盖
sf 数值 停牌标记:1 停牌、0 不停牌 0.0 实测以浮点 0.0 返回;判定写成「是否为数值」而不是「是否等于整数」

有三点值得单独提醒:

  1. t 是字符串,不是时间戳 。日线只有日期、分钟级会带 HH:MM:SS,落库前要按级别选格式解析,别用同一套正则硬切。
  2. c 与 pc 不能合并。一个周期一条记录里同时出现收盘价与前收盘价,若解析时按「最后一个价格字段」取值,很容易把两者混成一列。
  3. 零值不代表字段失效 。本次 3 条的 sf 全是 0.0,含义是「不停牌」,是一个有效取值。校验应检查「字段存在且类型正确」,而不是「值是否为真」。

四、接口结构与参数

地址结构如下:

text 复制代码
https://api.mairuiapi.com/hsstock/history/{股票代码.市场}/{分时级别}/{除权方式}/{licence}?st=&et=&lt=
位置 本次取值 说明
股票代码.市场 000001.SZ 六位代码加市场后缀
分时级别 d 支持 5、15、30、60、d、w、m、y
除权方式 n 日线以上支持 n、f、b、fr、br;分钟级别按文档只用 n
licence 演示证书 放在路径最后一段,不是查询参数
st / et 20250101 / 20251231 起止时间,YYYYMMDD 或 YYYYMMDDhhmmss;不设置则取全部历史
lt 3 取最新的 N 条

请求方法为 GET。这里要特别注意「股票代码.市场」这一段:不同接口对代码的写法要求不同,历史分时路径需要带市场后缀,而实时行情路径用的是六位代码。把两段路径封装进同一个函数时,建议分别构造,不要共用一个 code 字符串直接拼接。

另外,lt 指定的是条数而不是日期区间裁剪条件。实测 lt=3 返回 3 条,不带 lt 时同一区间返回 50 条;返回条数一律以实际响应的长度为准,不要把「请求了几条」写成校验规则里的常量。

五、代码实现:拉取 + 字段校验

下面这段只依赖 Python 标准库,完成三件事:确认顶层形态、检查 9 个字段是否齐全、校验 OHLC 逻辑与时间顺序。

python 复制代码
import json
from datetime import datetime
from urllib.request import Request, urlopen

LICENCE = "LICENCE-8003-6DCF-E0C78035A455"
URL = ("https://api.mairuiapi.com/hsstock/history/000001.SZ/d/n/"
       + LICENCE + "?st=20250101&et=20251231&lt=3")
FIELDS = ("t", "o", "h", "l", "c", "v", "a", "pc", "sf")


def fetch(url):
    req = Request(url, headers={"User-Agent": "Mozilla/5.0"})
    with urlopen(req, timeout=20) as resp:
        return resp.status, json.loads(resp.read().decode("utf-8"))


def check(bars):
    if not isinstance(bars, list):
        raise TypeError("expect list, got %s" % type(bars).__name__)
    if not bars:
        raise ValueError("empty rows")
    missing = [k for k in FIELDS if k not in bars[0]]
    bad_ohlc = [b["t"] for b in bars
                if not (b["l"] <= b["o"] <= b["h"]
                        and b["l"] <= b["c"] <= b["h"])]
    dates = [datetime.strptime(b["t"], "%Y-%m-%d") for b in bars]
    return missing, bad_ohlc, dates == sorted(dates)


status, bars = fetch(URL)
missing, bad_ohlc, ascending = check(bars)
print("HTTP %d  type=list  rows=%d  fields=%d" % (status, len(bars), len(bars[0])))
print("first:", json.dumps(bars[0], ensure_ascii=False, sort_keys=True))
print("missing=%s  ohlc_bad=%s  ascending=%s" % (missing, bad_ohlc, ascending))
print("t types:", sorted({type(b["t"]).__name__ for b in bars}),
      " sf values:", sorted({str(b["sf"]) for b in bars}))

实测运行输出(原样):

text 复制代码
HTTP 200  type=list  rows=3  fields=9
first: {"a": 1178308953.0, "c": 11.71, "h": 11.71, "l": 11.58, "o": 11.66, "pc": 11.69, "sf": 0.0, "t": "2025-11-27", "v": 1011847.0}
missing=[]  ohlc_bad=[]  ascending=True
t types: ['str']  sf values: ['0.0']

check() 把三件事分开做,是有意的:先判容器、再查字段、最后查业务约束 。OHLC 那条检查(l <= o <= h 且 l <= c <= h)在本次 3 条上全部通过,成本很低却能挡住大部分错位映射;dates == sorted(dates) 则确认返回确实按时间升序,后续做「取最近 N 条」时才不用自己重排。

六、演示证书的行为说明

再点开下面这条,只把路径里的代码从 000001.SZ 换成 600519.SH:

api.mairuiapi.com/hsstock/his...

把两条返回放在一起比对,实测结果是逐字节相同 :字段集合、数值、时间字段都一致,并不会因为代码换了就变成另一只标的的行情。把级别换成 w、m、5,或换成实时行情路径做同样的对照,结果也是逐字节相同。

原因是示例使用的是演示证书,服务端固定返回 000001 的样例数据,因此替换请求中的股票代码不会改变返回结果。演示证书的作用只有两个:确认接口连通、看清返回字段结构。若需要验证其他标的的真实数据,需要在请求中使用自己的 licence 参数。

这也意味着本文表里的数值只能用于理解字段结构,不能当作某个标的真实数值序列使用。

七、常见问题与踩坑

把数组当对象。 bars["o"] 会直接抛 TypeError。进入解析前用 isinstance(bars, list) 判断一次,再取 bars[0]。

用严格白名单丢字段。 有的字段表只列了常见的 OHLC 与成交量,实际返回还带 a、pc、sf。宽松解析(保留原字段、按需取用)比严格白名单更稳。

把 pc 当 c。 两个字段在同一条记录里同时出现,名字接近但语义不同。落库时显式改名,别依赖字段顺序取值。

把零值当缺失。 sf 为 0.0 表示不停牌,a 也可能在非交易时段为 0。校验写「键存在且类型正确」,不要写 if not value。

用单精度存成交额。 本次 a 是 1178308953.0 这种十位数量级,转 float32 会丢位,落库用 REAL / DECIMAL 并显式声明列类型。

按请求参数推算行数。 lt=3 返回 3 条、不带 lt 返回 50 条,是本次演示返回的表现,不是接口契约。行数一律取 len(bars),循环只遍历实际拿到的记录。

靠样例反推单位。 接口文档标注 v 为成交量,但演示返回里的数值较大;单位口径以正式数据的接口文档为准,本文不对演示样例做「手 / 股」换算,避免在错误的基数上继续推导。

把不同级别的时间格式混进一列。 日线 t 是 2025-11-27,分钟级是 2025-12-01 14:50:00。建议保留原始字符串一列,另存一列解析后的时间。

八、小结与数据来源说明

本文只做了一件事:把一条历史分时接口的返回逐字段拆开,给出校验代码和几个实测踩坑。核心结论是------先确认返回形态与字段口径,再写业务逻辑,比拿到数据就画图省掉很多返工。

本文所有返回样例、字段类型、数值与代码输出均来自对上述公开接口地址的实际请求,采集时间为 2026-11-12;接口字段含义以当期接口文档定义为准,演示返回的条数与数值是固定样例特征,不是接口契约。文中未核实的业务口径一律按原值透传处理,未做业务解释。

本文只讨论接口返回结构与数据处理,不涉及任何行情判断或投资建议。

相关推荐
Dmz丶1 小时前
前端转 AI 100 天 Day 42:跨模型对比实战——用数据选模型,不靠“听说”
python
路溪非溪1 小时前
python标准库使用总结
开发语言·python
青少儿编程课堂2 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级:折半搜索优化子集枚举(Meet in the Middle)解析
c++·python·算法·bfs·信息学竞赛
用户956256254292 小时前
五分钟用 E2B SDK 打通沙箱:环境变量只改三行
python·api
用户95179422510293 小时前
最小循环:让 Agent 跑起来(I1)
python·agent
沉默小管3 小时前
上班鼓励器_桌宠版
python
SomeOtherTime3 小时前
Rust 面向对象介绍(AI回答)
开发语言·python·rust
duansamve3 小时前
Flask中视图函数接收请求数据和返回响应实例
python·flask
用户298698530144 小时前
用 Python 自动化把 JavaScript 代码转换为 Word 文档
javascript·python·api