Skip to content

Commit 2f5dc58

Browse files
authored
Clarify opportunity notification copy (#18)
1 parent 5b0b36f commit 2f5dc58

2 files changed

Lines changed: 133 additions & 26 deletions

File tree

src/quant_strategy_plugins/strategy_plugin_runner.py

Lines changed: 125 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,17 @@ def _review_attack_symbol(*sources: Mapping[str, Any]) -> str:
946946
return ""
947947

948948

949+
def _review_benchmark_symbol(*sources: Mapping[str, Any]) -> str:
950+
for source in sources:
951+
for container_key in ("metrics", "rebound_confirmation"):
952+
container = source.get(container_key)
953+
if isinstance(container, Mapping):
954+
symbol = str(container.get("benchmark_symbol") or "").strip().upper()
955+
if symbol:
956+
return symbol
957+
return ""
958+
959+
949960
def _manual_review_source_title(
950961
*,
951962
panic_payload: Mapping[str, Any],
@@ -1101,23 +1112,70 @@ def _format_manual_review_notification_message(
11011112

11021113
vetoes = _message_reason_codes(_nested_mapping(payload, "arbiter").get("vetoes"))
11031114
attack_symbol = _review_attack_symbol(panic_payload, taco_payload) or target_label
1115+
benchmark_symbol = _review_benchmark_symbol(panic_payload, taco_payload)
11041116
source_title = _manual_review_source_title(panic_payload=panic_payload, taco_payload=taco_payload, locale=locale)
11051117
if locale == "zh-CN":
1118+
benchmark_label = benchmark_symbol or "基准指数"
11061119
route_status = _localized_opportunity_status(route, locale)
11071120
veto_text = _message_join(_localized_opportunity_veto_labels(vetoes, locale), locale)
11081121
card_prefix = "机会被拦截" if is_vetoed_opportunity_notice else "机会复核"
1109-
situation_lines = ["- 机会信号已触发。"]
1122+
if panic_payload and taco_payload:
1123+
opportunity_summary = (
1124+
f"- 事件缓和与 VIX 恐慌回落同时出现,{benchmark_label}/{attack_symbol} 已从短期低点反弹,"
1125+
"属于恐慌后反转复核信号。"
1126+
)
1127+
elif panic_payload:
1128+
opportunity_summary = (
1129+
f"- VIX 已从极端恐慌高位回落,{benchmark_label}/{attack_symbol} 已从短期低点反弹,"
1130+
"出现恐慌后反转观察信号。"
1131+
)
1132+
else:
1133+
opportunity_summary = (
1134+
f"- 事件缓和后,{benchmark_label}/{attack_symbol} 出现反弹确认,出现事件反弹复核信号。"
1135+
)
1136+
situation_lines = [opportunity_summary]
11101137
if is_vetoed_opportunity_notice:
1111-
situation_lines.append(f"- 当前仍处于{route_status}。")
1138+
situation_lines.append(f"- 但当前仍处于{route_status},说明策略环境还没有完全解除防守。")
11121139
if vetoes:
1113-
situation_lines.append(f"- {veto_text}。")
1140+
situation_lines.append(f"- {veto_text},所以这条通知先作为人工检查线索。")
11141141
else:
1115-
situation_lines.append(f"- 当前状态:{route_status}。")
1116-
guidance_first = (
1117-
"- 人工复核恐慌是否已缓和,以及策略侧是否已按自身风控处理。"
1118-
if is_vetoed_opportunity_notice
1119-
else "- 结合策略自身风控、持仓状态和最新基本面判断是否需要干预。"
1120-
)
1142+
situation_lines.append(f"- 当前处于{route_status},可以进入人工复核。")
1143+
if is_vetoed_opportunity_notice:
1144+
if panic_payload and taco_payload:
1145+
confirmation_line = (
1146+
f"- 先看 VIX 是否继续回落、事件是否继续缓和、{benchmark_label}/{attack_symbol} "
1147+
"反弹是否维持,避免把一次反抽误判为趋势恢复。"
1148+
)
1149+
elif panic_payload:
1150+
confirmation_line = (
1151+
f"- 先看 VIX 是否继续回落、{benchmark_label}/{attack_symbol} 反弹是否维持,"
1152+
"避免把一次反抽误判为趋势恢复。"
1153+
)
1154+
else:
1155+
confirmation_line = (
1156+
f"- 先看事件是否继续缓和、{benchmark_label}/{attack_symbol} 反弹是否维持,"
1157+
"避免把一次反抽误判为趋势恢复。"
1158+
)
1159+
guidance_lines = [
1160+
confirmation_line,
1161+
"- 对照策略侧当前仓位和风控状态,判断是否停止继续降风险、恢复观察,或继续保持防守。",
1162+
"- 如果 VIX 回升、价格跌回短期低点附近,忽略本次机会信号。",
1163+
]
1164+
else:
1165+
if panic_payload and taco_payload:
1166+
confirmation_target = f"VIX 和事件是否继续改善,{benchmark_label}/{attack_symbol}"
1167+
reversal_target = "VIX、事件或价格确认"
1168+
elif panic_payload:
1169+
confirmation_target = f"VIX 是否继续回落,{benchmark_label}/{attack_symbol}"
1170+
reversal_target = "VIX 或价格确认"
1171+
else:
1172+
confirmation_target = f"事件是否继续缓和,{benchmark_label}/{attack_symbol}"
1173+
reversal_target = "事件或价格确认"
1174+
guidance_lines = [
1175+
f"- 检查 {confirmation_target} 的价格确认是否延续。",
1176+
"- 对照策略侧仓位和风险预算,判断是否需要人工干预。",
1177+
f"- 如果 {reversal_target}反转,忽略本次信号。",
1178+
]
11211179
lines = [
11221180
f"【{card_prefix}{attack_symbol}{source_title}】",
11231181
f"日期:{as_of or '未知日期'}",
@@ -1131,27 +1189,73 @@ def _format_manual_review_notification_message(
11311189
lines.extend(
11321190
[
11331191
"建议操作:",
1134-
guidance_first,
1135-
"- 若后续 VIX 或价格确认反转,本信号需要重新评估。",
1192+
*guidance_lines,
11361193
]
11371194
)
11381195
return "\n".join(lines)
11391196

1197+
benchmark_label = benchmark_symbol or "benchmark"
11401198
route_status = _localized_opportunity_status(route, locale)
11411199
veto_text = _message_join(_localized_opportunity_veto_labels(vetoes, locale), locale)
11421200
card_prefix = "Opportunity Vetoed" if is_vetoed_opportunity_notice else "Opportunity Review"
1143-
situation_lines = ["- Opportunity signal triggered."]
1201+
if panic_payload and taco_payload:
1202+
opportunity_summary = (
1203+
f"- Event de-escalation and a VIX panic pullback are both present; {benchmark_label}/{attack_symbol} "
1204+
"has rebounded from short-term lows, so this is a panic-reversal review signal."
1205+
)
1206+
elif panic_payload:
1207+
opportunity_summary = (
1208+
f"- VIX has pulled back from extreme panic levels, and {benchmark_label}/{attack_symbol} "
1209+
"has rebounded from short-term lows, so this is a panic-reversal watch signal."
1210+
)
1211+
else:
1212+
opportunity_summary = (
1213+
f"- After event de-escalation, {benchmark_label}/{attack_symbol} shows rebound confirmation, "
1214+
"so this is an event-rebound review signal."
1215+
)
1216+
situation_lines = [opportunity_summary]
11441217
if is_vetoed_opportunity_notice:
1145-
situation_lines.append(f"- Current state is still {route_status}.")
1218+
situation_lines.append(f"- Current state is still {route_status}, so the strategy environment is not fully out of defense.")
11461219
if vetoes:
1147-
situation_lines.append(f"- {veto_text}.")
1220+
situation_lines.append(f"- {veto_text}; treat this notification as a manual check cue for now.")
11481221
else:
1149-
situation_lines.append(f"- Current state: {route_status}.")
1150-
guidance_first = (
1151-
"- Manually review whether panic has eased and whether the strategy-side risk controls have already handled it."
1152-
if is_vetoed_opportunity_notice
1153-
else "- Consider strategy-side risk controls, current exposure, and latest fundamentals before intervening."
1154-
)
1222+
situation_lines.append(f"- Current state is {route_status}, so it can move into manual review.")
1223+
if is_vetoed_opportunity_notice:
1224+
if panic_payload and taco_payload:
1225+
confirmation_line = (
1226+
f"- Check whether VIX keeps falling, the event keeps de-escalating, and the "
1227+
f"{benchmark_label}/{attack_symbol} rebound holds; avoid treating a one-day bounce as recovery."
1228+
)
1229+
elif panic_payload:
1230+
confirmation_line = (
1231+
f"- Check whether VIX keeps falling and whether the {benchmark_label}/{attack_symbol} rebound holds; "
1232+
"avoid treating a one-day bounce as recovery."
1233+
)
1234+
else:
1235+
confirmation_line = (
1236+
f"- Check whether the event keeps de-escalating and whether the "
1237+
f"{benchmark_label}/{attack_symbol} rebound holds; avoid treating a one-day bounce as recovery."
1238+
)
1239+
guidance_lines = [
1240+
confirmation_line,
1241+
"- Compare against the strategy-side exposure and risk-control state before deciding whether to stop further de-risking, return to watch, or stay defensive.",
1242+
"- Ignore this opportunity signal if VIX rises again or price falls back near short-term lows.",
1243+
]
1244+
else:
1245+
if panic_payload and taco_payload:
1246+
confirmation_target = f"VIX and event conditions keep improving and whether {benchmark_label}/{attack_symbol}"
1247+
reversal_target = "VIX, event context, or price confirmation"
1248+
elif panic_payload:
1249+
confirmation_target = f"VIX keeps falling and whether {benchmark_label}/{attack_symbol}"
1250+
reversal_target = "VIX or price confirmation"
1251+
else:
1252+
confirmation_target = f"the event keeps de-escalating and whether {benchmark_label}/{attack_symbol}"
1253+
reversal_target = "event context or price confirmation"
1254+
guidance_lines = [
1255+
f"- Check whether {confirmation_target} price confirmation continues.",
1256+
"- Compare against strategy-side exposure and risk budget before any manual intervention.",
1257+
f"- Ignore this signal if {reversal_target} reverses.",
1258+
]
11551259
lines = [
11561260
f"[{card_prefix} | {attack_symbol} | {source_title}]",
11571261
f"Date: {as_of or 'unknown date'}",
@@ -1165,8 +1269,7 @@ def _format_manual_review_notification_message(
11651269
lines.extend(
11661270
[
11671271
"Suggested action:",
1168-
guidance_first,
1169-
"- Reassess this signal if VIX or price confirmation later reverses.",
1272+
*guidance_lines,
11701273
]
11711274
)
11721275
return "\n".join(lines)

tests/test_strategy_plugin_runner.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,10 @@ def test_strategy_plugin_runner_can_enable_panic_reversal_inside_market_regime_c
405405
zh_notification = payload["notification"]["localized_messages"]["zh-CN"]
406406
assert "【机会复核|TQQQ|VIX 恐慌反转】" in zh_notification
407407
assert "情况说明:" in zh_notification
408-
assert "- 机会信号已触发。" in zh_notification
408+
assert "VIX 已从极端恐慌高位回落" in zh_notification
409+
assert "QQQ/TQQQ 已从短期低点反弹" in zh_notification
409410
assert "建议操作:" in zh_notification
411+
assert "检查 VIX 是否继续回落" in zh_notification
410412
assert "VIX 曾达到恐慌区间" in zh_notification
411413
assert "VIX 已从高点回落" in zh_notification
412414
assert "QQQ 3 日收益" in zh_notification
@@ -512,9 +514,11 @@ def test_market_regime_control_notification_surfaces_vetoed_panic_reversal() ->
512514
zh_notification = contracted["notification"]["localized_messages"]["zh-CN"]
513515
assert "【机会被拦截|TQQQ|VIX 恐慌反转】" in zh_notification
514516
assert "情况说明:" in zh_notification
515-
assert "- 当前仍处于降风险状态。" in zh_notification
516-
assert "- 宏观降风险信号优先于 VIX 恐慌反转。" in zh_notification
517+
assert "VIX 已从极端恐慌高位回落" in zh_notification
518+
assert "但当前仍处于降风险状态,说明策略环境还没有完全解除防守" in zh_notification
519+
assert "宏观降风险信号优先于 VIX 恐慌反转,所以这条通知先作为人工检查线索" in zh_notification
517520
assert "建议操作:" in zh_notification
521+
assert "避免把一次反抽误判为趋势恢复" in zh_notification
518522
assert "TQQQ 从近 5 日低点反弹 +35.2%" in zh_notification
519523
assert "market_regime_control" not in zh_notification
520524
assert "veto" not in zh_notification
@@ -829,7 +833,7 @@ def test_strategy_plugin_runner_runs_taco_rebound_notification_mount_for_tqqq(tm
829833
zh_notification = latest["localized_messages"]["notification"]["zh-CN"]
830834
assert "【机会复核|TQQQ|TACO 事件反弹】" in zh_notification
831835
assert "情况说明:" in zh_notification
832-
assert "- 机会信号已触发。" in zh_notification
836+
assert "事件缓和后,QQQ/TQQQ 出现反弹确认" in zh_notification
833837
assert "事件:" in zh_notification
834838
assert "价格确认:" in zh_notification
835839
assert "建议操作:" in zh_notification

0 commit comments

Comments
 (0)