DSAWiki
05系统 · 数据

数据从哪来:多源、降级与质量

行情数据像水电:没有哪个供应商永远不断。DSA 的答案是——永远准备第二条管路。

约 8 分钟 进阶 第 05 章 · 系统

行情数据像水电:没有哪个供应商能保证永远不断。DSA 的做法不是「找到最稳的那一家」,而是同时接很多家,并假设每一家都会坏。这一章讲它如何选源、降级、缓存,以及哪些市场天生就只能做「有限的事」。

类比:家里停水怎么办

你不会因为自来水公司检修就三天不洗澡。你会提前接一桶水、买几瓶矿泉水,或者去楼下便利店。数据源适配器就是这些「备用水源」:主管道断了切备用水箱,备用水箱也空了,至少还有瓶装水撑过今天。关键是——每一条水路都要能接到同一个水龙头上。

一张能力地图:六个市场,不是六套等价功能

先打破一个常见误解:支持某个市场,不等于支持该市场的所有数据。A 股有涨跌停、北向资金、龙虎榜、筹码分布;美股没有这些概念。系统用「市场能力矩阵」把差异显式写出来,缺什么就标什么,而不是硬造。

市场日线 / 实时基本面特色能力边界
A 股多源,较完整AkShare 等涨跌停、板块、筹码、资金流、北向、龙虎榜免费源有限流与接口变动风险
港股AkShare / Tushare / Longbridge / YFinance / Futu部分Longbridge/Futu 行情更稳A 股专属字段按 not_supported 降级
美股Finnhub / AlphaVantage / YFinance / Longbridge基础行情与新闻无 A 股式市场宽度与板块统计
日股 / 韩股仅 YFinance(后缀代码)轻量Yahoo 指数复盘不承诺实时与完整基本面;无全市场列表
台股仅 YFinance(.TW / .TWO)轻量三大法人买卖超无台股大盘复盘、无台股告警
ETF走股票路径视标的与股票同一套分析链路不进指数注册表,不套指数前缀

这张表解释了为什么你有时会看到「未配置 / 不支持」:那不是 bug,而是系统在诚实地告诉你能力边界。对 LLM 来说,知道自己不知道,比编一个答案重要得多。

14 个适配器:谁负责什么

每个适配器都实现同一组契约:取原始数据、标准化列、清洗、计算基础指标。新增一个源,不需要改流水线,只需要实现这些方法并在管理器里注册。

适配器默认优先级覆盖市场认证 / 依赖特点
Efinance0A 股无东方财富爬虫,日线/实时/板块/筹码
AkShare1A 股、港股无覆盖面最广,接口多但稳定性波动
Tushare2(有 Token 时升到 -1)A 股、港股TUSHARE_TOKEN字段规范,专业用户首选
Pytdx2A 股无直连通达信行情服务器
TickFlow2A 股TICKFLOW_API_KEY行情/指数/板块/市场统计
Baostock3A 股免费登录历史数据稳定
YFinance4六个市场无 Key海外市场主力;非官方接口
腾讯行情5A 股无直连腾讯,指数固定链的第一站
Longbridge5港股、美股OAuth港美行情与基本面,质量较好
Futu港股专用港股本机 OpenD可读行情与真实持仓
Finnhub2(美股链)美股FINNHUB_API_KEY免费 60 次/分钟
AlphaVantage3(美股链)美股ALPHAVANTAGE_API_KEY免费额度小,适合兜底
妙想6补充MX_APIKEY自然语言查资金流/筹码,不供日线
台股三大法人补充台股无TWSE / TPEx 法人买卖超

优先级不是写死的。每个源都可以用对应的 *_PRIORITY 环境变量覆盖;Tushare 是特例:只要 Token 有效且初始化成功,它会动态获得最高优先级。

管理器:三个独立的路由链

这是最容易误解的地方。DSA 不是「一条全局优先级链」,而是至少三套互相独立的路由:

① 普通日线链

按优先级排序,按市场过滤,逐个尝试。A/HK 走通用链;美股走 Finnhub → AlphaVantage → YFinance → Longbridge 的专用链。

② 已登记 A 股指数固定链

固定为 腾讯 → AkShare → TickFlow → YFinance,不读取各源的 *_PRIORITY。指数的代码语义与股票不同,需要单独兜底。

③ 实时行情链

默认 tencent → akshare_sina → efinance → akshare_em;配了 Tushare 会自动前置;港股默认 futu → longbridge → akshare → yfinance。

为什么分开?

实时行情要求低延迟,历史日线要求完整与稳定,指数要求代码映射正确。用同一套优先级会把不同诉求混在一起,反而更脆弱。

降级与熔断:把「坏掉」当常态

一条典型请求的生命周期:

选择市场market
按优先级尝试try
成功则标准化normalize
失败记录并切换fallback
全失败才报错raise
  • 熔断器:同一个源、同一个市场连续失败 3 次,冷却 300 秒。它不是永久封禁,5 分钟后会自动再试。
  • 进程内状态:熔断状态保存在当前进程内存里,重启即清空;多实例之间不共享。
  • 错误汇总:全部失败时,异常里会带上每个源的错误类型,方便判断是网络、限流还是字段变化。
  • 随机抖动:部分源在请求间加入随机 sleep,降低被识别为爬虫的概率。

免费源不承诺稳定性

