股票数据接口返回字段逐项解析:从在线验证到 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 返回;判定写成「是否为数值」而不是「是否等于整数」 |
有三点值得单独提醒:
t是字符串,不是时间戳 。日线只有日期、分钟级会带HH:MM:SS,落库前要按级别选格式解析,别用同一套正则硬切。c与pc不能合并。一个周期一条记录里同时出现收盘价与前收盘价,若解析时按「最后一个价格字段」取值,很容易把两者混成一列。- 零值不代表字段失效 。本次 3 条的
sf全是0.0,含义是「不停牌」,是一个有效取值。校验应检查「字段存在且类型正确」,而不是「值是否为真」。
四、接口结构与参数
地址结构如下:
text
https://api.mairuiapi.com/hsstock/history/{股票代码.市场}/{分时级别}/{除权方式}/{licence}?st=&et=<=
| 位置 | 本次取值 | 说明 |
|---|---|---|
| 股票代码.市场 | 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<=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;接口字段含义以当期接口文档定义为准,演示返回的条数与数值是固定样例特征,不是接口契约。文中未核实的业务口径一律按原值透传处理,未做业务解释。
本文只讨论接口返回结构与数据处理,不涉及任何行情判断或投资建议。