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.py 的 refresh_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.id 是 SQLAlchemy 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 页面,看到股票卡片的入场/止损/目标数字跟详情弹窗里真实价格完全对不上

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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