# 价格口径架构（PRICE_SCHEMA）

> 两端（Mac 实操侧 / WSL 研究侧）的**权威定义**——列名、派生、增量、用途必须与此一致。
> 任何改动（加列、改口径、改列名）先读 `COLLAB_RULES.md`（口径/列名变更属红线，需用户批准）。

## 1. 三层价格口径

| 口径 | 定义 | 基准 | 用途 | 是否可变 |
|---|---|---|---|---|
| **raw（未复权）** | tushare 原始价 | — | **撮合/估值/实盘成交**（股数、持仓市值、净值） | 历史永不变 |
| **hfq（后复权）** | `raw × adj_factor` | 上市日 | **策略判定**（回调/突破/止盈止损/链特征） | 历史永不变 |
| **qfq（前复权）** | `raw × adj_factor / latest_factor` | 最新交易日 | **K线展示**（除权平滑，好看） | 随新除权漂移 → **不落盘** |

## 2. 持久化 vs 派生

**持久化（落盘，不变根）** —— 每个日线 `daily_{pool}/{code}.csv` 共 12 列（**列集合**；此处按首字母排序仅为避免暗示落盘序）：
```
adj_factor, amount, change, pct_chg, pre_close,
raw_close, raw_high, raw_low, raw_open, ts_code, trade_date, vol
```
辅助表 `adj_factor_all.csv`（最新因子快照）。

**列序不约定（2026-09-13 裁定：方式 A「记录 + 铁律」）**：两端**列集合与列数严格一致**（12/12，生产库 5400/5400
文件首行同为该集合），但**落盘列序两端不同**（各按自身实现常量，本文档不登记任一顺序，以免被误当规范）。
列序**不参与任何判定**：
- 读写**一律按列名**，禁止位置式访问（`iloc[:, n]` / 位置式 `usecols` / `header=None` 无表头读取）；
- 跨机 diff **一律按名**（键 `code|trade_date`）；列序级/字节级差异**不作为差异依据**；
- 任何必须按位置取列的新代码，须在本文件登记理由。
- 不物理对齐（不重写任何一侧 5400 文件）：物理对齐换不来功能收益，只多一次迁移/校验风险面。

**派生（不落盘，运行时算）** —— `grid_or._derive_split_prices(d)` 单点：
```
hfq_close = raw_close × adj_factor
qfq_close = raw_close × adj_factor / latest_factor（该股末行 factor）
```
open/high/low 同式。

## 3. 每日增量（update_daily）

```
收盘后 → pro.daily 拉当日 raw + pro.adj_factor 拉当日因子
       → 追加一行：raw_*4 + adj_factor + 技术字段
       → 不写 qfq/hfq（派生，无需落盘）
```

## 4. 取价约定

| 场景 | 用价 | 函数 |
|---|---|---|
| K线展示（看板/纯图形，无价格标注）| qfq | `_kcol`（qfq 缺则 raw 兜底）|
| **含 raw 价格标注的复盘图**（交易详情图/持仓图/今日信号图）| **raw** | 直接取 `raw_*` |
| 持仓/估值/净值 | raw | `raw_col` |
| 判定/链特征 | hfq | `hfq_col` |
| 引擎加载（统一派生入口）| — | `grid_or.load_pool` → `_derive_split_prices` |

**同图同口径铁律（2026-09-13 补）**：一张图里的 K线与价格标注（买价/卖价/成本）必须同族——
复盘图的标注价取自 baseline 的 `sell_price`/`buy_px`（= raw），故这三张图的 K线也用 raw；
否则除权日之前 qfq 与 raw 差 `factor/latest` 倍，标注会错位。若某图要用 qfq 画K线，
标注价必须按同比例换算（标注会变成复权价，不利于对实盘报价）。看板 K线墙的买/卖标记
按索引取该序列自身价位，与口径无关，故可用 qfq。

**图表不进对账（2026-09-13 补）**：图是本地产物、依赖渲染与本地数据快照，跨机对账**只比四个文件**
（`sim_trading_nav.csv`、`sim_trading_state.json`、`strength_baseline.csv`、`strength_model.json`），
阈值：个股链特征 1e-4 / 池级 f12·f13·f14 1e-2 / 净值 0.01 元 / 浮点末位不报。

