Bug 描述
/api/recommendations/strategy-signals 接口返回的 entry_low / entry_high / stop_loss / target_price 字段和对应股票的真实价格对不上(错 8-10 倍)。
复现步骤
- 部署 PanWatch 最新版(main 分支,2026-08-01 commit)
- 调用
/api/recommendations/strategy-signals/refresh 重建候选
- 拉取
/api/recommendations/strategy-signals?market=CN&holding=unheld&min_score=90&...
- 对比返回的
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 开始重新分配,导致:
- 不同股票(000815/000333/603259)遍历时临时 id 撞车
- 第一个遍历到的 candidate 创建 row 后填入 stock_symbol=A, entry_low=X
- 第二个遍历到的 candidate(id 相同)复用这个 row,只覆盖 stock_symbol 等少量字段
- 结果 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 页面,看到股票卡片的入场/止损/目标数字跟详情弹窗里真实价格完全对不上
Bug 描述
/api/recommendations/strategy-signals接口返回的entry_low / entry_high / stop_loss / target_price字段和对应股票的真实价格对不上(错 8-10 倍)。复现步骤
/api/recommendations/strategy-signals/refresh重建候选/api/recommendations/strategy-signals?market=CN&holding=unheld&min_score=90&...entry_low和股票的真实当前价实际现象
→ 000815 美利云真实价 18.69 元,接口返回 146.84(错 8 倍),且 source_candidate_id=6 对应的 entry_candidates 实际是 603259 药明康德。
根因分析
在
src/core/strategy_engine.py的refresh_strategy_signals函数(约 1299 行附近):问题:
existing字典的 key 用的是int(c.id)——但c.id是 SQLAlchemy session 临时的内存 id,不是真实 DB 主键。每次 refresh 重建 session,id 从 1 开始重新分配,导致:为什么 603259 数据看起来对? 因为它本来 entry_low=146.84 就是真实的,而 000815 / 000333 是被错关联到 603259 的 candidate 上去了。
修复建议
把 key 从
(int(c.id), str(code))改成业务字段:我已在本地验证修复效果:300750 宁德时代显示
entry_low=400.06(修复前错为 146.84),603259 药明康德仍显示 146.84,601899 紫金矿业显示 33.21(修复前错为 33.40)——每个股票用各自 candidate 的真实价格。影响面
环境
sunxiao0721/panwatch:custom/opportunities页面,看到股票卡片的入场/止损/目标数字跟详情弹窗里真实价格完全对不上