Files
tick-stock-panel/backend/tests/test_stock_analyzer_index.py
T
shy3130 f249856bf7 feat(paper): V3 补全 — 结算成交留痕/自动跟单下单事件推送/AI 持仓注入多账户
- paper.day_fill_events: 交易日成交台账 → AlertEvent 同构事件;
  daily_pipeline paper_settle 阶段补 alert_store 留痕 (next_open/close
  单的成交发生在盘后管道, 盘中钩子覆盖不到; 管道无 SSE 广播器, 留痕即可见)
- paper_auto.auto_order_events: 自动跟单创建的订单 → 推送事件
  (rule_id 从 order.source 还原), quote_service 钩子随成交事件一并走
  SSE toast / 语音 / 系统通知 / 留痕 / Webhook 既有通道
- stock_analyzer: 虚拟持仓注入改为逐账户收集 (V2 多账户), 空仓账户跳过,
  每账户一行标注账户名; raw 口径浮盈不变
- tests: 新增 day_fill_events / auto_order_events / 多账户注入用例,
  既有注入用例适配列表签名 (66 passed)
2026-09-24 20:01:14 +08:00

92 lines
3.6 KiB
Python

"""AI 分析 prompt 指数 / ETF 文案测试。"""
from app.services.stock_analyzer import _build_user_prompt
def test_user_prompt_index_no_financials():
prompt = _build_user_prompt(
kline_tail=[{"date": "2026-07-24", "close": 3000.0}],
fins={"metrics": [], "income": []},
levels={}, close=3000.0, symbol="000001.SH", focus="",
asset_type="index",
)
assert "指数" in prompt
assert "Free 模式" not in prompt # 指数无财务是常态, 不走 Free 文案
def test_user_prompt_etf_no_financials():
"""ETF 无上市公司财务是常态, 不得走股票 Free/未同步文案。
指数分支已单独处理, ETF 若落入 else, 提示词会写成「Free 模式或尚未
同步财务报表」, 模型按系统提示词第 4 节输出「财务接入中」, 用户会以为
是套餐或同步问题, 而不是场内基金本来就没有公司报表。
"""
prompt = _build_user_prompt(
kline_tail=[{"date": "2026-07-24", "close": 4.12}],
fins={"metrics": [], "income": []},
levels={}, close=4.12, symbol="510300.SH", focus="",
asset_type="etf",
)
assert "ETF" in prompt
assert "Free 模式" not in prompt
assert "尚未同步财务报表" not in prompt
# ===== V3: 虚拟持仓注入 =====
def test_user_prompt_injects_paper_position():
"""模拟盘持有该标的: 注入持仓数量/成本/浮盈, 并要求 AI 给持仓建议。"""
from app.services.stock_analyzer import _paper_position_part, _build_user_prompt
part = _paper_position_part([{"account_id": "default", "qty": 1000, "avg_cost": 10.0}],
close=11.0, raw_close=11.0)
assert "1000 股" in part
assert "10.000" in part
assert "+10.00%" in part
assert "虚拟持仓" in part
prompt = _build_user_prompt(
kline_tail=[{"date": "2026-07-24", "close": 11.0}],
fins={"metrics": [], "income": []},
levels={}, close=11.0, symbol="600000.SH", focus="",
paper_positions=[{"account_id": "default", "qty": 1000, "avg_cost": 10.0}],
raw_close=11.0,
)
assert "模拟盘" in prompt and "持仓 1000 股" in prompt
def test_paper_position_part_multi_account():
"""V2 多账户: 逐账户注入, 空仓账户跳过, 全空返回空串。"""
from app.services.stock_analyzer import _paper_position_part
part = _paper_position_part(
[
{"account_id": "a1", "qty": 100, "avg_cost": 10.0},
{"account_id": "a2", "qty": 0, "avg_cost": 10.0},
{"account_id": "a3", "qty": 200, "avg_cost": 20.0},
],
close=11.0, raw_close=11.0,
)
assert "账户 a1" in part and "账户 a3" in part
assert "a2" not in part
def test_paper_position_part_empty_without_position():
"""无持仓/零仓/无效成本: 返回空串, 不注入误导性「空仓」表述。"""
from app.services.stock_analyzer import _paper_position_part
assert _paper_position_part([], 11.0, 11.0) == ""
assert _paper_position_part([{"qty": 0, "avg_cost": 10.0}], 11.0, 11.0) == ""
assert _paper_position_part([{"qty": 100, "avg_cost": 0}], 11.0, 11.0) == ""
def test_paper_position_part_prefers_raw_close():
"""除权后前复权 close 与 raw 成本混算会产生虚假盈亏: 必须优先 raw_close。
raw 成本 20 元, 除权后前复权 close=11、raw_close=21: 用 close 算出 -45% 是错的。
"""
from app.services.stock_analyzer import _paper_position_part
part = _paper_position_part([{"qty": 100, "avg_cost": 20.0}], close=11.0, raw_close=21.0)
assert "+5.00%" in part
assert "-45" not in part