**共享数据文件禁止覆盖式重写（2026-09-13 补）**：`backtest_zt_full/adj_factor_all.csv` **两端都有同构写路径**
（`pd.concat` 旧表 + 当日 → `drop_duplicates(['ts_code','trade_date'], keep='last')` 追加，DRY 跳过写盘），
累计量差异仅来自启用时点（不参与跨机对账）；任何"整理/重建"操作**不得覆盖式重写**该文件，否则抹掉对端累计。

**增量写路径两道守卫（2026-09-13 补，commit 0f582bf / e377843）**：
① 列结构守卫——读入 `old` 后 `list(old.columns) != COLS` 直接拒写并计数。混 schema 追加会被
`pd.concat` 按列名对齐，静默产出多余列 + 末行 NaN，且"价格列非空"自检查不出来（NaN 恰在未查的旧列上）。
② 因子三级回退同时认新 12 列（`adj_factor` 列）与旧 19 列（`hfq_close/raw_close` 反推）——只认旧 schema
会让②级永不命中，`adj_factor` 接口不可用时全部股票被拒写（fail-safe 但当天不更新）。

**除权守卫（2026-09-13 补，用户批准；判据 **B-only** 定稿）**：增量因子三级回退的 **②末行因子兜底 ×
当日真除权** 是静默错（因子没更新 → 少一次除权调整，该股此后所有派生价继承）→ 当日判定除权则
**拒写并告警**，绝不猜因子。判据单点 `code/exdiv_judge.py`：
`|(raw_close/该股上一有效交易日raw_close-1)*100 - pct_chg| ≥ 0.05pp`。
- **不用 A=(raw_close/pre_close-1)*100 参与判定**：`pre_close` 是脏列（见下），且大比例分红票 A/B
  双侧同时失配会被漏判（000338.SZ 20241018：raw 13.46/prev 13.56/pre_close 12.43/pct 2.09 →
  两侧都失配）。全库评估（真值=tushare 因子变动，`code/chk_exdiv_judge_eval.py`）：因子跳幅 >0.5% 的
  真除权里 A∧B 漏 1222 条、B-only 只漏 7 条。
- 判据中的"前日 `raw_close`"必须是**该股自身上一有效交易日**（文件末行），不是全市场日历前一日 ——
  停牌/退市/次新股各股前一交易日不同，取日历前一日会让停牌复牌股假除权。
- 因子跳幅 j% 对应的价格口径差 ≈ j pp：j < 0.05pp 的因子微调**没有可测价格差**，属亚阈噪声不必判出
  （B-only 漏判集全是这一类）。B-only 在非除权日的误报只在②兜底路径生效、后果为拒写等接口恢复，属安全侧。
- **`pre_close` 历史上是脏列，不可作校验/兜底源（2026-09-13 已全库修准，见下节）**：修前全库 4.9%（34.3 万行）的 `pre_close` 既不等于
  前一日 `raw_close`、也不与 `pct_chg` 自洽（推算残留，例 000001.SZ 20210105 起 `pre_close` 整段低约 20%，
  而 `pct_chg` 跟的是 `raw_close`）；因子真变动的 20 万行里仅 12.3% 能用 `pre_close` 反推出因子比。
- 被拦的股票须等 `adj_factor` 接口恢复后 `--force` 重算该日。

