Skip to content

Commit 72eb138

Browse files
Pigbibicodex
andcommitted
feat: add auditable position control evidence
Co-Authored-By: Codex <noreply@openai.com>
1 parent 949de68 commit 72eb138

8 files changed

Lines changed: 106 additions & 0 deletions

docs/market-regime-control-plan.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ allowlist:
148148
position-control fields.
149149
- `evidence_status`: records whether the strategy/plugin pair is
150150
`automation_approved`, `notification_only`, or `deprecated_compatibility`.
151+
- When `position_control_allowed = true`, the runner output should also expose
152+
a machine-readable `auditable_position_control` block with
153+
`evidence_package_id`, `evidence_valid_until`, and `bounded_budget`.
151154
- `since_version`: records the runner schema version where the permission
152155
became effective.
153156

docs/market-regime-control-plan.zh-CN.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@
8787
- `notification_allowed`:允许生成和分发通知 artifact。
8888
- `position_control_allowed`:允许策略 runtime 自动消费仓位控制字段。
8989
- `evidence_status`:记录该策略/插件组合是 `automation_approved``notification_only` 还是 `deprecated_compatibility`
90+
-`position_control_allowed = true` 时,runner 输出还应暴露机器可读的
91+
`auditable_position_control` 块,包含 `evidence_package_id`
92+
`evidence_valid_until``bounded_budget`
9093
- `since_version`:记录该消费权限从哪个 runner schema 开始生效。
9194

9295
权限边界写在文档和机器字段里,不重复写进人工通知正文:

docs/plugin_lifecycle_policy.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ For any plugin-driven capital impact, all three gates must pass:
3636
2. **Plugin evidence gate**
3737
- The plugin must be marked `automation_approved` and
3838
`position_control_allowed = true`.
39+
- If the platform enables automated position control, the runner should also
40+
carry a machine-readable `auditable_position_control` block with
41+
`evidence_package_id`, `evidence_valid_until`, and `bounded_budget`.
3942
3. **Strategy/platform gate**
4043
- The consuming strategy must explicitly opt in to the plugin and remain
4144
allowed by the platform catalog.

docs/plugin_lifecycle_policy.zh-CN.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
- artifact 必须匹配支持的 schema version,并在共享契约中保持 `shadow` 模式。
3333
2. **插件证据门槛**
3434
- 插件必须标记为 `automation_approved`,且 `position_control_allowed = true`
35+
- 如果平台启用自动仓位控制,runner 还应携带机器可读的
36+
`auditable_position_control` 块,包含 `evidence_package_id`
37+
`evidence_valid_until``bounded_budget`
3538
3. **策略 / 平台门槛**
3639
- 消费策略必须显式 opt-in,并且仍然被平台 catalog 允许。
3740

src/quant_strategy_plugins/plugin_policies.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
from dataclasses import dataclass
4+
from typing import Any, Mapping
45

56
EVIDENCE_AUTOMATION_APPROVED = "automation_approved"
67
EVIDENCE_NOTIFICATION_ONLY = "notification_only"
@@ -32,6 +33,12 @@
3233
}
3334
PLUGIN_RESEARCH_ONLY_REASONS: dict[str, str] = {}
3435

36+
AUDITABLE_POSITION_CONTROL_FIELDS: tuple[str, ...] = (
37+
"evidence_package_id",
38+
"evidence_valid_until",
39+
"bounded_budget",
40+
)
41+
3542

3643
@dataclass(frozen=True)
3744
class PluginConsumptionPolicy:
@@ -240,6 +247,25 @@ class PluginLifecyclePolicy:
240247
(policy.plugin, policy.notification_target): policy for policy in PLUGIN_NOTIFICATION_TARGET_POLICIES
241248
}
242249

