27 KiB
量化因子导出与合成设计 v2.0 · 交叉评审(2026-07-26)
评审范围:
akg-factor-bridge全部代码 +量化因子导出与合成设计.mdv2.0 + astock-kg 供数侧 (transmission.py/hotspot.py/chainmap.py/market_snapshot.py/themes.py/graph_store.py/claim_store.py/enums.py/factor_coverage_probe.py)+ 相关设计文档。 结论按"必须修 / 需要决策 / 需要知道"三档排列,每条附代码出处。
0. 总体判断
方向可行,我认同 v2.0 的转向。 §2.2 的「观察 vs 推断」类型学是全文最有价值的部分: 传导 = 当日 movers × 当前态图谱,两个输入都无耐久历史,回填必然携带成员性前视 —— 这个论证 成立,而且是三条否决理由里唯一决定性的一条(另两条只是"效果不好",这一条是"方法上不可能")。 把回测从生死闸降为仪表盘,在一个自己承认"无法准确估值"的领域里是正确的取舍。
但 S1 现在有 3 处会让因子"看起来在跑、实际不成立"的硬伤,1 处方法论表述与实际行为不符, 以及 1 处架构级缺口(赛道门槛 C 在当前四视图上无法实现)。 这 5 项都在 G3 之前必须处理, 其中 1 项(快照冻结)每晚一天就永久少一天历史。
一句话版本:代码质量不错、点时纪律做得比多数量化工程好;问题全部集中在"桥以外" —— 桥假设上游是确定的、可复现的、可查询的,而这三条上游都不完全满足。
1. 必须修:三处硬伤
硬伤 1 · 第一权重项不可复现,且被两道截断天花板锁住
transmission.py:116 在装配 quiet 落库字段时写的是:
for m in quiet[:12]: # ← 只有前 12 个未动成员进 quiet JSONB
叠加 scan() 的 max_candidates: int = 12(transmission.py:48),
v_factor_transmission 的结构上限就是 12 × 12 = 144 行/日 —— 实测 83 行完全吻合,
说明它不是"覆盖稀薄",而是被一个为旁批/展示写的截断锁住了。
更麻烦的是这 12 个是哪 12 个。quiet 来自 graph_store.topic_context("Segment", name),
其 Cypher 是:
MATCH (c)-[m:IN_SEGMENT {status:'active'}]->(t:Segment {segment_name:$id})
RETURN ... LIMIT $cap -- cap 默认 30,且没有 ORDER BY
三个后果:
- 同一天重跑
transmission_scan可能得到不同的quiet集合与不同的moved_ratio—— 顺序由 Neo4j 执行计划决定。桥内写入是幂等的,但上游不是,所以判收标准 §10 「重跑不产生重复或漂移」按现状无法成立。 - 成员数 > 30 的大环节,
members_total/moved_ratio建立在一个任意的 ≤30 抽样上, 而moved_ratio直接是factor_value的一个乘数因子。 - 哪只股票能进因子表,事实上由数据库返回序决定。
修法(基座侧,属确定性缺陷修复,不算"新增计算",不破 §4 铁律)
topic_context加ORDER BY coalesce(c.ts_code, c.entity_key),cap提高或参数化;transmission.scan落库的quiet存全量未动成员,旁批 prompt 仍只喂前 12 (两个用途本来就该分开);- 视图额外暴露
members_total/moved,让桥能自检members_total == 30这类可疑值。
硬伤 2 · n_paths 被重复计数系统性抬高
v_factor_transmission 用 jsonb_array_length(t.paths) 当路径数
(sql/astock_kg_slot_views.sql:56),而 paths 是没有去重的:
graph_store.cascade()主干是变长边-[:SEGMENT_UPSTREAM_OF*1..N]->, 变长匹配会把每种长度的路径各返回一条 ——A→B与A→X→B同时出现、末节点都是 B; 去重只做了路径内去环,跨路径不去重,且LIMIT 60无ORDER BY。graph_store.transmission_targets()三个桶(updown / supply / drives)各自 DISTINCT, 但桶间用out += ...简单拼接,同一 target 可以从 supply 和 drives 各来一次。transmission._add()对每条(source, target, via)都 append 一条。
于是 n_paths 既不是"多少个源指向它",也不是"多少条独立路径",而是"多少次被提及"。
而 factor_value = n_paths × (1 − moved_ratio),n_paths 是主量级 —— 排序偏差直接传导到因子。
修法(只改视图,零基座改动,成本最低的一条) 把它改成 distinct source 数, 这也更贴合传导模块自己写的语义"多源汇聚 = 传导逻辑更硬":
CREATE OR REPLACE VIEW v_factor_transmission AS
SELECT t.scan_date,
q->>'ts_code' AS ts_code,
(SELECT count(DISTINCT p->>'source')
FROM jsonb_array_elements(COALESCE(t.paths, '[]'::jsonb)) p) AS n_sources,
jsonb_array_length(COALESCE(t.paths, '[]'::jsonb)) AS n_paths_raw,
t.moved_ratio,
t.members_total, -- 桥用来识别 ≤30 截断
t.moved
FROM transmission_candidates t
CROSS JOIN LATERAL jsonb_array_elements(COALESCE(t.quiet, '[]'::jsonb)) AS q
WHERE q->>'ts_code' IS NOT NULL AND q->>'ts_code' <> '';
桥侧 build_transmission 改用 n_sources,n_paths_raw 留着当观察列。
硬伤 3 · 事件回填会静默返回空(README 里那条示例命令现在就是无效的)
factors.build_event 用 common.trading_days(start, end) 当交易日历(factors.py:137),
而 common.trading_days 查的是热度表:
# common.py:24-30
"SELECT DISTINCT trade_date FROM stock_fund_heat_scores WHERE trade_date BETWEEN %s AND %s"
热度最早只有 2025-03-26(文档 §6.2 自己记着)。所以:
python run.py build akg_event --mode history --start 2024-01-01 --end 2026-07-24 # README 第 58 行
→ cal 为空 → return _EMPTY → 只打印「无数据(跳过)」,不报错、不告警。
文档 §7 声称事件可回填到 2014 年,实际上 2025-03-26 之前一天都拿不到。
修法:日历改用 gp_day_data 的 DISTINCT timestamp(5584 天,覆盖全历史),
热度表最多作为并集补充。顺带在 cal 为空时打印告警而不是静默跳过。
附带两条:放量时会炸,现在不炸
_read_gp_price不带 universe 过滤也不分块(factors.py:64-67):单日 ~5000 行没问题, 但--mode history --start 2006-01-01会把千万级行拉进 pandas。加AND \{col}` IN (...)`- 按月分块。
write_factor单事务executemany全量(common.py:69-75):热度全史约 322 × 1674 ≈ 54 万行一次提交,走 ShardingSphere 代理有风险。按日期分块提交。 另外write_factor(mode)这个参数收了但没用到,可以删。
2. 需要决策:0.5 / 0.3 / 0.2 实际上是"传导优先",不是"加权混合"
这是我最想让你在 G3 开工前想清楚的一条,因为它是逻辑选择而不是回测选择。
传导在池内约 95% 为 0(实测 83/1675)。对一列 95% 是 0 的数做
(x − mean) / std,std 完全由那 5% 决定,于是非零值的 z 落在 +4~+5,零值落在 −0.2 附近,
0.5 · z_T 的组间落差约 2.2;而 0.3 · z_H + 0.2 · z_V 用稳健 z 之后全幅只有约 1.7。
落差大于全幅 → 跨组不可逆。
我做了数值验证(200 只候选、其中 10 只有传导、其余口径按 §3.4 实现,600 次重复):
| w_T | top20 中传导票 | top20 中非传导票 |
|---|---|---|
| 0.50(当前) | 9.8 / 10 | 10.2 |
| 0.30 | 9.1 / 10 | 10.9 |
| 0.20 | 7.8 / 10 | 12.2 |
| 0.15 | 6.6 / 10 | 13.4 |
| 0.10 | 4.7 / 10 | 15.3 |
也就是说:当前参数下,有传导的票几乎必然全部进入前列;0.3 和 0.2 只在"有传导组内"
和"无传导组内"各自排序,跨组不起作用。 要让"很冷很便宜的非传导票"真有机会赢过
"边际传导票",w_T 得降到 0.10~0.15。
两条路,选一条并写进文档:
- A(我推荐,而且我认为它更贴合你原话"传导第一"):承认它是两段式,直接写成
primary = 传导档位(0 / 1 / 2),tiebreak = 0.6·z_H + 0.4·z_V。 好处:语义与实际行为一致、可解释、调试时能一眼看出是哪一段在起作用、没有隐藏非线性。 代价:无。这同时把 §9-9 的「还没热该不该做成条件项」自然解决了 —— 现状在 95% 稀疏下已经等价于条件项了,不如写明。 - B:真要连续混合 →
w_T ≈ 0.12,或对z_T做有界变换(如clip(z,±3)/3 × 1.5, 实测能把 9.8 拉到 7.5)。
顺带一个共线性提醒:门槛② (upside ≥ θ_v) 与 z_V (upside) 是同一个变量进两次;
而"还没热"与"还便宜"在 A 股高度共线(涨上去了 → 又热又贵)。
建议 G1 的 probe 直接加一节输出池内 (z_T, z_H, z_V) 的相关矩阵 ——
如果 corr(z_H, z_V) > 0.5,那"三项加权"实际是"两项",权重讨论要重开。 这一节几乎零成本。
3. 架构缺口:赛道门槛 C 在当前四视图上无法实现
图谱在 Neo4j,不在 PostgreSQL(graph_store.py:17 起全走 neo4j driver)。PG 侧
industry_pools 的完整 DDL 只有 4 列(claim_store.py:439-445):
theme TEXT PRIMARY KEY, members JSONB NOT NULL, stats JSONB, refreshed_at TIMESTAMPTZ
members 元素只有 ts_code / name / tier / confidence / supporting ——
成员级既没有 segment,也没有 layer。环节归属是读时从 Neo4j 重算的
(chainmap.py:61 → graph_store.chain_view(theme))。
所以 frontier_tracks.yml 里规划的 kg_segments / kg_concepts 两个映射,
在现有四个只读视图上没有数据源。三条路:
- 从 claims 重建(可行,一条 SQL:
WHERE predicate='IN_SEGMENT' AND object_type='Segment', 再 joinentity_links/company_master取 ts_code)。但claims是融合前的只追加表, 没有status/supersede/ tier 裁决(那些只长在 Neo4j 边上),会把已被顶替的历史归属 一起捞出来;且 object 侧没有 norm 生成列,同义环节不会合并。可以救急,不能当正解。 - 桥直连 Neo4j —— 破坏"桥只连三库"的设计,且把裁决逻辑复制进桥。不推荐。
- 推荐:基座加投影表 + 第五、第六个只读视图。
v_factor_segment_members(segment_name, chain, ts_code, tier, status, updated_at)与v_factor_segment_edges(up, down),由基座现有的 Neo4j 投影 beat 顺手物化到 PG。 这是投影不是打分,不破 §4 的"零新增因子计算",桥仍然只连三库。probe.py:12-13的注释其实已经想到了这件事("G2 把池清单固化成第五个插槽视图"), 只是低估了它的必要性 —— 它不是优化,是 C 门槛能不能实现的前提。
3.1 四层枚举(材料→设备→制造→应用)在系统里根本不存在
全仓唯一的层级来源是 chainmap._topo_layers()(chainmap.py:310-336)的 Kahn 拓扑 depth:
per-theme、相对、孤点与环上节点为 None,标签由 _layer_label 现算成上游/中游/下游,
不落库。而且"不存层级"是刻意的 —— enums._SEGMENT_BAD_SUBSTR 把
「上游 / 中游 / 下游 / 环节 / 场景 / 领域」列为环节名禁用词素,注释写明
"链位置由图上边表达"。
结论:track_members.layer 只能在 yml 里人工指定,并记一列 layer_source
(yml / graph_depth / unknown)。而且 S1 的公式压根不用 layer ——
建议把它降级为可选元数据,不要让它阻塞 G2。
3.2 赛道覆盖风险的具体量级
industry_pools 的起点是人工建的两个池(光通信 / 消费电子,见 涌现式应用层重构设计.md:30),
之后靠周一 refresh_pools 自动收录达标涌现主题(≥3 环节且 ≥3 上市成员,
graph_store.EMERGE_MIN_SEGMENTS/EMERGE_MIN_LISTED)。theme 是 IN_SEGMENT.chain 修饰符原文,
过 segment_name_ok 卫生闸(≤12 字中文裸名)。
在这个生成机制下,核聚变 / 低空经济 / 商业航空航天大概率查不到 —— 它们需要有人先投过对应的产业链研报。文档 §3.2 已经把这列为"关键前置风险",但给的对策 ("覆盖为空的赛道宁可先不放进来")会让 C 塌成「通信 + 算力 + AI」,也就是 退化成"电子 + 计算机",门槛几乎不筛东西。
建议 C 做成两级并强制标注来源:
| source_rule | 证据强度 | 来源 |
|---|---|---|
graph |
强,可审计到具体 claim | IN_SEGMENT / HAS_CONCEPT 边 |
fallback |
弱,仅作占位 | 概念标签命中 / 申万行业白名单 |
track_members 里逐行记 source_rule,仪表盘里把两级分开看。这样 C 既不会因为图谱覆盖
不足而变空集,也不会把弱证据伪装成强证据。同时,覆盖为空的赛道本身就是一张采集清单
—— 这正是基座「需求闭环」的用法,比放弃赛道有价值。
4. 逻辑一致性:v2.0 把 v1.1 一个仍然成立的开放问题删掉了
v1.1 §8-2 问过「覆盖池历史快照缺失:回填期 universe 用当前池近似(有轻微前视) 还是曾入池并集?」—— v2.0 的 §9 十个开放问题里没有这一条。
但按 §2.2 自己的论证,这一条现在更重要:
v_factor_universe= 全部industry_pools成员并集,而industry_pools只存最新态、每周一自动长大(beat 表:池刷新周一 6:00)。- §7 判定"可直接回填"的三路(heat / event / upside)全部经过这个 universe 过滤。 于是它们的值是点时正确的,成员性却是前视的 —— 用今天的池去筛 2025 年的历史, 而今天的池里有大量主题是 2026 年的研报才长出来的。这就是 §2.2 批评传导时用的同一条论证, 只是上升了一层。
- 更直接的工程后果:
z统计量(mean / std / median / MAD)在每周一发生结构性跳变, IC 序列和分层读数会带一个周期性伪影。
建议(两条一起做,都很便宜):
- 子因子表去掉 universe 过滤,全市场出行。理由:子因子表的定位是"可独立观察的仪表", 过滤只该发生在消费端。行数代价很小(heat 池内 1674 → 全市场约 5000), 换来的是三张点时干净的面板,且平台上的子因子 IC 读数才有市场级意义。
- universe 只在
akg_score侧当过滤器,并每日落快照(见下节)。 基座侧同步加一张industry_pools_history(snapshot_date, theme, members), 由refresh_pools顺手写。
5. 今天最该做、也是唯一不可补救的一件事:开始冻结快照
传导、industry_pools、Segment 边、前复权 close —— 这四样都是"过期即不可复原":
- 传导:§2.2 已经论证清楚。
- 池 / Segment 边:随研报持续生长,只存最新态。
- 前复权 close 比文档写的更麻烦一层:
gp_day_data是前复权、锚在最新日, 所以每次有股票除权,历史 close 会被整体重写 ——akg_upside的历史值因此 不可复现(今天回填的和三个月后回填的不是同一组数)。文档 §7 只提了 "除权点系统性偏移",实际还叠加一个可复现性问题。
而 §10 写的「GRU 复活条件:传导积累出 ≥1 年 live 历史」—— 这个计时器只有在开始存快照的那一天才启动。 现在已经过去 3 天(07-11 起), 每晚一天就永久少一天。
建议在 G1 之前插一个 G0.5「冻结与修复」里程碑,做三件事,都不依赖赛道清单:
- 输入冻结:每日把
universe/ 四路原始值 / 门槛判定结果 / 最终分 落一份data/frozen/YYYY-MM-DD.parquet(桥自己的data/,与赛道快照同一机制)。 这一条同时满足 §10 的「重跑不产生漂移」、「赛道成员快照可审计」、 以及未来任何回溯需求 —— 是全文性价比最高的工程动作。 - 修掉第 1 节的三个硬伤。
- 基座开始积累
industry_pools/ Segment 边的每日(或每周)快照。
6. 供数侧需要知道的事实(会影响读数怎么解释)
6.1 传导链条的上游有两张空表 —— 这才是 83 行/日的真正原因
数据源盘点.md §1a 记着:gp_sector_daily 当前空表、gp_market_sentiment 当前空表、
t_signal_daily_results 05-15 停更。于是 hotspot._pick_anomalies 的四路异动源里:
| 源 | 状态 |
|---|---|
① 板块相对强度(gp_sector_daily) |
❌ 空表 |
| ② 概念 / 行业资金净流入 | ✅ |
③ 池内个股(pct_change ≥ 5 或 total_score ≥ 0.9) |
⚠️ 只剩涨幅门(评分停更) |
| ④ 事件 / 盘中流雷达 | ✅ |
本来最该驱动"板块传导"的板块级异动源现在是缺的。
所以传导稀薄不主要是"图谱覆盖不够",而是「空表 + 12×12 截断」两件事叠加。
要提传导覆盖,优先补 gp_sector_daily,比调任何参数都有用。
6.2 movers / quiet 不看 scan_date
hotspot._movers_set() → _latest_mkt("stock") → SELECT max(trade_date) FROM mkt_daily
(hotspot.py:45-55, 86-89)。也就是用最新快照,不是 scan_date 当天的快照。
live 同日运行时两者一致,没问题。但如果 17:30 的 sync_market 失败或晚点,
18:15 的传导扫描会用 T−1 的 movers,写成 scan_date = T,而桥完全无从察觉 ——
第一权重项拿到一份"陈旧数据贴着新鲜标签"。
建议:基座加一道 freshness 断言(mkt_daily 无 scan_date 当日 stock 行则 abort scan),
并在视图里带上实际用到的 mkt_trade_date 让桥能自检。
这也是除 §2.2 之外,传导不该回填的第二个独立的、机械层面的理由 —— 值得补进文档。
6.3 一致预期的口径细节(影响门槛②怎么理解)
sync_consensus(market_snapshot.py:493-588):
asof_date = date.today(),聚合窗口report_date >= today − 90d→ 桥的merge_asof(backward)点时正确 ✅,这块没问题。target_mid_avg是90 天内全部研报行的简单平均(market_snapshot.py:548遍历的是rows而不是qrows),不按机构去重、不按时间加权。 一家券商 90 天发 5 篇就有 5 倍权重;89 天前的目标价与今天的等权。- 实测
max_price非空仅 0.4% /min_price31%(docstring 记录),所以"目标价中枢" 主要由单值目标价构成,"中值"的语义比文档写的弱。
含义:θ_v = 0 这道门槛在急涨阶段会自动收紧(分母是今天的价、分子是最长 90 天前的目标价), 它天然带反动量色彩。这和你的漏斗逻辑同向(涨上去了就没容错度), 但要知道它不是一道纯"估值"门槛 —— 这也加重了第 2 节说的三项共线性问题。
6.4 §9-6(upside 历史重建 甲 / 乙):甲案,成本远低于文档估计
sync_consensus 只要把 date.today()(market_snapshot.py:529)和
since(:507)参数化成一个 asof: date,就能对任意历史日重放同一套聚合逻辑,
live 与 history 共用一份代码、零漂移风险。改动量大约 3 行。
乙案(桥侧自行聚合 gp_report_rc)会让聚合逻辑在基座和桥各存一份 ——
这正是文档自己指出的漂移风险,而甲案的"工作量在基座侧"这个缺点被高估了。
回填时记得把用到的 close 一并落进 data/frozen/(前复权可复现性问题,见第 5 节)。
6.5 事件因子的年报污染 —— S2 上否决闸之前必须处理
v_factor_events 的 ts_code 取 documents.meta->>'company_ts_code'。公告确实 100% 可靠,
但年报也有这个锚,而一份年报能抽出十几条 EVENT —— hotspot 为此专门做了防刷屏
(hotspot.py:92-99 的注释:"实测年报副产物把台账刷满的教训",对策是 other 不进、
同主体同类型只取最新、单主体 ≤2 条)。桥侧没有任何等价保护。
后果:年报披露日会形成一个巨大的尖峰,且年报里提到的历史诉讼 / 处罚会被记成
当日的负面事件。若 S2 按计划用 akg_event < −θ_e 做否决闸,
会因为一份年报提到历史诉讼而否决掉一只好股。
修法(S2 之前):按文档类型只取公告,或按 (ts_code, event_type, 自然日) 去重 + 单日封顶。
极性表(§9-5)在这个问题修掉之前,调得再准也没意义。
6.6 probe 与桥的事件口径不一致
factor_coverage_probe.cov_event(:115-128)用 claims.subject_id 去匹配池成员名/码,
而桥用文档锚 documents.meta->>'company_ts_code'。两个数不会对得上 ——
读 G1 报告时别把它们当同一个指标。建议顺手把 probe 改成同一口径,否则会误判覆盖面。
6.7 一处枚举不一致(小,但会咬人)
enums.EntityKind 只有 6 个成员,没有 Segment;但 _ID_KEYS、ALLOWED_OBJECT_TYPES
都把 Segment 当一等节点。类型闸门走的是 ALLOWED_OBJECT_TYPES 所以不炸,
但 EntityKind 已经不是节点类型的可信清单了 —— 谁下次拿它做遍历会漏 Segment。
7. 判收标准的两处需要改写
§10「每日调度幂等,重跑不产生重复或漂移」 桥内成立 ✅,上游不成立 ❌(硬伤 1 的 Neo4j 返回序、前复权重写)。建议拆成两条:
- 桥内写入幂等 —— 现状已满足;
- 外部不可复现的部分靠输入冻结解决 —— 即第 5 节的
data/frozen/。 把"可复现"重新定义为"可从冻结输入重算出同一个值",这是唯一诚实且可达的口径。
§7-A「akg_score 只在当日传导表有数据的交易日出行」
应加强为 当日「候选池内」传导非空。传导 83 行/日是全池口径,
过完 C ∩ V 之后池内可能一只都不剩 —— 那天照样会写出一张"第一权重项全为 0"的表,
正是 §7-A 想避免的情况。
同时建议每日日志固定打两个数:|P|(候选池规模)与 |P ∩ 传导>0|。
后者才是真正决定榜首的数量。若它长期是 0~3,那么"组合"实际上是 1~3 只票,
且这 1~3 只是从一个 12 × 12 截断列表里出来的 —— 集中度需要在文档里明写。
8. 给平台评价层的一个建议(成本极低、收益很大)
按现在的设计,akg_score 只在候选池内出行 →
平台的 IC / 分层只能看到"池内排序"的价值,完全看不到两道门槛的价值,
而门槛才是这套逻辑的主体。仪表盘会系统性低估这个因子。
加一个因子就解决:
| 因子 | 出行范围 | 回答的问题 |
|---|---|---|
akg_gate |
全覆盖池,0/1(1 = 过 C ∩ V) | 门槛本身有没有价值(分层直接读出门槛内外的收益差) |
akg_score |
候选池内 | 池内排序有没有价值 |
两个仪表各答一个问题,"哪一项在拖后腿"一眼可见。这比在一个因子上纠结 IC 高低有用得多, 而且完全符合"回测是仪表盘不是判官"的定位。
9. 开放问题(§9)我的建议答案
| # | 议题 | 建议 | 依据 |
|---|---|---|---|
| 1 | 热度语义翻转 | 同意取负,但别做成独立加性项 → 做成组内 tiebreak | 第 2 节 |
| 2 | 完整赛道清单 | 先跑 G1 体检再定清单,不要先定清单再体检;C 做两级(graph / fallback)并强制标 source_rule |
3.2 |
| 3 | θ_v 的 q | 先留 q = 0,等 G1 分布定;但注意 upside 主要由单值目标价构成,绝对水平的可信度低于相对排序 | 6.3 |
| 4 | 无券商覆盖股 | S1 保持"不过门槛",但单独输出一张「赛道内 + 无覆盖」观察名单 —— 这正是你论题里最该出现的标的,别让它无声消失 | §1 研报滞后论 |
| 5 | 事件极性 / 半衰期 | 延后到 S2 没问题,但先修年报污染 | 6.5 |
| 6 | upside 重建 甲 / 乙 | 甲案,只需给 sync_consensus 加 asof 参数(约 3 行) |
6.4 |
| 7 | score 历史 A / B | A,且加强为"池内传导非空" | 第 7 节 |
| 8 | 权重 0.5/0.3/0.2 | 次序认可;建议改结构(两段式)而不是改数值 | 第 2 节 |
| 9 | 还没热是否条件项 | S1 就上。现状在 95% 稀疏下已经等价于条件项,不如写明 | 第 2 节 |
| 10 | 调度时点 | T 日 18:40 算(热度用 T−1)。漏斗赌的是左侧位置,热度差一天的信息损失远小于因子延迟一天的机会损失;且 T+1 早间补算会让"T 日因子"依赖 T+1 数据,语义上更难向下游解释 | §3.4 末 |
| 11(新增) | universe 历史快照 | v1.1 §8-2 被 v2.0 删掉了,但它现在更重要 → 子因子去掉池过滤 + 池每日快照 | 第 4 节 |
10. 对 G 系列里程碑的微调建议
G0 文档评审(含本评审的取舍)
G0.5 ★新增★ 冻结与修复 —— 不依赖赛道清单,且第 3 项每晚一天永久少一天历史
① data/frozen/ 输入冻结机制
② 修硬伤 1/2/3(其中硬伤 2 只改视图)
③ 基座开始积累 industry_pools / Segment 边快照
G1 赛道覆盖体检 + probe 加两节:
corr(池内三项相关矩阵)· tracks(yml 草案在图谱里做成员性试算)
+ 原有三件(factor_coverage_probe 补跑 / upside 分布定 q / gp_day_data 起始日复核)
G2 赛道成员表落地(layer 降级为可选元数据,不阻塞)+ 第五/第六个插槽视图
G3 漏斗实现 + 注册 akg_score **与 akg_gate**(第 8 节)
G4 回填与仪表盘(upside 走甲案)
G5 每日调度上线(T 日 18:40)
11. 提交提醒
已核对两个仓库的实际状态(git status):
akg-factor-bridge 有 4 项未提交(最后一次 commit 是 42659b9 历史深度数据探查):
M README.md
M run.py
?? probe.py ← G1 体检脚本,全新未跟踪
?? 量化因子导出与合成设计.md ← v2.0,全新未跟踪
astock-kg 工作区干净 —— factor_coverage_probe.py 已在 32448c3 入库,无需再提交。
一处文档不同步需要处理:astock-kg/docs/量化因子导出与合成设计.md 还是 v1
(2026-07-24,四路对称 + 平台拟合合成),而桥仓库那份已经是 v2.0(自洽公式)。
两份同名文档在两个仓库里讲相反的方案,下次翻文档一定踩坑。
建议:只留桥仓库这一份为唯一事实源,astock-kg/docs/ 那份改成一行指路
("本设计已迁至 akg-factor-bridge 仓库,见该工程根目录同名文档"),并在提交信息里写明。
开发机执行(按硬规矩,push 由你做):
cd ~/Documents/work/project/akg-factor-bridge
git add -A
git commit -m "docs: 量化因子设计 v2.0(推翻拟合合成,转自洽景气度漏斗)
feat: G1 赛道覆盖体检 probe(pools/price/upside 三节,只读)
docs: README 补 probe 用法与数据前沿自检"
# 文档去重后(把 astock-kg/docs 那份改成指路)
cd ~/Documents/work/project/astock-kg
git add -A
git commit -m "docs: 量化因子设计迁至 akg-factor-bridge 仓库(v1 留指路,避免与 v2.0 并存)"
本评审文档建议放 akg-factor-bridge/评审_v2.0_2026-07-26.md 一并提交。