diff --git a/docs/adaptive_allocation_control_plane.zh-CN.md b/docs/adaptive_allocation_control_plane.zh-CN.md index 39c7bc9..7f8dd82 100644 --- a/docs/adaptive_allocation_control_plane.zh-CN.md +++ b/docs/adaptive_allocation_control_plane.zh-CN.md @@ -32,7 +32,8 @@ quant-adaptive-selection --input selection-input.json --output selection-decisio - 不读取新闻叙事并直接交易;因子必须来自版本化的数据链。 - 数据过期、状态不明、置信度不足、插件未批准、平台未对账或不健康时 fail-closed。 - 插件只能给出 `0..1` 的风险缩放,不能增加风险或提交订单。 -- 只有已获 Shadow 准入、并绑定 immutable release 的候选可以被排名。 +- 只有已获 Shadow 准入、并绑定 immutable release 的候选可以被排名;`shadow_candidate` + 仅可得到“建议进入 Shadow”的零仓位结论,不能由此启动 runtime。 - P0 不能改变平台、策略、插件挂载、仓位或调度器。 后续 M1 才能把 Shadow 结论呈现给人工;M2/M3 还必须经过独立的 Paper、Canary、 diff --git a/src/quant_platform_kit/adaptive_allocation/contracts.py b/src/quant_platform_kit/adaptive_allocation/contracts.py index 46d2ade..810ae98 100644 --- a/src/quant_platform_kit/adaptive_allocation/contracts.py +++ b/src/quant_platform_kit/adaptive_allocation/contracts.py @@ -14,7 +14,9 @@ PLATFORM_HEALTH_SCHEMA = "qsl.platform_health_snapshot.v1" SELECTION_DECISION_SCHEMA = "qsl.selection_decision.v1" SHADOW_ONLY_AUTHORITY = "shadow_only" -_SHADOW_ELIGIBLE_STAGES = frozenset({"shadow_active", "paper_active", "live_candidate", "live_enabled"}) +_SHADOW_ELIGIBLE_STAGES = frozenset( + {"shadow_candidate", "shadow_active", "paper_active", "live_candidate", "live_enabled"} +) def _nonblank(value: str, field_name: str) -> str: diff --git a/src/quant_platform_kit/adaptive_allocation/selector.py b/src/quant_platform_kit/adaptive_allocation/selector.py index 14a733b..5979741 100644 --- a/src/quant_platform_kit/adaptive_allocation/selector.py +++ b/src/quant_platform_kit/adaptive_allocation/selector.py @@ -62,7 +62,10 @@ def select_shadow( `base_score` and `factor_exposures` must come from a separately validated, versioned research pipeline. This function intentionally has no market-data - fetcher and does not infer financial features from narratives. + fetcher and does not infer financial features from narratives. A + ``shadow_candidate`` that is explicitly approved for Shadow may be ranked + so that P0 can produce a reviewable recommendation, but the result still + cannot start a Shadow runtime or authorize an order. """ health_by_platform = {item.platform_id: item for item in platform_health} diff --git a/tests/test_adaptive_allocation.py b/tests/test_adaptive_allocation.py index 28f05a3..31dc048 100644 --- a/tests/test_adaptive_allocation.py +++ b/tests/test_adaptive_allocation.py @@ -116,6 +116,21 @@ def test_shadow_selector_rejects_unhealthy_platform_or_missing_required_plugin() assert "no_healthy_reconciled_shadow_platform" in item.reasons +def test_shadow_selector_can_rank_an_approved_shadow_candidate_without_starting_it(): + decision = select_shadow( + decision_id="decision-004", + market_context=_context(), + candidates=[_candidate("candidate_a", 0.7, lifecycle_stage="shadow_candidate")], + platform_health=[_health()], + plugin_adjustments=[PluginRiskAdjustment("market_regime_control", 1.0)], + policy=_policy(), + ) + + assert decision.recommended_strategy_profile == "candidate_a" + assert decision.no_order is True + assert decision.candidate_decisions[0].proposed_weight == 0.0 + + def test_plugin_risk_adjustment_cannot_increase_risk(): with pytest.raises(ValueError, match="risk_multiplier"): PluginRiskAdjustment("market_regime_control", 1.01)