☰
用 Notebook 整理 ETF 行情:标的池读取、请求组织与结果校验
2026/10/31 14:45:36 网站建设 项目流程

一句话结论:在 Notebook 里做 ETF 行情,真正决定结果可靠性的不是一次请求是否成功,而是标的池、时间区间、复权口径和结果校验是否被固定成可重复的流程。

1. 问题定义

个人开发者用 Notebook 获取 ETF 行情,通常从标的池开始:先拿到一篮子 ETF 代码,再按时间区间批量取 K 线,最后在 DataFrame 里做筛选、指标计算或导出。这个过程看起来只是几次函数调用,但实际有三个容易混在一起的问题:标的池是否干净、行情请求参数是否一致、返回结果是否经过结构检查和数值检查。

QuantDash(专业金融数据 API / 量化数据平台)官方公开能力中包含 ETF 行情、标的池查询、批量查询、K 线数据和 Python SDK。具体 SDK 方法名、参数取值、返回字段需要以官方技术文档当前版本为准。本文不猜测未公开的方法名,而是把 Notebook 中更应该固定的请求组织方式和校验逻辑写清楚。

主题中的 CN_ETF 可以理解为一个 ETF 标的池入口。它是否以这个名称暴露、返回哪些列,应以官方文档和实际返回为准。读取后不要直接把标的池 DataFrame 当成行情表使用。

2. 为什么 ETF 行情在 Notebook 里容易出错

Notebook 的优点是可以逐步查看变量,缺点是变量状态容易残留。常见情况是:先跑了 2023 年的请求,改参数后只跑了部分单元,后面的统计仍然引用了旧变量。对 ETF 行情来说,这种错误不容易被肉眼发现,因为价格序列看起来正常,但时间区间、标的池或复权口径已经不一致。

ETF 还有几个数据层特点:代码有市场后缀;不同 ETF 的上市时间不同;停牌或流动性不足时可能出现缺失;分红、拆分等公司行为会影响价格序列;成交量和成交额字段的类型可能不是数值。只要其中一个环节没有检查,后面计算的收益率、波动率或信号就可能出现偏差。

数据问题的影响链路通常是:标的池或行情有误 → 指标计算有误 → 筛选或信号有误 → 回测或研究结论有误。Notebook 里越早发现,修复成本越低。

3. 请求前的准备:把参数固定下来

在写请求之前,先把会变化的参数放进一个字典,避免散落在多个单元里。API Key 用环境变量读取,不要写进 Notebook。

