Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# https://github.com/github/gitignore/blob/master/Python.gitignore
__pycache__/
.venv/
data/courage_strict_v1/
artifacts/courage_strict_v1/

*.pyc
*.pyd
Expand Down
40 changes: 40 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Rolexl Qlib fork: Courage Strict V1

This repository is the only active implementation workspace for `courage_strict_v1`.
The upstream framework remote is `https://github.com/microsoft/qlib.git`; the maintained fork remote is
`https://github.com/Rolexl/qlib.git`.

## Active route

- evidence route: `docs/courage_strict_v1/evidence/COURAGE_TECHNICAL_ROUTE_STRICT_20260818.md`;
- evidence SHA-256: `ef6534868cd42e554f6dc7903e35459b42ecd21c020cf83e7340ab81938d9a91`;
- implementation decisions: `examples/courage_strict_v1/configs/courage_strict_c1_implementation_decisions_v1.json`;
- implementation-decision SHA-256: `baf373d37918b9ef91efd026e25e9f55bb720b8749ce698c4de0c11de651fe45`.

This is the new V1 implementation. Its canonical machine identity is exactly `courage_strict_v1`; do not append
`reproduction`, `migration`, `ACOT`, or another project qualifier. Historical Courage evidence and later engineering
decisions remain provenance only and must not become runtime dependencies.

## Storage

- external source data: `/data1/lxl/workspace/datasets/.tmp/dataset`;
- Xianyu source: `/data1/lxl/workspace/datasets/.tmp/dataset/xianyu`;
- governed generated data: `/data1/lxl/workspace/datasets/.tmp/qlib/data/courage_strict_v1`;
- experiment artifacts: `/data1/lxl/workspace/datasets/.tmp/qlib/artifacts/courage_strict_v1`;
- project virtual environment: `/data1/lxl/workspace/datasets/.tmp/qlib/.venv`.

No active code, configuration, catalog, command or generated artifact may resolve an ACOT path. The external raw
vendor dataset remains read-only at `/data1/lxl/workspace/datasets/.tmp/dataset/xianyu`; source facts imported during
the one-time transfer must be copied under this repository's `data/courage_strict_v1/source/` identity.

Do not commit raw data, generated Qlib bins, checkpoints, predictions, or credentials. Never create or use paths
named `AQuantLab` or `AQuantLab_raw`.

## Project boundary

Only Courage Strict V1 evidence, contracts, PIT/data kernels, V1 features/labels, Qlib dataset/model integration,
tests, and new-run reports belong here. Do not import V2, V3, Long Alpha LA0--LA10, old authorizations, old
scalers, old checkpoints, optimizer state, predictions, strategy, backtest, or trading code.

All learned state must be fitted from scratch by this Qlib V1. April 2026 is Development replay only;
May is historical-consumed evidence and June remains sealed. ACC is reported as a diagnostic, not a selection gate.
46 changes: 46 additions & 0 deletions docs/TRAINING_ARTIFACT_POLICY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# 每次训练的 Train/Valid Loss 曲线强制规则

状态:`ACCEPTED_REPOSITORY_WIDE_TRAINING_RULE`。

本规则适用于此仓库之后的每一次正式模型训练,不限于某个 Courage 版本。参考图为 [Courage V1 step curve](courage_strict_v1_step_curve/TRAIN_VALID_LOSS_CURVE.png)。

## 必须记录

- 在配置冻结的每个 validation step 同时记录当前 Train window loss 与完整固定 Valid loss。
- Train loss 必须是刚结束窗口的损失,不能用从step 1开始的累计平均值冒充局部训练曲线。
- Valid population、Label mask、scaler和选择loss在一次运行中必须固定。
- `loss_curve.json` 必须保留原始逐点值、global step、学习率、样本覆盖和best checkpoint选择指标。

## 必须绘图

每次训练必须生成:

1. 运行目录中的 `TRAIN_VALID_LOSS_CURVE.png`;
2. 训练报告文档目录中的同名字节副本;
3. 图中使用global optimizer step作为横轴,绘制未平滑的Train与Valid loss,并标出best step;
4. 运行目录和训练报告目录中的 `PER_HEAD_TRAIN_VALID_LOSS_CURVES.png`,分别展示每个horizon的Train-window与完整固定Valid曲线;
5. 训练报告必须链接两张图,并写明验证间隔、总验证点数和best step。

