一些数据源的实际使用情况
刚开始做 WZETF 时,我以为数据源问题只是找到一个接口,把行情下载下来。真正上线以后才发现,能拿到数据和敢把数据稳定地发布给用户,完全是两件事。
WZETF 同时覆盖中国、美国、香港和台湾市场。每个市场的交易时间、节假日、代码格式、行情口径和数据发布节奏都不一样。更麻烦的是,同一个接口在本地电脑上可以正常工作,部署到云服务器后却可能超时、限流或者直接断开连接。
这篇文章记录我实际使用这些数据源时遇到的问题,以及后来逐步形成的处理方法。它不是一份数据源推荐清单,而是一篇真实的使用复盘。

拿到价格,不等于拿到了可以使用的数据
最初的流程很直接:从公开数据源取得 ETF 行情,计算动能分,生成排行榜,再把结果展示到网站和 App。
上线后很快遇到第一个问题:大家口中的“价格”,可能指完全不同的数据。
- 盘中最新成交价;
- 某一分钟的价格;
- 当日收盘价;
- 前一个交易日收盘价;
- 复权后的历史日线;
- 基金单位净值。
这些数字都可能是正确的,但不能随意互换。
例如,中国市场需要判断 ETF 溢价率:
\[ \text{溢价率}=\frac{\text{ETF 市场价格}-\text{单位净值}}{\text{单位净值}} \]
这里的市场价格和基金单位净值来自两套不同的数据体系。净值还有 nav_date(净值日期)和 ann_date(公布日期)的区别。如果把当时尚未公布的净值提前用于计算,就会产生“回看时正确、实盘时不可获得”的未来数据问题。
这让我认识到,数据系统首先需要定义的不是接口,而是数据口径。
多市场最麻烦的不只是时差
四个市场意味着四套交易日历和四套数据发布节奏。
美国、香港和台湾市场会遇到交易日不同、夏令时变化、午间休市和节假日不同步等问题。服务器时区如果和市场时区混在一起,任务就可能提前执行、延后执行,甚至在休市日误判为更新失败。
后来,每个市场的任务都使用自己的市场时区,并把流程拆成几个明确阶段:
- 判断该市场今天是否交易;
- 在约定时间采集价格;
- 检查数据日期、数量和有效性;
- 计算排行榜与轮动记录;
- 只有通过质量检查后才生成公开快照;
- 最后刷新网站和 App 使用的缓存。
这套顺序很重要。以前把抓取、计算和发布放在同一个请求里,任何一个环节变慢都可能导致网关超时。后来即使客户端显示超时,也必须先查看任务记录和通知日志,确认后台是否已经完成,不能盲目重复执行。
本地能用,不代表服务器能用
开发过程中,我测试过多类公开或免费数据源。最常见的情况包括:
- 本地请求成功,云服务器连接却被重置;
- 实时接口可用,历史接口需要更高权限;
- 返回 HTTP 200,但内容为空或者数据日期已经过期;
- 请求频率并不高,云服务器出口 IP 仍然被限流;
- 同一个平台的日线接口可用,分钟线接口却不可用。
香港市场的数据采集尤其典型。我尝试过降低请求频率、拆成小批次、扩大批次间隔,并分别验证实时、分钟和日线接口。
台湾市场也出现过第三方库在本地可以使用,放到服务器后却被远端重置连接的情况。最后只能改用交易所提供的日线和最新行情接口。
这段经历改变了我的判断标准:
数据源是否可用,必须在真实服务器、真实时间窗口和真实股票池上验证。
单只 ETF 的一次请求成功,只能证明这个接口曾经响应过,不能证明它适合生产环境。
中国市场:价格与净值必须分开处理
中国 ETF 排行榜曾经出现过一个风险:候选 ETF 的动能很高,但单位净值缺失,导致溢价率没有完成验证。如果此时继续发布,排行榜可能纳入溢价明显过高的 ETF。
后来,我把净值处理独立出来,同步 unit_nav,并制定了几条规则:
- 只使用在计算时点已经公布的单位净值;
- 以最近一个已经完成的交易日为数据截止点;
- 节假日可以向前寻找有限天数,但不能无限回退;
- 排行榜前三名必须全部完成溢价验证;
- 溢价超过策略阈值的 ETF 直接排除;
- 前三名任何一只缺少有效净值时,暂停发布并通知管理员。
这种方式比“缺数据时换一个接口继续计算”更保守,但也更符合研究产品的边界:宁可明确显示待验证,也不要用猜测填补关键字段。
从接口调用变成数据发布流水线
经过多次问题处理,WZETF 的数据流程逐渐形成了几道质量门槛。