爬虫类接口会随上游改版而失效,也可能限流。项目会持续修复,但不能保证「任何时候都能取到数据」。长期定时、批量分析或更稳的行情,建议配置 Tushare、Longbridge 等 Token 型数据源。

实时行情:盘中的「另一条命」

实时行情主要用于:盘中最新价、涨跌幅、量比,以及让技术指标「活」起来。它不是交易所级 tick 数据,延迟与字段完整度取决于供应商。

失败时,系统会退回最近一根日线收盘价,并在结果里标注来源。这意味着你在盘中看到的报告,可能基于「昨收 + 部分实时字段」——报告会尽量把这种不确定性写出来,而不是假装一切实时。

缓存与存储:稳定性的缓冲垫

缓存位置 / TTL作用
日线SQLite stock_daily断点续传、历史指标、避免重复抓取
实时行情内存,默认 600 秒同一轮分析里复用,减少请求
基本面内存,默认 120 秒 / 256 条短时间重复分析不重复拉取
概念板块榜300 秒(空结果 30 秒)板块数据变化没那么快
股票索引本地 + 远程,TTL 48 小时自动补全、代码→名称解析
LLM Prompt 缓存供应商侧重复前缀按缓存价计费,降低长 Prompt 成本

存储是 SQLite + SQLAlchemy,默认 WAL 模式、5 秒 busy timeout、写重试 3 次。共有约 32 张表,覆盖行情、分析历史、决策信号、会话、回测、持仓、告警与用量。迁移采用「自愈式」:启动时检查列/索引,缺了就补,不要求用户手动执行脚本。

注意:stock_daily 只持久化 MA5/10/20 与量比等轻量指标;MACD、RSI 等每次分析现算。这样做的好处是表结构简单,坏处是历史指标不能直接 SQL 查询。

数据质量:不要假装一切正常

系统会给数据集打质量状态,而不是只有「成功/失败」二元:

状态含义下游怎么处理
ok数据完整且新鲜正常使用
degraded可用,但部分字段缺失或延迟降置信度,Prompt 里注明
partial只拿到一部分数据集缺失块跳过,不推断
stale数据过期(如非交易日或缓存过旧)不允许高置信度结论
unavailable当前不可用降级或跳过
unknown冷启动,还没探测不假装 ok

Web 的「数据中心」页面会读取能力矩阵与质量状态,展示每个数据集在各市场的支持情况、当前路由优先级与警告。这张表比「支持 100 个数据源」的宣传语有用得多。

股票索引:3 万条代码背后的自动补全

输入「茅台」「600519」「gzmt」都能找到贵州茅台,靠的是一份约 3.17 万条的索引:规范代码、展示代码、中文名、拼音、缩写、别名、市场、资产类型、启用状态与排序。前端用它做自动补全,后端用它做名称→代码解析。

索引会优先读本地缓存,缺失时尝试远程拉取,48 小时过期;连续失败会短暂抑制远程请求,避免每次输入都卡在网络上。指数与 ETF 的代码规则不同,因此索引里还带资产类型,避免把 000016 这只股票和 sh000016 这只指数混为一谈。

复权、基本面与新闻:三个容易忽略的细节

复权

分红送股会让股价出现「假跳空」。前复权把历史价格调整到与当前可比,技术指标才不会被分红缺口误导。DSA 取的多为前复权日线(例如腾讯源显式请求 qfq);不同数据源的复权口径可能略有差异,跨源比较时要留意。

基本面

财务快照、估值、行业归属、资金流等数据按市场能力抓取。A 股基本面主要走 AkShare,港股与美股部分走 Longbridge/YFinance。抓取有独立时间预算,超时不会阻塞主分析,只会留下缺失标记。

新闻与情报

新闻不是行情数据,而是「外部证据」。系统通过搜索服务抓取新闻、公告与舆情,最多 5 组检索词,并把命中结果落库复用。没有搜索源时,报告仍能生成,但风险与催化部分会更薄。新闻的时效、来源与去重都会影响最终质量。

交易日历与市场阶段

「今天要不要跑」不是看星期几,而是看交易所日历。系统用 exchange-calendars 判断 A 股、港股、美股、日股、韩股、台股是否开市,并区分盘前、盘中、午休、收盘集合竞价与盘后。

市场阶段会进入 Prompt。例如盘前不能假装知道今天的收盘走势;非交易日的数据只能作为历史参考。这个看似不起眼的细节,能显著减少「模型一本正经地描述尚未发生的事」。

能力边界清单(很重要)

使用前请确认

① 日/韩/台股只支持带后缀的 Yahoo 代码,裸码可能被当成 A 股;② 日/韩股不承诺实时行情与完整基本面;③ 台股大盘复盘与告警尚未覆盖;④ MARKET_REVIEW_REGION 只接受 cn/hk/us/jp/kr/both 及子集,不含 tw;⑤ 裸 6 位数字默认 A 股股票。

自测一下

你配置了有效的 TUSHARE_TOKEN。关于 A 股日线数据源优先级,下面哪种说法正确?

原因:Tushare 初始化成功后会把自身优先级提到 -1,因此在普通日线请求中最先尝试。但「已登记 A 股指数」使用固定的腾讯→AkShare→TickFlow→YFinance 链,实时行情也有独立的 REALTIME_SOURCE_PRIORITY。三套链互不覆盖,这正是本章反复强调的重点。

源码与文档锚点