250+
251+
def extract_auditable_position_control_context(raw: Mapping[str, Any] | None) -> dict[str, Any]:
252+
"""Return the auditable position-control evidence block if present.
253+
254+
The preferred shape is ``{"auditable_position_control": {...}}`` but the
255+
helper also accepts a flat mapping so existing configs can opt in without a
256+
schema migration.
257+
"""
258+
if not isinstance(raw, Mapping):
259+
return {}
260+
candidate = raw.get("auditable_position_control")
261+
if isinstance(candidate, Mapping):
262+
raw = candidate
263+
return {
264+
key: raw[key]
265+
for key in AUDITABLE_POSITION_CONTROL_FIELDS
266+
if raw.get(key) is not None
267+
}
268+
243269
PLUGIN_COMPATIBLE_STRATEGIES: dict[str, tuple[str, ...]] = {
244270
plugin: tuple(
245271
policy.strategy
@@ -262,6 +288,7 @@ class PluginLifecyclePolicy:
262288
"EVIDENCE_AUTOMATION_APPROVED",
263289
"EVIDENCE_DEPRECATED_COMPATIBILITY",
264290
"EVIDENCE_NOTIFICATION_ONLY",
291+
"AUDITABLE_POSITION_CONTROL_FIELDS",
265292
"GENERAL_MARKET_REGIME_NOTIFICATION_TARGET",
266293
"PLUGIN_COMPATIBLE_NOTIFICATION_TARGETS",
267294
"PLUGIN_COMPATIBLE_STRATEGIES",
@@ -285,4 +312,5 @@ class PluginLifecyclePolicy:
285312
"PluginConsumptionPolicy",
286313
"PluginLifecyclePolicy",
287314
"PluginNotificationTargetPolicy",
315+
"extract_auditable_position_control_context",
288316
]

src/quant_strategy_plugins/strategy_plugin_runner.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
PLUGIN_RESEARCH_ONLY_REASONS,
5555
PLUGIN_SCHEMA_VERSIONS,
5656
PLUGIN_TACO_REBOUND_SHADOW,
57+
extract_auditable_position_control_context,
5758
PluginConsumptionPolicy,
5859
PluginLifecyclePolicy,
5960
PluginNotificationTargetPolicy,
@@ -1323,6 +1324,7 @@ def _apply_plugin_contract(
13231324
notification_target: str | None = None,
13241325
plugin: str,
13251326
mode: str,
1327+
auditable_position_control: Mapping[str, Any] | None = None,
13261328
consumption_policy: PluginConsumptionPolicy | None = None,
13271329
notification_target_policy: PluginNotificationTargetPolicy | None = None,
13281330
) -> dict[str, Any]:
@@ -1368,6 +1370,10 @@ def _apply_plugin_contract(
13681370
execution_controls["capital_impact"] = "strategy_opt_in"
13691371
execution_controls["strategy_runtime_metadata_allowed"] = True
13701372
execution_controls["position_control_shadow_only"] = False
1373+
if auditable_position_control:
1374+
execution_controls["auditable_position_control"] = auditable_position_control
1375+
contracted_payload["position_control"] = dict(contracted_payload.get("position_control") or {})
1376+
contracted_payload["position_control"]["auditable_position_control"] = auditable_position_control
13711377
else:
13721378
execution_controls["capital_impact"] = "notification_only"
13731379
execution_controls["strategy_runtime_metadata_allowed"] = False
@@ -1513,6 +1519,9 @@ def _run_table_strategy_plugin(
15131519
strategy=strategy,
15141520
plugin=plugin,
15151521
mode=mode,
1522+
auditable_position_control=extract_auditable_position_control_context(
1523+
plugin_config.get("auditable_position_control")
1524+
),
15161525
consumption_policy=consumption_policy,
15171526
)
15181527
paths = spec.write_outputs(payload, output_dir)

tests/test_plugin_policies.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import quant_strategy_plugins.strategy_plugin_runner as strategy_plugin_runner
44
from quant_strategy_plugins.plugin_policies import (
5+
AUDITABLE_POSITION_CONTROL_FIELDS,
56
EVIDENCE_AUTOMATION_APPROVED,
67
GENERAL_MARKET_REGIME_NOTIFICATION_TARGET,
78
PLUGIN_COMPATIBLE_STRATEGIES,
@@ -11,6 +12,7 @@
1112
PLUGIN_LIFECYCLE_POLICY_REGISTRY,
1213
PLUGIN_MARKET_REGIME_CONTROL,
1314
PLUGIN_NOTIFICATION_TARGET_POLICY_REGISTRY,
15+
extract_auditable_position_control_context,
1416
)
1517

1618

@@ -37,3 +39,39 @@ def test_deprecated_plugin_lifecycle_blocks_new_mounts_but_keeps_replay() -> Non
3739
assert policy.new_mount_allowed is False
3840
assert policy.replay_only is True
3941
assert policy.successor == PLUGIN_MARKET_REGIME_CONTROL
42+
43+
44+
def test_extract_auditable_position_control_context_supports_nested_and_flat_shapes() -> None:
45+
nested = extract_auditable_position_control_context(
46+
{
47+
"auditable_position_control": {
48+
"evidence_package_id": "pkg_001",
49+
"evidence_valid_until": "2026-08-01T00:00:00Z",
50+
"bounded_budget": {"name": "position_control", "amount": 0.5, "unit": "fraction"},
51+
"ignored": "value",
52+
}
53+
}
54+
)
55+
flat = extract_auditable_position_control_context(
56+
{
57+
"evidence_package_id": "pkg_002",
58+
"evidence_valid_until": "2026-08-02T00:00:00Z",
59+
"bounded_budget": {"name": "position_control", "amount": 0.25, "unit": "fraction"},
60+
}
61+
)
62+
63+
assert tuple(AUDITABLE_POSITION_CONTROL_FIELDS) == (
64+
"evidence_package_id",
65+
"evidence_valid_until",
66+
"bounded_budget",
67+
)
68+
assert nested == {
69+
"evidence_package_id": "pkg_001",
70+
"evidence_valid_until": "2026-08-01T00:00:00Z",
71+
"bounded_budget": {"name": "position_control", "amount": 0.5, "unit": "fraction"},
72+
}
73+
assert flat == {
74+
"evidence_package_id": "pkg_002",
75+
"evidence_valid_until": "2026-08-02T00:00:00Z",
76+
"bounded_budget": {"name": "position_control", "amount": 0.25, "unit": "fraction"},
77+
}

tests/test_strategy_plugin_runner.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,15 @@ def test_strategy_plugin_runner_runs_unified_market_regime_control_for_soxl(tmp_
680680
"plugin": PLUGIN_MARKET_REGIME_CONTROL,
681681
"enabled": True,
682682
"inputs": {"prices": str(prices_path), "benchmark_symbol": "SOXX", "attack_symbol": "SOXL"},
683+
"auditable_position_control": {
684+
"evidence_package_id": "pkg_001",
685+
"evidence_valid_until": "2026-08-01T00:00:00Z",
686+
"bounded_budget": {
687+
"name": "position_control",
688+
"amount": 0.5,
689+
"unit": "fraction",
690+
},
691+
},
683692
"outputs": {"output_dir": str(output_dir)},
684693
}
685694
],
@@ -697,6 +706,15 @@ def test_strategy_plugin_runner_runs_unified_market_regime_control_for_soxl(tmp_
697706
assert payload["execution_controls"]["strategy_runtime_metadata_allowed"] is True
698707
assert payload["execution_controls"]["capital_impact"] == "strategy_opt_in"
699708
assert payload["execution_controls"]["position_control_shadow_only"] is False
709+
assert payload["execution_controls"]["auditable_position_control"] == {
710+
"evidence_package_id": "pkg_001",
711+
"evidence_valid_until": "2026-08-01T00:00:00Z",
712+
"bounded_budget": {
713+
"name": "position_control",
714+
"amount": 0.5,
715+
"unit": "fraction",
716+
},
717+
}
700718
assert payload["execution_controls"]["manual_review_notification_delegated"] is True
701719
assert (
702720
payload["execution_controls"]["manual_review_notification_target"]
@@ -709,6 +727,7 @@ def test_strategy_plugin_runner_runs_unified_market_regime_control_for_soxl(tmp_
709727
volatility_delever_context = payload["position_control"]["volatility_delever_context"]
710728
assert volatility_delever_context["actionable_for_position_control"] is True
711729
assert volatility_delever_context["retention_profiles"]["soxl_step_rebound_0.25_0.50"]["retention_ratio"] == 0.0
730+
assert payload["position_control"]["auditable_position_control"] == payload["execution_controls"]["auditable_position_control"]
712731

713732

714733
def test_strategy_plugin_runner_marks_pending_strategy_mount_notification_only(tmp_path) -> None:

0 commit comments

Comments
 (0)