From 6d184a7df29a4d1352a0339014d57c65c79deb6a Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Wed, 10 Jun 2026 04:43:40 +0800 Subject: [PATCH] Include dynamic volatility context in Telegram logs --- application/signal_snapshot.py | 11 ++++++++++ notifications/telegram.py | 8 +++++++ tests/test_notifications.py | 39 ++++++++++++++++++++++++++++++++++ tests/test_signal_snapshot.py | 23 ++++++++++++++++++++ tests/test_strategy_loader.py | 2 +- 5 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 tests/test_signal_snapshot.py diff --git a/application/signal_snapshot.py b/application/signal_snapshot.py index 4e0463d..992e23f 100644 --- a/application/signal_snapshot.py +++ b/application/signal_snapshot.py @@ -24,6 +24,17 @@ "trend_rsi14_dynamic_threshold", "trend_rsi14_effective_threshold", "trend_bb_upper", + "blend_gate_volatility_delever_symbol", + "blend_gate_volatility_delever_window", + "blend_gate_volatility_delever_threshold_mode", + "blend_gate_volatility_delever_threshold", + "blend_gate_volatility_delever_dynamic_threshold", + "blend_gate_volatility_delever_dynamic_sample_count", + "blend_gate_volatility_delever_dynamic_lookback", + "blend_gate_volatility_delever_dynamic_percentile", + "blend_gate_volatility_delever_dynamic_min_periods", + "blend_gate_volatility_delever_dynamic_floor", + "blend_gate_volatility_delever_dynamic_cap", "blend_gate_volatility_delever_metric", "blend_gate_volatility_delever_triggered", ) diff --git a/notifications/telegram.py b/notifications/telegram.py index a43b609..b4ed1d5 100644 --- a/notifications/telegram.py +++ b/notifications/telegram.py @@ -81,6 +81,10 @@ "blend_gate_reason_rsi_cap": "RSI 超阈值", "blend_gate_reason_bollinger_cap": "突破布林上轨", "blend_gate_reason_volatility_delever": "{symbol} {window} 日年化波动率 {volatility} 高于 {threshold},SOXL 转向 {redirect_symbol}", + "blend_gate_reason_volatility_delever_dynamic": "{symbol} {window} 日年化波动率 {volatility} 高于实际阈值 {threshold}({threshold_detail}),SOXL 转向 {redirect_symbol}", + "blend_gate_volatility_threshold_detail_dynamic": "动态 {percentile},{lookback}日窗口,范围 {floor}-{cap},样本 {sample_count}", + "blend_gate_volatility_threshold_detail_dynamic_fallback": "动态样本不足,回退固定 {fixed_threshold}(样本 {sample_count}/{min_periods},{percentile})", + "blend_gate_volatility_threshold_detail_fixed": "固定阈值 {threshold}", "strategy_plugin_line": "🧩 插件:{plugin} | 状态:{route} | 提醒:{action}", "strategy_plugin_alert_subject": "🚨 策略插件告警:{plugin} | {route}", "strategy_plugin_alert_title": "🚨 【策略插件告警】", @@ -223,6 +227,10 @@ "blend_gate_reason_rsi_cap": "RSI over threshold", "blend_gate_reason_bollinger_cap": "price above upper band", "blend_gate_reason_volatility_delever": "{symbol} {window}d annualized volatility {volatility} is above {threshold}; redirect SOXL to {redirect_symbol}", + "blend_gate_reason_volatility_delever_dynamic": "{symbol} {window}d annualized volatility {volatility} is above effective threshold {threshold} ({threshold_detail}); redirect SOXL to {redirect_symbol}", + "blend_gate_volatility_threshold_detail_dynamic": "dynamic {percentile}, {lookback}d lookback, bounded {floor}-{cap}, samples {sample_count}", + "blend_gate_volatility_threshold_detail_dynamic_fallback": "dynamic warm-up, fallback fixed {fixed_threshold} (samples {sample_count}/{min_periods}, {percentile})", + "blend_gate_volatility_threshold_detail_fixed": "fixed threshold {threshold}", "strategy_plugin_line": "🧩 Plugin: {plugin} | status: {route} | notice: {action}", "strategy_plugin_alert_subject": "🚨 Strategy plugin alert: {plugin} | {route}", "strategy_plugin_alert_title": "🚨 【Strategy Plugin Alert】", diff --git a/tests/test_notifications.py b/tests/test_notifications.py index 91b1393..ad0e3a5 100644 --- a/tests/test_notifications.py +++ b/tests/test_notifications.py @@ -36,6 +36,45 @@ def test_build_translator_supports_chinese(): ) == "SOXX 10 日年化波动率 55.0% 高于 50.0%,SOXL 转向 SOXX" ) + assert ( + translate( + "blend_gate_reason_volatility_delever_dynamic", + symbol="SOXX", + window=10, + volatility="61.0%", + threshold="60.0%", + threshold_detail=translate( + "blend_gate_volatility_threshold_detail_dynamic", + percentile="p95", + lookback="252", + floor="50.0%", + cap="75.0%", + sample_count="252", + ), + redirect_symbol="SOXX", + ) + == "SOXX 10 日年化波动率 61.0% 高于实际阈值 60.0%(动态 p95,252日窗口,范围 50.0%-75.0%,样本 252),SOXL 转向 SOXX" + ) + en_translate = build_translator("en") + assert ( + en_translate( + "blend_gate_reason_volatility_delever_dynamic", + symbol="SOXX", + window=10, + volatility="61.0%", + threshold="60.0%", + threshold_detail=en_translate( + "blend_gate_volatility_threshold_detail_dynamic", + percentile="p95", + lookback="252", + floor="50.0%", + cap="75.0%", + sample_count="252", + ), + redirect_symbol="SOXX", + ) + == "SOXX 10d annualized volatility 61.0% is above effective threshold 60.0% (dynamic p95, 252d lookback, bounded 50.0%-75.0%, samples 252); redirect SOXL to SOXX" + ) assert ( translate( "strategy_plugin_line", diff --git a/tests/test_signal_snapshot.py b/tests/test_signal_snapshot.py new file mode 100644 index 0000000..fb6959b --- /dev/null +++ b/tests/test_signal_snapshot.py @@ -0,0 +1,23 @@ +from application.signal_snapshot import build_signal_snapshot + + +def test_includes_soxl_dynamic_volatility_fields(): + snapshot = build_signal_snapshot( + platform="ibkr", + strategy_profile="soxl_soxx_trend_income", + execution={ + "blend_gate_volatility_delever_threshold_mode": "rolling_percentile", + "blend_gate_volatility_delever_threshold": 0.60, + "blend_gate_volatility_delever_dynamic_threshold": 0.60, + "blend_gate_volatility_delever_dynamic_sample_count": 252, + "blend_gate_volatility_delever_dynamic_percentile": 0.95, + "blend_gate_volatility_delever_metric": 0.61, + "blend_gate_volatility_delever_triggered": True, + }, + ) + + indicators = snapshot["indicators"] + assert indicators["blend_gate_volatility_delever_threshold_mode"] == "rolling_percentile" + assert indicators["blend_gate_volatility_delever_dynamic_threshold"] == 0.60 + assert indicators["blend_gate_volatility_delever_dynamic_sample_count"] == 252 + assert indicators["blend_gate_volatility_delever_triggered"] is True diff --git a/tests/test_strategy_loader.py b/tests/test_strategy_loader.py index 3ae9d2e..fc3bf3d 100644 --- a/tests/test_strategy_loader.py +++ b/tests/test_strategy_loader.py @@ -51,7 +51,7 @@ def test_load_strategy_entrypoint_for_profile_resolves_tqqq_growth_income(monkey assert entrypoint.manifest.default_config["benchmark_symbol"] == "QQQ" assert entrypoint.manifest.default_config["managed_symbols"] == ( "TQQQ", - "QQQ", + "QQQM", "BOXX", "SCHD", "DGRO",