**`pre_close/pct_chg` 脏列已修准（2026-09-13，值修复、非口径变更）**：库内 `raw_*` 一直是真 tushare 原值
（全库逐值零差异），但 `pre_close/change/pct_chg` 为早期"推算"残值 —— 全库 6,991,185 可判行里
**343,167 行（4.91%）`pre_close` 与 `pct_chg` 不自洽**（既有 2dp 舍入，也有整段尺度错：000001.SZ
20210105 库内 14.86 vs 原值 18.60；300394.SZ 20210402 库内 7.91 vs 原值 41.33），且**脏值集中在
hs300 副本**（需改 294/5400 文件全在 hs300）。修法 = `code/fix_daily_from_tushare.py`：逐只
`pro.daily(ts_code, start_date, end_date)` **带边界**拉取（不带边界只返回最近 6000 行，会截掉最老段）
→ 只覆盖有差异字段（`raw_*` 差异单独记账单，实测 0）。执行结果：`pre_close` 344,068 行、`change`
233,177 行、`pct_chg` 383,225+2,226 行对齐原值，跳过 0/异常 0；**修后"store pre_close 不自洽" 0 行**，
`chk_repair_verify.py`(300 抽样) 三列差异 **0 文件**。备份 `_preclose_repair_backup/`，明细
`code/_repair_manifest_*.csv.gz`。
- **不要改回"由 adj_factor 反推 pre_close"**：因子只存 6dp，反推引入 0.005–0.009 元（≤0.1%）误差，
  且上市首日类行会真错（修后全库仍有 37,286 行 0.53% 反推不自洽）→ 取值只认 tushare 原值，派生式仅作交叉校验。
- 落盘/对账口径未变（同列名同语义），故**信号层零影响**：`grid_or.signal_detect` 的 t0 判据是把 `pct_chg`
  与板幅阈值比（9.95/19.95/29.95），2dp→4dp 判定翻转实测 0 行；`pre_close` 在策略链里无人使用。

**`adj_factor` 仍待修（2026-09-13 定位）**：跨副本同 `(code,date)` 因子不一致（例 002812.SZ hs300
3.492085 vs zz500 3.495246，1313/1366 行 >1e-4），逐池比 tushare：20210802 hs300 80.8% / zz500 33.1% /
zz1000 35.5% / zz2000 41.9%，2024 之后只剩 hs300 副本脏。修法 = `code/fix_adj_factor.py`
（`--build` 按日 `pro.adj_factor(trade_date=)` 全市场拉取建累积因子表 ≈1381 次；`--apply` 只改
`adj_factor` 一列、raw 不动、备份到 `_adjfactor_repair_backup/`）。**这是唯一进判定链的脏列
（`hfq = raw × adj_factor`），修完前 hfq 跨副本不等价。**

**上 cron 前必跑**：`python code/preflight_incremental_update.py`（合成测试树真跑增量写路径，八项校验，
⑦ = 除权守卫负向：抬前一日 `raw_close`×1.5 模拟除权 → 期望该股拒写、对照股正常写；⑧ = `exdiv_judge.py`
判据自检，含大比例分红用例）。

## 5. 为什么

- `raw`（客观事实）+ `adj_factor`（历史既定、只增）是**不变根** → 任何口径任何时刻可重新推导，**无未来函数、无漂移、可复现**。
- 未来某股除权：只需**更新该股 factor 序列**（raw 不动），hfq/qfq 自动正确。
- 落盘 qfq 的缺点（会随最新因子漂移、需重写）→ 根除。

## 6. 关键文件 / 函数

- `grid_or.py`: `_derive_split_prices`/`hfq_col`/`raw_col`
- `dashboard_data.py`: `kline_for`（派生 + `_kcol` 兜底）
- `update_daily.py`: 增量落 raw+factor
- 工具: `code/zt_add_factor.py`（补 factor）/ `zt_strip_prices.py`（删落盘 qfq/hfq）/ `zt_verify_factor.py`（核对）
- 体检: `code/chk_price_health.py`（只读；`--factor` adj_factor 覆盖 / `--preclose` pre_close 脏行占比与归属 /
  `--exdiv` 真除权日因子比吻合率）—— 支撑"hfq 精确、无需重拉；pre_close 不可作校验源"两条结论

## 7. 版本历史

- **price-schema-v1**（**2026-09-13 生效**，2026-09-12 用户批准）：两端统一为 **12列持久（`raw*4 + adj_factor` + 技术字段）+ qfq/hfq 运行时派生**，K线展示用 **qfq**。WSL 研究侧按此迁移；Mac 实操侧已同构完成（删落盘 qfq/hfq，全链路验证过）。此前研究侧 16列+adj_* 落盘+画 hfq 的结构废除。