From 8dd12ca1e8247839b583acc07cc136a52fb0a6c3 Mon Sep 17 00:00:00 2001 From: xxm Date: Fri, 21 Aug 2026 02:30:41 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E6=9E=B6=E6=9E=84?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E2=80=94=E2=80=94=E8=AE=B0=E5=BD=95A-D?= =?UTF-8?q?=E7=BB=84=E6=94=B9=E5=8A=A8/=E7=AD=96=E7=95=A5=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=8C=96/=E4=B8=A4=E4=BB=BD=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E8=AE=B0=E5=BD=95/=E7=AD=96=E7=95=A5=E5=88=B0=E6=9C=9F?= =?UTF-8?q?=E8=AF=84=E4=BC=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...holding-strategies-multewriter-analysis.md | 268 ++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 docs/holding-strategies-multewriter-analysis.md diff --git a/docs/holding-strategies-multewriter-analysis.md b/docs/holding-strategies-multewriter-analysis.md new file mode 100644 index 00000000..f2d132b2 --- /dev/null +++ b/docs/holding-strategies-multewriter-analysis.md @@ -0,0 +1,268 @@ +# holding_strategies 多写方分析(2026-08-20) + +> 目标:梳理所有写入 holding_strategies 表的模块,按业务关联性分组,明确每个模块的职责、触发时机、改什么字段、与其他模块的关联。为后续"REPLACE→UPDATE"改造提供依据。 + +--- + +## 一、背景 + +holding_strategies 是 MoFin 系统的核心表——每只股票的"策略卡",记录买入区/止损/止盈/信号/完整分析等。系统里所有模块都围着这张表转。 + +**历史问题**:8+ 个模块反复覆写同一行,参数互相覆盖,66% 的 strategy_history 是同参数重复快照。 + +**已修复**(8/19):白名单机制——只有 per_stock_12d/batch_12d/promote 能写核心参数(entry/stop/tp/rr/position_advice),其他模块保留 DB 当前值。 + +**待修复**:所有模块仍用 REPLACE 整行(INSERT OR REPLACE),改一个字段连带重写全行。应改为 UPDATE 特定字段。 + +--- + +## 二、写入保护机制(在 mofin_db.py 的 write_holding_strategy 内部) + +| 保护 | 代码位置 | 保护什么 | 规则 | +|---|---|---|---| +| 参数白名单 | L2080 `_PARAM_WHITELIST` | entry_low/entry_high/stop_loss/take_profit/rr_ratio/position_advice | 只有 `per_stock_12d`/`batch_12d`/`promote` 能写,其他保留DB当前值 | +| 信号权威 | L2058-2075 | timing_signal | 技术路径不得降级新鲜(<20h)的12维信号 | +| action权威 | L2098-2107 | action | 非LLM路径不得覆盖新鲜(<20h)的12维action | +| tag保护 | L2108-2109 | tag | `active_manual`(人工标记)永远不被自动流程覆盖 | +| 快照瘦身 | L1999-2020 | strategy_history | 只有关键字段(entry/stop/tp/signal/rr)有变化才拍快照 | + +--- + +## 三、12个写入方(按业务关联性分组) + +### 组A:策略参数制定(核心分析引擎) + +| 模块 | 文件 | 业务角色 | 触发频率 | 改什么字段 | +|---|---|---|---|---| +| 个股12维重评 | `per_stock_reassess.py` | **权威源**——AI分析给出买入区/止损/止盈/信号 | 交易日12:00/15:00 | entry_low/entry_high/stop_loss/take_profit/rr_ratio/position_advice/timing_signal/full_analysis | + +**职责**:系统的"大脑",所有其他模块的判断都基于它给出的参数。唯一被白名单授权写核心参数的模块。 + +**关联**:它的输出 → 被组B/C/D/E所有模块读取和使用。 + +--- + +### 组B:候选发现与评估(分支自成长管道) + +| 模块 | 文件 | 业务角色 | 触发频率 | 改什么字段 | +|---|---|---|---|---| +| 分支扫描 | `branch_scanner.py` | 盘中实时发现候选信号 | 每15分钟 | signal_factors | +| 分支评估 | `branch_evaluator.py` | 评估候选信号质量 | 被扫描器调用 | action/timing_signal | +| 策略树分析 | `strategy_tree.py` | 宏观→大盘→行业→个股层级分析 | 被其他模块调用 | signal_factors/action | +| 分支剪枝 | `prune_branches.py` | 砍掉低质量分支 | 每日21:00 | status | + +**业务逻辑**:一条管道—— + +``` +策略树分析(提供层级框架) + ↓ +分支扫描(盘中发现"这只股票的某个条件触发了") + ↓ +分支评估("这个触发信号可靠吗?") + ↓ +写入策略卡(signal_factors/action) + ↓ +分支剪枝(收盘后砍掉成功率低的分支) +``` + +**关联**:只改策略卡的 `signal_factors` 和 `action` 字段(信号来源明细和动作建议),不动核心参数(买入区/止损/止盈)。 + +**关键发现**:4个模块形成闭环——扫描→评估→写入→剪枝→下一轮扫描。数据依赖是顺序的(扫描在前,评估在后,剪枝在收盘后),不存在并发冲突。 + +--- + +### 组C:自选池维护 + +| 模块 | 文件 | 业务角色 | 触发频率 | 改什么字段 | +|---|---|---|---|---| +| 清理自选池 | `clean_watchlist.py` | 移除不在持仓的股票 | 每日9:05 | tag/status | +| 持仓对账 | `holdings_reconciliation.py` | 核对系统股数与券商持仓 | 手动 | shares/position_pct | +| 导入持仓 | `import_holding_xls.py` | 从Excel导入最新持仓 | 手动 | shares/cost/market_value | + +**业务逻辑**:维护"自选池"的准确性—— + +``` +导入持仓(券商导出Excel → 导入系统) + ↓ +持仓对账(系统记录 vs 券商实际持仓,以券商为准) + ↓ +清理自选池(不在持仓的股票从自选移除) +``` + +**关联**:只改 `shares`/`position_pct`/`tag`/`status` 字段(股数/仓位/标签/状态),不动核心参数。数据源是券商持仓(外部输入),与组A的AI分析是两个独立的数据流。 + +**关键发现**:持仓对账和导入持仓是手动触发(低频),清理自选池是每日一次。顺序依赖(先导入→再对账→再清理),不存在并发冲突。 + +--- + +### 组D:准确率回顾 + +| 模块 | 文件 | 业务角色 | 触发频率 | 改什么字段 | +|---|---|---|---|---| +| 策略评估 | `strategy_evaluator.py` | 统计历史预测准确率 | 每周六21:00 | evaluation | +| 建议对账 | `advice_reconciliation.py` | 核对AI建议是否被执行 | 每周六20:00 | advice_timeline标记 | + +**业务逻辑**:事后复盘—— + +``` +建议对账("上周说卖出,实际卖了吗?") + ↓ +策略评估("整体准确率多少?") +``` + +**关联**:只加评估/标记字段(evaluation/advice_timeline),完全不动核心参数和信号。只读分析+标记,不改变策略逻辑。 + +**关键发现**:每周一次的低频任务,与其他模块没有数据竞争。 + +--- + +### 组E:策略状态管理 + +| 模块 | 文件 | 业务角色 | 触发频率 | 改什么字段 | +|---|---|---|---|---| +| 策略生命周期 | `strategy_lifecycle.py` | 管理推荐标签/动作判断 | 被其他模块调用 | tag/action | + +**业务逻辑**:管理策略卡的"状态机"——标记"推荐操作"、"人工标记"等标签,决定策略的生命周期流转。 + +**关联**:改 `tag` 和 `action` 字段。被组A(重评后标记推荐)和组B(分支评估后标记状态)调用。 + +**关键发现**:不是独立运行的,是被其他模块调用的"工具函数"。写入时机取决于调用方。 + +--- + +## 四、5组的关联关系 + +``` +组A(12维重评)──输出──→ 组B(分支扫描/评估/剪枝)──读取──→ 组A的参数 + │ │ + │ ↓ + │ 组E(策略状态管理)──标记──→ 推荐/人工标签 + │ + ├──输出──→ 组C(自选池维护)──独立数据流──→ 券商持仓 + │ + └──输出──→ 组D(准确率回顾)──只读分析──→ 评估标记 +``` + +**核心结论**: +1. **组A是权威源**,其他组都基于它的输出做判断 +2. **组B内部是顺序管道**(扫描→评估→剪枝),不存在并发 +3. **组C是独立数据流**(券商持仓),与组A的AI分析互不干扰 +4. **组D是只读分析**,不改变策略逻辑 +5. **组E是工具函数**,被其他组调用 + +--- + +## 五、当前问题与改造方向 + +### 问题:REPLACE 整行 + +每个模块只改自己负责的字段,但 `write_holding_strategy` 用的是 `INSERT OR REPLACE`(整行替换)。如果两个模块并发运行(比如组B的分支扫描和组C的持仓对账同时跑),后写的会覆盖先写的。 + +### 改造方向:REPLACE → UPDATE 特定字段 + +每个模块只 UPDATE 自己负责的列,不动其他列。具体方案: + +| 组 | 改哪些列 | 不动哪些列 | +|---|---|---| +| 组A | entry_low/entry_high/stop_loss/take_profit/rr_ratio/position_advice/timing_signal/full_analysis | shares/cost/market_value/tag/status/evaluation/advice_timeline/signal_factors | +| 组B | signal_factors/action/timing_signal | entry/stop/tp/rr/position_advice/shares/cost/market_value/tag/status/evaluation | +| 组C | shares/position_pct/tag/status | entry/stop/tp/rr/position_advice/timing_signal/full_analysis/signal_factors/evaluation | +| 组D | evaluation/advice_timeline | entry/stop/tp/rr/position_adice/timing_signal/full_analysis/signal_factors/shares/cost/market_value/tag/status | +| 组E | tag/action | entry/stop/tp/rr/position_advice/timing_signal/full_analysis/signal_factors/shares/cost/market_value/evaluation | + +### 前置条件 + +1. 确认每个模块的 source_trigger 映射到哪个组 +2. 在 `write_holding_strategy` 内部根据 source_trigger 决定用 REPLACE 还是 UPDATE +3. UPDATE 时只 SET 该组负责的列,其他列保持不变 + +--- + +## 六、验证要点 + +改造后需要验证: +1. 组A(12维重评)仍能正常写入所有字段(它是权威源,应该用 REPLACE 或全列 UPDATE) +2. 组B/C/D/E 改用 UPDATE 特定列后,不会覆盖其他组的字段 +3. 快照逻辑仍正常(只有关键字段变化时才拍快照) +4. 并发场景下(组B扫描 + 组C对账同时跑)不会互相覆盖 + +--- + +## 七、架构演进记录(2026-08-20~21) + +### 7.1 策略版本化 + +**问题**:旧版 write_holding_strategy 直接覆盖 active 记录,无历史版本,D 组评估无数据。 + +**方案**: +- 重评时对比关键字段(entry/stop/tp/signal) +- 有变化 → 旧策略 superseded + 新策略 active(version minor+1) +- 无变化 → 不动(不浪费记录) + +**版本号格式**:`major.minor`(如 1.1、1.2、2.1) +- 新入自选 → 1.1 +- 重评有变化 → minor+1(1.1→1.2) +- 清仓回归自选 → major+1, minor=1(→2.1) +- 淘汰后重新选入 → major+1(→3.1) + +### 7.2 两份历史记录 + +**recommendation_log**(推荐记录): +- 写入时机:重评写入策略卡时同步写入 +- 记录:推荐时间、动作、价位、策略版本 + +**execution_log**(执行记录): +- 写入时机:trade_capture / import_holding_xls 执行后 +- 记录:执行时间、动作、数量、价格、来源 + +### 7.3 策略到期评估 + +**strategy_effectiveness**(替代旧 strategy_evaluator + advice_reconciliation): +- 触发:盘后自动(16:45 cron) +- 数据源:stock_daily(完整K线)+ holding_strategies(区间定义)+ recommendation_log + execution_log +- 评估:每个区间的职责履行(买入区是否盈利/止损是否有效/止盈是否正确) +- 输出:买入区准确性/止损准确性/止盈准确性/时间准确性 + 综合评价 + 改进建议 + +### 7.4 已删除模块 + +| 模块 | 删除原因 | +|---|---| +| strategy_evaluator.py | 功能被 strategy_effectiveness 替代 | +| advice_reconciliation.py | 功能合并到 strategy_effectiveness | +| stale_push_wlin.py | 功能被 price_monitor 完全覆盖 | +| branch_scanner.py | 违反"只有重评才能改信号和操作"的方法论 | +| branch_evaluator.py | 违反方法论(越权给信号)| +| strategy_tree.py | 与重评12维分析重复 | +| prune_branches.py | 剪枝概念多余(重评自然覆盖)| + +### 7.5 数据表结构 + +| 表 | 用途 | 关键字段 | +|---|---|---| +| holding_strategies | 策略卡(版本化)| code, version, strategy_source, status, entry/stop/tp | +| recommendation_log | 推荐历史 | strategy_id, code, recommend_time, action, entry/stop/tp | +| execution_log | 执行历史 | code, action, shares, price, execute_time, source | +| strategy_effectiveness | 到期评估 | strategy_id, buy_zone_accuracy, stop_loss_accuracy, overall_assessment | +| stock_daily | K线数据(全市场)| code, date, open/close/high/low/volume | +| stock_fundamentals | 基本面(全市场)| code, pe, pb, mcap_total | + +### 7.6 数据流全景 + +``` +采集层: + daily_kline_collector(16:10) → stock_daily(全市场K线) + fundamentals_full_refresh(16:35) → stock_fundamentals(全市场PE/PB/市值) + +加工层: + factor_engine → stock_indicators(mcap_q/pe_q/bias60/rsi等) + technical_analysis → 支撑阻力/MA/多周期 + +业务层: + 重评(per_stock_reassess) → holding_strategies(版本化) + recommendation_log + 交易(trade_capture/import_holding_xls) → holdings + execution_log + 监控(price_monitor) → 区间触发 → 重评 + +评估层: + strategy_effectiveness(16:45) → 读K线+推荐+执行 → 评估区间职责 + → MoFin研究页面(展示) → 策略自我进化 +```