113 lines
4.2 KiB
Python
113 lines
4.2 KiB
Python
# sanguo_factor/fundamental_adapter.py
|
||
"""财务因子适配层: NAS 静态域三表+forecast parquet → PIT 日频特征列.
|
||
|
||
口径红线(docs/fundamental_factor_survey_20260907.md §7):
|
||
- R1 单季差分: Q1 直接取累计,其余 = 本期累计 − 年内上期累计;缺上期置 NaN 不填 0
|
||
- R2 TTM: 连续 4 个单季之和,不足 4 期 NaN
|
||
- R3 PIT: NOTICE_DATE ≤ 决策日才可见(有效披露日 = 三表 NOTICE_DATE 最大值,保守);
|
||
NOTICE_DATE 缺失的报告期整期跳过(宁缺毋假)
|
||
- 金融股(OPERATE_COST 缺失/为 0 的银行模板): 盈利质量/成长族特征置 NaN(估值族保留)
|
||
|
||
数据形态(NAS 实测 2026-09-07):
|
||
- 三表按股: static/{income,balance,cashflow}/{code}.{SH|SZ}_{table}.parquet
|
||
(北交 920xxx 残留零行文件、沪深退市 355 只文件不存在 → 读取容错跳过)
|
||
- forecast 按报告期全市场: static/forecast/{YYYYMMDD}_forecast.parquet(中文列,
|
||
一股多行=按「预测指标」,归母净利润行优先)
|
||
- 日期列为 "YYYY-MM-DD 00:00:00" 字符串;东财金额单位=元
|
||
- valuation(中文列)不读: 估值类因子市值 = close × SHARE_CAPITAL 自算(表达式层)
|
||
|
||
P1-B 四新域(NAS 实测 2026-09-08):
|
||
- dividend 按股: static/dividend/{code}.{SH|SZ}_dividend.parquet;code 列为
|
||
baostock 小写格式(sh.600519)与文件名不同 → 以文件名路由;日期/数值列为
|
||
字符串('' 为空);一行=一次分红事件,同年可多行
|
||
- gdhs 按期: static/gdhs/{YYYYMMDD}_gdhs.parquet(53 期 2013Q1→2026Q1);
|
||
用 代码/股东户数-本次/股东户数统计截止日-本次/公告日期 四列(实测列名
|
||
无第二连字符;噪声行情快照列不读)
|
||
- top_holders: 只读聚合产物 factor_cache/top_holders_agg.parquet(static
|
||
兄弟目录;由 scripts/factor_research/preaggregate_top_holders.py 一次性
|
||
预聚合 108,610 个按股×期小文件)
|
||
- valuation_baostock 按年: data/valuation_baostock/{year}.parquet(static
|
||
兄弟目录);date 为字符串,exchange 实测为 SH/SZ(映射回 SSE/SZSE);
|
||
2026 文件仅 08-13 起(bs 日喂起点)→ 01-01~08-12
|
||
缺口用 static/valuation(ak em 中文列 PE(TTM)/市净率)倒数补
|
||
- 四域缺任一 → 相关特征列全 NaN + 一次性 warning(优雅降级,本地可跑通)
|
||
|
||
输出: vt_symbol × datetime(日频) × FEATURE_COLUMNS,join 到 alpha_df 后
|
||
供 cs_rank(列) 表达式直接消费。
|
||
|
||
模块拆分(2026-09-10,纯结构重构零行为变化): 本文件保留为门面,re-export
|
||
全部原模块级名字(引用方与测试零改动),实现按数据域拆为:
|
||
- fundamental_schema.py 常量/列契约(FEATURE_COLUMNS 等)
|
||
- fundamental_statements.py 三表读取层(报告期宽表)
|
||
- fundamental_report_features.py 报告期级指标预计算(单季化/TTM/比率族)
|
||
- fundamental_forecast.py 业绩预告事件层
|
||
- fundamental_domains.py P1-B 四新域事件层
|
||
- fundamental_pit.py PIT 对齐 + 分块 join 主入口
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from .fundamental_schema import ( # noqa: F401
|
||
FEATURE_COLUMNS,
|
||
FORECAST_TYPE_SCORE,
|
||
DEFAULT_STATIC_DIR,
|
||
_BALANCE_COLS,
|
||
_BEAT_COLS,
|
||
_CUM_MAP,
|
||
_DA_PARTS,
|
||
_DATE_COLS,
|
||
_DOMAIN_COLS,
|
||
_FIN_NULL_COLS,
|
||
_FORECAST_COLS,
|
||
_IBD_PARTS,
|
||
_SYM,
|
||
_VT_TO_FILE_SUFFIX,
|
||
)
|
||
from .fundamental_statements import ( # noqa: F401
|
||
_TABLE_RAW,
|
||
_dedupe_reports,
|
||
_load_statements,
|
||
_norm_dates,
|
||
_read_static,
|
||
_vt_to_file_code,
|
||
)
|
||
from .fundamental_report_features import ( # noqa: F401
|
||
_cagr5,
|
||
_compute_report_features,
|
||
_delta4,
|
||
_mean4,
|
||
_qidx,
|
||
_safe_ratio,
|
||
_single_quarter,
|
||
_std16,
|
||
_ttm_of,
|
||
_yoy4,
|
||
)
|
||
from .fundamental_forecast import ( # noqa: F401
|
||
_ANNUALIZE_FACTOR,
|
||
_load_forecast_events,
|
||
)
|
||
from .fundamental_domains import ( # noqa: F401
|
||
_DIVIDEND_CHAIN,
|
||
_EMPTY_EVENTS,
|
||
_GDHS_CUTOFF_CANDIDATES,
|
||
_VB_GAP,
|
||
_code6_to_vt,
|
||
_inv_guard,
|
||
_load_dividend_cum,
|
||
_load_extra_domains,
|
||
_load_gdhs_events,
|
||
_load_topholder_events,
|
||
_load_vb_daily,
|
||
_missing_warned,
|
||
_warn_domain_missing,
|
||
)
|
||
from .fundamental_pit import ( # noqa: F401
|
||
BATCH_CODES,
|
||
_build_beat_events,
|
||
_build_grid,
|
||
_load_feature_events,
|
||
_prepare_days,
|
||
build_fundamental_features,
|
||
iter_fundamental_feature_chunks,
|
||
)
|