10 KiB
akg-factor-bridge
astock-kg(知识图谱基座)↔ quant_factor_service(通用因子平台)之间的因子导出桥。
把基座的四路产出(分析师预期空间 / 热度 / 利好利空事件 / 板块传导)做成符合平台规范的
子因子;复合因子 akg_score 在桥内用自洽的景气度漏斗算出,平台只负责注册/存储/
调度/评价,不参与合成。
设计与决策依据见本工程根目录
量化因子导出与合成设计.md(v2.0,唯一事实源)。 ⚠️astock-kg/docs/下的同名文档还是 v1,讲的是已被推翻的平台拟合合成方案,别读。 本工程不 import 基座或平台任何代码,只靠.env里三处数据库连接工作, 可单独部署于任意能连通三库的服务器。
架构(基座出视图,桥算因子与合成,平台管存储与评价)
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)
▼
akg-factor-bridge:读视图+热度 → 四路日截面变换 → 【漏斗合成 akg_score】
→ 转前缀码 SH600000 → 写平台 t_factor_akg_* + 注册 factor_metadata
→ 每日冻结输入快照 data/frozen/(freeze.py)
▼
平台 quant_factor_service:当普通 single 因子 → 评价(RankIC/分层)=仪表盘 / 调度 / 下游消费
- 基座只暴露数据、不算因子;桥承载全部因子建模(极性表、衰减、门槛、权重); 平台不感知 astock-kg。
- universe = KG 覆盖池(
v_factor_universe= 全部industry_pools成员并集)。 ⚠️ 它是当前态且每周一自动长大——子因子是否受它限制由SUBFACTOR_UNIVERSE控制。
四个子因子
| factor_code | 表 | 口径 | 缺失 |
|---|---|---|---|
akg_upside |
t_factor_akg_upside | 目标价中枢/现价−1(as-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) |
用法(全程 Docker,不在宿主机直跑)
# 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
# 1) 配连接(地址填「桥容器可达」的:跨机=LAN IP;与基座同机同网=服务名,见下「网络」)
cp .env.example .env && vim .env
# 2) 构建并起桥容器(常驻)
docker compose up -d --build
# 3) 连通性 + 视图版本 + 数据前沿自检
docker compose exec akg-factor-bridge python run.py views
# 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 是混合还是传导优先」
# 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
# 5) 输入冻结(daily 构建会自动跑一次;也可单独补)
docker compose exec akg-factor-bridge python run.py freeze --date 2026-07-24
生产触发:宿主 cron 或平台 XXL-JOB →
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 |
上游截断体检阈值,命中只告警不拦截 |
网络(Docker)
桥要连三处,按「桥部署在哪」定地址:
- 跨机部署(默认,推荐):三处都用 LAN IP/域名(
.env里填)。默认 bridge 网络即可 出网到 LAN——前提是基座 PG、153、平台 MySQL 都对该服务器可达。 - 与基座同机同网:让桥加入 astock-kg 的 compose 网络,用服务名
postgres连基座 PG (取消docker-compose.yml里 networks 段的注释,填 astock-kg 的网络名)。
已知的上游约束(桥修不了,但会显式告警)
跑 build akg_transmission 时若看到下列告警,是基座侧的问题,需在 astock-kg 修:
quiet存满 12 条 ——transmission.py的quiet[:12]是给旁批/展示用的, 却把因子的结构上限锁在12 候选 × 12 成员 = 144 行/日。改法:落库存全量, 旁批仍只喂前 12。members_total >= 30—— 基座graph_store.topic_context的 Cypher 是LIMIT $cap(默认 30)且无ORDER BY,于是moved_ratio建立在一个任意顺序的 ≤30 抽样上,同日重跑结果可能不同。改法:加ORDER BY、cap 提到 200。mkt_trade_date ≠ scan_date——hotspot._latest_mkt取的是max(trade_date) FROM mkt_daily而非scan_date当天的快照。17:30sync_market晚点时,18:15 的传导扫描会用 T−1 的 movers 写成今日戳。改法:_latest_mkt加trade_date参数 + scan 开头加新鲜度断言。
(另:gp_sector_daily / gp_market_sentiment 当前空表、t_signal_daily_results
05-15 停更 → hotspot 的板块级异动源整个缺失,这才是传导每日只有几十行的主因。
要提传导覆盖,优先补 gp_sector_daily,比调任何参数都有用。)
⚠️ 待实机核实项
- 三库连通性:
python run.py views七项全 ✅ 才算通。 - 视图版本:
views会打印n_sources / mkt_trade_date / source_type是否就绪; 有 ⬜ 说明sql/astock_kg_slot_views.sql还没应用到基座(桥会自动退回旧口径并告警)。 factor_metadata列:register自适应实际列写入;若无factor_type列,平台/mining/factors/all可能查不到本因子(会打印告警),需与平台侧确认。- 事件极性/方向:
factors.EVENT_POLARITY / EVENT_DIR是草案(设计 §9-5);增减持的增/减方向若qualifiers.direction里没有会落 0,需确认基座 EVENT 的方向 存在哪个 qualifier 键。半衰期 10 交易日 / 窗口 60 交易日可调。 - 事件交易日龄近似:v1 用自然日×(5/7) 折算交易日龄,非精确交易日历——够用, 后续可换真实交易日历向量化。
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参数」)。