diff --git a/docs/superpowers/specs/2026-09-22-research-to-trading-pipeline-design.md b/docs/superpowers/specs/2026-09-22-research-to-trading-pipeline-design.md index 7f3b7d40..0727b0cb 100644 --- a/docs/superpowers/specs/2026-09-22-research-to-trading-pipeline-design.md +++ b/docs/superpowers/specs/2026-09-22-research-to-trading-pipeline-design.md @@ -109,6 +109,7 @@ F 环是「管道」与「飞轮」的分界:归因发现→可能=衰减触 - 判读:等权≈ICIR=因子相关性高、加权信息少,安心用简单;LGBM 明显跑赢两者=组合方式升级叙事,且 feature importance 天然产出「组合内各因子贡献」(对接 §4.7 归因);预期两三个完整月度周期才有判读力; - 纪律:challenger 永远影子不进生产;若升级仍走决议 K 考场晋级+用户口令,与实盘升级同构。 - **班次二落地(2026-10-10)**:`sanguo_factor/challenger_lgbm.py`——v1 池=quant12 在役 12 源(扩池挂后续);三路=方向调整等权/ICIR 档案加权/LGBM(qlib 基准超参,固定 500 轮——无独立 valid 段不早停,注记在对拍件);walk-forward=前 11M 训练+末 1M 样外(test 隔离铁律:样外月绝不进训练;codex 复审加固=训练窗末 2 交易日 embargo——build_label 双 shift 前视,不 embargo 则窗口末样本的 label 吃样外月收盘=信息层泄漏;三路统一可评样本 mask、缺源/坏件整月 skip 不产半吊子件);对拍件=`challenger_lgbm/{host}_{as_of}.json`(三路样外 IC/ICIR/Q5-Q1 多空+feature importance+池冗余注记——`|corr|>0.7` 只记录不剔除,首年观察期剔除留人);挂月度链 stage6(失败不阻链);判读需累积≥2-3 个样外月。**宽表源现场核实修正**:月度批 `values/` 导出只含注册表因子(fa 族+composite),永不含 quant12 12 源——stage6 `--values-dir` 改指 stage4 截面盘 `/volume1/stock/factor_cross_section`(daily_section 幂等 merge 累积的 12 源主 parquet,12/12 在位实证),零重算同链上游供给。 + - **对拍前端可见性(2026-10-10 补,用户质问「合成无 UI」驱动;QA frontend-v2 全端点核实=无合成视图可抄,QA 全自动进化人不下场故合成黑盒——我们在环决策必须有证据面)**:合成层页(/pipeline/composite)下方第二卡「三路对拍 · 加权方式之辩」——`GET /pipeline/composite/challenge` 读报告根 `challenger_lgbm/` 最新件(as_of 最大,坏件 skip);三路对比表(样外 IC/ICIR/Q5-Q1/有效天数,每列最优高亮)+规则判读行(|ΔIC|≤0.005→「等权≈ICIR 简单加权足够」;LGBM 超 max+0.005→「非线性有增量」)+LGBM 特征贡献条+冗余 chips+「永不进生产/单月不构成结论」免责行;无件=空态文案(11-01 首考)。**修复 CLI 断链(部署验证现场抓获)**:challenger_lgbm.py 尾部漏 `if __name__ == "__main__"` guard——`python -m` 静默导入即退 exit 0,11-01 stage6 会空跑;测试直调 main(argv) 函数全绿掩盖 CLI 缝隙,guard+import sys 已补。**产物双机可见**:challenger 依赖 stage4 截面盘(NAS 数据面,VPS 无)→只在 NAS 跑;VPS 前端展示靠产物件同步 NAS→VPS(验证期手动 scp,常态化挂 11-01 前小班)。 - **test 隔离条款(2026-10-10 成文,QuantaAlpha 附录 E 纪律借鉴)**:**合成层(含 challenger)权重/训练只用截至 as_of 的数据,表现评估只看 as_of 之后——权重窗与成绩窗永不重叠**。现状核实动作=审计 quant12_v2a 权重计算代码确认滚动结构天然合规(每月 T-12~T 算权、T+1 后出成绩),核实结论落本档。防什么=将来实盘与回测打架时,回测成绩有「未被自己污染」的底气;防将来改动(全历史拟合/未来数据调参类)悄悄破坏。 核实结论(2026-10-10 审计):quant12_v2a(quant12_icirfit_v1)fit_window=2018-01~2021-06、h2 考场窗=2021-07-01 起(exam_gate DEFAULT_START,同批同 run 对照)——**无重叠**(考场首日恰后于拟合窗末月,首尾相接);v2c 滚动天然合规:Y 年制度档案 fit [Y-3 年初, Y-1 年末]、考核 Y 年段(walk-forward),且注册路径只挂载档案冻结权重(load_profile→resolve_weights),评估数据不回流拟合。 - **版本钉住+影子对照闸门(决议 A)**:策略钉住合成层具体版本;合成层变更(晋级/退役/调权)→新旧版影子对照(起步默认 4 周,首年校准)→用户口令→各策略**择期**升级。可回滚、历史可复现,与推 vps 纪律同构。 diff --git a/frontend/src/api/pipeline.ts b/frontend/src/api/pipeline.ts index 11b5d59d..307b72a5 100644 --- a/frontend/src/api/pipeline.ts +++ b/frontend/src/api/pipeline.ts @@ -110,6 +110,28 @@ export interface CompositeFamily { note: string } +// —— 三路对拍(challenger_lgbm 影子实验产物,月度链 stage6) —— + +export interface ChallengeSignal { + ic_mean: number | null + icir: number | null + q5q1: number | null + days: number +} + +export interface ChallengeDoc { + as_of: string + generated_at: string + host: string + file?: string + pool: { names: string[]; size: number } + split: { oos_month: string; note: string } + signals: Record<'equal' | 'icir' | 'lgbm', ChallengeSignal> + feature_importance: Record + redundancy_flagged: Array<{ a: string; b: string; corr: number } | Record> + lgbm_params: Record +} + export interface ResearchCardOption { level: number; action: string; meaning: string; cost: string } export interface Ruling { month: string; level: number; reason: string; decided_at: string } @@ -226,6 +248,10 @@ export async function getCompositeVersions(family?: string): Promise<{ family: s return { family: data.family, families: data.families, versions: data.items } } +export async function getCompositeChallenge(): Promise<{ challenge: ChallengeDoc | null; available: string[]; note: string }> { + return apiClient.get('/pipeline/composite/challenge') +} + export async function getLadder(): Promise { const { data } = await apiClient.get<{ items: LadderStrategy[] }>('/pipeline/ladder') return data.items diff --git a/frontend/src/views/pipeline/CompositeTree.spec.ts b/frontend/src/views/pipeline/CompositeTree.spec.ts index 126b3522..dd23fd17 100644 --- a/frontend/src/views/pipeline/CompositeTree.spec.ts +++ b/frontend/src/views/pipeline/CompositeTree.spec.ts @@ -56,3 +56,50 @@ describe('CompositeTree.vue', () => { expect(w.text()).toContain('该族暂无可展示的版本树') }) }) + +// —— 三路对拍卡(challenger_lgbm 影子产物) —— + +vi.mock('@/api/pipeline', async (importOriginal) => { + const actual = await importOriginal() + return { ...actual, getCompositeVersions: vi.fn(), getCompositeChallenge: vi.fn() } +}) + +import type { ChallengeDoc } from '@/api/pipeline' + +const chalDoc: ChallengeDoc = { + as_of: '2026-09-30', generated_at: '2026-09-30T21:40:00', host: 'nas', + pool: { names: ['alpha12', 'klow'], size: 2 }, + split: { oos_month: '2026-09', note: '' }, + signals: { + equal: { ic_mean: 0.05, icir: 0.8, q5q1: 0.01, days: 20 }, + icir: { ic_mean: 0.0505, icir: 0.81, q5q1: 0.011, days: 20 }, + lgbm: { ic_mean: 0.062, icir: 0.9, q5q1: 0.012, days: 20 }, + }, + feature_importance: { alpha12: 0.6, klow: 0.3 }, + redundancy_flagged: [{ a: 'alpha12', b: 'klow', corr: 0.73 }], + lgbm_params: {}, +} + +describe('CompositeTree.vue 三路对拍卡', () => { + beforeEach(() => vi.clearAllMocks()) + it('有件:三路表+判读行+特征贡献+冗余 chip 全渲染', async () => { + vi.mocked(api.getCompositeVersions).mockResolvedValue({ family: 'composite_quant12', families: fams, versions: compositeMock }) + vi.mocked(api.getCompositeChallenge).mockResolvedValue({ challenge: chalDoc, available: ['nas_2026-09-30.json'], note: '' }) + const w = mount(CompositeTree, { global: { plugins: [ElementPlus] } }) + await flushPromises() + expect(w.text()).toContain('三路对拍 · 加权方式之辩') + expect(w.text()).toContain('样外月 2026-09') + expect(w.text()).toContain('LGBM 样外领先') // 0.062 > 0.0505+0.005 判读分支 + expect(w.text()).toContain('alpha12') // 特征贡献 + expect(w.text()).toContain('↔ klow') // 冗余 chip + expect(w.text()).toContain('challenger 永不进生产') + }) + it('无件:空态文案(11-01 首考),版本树不受影响', async () => { + vi.mocked(api.getCompositeVersions).mockResolvedValue({ family: 'composite_quant12', families: fams, versions: compositeMock }) + vi.mocked(api.getCompositeChallenge).mockResolvedValue({ challenge: null, available: [], note: '暂无对拍数据——月度链 stage6 每月产出(11-01 首考);challenger 永不进生产(影子实验)' }) + const w = mount(CompositeTree, { global: { plugins: [ElementPlus] } }) + await flushPromises() + expect(w.text()).toContain('暂无对拍数据') + expect(w.text()).toContain('composite_quant12') // 版本树照常 + }) +}) diff --git a/frontend/src/views/pipeline/CompositeTree.vue b/frontend/src/views/pipeline/CompositeTree.vue index bb5be5a4..a3968fa7 100644 --- a/frontend/src/views/pipeline/CompositeTree.vue +++ b/frontend/src/views/pipeline/CompositeTree.vue @@ -1,7 +1,7 @@