Skip to content

[bug] strategy_signals 表唯一键冲突导致 entry_low 等字段错位显示(10倍价格错误) #94

Description

@edison6448

Bug 描述

/api/recommendations/strategy-signals 接口返回的 entry_low / entry_high / stop_loss / target_price 字段和对应股票的真实价格对不上(错 8-10 倍)。

复现步骤

  1. 部署 PanWatch 最新版(main 分支,2026-08-01 commit)
  2. 调用 /api/recommendations/strategy-signals/refresh 重建候选
  3. 拉取 /api/recommendations/strategy-signals?market=CN&holding=unheld&min_score=90&...
  4. 对比返回的 entry_low 和股票的真实当前价

实际现象

stock_symbol 返回 entry_low 真实当前价 source_candidate_id
000815 美利云 146.84 18.69 6
000333 美的集团 146.84 ❌ ~80 6
603259 药明康德 146.84 ✅ ~146.81 6

000815 美利云真实价 18.69 元,接口返回 146.84(错 8 倍),且 source_candidate_id=6 对应的 entry_candidates 实际是 603259 药明康德。

根因分析

src/core/strategy_engine.pyrefresh_strategy_signals 函数(约 1299 行附近):

key = (int(c.id), str(code))   # ← bug 源
row = existing.get(key)
if not row:
    row = StrategySignalRun(
        snapshot_date=snapshot,
        stock_symbol=c.stock_symbol,
        ...
        source_candidate_id=c.id,
    )
    db.add(row)
    existing[key] = row

row.entry_low = c.entry_low
row.entry_high = c.entry_high
row.stop_loss = c.stop_loss
row.target_price = c.target_price

问题existing 字典的 key 用的是 int(c.id)——但 c.idSQLAlchemy session 临时的内存 id,不是真实 DB 主键。每次 refresh 重建 session,id 从 1 开始重新分配,导致:

  1. 不同股票(000815/000333/603259)遍历时临时 id 撞车
  2. 第一个遍历到的 candidate 创建 row 后填入 stock_symbol=A, entry_low=X
  3. 第二个遍历到的 candidate(id 相同)复用这个 row只覆盖 stock_symbol 等少量字段
  4. 结果 row 显示 stock_symbol=B,但 entry_low 还是 X

为什么 603259 数据看起来对? 因为它本来 entry_low=146.84 就是真实的,而 000815 / 000333 是被错关联到 603259 的 candidate 上去了

修复建议

把 key 从 (int(c.id), str(code)) 改成业务字段:

# 改之前
key = (int(c.id), str(code))

# 改之后
key = (str(c.stock_symbol).upper(), str(market), str(code))

# existing 字典构建也要对应改(约 1249 行):
existing[(str(row.stock_symbol or "").upper(), str(row.stock_market or "").upper(), str(row.strategy_code or ""))] = row

我已在本地验证修复效果:300750 宁德时代显示 entry_low=400.06(修复前错为 146.84),603259 药明康德仍显示 146.84,601899 紫金矿业显示 33.21(修复前错为 33.40)——每个股票用各自 candidate 的真实价格。

影响面

  • paper_trading 不受影响(撮合用 entry_candidates 接口,不是 strategy-signals)
  • 前端机会页卡片显示错误价格——入场/止损/目标数字错 8-10 倍,用户做决策会被误导
  • 详情弹窗价格是对的(用的不是这个接口)

环境

  • 版本:main 分支最新 commit(2026-08-01)
  • 部署:Docker sunxiao0721/panwatch:custom
  • 前端复现:访问 /opportunities 页面,看到股票卡片的入场/止损/目标数字跟详情弹窗里真实价格完全对不上

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions