股票历史数据的复权参数对照:多级别返回的字段与数值差异

股票历史数据的复权参数对照:多级别返回的字段与数值差异

一、问题:只换一个参数段,价格就差了两个数量级

历史分时接口的路径里有一段「除权方式」。很多人默认它只是同一个序列的展示选项,于是把不同参数下返回的 c 直接拼进同一张表做比较。实际请求下来会发现:n 下日线收盘价是 11.71,b 下同一天的收盘价是 1197.96,相差两个数量级,而 v 与 a 却完全不变。

如果落库时没把参数写进元数据,后续按同一个阈值过滤价格,会把一半记录判成异常;如果解析层默认所有参数组合都会返回 JSON 数组,遇到一个无效参数就会在 json.loads 那一行直接抛错。

本文用一组真实请求,把五种除权方式的返回字段与数值差异并列摆出来,给出一段能同时处理正常数组与 404 纯文本的解析代码,并说明不同分时级别下这个参数该怎么取。级别与除权方式的取值定义以当期接口文档为准,本文只对可观测的返回结构与数值做记录,不涉及行情判断。

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

下面几条地址可以直接点开对比,区别只在路径中的「除权方式」这一段:

紧跟一句说明:hsstock/history 是历史分时路径,000001.SZ 是「股票代码.市场」,d 是日线级别,倒数第二段是除权方式,最后一段是演示证书;st、et 是起止时间,lt=3 指定取最新 3 条。五条请求都返回数组,字段集合一致,只有价格类字段的数值不同。

n 数组首条的实测内容(原样):

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}

b 数组首条的实测内容(原样):

json 复制代码
{"t":"2025-11-27","o":1193.91,"h":1197.96,"l":1187.45,"c":1197.96,"v":1011847.0,"a":1178308953.0,"pc":1196.34,"sf":0.0}

对照看得很清楚:t、v、a、sf 逐字段相同,o、h、l、c、pc 全部换了一组数值。这个差别就是本文要处理的问题。

三、返回字段逐项说明

五种除权方式返回的字段集合实测一致,都是 9 个。下表以 n 与 b 的首条为样例:

字段 实测类型 含义(接口文档定义) n 首条 b 首条 随除权方式变化
t str 交易时间 "2025-11-27" "2025-11-27" 否
o float 开盘价 11.66 1193.91 是
h float 最高价 11.71 1197.96 是
l float 最低价 11.58 1187.45 是
c float 收盘价 11.71 1197.96 是
v float 成交量 1011847.0 1011847.0 否
a float 成交额 1178308953.0 1178308953.0 否
pc float 前收盘价 11.69 1196.34 是
sf 数值 停牌标记:1 停牌、0 不停牌 0.0 0.0 否

三点从表里直接读出的约束:

  1. 价格类 5 个字段整体变化,量类 4 个字段不动。所以「换参数只影响价格」可以被当作结构事实写进落库设计:同一份量类数据不必重复存五遍,但价格列必须带口径标签。
  2. 不要按数值大小反推口径。五种参数里有两种在 11 附近、两种在 1200 附近、一种在 11.09 附近,仅凭量级猜是哪一种,遇到上游调整就会静默出错。口径以接口文档中该参数的定义为准。
  3. sf 是 0.0 而不是空值。它是有效的「不停牌」标记,校验写「键存在且为数值」。

四、接口结构与参数

地址结构:

text 复制代码
https://api.mairuiapi.com/hsstock/history/{股票代码.市场}/{分时级别}/{除权方式}/{licence}?st=&et=&lt=

按接口文档定义,日线及以上级别可选的除权方式有五种:

除权方式 接口文档定义 实测首条 c 实测首条 o
n 不复权 11.71 11.66
f 前复权 11.1 11.05
b 后复权 1197.96 1193.91
fr 等比前复权 11.09 11.05
br 等比后复权 1220.5 1215.29

分时级别支持 5、15、30、60、d、w、m、y。这里有一个和「多级别」直接相关的约束:分钟级别没有除权数据,该段按文档只用 n ;日线以上才用得上上面那张表。把 d 级别的参数组合直接套到 5 级别上,得到的不是预期结构,排查时又容易先怀疑数值本身。

同一除权方式下切换级别,返回形态不变(都是数组),但 t 的格式与数值量级会变。本次实测 d 首条 t=2025-11-27、c=11.71;w 首条 t=2025-11-16、c=11.75;m 首条 t=2025-09-30、c=11.34;5 首条 t=2025-12-01 14:50:00、c=11.69。所以落库时至少要有两列元数据:级别、除权方式。

还有一个必须处理的分支:无效参数不返回 JSON。本次把除权段写成 q,实测返回 HTTP 404 与一段纯文本 404 page not found,既不是数组也不是错误对象。客户端不能无条件 json.loads。

五、代码实现:对照五种口径并区分 404 文本

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

LICENCE = "LICENCE-2413-E1FB-743443B19BBD"
BASE = ("https://api.mairuiapi.com/hsstock/history/000001.SZ/d/"
        "{div}/" + LICENCE + "?st=20250101&et=20251231&lt=3")
DIVS = ("n", "f", "b", "fr", "br", "q")