gradient/LR曲线建议同时生成,但不能替代上述两张Train/Valid曲线。

## 最佳checkpoint分组诊断

日期、分钟位置、PIT行业和换手率分组不在每个validation step重复计算。训练完成并冻结best checkpoint后,必须对完整固定Valid自动执行一次,生成`BEST_CHECKPOINT_GROUPED_METRICS.csv`或内容等价且被manifest固定的产物。

- 分组至少覆盖`date`、`minute_slot`、`industry_id`、`turnover_band`;
- 必须覆盖所有已训练horizon,并保存样本数、RMSE、bias、ACC、BAcc、MCC、AUC、target/prediction up rate;
- 该步骤只允许读取训练时已经授权的固定Valid,不能用分组结果重新选择checkpoint;
- 若基线门要求先比较zero、Train mean/median、momentum/reversal和Ridge,则分组诊断应在同一post-training closure中完成。

## 完成门禁

以下任一情况都标记为 `TRAINING_ARTIFACTS_INCOMPLETE`:

- 缺少`loss_curve.json`或`TRAIN_VALID_LOSS_CURVE.png`;
- 缺少逐horizon Train/Valid曲线或best-checkpoint分组指标;
- 图与JSON的step或best checkpoint不一致;
- 文档副本缺失或与运行目录不是相同字节;
- 只有epoch末值,无法观察epoch内部变化;
- 用平滑值替换原始loss,或者Train和Valid使用不同选择口径。

该门禁只约束训练可观测性,不授权读取新数据、训练、refit、策略、回测或交易。
162 changes: 162 additions & 0 deletions docs/courage_strict_continuous_v1/BASELINE_CLOSURE_EVALUATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Courage Strict Continuous V1 测评基线闭环

> 冻结 step-2250 checkpoint 的 Train-only baseline 与完整 January rolling Valid 诊断;不是未见 Test。

## 结论

- 门禁:`FAIL_BASELINE_GATE`;平均 best-baseline RMSE skill `-0.184%`,正 skill `0/7`。
- 当前模型未改动,checkpoint 选择未重做;全部 baseline 在完全相同的 Valid key/Label mask 上计算。

## 诊断解读

- 模型输出明显收缩:七个 horizon 的 `prediction_std / target_std` 仅 `1.0%`~`12.4%`;5m/15m prediction up rate 分别只有 `0.054%`/`0.405%`。
- Target 水平发生时间漂移:120m Train→Valid mean 为 `-0.022%`→`+0.075%`,240m 为 `-0.037%`→`+0.140%`,480m 为 `+0.048%`→`+0.430%`。
- 240m/480m 仍有弱排序信息(AUC `54.180%`/`54.381%`,Rank IC `+0.06176`/`+0.05940`),但绝对收益点预测均输给常数基线;这是“排序信号存在、条件均值校准失败”,不能视为收益回归通过。
- 最强 RMSE baseline 分布为:5/15/30m Ridge,60/120/240m zero,480m Train mean;继续增加 epoch 或更换网络不能直接解释这一模式。

## 三项 Sanity Check 闭环

基线闭环之后,对同一 `courage_strict_continuous_v1` 数据链路和模型实现执行了三项有界 sanity check。它们用于区分明显的数据/实现错误与真实的泛化失败,不用于重新选择或晋升 checkpoint。

| 检查 | 执行口径 | 结果 | 结论 |
|---|---|---:|---|
| 原始 Bar Label 独立复算 | 每个 horizon 时间分层抽取15条,共105条;从不可变分钟 Parquet 以 `amount/volume` 重算 entry/exit VWAP 和收益,再与 provider float32 bin 做 bit-level 比较 | 105/105 bit-exact;最大误差0 | `PASS_LABEL_RECOMPUTATION` |
| 固定小样本过拟合 | 固定512条七头均成熟样本;dropout=0、weight decay=0;最多3000 steps | 标准化 Huber `0.585392 → 0.00009093`,下降99.984%;七头全部过门 | `PASS_SMALL_SAMPLE_OVERFIT` |
| Shuffled-label 随机对照 | Train 标签按 horizon 独立置换;保留输入、mask和完整 January Valid | 首轮3 seeds × 7 heads 为19/21过门,480m与15m各有一个孤立越界 | 首轮严格记为 `FAIL_SHUFFLED_LABEL_RANDOM_CONTROL` |

### Shuffled-label 后续复核

