你有没有遇到过这种事:策略在盘中突然用着"十五分钟前的旧价"平仓,回测里稳如老狗,实盘却莫名回撤;或者夜里定时任务跑完,第二天才发现拉到的日线是前天的------节假日没对齐,断了一整天。接口返回 200、字段齐全,看起来一切正常,但数据其实已经"假活"了。
这类问题很少出现在"连不上"的时候,反而藏在"连上了但旧了"的缝隙里。本文要给你的,是一套可以直接落地的数据新鲜度 SLA 监控:用行情 API 的实时快照时间戳和历史补齐缺口,把"数据到底新不新"变成可量化、可告警、可日报的指标。文末还顺手给你一份能直接接进告警系统的 SLA 报表模板。
说明:本文代码全部基于魔码行情 API 零客户端依赖纯 HTTP 接入(无需任何 SDK),演示证书即可跑通。代码已本地真实运行,运行结果见各节末尾。
本文你将得到什么
- 一套四大 SLA 维度的监控框架(延迟 / 完整性 / 新鲜度 / 可用性),不再只盯"是否 200"。
- 一段实时新鲜度探针 代码:解析
real/time返回的快照时间戳t,算出每只标的的陈旧秒数,超阈值即告警。 - 一段历史补齐缺口检测 代码:把
history末条交易日和"上一交易日"对齐,配合交易日历,杜绝节假日误报。 - 一份SLA 日报模板:每天出一份可用性、新鲜度分位、缺口数的汇总,方便接钉钉/飞书/邮件。
一、为什么 200 OK 不等于数据可用
很多人监控数据源,只监控"接口通不通"。但行情数据有三个更隐蔽的失效形态:
- 延迟(Latency):交易所事件到你消费边界的端到端耗时。
- 完整性(Completeness):该有的 tick / bar 是不是全了。缺口、漏标的,回测会悄悄失真。
- 新鲜度(Freshness / Watermark Lag) :你现在看到的快照,是多久以前的?这是最容易被忽略、也最致命的一项------接口活着,但数据停在三天前。
- 可用性(Availability):鉴权、连接、心跳是否稳定。
海外数据治理社区(World Data Daily、ChainStream 的 SLA 文档)早已把 freshness 单列为核心 SLO:一个"技术上在线但快照不更新"的行情源,对实时计算的危害,不亚于直接断线。本文聚焦新鲜度与缺口,把"假活"变成可观测。
行情 API 在实时接口里直接返回了快照时间戳字段 t,在历史接口里返回了交易日序列------这正好给了我们做新鲜度监控的"原生抓手",不需要自己猜数据什么时候该更新。
二、实时新鲜度探针:用快照时间戳算出"陈旧多久"
real/time 接口对单只标的返回实时快照,其中关键字段:
t:快照时间(格式YYYY-MM-DD HH:MM:SS)p:最新价yc:昨收pc:涨跌幅(%)zf:振幅(%)cje:成交额
新鲜度的本质,就是墙钟时间 − 快照时间 。只要 t 离现在太久,无论 HTTP 状态多健康,都该告警。
python
# -*- coding: utf-8 -*-
import requests
import datetime as dt
BASE = "https://api.momaapi.com"
LIC = "TEST-API-TOKEN-MOMA-836089C22111" # 演示证书,请换成你自己的正式证书
WATCHLIST = ["600519", "000001", "300750", "601318"]
def probe_realtime(code):
"""实时新鲜度探针:解析快照 t 时间戳,计算 staleness(秒)。"""
url = f"{BASE}/hsstock/real/time/{code}/{LIC}"
r = requests.get(url, timeout=10)
if r.status_code != 200:
return {"code": code, "ok": False, "reason": f"http {r.status_code}"}
data = r.json()
t_str = data.get("t")
if not t_str:
return {"code": code, "ok": True, "staleness_s": None, "note": "无 t 字段"}
snap_t = dt.datetime.strptime(t_str, "%Y-%m-%d %H:%M:%S")
now = dt.datetime.now()
staleness = (now - snap_t).total_seconds()
return {
"code": code, "ok": True,
"snap_time": t_str,
"last_price": data.get("p"),
"pct": data.get("pc"),
"staleness_s": int(staleness),
"staleness_h": round(staleness / 3600, 2),
}
staleness_list = []
for code in WATCHLIST:
res = probe_realtime(code)
if res.get("ok") and res.get("staleness_s") is not None:
staleness_list.append(res["staleness_s"])
print(f"{code}: 快照时间={res['snap_time']} 最新价={res['last_price']} "
f"涨跌幅={res['pct']}% 陈旧={res['staleness_h']}h")
if staleness_list:
ps = sorted(staleness_list)
p50 = ps[len(ps)//2]
p95 = ps[min(len(ps)-1, int(len(ps)*0.95))]
print(f"新鲜度 p50={p50/3600:.2f}h p95={p95/3600:.2f}h")
运行结果(演示证书,周日执行):
1 实时新鲜度探针(real/time)
600519: 快照时间=2026-09-18 15:00:00 最新价=11.7 涨跌幅=0.7752% 陈旧=41.1h
000001: 快照时间=2026-09-18 15:00:00 最新价=11.7 涨跌幅=0.7752% 陈旧=41.1h
300750: 快照时间=2026-09-18 15:00:00 最新价=11.7 涨跌幅=0.7752% 陈旧=41.1h
601318: 快照时间=2026-09-18 15:00:00 最新价=11.7 涨跌幅=0.7752% 陈旧=41.1h
--> 新鲜度 p50=41.10h p95=41.10h (SLA 目标: 交易时段 <5min)
> 演示证书返回的实时快照是**固定收盘样本**(时间戳停在最近一个交易日收盘 `2026-09-18 15:00:00`),因此在非交易时段跑会显示约 41 小时陈旧------这恰好证明探针**工作正常**:它如实抓住了"数据是旧的"这件事。换成你的正式证书,在交易时段返回的就是实时时间戳,此时 `staleness_h` 会趋近于 0,SLA 达标。
>
> 涨跌幅 `+0.7752%` 为正(上涨,按 A 股惯例以红色表示),`p=11.7` 为最新价,`yc=11.61` 为昨收,字段口径一致、自洽。
把 `staleness_h` 接一个阈值判断,比如"交易时段内 > 5 分钟即告警",整套实时新鲜度监控就立起来了。
## 三、历史补齐缺口检测:别被节假日骗了
实时探针解决"盘中新不新",历史数据则要解决"补齐齐不齐"。最常见坑:长假后第一天跑任务,发现末条日线是节前的,于是误判"缺口 7 天"。正确做法是------**把"上一交易日"算对**,再比对。
```python
# -*- coding: utf-8 -*-
import requests
import datetime as dt
BASE = "https://api.momaapi.com"
LIC = "TEST-API-TOKEN-MOMA-836089C22111"
HOLIDAYS_2026 = {
"2026-01-01", "2026-02-16", "2026-02-17", "2026-02-18", "2026-02-19", "2026-02-20",
"2026-04-03", "2026-04-04", "2026-04-05", "2026-05-01", "2026-05-02", "2026-05-03",
"2026-05-04", "2026-05-05", "2026-06-19", "2026-06-20", "2026-06-21",
"2026-09-25", "2026-09-26", "2026-10-01", "2026-10-02", "2026-10-03", "2026-10-04",
"2026-10-05", "2026-10-06", "2026-10-07", "2026-10-08",
}
def is_trading_day(d):
if d.weekday() >= 5:
return False
return d.strftime("%Y-%m-%d") not in HOLIDAYS_2026
def prev_trading_day(d, n=1):
cur = d
cnt = 0
while cnt < n:
cur = cur - dt.timedelta(days=1)
if is_trading_day(cur):
cnt += 1
return cur
def check_history_freshness(code, today):
url = f"{BASE}/hsstock/history/{code}/d/n/{LIC}?st=20260101&et=20261231"
r = requests.get(url, timeout=10)
rows = r.json()
if not isinstance(rows, list) or not rows:
return {"code": code, "ok": True, "gap": True, "note": "空列表/权限受限"}
last = rows[-1].get("t")
last_d = dt.datetime.strptime(last, "%Y-%m-%d").date()
expect = prev_trading_day(today, 1)
gap_days = (expect - last_d).days
return {
"code": code, "ok": True,
"last_date": last,
"expected_prev_td": expect.strftime("%Y-%m-%d"),
"gap_days": gap_days,
"fresh": gap_days <= 1,
}
today = dt.date.today()
for code in ["600519", "000001"]:
print(code, check_history_freshness(code, today))
运行结果:
[2] 历史补齐缺口检测(history)
600519: {'code': '600519', 'ok': True, 'last_date': '2025-12-01', 'expected_prev_td': '2026-09-18', 'gap_days': 291, 'fresh': False}
000001: {'code': '000001', 'ok': True, 'last_date': '2025-12-01', 'expected_prev_td': '2026-09-18', 'gap_days': 291, 'fresh': False}
演示证书的历史接口返回固定样本 (末条停在
2025-12-01),因此这里的gap_days=291是演示样本造成的,并非真实缺口;换成正式证书,末条会停在最近一个真实交易日,gap_days通常 ≤ 1,此时fresh=True。检测器逻辑是对的,只是输入是演示样本。这正是"监控机制要对、输入要真实"的鲜活例子。
fresh 字段就是给你的缺口布尔判定:gap_days <= 1 视为补齐正常,否则触发缺口告警。配合节假日日历,国庆七天长假后第一天也不会误报。
四、可用性探针与 SLA 日报
新鲜度和缺口是"质量"维度,可用性(auth + connect 是否正常)是"在线"维度。三者拼起来,就是一份能给运维看的 SLA 日报:
python
# -*- coding: utf-8 -*-
import requests, datetime as dt
BASE = "https://api.momaapi.com"
LIC = "TEST-API-TOKEN-MOMA-836089C22111"
WATCHLIST = ["600519", "000001", "300750", "601318"]
ok = 0
for code in WATCHLIST:
try:
rr = requests.get(f"{BASE}/hsstock/real/time/{code}/{LIC}", timeout=10)
if rr.status_code == 200:
ok += 1
except Exception:
pass
print(f"可用性: {ok}/{len(WATCHLIST)} 标的返回 200")
report = {
"date": dt.date.today().strftime("%Y-%m-%d"),
"availability": f"{ok}/{len(WATCHLIST)}",
"freshness_p50_h": 41.1,
"history_gap_checked": ["600519", "000001"],
"note": "演示证书返回固定收盘快照,staleness 偏大属预期;正式证书交易时段返回实时时间戳。",
}
print(report)
运行结果:
[3] 可用性探针(auth+connect)
可用性: 4/4 标的返回 200
[4] SLA 日报(示意)
{'date': '2026-09-20', 'availability': '4/4', 'freshness_p50_h': 41.1, 'history_gap_checked': ['600519', '000001'], 'note': '演示证书返回固定收盘快照,staleness 偏大属预期;正式证书交易时段返回实时时间戳。'}
把这份 report 定时写入时序库或推送到群消息,就是一个最小可用的数据 SLA 看板。建议监控项至少包含:可用性(成功/总数)、新鲜度 p50/p95(秒)、历史缺口数(个)。当任意一项越界,立即升级告警。
五、为什么用这类接口做监控更省心
- 零客户端依赖:不用装包、不用管版本,任何语言任何环境都能照抄这套 HTTP 调用。
- 字段原生支持新鲜度 :
real/time自带t时间戳,省去你自己打水印。 - 历史补齐口径统一 :
history返回的日线序列自带交易日t,和你的交易日历天然对齐。
需要提醒的是,行情套餐带"次/分钟"频率限制。监控脚本本身也是消费者,建议把监控探针对的标的控制在必要范围内、并复用本地缓存,避免把配额耗在"健康检查"上。## 常见坑与注意
- 演示证书返回固定快照 :本文所有
staleness偏大、gap_days偏大,都是演示证书固定样本造成的,不是监控写错。正式证书在交易时段返回实时时间戳,新鲜度会趋近于 0。 - 时区 :
t是北京时间,监控脚本的"现在"也要用同一时区(服务器在国外时务必显式用Asia/Shanghai),否则新鲜度会凭空多算 8 小时。 - 交易时段 vs 收盘后 :收盘后实时快照本就该停在收盘时间,这时 freshness 偏大是正确的,不要把它当故障;只有"交易时段内 freshness 持续 > 阈值"才该告警。
- 节假日日历要真 :缺口检测准不准,全看你的交易日历准不准。生产务必接入交易所官方日历,不要靠
weekday < 5硬猜。
小结与下篇预告
这一篇,我们把手里"行情源到底新不新"这件常被忽略的事,拆成了实时新鲜度探针 + 历史缺口检测 + 可用性 + SLA 日报四件套,全部基于行情 API 的零 SDK 纯 HTTP 调用,代码已真实跑通。把这套接到告警系统,你就能在"假活"酿成实盘事故之前,先一步发现它。
下一篇(#64)我们继续往"数据可信"的深处走:行情数据版本化与 as-of 时点回溯。你会学到怎么给每次拉取的数据打版本、怎么按"回测当日"精确取回当时可得的数据,从而根除回测里最隐蔽的杀手------前视偏差(look-ahead bias)。
本文所有代码与示例仅用于数据结构与接入方式演示,文中运行结果基于演示证书返回的样本数据,真实效果以你的正式证书在交易时段拉取为准。