Compare commits
2
Commits
main
...
8c5af40dad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c5af40dad | ||
|
|
d4ee6f005f |
+6
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"module_id": "quant_engine",
|
||||
"authority": {"scope": "module_metadata", "subject": "quant_engine", "owner": "quant-engine-owner", "source": "MODULE_SPEC.yaml", "revision": 3, "effective_from": "2026-09-01T00:00:00+08:00"},
|
||||
"authority": {"scope": "module_metadata", "subject": "quant_engine", "owner": "quant-engine-owner", "source": "MODULE_SPEC.yaml", "revision": 4, "effective_from": "2026-09-01T00:00:00+08:00"},
|
||||
"repository": {"name": "quant_engine", "workspace_id": "researchhub", "type": "research_engine", "maturity": "operational"},
|
||||
"bounded_context": {
|
||||
"domain": "quantitative-research-engine",
|
||||
@@ -11,6 +11,7 @@
|
||||
"Submitting live orders, routing trades, managing brokerage accounts, or claiming transaction execution",
|
||||
"Owning market-data source facts, research-result publication, or platform presentation state",
|
||||
"Loading provider credentials, brokerage credentials, or production secrets",
|
||||
"Granting portfolio approval, maker-checker decisions, publication eligibility, paper execution, or live execution authority",
|
||||
"Changing financial model semantics through module metadata"
|
||||
]
|
||||
},
|
||||
@@ -19,6 +20,7 @@
|
||||
{"id": "execution-simulation", "summary": "Simulate costs, slippage, market constraints, fills, NAV, and PnL without live order routing.", "status": "operational"},
|
||||
{"id": "portfolio-backtesting", "summary": "Run weight-based backtests and benchmark comparisons.", "status": "operational"},
|
||||
{"id": "backtest-evidence-contracts", "summary": "Identify governed offline backtest inputs and close existing research artifact evidence without persistence or decision authority.", "status": "operational"},
|
||||
{"id": "portfolio-risk-computation-contracts", "summary": "Verify deterministic portfolio-computation receipts and expose S3-bound portfolio decisions and risk assessments without adding algorithms or execution authority.", "status": "operational"},
|
||||
{"id": "risk-and-performance-analysis", "summary": "Calculate portfolio decomposition, risk contribution, and performance statistics.", "status": "operational"}
|
||||
],
|
||||
"data": {"owns": [
|
||||
@@ -30,7 +32,9 @@
|
||||
{"contract_id": "researchhub.factor-definition", "version": "1.0.0", "authority": "quant_engine", "path": "src/quant_engine/factor_contracts.py"},
|
||||
{"contract_id": "researchhub.factor-set-ref", "version": "1.0.0", "authority": "quant_engine", "path": "src/quant_engine/factor_contracts.py"},
|
||||
{"contract_id": "researchhub.backtest-run-ref", "version": "1.0.0", "authority": "quant_engine", "path": "src/quant_engine/governed_pipeline.py"},
|
||||
{"contract_id": "researchhub.backtest-evidence-manifest", "version": "1.0.0", "authority": "quant_engine", "path": "src/quant_engine/artifact.py"}
|
||||
{"contract_id": "researchhub.backtest-evidence-manifest", "version": "1.0.0", "authority": "quant_engine", "path": "src/quant_engine/artifact.py"},
|
||||
{"contract_id": "researchhub.portfolio-decision", "version": "1.0.0", "authority": "quant_engine", "path": "src/quant_engine/portfolio_risk_contracts.py"},
|
||||
{"contract_id": "researchhub.risk-assessment", "version": "1.0.0", "authority": "quant_engine", "path": "src/quant_engine/portfolio_risk_contracts.py"}
|
||||
],
|
||||
"consumes": [
|
||||
{"contract_id": "researchhub.dataset-snapshot", "version": "1.0.0", "authority": "researchhub.data", "admission": "qualified_immutable_envelope"},
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
- `research_pipeline` — 因子日 → 下一真实交易日 → 显式执行价 → 日末估值 → 成本后绩效(防前视编排)
|
||||
- `governed_pipeline` — 数据快照 → 因子版本 → 策略版本 → 回测运行 → 目标组合 → 风险决策 → Paper 订单意图;同时拥有输入/配置/重放血缘决定的 `BacktestRunRef`
|
||||
- `artifact` — 版本化、确定性、存储中立的完整 research run 事实表,以及只映射现有表的 `BacktestEvidenceManifest`
|
||||
- `portfolio_risk_contracts` — S3 证据闭合的 `PortfolioDecision` / `RiskAssessment` v1;独立复核 freshness、约束与 computation receipt,并复用既有标签安全风险分解
|
||||
- `attribution` — 基于实际成交后持仓的隔夜 / 日内 / 交易成本逐日收益归因与闭合审计
|
||||
- `metrics` — 绝对绩效 + 严格日期对齐的 TE / IR / alpha / beta 基准相对绩效
|
||||
- `factor_library` — 通用方法(turnover / winsorize / IC / OLS / jb_test)
|
||||
@@ -259,6 +260,111 @@ digest 等价,也不会把旧 run 静默升级为新合同。
|
||||
`LEGACY_EXPLORATORY`;不能隐式提升为 `CONTRACT_QUALIFIED`。所有资格均只描述离线证据闭合,
|
||||
不表示投资有效、组合获批、Paper、生产或实盘就绪。
|
||||
|
||||
## 组合决策与风险评估合同 v1
|
||||
|
||||
`quant_engine.portfolio_risk_contracts` 是现有计算 owner 外围的薄合同层。创建
|
||||
`PortfolioDecision` 必须同时提供完整 `BacktestRunRef`、嵌入同一 RunRef 的
|
||||
`CONTRACT_QUALIFIED` 非 legacy `BacktestEvidenceManifest`、现有 `PortfolioTarget`、
|
||||
`FreshnessPolicy`、`ConstraintSetV1` 与 `ComputationReceipt`。适配器会从权威输入独立重算
|
||||
receipt 的 input/constraint/output digest、敞口、持仓数和 L1 turnover 残差;receipt 自报
|
||||
成功、fallback 或放宽 tolerance 均不能替代复核。
|
||||
|
||||
```python
|
||||
from quant_engine.portfolio_risk_contracts import (
|
||||
ComputationReceipt,
|
||||
ConstraintSetV1,
|
||||
FreshnessPolicy,
|
||||
assess_portfolio_risk,
|
||||
build_portfolio_decision,
|
||||
compute_portfolio_receipt_digests,
|
||||
)
|
||||
|
||||
freshness = FreshnessPolicy(
|
||||
max_manifest_age_seconds=3600,
|
||||
max_covariance_age_days=5,
|
||||
)
|
||||
constraints = ConstraintSetV1(
|
||||
gross_exposure_max=1.0,
|
||||
single_asset_max=0.10,
|
||||
position_count_max=20,
|
||||
turnover_max=0.30,
|
||||
)
|
||||
|
||||
# 生产者先形成公开 canonical digest;decision 构建时仍会独立重算。
|
||||
expected = compute_portfolio_receipt_digests(
|
||||
backtest_run_ref=run_ref,
|
||||
manifest=evidence_manifest,
|
||||
target=portfolio_target,
|
||||
objective_name="long_only_allocation",
|
||||
objective_version="1.0.0",
|
||||
objective_digest=objective_digest,
|
||||
model_name="factor_weighting",
|
||||
model_version="1.0.0",
|
||||
model_digest=model_digest,
|
||||
expected_return_digest=expected_return_digest,
|
||||
covariance_digest=covariance_digest,
|
||||
scenario_digest=scenario_digest,
|
||||
constraints=constraints,
|
||||
freshness_policy=freshness,
|
||||
prior_weights=prior_weights,
|
||||
)
|
||||
|
||||
receipt = ComputationReceipt(
|
||||
algorithm="factor_weighting",
|
||||
algorithm_version="1.0.0",
|
||||
implementation_digest=implementation_digest,
|
||||
parameter_digest=parameter_digest,
|
||||
input_digest=expected["input_digest"],
|
||||
constraint_digest=expected["constraint_digest"],
|
||||
output_digest=expected["output_digest"],
|
||||
status="completed",
|
||||
solver_required=False,
|
||||
solver_name=None,
|
||||
solver_version=None,
|
||||
solver_config_digest=None,
|
||||
iterations=None,
|
||||
objective_value=None,
|
||||
max_constraint_residual=expected["max_constraint_residual"],
|
||||
tolerance=1e-12,
|
||||
computed_at=computed_at,
|
||||
)
|
||||
|
||||
decision = build_portfolio_decision(
|
||||
backtest_run_ref=run_ref,
|
||||
manifest=evidence_manifest,
|
||||
target=portfolio_target,
|
||||
objective_name="long_only_allocation",
|
||||
objective_version="1.0.0",
|
||||
objective_digest=objective_digest,
|
||||
model_name="factor_weighting",
|
||||
model_version="1.0.0",
|
||||
model_digest=model_digest,
|
||||
expected_return_digest=expected_return_digest,
|
||||
covariance_digest=covariance_digest,
|
||||
scenario_digest=scenario_digest,
|
||||
constraints=constraints,
|
||||
freshness_policy=freshness,
|
||||
receipt=receipt,
|
||||
computed_at=computed_at,
|
||||
prior_weights=prior_weights,
|
||||
)
|
||||
|
||||
assessment = assess_portfolio_risk(
|
||||
portfolio_decision=decision,
|
||||
backtest_run_ref=run_ref,
|
||||
manifest=evidence_manifest,
|
||||
covariance=covariance_snapshot,
|
||||
risk_model_name="euler_volatility",
|
||||
risk_model_version="1.0.0",
|
||||
risk_model_digest=risk_model_digest,
|
||||
)
|
||||
```
|
||||
|
||||
`source_universe_digest` 保留 S3 研究 universe 身份,`portfolio_asset_set_digest` 只描述实际
|
||||
目标资产标签;二者不会互相冒充成员证明。风险评估在任何数值计算前要求 covariance、target、
|
||||
RunRef 的 dataset identity 三方一致,并且只调用一次现有 `labeled_component_risk()`。合同中的
|
||||
`qualified` 仅表示 S4.1 计算证据闭合,不授予 maker-checker、发布、订单、Paper、生产或实盘权限。
|
||||
|
||||
## 治理垂直切片
|
||||
|
||||
`governed_pipeline` 不复制因子、回测、组合或执行算法,只编排现有能力并补充版本与风险契约。
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,129 @@
|
||||
{
|
||||
"portfolio_decision": {
|
||||
"computed_at": "2026-01-08T03:01:00Z",
|
||||
"constraint_residuals": {
|
||||
"gross_exposure_max": 0.0,
|
||||
"net_exposure_max": 0.0,
|
||||
"net_exposure_min": 0.0,
|
||||
"position_count_max": 0.0,
|
||||
"single_asset_max": 0.0,
|
||||
"single_asset_min": 0.0,
|
||||
"turnover_max": 0.0
|
||||
},
|
||||
"constraints": {
|
||||
"gross_exposure_max": 1.0,
|
||||
"net_exposure_max": 1.0,
|
||||
"net_exposure_min": 1.0,
|
||||
"position_count_max": 2,
|
||||
"schema_version": "1.0.0",
|
||||
"single_asset_max": 0.7,
|
||||
"single_asset_min": 0.2,
|
||||
"turnover_max": 0.2
|
||||
},
|
||||
"contract_name": "researchhub.portfolio-decision",
|
||||
"covariance_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"dataset_snapshot_id": "rhdsv1:sha256:f63a29b4795c63fb7d6b2d3b5544cee9274b633db77c75c63d50a340c0827d57",
|
||||
"decision_id": "rhportfoliodecisionv1:sha256:0e6e5ce2fa08de8006cc392610695a013327fc80645bfa4d7a908ad3f615bebd",
|
||||
"effective_at": "2026-01-08T03:00:00Z",
|
||||
"evidence_digest": "sha256:f3913894d032c389c64eef59058b3cbc694cb9cc14ce9cee2699f0068200b650",
|
||||
"expected_return_digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
|
||||
"freshness_policy": {
|
||||
"max_covariance_age_days": 0,
|
||||
"max_manifest_age_seconds": 3600,
|
||||
"schema_version": "1.0.0"
|
||||
},
|
||||
"gross_exposure": 1.0,
|
||||
"manifest_document_sha256": "fbf54218f770528978f9ccd35577e1ab00877143ea397a576e071e75f0afbab0",
|
||||
"manifest_id": "rhbacktestevidencev1:sha256:681c49cbdfb3e221b273e7bc616602ad80a7ab01206970807ad4294b176ebc75",
|
||||
"model_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
|
||||
"model_name": "deterministic_weights",
|
||||
"model_version": "1.0.0",
|
||||
"net_exposure": 1.0,
|
||||
"objective_digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
"objective_name": "long_only_allocation",
|
||||
"objective_version": "1.0.0",
|
||||
"output_digest": "sha256:bd3b964c628c8648322d036e57dd6f444ca287017d1578bab3689d07d32b28ce",
|
||||
"portfolio_asset_set_digest": "sha256:b64e3448a83a5b86466465080361c1a7e1157a27ddccd4b68069cb18caffb74a",
|
||||
"position_count": 2,
|
||||
"prior_weights": {
|
||||
"A": 0.5,
|
||||
"B": 0.5
|
||||
},
|
||||
"receipt": {
|
||||
"algorithm": "bounded_allocation",
|
||||
"algorithm_version": "1.0.0",
|
||||
"computed_at": "2026-01-08T03:01:00Z",
|
||||
"constraint_digest": "sha256:34df0e5c00f503748ff936f9cd415a8947169181f8ca0917bce97dd606e08e94",
|
||||
"implementation_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"input_digest": "sha256:ebd8ff957115e1adfd84eafa5cea49470356194d747b64ee5897858f9dd067b7",
|
||||
"iterations": null,
|
||||
"max_constraint_residual": 0.0,
|
||||
"objective_value": null,
|
||||
"output_digest": "sha256:bd3b964c628c8648322d036e57dd6f444ca287017d1578bab3689d07d32b28ce",
|
||||
"parameter_digest": "sha256:0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"schema_version": "1.0.0",
|
||||
"solver_config_digest": null,
|
||||
"solver_name": null,
|
||||
"solver_required": false,
|
||||
"solver_version": null,
|
||||
"status": "completed",
|
||||
"tolerance": 1e-12
|
||||
},
|
||||
"run_id": "rhbacktestrunv1:sha256:5036c771c44a2adade9ea590eee0d8cf824ff8f519fadd8ba424e5d914856386",
|
||||
"run_ref_document_sha256": "6a798adb3e0568aca84ed3e3a285b92d181ec569462fc003952a8c0d8382ae3a",
|
||||
"scenario_digest": "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
"schema_version": "1.0.0",
|
||||
"source_universe_digest": "sha256:5555555555555555555555555555555555555555555555555555555555555555",
|
||||
"target_id": "portfolio-target:synthetic-v1",
|
||||
"target_weights": {
|
||||
"A": 0.6,
|
||||
"B": 0.4
|
||||
},
|
||||
"turnover_l1": 0.19999999999999996
|
||||
},
|
||||
"risk_assessment": {
|
||||
"assessment_id": "rhriskassessmentv1:sha256:dbc38825cffcf6d95bd0216d22dbba4e0d4a1359ca5924a3ee529b99a7d78b6d",
|
||||
"component_risk": {
|
||||
"A": 1.4549226783578566,
|
||||
"B": 1.4549226783578568
|
||||
},
|
||||
"contract_name": "researchhub.risk-assessment",
|
||||
"covariance_as_of_date": "2026-01-08",
|
||||
"covariance_data_snapshot_id": "rhdsv1:sha256:f63a29b4795c63fb7d6b2d3b5544cee9274b633db77c75c63d50a340c0827d57",
|
||||
"covariance_input_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"covariance_snapshot_id": "covariance:synthetic-v1",
|
||||
"dataset_snapshot_id": "rhdsv1:sha256:f63a29b4795c63fb7d6b2d3b5544cee9274b633db77c75c63d50a340c0827d57",
|
||||
"decision_id": "rhportfoliodecisionv1:sha256:0e6e5ce2fa08de8006cc392610695a013327fc80645bfa4d7a908ad3f615bebd",
|
||||
"findings": [],
|
||||
"freshness_policy_digest": "sha256:833f58f4d1056f0450f7369fd4edd70534cc74e9e55cd60f05b2b3d7a2979763",
|
||||
"group_exposure": {
|
||||
"equity": 1.4549226783578566,
|
||||
"fixed_income": 1.4549226783578568
|
||||
},
|
||||
"manifest_id": "rhbacktestevidencev1:sha256:681c49cbdfb3e221b273e7bc616602ad80a7ab01206970807ad4294b176ebc75",
|
||||
"marginal_risk": {
|
||||
"A": 2.424871130596428,
|
||||
"B": 3.637306695894642
|
||||
},
|
||||
"percentage_risk": {
|
||||
"A": 0.49999999999999983,
|
||||
"B": 0.49999999999999994
|
||||
},
|
||||
"periods_per_year": 252,
|
||||
"portfolio_volatility": 2.909845356715714,
|
||||
"portfolio_volatility_limit": 10.0,
|
||||
"qualified": true,
|
||||
"return_frequency": "1d",
|
||||
"risk_budget": {
|
||||
"A": 0.8,
|
||||
"B": 0.8
|
||||
},
|
||||
"risk_model_digest": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"risk_model_name": "euler_volatility",
|
||||
"risk_model_version": "1.0.0",
|
||||
"run_id": "rhbacktestrunv1:sha256:5036c771c44a2adade9ea590eee0d8cf824ff8f519fadd8ba424e5d914856386",
|
||||
"scenario_digest": "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
"schema_version": "1.0.0",
|
||||
"status": "ready"
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ def test_module_spec_declares_pure_research_engine_boundary() -> None:
|
||||
prohibited = " ".join(spec["bounded_context"]["prohibited_responsibilities"]).lower()
|
||||
for term in ("investment advice", "live order", "credentials", "source facts"):
|
||||
assert term in prohibited
|
||||
assert spec["authority"]["revision"] == 3
|
||||
assert spec["authority"]["revision"] == 4
|
||||
assert {
|
||||
(item["contract_id"], item["version"])
|
||||
for item in spec["contracts"]["provides"]
|
||||
@@ -25,12 +25,16 @@ def test_module_spec_declares_pure_research_engine_boundary() -> None:
|
||||
("researchhub.factor-set-ref", "1.0.0"),
|
||||
("researchhub.backtest-run-ref", "1.0.0"),
|
||||
("researchhub.backtest-evidence-manifest", "1.0.0"),
|
||||
("researchhub.portfolio-decision", "1.0.0"),
|
||||
("researchhub.risk-assessment", "1.0.0"),
|
||||
}
|
||||
expected_paths = {
|
||||
"researchhub.factor-definition": "src/quant_engine/factor_contracts.py",
|
||||
"researchhub.factor-set-ref": "src/quant_engine/factor_contracts.py",
|
||||
"researchhub.backtest-run-ref": "src/quant_engine/governed_pipeline.py",
|
||||
"researchhub.backtest-evidence-manifest": "src/quant_engine/artifact.py",
|
||||
"researchhub.portfolio-decision": "src/quant_engine/portfolio_risk_contracts.py",
|
||||
"researchhub.risk-assessment": "src/quant_engine/portfolio_risk_contracts.py",
|
||||
}
|
||||
assert all(item["authority"] == "quant_engine" for item in spec["contracts"]["provides"])
|
||||
assert {
|
||||
@@ -48,6 +52,14 @@ def test_module_spec_declares_pure_research_engine_boundary() -> None:
|
||||
for item in spec["contracts"]["consumes"]
|
||||
)
|
||||
assert spec["dependencies"] == []
|
||||
capabilities = {item["id"]: item for item in spec["capabilities"]}
|
||||
portfolio_contract = capabilities["portfolio-risk-computation-contracts"]
|
||||
assert portfolio_contract["status"] == "operational"
|
||||
summary = portfolio_contract["summary"].lower()
|
||||
for term in ("receipt", "portfolio decisions", "risk assessments", "without"):
|
||||
assert term in summary
|
||||
for term in ("approval", "maker-checker", "publication", "paper", "live"):
|
||||
assert term in prohibited
|
||||
assert all(
|
||||
command["required"] and not command["network"]
|
||||
for command in spec["verification"]["commands"]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user