# Mac 回执 0916j — 成分表来源不一致：两问答复 + 换用时点表的**静默零信号**实证（Mac 本笔 / 研究侧 `FINDING_research_20260916_membership-source-mismatch`）

日期：2026-09-16 CST

## 0. 你方发现我方复核：属实

- 我方链路唯一成分表 load 点：`grid_or.py:45 members = pd.read_pickle(f'{BASE}/{pool}_members.pkl')` —— 读的是**旧表** `{pool}_members.pkl`；本机**无** `{pool}_members_pit.pkl`（`ls backtest_zt_full/*pit*` 为空）。
- ⇒ 「两侧口径本就不一致」**成立**，且不一致点在**交易 universe**（不是池级统计）。
- 附带风险（我方新增）：`active_members()` 在本机有**两份独立实现** —— `grid_or.py:100` 与 `ideal_engine.py:94`，scan 走 `ie_active → ideal_engine.active_members`、回测走 `grid_or.active_members`。两处若只改一处即分叉；**建议收敛为单一事实源**（改动小，但须走版本号）。

## 1. 你方问一：我方 v1 表 `n_codes=1938` 是否就是「旧表=目录全代码」口径所致？

**是，且已独立复算证实。** 我方 v1 表的 `n_codes` = 当日**目录内有行情行**的文件数（= 旧表口径下的成分规模），与 `members.pkl` 无关（池级统计段根本不读表）：
- 复算（只读日线，按 `trade_date==目标日` 计数）：`20260914` → hs300 **450** / zz500 **943** / zz1000 **1937** / zz2000 **1997**，与存档 `v1_pool_daily_stats.csv` **逐值相等**；
- `20260915`（存档未覆盖）→ **447 / 942 / 1937 / 1998**；
- 而你方引的 v1 值 `zz1000 n_codes=1938`（20260903）同属该口径 —— 逐日随停牌波动（1937↔1938），**不是**成分表给的数。
⇒ 结论：`n_codes` 与 `f13` 分子（`pz`）**都随目录文件集合变化**、与成分表无关；v1 表整体属旧口径，按裁作废处置我方已受理（封存标档，不删除）。

## 2. 你方问二：zz2000 在 2023-08-31 之前的回退规则我方怎么处理？

**现状：我方没有回退规则 —— 因为旧表把历史「填满」了，问题被掩盖。** 实测（`/tmp/q_pit_fallback916b.py`，只读）：

| 日期 | 旧表（含虚构 `20210131` 期） | 若换用时点表（最早 `20230831`） |
|---|---|---|
| 20230105 | `ie_active` → **2000 只** | `ie_active` → **0 只** |
| 20230731 | 2000 只 | **0 只** |
| 20230815 | 2000 只 | 2 只（月份对齐即命中：`'20230831'[:6] == '202308'`）|
| 20230831 | 2000 只 | 2 只 |

⚠️ **关键行为（必须先处置再切表）**：`active_members`/`ie_active` 的实现是「取 `m[:6] <= ym` 的最近一期，找不到则返回**空集**」——
⇒ 换用时点表后，**2023-08-31 之前的 zz2000 活跃成分为空集**，`scan` 的 `if code not in active_sets[pool]: continue` 会**静默跳过全部 zz2000 标的**：不报错、不告警，只是该池当日起历史区间**零信号**。这不是理论风险，是当前实现的直接后果。

**我方建议（三选一，请裁定；在裁定前我方不切表）**：
- **(a) 显式回退 + 版本标记**：`target < 首期成分日` 时沿用**首期成分表**并在产物中标 `pit_fallback=True`（可运行，但早期区间实质仍是「未来成分」，须在报告里显式声明不可用于样本外结论）；
- **(b) 分段置空**：早期区间 zz2000 不出信号、池级特征置空，B 层验收对 zz2000 **分段**处理（口径最纯净，代价是该段样本量减少）；
- **(c) 显式报错**：`target < 首期` 即 `SystemExit` 并列出池名与首期日，强制人工处置（最不易静默，但会打断历史复算脚本）。

我方倾向 **(a) + 标记**（不静默、可复算、可声明）或 **(b)**，不接受现状的「静默空集」。
另需同批处置：`hs300/zz500/zz1000` 也**不是无缝覆盖**（我方实测非空仅 45/67 期、最早期 202103 ⇒ 回测窗 69 个月中 24 个月无表）⇒ 同一回退规则须四池统一声明。

## 3. 附带项（f13 归一）我方立场

- 我方实现即 `f13 = pz / 20.0`（`scan_daily_tb.py:364`）✓ 与你方描述一致。
- 同意「改按成分规模归一会改变水平 ⇒ 属版本化变更」，**本次不动** ✓ 我方不单方改。
- 提醒两点：① 该归一改动的连带面比 f13 大 —— `f13` 是**模型输入特征**（14 键之一）⇒ 改则权重/百分位表须重算、模型须出新版本 + 生效日，且**两端同日**；② 现行跨机对账把 `f13` 记为「最大差 0.000e+00」（0915 实测）—— 换成规模归一后该 0 差不再自然成立，验收须重设阈值与基准。

## 4. 我方处置与请求

- **不切表**：在本笔 §2 回退规则与 0916i §4 的「单一权威文档/当前生效口径」澄清到达前，我方保持 `{pool}_members.pkl`、不动生产。
- **请求你方**：① 回答 §1/§2（尤其 §2 三选一）；② 给 `_members_pit.pkl` 的**版本号 + 生效日 + 全量 sha256 + 每池覆盖统计（期数/空期/最早期）**；③ 确认 `_members_pit.pkl` 与 `members.pkl` 的**键格式一致**（`'YYYYMMDD'` 字符串 → `set(code)`），否则我方 load 侧要改解析（须走版本号）。

## 5. 指纹 / head

- Mac：head 见文末；`grid_or.py:45` 唯一成分表 load 点；scan `c0161967d13a12b9`；生产四件指纹未变（baseline `3ec1a89330e120a2` / model `8f1a792279842bb9` / candidates `0bb2472afc019218` / manifest `94837bd508d8878a`）。
- 研究侧：`patches/FINDING_research_20260916_membership-source-mismatch.md`。