由于首轮两个异常没有在同一 horizon 跨 seed 重复,随后预注册并执行了更强的 null-distribution 复核:10个配对 seed;每个 seed 先评估 initialization-only,再训练500-step shuffled-label;两种条件都评估完整 January Valid,并以交易日为单位进行3日 circular moving-block bootstrap(5,000次)。

升级泄漏调查必须同时满足:

1. shuffled 聚合95% CI排除随机值;
2. shuffled−initialization 配对95% CI同方向排除0;
3. 至少4/10 seed的单独交易日 block CI同方向排除随机值。

最终没有任何 horizon/metric 同时满足三项条件,判定为 `PASS_NO_REPEATABLE_SHUFFLED_LABEL_LEAKAGE_PATTERN`。原两个异常的关键复核如下:

| 指标 | Shuffled mean [95% CI] | Shuffled−Init [95% CI] | 泄漏升级 |
|---|---:|---:|---|
| 480m AUC | `0.50940 [0.49695, 0.52223]` | `0.01166 [-0.00876, 0.03148]` | NO |
| 480m Rank IC | `0.00857 [-0.00946, 0.02733]` | `0.01027 [-0.02200, 0.04137]` | NO |
| 15m Rank IC | `-0.00763 [-0.01637, 0.00046]` | `-0.01665 [-0.03160, -0.00215]` | NO |

综合解释:已抽查 Label 的公式与落盘值正确;模型、mask、标准化和优化器具备正常记忆能力;没有发现可重复的 shuffled-label 泄漏模式。因此“明显实现错误或稳定泄漏”从首要原因中降级,但这不改变正式模型 `FAIL_BASELINE_GATE`,下一步仍应聚焦输出收缩、绝对收益条件均值校准和时间状态漂移。

详细证据:

- [三项 Sanity Check 报告](SANITY_CHECK_REPORT.md)
- [Initialization-only × Shuffled-label Null Distribution](SANITY_NULL_DISTRIBUTION_REPORT.md)

## 逐 horizon 最强基线

| H | Model RMSE | Best baseline | Baseline RMSE | Skill | ACC | BAcc | MCC | AUC | Rank IC | Pred/Target Std |
|---:|---:|:--|---:|---:|---:|---:|---:|---:|---:|---:|
| 5 | 0.00426189 | ridge_terminal | 0.00425595 | -0.140% | 51.845% | 50.006% | +0.00250 | 50.942% | +0.03486 | 0.010 |
| 15 | 0.00762374 | ridge_terminal | 0.00761276 | -0.144% | 51.818% | 50.037% | +0.00589 | 51.628% | +0.04646 | 0.023 |
| 30 | 0.01070863 | ridge_terminal | 0.01069625 | -0.116% | 51.536% | 50.289% | +0.01756 | 52.362% | +0.05342 | 0.026 |
| 60 | 0.01494003 | zero | 0.01492692 | -0.088% | 51.826% | 51.207% | +0.03565 | 52.852% | +0.05751 | 0.034 |
| 120 | 0.02058491 | zero | 0.02056740 | -0.085% | 52.171% | 52.160% | +0.04499 | 53.319% | +0.05724 | 0.070 |
| 240 | 0.02858734 | zero | 0.02849929 | -0.309% | 52.449% | 52.755% | +0.05711 | 54.180% | +0.06176 | 0.102 |
| 480 | 0.04235313 | train_mean | 0.04218151 | -0.407% | 53.177% | 52.997% | +0.06004 | 54.381% | +0.05940 | 0.124 |

## 全部 estimator RMSE

