@@ -338,6 +338,112 @@ def _build_timing_audit_lines(signal_metadata, *, translator) -> list[str]:
338338 return [f"{ label } : { value } " ]
339339
340340
341+ def _format_percent (value ) -> str :
342+ try :
343+ return f"{ float (value ) * 100 :.1f} %"
344+ except (TypeError , ValueError ):
345+ return "n/a"
346+
347+
348+ def _format_percentile (value ) -> str :
349+ try :
350+ percentile = float (value ) * 100
351+ except (TypeError , ValueError ):
352+ return "p?"
353+ if float (percentile ).is_integer ():
354+ return f"p{ int (percentile )} "
355+ return f"p{ percentile :.1f} "
356+
357+
358+ def _format_sample_count (value ) -> str :
359+ try :
360+ count = float (value )
361+ except (TypeError , ValueError ):
362+ return "n/a"
363+ if float (count ).is_integer ():
364+ return str (int (count ))
365+ return f"{ count :.1f} "
366+
367+
368+ def _present (value ) -> bool :
369+ return value not in (None , "" )
370+
371+
372+ def _is_truthy (value ) -> bool :
373+ if isinstance (value , bool ):
374+ return value
375+ return str (value or "" ).strip ().lower () in {"1" , "true" , "yes" , "y" }
376+
377+
378+ def _effective_volatility_delever_threshold (signal_metadata , * , prefix : str ):
379+ mode = str (signal_metadata .get (f"{ prefix } _threshold_mode" ) or "" ).strip ().lower ()
380+ dynamic_threshold = signal_metadata .get (f"{ prefix } _dynamic_threshold" )
381+ if mode == "rolling_percentile" and _present (dynamic_threshold ):
382+ return dynamic_threshold
383+ return signal_metadata .get (f"{ prefix } _threshold" )
384+
385+
386+ def _format_volatility_delever_threshold_detail (signal_metadata , * , prefix : str , translator ) -> str :
387+ mode = str (signal_metadata .get (f"{ prefix } _threshold_mode" ) or "" ).strip ().lower ()
388+ fixed_threshold = signal_metadata .get (f"{ prefix } _threshold" )
389+ dynamic_threshold = signal_metadata .get (f"{ prefix } _dynamic_threshold" )
390+ if mode == "rolling_percentile" :
391+ kwargs = {
392+ "percentile" : _format_percentile (signal_metadata .get (f"{ prefix } _dynamic_percentile" )),
393+ "lookback" : _format_sample_count (signal_metadata .get (f"{ prefix } _dynamic_lookback" )),
394+ "min_periods" : _format_sample_count (signal_metadata .get (f"{ prefix } _dynamic_min_periods" )),
395+ "sample_count" : _format_sample_count (signal_metadata .get (f"{ prefix } _dynamic_sample_count" )),
396+ "floor" : _format_percent (signal_metadata .get (f"{ prefix } _dynamic_floor" )),
397+ "cap" : _format_percent (signal_metadata .get (f"{ prefix } _dynamic_cap" )),
398+ "fixed_threshold" : _format_percent (fixed_threshold ),
399+ }
400+ if _present (dynamic_threshold ):
401+ return translator ("blend_gate_volatility_threshold_detail_dynamic" , ** kwargs )
402+ return translator ("blend_gate_volatility_threshold_detail_dynamic_fallback" , ** kwargs )
403+ return translator (
404+ "blend_gate_volatility_threshold_detail_fixed" ,
405+ threshold = _format_percent (fixed_threshold ),
406+ )
407+
408+
409+ def _build_tqqq_risk_control_lines (signal_metadata , * , translator ) -> list [str ]:
410+ prefix = "dual_drive_volatility_delever"
411+ if not _is_truthy (signal_metadata .get (f"{ prefix } _applied" )):
412+ return []
413+ redirect_symbol = str (signal_metadata .get (f"{ prefix } _redirect_symbol" ) or "QQQ" ).strip ().upper ()
414+ window = str (signal_metadata .get (f"{ prefix } _window" ) or "5" ).strip ()
415+ threshold = _effective_volatility_delever_threshold (signal_metadata , prefix = prefix )
416+ threshold_detail = _format_volatility_delever_threshold_detail (
417+ signal_metadata ,
418+ prefix = prefix ,
419+ translator = translator ,
420+ )
421+ if str (signal_metadata .get (f"{ prefix } _trigger_reason" ) or "" ).strip () == "hysteresis_hold" :
422+ return [
423+ translator (
424+ "risk_control_tqqq_volatility_delever_hysteresis_dynamic" ,
425+ window = window ,
426+ volatility = _format_percent (signal_metadata .get (f"{ prefix } _metric" )),
427+ exit_threshold = _format_percent (signal_metadata .get (f"{ prefix } _exit_threshold" )),
428+ threshold = _format_percent (threshold ),
429+ threshold_detail = threshold_detail ,
430+ source_symbol = "TQQQ" ,
431+ redirect_symbol = redirect_symbol or "QQQ" ,
432+ )
433+ ]
434+ return [
435+ translator (
436+ "risk_control_tqqq_volatility_delever_applied_dynamic" ,
437+ window = window ,
438+ volatility = _format_percent (signal_metadata .get (f"{ prefix } _metric" )),
439+ threshold = _format_percent (threshold ),
440+ threshold_detail = threshold_detail ,
441+ source_symbol = "TQQQ" ,
442+ redirect_symbol = redirect_symbol or "QQQ" ,
443+ )
444+ ]
445+
446+
341447def _format_signal_snapshot_line (snapshot , * , translator ) -> str :
342448 if not isinstance (snapshot , Mapping ):
343449 return ""
@@ -367,15 +473,17 @@ def _strategy_dashboard_text(signal_metadata, *, translator) -> str:
367473 metadata = signal_metadata if isinstance (signal_metadata , Mapping ) else {}
368474 raw_annotations = metadata .get ("execution_annotations" )
369475 annotations = raw_annotations if isinstance (raw_annotations , Mapping ) else {}
476+ risk_source = {** metadata , ** annotations }
370477 dashboard_text = _format_dashboard_text (
371478 annotations .get ("dashboard_text" )
372479 or metadata .get ("dashboard_text" )
373480 or metadata .get ("dashboard" )
374481 or ""
375482 )
483+ risk_control_lines = _build_tqqq_risk_control_lines (risk_source , translator = translator )
376484 timing_lines = _build_timing_audit_lines (metadata , translator = translator )
377485 snapshot_line = _format_signal_snapshot_line (metadata .get ("signal_snapshot" ), translator = translator )
378- audit_lines = [* timing_lines , * ([snapshot_line ] if snapshot_line else [])]
486+ audit_lines = [* risk_control_lines , * timing_lines , * ([snapshot_line ] if snapshot_line else [])]
379487 if not audit_lines :
380488 return dashboard_text
381489 if not dashboard_text :
0 commit comments