mirror of
https://github.com/shy3130/tick-stock-panel.git
synced 2026-11-02 10:26:13 +00:00
- 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)
92 lines
3.6 KiB
Python
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
|