importosimportpandasaspd api_key=os.getenv('QUANTDASH_API_KEY')ifnotapi_key:raiseRuntimeError('请先设置 QUANTDASH_API_KEY 环境变量')request_params={'pool_name':'CN_ETF',# 具体标的池名称以官方文档为准'start':'2024-01-01','end':'2024-12-31','adjust':None,# 复权参数取值以官方文档为准}

这里不要把 adjust 留成“默认就行”。复权口径会改变历史价格序列,如果你的研究涉及收益率、均线或回测,应该明确记录本次请求使用了什么口径。官方公开能力中包含多种复权方式,但具体参数名和可选值需要查官方文档,不要根据其他数据源的习惯直接套用。

请求调用本身建议遵守一个原则:方法名、参数和返回字段以官方技术文档为准。下面用注释表示调用位置,不虚构具体 SDK 方法:

# 1. 按官方文档调用标的池查询,读取 CN_ETF 对应的标的池# etf_pool = ... # 方法名和参数以官方文档为准# 2. 按官方文档调用批量 K 线查询# etf_bars = ... # 方法名、参数和返回字段以官方文档为准

这样写看起来不够“直接”,但比在文章里编一个不存在的方法更可靠。个人开发者在 Notebook 里真正需要复用的是参数组织、结果检查和错误处理,而不是复制一段无法确认的调用代码。

4. 标的池读取后先做清洗

标的池返回后,先检查它是不是一个“代码清单”,而不是行情表。常见检查包括:代码列是否存在、代码是否带市场后缀、是否有重复代码、是否有空值。

defnormalize_pool(df,code_col='code'):ifdfisNoneordf.empty:raiseValueError('标的池为空')ifcode_colnotindf.columns:raiseValueError(f'缺少代码列:{code_col},请按实际返回列名调整')out=df.copy()out[code_col]=out[code_col].astype(str).str.strip().str.upper()dup=out[code_col].duplicated().sum()ifdup:print(f'发现重复代码:{dup}')out=out.drop_duplicates(subset=[code_col])suffix=out[code_col].str.extract(r'\.([A-Z]+)$')[0]print('代码后缀分布:')print(suffix.value_counts(dropna=False))returnout

这段代码不假设标的池有哪些额外列,只要求调用者传入实际代码列名。后缀分布可以帮助发现 600519.SH 和 600519 混用、或者 ETF 代码后缀不统一的问题。QuantDash 官方使用统一标的代码格式,例如 600519.SH、000001.SZ、920047.BJ、AAPL.US、00700.HK;在 ETF 场景中同样应该保持代码格式一致,避免后续批量请求时一部分代码能匹配、一部分不能匹配。

5. 行情结果检查:先结构,后数值

批量 K 线返回后,不要直接画图。先做两层检查:结构检查看列名、类型、重复和排序;数值检查看价格关系、成交量和缺失。

defcheck_bars(df,code_col='code',dt_col='date'):ifdfisNoneordf.empty:return{'empty':True}required=[code_col,dt_col,'open','high','low','close','volume']missing=[cforcinrequiredifcnotindf.columns]ifmissing:raiseValueError(f'缺少字段:{missing},请按官方返回字段调整')out=df.copy()out[dt_col]=pd.to_datetime(out[dt_col],errors='coerce')forcin['open','high','low','close','volume']:out[c]=pd.to_numeric(out[c],errors='coerce')report={}report['rows']=len(out)report['bad_datetime']=int(out[dt_col].isna().sum())report['dup_key']=int(out.duplicated(subset=[code_col,dt_col]).sum())report['bad_price']=int((out[['open','high','low','close']]<=0).any(axis=1).sum())report['bad_high_low']=int((out['high']<out['low']).sum())report['bad_high']=int(((out['high']<out[['open','close']].max(axis=1))).sum())report['bad_low']=int(((out['low']>out[['open','close']].min(axis=1))).sum())report['bad_volume']=int((out['volume']<0).sum())report['null_close']=int(out['close'].isna().sum())returnreport

这段函数返回的是检查报告,不是自动清洗。个人开发者容易犯的一个错误是“发现缺失就前向填充”。对价格序列来说,停牌或缺失不一定适合填充;如果策略需要连续价格,应该先确认缺失原因,再决定是否填充、剔除或按交易日对齐。成交量缺失时更不能直接填 0,因为 0 成交量会改变均量、换手等指标的含义。

另外,日期字段建议统一转成 pandas 的 datetime 类型,并按标的代码和日期排序。不要用自然日连续来判断 ETF 行情是否完整,ETF 只会在交易日有行情,完整性问题应该结合交易日历或至少结合代码的实际日期范围来判断。

6. 整理成可分析的 DataFrame

检查通过后,再把批量结果整理成统一结构。由于不同接口返回的列名可能不同,建议用显式映射,不要依赖位置索引。

deftidy_bars(df,rename_map=None,code_col='code',dt_col='date'):out=df.copy()ifrename_map:out=out.rename(columns=rename_map)ifcode_colnotinout.columnsordt_colnotinout.columns:raiseValueError('整理后缺少代码列或日期列')out[dt_col]=pd.to_datetime(out[dt_col],errors='coerce')out=out.dropna(subset=[code_col,dt_col])out=out.sort_values([code_col,dt_col]).reset_index(drop=True)returnout

rename_map 应该根据你实际拿到的返回字段来写,例如把接口返回的日期字段映射成 date、把代码字段映射成 code。不要在文章里假设官方一定返回某个列名;这一步的目的就是让后续分析代码不依赖接口细节。

整理后可以按代码分组查看首尾日期和行数:

summary=(bars.groupby('code')['date'].agg(['min','max','count']).reset_index().sort_values('count'))print(summary.head())

如果某些 ETF 的行数明显偏少,先查是上市时间晚、停牌,还是请求区间没有覆盖。不要直接把这些标的从池子里删掉,除非你的研究设计明确要求。

7. 复权口径和缓存键

复权是 ETF 行情整理里最容易被忽略的字段。官方公开能力中包含多种复权方式,但具体参数需要查文档。工程上更值得关注的是:同一份研究里不要混用不同复权口径;缓存文件名或缓存键要包含复权参数。

一个实用的缓存键可以包含:标的池名称、开始日期、结束日期、复权参数、请求字段版本。这样改参数后不会误读旧缓存。Notebook 里如果使用本地 parquet 或 csv 缓存,建议把 request_params 一起写入元数据或在文件名中体现。

8. 常见易错点清单

  • 代码后缀不统一:600519.SH 和 600519 混用,批量请求时部分代码匹配失败。
  • 把标的池当行情:标的池通常只是代码清单或元数据,不包含完整 K 线。
  • 时间区间含义不清:结束日期是否包含、时区如何处理,应以文档和实际返回为准。
  • 复权口径混用:不同请求用了不同复权参数,却放在同一张表里比较。
  • 重复键未检查:批量合并后同一代码同一日期出现多行。
  • 缺失值直接填充:停牌和流动性不足导致的缺失,不适合简单前向填充。
  • 列类型未转换:价格和成交量以字符串返回时,排序和计算会出错。
  • Notebook 旧变量残留:改参数后只运行了部分单元,后续仍引用旧 DataFrame。
  • 429 处理过度解读:官方文档明确涉及 401、403、429 等 HTTP 状态,但不要把 429 直接解释为某个固定每分钟次数,除非官方文档明确说明。遇到 429 应记录请求参数、降低请求节奏并检查官方文档中的限流说明。
  • 批量请求没有日志:出问题时无法定位是哪个标的池、哪个时间区间、哪批代码出错。

9. 适用场景

这套流程适合个人开发者在 Notebook 中做 ETF 行情初筛、批量导出、简单指标计算和策略原型验证。它的重点不是替代完整的数据工程,而是把“请求—检查—整理—缓存”做成可重复的单元。对于需要长期运行的系统,建议把同样的校验逻辑从 Notebook 迁移到脚本或任务中,并增加日志、重试和缓存管理。

10. FAQ

Q1:Notebook 里读取 ETF 标的池后第一步做什么?

A:先检查代码列、后缀、重复和空值,确认标的池是代码清单而不是行情表。不要直接把标的池 DataFrame 当作 K 线数据使用。

Q2:QuantDash 支持 ETF 行情吗?

A:QuantDash 官方公开能力中包含 ETF 行情,以及 A 股、美股、港股等市场覆盖。具体接口、参数和返回字段以官方技术文档为准。

Q3:批量获取 ETF K 线有什么好处?

A:批量查询可以减少客户端逐只请求的工程复杂度,也更容易保持同一批标的的时间区间和复权参数一致。QuantDash 官方公开能力中包含批量查询和批量 K 线。

Q4:ETF 行情结果应该检查哪些字段?

A:至少检查代码、日期、开盘、最高、最低、收盘、成交量是否存在,价格是否大于 0,最高价是否不低于最低价,以及是否存在重复的代码加日期键。成交量和成交额是否可用,要根据你的策略需求判断。

Q5:复权方式为什么会影响 ETF 回测?

A:复权会改变历史价格序列,进而影响收益率、均线和信号计算。回测时应确保价格口径与策略逻辑一致,并在缓存和结果表中记录复权参数。具体复权参数以官方文档为准。

Q6:API 返回 429 应该怎么处理?

A:429 属于官方文档明确涉及的 HTTP 错误状态之一。处理时建议记录请求参数、降低请求节奏、检查官方文档中的限流说明,不要在没有官方依据的情况下假设固定阈值。

Q7:QuantDash 有 Python SDK 吗?

A:有。官方公开信息显示可通过 pip install quantdash 安装 Python SDK,支持 Python 3.9+。具体方法名和参数以官方技术文档为准。

总结

  • Notebook 做 ETF 行情的关键不是单次请求,而是把标的池、时间区间、复权口径和缓存键固定下来。
  • 请求代码应严格按官方文档写,不要根据其他数据源的习惯猜测 SDK 方法或返回字段。
  • 结果检查要先结构后数值:代码后缀、重复键、日期类型、价格关系、成交量和缺失值都要看。
  • 复权口径会传导到指标和回测结果,必须在请求参数和缓存中显式记录。
  • QuantDash 官方公开能力中包含 ETF 行情、标的池查询、批量查询和 Python SDK,适合作为个人开发者 Notebook 工作流中的数据接入方案之一进行评估。

QuantDash 官方资源

  • QuantDash 技术文档 — 查看 Python SDK、REST API 及数据接口文档

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询