| H | Estimator | RMSE | Skill vs best baseline | BAcc | MCC | AUC | Rank IC |
|---:|:--|---:|---:|---:|---:|---:|---:|
| 5 | model | 0.00426189 | -0.140% | 50.006% | +0.00250 | 50.942% | +0.03486 |
| 5 | zero | 0.00425740 | -0.034% | 50.000% | +0.00000 | 50.000% | +nan |
| 5 | train_mean | 0.00425716 | -0.028% | 50.000% | +0.00000 | 50.000% | +nan |
| 5 | train_median | 0.00425970 | -0.088% | 50.000% | +0.00000 | 50.000% | +nan |
| 5 | momentum_ret5 | 0.00659167 | -54.881% | 48.844% | -0.02358 | 48.693% | -0.03358 |
| 5 | reversal_ret5 | 0.00646818 | -51.980% | 50.853% | +0.01717 | 51.307% | +0.03358 |
| 5 | ridge_terminal | 0.00425595 | +0.000% | 51.088% | +0.02257 | 51.396% | +0.05035 |
| 15 | model | 0.00762374 | -0.144% | 50.037% | +0.00589 | 51.628% | +0.04646 |
| 15 | zero | 0.00761525 | -0.033% | 50.000% | +0.00000 | 50.000% | +nan |
| 15 | train_mean | 0.00761510 | -0.031% | 50.000% | +0.00000 | 50.000% | +nan |
| 15 | train_median | 0.00762208 | -0.123% | 50.000% | +0.00000 | 50.000% | +nan |
| 15 | momentum_ret5 | 0.00918254 | -20.620% | 48.389% | -0.03288 | 48.014% | -0.03202 |
| 15 | reversal_ret5 | 0.00894093 | -17.447% | 51.372% | +0.02763 | 51.986% | +0.03202 |
| 15 | ridge_terminal | 0.00761276 | +0.000% | 51.177% | +0.02354 | 51.528% | +0.05941 |
| 30 | model | 0.01070863 | -0.116% | 50.289% | +0.01756 | 52.362% | +0.05342 |
| 30 | zero | 0.01069731 | -0.010% | 50.000% | +0.00000 | 50.000% | +nan |
| 30 | train_mean | 0.01069742 | -0.011% | 50.000% | +0.00000 | 50.000% | +nan |
| 30 | train_median | 0.01071025 | -0.131% | 50.000% | +0.00000 | 50.000% | +nan |
| 30 | momentum_ret5 | 0.01185302 | -10.815% | 48.863% | -0.02322 | 48.765% | -0.02358 |
| 30 | reversal_ret5 | 0.01166981 | -9.102% | 50.760% | +0.01531 | 51.235% | +0.02358 |
| 30 | ridge_terminal | 0.01069625 | +0.000% | 51.105% | +0.02211 | 51.531% | +0.06406 |
| 60 | model | 0.01494003 | -0.088% | 51.207% | +0.03565 | 52.852% | +0.05751 |
| 60 | zero | 0.01492692 | +0.000% | 50.000% | +0.00000 | 50.000% | +nan |
| 60 | train_mean | 0.01492926 | -0.016% | 50.000% | +0.00000 | 50.000% | +nan |
| 60 | train_median | 0.01495127 | -0.163% | 50.000% | +0.00000 | 50.000% | +nan |
| 60 | momentum_ret5 | 0.01581712 | -5.964% | 48.835% | -0.02379 | 48.828% | -0.01629 |
| 60 | reversal_ret5 | 0.01558638 | -4.418% | 50.570% | +0.01148 | 51.172% | +0.01629 |
| 60 | ridge_terminal | 0.01493721 | -0.069% | 50.907% | +0.01841 | 51.739% | +0.06404 |
| 120 | model | 0.02058491 | -0.085% | 52.160% | +0.04499 | 53.319% | +0.05724 |
| 120 | zero | 0.02056740 | +0.000% | 50.000% | +0.00000 | 50.000% | +nan |
| 120 | train_mean | 0.02057639 | -0.044% | 50.000% | +0.00000 | 50.000% | +nan |
| 120 | train_median | 0.02061390 | -0.226% | 50.000% | +0.00000 | 50.000% | +nan |
| 120 | momentum_ret5 | 0.02125616 | -3.349% | 48.777% | -0.02500 | 48.955% | -0.01372 |
| 120 | reversal_ret5 | 0.02101251 | -2.164% | 50.286% | +0.00577 | 51.045% | +0.01372 |
| 120 | ridge_terminal | 0.02060984 | -0.206% | 51.583% | +0.03285 | 52.780% | +0.07312 |
| 240 | model | 0.02858734 | -0.309% | 52.755% | +0.05711 | 54.180% | +0.06176 |
| 240 | zero | 0.02849929 | +0.000% | 50.000% | +0.00000 | 50.000% | +nan |
| 240 | train_mean | 0.02851954 | -0.071% | 50.000% | +0.00000 | 50.000% | +nan |
| 240 | train_median | 0.02857717 | -0.273% | 50.000% | +0.00000 | 50.000% | +nan |
| 240 | momentum_ret5 | 0.02892367 | -1.489% | 49.439% | -0.01146 | 49.866% | -0.00517 |
| 240 | reversal_ret5 | 0.02889172 | -1.377% | 49.676% | -0.00654 | 50.134% | +0.00517 |
| 240 | ridge_terminal | 0.02861314 | -0.399% | 53.032% | +0.06342 | 54.443% | +0.06444 |
| 480 | model | 0.04235313 | -0.407% | 52.997% | +0.06004 | 54.381% | +0.05940 |
| 480 | zero | 0.04222734 | -0.109% | 50.000% | +0.00000 | 50.000% | +nan |
| 480 | train_mean | 0.04218151 | +0.000% | 50.000% | +0.00000 | 50.000% | +nan |
| 480 | train_median | 0.04235324 | -0.407% | 50.000% | +0.00000 | 50.000% | +nan |
| 480 | momentum_ret5 | 0.04250367 | -0.764% | 49.538% | -0.00942 | 49.953% | -0.00054 |
| 480 | reversal_ret5 | 0.04249819 | -0.751% | 49.663% | -0.00680 | 50.047% | +0.00054 |
| 480 | ridge_terminal | 0.04234481 | -0.387% | 54.407% | +0.08807 | 55.747% | +0.06072 |

