行情数据像水电:没有哪个供应商能保证永远不断。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 个适配器:谁负责什么
每个适配器都实现同一组契约:取原始数据、标准化列、清洗、计算基础指标。新增一个源,不需要改流水线,只需要实现这些方法并在管理器里注册。
| 适配器 | 默认优先级 | 覆盖市场 | 认证 / 依赖 | 特点 |
|---|---|---|---|---|
| Efinance | 0 | A 股 | 无 | 东方财富爬虫,日线/实时/板块/筹码 |
| AkShare | 1 | A 股、港股 | 无 | 覆盖面最广,接口多但稳定性波动 |
| Tushare | 2(有 Token 时升到 -1) | A 股、港股 | TUSHARE_TOKEN | 字段规范,专业用户首选 |
| Pytdx | 2 | A 股 | 无 | 直连通达信行情服务器 |
| TickFlow | 2 | A 股 | TICKFLOW_API_KEY | 行情/指数/板块/市场统计 |
| Baostock | 3 | A 股 | 免费登录 | 历史数据稳定 |
| YFinance | 4 | 六个市场 | 无 Key | 海外市场主力;非官方接口 |
| 腾讯行情 | 5 | A 股 | 无 | 直连腾讯,指数固定链的第一站 |
| Longbridge | 5 | 港股、美股 | OAuth | 港美行情与基本面,质量较好 |
| Futu | 港股专用 | 港股 | 本机 OpenD | 可读行情与真实持仓 |
| Finnhub | 2(美股链) | 美股 | FINNHUB_API_KEY | 免费 60 次/分钟 |
| AlphaVantage | 3(美股链) | 美股 | 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。
为什么分开?
实时行情要求低延迟,历史日线要求完整与稳定,指数要求代码映射正确。用同一套优先级会把不同诉求混在一起,反而更脆弱。
降级与熔断:把「坏掉」当常态
一条典型请求的生命周期:
- 熔断器:同一个源、同一个市场连续失败 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 股日线数据源优先级,下面哪种说法正确?
REALTIME_SOURCE_PRIORITY。三套链互不覆盖,这正是本章反复强调的重点。