查询指南¶
下游读数的推荐路径是 cnequity.query.load()。本文说明复权、Universe、PIT 与常见陷阱。
API 签名见 Python API 参考。
选择读取入口¶
| 入口 | 提供的能力 | 需要自己处理 |
|---|---|---|
load() |
物化数据,按参数做复权、PIT、股票池与严格校验 | 显式选择模式和证据窗口 |
load_with_receipt() |
固定可用修订,并返回行来源、PIT 与观察覆盖凭证 | 将凭证与研究产物一起保存;另核验完整性 |
scan() |
原始 LazyFrame,日期与标的过滤、版本选择 | 不含复权、PIT、股票池 |
cne query --sql |
本地视图上的只读 SELECT | SQL 的口径与覆盖校验 |
| 直读 Parquet | 脱离运行时读取开放文件 | 版本、去重、PIT、复权和股票池 |
初始化进行中,只有带 symbols 的 load("daily_bars", ...) 能读到已封存、尚未发布的未复权日线。不指定标的的读取仍只看已经发布的数据。
基本用法¶
from cnequity.query import load, scan, list_datasets
# 物化 DataFrame
df = load("daily_bars", start="2024-01-01", end="2024-12-31")
# Lazy scan(大窗口)
lf = scan("daily_bars", start="2020-01-01", end="2024-12-31")
# 湖内数据集概览(含 history_mode / backfill_source / coverage_*)
meta = list_datasets() # 或 list_datasets(config=cfg)
# snapshot_only → 无诚实历史;coverage_start 为盘上分区起点
配置解析顺序:config= → data_root= → configs/cnequity.toml
境内股票指数 ETF 使用单独的 etf_profiles 当前快照。按研究日筛掉之后的快照,
再选每只基金当时最新的记录;只有 eligibility_status="eligible" 可进入该范围。
深交所目录本身未提供足以核实指数资产类别的字段;仅按指数代码匹配并归档了明确
境内 A 股样本空间的官方编制方案后才可升级资格,其余行保持 unverified;
历史没有快照的日期也不能用今天的分类倒填。instruments.asset_type="etf" 是旧报价
口径,还混有 LOF,不是资格证明。
list_datasets() 是研究侧的数据发现入口,边界不证明中间无缺口:history_mode 区分 by_date / snapshot_with_backfill / snapshot_only;coverage_start 按已发布数据与分区边界解析(含 report_period=YYYYQn)。详见 数据集目录 — 历史可用性。
复权(adjust)¶
仅适用于含价量列的数据集(主要是 daily_bars)。
| 参数 | 行为 |
|---|---|
adjust=None |
原始未复权 OHLC |
adjust="hfq" |
用后复权因子乘价格;增列 adj_open…adj_close、adj_is_exact |
adjust="qfq" |
查询窗口内按最新 bar anchor 将 hfq 因子归一化 |
bars = load(
"daily_bars",
start="2020-01-01",
end="2024-12-31",
adjust="hfq",
)
strict_adj¶
bars = load("daily_bars", start="2024-01-01", adjust="hfq", strict_adj=True)
True:缺因子行抛出ReaderError,不填充 1.0False(默认):缺因子时adj_is_exact=False,价格按 factor=1.0 降级
研究建议:量化回测用 hfq + strict_adj=True;qfq 窗口 anchor 会漂移,不适合长期研究复现。
需要固定结果时,用 revision_map 同时固定价格、因子及股票池依赖,并留存所需质量证据、配置和软件版本。具体保证及快照边界见 Python API。
index_bars 是指数点位,不是个股价格,不支持 adjust=;请直接使用原始指数水平。
总收益复权(total_return)¶
bars = load("daily_bars", start="2024-01-01", adjust="total_return", symbols=["510300.SH"])
hfq / qfq 的含义保持不变:股票的后复权因子已包含分红再投资,基金(ETF/LOF)的后复权因子来自新浪基金因子,只随份额拆分、合并变化,不含现金分配,是价格收益口径。
adjust="total_return" 仅适用于 daily_bars:在后复权基础上,把基金每次现金分配按除息前一交易日收盘价再投资(因子乘以 前收盘 / (前收盘 − 每份分配));股票与 hfq 相同。每只基金在查询范围第一天的价格水平等于后复权,因此跨不同起点的查询请比较收益率而不是价格水平。需要的依赖(corporate_actions、instruments)会一并记入读取回执。
按用途选择:看价格走势用未复权价;做拆分可比的价格比较用 hfq;算投资者实际收益或回测用 total_return。
存储约定¶
- 湖内
daily_bars存未复权价 adj_factors在derived/,仅存hfq(adjust_type="hfq")- ETF/LOF 已记录的拆分和转增会在日常因子派生中校正,0.17 及更早版本留下的历史漏跳可用
cne repair fund-factors --apply重建(已弃用,将在 0.20.0 移除);修正来源为derived_fund_actions。保留其他来源台阶,现金分红仍由总回报读取单独计入。 - DuckDB 视图
daily_bars_adj/daily_bars_hfq/daily_bars_qfq与上述语义一致;daily_bars_total_return(start, end)表宏对应adjust="total_return",输出tr_*与adj_close
Universe 过滤¶
bars = load(
"daily_bars",
start="2024-01-01",
profile="cn_a_sh_sz_research_v1",
)
cn_a_sh_sz_research_v1 明确限定沪深两市,并在读取时严格核验证券身份、
逐日交易状态、历史 ST 和退市证据;缺少收据会拒绝返回。它不会把北交所缺口
藏在“全 A”标签下。需要包含北交所时可显式选
cn_a_all_experimental_v1,但该画像仍标为实验性,不应当作已验收的研究口径。
画像版本与哈希的用法见股票池画像。
旧的 universe="all_a" / "all_a_sh_sz" 参数仍供兼容使用;前者会发出
DeprecationWarning,两者都不会自动成为严格画像。兼容过滤的基础规则:
- instruments:
list_date <= trade_date,且未退市或delist_date > trade_date - 排除 CDR(689xxx.SH)
- trading_status(仅有数据的日期):剔除 ST/*ST 与停牌
历史 ST 限制¶
日更只抓当天 trading_status(交易所板块记录当天停牌)。init 和 cne backfill daily_bars 之后会按日线窗口自动重建停牌;更早的历史可由 cne derive trading_status --start/--end 按年重建,覆盖可与 daily_bars 同起点(约 2001)。ST 标签由 Baostock 的逐标的 isST 历史与可选 Tushare BJ 历史源共同提供;只有生成了完整、版本化的 historical_st_evidence 收据,才能把对应窗口用于研究。部分回补(例如仅从 2016 年开始)不能证明 2001 年起的历史 ST 已剔除。
收据可通过重叠的深历史范围与较新尾段范围合并,但新增标的必须有首个交易日证据。北交所(BJ)可通过显式配置的 Tushare Pro 回补:2016 年使用 bak_basic 历史简称,2017-01-01 起使用 stock_st;接口需要 token,2016 年以前仍会作为源端能力限制阻塞,不能把接口空结果当成 normal。未配置 Tushare 时,BJ 仍会显式阻塞。审计项 trading_status_coverage_start 区分总覆盖与 st_coverage_start,历史研究应使用 cne audit --full --research-start ... 复核。
显式研究画像会自动启用严格校验。若仍使用兼容参数,可加
strict_universe=True 校验逐日状态和请求范围的历史 ST 证据;缺收据或
包含无历史 ST 来源的 BJ 标的会抛出 UniverseCoverageError。
默认兼容读取适合探索,不应直接作为长历史回测输入。
交易所覆盖¶
all_a 包含沪深北。北交所证券名单与简称在日更时由 BSE 行情板补齐;TDX 名单枚举与 TDX 日线协议的市场能力不能混为一谈。
- BJ 日线历史优先走专用 TDX 路径(market id 2),当期 tip 可用 BSE 板快照,Sina 补未覆盖部分。显式小范围历史请求会跳过无关的全市场快照。
- Sina 历史行可能缺
amount。--tdx-amount-repair只在 OHLC 一致且成交量差小于一手时补成交额;旧代码等未被 TDX 服务的行仍保留缺失和 finding。 --bse-tip-repair只对已存当期日线核对 BSE 快照并补量额,不重新请求 Sina 历史。它不能恢复过去的 BSE 快照。
这些路线补的是行情;BJ 历史 ST 仍需独立证据。完整来源与修复入口见数据源说明。
北交所上市前的新三板行情¶
北交所证券在交易所上市前的场外行情(新三板做市、集合竞价)交易规则不同,没有可核对的涨跌幅。daily_bars 与 adj_factors 默认不返回这部分:每只北交所证券从其交易所上市起点读起,即 instruments.list_date 与精选层开板日 2020-07-27 中较晚者。沪深证券不受影响。
bars = load("daily_bars", symbols=["920826.BJ"]) # 2021-01-12 起
otc = load("daily_bars", symbols=["920826.BJ"], include_otc=True) # 含新三板行情
universe="all_a" 同样按交易所上市起点判定北交所证券是否在册。数据不会删除,仍保存在湖中。
PIT(Point-in-Time)¶
PIT 数据集:financial_statement_items、announcement_index、
share_structure、shareholder_counts、top_holders。
items = load(
"financial_statement_items",
items=["roe", "net_profit"],
as_of="2024-04-30",
pit_mode="strict",
)
pit_mode="strict"过滤announce_date、已知available_at/source_published_at和fetched_at/observed_at均不晚于as_of,并排除reconstructed回填行;pit_mode="best_effort"可保留它们,但返回pit_is_exact=False。- 同一
(symbol, report_period, item)取announce_date最新一行 - 禁止用
end=代替as_of做财报对齐
PIT 仍可使用 start / end 限定数据自身的日期列:公告与股东数据按
announce_date / change_date / count_date / record_date 过滤;财报的
report_period 是季度字符串,会按与日期边界相交的季度过滤。as_of 仍然是
公告可见时间,不能被日期窗口替代。
announce_date 在主键里,所以财报修订是新增一版而不是覆盖原值:同一科目可以同时存在
首发值和修订值。默认只返回 as_of 当时生效的那一版;要看修订本身(修订幅度和方向本身
就是信号)加 all_vintages=True:
# 000001.SZ 2024Q1 营收被改过几次、每次改了多少
load(
"financial_statement_items",
symbols=["000001.SZ"], items=["revenue"],
as_of="2026-07-21", pit_mode="strict", all_vintages=True,
).select("report_period", "announce_date", "item_value")
注意:回填行以 source=eastmoney_backfill 标记,并视为
pit_quality="reconstructed"。省略 pit_mode 或传 None 默认为
best_effort:在 as_of 之后采集的历史回填也可读取,并返回
pit_is_exact=False;公告及已知可用/发布时间仍受截止日约束。
需要湖在当时已有证据的研究代码请显式传 pit_mode="strict" 并记录该选择。
升级前依赖省略参数按采集时间过滤的调用,也应改为显式严格模式。
日更逐日累积、保存真实双时态列的版本才满足
严格模式(见 schema)。
回填默认自 2001 起(东财);list_datasets() 的 coverage_start 为盘上实际起点。
符号与列过滤¶
load("daily_bars", symbols=["600519.SH", "000001.SZ"], start="2024-01-01")
load("financial_statement_items", items=["roe"], as_of="2024-06-30", pit_mode="strict")
Symbol 格式:{code}.{SH|SZ|BJ},与 domain/symbols.py 一致。
DuckDB SQL¶
cne query --sql "SELECT * FROM instruments LIMIT 5"
常用视图:
| 视图 | 说明 |
|---|---|
daily_bars |
未复权;不含北交所上市前的新三板行情,与 load() 默认一致 |
daily_bars_including_otc |
未复权,含北交所新三板行情(adj_factors_including_otc 同理) |
daily_bars_hfq |
后复权价列 |
daily_bars_qfq |
前复权价列 |
daily_bars_adj |
含 adj_* 与 adj_is_exact |
daily_bars_total_return(start, end) |
表宏:总收益复权价 tr_*(基金现金分配再投资,股票同后复权) |
{dataset} |
各 curated/derived 数据集 |
数据库:{data.root}/duckdb/cnequity.duckdb(只读连接)。
直读 Parquet¶
不依赖本项目运行时:
import polars as pl
pl.scan_parquet("data/cnequity/curated/daily_bars/**/*.parquet")
需自行实现复权与 universe 逻辑;生产推荐 load()。
分区裁剪¶
query/parquet_scan.py 按 partition_col 与日期范围裁剪 Hive 分区目录。大窗口查询优先 scan() + lazy 算子链。
成交口径的分钟重采样¶
TDX 的零成交分钟可能沿用昨收或最近报价。需要成交口径时,从完整的 1m 数据重采样,避免把未成交报价混入 OHLC。1m 覆盖不到的更早历史,可以从 5m 重采样出 15m / 30m / 60m:
from cnequity.query import load, resample_trade_bars
minute = load("minute_bars", start="2026-09-15", end="2026-09-15",
symbols=["603869.SH"])
five_minute = resample_trade_bars(minute, "5m")
history = load("minute_bars_5m", start="2025-01-02", end="2025-12-31",
symbols=["603869.SH"])
hourly = resample_trade_bars(history, "60m")
1m 输入支持 5m、15m、30m、60m,5m 输入支持 15m、30m、60m;输入必须只有一种频率。都分别按 09:30 和 13:00 对齐,不跨午休。OHLC 只计入 volume > 0 或 amount > 0 的输入 K 线;后者可保留股数被供应商取整为零的小额成交。区间内无成交时保留末报价且量额为零,该价格仍不是成交价格。重复时间戳、午休记录、不在 5 分钟网格上的 5m 时间、日期错配和缺组成 K 线都会报错,完全没有记录的区间不会凭空补齐。结果不覆写原始湖,也不继承输入行的来源标签;研究应同时记录输入 revision,重采样无法恢复供应商丢失的成交细节。
5m 输入只能做到 5 分钟粒度的成交口径。供应商的 5m K 线由全部 1m 直接聚合,未成交分钟沿用的报价也算进开高低价。只要这根 5m 有成交,它的 OHLC 就原样参与计算,所以少数区间的开盘价、最高价、最低价会与从 1m 重采样的结果不同;收盘价和量额基本一致。1m 覆盖到的日期优先用 1m。
需要用 SQL、HTTP 接口或 MCP 读取时,可以用 cne derive minute_bars_15m(30m、60m 同理)按同一规则算进湖,见 15 / 30 / 60 分钟线。
resample_minute_history 把两者拼成一条序列:某只股票某天有 1m 就用 1m,没有就用 5m,结果多一列 resampled_from 标明每根 K 线来自 1m 还是 5m。1m 的起点按股票各不相同,所以不按统一日期切换。只要有 1m,那一天就只用 1m;1m 不完整时照常报错,不会悄悄改用 5m。
from cnequity.query import load, resample_minute_history
window = dict(start="2025-01-02", end="2026-09-30", symbols=["603869.SH"])
bars_30m = resample_minute_history(load("minute_bars", **window),
load("minute_bars_5m", **window), "30m")
错误处理¶
| 异常 | 常见原因 |
|---|---|
ReaderError: unknown dataset |
名称拼写或数据集未注册 |
ReaderError: no parquet data |
未 init/compact 或路径错误 |
ReaderError (strict_adj) |
缺 adj_factors 覆盖 |
PIT 无 as_of |
可能包含未来公告(不推荐) |
相关文档¶
衍生品研究¶
逐合约期货、期权链、连续序列与 Greeks 不使用股票 universe/复权假设。完整查询与质量验收例子见 商品期货与期权。