复权研究基线¶
这个 Recipe 用一只股票做最小验证:同一窗口里,未复权 close 与后复权 adj_close 的收益可能不同;研究代码必须明确选择口径,并确认因子覆盖是 exact。
1. 生成可验证的小湖¶
pip install cnequity
cne init --profile demo --research --symbols 600519.SH
--research 会把窗口扩展到约三年,额外从 Sina 派生 hfq 因子。命令末尾会打印 raw / hfq 收益与因子覆盖摘要,具体结果取决于当前窗口。
如果只需要确认 TDX 连通性,可以先跑不带 --research 的 cne init --profile demo;网络受限时不要把失败的研究输出当成“没有复权变化”。
2. 在 Python 中复核合同¶
from pathlib import Path
import polars as pl
from cnequity.config import load_config
from cnequity.query import load
cfg = load_config(Path("configs/cnequity.demo.toml"))
raw = load(
"daily_bars",
symbols=["600519.SH"],
config=cfg,
)
hfq = load(
"daily_bars",
symbols=["600519.SH"],
adjust="hfq",
strict_adj=True,
config=cfg,
)
if not bool(hfq["adj_is_exact"].all()):
raise RuntimeError("factor coverage is not exact; stop the research run")
comparison = (
raw.select(["trade_date", "close"])
.rename({"close": "raw_close"})
.join(
hfq.select(["trade_date", "adj_close", "adj_is_exact"]),
on="trade_date",
how="inner",
)
.sort("trade_date")
.with_columns(
(pl.col("raw_close") / pl.col("raw_close").first() - 1).alias("raw_return"),
(pl.col("adj_close") / pl.col("adj_close").first() - 1).alias("hfq_return"),
)
)
print(comparison.tail(1))
comparison.write_parquet("data/cnequity-demo/research-baseline.parquet")
这里的 raw_close 是湖内保留的原始价格,adj_close 是查询时把派生的 hfq 因子应用到原始价格的结果。湖内只持久化 hfq;qfq 通过 load(adjust="qfq") 按查询窗口推导,详见 产品边界。
3. 进入真实研究前的检查¶
from cnequity.query import list_datasets
print(
list_datasets(config=cfg)
.filter(pl.col("dataset").is_in(["daily_bars", "adj_factors"]))
.select(["dataset", "coverage_start", "coverage_end", "history_mode"])
)
不要仅凭行数判断覆盖完整:history_mode 可能是 snapshot_with_backfill,粗粒度分区需按实际日期列确认覆盖。股票池研究应明确版本化 profile(例如只研究沪深时用 cn_a_sh_sz_research_v1),由该范围严格核验证据;不要将单只股票 demo 当作全市场覆盖证明。
正式研究保存价格、因子、证券身份、交易状态、日历的依赖 revision 映射,并通过
revision_map 读取。长期保存使用 cne snapshot create NAME --dataset daily_bars --research,
一起封装完整数据依赖和 ST/退市证据、非敏感读取配置;若某依赖尚未生成会直接报错。
同时保存查询参数和软件版本,不能把快照创建成功等同于研究覆盖通过。详见
Python API 的版本边界。