def fetch(url):
    """返回 (status, text),把 HTTPError 也转成可判断的结构。"""
    req = Request(url, headers={"User-Agent": "Mozilla/5.0"})
    try:
        with urlopen(req, timeout=20) as resp:
            return resp.status, resp.read().decode("utf-8")
    except HTTPError as e:
        return e.code, e.read().decode("utf-8")


for div in DIVS:
    status, text = fetch(BASE.format(div=div))
    if status >= 400:
        print("%-3s HTTP=%d body=%r" % (div, status, text.strip()))
        continue
    rows = json.loads(text)
    if not isinstance(rows, list) or not rows:
        print("%-3s HTTP=%d unexpected shape" % (div, status))
        continue
    r = rows[0]
    print("%-3s HTTP=%d t=%s c=%-9s o=%-9s a=%-16s v=%s" % (
        div, status, r["t"], r["c"], r["o"], r["a"], r["v"]))

实测运行输出(原样):

text 复制代码
n   HTTP=200 t=2025-11-27 c=11.71     o=11.66     a=1178308953.0     v=1011847.0
f   HTTP=200 t=2025-11-27 c=11.1      o=11.05     a=1178308953.0     v=1011847.0
b   HTTP=200 t=2025-11-27 c=1197.96   o=1193.91   a=1178308953.0     v=1011847.0
fr  HTTP=200 t=2025-11-27 c=11.09     o=11.05     a=1178308953.0     v=1011847.0
br  HTTP=200 t=2025-11-27 c=1220.5    o=1215.29   a=1178308953.0     v=1011847.0
q   HTTP=404 body='404 page not found'

关键在两处:一是 fetch() 把 HTTPError 也读成文本返回,404 时不会丢掉响应体;二是 status >= 400 先分支出去,不把纯文本交给 json.loads。这样无效参数与「成功但空数组」就不会被混成同一类结局。

六、演示证书的行为说明

可以自己点开两条对照,只把股票代码从 000001.SZ 换成 600519.SH:

实测两次返回逐字节相同 。把除权方式换成 f、b、fr、br,把级别换成 w、m、5 重复这个对照,结果同样是逐字节相同。

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

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

七、常见问题与踩坑

把不同除权方式的价格拼进同一列。 n 的 c 是 11.71,b 的 c 是 1197.96,写进同一列再按同一阈值过滤,会直接把一半记录判成异常。分表或在表里加口径标签是更稳的做法。

凭数值猜口径。 五种参数里有两组数值接近,仅靠量级无法区分。参数定义以当期接口文档为准,别把猜测写进代码注释或字段映射表。

分钟级别沿用日线的除权参数。 接口文档说明分钟级别无除权数据、该段只用 n。把它当成通用段拼接,排查时容易先怀疑数据而不是参数。

把无效参数当成空数组。 q 实测是 404 加纯文本,不是 []。解析前先看状态码,再看响应体能不能解析成 JSON。

把 v / a 当成随口径变化的字段。 本次五种方式下这两个字段逐字段相同,但这是实测观察,不是可以写死的不变量;落库时仍按「每次响应原值透传」处理。

混淆 t 与级别。 日线 t 是纯日期,分钟级 t 带时分秒。除权方式不改变 t 的格式,改变格式的是分时级别。

把样例特征写成通用规则。 「3 条」「某个 c 值」都是这次演示返回的特征,不是接口契约。校验只约束字段集合、类型和结构,不约束具体数值范围。

八、小结与数据来源说明

历史分时的除权方式差异集中在一点:价格类字段换一组数值,量类与时间字段保持不动。落库时把「分时级别」和「除权方式」都写成元数据,解析时先分流 404 与空数组,后面就不会在换参数时把数据写错列。

本文所有返回样例、字段表、参数对照与代码输出均来自对上述公开接口地址的实际请求,采集时间为 2026-11-12;除权方式与分时级别的取值定义以当期接口文档为准,演示返回的条数与数值是固定样例特征,不是接口契约。

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

相关推荐
猫猫不是喵喵.17 分钟前
从0到1创建一个微服务项目
java·数据库·微服务·1024程序员节
瑞雪兆丰年兮28 分钟前
JavaWeb-day13-SpringBootWeb事务管理&AOP
java·开发语言·springboot·javaweb·事务·web·aop
曲径海盗37 分钟前
Try-Except怎么用,3个追问解答在自动化下的应用
java·jvm·oracle
程序员cxuan1 小时前
讲真,智能路由这事儿,还得看基元律动
人工智能·后端·程序员
vaskilab2 小时前
Schema 全绿,Agent 却把参数编造了
后端
Cosolar2 小时前
极简智库:快速搭建你的开源项目教程阅读平台,让 docs/ 目录被更多人看见
java·架构·github
Java与AI落地2 小时前
Spring AI 上手:给现有 Java 服务加一个能用的 AI 接口
后端
Swot2 小时前
SvelteKit 数据流研究:`+page.js` 和 `+page.server.js` 到底谁在跑,数据又去了哪儿
前端·后端
一条Go2 小时前
历史告警归档:为什么"删掉主库行"是最后一步
后端·架构·github