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
52 changes: 52 additions & 0 deletions docs/quant_strategy_spec_contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Quant Strategy Specification Contracts

This document defines the first versioned artifacts introduced by the Quant
Strategy Review and Optimization Standard. They are research evidence inputs;
they do not change existing lifecycle stages, evidence-package fields, or
runtime interfaces.

## Artifacts

| Artifact | Schema version | Purpose |
| --- | --- | --- |
| `ResearchSpec` | `research_spec.v1` | Freeze a falsifiable hypothesis, PIT data revision, four-layer benchmarks, net cost model, OOS plan, and complete trial ledger before evaluation. |
| `OptimizationSpec` | `optimization_spec.v1` | Freeze optimization inputs, permitted parameter ranges, constrained objective, nested WFA, locked holdout, multiple-testing control, cost stress, stop rules, and human-only promotion. |

Schemas are stored as package data under `quant_platform_kit/schemas/`, so they
remain available in installed wheels. Python validation is deliberately
dependency free so an evidence gate can consume the same artifact without
installing a JSON Schema runtime.

The schemas use QPK extensions for invariants that standard JSON Schema cannot
express: sibling date ordering (`x-qpk-date-order` and
`x-qpk-exclusive-date-order`) and parameter-name uniqueness
(`x-qpk-unique-by`). QPK's validator and CLI enforce these extensions; a generic
JSON Schema-only consumer must not be used as a promotion gate.

```bash
quant-strategy-spec path/to/spec.json
```

The command is silent on success, prints field-level contract violations to
stderr on failure, and returns a non-zero exit code. Code integrations may
use `validate_research_spec`, `validate_optimization_spec`, or
`validate_strategy_spec_file` from the lightweight
`quant_platform_kit.strategy_spec` package.
Source checkouts may use `python scripts/validate_strategy_spec.py` as an
equivalent compatibility wrapper.

## v1 safety rules

- Research evaluation requires non-overlapping in-sample/OOS ranges, a locked
OOS interval, at least three walk-forward folds, PIT and survivorship checks,
net-of-cost accounting, all-trial recording, and capital/passive/risk-matched/
simple-rule benchmarks.
- Optimization can only vary declared parameters. It requires frozen data,
universe, benchmark, cost-model, and code references; nested walk-forward;
a non-reused locked holdout; complete trial recording; and 1x/2x/3x cost
stress.
- v1 rejects full Kelly and automatic risk increases. Kelly is retained only
as a bounded fractional-cap input and human approval remains mandatory.

`StrategyReviewReport` and `PositionBudgetReport` are intentionally deferred.
They will consume these immutable inputs rather than duplicate them.
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ dependencies = []

[project.scripts]
quant-lifecycle = "quant_platform_kit.strategy_lifecycle.cli:main"
quant-strategy-spec = "quant_platform_kit.strategy_spec.cli:main"

[tool.setuptools]
package-dir = { "" = "src" }

[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools.package-data]
quant_platform_kit = ["schemas/*.json"]

[tool.ruff]
target-version = "py310"

Expand Down
17 changes: 17 additions & 0 deletions scripts/validate_strategy_spec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env python3
"""Validate a versioned ResearchSpec or OptimizationSpec JSON artifact."""

from __future__ import annotations

import sys
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))

from quant_platform_kit.strategy_spec.cli import main


