akg-factor-bridge/README.md

172 lines
11 KiB
Markdown
Raw Normal View History

2026-07-24 14:20:54 +08:00
# akg-factor-bridge
astock-kg知识图谱基座`quant_factor_service`(通用因子平台)之间的**因子导出桥**。
把基座的四路产出(分析师预期空间 / 热度 / 利好利空事件 / 板块传导)做成符合平台规范的
2026-07-27 09:49:05 +08:00
子因子;**复合因子 `akg_score` 在桥内用自洽的景气度漏斗算出**,平台只负责注册/存储/
调度/评价,不参与合成。
2026-07-24 14:20:54 +08:00
2026-07-27 09:49:05 +08:00
> 设计与决策依据见本工程根目录 `量化因子导出与合成设计.md`v2.0,唯一事实源)。
> ⚠️ `astock-kg/docs/` 下的同名文档还是 v1讲的是**已被推翻**的平台拟合合成方案,别读。
> 本工程**不 import** 基座或平台任何代码,只靠 `.env` 里三处数据库连接工作,
> 可单独部署于任意能连通三库的服务器。
2026-07-24 14:20:54 +08:00
2026-07-27 09:49:05 +08:00
## 架构(基座出视图,桥算因子与合成,平台管存储与评价)
2026-07-24 14:20:54 +08:00
```
astock-kg 基座 PG ── 只读视图sql/astock_kg_slot_views.sql
v_factor_universe / v_factor_consensus / v_factor_events / v_factor_transmission
│ (热度不经基座:桥直连 153 读 stock_fund_heat_scores
2026-07-27 09:49:05 +08:00
akg-factor-bridge读视图+热度 → 四路日截面变换 → 【漏斗合成 akg_score】
→ 转前缀码 SH600000 → 写平台 t_factor_akg_* + 注册 factor_metadata
→ 每日冻结输入快照 data/frozen/freeze.py
2026-07-24 14:20:54 +08:00
2026-07-27 09:49:05 +08:00
平台 quant_factor_service当普通 single 因子 → 评价(RankIC/分层)=仪表盘 / 调度 / 下游消费
2026-07-24 14:20:54 +08:00
```
2026-07-27 09:49:05 +08:00
- **基座只暴露数据、不算因子**;桥承载全部因子建模(极性表、衰减、门槛、权重);
平台不感知 astock-kg。
- universe = KG 覆盖池(`v_factor_universe` = 全部 `industry_pools` 成员并集)。
⚠️ 它是**当前态**且每周一自动长大——子因子是否受它限制由 `SUBFACTOR_UNIVERSE` 控制。
2026-07-24 14:20:54 +08:00
## 四个子因子
| factor_code | 表 | 口径 | 缺失 |
|---|---|---|---|
2026-07-27 09:49:05 +08:00
| `akg_upside` | t_factor_akg_upside | 目标价中枢/现价1as-of backward | NaN无覆盖不出行 |
| `akg_heat` | t_factor_akg_heat | 热度分 0~1最新批次T+1 到达) | NaN |
| `akg_event` | t_factor_akg_event | Σ 事件极性×时间衰减(**仅公告来源、单文档封顶** | 不出行 |
| `akg_transmission` | t_factor_akg_transmission | **distinct 源数**×(1已动比例) | 不出行(合成侧填 0 |
2026-07-24 14:20:54 +08:00
2026-08-03 14:02:13 +08:00
## 选股计划入池2026-08-03
`python run.py push-pool` 把每日计划写进 Mongo 股票池分组group_code=AKG_PLAN
决策系统每晚认知扫描按分组并集覆盖 → 候选票自动获得夜间推理(支撑/压力/定性),
PMS 的参考位、择时执行区间、研判上下文由此可用。入池=当日计划(强传导前20)∪持仓;
掉榜未恶化留池观察;无持仓、不在计划且形态恶化 → 移入回收站 stock_recycle_bin
持仓永不出池。规则、时间线、部署与判收见 `docs/选股计划入池_对接说明.md`
2026-07-24 14:31:59 +08:00
## 用法(全程 Docker不在宿主机直跑
2026-07-24 14:20:54 +08:00
```bash
2026-07-27 10:03:45 +08:00
# 0) 基座视图建一次/改一次。**按你在哪台机器上选一条**——桥是跨机部署的独立单元,
# 部署它的服务器上通常没有 astock-kg 的容器。
# (a) 在桥这边(推荐,无需 psql 客户端,用桥自己的 AKG_PG_* 连接):
docker compose exec -T akg-factor-bridge python run.py apply-views --dry-run # 先看要跑哪几条
docker compose exec -T akg-factor-bridge python run.py apply-views
# ⚠️ 需要 DDL 权限。日常只读账号会报 permission denied用基座 owner 跑这一次:
# docker compose exec -T -e AKG_PG_USER=<owner> -e AKG_PG_PASSWORD=<pw> \
# akg-factor-bridge python run.py apply-views
# (必须用 -e 传进容器;写在 docker 前面只影响宿主机进程)
# (b) 在桥这边、想用原生 psql一次性容器不装客户端
# set -a; . ./.env; set +a
# docker run --rm -i -e PGPASSWORD="$AKG_PG_PASSWORD" postgres:16-alpine \
# psql -h "$AKG_PG_HOST" -p "${AKG_PG_PORT:-5432}" -U "$AKG_PG_USER" -d "$AKG_PG_DB" \
# -v ON_ERROR_STOP=1 < sql/astock_kg_slot_views.sql
# (c) 在 astock-kg 那台机器上(把本文件带过去):
# docker exec -i akg-postgres psql -U akg -d akg < sql/astock_kg_slot_views.sql
2026-07-24 14:20:54 +08:00
2026-07-24 14:31:59 +08:00
# 1) 配连接(地址填「桥容器可达」的:跨机=LAN IP与基座同机同网=服务名,见下「网络」)
cp .env.example .env && vim .env
2026-07-24 14:20:54 +08:00
2026-07-24 14:31:59 +08:00
# 2) 构建并起桥容器(常驻)
docker compose up -d --build
2026-07-24 14:20:54 +08:00
2026-07-27 09:49:05 +08:00
# 3) 连通性 + 视图版本 + 数据前沿自检
2026-07-24 14:31:59 +08:00
docker compose exec akg-factor-bridge python run.py views
2026-07-24 14:20:54 +08:00
2026-07-27 09:49:05 +08:00
# 3.5) G1 体检(只读:池结构 / 行情年表 / upside 分布 / 三项相关矩阵与权重体检)
docker compose exec akg-factor-bridge python run.py probe
docker compose exec akg-factor-bridge python run.py probe --section corr # 单跑某节
# price 节全表聚合,可能 1~3 分钟corr 节直接回答「0.5/0.3/0.2 是混合还是传导优先」
2026-07-24 14:31:59 +08:00
# 4) 注册 + 跑因子(幂等,可重跑)
docker compose exec akg-factor-bridge python run.py register
docker compose exec akg-factor-bridge python run.py build akg_heat --mode daily --date 2026-07-24
docker compose exec akg-factor-bridge python run.py build all --mode history --start 2024-01-01 --end 2026-07-24
2026-07-27 09:49:05 +08:00
# 5) 输入冻结daily 构建会自动跑一次;也可单独补)
docker compose exec akg-factor-bridge python run.py freeze --date 2026-07-24
2026-07-24 14:20:54 +08:00
```
2026-07-24 14:31:59 +08:00
生产触发:宿主 cron 或平台 XXL-JOB →
2026-07-27 09:49:05 +08:00
`docker exec akg_factor_bridge python run.py build all --mode daily`(跑完自动冻结)。
## ❄️ 输入冻结freeze.py——为什么每天都要跑
桥内写入幂等,但**上游不是**:传导的 quiet 集合依赖 Neo4j 返回序(基座 Cypher 无
`ORDER BY`)、`industry_pools` 只存最新态且每周一长大、`gp_day_data` 是前复权且
锚在最新日(每次除权历史 close 被整体重写)、`consensus_daily` 会被重跑覆盖。
所以「可复现」唯一诚实可达的定义是:**能从冻结的输入重算出同一个值**。
`data/frozen/<日期>/` 每天存一份 universe / 传导原始行 / 一致预期 / 当日收盘 / 热度
+ 当日算出的因子值 + `manifest.json`行数、告警、git rev
它一次性满足:判收标准的「重跑不产生漂移」、「赛道成员快照可审计」、以及未来任何回溯
需求(含设计 §10 里 GRU 复活所需的「≥1 年 live 传导史」——**那个计时器只在开始存快照
的那天启动**)。`manifest.json` 才是可 diff 的审计线,建议 git 跟踪 manifest、忽略数据文件。
## 运行旋钮(`.env`,全部可留空取默认)
| 变量 | 默认 | 作用 |
|---|---|---|
| `SUBFACTOR_UNIVERSE` | `pool` | 子因子是否受覆盖池限制。`market` 可避免成员性前视与周一跳变(设计评审 §4 建议) |
| `EVENT_SOURCE_TYPES` | `announcement` | 事件只认哪些 `documents.source_type`。默认只认公告——年报会把**历史**诉讼记成披露日的当日负面事件 |
| `EVENT_MAX_PER_DOC` | `3` | 单文档最多贡献几条事件(一份年报能抽十几条) |
| `PRICE_CHUNK_DAYS` | `31` | 行情按月分块读取,防长区间回填 OOM |
| `WRITE_CHUNK_ROWS` | `50000` | 因子表分块提交,防单事务过大 |
| `FROZEN_ROOT` | `/app/data/frozen` | 冻结落点 |
| `UPSTREAM_MEMBER_CAP` / `UPSTREAM_QUIET_CAP` | `30` / `12` | 上游截断体检阈值,命中只告警不拦截 |
2026-07-24 14:31:59 +08:00
## 网络Docker
桥要连三处,按「桥部署在哪」定地址:
2026-07-27 09:49:05 +08:00
- **跨机部署(默认,推荐)**:三处都用 LAN IP/域名(`.env` 里填)。默认 bridge 网络即可
出网到 LAN——前提是基座 PG、153、平台 MySQL 都对该服务器可达。
2026-07-24 14:31:59 +08:00
- **与基座同机同网**:让桥加入 astock-kg 的 compose 网络,用服务名 `postgres` 连基座 PG
(取消 `docker-compose.yml` 里 networks 段的注释,填 astock-kg 的网络名)。
2026-07-24 14:20:54 +08:00
2026-07-27 09:49:05 +08:00
## 已知的上游约束(桥修不了,但会显式告警)
`build akg_transmission` 时若看到下列告警,是**基座侧**的问题,需在 astock-kg 修:
1. **`quiet` 存满 12 条** —— `transmission.py``quiet[:12]` 是给旁批/展示用的,
却把因子的结构上限锁在 `12 候选 × 12 成员 = 144 行/日`。改法:落库存全量,
旁批仍只喂前 12。
2. **`members_total >= 30`** —— 基座 `graph_store.topic_context` 的 Cypher 是
`LIMIT $cap`(默认 30且**无 `ORDER BY`**,于是 `moved_ratio` 建立在一个任意顺序的
≤30 抽样上,同日重跑结果可能不同。改法:加 `ORDER BY`、cap 提到 200。
3. **`mkt_trade_date ≠ scan_date`** —— `hotspot._latest_mkt` 取的是
`max(trade_date) FROM mkt_daily` 而非 `scan_date` 当天的快照。17:30 `sync_market`
晚点时18:15 的传导扫描会用 T1 的 movers 写成今日戳。改法:`_latest_mkt` 加
`trade_date` 参数 + scan 开头加新鲜度断言。
(另:`gp_sector_daily` / `gp_market_sentiment` 当前空表、`t_signal_daily_results`
05-15 停更 → hotspot 的**板块级异动源整个缺失**,这才是传导每日只有几十行的主因。
要提传导覆盖,优先补 `gp_sector_daily`,比调任何参数都有用。)
## ⚠️ 待实机核实项
2026-07-24 14:20:54 +08:00
2026-07-27 09:49:05 +08:00
1. **三库连通性**`python run.py views` 七项全 ✅ 才算通。
2. **视图版本**`views` 会打印 `n_sources / mkt_trade_date / source_type` 是否就绪;
有 ⬜ 说明 `sql/astock_kg_slot_views.sql` 还没应用到基座(桥会自动退回旧口径并告警)。
2026-07-24 14:20:54 +08:00
3. **`factor_metadata` 列**`register` 自适应实际列写入;若无 `factor_type` 列,平台
`/mining/factors/all` 可能查不到本因子(会打印告警),需与平台侧确认。
2026-07-27 09:49:05 +08:00
4. **事件极性/方向**`factors.EVENT_POLARITY / EVENT_DIR` 是草案(设计 §9-5
`增减持` 的增/减方向若 `qualifiers.direction` 里没有会落 0需确认基座 EVENT 的方向
存在哪个 qualifier 键。半衰期 10 交易日 / 窗口 60 交易日可调。
5. **事件交易日龄近似**v1 用自然日×(5/7) 折算交易日龄,非精确交易日历——够用,
后续可换真实交易日历向量化。
6. **`documents.source_type` 实际取值**:默认白名单是 `announcement`。若基座里公告的
`source_type` 不是这个字符串,事件因子会被过滤成空(会显式告警),按实际值改
`EVENT_SOURCE_TYPES`
## 未完成(设计 §8 里程碑)
- **G2**`config/frontier_tracks.yml` + `tracks.py` + `data/track_members.csv`
(赛道门槛 C需要基座先出第五/六个插槽视图 `v_factor_segment_members` /
`v_factor_segment_edges`——`industry_pools` 成员级没有 segment/layer图谱在 Neo4j
- **G3**:漏斗合成 `akg_score` + `akg_gate`0/1 全池出行,让平台分层能读出门槛的价值)。
- **G4**回填与仪表盘upside 历史重建走「给基座 `sync_consensus``asof` 参数」)。