## 逐日稳定性摘要

| H | Days | IC>0 days | BAcc>50% days | Mean daily MCC |
|---:|---:|---:|---:|---:|
| 5 | 20 | 19 | 12 | +0.00176 |
| 15 | 20 | 19 | 17 | +0.00614 |
| 30 | 20 | 19 | 19 | +0.01776 |
| 60 | 20 | 18 | 19 | +0.02985 |
| 120 | 20 | 16 | 18 | +0.03496 |
| 240 | 19 | 14 | 15 | +0.04083 |
| 480 | 18 | 9 | 15 | +0.05124 |

## Prediction 收缩

`Pred/Target Std` 显著小于 1 表示模型输出接近常数;完整均值、标准差和分位数见 `target_prediction_distribution.csv`。

## 产物

- `baseline_metrics.csv`:全部 estimator × horizon 指标;
- `daily_metrics.csv`:逐日 IC/BAcc/MCC/RMSE;
- `grouped_model_metrics.csv`:日期、分钟位置、行业和换手率分组;
- `feature_distribution.csv`:Train/Valid 终点特征缺失率、median/IQR;
- `target_prediction_distribution.csv`:Target/Prediction 均值、标准差、分位数和上涨比例;
- `baseline_predictions.parquet`:同人口 baseline 预测;
- `ridge_coefficients.json`:Train-only Ridge 系数和拟合人口。
- `sanity_checks_v1/`:Label逐条复算、小样本过拟合曲线与3-seed shuffled-label结果;
- `sanity_null_distribution_v1/`:10-seed配对日级指标、bootstrap摘要与最终机器判定。

## 边界

- Ridge 只使用信号时点的 12 个动态特征、5 个慢特征、缺失指示和时钟变量;不使用 Valid 拟合或调参。
- Momentum/Reversal 是可观测 `stock_ret_5` 的原值/相反数,缺失时预测 0。
- 基线评测本身未重新训练 PatchTST;其后的三项 sanity check 使用独立诊断命名空间,未覆盖或晋升正式 checkpoint。
- 未重训或改选正式 PatchTST;仅训练不可晋升的 sanity 诊断模型。未读取 2026-02-02 及以后数据,未执行 refit、策略、回测、交易或远端推送。

## 后续 March 漂移诊断闭环(只读)

基线评测完成后,另以冻结 step-2250 checkpoint 及既有 January—March 预测补齐了以下事后诊断:

- March 逐日 prediction/target 均值、标准差、上涨率、BAcc、MCC 与 Rank IC;
- 按 30 分钟位置桶、PIT 行业和 T-1 换手率档分组;
- Train→January→February→March 的特征缺失率、median/IQR、PSI 与标签分布漂移;
- 每期系统抽样 200,000 条序列,扫描完整 1,200 分钟内部历史位置的可用率和连续缺失长度。

结果显示 March 慢特征及绝对收益基准发生明显漂移,而完整 lookback 动态特征平均可用率仍约 90%,不支持“历史窗口因大面积缺失而失效”。详细结果见 [March 漂移诊断闭环](MARCH_DRIFT_DIAGNOSTIC_CLOSURE.md)。该后续诊断读取范围严格早于 2026-04-01;未重训、改选 checkpoint 或读取 April 及以后数据。
Loading