if __name__ == "__main__":
raise SystemExit(main())
6 changes: 6 additions & 0 deletions src/quant_platform_kit.egg-info/SOURCES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ src/quant_platform_kit/risk/contracts.py
src/quant_platform_kit/risk/engine.py
src/quant_platform_kit/risk/gate.py
src/quant_platform_kit/risk/portfolio_diagnostics.py
src/quant_platform_kit/schemas/optimization-spec.v1.schema.json
src/quant_platform_kit/schemas/research-spec.v1.schema.json
src/quant_platform_kit/schwab/__init__.py
src/quant_platform_kit/schwab/auth.py
src/quant_platform_kit/schwab/execution.py
Expand Down Expand Up @@ -140,6 +142,9 @@ src/quant_platform_kit/strategy_lifecycle/shadow_validator.py
src/quant_platform_kit/strategy_lifecycle/strategy_health_score.py
src/quant_platform_kit/strategy_lifecycle/update_orchestrator.py
src/quant_platform_kit/strategy_lifecycle/update_policy.py
src/quant_platform_kit/strategy_spec/__init__.py
src/quant_platform_kit/strategy_spec/cli.py
src/quant_platform_kit/strategy_spec/validation.py
tests/test_backtest_orchestrator.py
tests/test_binance_account.py
tests/test_binance_client.py
Expand Down Expand Up @@ -216,6 +221,7 @@ tests/test_strategy_plugin_push_notifications.py
tests/test_strategy_plugin_sms_notifications.py
tests/test_strategy_plugin_telegram_notifications.py
tests/test_strategy_plugins.py
tests/test_strategy_spec_validation.py
tests/test_telegram.py
tests/test_update_orchestrator.py
tests/test_validate_strategy_evidence_package.py
213 changes: 213 additions & 0 deletions src/quant_platform_kit/schemas/optimization-spec.v1.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://quantplatformkit.local/schemas/optimization-spec.v1.schema.json",
"title": "OptimizationSpec v1",
"type": "object",
"required": [
"schema_version",
"spec_id",
"research_spec_id",
"strategy_profile",
"created_at",
"frozen_inputs",
"allowed_parameters",
"objective",
"search",
"validation",
"stop_rules",
"promotion"
],
"properties": {
"schema_version": { "const": "optimization_spec.v1" },
"spec_id": { "type": "string", "minLength": 1 },
"research_spec_id": { "type": "string", "minLength": 1 },
"strategy_profile": { "type": "string", "minLength": 1 },
"created_at": {
"type": "string",
"format": "date-time",
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?(Z|[+-][0-9]{2}:[0-9]{2})$"
},
"frozen_inputs": { "$ref": "#/$defs/frozen_inputs" },
"allowed_parameters": {
"type": "array",
"minItems": 1,
"x-qpk-unique-by": "name",
"items": { "$ref": "#/$defs/parameter" }
Comment thread
Pigbibi marked this conversation as resolved.
},
"objective": { "$ref": "#/$defs/objective" },
"search": { "$ref": "#/$defs/search" },
"validation": { "$ref": "#/$defs/validation" },
"stop_rules": {
"type": "array",
"minItems": 1,
"items": { "type": "string", "minLength": 1 }
},
"promotion": { "$ref": "#/$defs/promotion" }
},
"$defs": {
"frozen_inputs": {
"type": "object",
"required": ["data_manifest_id", "universe_id", "benchmark_ids", "cost_model_id", "code_revision"],
"properties": {
"data_manifest_id": { "type": "string", "minLength": 1 },
"universe_id": { "type": "string", "minLength": 1 },
"benchmark_ids": {
"type": "array",
"minItems": 1,
"items": { "type": "string", "minLength": 1 }
},
"cost_model_id": { "type": "string", "minLength": 1 },
"code_revision": { "type": "string", "minLength": 1 }
},
"additionalProperties": true
},
"parameter": {
"type": "object",
"required": ["name", "kind"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"kind": { "enum": ["integer", "number", "choice", "boolean"] },
"bounds": {
Comment thread
Pigbibi marked this conversation as resolved.
"type": "array",
"minItems": 2,
"maxItems": 2,
"items": { "type": "number" }
Comment thread
Pigbibi marked this conversation as resolved.
},
"choices": {
"type": "array",
"minItems": 1,
"items": { "type": ["string", "number", "boolean"] }
},
"step": { "type": "number", "exclusiveMinimum": 0 }
},
"allOf": [
{
"if": {
"required": ["kind"],
"properties": { "kind": { "const": "integer" } }
},
"then": {
"required": ["bounds"],
"properties": {
"bounds": { "items": { "type": "integer" } },
"step": { "type": "integer", "exclusiveMinimum": 0 }
}
}
},
{
"if": {
"required": ["kind"],
"properties": { "kind": { "const": "number" } }
},
"then": { "required": ["bounds"] }
},
{
"if": {
"required": ["kind"],
"properties": { "kind": { "const": "choice" } }
},
"then": { "required": ["choices"] }
}
],
"additionalProperties": true
},
"objective": {
"type": "object",
"required": ["primary_metric", "hard_constraints"],
"properties": {
"primary_metric": { "type": "string", "minLength": 1 },
"secondary_metrics": {
"type": "array",
"items": { "type": "string", "minLength": 1 }
},
"hard_constraints": {
"type": "array",
"minItems": 1,
"items": { "type": "string", "minLength": 1 }
}
},
"additionalProperties": true
},
"search": {
"type": "object",
"required": ["method", "max_trials", "random_seed"],
"properties": {
"method": { "enum": ["grid", "random", "bayesian"] },
"max_trials": { "type": "integer", "minimum": 1 },
"random_seed": { "type": "integer" }
},
"additionalProperties": true
},
"validation": {
"type": "object",
"required": ["nested_walk_forward", "locked_holdout", "multiple_testing", "cost_stress"],
"properties": {
"nested_walk_forward": {
"type": "object",
"required": ["enabled", "fold_count", "selection_scope"],
"properties": {
"enabled": { "const": true },
"fold_count": { "type": "integer", "minimum": 3 },
"selection_scope": { "const": "train_validation_only" }
},
"additionalProperties": true
},
"locked_holdout": {
"type": "object",
"required": ["enabled", "reused_for_selection"],
"properties": {
"enabled": { "const": true },
"reused_for_selection": { "const": false }
},
"additionalProperties": true
},
"multiple_testing": {
"type": "object",
"required": ["method", "trial_ledger_id", "record_all_trials"],
"properties": {
"method": { "enum": ["dsr", "pbo", "spa", "reality_check", "fdr", "other_equivalent"] },
"trial_ledger_id": { "type": "string", "minLength": 1 },
"record_all_trials": { "const": true }
},
"additionalProperties": true
},
"cost_stress": {
"type": "object",
"required": ["multipliers", "required_pass"],
"properties": {
"multipliers": {
"type": "array",
"minItems": 3,
"items": { "type": "number", "minimum": 1 },
"allOf": [
{ "contains": { "const": 1 } },
{ "contains": { "const": 2 } },
{ "contains": { "const": 3 } }
]
},
"required_pass": { "const": true }
},
"additionalProperties": true
}
},
"additionalProperties": true
},
"promotion": {
"type": "object",
"required": [
"requires_human_approval",
"automatic_risk_increase_allowed",
"full_kelly_allowed",
"max_fractional_kelly"
],
"properties": {
"requires_human_approval": { "const": true },
"automatic_risk_increase_allowed": { "const": false },
"full_kelly_allowed": { "const": false },
"max_fractional_kelly": { "type": "number", "exclusiveMinimum": 0, "exclusiveMaximum": 1 }
},
"additionalProperties": true
}
},
"additionalProperties": true
}
Loading
Loading