Skip to content

Commit 113bae5

Browse files
Pigbibicodex
andcommitted
fix(strategy): formalize canary and verified provenance
Co-Authored-By: Codex <noreply@openai.com>
1 parent 23256e3 commit 113bae5

4 files changed

Lines changed: 36 additions & 3 deletions

File tree

schemas/strategy-review.v1.schema.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,19 @@
8585
"research_auto_after_hard_gates": { "const": true },
8686
"shadow_auto_after_hard_gates": { "const": true },
8787
"canary_mode": { "const": "bounded_preapproved_only" },
88-
"canary_limits": { "type": "object" },
88+
"canary_limits": {
89+
"type": "object",
90+
"required": ["max_capital", "capital_currency", "max_duration_days", "max_drawdown_fraction", "max_leverage", "max_concurrency"],
91+
"properties": {
92+
"max_capital": { "type": "number", "exclusiveMinimum": 0 },
93+
"capital_currency": { "type": "string", "minLength": 3 },
94+
"max_duration_days": { "type": "integer", "minimum": 1 },
95+
"max_drawdown_fraction": { "type": "number", "exclusiveMinimum": 0, "exclusiveMaximum": 1 },
96+
"max_leverage": { "type": "number", "exclusiveMinimum": 0 },
97+
"max_concurrency": { "type": "integer", "minimum": 1 }
98+
},
99+
"additionalProperties": false
100+
},
89101
"auto_scale_allowed": { "const": false },
90102
"normal_live_requires_human": { "const": true },
91103
"funding_leverage_risk_override_requires_human": { "const": true },

scripts/validate_strategy_review.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def validate_review(payload: Any) -> list[str]:
4747
if not isinstance(payload.get("scorecard"), dict):
4848
issues.append("scorecard must be an object")
4949
evidence = payload.get("evidence")
50+
provenance: dict[str, Any] = {}
5051
if not isinstance(evidence, dict):
5152
issues.append("evidence must be an object")
5253
else:
@@ -69,6 +70,14 @@ def validate_review(payload: Any) -> list[str]:
6970
issues.append(f"evidence.provenance.{source}.{field} must be non-empty")
7071
if item.get("status") not in {"verified", "legacy_missing", "unavailable"}:
7172
issues.append(f"evidence.provenance.{source}.status is invalid")
73+
if item.get("status") == "verified":
74+
try:
75+
from datetime import datetime
76+
timestamp = datetime.fromisoformat(item["data_timestamp"].replace("Z", "+00:00"))
77+
if timestamp.tzinfo is None:
78+
raise ValueError
79+
except (AttributeError, TypeError, ValueError):
80+
issues.append(f"evidence.provenance.{source}.verified requires a real timestamp")
7281
if evidence.get("placeholder_metrics") is not False:
7382
issues.append("placeholder metrics are not admissible evidence")
7483
if not isinstance(evidence.get("sample_count"), int) or isinstance(evidence.get("sample_count"), bool) or evidence["sample_count"] < 0:
@@ -109,6 +118,18 @@ def validate_review(payload: Any) -> list[str]:
109118
issues.append(f"decision_packet.automation_boundary.{field} is unsafe")
110119
if not isinstance(boundary.get("canary_limits"), dict):
111120
issues.append("decision_packet.automation_boundary.canary_limits must be an object")
121+
else:
122+
limits = boundary["canary_limits"]
123+
checks = {
124+
"max_capital": lambda v: isinstance(v, (int, float)) and not isinstance(v, bool) and v > 0,
125+
"capital_currency": lambda v: isinstance(v, str) and len(v.strip()) >= 3,
126+
"max_duration_days": lambda v: isinstance(v, int) and not isinstance(v, bool) and v >= 1,
127+
"max_drawdown_fraction": lambda v: isinstance(v, (int, float)) and not isinstance(v, bool) and 0 < v < 1,
128+
"max_leverage": lambda v: isinstance(v, (int, float)) and not isinstance(v, bool) and v > 0,
129+
"max_concurrency": lambda v: isinstance(v, int) and not isinstance(v, bool) and v >= 1,
130+
}
131+
if set(limits) != set(checks) or any(not check(limits.get(key)) for key, check in checks.items()):
132+
issues.append("decision_packet.automation_boundary.canary_limits is incomplete or invalid")
112133
allowed = packet.get("allowed_human_decisions")
113134
if not isinstance(allowed, list) or not allowed or any(item not in {"approve_research", "approve_shadow", "approve_canary", "approve_live", "reject_rollback"} for item in allowed):
114135
issues.append("decision_packet.allowed_human_decisions is invalid")

src/quant_platform_kit/strategy_lifecycle/performance_export.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def export_strategy_performance(
146146
"source_revision": backtest.source_revision or "legacy_missing",
147147
"cost_model": backtest.cost_model or "legacy_missing",
148148
"data_timestamp": backtest.end_date.isoformat() if backtest.end_date else "unavailable",
149-
"status": "verified" if backtest.source_revision and backtest.cost_model else "legacy_missing",
149+
"status": "verified" if backtest.source_revision and backtest.cost_model and backtest.end_date else "legacy_missing",
150150
},
151151
},
152152
"data_timestamp": snapshot.as_of.isoformat(),

tests/test_strategy_review_validator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def _review(**overrides: object) -> dict[str, object]:
4949
"research_auto_after_hard_gates": True,
5050
"shadow_auto_after_hard_gates": True,
5151
"canary_mode": "bounded_preapproved_only",
52-
"canary_limits": {"capital": "preapproved", "duration": "preapproved", "max_drawdown": "preapproved", "max_leverage": "preapproved", "max_concurrency": "preapproved"},
52+
"canary_limits": {"max_capital": 1000.0, "capital_currency": "USD", "max_duration_days": 14, "max_drawdown_fraction": 0.05, "max_leverage": 1.0, "max_concurrency": 1},
5353
"auto_scale_allowed": False,
5454
"normal_live_requires_human": True,
5555
"funding_leverage_risk_override_requires_human": True,

0 commit comments

Comments
 (0)