东方财富北向资金双接口同时坏死——一个降级+迁移方案
本文最后更新于 2026年7月1日 中午
某套量化系统的北向资金模块在例行巡检中遭遇了一次”教科书级”的接口故障:东方财富的两个北向资金 API 同时坏死——一个返回空结果,一个返回全 NaN。如果系统只依赖其中一个接口,故障会在当天收盘后才会暴露;但因为两个接口在同一时段同时挂了,数据断档直接触发了告警。
本文记录从发现故障、定位根因到设计降级方案的完整过程,重点讨论:如何在多源数据管道中优雅地处理”主备同时失效”的场景。
一、问题:两个接口,同时坏死
北向资金是 A 股量化策略的重要因子。该系统的北向数据采集依赖东方财富的两个接口:
stock_hsgt_individual_em—— 个股资金流向(实时/近7天)stock_hsgt_hist_em—— 历史净流入数据(日线级别,可回溯多年)
这两个接口在设计上互为补充:一个负责”新鲜数据”,一个负责”历史深度”。正常情况下,它们各自独立运行,互不影响。
但某日 15:05 开始巡检时,两个接口同时出了问题:
1 | |
两个接口在同一时段同时坏死。这意味着:当天北向资金数据完全断档。
更令人不安的是,这两个接口的故障模式不同——一个返回空,一个返回 NaN——说明它们走的是不同的底层请求路径。如果只监控”是否有数据”而不检查”数据质量”,可能两个接口都通过健康检查,但实际数据都是无效的。
二、诊断:为什么两个接口会同时挂?
逐一排查后,发现根因并非东方财富整体故障,而是两个接口的 WBI 签名参数 在同一时段过期:
stock_hsgt_individual_em的 WBI 签名依赖一个动态生成的 token,该 token 有有效期。token 过期后,接口返回空结果而非报错。stock_hsgt_hist_em使用另一个签名参数,其生成逻辑依赖于页面加载时的某个 JavaScript 变量。该变量在特定条件下(如 Cookie 变更、UA 不匹配)会重新生成,但旧签名仍然被缓存使用。
两个接口的签名过期时间恰好重合,导致”双接口同时坏死”。如果只监控其中一个,另一个可能暂时正常,故障会在切换数据源时暴露。
关键教训:多源数据管道的健康检查不能只看”是否返回结果”,还要检查”结果是否有效”。空 DataFrame 和全 NaN DataFrame 都是”有结果”,但都不是”好结果”。
三、方案:三级降级策略
基于上述诊断,设计了三级降级策略,从最优到最差依次尝试:
第一级:修复主接口签名
重新生成 WBI 签名参数,恢复两个接口的正常调用。这是最快的修复方式,但需要处理签名的自动刷新逻辑。
1 | |
五、验证:docker restart 后全链路测试
修改完成后,通过 docker restart 重启服务,并运行端到端测试:
- 主接口正常时:返回正确的北向资金数据,列名标准化无误。
- 模拟签名过期:强制让 WBI 签名失效,验证自动切换到备用接口。
- 备用接口异常:模拟备用接口返回空,验证切换到 yfinance。
- yfinance 不可用:断网测试,验证正确标记数据为”缺失”。
全部测试通过。系统日志显示:
1 | |
验证结果:三级降级策略全部通过。系统在签名过期场景下自动切换到备用接口,数据完整性和列名标准化均正常。
六、回头看:从这次故障中学到了什么
这次”双接口同时坏死”的故障暴露了数据采集管道中的几个关键问题:
- 健康检查不够细粒度:”返回结果”不等于”返回有效数据”。空 DataFrame 和全 NaN DataFrame 需要通过内容检查才能识别。
- 签名过期是静默故障:与 HTTP 500 不同,签名过期通常不会报错,而是返回空结果或错误数据。这种故障更难发现。
- **多源管道的价值在于”不重叠的失效模式”**:如果两个接口的失效原因相同(如同一个 WBI token),它们就不是真正的冗余。设计降级策略时,需要确保各层使用不同的数据源和签名机制。
最终,这套三级降级策略不仅解决了北向资金采集的问题,其”主接口→备用接口→兜底方案→标记缺失”的模式也被复用到其他数据采集模块(QFII、产业链数据等)。
总结:多源数据管道的健壮性不在于接口的数量,而在于各层失效模式的独立性。签名过期这类”静默故障”是最大威胁,需要内容级健康检查 + 自动降级 + 明确标记三重保障。