来源约定
每个字段都要知道它来自哪里、代表什么,以及什么时间才可以使用。价格、净值、交易日期和公布日期不能全部混成一个“最新值”。
完整性检查
不能只检查文件是否存在,还要检查:
- 数据是否属于目标交易日;
- 股票池覆盖数量是否达到要求;
- 价格是否为正数,是否存在异常跳变;
- 排名前列需要的字段是否完整;
- 排行榜和轮动记录是否使用同一份价格。
原子快照
新数据先写入临时版本,全部校验通过以后再替换当前快照。这样即使下载中途失败,线上用户看到的仍然是上一份完整数据,而不是一半新、一半旧的结果。
缓存失效与重建
修正源数据,并不代表用户已经看到了修正后的结果。排行榜缓存、轮动记录缓存、首页快照和 App 接口缓存,都必须按照依赖关系重建。
以前出现过文件已经修改正确,页面仍然显示旧数据的问题,原因就是缓存链路没有一起更新。
可审计状态
每次任务都应该记录数据日期、覆盖率、排除原因、发布时间和通知结果。出现问题后,至少要能回答三个问题:系统拿到了什么、为什么没有发布、用户最终看到了什么。
失败后不能只靠手工重跑
数据任务失败时,最危险的操作是立即重复点击“发布”。如果第一次请求只是前端超时,而后台仍在运行,第二次执行可能造成重复通知或重复写入。

现在的处理方式分为四步:
- 先审计:查看任务日志、公开快照和通知记录,确认实际执行到了哪一步;
- 再预览:重新计算但不写入、不通知,对比原榜单和修正榜单;
- 正式更正:使用可以重复执行的发布流程,确保同一日期不会重复处理;
- 公开说明:如果用户已经收到错误结果,及时发送更正通知,说明原因和影响范围。
对于历史数据缺口,则使用独立的补齐脚本。脚本显示进度、控制请求间隔,遇到明确限流或者拒绝访问时立即停止,避免失败请求继续冲击数据源。
补齐完成后,还要重新计算轮动记录,而不是只修改价格文件。
为什么后来反而减少了采集次数
一开始,我认为采集越频繁越可靠。实际情况恰恰相反:如果产品只需要某个约定时点的研究快照,高频轮询反而会增加限流、网络故障和口径不一致的概率。
后来,不同市场都改为在明确时间窗口采集一次,或者分批完成采集。排行榜和轮动记录共用同一份经过验证的价格快照。
这样既降低了外部接口压力,也避免排行榜使用一种价格、轮动收益又使用另一种价格。
采集频率应该由产品的数据口径决定,而不是由接口允许调用多少次决定。
现在判断一个数据源,我会看什么
经历这些问题后,我不再只看免费额度和能否返回数据,而会评估以下方面:
| 维度 | 需要回答的问题 |
|---|---|
| 数据口径 | 是实时、延时、分钟线、日线,还是基金净值? |
| 时间可得性 | 这条数据在策略计算时是否已经公布? |
| 服务器可用性 | 云服务器出口 IP 能否稳定访问? |
| 完整性 | 股票池覆盖率和目标日期是否符合要求? |
| 限流方式 | 按分钟、按天、按 IP,还是按接口权限限制? |
| 可恢复性 | 失败后能否补齐历史数据并重新计算? |
| 可审计性 | 能否保存来源、时间、状态和排除原因? |
| 使用边界 | 数据许可是否适合当前产品的展示方式? |
免费数据源不是不能用于产品,而是不能把“免费”理解为“没有成本”。真正的成本会转移到验证、降频、重试、缓存、监控和人工处理上。
一些实际结论
第一,金融数据系统最重要的不是计算公式,而是输入数据在当时是否真实可得。
第二,关键数据异常应该阻止发布,而不是只在日志中留下一条警告。
第三,网站、App、轮动记录和通知应该来自同一份已经确认的快照,否则用户可能在不同入口看到不同答案。
第四,数据源一定会失败。可靠系统的目标不是永远不失败,而是在失败时不发布错误结果,并且能够快速定位、补齐和解释。
第五,对用户透明比掩盖问题更重要。研究产品建立信任,不只依靠收益曲线,也取决于出现错误后能否及时更正。
结语
从网站上线到多市场 App,WZETF 的数据处理经历了许多看起来很小、实际却会影响用户判断的问题。现在回头看,最大的变化并不是换了多少数据源,而是建立了一套更严格的发布观念:
数据可以暂时缺失,但不能未经验证就被当成结论发布。
未来还会增加新的市场和新的数据来源。但新增市场的第一步不会再是先接入一个接口,而是先定义数据口径、质量门槛、失败状态和恢复流程。
只有这样,新增的覆盖范围才是真正的产品能力,而不是新的不确定性。
WZETF 提供 ETF 动能研究数据与历史回测展示,不构成个性化投资建议、交易指令或收益保证。