diff --git a/src/quant_platform_kit/risk/gate.py b/src/quant_platform_kit/risk/gate.py index 4d61e69..869f2cf 100644 --- a/src/quant_platform_kit/risk/gate.py +++ b/src/quant_platform_kit/risk/gate.py @@ -196,7 +196,7 @@ def _normalized_weight_map(raw: Mapping[str, Any] | None) -> dict[str, float] | return None if number > 0.0: result[symbol] = float(number) - return result + return result or None def _portfolio_current_weights( @@ -219,7 +219,7 @@ def _portfolio_current_weights( weights[symbol] = weights.get(symbol, 0.0) + float(market_value) / verified_nav except TypeError: return None - return weights + return weights or None def _resolve_small_account_hold_policy( diff --git a/tests/test_risk_gate.py b/tests/test_risk_gate.py index 3a05aa5..712cfc2 100644 --- a/tests/test_risk_gate.py +++ b/tests/test_risk_gate.py @@ -461,6 +461,22 @@ def test_small_account_hold_explicit_disable_rejects_overrun(self) -> None: self.assertEqual(result.positions, ()) self.assertEqual(result.risk_flags, ("rejected:runtime_risk_limits",)) + def test_small_account_hold_rejects_when_book_weights_unavailable(self) -> None: + """Empty derived book weights must not look like a zero book.""" + result = apply_risk_gate( + _decision(positions=(PositionTarget(symbol="SOXL", target_weight=0.90),)), + max_single_weight=1.0, + max_total_exposure=1.0, + portfolio_snapshot=_portfolio_snapshot(), + capital_base=_capital_base(reported_equity=472.0), + capital_base_binding=_capital_base_binding(), + runtime_risk_limits=self._runtime_limits(), + current_portfolio_weights={}, + cash_only_execution=True, + ) + self.assertEqual(result.positions, ()) + self.assertEqual(result.risk_flags, ("rejected:runtime_risk_limits",)) + def test_explicit_runtime_limits_reject_uncovered_budget_symbol(self) -> None: result = apply_risk_gate( StrategyDecision(