@@ -228,10 +228,19 @@ def format_small_account_cash_substitution_notes(
228228 "signal_blend_gate_risk_on" : "{trend_symbol} 站上 {window} 日门槛线,持有 SOXL {soxl_ratio} + SOXX {soxx_ratio}" ,
229229 "signal_blend_gate_defensive" : "{trend_symbol} 跌破门槛线,防守持有 SOXX {soxx_ratio}" ,
230230 "signal_blend_gate_overlay_capped" : "{trend_symbol} 仍在 {window} 日门槛线上方,但触发风控降档({reasons}),目标仓位 {allocation_text}" ,
231- "risk_control_tqqq_volatility_delever_applied" : "🛡️ 风控: QQQ {window} 日年化波动率 {volatility} 高于 {threshold},{source_symbol} 转向 {redirect_symbol}" ,
232- "risk_control_tqqq_volatility_delever_applied_dynamic" : "🛡️ 风控: QQQ {window} 日年化波动率 {volatility} 高于实际阈值 {threshold}({threshold_detail}),{source_symbol} 转向 {redirect_symbol}" ,
233- "risk_control_tqqq_volatility_delever_hysteresis" : "🛡️ 风控: QQQ {window} 日年化波动率 {volatility} 仍高于退出阈值 {exit_threshold},维持 {source_symbol} 转向 {redirect_symbol}" ,
234- "risk_control_tqqq_volatility_delever_hysteresis_dynamic" : "🛡️ 风控: QQQ {window} 日年化波动率 {volatility} 仍高于退出阈值 {exit_threshold};入场实际阈值 {threshold}({threshold_detail}),维持 {source_symbol} 转向 {redirect_symbol}" ,
231+ "risk_control_tqqq_volatility_delever_applied" : "🛡️ 风控: QQQ {window} 日年化波动率 {volatility} 高于 {threshold},{source_symbol} 转向 {redirect_symbol}({allocation_detail})" ,
232+ "risk_control_tqqq_volatility_delever_applied_dynamic" : "🛡️ 风控: QQQ {window} 日年化波动率 {volatility} 高于实际阈值 {threshold}({threshold_detail}),{source_symbol} 转向 {redirect_symbol}({allocation_detail})" ,
233+ "risk_control_tqqq_volatility_delever_hysteresis" : "🛡️ 风控: QQQ {window} 日年化波动率 {volatility} 仍高于退出阈值 {exit_threshold},维持 {source_symbol} 转向 {redirect_symbol}({allocation_detail})" ,
234+ "risk_control_tqqq_volatility_delever_hysteresis_dynamic" : "🛡️ 风控: QQQ {window} 日年化波动率 {volatility} 仍高于退出阈值 {exit_threshold};入场实际阈值 {threshold}({threshold_detail}),维持 {source_symbol} 转向 {redirect_symbol}({allocation_detail})" ,
235+ "tqqq_volatility_delever_allocation_detail" : "杠杆仓位:TQQQ 保留 {retained_ratio},{redirect_symbol} {redirected_ratio}" ,
236+ "tqqq_signal_reason_entry_trend" : "原因:QQQ 高于 MA200,MA20 斜率为正" ,
237+ "tqqq_signal_reason_entry_pullback" : "原因:QQQ 低于 MA200,但站上 MA20 且回撤反弹确认" ,
238+ "tqqq_signal_reason_hold_trend" : "原因:已持有风险仓位,QQQ 仍高于 MA200" ,
239+ "tqqq_signal_reason_exit_ma200" : "原因:QQQ 跌破 MA200 退出线" ,
240+ "tqqq_signal_reason_idle_waiting" : "原因:等待 QQQ 站上 MA200 且 MA20 斜率转正" ,
241+ "tqqq_signal_reason_macro_delever" : "原因:宏观风控降低杠杆" ,
242+ "tqqq_signal_reason_macro_defense" : "原因:宏观风控转入防守" ,
243+ "tqqq_signal_reason_crisis_defense" : "原因:危机防御转入避险仓位" ,
235244 "market_status_risk_on" : "🚀 风险开启({asset})" ,
236245 "market_status_delever" : "🛡️ 降杠杆({asset})" ,
237246 "signal_risk_on" : "SOXL 站上 {window} 日均线,持有 SOXL,交易层风险仓位 {ratio}" ,
@@ -369,10 +378,19 @@ def format_small_account_cash_substitution_notes(
369378 "signal_blend_gate_risk_on" : "{trend_symbol} is above the {window}-day gate; hold SOXL {soxl_ratio} + SOXX {soxx_ratio}" ,
370379 "signal_blend_gate_defensive" : "{trend_symbol} is below the gate; hold SOXX {soxx_ratio}" ,
371380 "signal_blend_gate_overlay_capped" : "{trend_symbol} remains above the {window}-day gate, but risk cap is active ({reasons}); target {allocation_text}" ,
372- "risk_control_tqqq_volatility_delever_applied" : "🛡️ Risk control: QQQ {window}d annualized volatility {volatility} is above {threshold}; {source_symbol} redirects to {redirect_symbol}" ,
373- "risk_control_tqqq_volatility_delever_applied_dynamic" : "🛡️ Risk control: QQQ {window}d annualized volatility {volatility} is above effective threshold {threshold} ({threshold_detail}); {source_symbol} redirects to {redirect_symbol}" ,
374- "risk_control_tqqq_volatility_delever_hysteresis" : "🛡️ Risk control: QQQ {window}d annualized volatility {volatility} remains above the exit threshold {exit_threshold}; keep {source_symbol} redirected to {redirect_symbol}" ,
375- "risk_control_tqqq_volatility_delever_hysteresis_dynamic" : "🛡️ Risk control: QQQ {window}d annualized volatility {volatility} remains above exit threshold {exit_threshold}; entry effective threshold {threshold} ({threshold_detail}); keep {source_symbol} redirected to {redirect_symbol}" ,
381+ "risk_control_tqqq_volatility_delever_applied" : "🛡️ Risk control: QQQ {window}d annualized volatility {volatility} is above {threshold}; {source_symbol} redirects to {redirect_symbol} ({allocation_detail})" ,
382+ "risk_control_tqqq_volatility_delever_applied_dynamic" : "🛡️ Risk control: QQQ {window}d annualized volatility {volatility} is above effective threshold {threshold} ({threshold_detail}); {source_symbol} redirects to {redirect_symbol} ({allocation_detail})" ,
383+ "risk_control_tqqq_volatility_delever_hysteresis" : "🛡️ Risk control: QQQ {window}d annualized volatility {volatility} remains above the exit threshold {exit_threshold}; keep {source_symbol} redirected to {redirect_symbol} ({allocation_detail})" ,
384+ "risk_control_tqqq_volatility_delever_hysteresis_dynamic" : "🛡️ Risk control: QQQ {window}d annualized volatility {volatility} remains above exit threshold {exit_threshold}; entry effective threshold {threshold} ({threshold_detail}); keep {source_symbol} redirected to {redirect_symbol} ({allocation_detail})" ,
385+ "tqqq_volatility_delever_allocation_detail" : "leveraged sleeve: TQQQ retained {retained_ratio}, {redirect_symbol} {redirected_ratio}" ,
386+ "tqqq_signal_reason_entry_trend" : "reason: QQQ is above MA200 and MA20 slope is positive" ,
387+ "tqqq_signal_reason_entry_pullback" : "reason: QQQ is below MA200 but above MA20 with a confirmed pullback rebound" ,
388+ "tqqq_signal_reason_hold_trend" : "reason: existing risk sleeve remains active while QQQ stays above MA200" ,
389+ "tqqq_signal_reason_exit_ma200" : "reason: QQQ fell below the MA200 exit line" ,
390+ "tqqq_signal_reason_idle_waiting" : "reason: waiting for QQQ to reclaim MA200 with positive MA20 slope" ,
391+ "tqqq_signal_reason_macro_delever" : "reason: macro risk governor reduced leverage" ,
392+ "tqqq_signal_reason_macro_defense" : "reason: macro risk governor moved the strategy defensive" ,
393+ "tqqq_signal_reason_crisis_defense" : "reason: crisis defense moved the strategy to the safe sleeve" ,
376394 "market_status_risk_on" : "Risk on ({asset})" ,
377395 "market_status_delever" : "Delever ({asset})" ,
378396 "signal_risk_on" : "SOXL is above the {window}-day average; hold SOXL at risk sleeve {ratio}" ,
@@ -763,6 +781,13 @@ def _format_percent(value: Any) -> str:
763781 return "n/a"
764782
765783
784+ def _as_float_or_none (value : Any ) -> float | None :
785+ try :
786+ return float (value )
787+ except (TypeError , ValueError ):
788+ return None
789+
790+
766791def _format_percentile (value : Any ) -> str :
767792 try :
768793 percentile = float (value ) * 100
@@ -829,6 +854,27 @@ def _format_volatility_delever_threshold_detail(
829854 )
830855
831856
857+ def _format_tqqq_volatility_delever_allocation_detail (
858+ execution : Mapping [str , Any ],
859+ * ,
860+ prefix : str ,
861+ redirect_symbol : str ,
862+ translator : Callable [..., str ],
863+ ) -> str :
864+ retained_ratio = _as_float_or_none (execution .get (f"{ prefix } _retained_ratio" ))
865+ redirected_ratio = _as_float_or_none (execution .get (f"{ prefix } _redirected_ratio" ))
866+ if retained_ratio is None :
867+ retained_ratio = _as_float_or_none (execution .get (f"{ prefix } _retention_ratio" ))
868+ if redirected_ratio is None and retained_ratio is not None :
869+ redirected_ratio = max (0.0 , min (1.0 , 1.0 - retained_ratio ))
870+ return translator (
871+ "tqqq_volatility_delever_allocation_detail" ,
872+ retained_ratio = _format_percent (retained_ratio ),
873+ redirected_ratio = _format_percent (redirected_ratio ),
874+ redirect_symbol = redirect_symbol or "QQQ" ,
875+ )
876+
877+
832878def _format_tqqq_risk_control_lines (
833879 execution : Mapping [str , Any ],
834880 * ,
@@ -845,6 +891,12 @@ def _format_tqqq_risk_control_lines(
845891 prefix = prefix ,
846892 translator = translator ,
847893 )
894+ allocation_detail = _format_tqqq_volatility_delever_allocation_detail (
895+ execution ,
896+ prefix = prefix ,
897+ redirect_symbol = redirect_symbol or "QQQ" ,
898+ translator = translator ,
899+ )
848900 if str (execution .get (f"{ prefix } _trigger_reason" ) or "" ).strip () == "hysteresis_hold" :
849901 return [
850902 translator (
@@ -856,6 +908,7 @@ def _format_tqqq_risk_control_lines(
856908 threshold_detail = threshold_detail ,
857909 source_symbol = "TQQQ" ,
858910 redirect_symbol = redirect_symbol or "QQQ" ,
911+ allocation_detail = allocation_detail ,
859912 )
860913 ]
861914 return [
@@ -867,6 +920,7 @@ def _format_tqqq_risk_control_lines(
867920 threshold_detail = threshold_detail ,
868921 source_symbol = "TQQQ" ,
869922 redirect_symbol = redirect_symbol or "QQQ" ,
923+ allocation_detail = allocation_detail ,
870924 )
871925 ]
872926
0 commit comments