Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions application/signal_snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,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",
)
Expand Down
45 changes: 45 additions & 0 deletions decision_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@
"dual_drive_volatility_delever_vetoed",
"dual_drive_volatility_delever_redirect_symbol",
)
_SOXL_RISK_CONTROL_EXECUTION_FIELDS = (
"blend_gate_volatility_delever_enabled",
"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",
"blend_gate_volatility_delever_retention_ratio",
"blend_gate_volatility_delever_redirect_symbol",
"blend_gate_volatility_delever_removed_ratio",
)


def _build_portfolio_inputs(
Expand Down Expand Up @@ -176,6 +195,27 @@ def _attach_tqqq_risk_control_execution_fields(
execution[field] = value


def _attach_soxl_risk_control_execution_fields(
plan: dict[str, Any],
*,
decision: StrategyDecision,
runtime_metadata: Mapping[str, Any] | None,
) -> None:
if _resolve_canonical_profile(str(plan.get("strategy_profile") or "")) != "soxl_soxx_trend_income":
return
execution = plan.get("execution")
if not isinstance(execution, dict):
return
diagnostics = {**dict(runtime_metadata or {}), **dict(decision.diagnostics)}
annotations = diagnostics.get("execution_annotations")
if isinstance(annotations, Mapping):
diagnostics = {**diagnostics, **dict(annotations)}
for field in _SOXL_RISK_CONTROL_EXECUTION_FIELDS:
value = diagnostics.get(field)
if value not in (None, ""):
execution[field] = value


def _apply_reserved_cash_policy(
annotations: ValueTargetExecutionAnnotations,
*,
Expand Down Expand Up @@ -610,4 +650,9 @@ def map_strategy_decision_to_plan(
decision=normalized_decision,
runtime_metadata=runtime_metadata,
)
_attach_soxl_risk_control_execution_fields(
plan,
decision=normalized_decision,
runtime_metadata=runtime_metadata,
)
return plan
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
flask
gunicorn
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@e0f760255232b62481444a8c1d6637546ba2c07e
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@5fe430699e532ee444e6c2370b34da3dc8b01b06
hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@8d539aeef707b3594af4073f4cd4c3b13140b73f
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@023641c88506c732624a7329e48b51b9dbbe3c2a
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@7d35772d1125b534d0bcca557cb6dbaf28914719
hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@2e0075004239e7ede7ba256763a3441d4ec4ca73
pandas
requests
pytz
Expand Down
18 changes: 18 additions & 0 deletions tests/test_decision_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,17 @@ def test_prefers_normalized_execution_annotations_when_present(self):
"active_risk_asset": "SOXL",
"investable_cash": 9000.0,
"current_min_trade": 100.0,
"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_lookback": 252,
"blend_gate_volatility_delever_dynamic_percentile": 0.95,
"blend_gate_volatility_delever_dynamic_min_periods": 126,
"blend_gate_volatility_delever_dynamic_floor": 0.50,
"blend_gate_volatility_delever_dynamic_cap": 0.75,
"blend_gate_volatility_delever_metric": 0.61,
"blend_gate_volatility_delever_triggered": True,
}
},
)
Expand All @@ -94,6 +105,13 @@ def test_prefers_normalized_execution_annotations_when_present(self):
self.assertEqual(plan["execution"]["signal_display"], "signal")
self.assertEqual(plan["execution"]["dashboard_text"], "strategy dashboard")
self.assertEqual(plan["execution"]["investable_cash"], 9000.0)
self.assertEqual(plan["execution"]["blend_gate_volatility_delever_threshold_mode"], "rolling_percentile")
self.assertEqual(plan["execution"]["blend_gate_volatility_delever_threshold"], 0.60)
self.assertEqual(plan["execution"]["blend_gate_volatility_delever_dynamic_threshold"], 0.60)
self.assertEqual(plan["execution"]["blend_gate_volatility_delever_dynamic_sample_count"], 252)
self.assertEqual(plan["execution"]["blend_gate_volatility_delever_dynamic_percentile"], 0.95)
self.assertEqual(plan["execution"]["blend_gate_volatility_delever_metric"], 0.61)
self.assertIs(plan["execution"]["blend_gate_volatility_delever_triggered"], True)

def test_maps_hybrid_decision_from_snapshot_source(self):
decision = StrategyDecision(
Expand Down
33 changes: 33 additions & 0 deletions tests/test_signal_snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,39 @@ def test_uses_price_as_of_as_snapshot_date_fallback(self):
self.assertEqual(snapshot["price_as_of"], "2026-06-01")
self.assertEqual(snapshot["universe_as_of"], "2026-05-14")

def test_includes_soxl_dynamic_volatility_fields(self):
snapshot = build_signal_snapshot(
platform="longbridge",
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,
},
)

self.assertEqual(
snapshot["indicators"]["blend_gate_volatility_delever_threshold_mode"],
"rolling_percentile",
)
self.assertEqual(
snapshot["indicators"]["blend_gate_volatility_delever_dynamic_threshold"],
0.60,
)
self.assertEqual(
snapshot["indicators"][
"blend_gate_volatility_delever_dynamic_sample_count"
],
252,
)
self.assertIs(
snapshot["indicators"]["blend_gate_volatility_delever_triggered"], True
)


if __name__ == "__main__":
unittest.main()