Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7103594481 | ||
|
|
e7a6e20826 | ||
|
|
a724e1e57a |
+3
-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": 4, "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": 5, "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",
|
||||
@@ -19,7 +19,7 @@
|
||||
{"id": "factor-and-indicator-calculation", "summary": "Calculate reusable alpha factors and technical indicators from caller-supplied data.", "status": "operational"},
|
||||
{"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": "backtest-evidence-contracts", "summary": "Identify governed offline backtest inputs and close existing research artifact and performance-methodology evidence without recomputation, 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"}
|
||||
],
|
||||
@@ -33,6 +33,7 @@
|
||||
{"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.performance-evidence", "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"}
|
||||
],
|
||||
|
||||
@@ -260,6 +260,35 @@ digest 等价,也不会把旧 run 静默升级为新合同。
|
||||
`LEGACY_EXPLORATORY`;不能隐式提升为 `CONTRACT_QUALIFIED`。所有资格均只描述离线证据闭合,
|
||||
不表示投资有效、组合获批、Paper、生产或实盘就绪。
|
||||
|
||||
## 绩效证据与方法论合同 v1
|
||||
|
||||
`quant_engine.artifact.PerformanceEvidenceV1` 在现有计算和事实表之外增加一层只读、内容寻址的
|
||||
owner 证据。`build_performance_evidence()` 只接受同一运行的完整 `ResearchRunArtifact`、
|
||||
`BacktestRunRef` 与 `CONTRACT_QUALIFIED BacktestEvidenceManifest`;它核对全部 artifact 表、
|
||||
performance 表和唯一行摘要,并绑定 artifact、row 与严格对齐 benchmark series 的独立摘要。
|
||||
生产 builder 不重算、填补、重命名或覆盖任何绩效值。
|
||||
|
||||
方法论固定为日简单收益、252 期年化、绝对指标年化无风险利率 `0.0`、benchmark 日无风险利率
|
||||
`0.0`,以及 benchmark 存在时的 `exact_session_index`。相对指标使用封闭 availability:无基准为
|
||||
`benchmark_absent`;active variance、benchmark variance 或 alpha 几何年化域不足时分别使用
|
||||
对应 `not_estimable_*` 原因。benchmark 存在时 tracking error 始终必须是有限非负值;null 不会
|
||||
被转成零。
|
||||
|
||||
```python
|
||||
from quant_engine.artifact import build_performance_evidence
|
||||
|
||||
performance_evidence = build_performance_evidence(
|
||||
artifact,
|
||||
backtest_run_ref,
|
||||
backtest_evidence_manifest,
|
||||
)
|
||||
canonical_bytes = performance_evidence.canonical_bytes()
|
||||
```
|
||||
|
||||
该合同范围固定为 `offline_research_only`。它不授予排名、推荐、决策、发布、论文、Paper、生产、
|
||||
实盘、交易或投资建议权限,也不包含原始参数、returns、NAV、benchmark series、表字节、存储
|
||||
locator、URI 或凭证。
|
||||
|
||||
## 组合决策与风险评估合同 v1
|
||||
|
||||
`quant_engine.portfolio_risk_contracts` 是现有计算 owner 外围的薄合同层。创建
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+871
@@ -0,0 +1,871 @@
|
||||
{
|
||||
"cases": {
|
||||
"absent": {
|
||||
"artifact_available_at": "2026-01-08T02:05:00Z",
|
||||
"authority": "quant_engine",
|
||||
"backtest_evidence_manifest_document_sha256": "sha256:b7e9139e021de3122bee9376a8c8387fca3db75fe2a698adccf33471caf971d2",
|
||||
"backtest_evidence_manifest_evidence_digest": "sha256:fae26a93d754e98f437bf9e4b635fc1cdc4f85d004a4bde396823b52e2115de5",
|
||||
"backtest_evidence_manifest_id": "rhbacktestevidencev1:sha256:3f67fcad23c75684ffeb325d8405b2f81139a732e237d30fbb30d23f91e32726",
|
||||
"backtest_evidence_qualification": "contract_qualified",
|
||||
"backtest_run_ref_document_sha256": "sha256:6a798adb3e0568aca84ed3e3a285b92d181ec569462fc003952a8c0d8382ae3a",
|
||||
"backtest_run_ref_id": "rhbacktestrunv1:sha256:5036c771c44a2adade9ea590eee0d8cf824ff8f519fadd8ba424e5d914856386",
|
||||
"benchmark_alignment_policy": "none",
|
||||
"benchmark_id": "",
|
||||
"benchmark_series_digest": null,
|
||||
"calendar": "CN-A",
|
||||
"code_revision": "dddddddddddddddddddddddddddddddddddddddd",
|
||||
"configuration_digest": "sha256:d28b49ea1bebc78d0023667f4fb9990b1fb45807176c2193b458525def056f4d",
|
||||
"cost_model_digest": "sha256:8888888888888888888888888888888888888888888888888888888888888888",
|
||||
"cost_model_version": "1.0.0",
|
||||
"dataset_content_digest": "sha256:44ea11ba64dc2e6fd55c6d8e038c5edc84ee38d6fd46e38b15a1d5409662a020",
|
||||
"dataset_manifest_digest": "sha256:d991bb2f8f6b80525f93c51e0b371213a3ed4649dffb073ed4605bfbd32349bd",
|
||||
"dataset_snapshot_id": "rhdsv1:sha256:f63a29b4795c63fb7d6b2d3b5544cee9274b633db77c75c63d50a340c0827d57",
|
||||
"document_sha256": "sha256:729852af307fd4a94ac566ae45df2e57b3d45c4fbdf45820351b5ef19cdf0ad3",
|
||||
"end_date": "2026-01-08",
|
||||
"environment_lock_digest": "sha256:9999999999999999999999999999999999999999999999999999999999999999",
|
||||
"execution_model_digest": "sha256:7777777777777777777777777777777777777777777777777777777777777777",
|
||||
"execution_model_version": "1.0.0",
|
||||
"factor_output_content_digest": "sha256:d78751460dc27fc796163c462d946924ce2bcb45dcfceb5e4b77f76093befc9e",
|
||||
"factor_set_digest": "sha256:e9339581cf569e92459f672e8081337712e7bf98e58ad42d60d7ed13f9b5a021",
|
||||
"factor_set_id": "rhfactorsetv1:sha256:e9339581cf569e92459f672e8081337712e7bf98e58ad42d60d7ed13f9b5a021",
|
||||
"foundation_digest": "sha256:d848237ab753ee9432ae78ec1f93b6ac45c8072d6694023b7f288203daf9d838",
|
||||
"foundation_id": "rhdfv1:sha256:d848237ab753ee9432ae78ec1f93b6ac45c8072d6694023b7f288203daf9d838",
|
||||
"frequency": "1d",
|
||||
"methodology": {
|
||||
"alpha": "daily_ols_intercept_geometric_annualization",
|
||||
"annual_risk_free": 0.0,
|
||||
"annualized_return": "geometric_compound",
|
||||
"annualized_volatility": "sample_std_sqrt_periods",
|
||||
"benchmark_alignment": "none",
|
||||
"benchmark_risk_free_daily": 0.0,
|
||||
"beta": "sample_covariance_over_sample_variance",
|
||||
"calmar_ratio": "unadjusted_annualized_return_over_absolute_maximum_drawdown",
|
||||
"code_revision": "dddddddddddddddddddddddddddddddddddddddd",
|
||||
"implementation_module": "quant_engine.metrics",
|
||||
"implementation_version": "researchhub.quant-performance-methodology.v1",
|
||||
"information_ratio": "mean_active_over_sample_std_active_sqrt_periods",
|
||||
"maximum_drawdown": "non_positive_peak_to_trough_ratio_with_initial_nav_one",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"periods_per_year": 252,
|
||||
"return_type": "simple",
|
||||
"sharpe_ratio": "annualized_return_minus_annual_risk_free_over_annualized_volatility",
|
||||
"sortino_ratio": "annualized_return_minus_annual_risk_free_over_root_mean_square_negative_returns_sqrt_periods",
|
||||
"source_frequency": "1d",
|
||||
"total_return": "final_nav_minus_one",
|
||||
"tracking_error": "sample_std_active_return_sqrt_periods",
|
||||
"win_rate": "positive_daily_return_count_over_observation_count"
|
||||
},
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"metrics": [
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "total_return",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "total_ret",
|
||||
"unit": "ratio",
|
||||
"value": 0.575
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "annualized_return",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "ann_ret",
|
||||
"unit": "ratio_per_year",
|
||||
"value": 2683336646708.1
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "annualized_volatility",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "ann_volatility",
|
||||
"unit": "ratio_per_year",
|
||||
"value": 1.38901943830891
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "sharpe_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "sharpe",
|
||||
"unit": "ratio",
|
||||
"value": 1931820803008.3313
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "sortino_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "sortino",
|
||||
"unit": "ratio",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "maximum_drawdown",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "max_dd",
|
||||
"unit": "ratio",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "calmar_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "calmar",
|
||||
"unit": "ratio",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "win_rate",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "win_rate",
|
||||
"unit": "ratio",
|
||||
"value": 0.75
|
||||
},
|
||||
{
|
||||
"availability": "benchmark_absent",
|
||||
"key": "tracking_error",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "tracking_error",
|
||||
"unit": "ratio_per_year",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"availability": "benchmark_absent",
|
||||
"key": "information_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "ir",
|
||||
"unit": "ratio",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"availability": "benchmark_absent",
|
||||
"key": "alpha",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "alpha",
|
||||
"unit": "ratio_per_year",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"availability": "benchmark_absent",
|
||||
"key": "beta",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "beta",
|
||||
"unit": "ratio",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "trade_count",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "n_trades",
|
||||
"unit": "count",
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "day_count",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "n_days",
|
||||
"unit": "count",
|
||||
"value": 4
|
||||
}
|
||||
],
|
||||
"performance_evidence_id": "rhperformanceevidencev1:sha256:d735abe76c28db429e107cb5ba6a1929c2c0f2084a6c4ac41b38ffbc02b2d2bd",
|
||||
"performance_row_digest": "sha256:4e6329b0db4731513fe793491a163dd358e3a6f7baa84d3c94a4d4d2355c7a7d",
|
||||
"performance_table_content_digest": "sha256:074b8511ff9a2154235e4dde8bac300658cb69f900e49f332d61560b82be9af5",
|
||||
"performance_table_logical_name": "performance",
|
||||
"performance_table_row_count": 1,
|
||||
"performance_table_schema_digest": "sha256:16cef93a679761103ae405e622b7929abbfe07115bf276be4f164da5a16128d0",
|
||||
"research_artifact_content_digest": "sha256:ad85ee1317bd8ce5fbef8fddc268b91be8678701ba5b8fbf6d61bf670f918bf9",
|
||||
"research_artifact_schema_version": "1.1.0",
|
||||
"run_id": "rhbacktestrunv1:sha256:5036c771c44a2adade9ea590eee0d8cf824ff8f519fadd8ba424e5d914856386",
|
||||
"schema_version": "researchhub.performance-evidence.v1",
|
||||
"scope": "offline_research_only",
|
||||
"start_date": "2026-01-05",
|
||||
"strategy_digest": "sha256:6666666666666666666666666666666666666666666666666666666666666666",
|
||||
"strategy_id": "alpha-top1",
|
||||
"strategy_version": "1.0.0",
|
||||
"timezone": "Asia/Shanghai"
|
||||
},
|
||||
"estimable": {
|
||||
"artifact_available_at": "2026-01-08T02:05:00Z",
|
||||
"authority": "quant_engine",
|
||||
"backtest_evidence_manifest_document_sha256": "sha256:e2e0e63fb4c733d2dba9a290511b6c8b0132bfe877dfe89ee7e614b74b87bc51",
|
||||
"backtest_evidence_manifest_evidence_digest": "sha256:f3913894d032c389c64eef59058b3cbc694cb9cc14ce9cee2699f0068200b650",
|
||||
"backtest_evidence_manifest_id": "rhbacktestevidencev1:sha256:f94733e849433f62f3da1e1ec8999891b93d49c7d657832d64e64bef9e211117",
|
||||
"backtest_evidence_qualification": "contract_qualified",
|
||||
"backtest_run_ref_document_sha256": "sha256:6a798adb3e0568aca84ed3e3a285b92d181ec569462fc003952a8c0d8382ae3a",
|
||||
"backtest_run_ref_id": "rhbacktestrunv1:sha256:5036c771c44a2adade9ea590eee0d8cf824ff8f519fadd8ba424e5d914856386",
|
||||
"benchmark_alignment_policy": "exact_session_index",
|
||||
"benchmark_id": "000300.SH",
|
||||
"benchmark_series_digest": "sha256:1c564e4d12af0d59b1fd707d7816da2895238969d9207470984db63cfeac423a",
|
||||
"calendar": "CN-A",
|
||||
"code_revision": "dddddddddddddddddddddddddddddddddddddddd",
|
||||
"configuration_digest": "sha256:d28b49ea1bebc78d0023667f4fb9990b1fb45807176c2193b458525def056f4d",
|
||||
"cost_model_digest": "sha256:8888888888888888888888888888888888888888888888888888888888888888",
|
||||
"cost_model_version": "1.0.0",
|
||||
"dataset_content_digest": "sha256:44ea11ba64dc2e6fd55c6d8e038c5edc84ee38d6fd46e38b15a1d5409662a020",
|
||||
"dataset_manifest_digest": "sha256:d991bb2f8f6b80525f93c51e0b371213a3ed4649dffb073ed4605bfbd32349bd",
|
||||
"dataset_snapshot_id": "rhdsv1:sha256:f63a29b4795c63fb7d6b2d3b5544cee9274b633db77c75c63d50a340c0827d57",
|
||||
"document_sha256": "sha256:a6a1a3313f511505c0cf57fddeebdf20a3b0f83a2958f31332d5597d172165e7",
|
||||
"end_date": "2026-01-08",
|
||||
"environment_lock_digest": "sha256:9999999999999999999999999999999999999999999999999999999999999999",
|
||||
"execution_model_digest": "sha256:7777777777777777777777777777777777777777777777777777777777777777",
|
||||
"execution_model_version": "1.0.0",
|
||||
"factor_output_content_digest": "sha256:d78751460dc27fc796163c462d946924ce2bcb45dcfceb5e4b77f76093befc9e",
|
||||
"factor_set_digest": "sha256:e9339581cf569e92459f672e8081337712e7bf98e58ad42d60d7ed13f9b5a021",
|
||||
"factor_set_id": "rhfactorsetv1:sha256:e9339581cf569e92459f672e8081337712e7bf98e58ad42d60d7ed13f9b5a021",
|
||||
"foundation_digest": "sha256:d848237ab753ee9432ae78ec1f93b6ac45c8072d6694023b7f288203daf9d838",
|
||||
"foundation_id": "rhdfv1:sha256:d848237ab753ee9432ae78ec1f93b6ac45c8072d6694023b7f288203daf9d838",
|
||||
"frequency": "1d",
|
||||
"methodology": {
|
||||
"alpha": "daily_ols_intercept_geometric_annualization",
|
||||
"annual_risk_free": 0.0,
|
||||
"annualized_return": "geometric_compound",
|
||||
"annualized_volatility": "sample_std_sqrt_periods",
|
||||
"benchmark_alignment": "exact_session_index",
|
||||
"benchmark_risk_free_daily": 0.0,
|
||||
"beta": "sample_covariance_over_sample_variance",
|
||||
"calmar_ratio": "unadjusted_annualized_return_over_absolute_maximum_drawdown",
|
||||
"code_revision": "dddddddddddddddddddddddddddddddddddddddd",
|
||||
"implementation_module": "quant_engine.metrics",
|
||||
"implementation_version": "researchhub.quant-performance-methodology.v1",
|
||||
"information_ratio": "mean_active_over_sample_std_active_sqrt_periods",
|
||||
"maximum_drawdown": "non_positive_peak_to_trough_ratio_with_initial_nav_one",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"periods_per_year": 252,
|
||||
"return_type": "simple",
|
||||
"sharpe_ratio": "annualized_return_minus_annual_risk_free_over_annualized_volatility",
|
||||
"sortino_ratio": "annualized_return_minus_annual_risk_free_over_root_mean_square_negative_returns_sqrt_periods",
|
||||
"source_frequency": "1d",
|
||||
"total_return": "final_nav_minus_one",
|
||||
"tracking_error": "sample_std_active_return_sqrt_periods",
|
||||
"win_rate": "positive_daily_return_count_over_observation_count"
|
||||
},
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"metrics": [
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "total_return",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "total_ret",
|
||||
"unit": "ratio",
|
||||
"value": 0.575
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "annualized_return",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "ann_ret",
|
||||
"unit": "ratio_per_year",
|
||||
"value": 2683336646708.1
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "annualized_volatility",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "ann_volatility",
|
||||
"unit": "ratio_per_year",
|
||||
"value": 1.38901943830891
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "sharpe_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "sharpe",
|
||||
"unit": "ratio",
|
||||
"value": 1931820803008.3313
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "sortino_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "sortino",
|
||||
"unit": "ratio",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "maximum_drawdown",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "max_dd",
|
||||
"unit": "ratio",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "calmar_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "calmar",
|
||||
"unit": "ratio",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "win_rate",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "win_rate",
|
||||
"unit": "ratio",
|
||||
"value": 0.75
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "tracking_error",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "tracking_error",
|
||||
"unit": "ratio_per_year",
|
||||
"value": 1.3032171729991897
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "information_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "ir",
|
||||
"unit": "ratio",
|
||||
"value": 22.801264912443322
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "alpha",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "alpha",
|
||||
"unit": "ratio_per_year",
|
||||
"value": 123663320625.66454
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "beta",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "beta",
|
||||
"unit": "ratio",
|
||||
"value": 3.2500000000000013
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "trade_count",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "n_trades",
|
||||
"unit": "count",
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "day_count",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "n_days",
|
||||
"unit": "count",
|
||||
"value": 4
|
||||
}
|
||||
],
|
||||
"performance_evidence_id": "rhperformanceevidencev1:sha256:f1f72c34d7427bd0d2e273a66b9677d112aa51c2417ea1cda8245e51e334e4f6",
|
||||
"performance_row_digest": "sha256:bad2b506679a6ca13d80ab00859a442ba692f3872d42b0b45bbc3b1d6ffe72a4",
|
||||
"performance_table_content_digest": "sha256:0856439ea7ec84e38887ccfa0067324f2e9cd293543e4ef683b9c2beb9eb34cf",
|
||||
"performance_table_logical_name": "performance",
|
||||
"performance_table_row_count": 1,
|
||||
"performance_table_schema_digest": "sha256:16cef93a679761103ae405e622b7929abbfe07115bf276be4f164da5a16128d0",
|
||||
"research_artifact_content_digest": "sha256:6ec566d73da3966d3d5f946313e2b0181993374c9f4ceb4b56e165cc6793d382",
|
||||
"research_artifact_schema_version": "1.1.0",
|
||||
"run_id": "rhbacktestrunv1:sha256:5036c771c44a2adade9ea590eee0d8cf824ff8f519fadd8ba424e5d914856386",
|
||||
"schema_version": "researchhub.performance-evidence.v1",
|
||||
"scope": "offline_research_only",
|
||||
"start_date": "2026-01-05",
|
||||
"strategy_digest": "sha256:6666666666666666666666666666666666666666666666666666666666666666",
|
||||
"strategy_id": "alpha-top1",
|
||||
"strategy_version": "1.0.0",
|
||||
"timezone": "Asia/Shanghai"
|
||||
},
|
||||
"zero_active_variance": {
|
||||
"artifact_available_at": "2026-01-08T02:05:00Z",
|
||||
"authority": "quant_engine",
|
||||
"backtest_evidence_manifest_document_sha256": "sha256:8a13212575154d97f85011ed54ed3bb588f73a691926b3ca03b360d563e9fc29",
|
||||
"backtest_evidence_manifest_evidence_digest": "sha256:8c0c2052ab93aa920254bc7f4d89435d839b7150ecaaaa798703e89ddf876ec1",
|
||||
"backtest_evidence_manifest_id": "rhbacktestevidencev1:sha256:699ad35ee30a294082c607c40617097aa90d5d58343e7d8c8185d8a53f52596c",
|
||||
"backtest_evidence_qualification": "contract_qualified",
|
||||
"backtest_run_ref_document_sha256": "sha256:6a798adb3e0568aca84ed3e3a285b92d181ec569462fc003952a8c0d8382ae3a",
|
||||
"backtest_run_ref_id": "rhbacktestrunv1:sha256:5036c771c44a2adade9ea590eee0d8cf824ff8f519fadd8ba424e5d914856386",
|
||||
"benchmark_alignment_policy": "exact_session_index",
|
||||
"benchmark_id": "000300.SH",
|
||||
"benchmark_series_digest": "sha256:7668b77ac689cd542005fae42cb2abd48fdf772d2f982086dca409606653e6ac",
|
||||
"calendar": "CN-A",
|
||||
"code_revision": "dddddddddddddddddddddddddddddddddddddddd",
|
||||
"configuration_digest": "sha256:d28b49ea1bebc78d0023667f4fb9990b1fb45807176c2193b458525def056f4d",
|
||||
"cost_model_digest": "sha256:8888888888888888888888888888888888888888888888888888888888888888",
|
||||
"cost_model_version": "1.0.0",
|
||||
"dataset_content_digest": "sha256:44ea11ba64dc2e6fd55c6d8e038c5edc84ee38d6fd46e38b15a1d5409662a020",
|
||||
"dataset_manifest_digest": "sha256:d991bb2f8f6b80525f93c51e0b371213a3ed4649dffb073ed4605bfbd32349bd",
|
||||
"dataset_snapshot_id": "rhdsv1:sha256:f63a29b4795c63fb7d6b2d3b5544cee9274b633db77c75c63d50a340c0827d57",
|
||||
"document_sha256": "sha256:44a04be0105f29e1cbf8d2430b6b0eb084853d428bc2ccd5834dfdbcf9482bd7",
|
||||
"end_date": "2026-01-08",
|
||||
"environment_lock_digest": "sha256:9999999999999999999999999999999999999999999999999999999999999999",
|
||||
"execution_model_digest": "sha256:7777777777777777777777777777777777777777777777777777777777777777",
|
||||
"execution_model_version": "1.0.0",
|
||||
"factor_output_content_digest": "sha256:d78751460dc27fc796163c462d946924ce2bcb45dcfceb5e4b77f76093befc9e",
|
||||
"factor_set_digest": "sha256:e9339581cf569e92459f672e8081337712e7bf98e58ad42d60d7ed13f9b5a021",
|
||||
"factor_set_id": "rhfactorsetv1:sha256:e9339581cf569e92459f672e8081337712e7bf98e58ad42d60d7ed13f9b5a021",
|
||||
"foundation_digest": "sha256:d848237ab753ee9432ae78ec1f93b6ac45c8072d6694023b7f288203daf9d838",
|
||||
"foundation_id": "rhdfv1:sha256:d848237ab753ee9432ae78ec1f93b6ac45c8072d6694023b7f288203daf9d838",
|
||||
"frequency": "1d",
|
||||
"methodology": {
|
||||
"alpha": "daily_ols_intercept_geometric_annualization",
|
||||
"annual_risk_free": 0.0,
|
||||
"annualized_return": "geometric_compound",
|
||||
"annualized_volatility": "sample_std_sqrt_periods",
|
||||
"benchmark_alignment": "exact_session_index",
|
||||
"benchmark_risk_free_daily": 0.0,
|
||||
"beta": "sample_covariance_over_sample_variance",
|
||||
"calmar_ratio": "unadjusted_annualized_return_over_absolute_maximum_drawdown",
|
||||
"code_revision": "dddddddddddddddddddddddddddddddddddddddd",
|
||||
"implementation_module": "quant_engine.metrics",
|
||||
"implementation_version": "researchhub.quant-performance-methodology.v1",
|
||||
"information_ratio": "mean_active_over_sample_std_active_sqrt_periods",
|
||||
"maximum_drawdown": "non_positive_peak_to_trough_ratio_with_initial_nav_one",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"periods_per_year": 252,
|
||||
"return_type": "simple",
|
||||
"sharpe_ratio": "annualized_return_minus_annual_risk_free_over_annualized_volatility",
|
||||
"sortino_ratio": "annualized_return_minus_annual_risk_free_over_root_mean_square_negative_returns_sqrt_periods",
|
||||
"source_frequency": "1d",
|
||||
"total_return": "final_nav_minus_one",
|
||||
"tracking_error": "sample_std_active_return_sqrt_periods",
|
||||
"win_rate": "positive_daily_return_count_over_observation_count"
|
||||
},
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"metrics": [
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "total_return",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "total_ret",
|
||||
"unit": "ratio",
|
||||
"value": 0.575
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "annualized_return",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "ann_ret",
|
||||
"unit": "ratio_per_year",
|
||||
"value": 2683336646708.1
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "annualized_volatility",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "ann_volatility",
|
||||
"unit": "ratio_per_year",
|
||||
"value": 1.38901943830891
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "sharpe_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "sharpe",
|
||||
"unit": "ratio",
|
||||
"value": 1931820803008.3313
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "sortino_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "sortino",
|
||||
"unit": "ratio",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "maximum_drawdown",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "max_dd",
|
||||
"unit": "ratio",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "calmar_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "calmar",
|
||||
"unit": "ratio",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "win_rate",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "win_rate",
|
||||
"unit": "ratio",
|
||||
"value": 0.75
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "tracking_error",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "tracking_error",
|
||||
"unit": "ratio_per_year",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"availability": "not_estimable_active_variance",
|
||||
"key": "information_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "ir",
|
||||
"unit": "ratio",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "alpha",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "alpha",
|
||||
"unit": "ratio_per_year",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "beta",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "beta",
|
||||
"unit": "ratio",
|
||||
"value": 1.0000000000000002
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "trade_count",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "n_trades",
|
||||
"unit": "count",
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "day_count",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "n_days",
|
||||
"unit": "count",
|
||||
"value": 4
|
||||
}
|
||||
],
|
||||
"performance_evidence_id": "rhperformanceevidencev1:sha256:e6730d25d90c85a6370adb72bc45c1bf505235f77b72521dd8197898ca369fe5",
|
||||
"performance_row_digest": "sha256:6528c47fa9416e350aa5010477dbd8c83a35cecf87aa5bc413cec95a9765114d",
|
||||
"performance_table_content_digest": "sha256:7d57a966431c68093a9f6193ae62f79a63d33832f11dd68524ed9bcd2cb6257c",
|
||||
"performance_table_logical_name": "performance",
|
||||
"performance_table_row_count": 1,
|
||||
"performance_table_schema_digest": "sha256:16cef93a679761103ae405e622b7929abbfe07115bf276be4f164da5a16128d0",
|
||||
"research_artifact_content_digest": "sha256:b17ab9e158a7ceeb5107f6fe8a61f332009c314186f3436d4750aa44d6ca3856",
|
||||
"research_artifact_schema_version": "1.1.0",
|
||||
"run_id": "rhbacktestrunv1:sha256:5036c771c44a2adade9ea590eee0d8cf824ff8f519fadd8ba424e5d914856386",
|
||||
"schema_version": "researchhub.performance-evidence.v1",
|
||||
"scope": "offline_research_only",
|
||||
"start_date": "2026-01-05",
|
||||
"strategy_digest": "sha256:6666666666666666666666666666666666666666666666666666666666666666",
|
||||
"strategy_id": "alpha-top1",
|
||||
"strategy_version": "1.0.0",
|
||||
"timezone": "Asia/Shanghai"
|
||||
},
|
||||
"zero_benchmark_variance": {
|
||||
"artifact_available_at": "2026-01-08T02:05:00Z",
|
||||
"authority": "quant_engine",
|
||||
"backtest_evidence_manifest_document_sha256": "sha256:2389b804f5d8d20a37b31f596b52892484777c5da799dd68865a082ee90e6e5b",
|
||||
"backtest_evidence_manifest_evidence_digest": "sha256:54bb315bc1940ef6df79999cf03ebb8d10defaf526bf7802d63da33f612520ec",
|
||||
"backtest_evidence_manifest_id": "rhbacktestevidencev1:sha256:ad9559e1f8feca28bb310765216a975935f05f51057139c2d02fd2fe7dfe501e",
|
||||
"backtest_evidence_qualification": "contract_qualified",
|
||||
"backtest_run_ref_document_sha256": "sha256:6a798adb3e0568aca84ed3e3a285b92d181ec569462fc003952a8c0d8382ae3a",
|
||||
"backtest_run_ref_id": "rhbacktestrunv1:sha256:5036c771c44a2adade9ea590eee0d8cf824ff8f519fadd8ba424e5d914856386",
|
||||
"benchmark_alignment_policy": "exact_session_index",
|
||||
"benchmark_id": "000300.SH",
|
||||
"benchmark_series_digest": "sha256:d0f1e954d796e95254bcccc893d6e3a8f9d541c3028b88b2c0b24c1658bb2408",
|
||||
"calendar": "CN-A",
|
||||
"code_revision": "dddddddddddddddddddddddddddddddddddddddd",
|
||||
"configuration_digest": "sha256:d28b49ea1bebc78d0023667f4fb9990b1fb45807176c2193b458525def056f4d",
|
||||
"cost_model_digest": "sha256:8888888888888888888888888888888888888888888888888888888888888888",
|
||||
"cost_model_version": "1.0.0",
|
||||
"dataset_content_digest": "sha256:44ea11ba64dc2e6fd55c6d8e038c5edc84ee38d6fd46e38b15a1d5409662a020",
|
||||
"dataset_manifest_digest": "sha256:d991bb2f8f6b80525f93c51e0b371213a3ed4649dffb073ed4605bfbd32349bd",
|
||||
"dataset_snapshot_id": "rhdsv1:sha256:f63a29b4795c63fb7d6b2d3b5544cee9274b633db77c75c63d50a340c0827d57",
|
||||
"document_sha256": "sha256:077f3ec84802aff2f046365a3a5f61f475b1ad63ec05e96742844bc531bcb551",
|
||||
"end_date": "2026-01-08",
|
||||
"environment_lock_digest": "sha256:9999999999999999999999999999999999999999999999999999999999999999",
|
||||
"execution_model_digest": "sha256:7777777777777777777777777777777777777777777777777777777777777777",
|
||||
"execution_model_version": "1.0.0",
|
||||
"factor_output_content_digest": "sha256:d78751460dc27fc796163c462d946924ce2bcb45dcfceb5e4b77f76093befc9e",
|
||||
"factor_set_digest": "sha256:e9339581cf569e92459f672e8081337712e7bf98e58ad42d60d7ed13f9b5a021",
|
||||
"factor_set_id": "rhfactorsetv1:sha256:e9339581cf569e92459f672e8081337712e7bf98e58ad42d60d7ed13f9b5a021",
|
||||
"foundation_digest": "sha256:d848237ab753ee9432ae78ec1f93b6ac45c8072d6694023b7f288203daf9d838",
|
||||
"foundation_id": "rhdfv1:sha256:d848237ab753ee9432ae78ec1f93b6ac45c8072d6694023b7f288203daf9d838",
|
||||
"frequency": "1d",
|
||||
"methodology": {
|
||||
"alpha": "daily_ols_intercept_geometric_annualization",
|
||||
"annual_risk_free": 0.0,
|
||||
"annualized_return": "geometric_compound",
|
||||
"annualized_volatility": "sample_std_sqrt_periods",
|
||||
"benchmark_alignment": "exact_session_index",
|
||||
"benchmark_risk_free_daily": 0.0,
|
||||
"beta": "sample_covariance_over_sample_variance",
|
||||
"calmar_ratio": "unadjusted_annualized_return_over_absolute_maximum_drawdown",
|
||||
"code_revision": "dddddddddddddddddddddddddddddddddddddddd",
|
||||
"implementation_module": "quant_engine.metrics",
|
||||
"implementation_version": "researchhub.quant-performance-methodology.v1",
|
||||
"information_ratio": "mean_active_over_sample_std_active_sqrt_periods",
|
||||
"maximum_drawdown": "non_positive_peak_to_trough_ratio_with_initial_nav_one",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"periods_per_year": 252,
|
||||
"return_type": "simple",
|
||||
"sharpe_ratio": "annualized_return_minus_annual_risk_free_over_annualized_volatility",
|
||||
"sortino_ratio": "annualized_return_minus_annual_risk_free_over_root_mean_square_negative_returns_sqrt_periods",
|
||||
"source_frequency": "1d",
|
||||
"total_return": "final_nav_minus_one",
|
||||
"tracking_error": "sample_std_active_return_sqrt_periods",
|
||||
"win_rate": "positive_daily_return_count_over_observation_count"
|
||||
},
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"metrics": [
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "total_return",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "total_ret",
|
||||
"unit": "ratio",
|
||||
"value": 0.575
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "annualized_return",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "ann_ret",
|
||||
"unit": "ratio_per_year",
|
||||
"value": 2683336646708.1
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "annualized_volatility",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "ann_volatility",
|
||||
"unit": "ratio_per_year",
|
||||
"value": 1.38901943830891
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "sharpe_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "sharpe",
|
||||
"unit": "ratio",
|
||||
"value": 1931820803008.3313
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "sortino_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "sortino",
|
||||
"unit": "ratio",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "maximum_drawdown",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "max_dd",
|
||||
"unit": "ratio",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "calmar_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "calmar",
|
||||
"unit": "ratio",
|
||||
"value": 0.0
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "win_rate",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "win_rate",
|
||||
"unit": "ratio",
|
||||
"value": 0.75
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "tracking_error",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "tracking_error",
|
||||
"unit": "ratio_per_year",
|
||||
"value": 1.38901943830891
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "information_ratio",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "ir",
|
||||
"unit": "ratio",
|
||||
"value": 22.299903907544408
|
||||
},
|
||||
{
|
||||
"availability": "not_estimable_benchmark_variance",
|
||||
"key": "alpha",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "alpha",
|
||||
"unit": "ratio_per_year",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"availability": "not_estimable_benchmark_variance",
|
||||
"key": "beta",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": true,
|
||||
"source_column": "beta",
|
||||
"unit": "ratio",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "trade_count",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "n_trades",
|
||||
"unit": "count",
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"availability": "available",
|
||||
"key": "day_count",
|
||||
"methodology_id": "researchhub.quant-performance-methodology.v1",
|
||||
"metric_schema_id": "researchhub.quant-performance-metrics.v1",
|
||||
"nullable": false,
|
||||
"source_column": "n_days",
|
||||
"unit": "count",
|
||||
"value": 4
|
||||
}
|
||||
],
|
||||
"performance_evidence_id": "rhperformanceevidencev1:sha256:86133a6b3c3091477cdc4618ebc294e671c9ba0a3ef5015a068f92bb575729b9",
|
||||
"performance_row_digest": "sha256:90c9aa2f4168c15ff9ac5d4da2a35c3e915bb4e04736e5a440ea80c787a99553",
|
||||
"performance_table_content_digest": "sha256:1570b64d83ad2a849607e6f5203f5ec2f0291a8d6cbb00b8edfe0ae030b7967d",
|
||||
"performance_table_logical_name": "performance",
|
||||
"performance_table_row_count": 1,
|
||||
"performance_table_schema_digest": "sha256:16cef93a679761103ae405e622b7929abbfe07115bf276be4f164da5a16128d0",
|
||||
"research_artifact_content_digest": "sha256:fed7a28888a6e98ccce78e7d84f22d4e3636e928485861af7a97f90eddc91f94",
|
||||
"research_artifact_schema_version": "1.1.0",
|
||||
"run_id": "rhbacktestrunv1:sha256:5036c771c44a2adade9ea590eee0d8cf824ff8f519fadd8ba424e5d914856386",
|
||||
"schema_version": "researchhub.performance-evidence.v1",
|
||||
"scope": "offline_research_only",
|
||||
"start_date": "2026-01-05",
|
||||
"strategy_digest": "sha256:6666666666666666666666666666666666666666666666666666666666666666",
|
||||
"strategy_id": "alpha-top1",
|
||||
"strategy_version": "1.0.0",
|
||||
"timezone": "Asia/Shanghai"
|
||||
}
|
||||
},
|
||||
"schema_version": 1,
|
||||
"source_commit": "a724e1e57a99d1304a932d01ee836bac56c5c15c",
|
||||
"source_tree": "4774e88442d25bf79a54eab3d7106ff4d0ba9603"
|
||||
}
|
||||
@@ -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"] == 4
|
||||
assert spec["authority"]["revision"] == 5
|
||||
assert {
|
||||
(item["contract_id"], item["version"])
|
||||
for item in spec["contracts"]["provides"]
|
||||
@@ -25,6 +25,7 @@ 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.performance-evidence", "1.0.0"),
|
||||
("researchhub.portfolio-decision", "1.0.0"),
|
||||
("researchhub.risk-assessment", "1.0.0"),
|
||||
}
|
||||
@@ -33,6 +34,7 @@ def test_module_spec_declares_pure_research_engine_boundary() -> None:
|
||||
"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.performance-evidence": "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",
|
||||
}
|
||||
@@ -53,6 +55,11 @@ def test_module_spec_declares_pure_research_engine_boundary() -> None:
|
||||
)
|
||||
assert spec["dependencies"] == []
|
||||
capabilities = {item["id"]: item for item in spec["capabilities"]}
|
||||
evidence_contract = capabilities["backtest-evidence-contracts"]
|
||||
assert evidence_contract["status"] == "operational"
|
||||
evidence_summary = evidence_contract["summary"].lower()
|
||||
for term in ("performance-methodology", "without recomputation", "decision authority"):
|
||||
assert term in evidence_summary
|
||||
portfolio_contract = capabilities["portfolio-risk-computation-contracts"]
|
||||
assert portfolio_contract["status"] == "operational"
|
||||
summary = portfolio_contract["summary"].lower()
|
||||
|
||||
@@ -0,0 +1,727 @@
|
||||
"""Closed performance-evidence contract conformance tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import hashlib
|
||||
import json
|
||||
from dataclasses import replace
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pytest
|
||||
|
||||
from quant_engine.artifact import (
|
||||
PERFORMANCE_EVIDENCE_SCHEMA_VERSION,
|
||||
PERFORMANCE_METRIC_SCHEMA_ID,
|
||||
PERFORMANCE_METHODOLOGY_ID,
|
||||
BacktestEvidenceManifest,
|
||||
EvidenceQualification,
|
||||
PerformanceEvidenceError,
|
||||
PerformanceEvidenceErrorCode,
|
||||
PerformanceEvidenceV1,
|
||||
PerformanceMetricAvailability,
|
||||
ResearchRunArtifact,
|
||||
build_backtest_evidence_manifest,
|
||||
build_performance_evidence,
|
||||
build_research_run_artifact,
|
||||
)
|
||||
from quant_engine.execution import ExecutionConfig
|
||||
from quant_engine.factor_contracts import (
|
||||
ActorIdentity,
|
||||
AvailabilityMode,
|
||||
Causation,
|
||||
DataFoundationEnvelope,
|
||||
DatasetSnapshotEnvelope,
|
||||
FactorInput,
|
||||
FactorSetRef,
|
||||
InputBinding,
|
||||
OutputArtifactRef,
|
||||
OutputCoverage,
|
||||
OutputQuality,
|
||||
OutputQualityCheck,
|
||||
ProducerIdentity,
|
||||
ViewAvailability,
|
||||
canonical_json_bytes,
|
||||
factor_definition_from_alpha158,
|
||||
factor_input_schema_digest,
|
||||
)
|
||||
from quant_engine.governed_pipeline import BacktestRunRef
|
||||
from quant_engine.metrics import TRADING_DAYS_PER_YEAR, benchmark_summary, summary
|
||||
from quant_engine.research_pipeline import FactorBacktestResult, run_factor_backtest_research
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
FACTOR_FIXTURE = ROOT / "tests" / "fixtures" / "factor-contracts-v1.golden.json"
|
||||
PERFORMANCE_FIXTURE = (
|
||||
ROOT / "tests" / "fixtures" / "performance-evidence-v1.golden.json"
|
||||
)
|
||||
VIEW_REF_ID = "rhviewrefv1:sha256:bf776bcd26d940fafde1d650776a5505fb3fe8b5b068c351622bf2c42385629c"
|
||||
VIEW_SCHEMA_DIGEST = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
CALENDAR_REVISION_ID = "rhcalv1:sha256:1f4ca22557063389badd669cf774bb35234066e847646682dccfe411e252a078"
|
||||
ACTION_REVISION_ID = "rhcav1:sha256:0f947df29f152bfa2c6ab0da7a0d464670c7ad2526cd10f2a7939b42fee5c275"
|
||||
PARAMETERS = {"lag_sessions": 1, "top_k": 1}
|
||||
|
||||
|
||||
def _sha256(value: bytes) -> str:
|
||||
return f"sha256:{hashlib.sha256(value).hexdigest()}"
|
||||
|
||||
|
||||
def _accepted_authorities() -> tuple[
|
||||
DatasetSnapshotEnvelope,
|
||||
DataFoundationEnvelope,
|
||||
FactorSetRef,
|
||||
]:
|
||||
fixture = json.loads(FACTOR_FIXTURE.read_text(encoding="utf-8"))
|
||||
snapshot = DatasetSnapshotEnvelope.from_dict(fixture["dataset_snapshot"])
|
||||
foundation = DataFoundationEnvelope.from_dict(fixture["data_foundation"])
|
||||
factor_input = FactorInput("market", VIEW_SCHEMA_DIGEST, ("close", "volume"))
|
||||
definition = factor_definition_from_alpha158(
|
||||
"alpha_005",
|
||||
version="1.0.0",
|
||||
parameters={},
|
||||
inputs=(factor_input,),
|
||||
implementation_digest="sha256:" + "1" * 64,
|
||||
input_schema_digest=factor_input_schema_digest((factor_input,)),
|
||||
valid_from="2026-01-01T00:00:00.000000Z",
|
||||
valid_until="2027-01-01T00:00:00Z",
|
||||
warmup_sessions=10,
|
||||
lag_sessions=1,
|
||||
producer=ProducerIdentity("quant_engine", "1.0.0"),
|
||||
code_revision="c" * 40,
|
||||
)
|
||||
output_schema_bytes = canonical_json_bytes(fixture["output_schema"])
|
||||
output_content_bytes = canonical_json_bytes(fixture["output_content"])
|
||||
artifact_ref = OutputArtifactRef.create(
|
||||
schema_digest=_sha256(output_schema_bytes),
|
||||
content_digest=_sha256(output_content_bytes),
|
||||
)
|
||||
factor_set = FactorSetRef.create(
|
||||
definitions=(definition,),
|
||||
dataset_snapshot=snapshot,
|
||||
foundation=foundation,
|
||||
selected_view_ref_ids=(VIEW_REF_ID,),
|
||||
input_bindings=(
|
||||
InputBinding(
|
||||
definition.definition_id,
|
||||
"market",
|
||||
VIEW_REF_ID,
|
||||
VIEW_SCHEMA_DIGEST,
|
||||
),
|
||||
),
|
||||
view_availability=(
|
||||
ViewAvailability(VIEW_REF_ID, "2026-01-02T23:50:00Z", "sha256:" + "2" * 64),
|
||||
),
|
||||
output_quality=OutputQuality(
|
||||
"passed",
|
||||
(OutputQualityCheck("finite_values", "passed", "sha256:" + "3" * 64),),
|
||||
),
|
||||
output_coverage=OutputCoverage(
|
||||
"complete",
|
||||
1,
|
||||
1,
|
||||
"row",
|
||||
"alpha_005.cn_a",
|
||||
"sha256:" + "4" * 64,
|
||||
),
|
||||
output_schema_bytes=output_schema_bytes,
|
||||
output_content_bytes=output_content_bytes,
|
||||
output_artifact_ref=artifact_ref,
|
||||
availability_mode=AvailabilityMode.AS_AVAILABLE,
|
||||
evaluation_at="2026-01-03T11:00:00Z",
|
||||
computed_at="2026-01-03T10:15:00Z",
|
||||
artifact_available_at="2026-01-03T10:20:00Z",
|
||||
producer=ProducerIdentity("quant_engine", "1.0.0"),
|
||||
code_revision="c" * 40,
|
||||
actor=ActorIdentity("service", "factor_worker_v1"),
|
||||
correlation_id="research_run_001",
|
||||
causation=Causation("foundation", foundation.foundation_id),
|
||||
evidence_scope="synthetic_fixture",
|
||||
decision_eligible=False,
|
||||
)
|
||||
return snapshot, foundation, factor_set
|
||||
|
||||
|
||||
def _configuration_digest() -> str:
|
||||
return _sha256(
|
||||
json.dumps(
|
||||
PARAMETERS,
|
||||
ensure_ascii=False,
|
||||
sort_keys=True,
|
||||
separators=(",", ":"),
|
||||
allow_nan=False,
|
||||
).encode("utf-8")
|
||||
)
|
||||
|
||||
|
||||
def _run_ref(**overrides: Any) -> BacktestRunRef:
|
||||
snapshot, foundation, factor_set = _accepted_authorities()
|
||||
arguments: dict[str, Any] = {
|
||||
"dataset_snapshot": snapshot,
|
||||
"foundation": foundation,
|
||||
"factor_set": factor_set,
|
||||
"universe_digest": "sha256:" + "5" * 64,
|
||||
"trading_calendar_revision_ids": (CALENDAR_REVISION_ID,),
|
||||
"corporate_action_revision_ids": (ACTION_REVISION_ID,),
|
||||
"strategy_id": "alpha-top1",
|
||||
"strategy_version": "1.0.0",
|
||||
"strategy_digest": "sha256:" + "6" * 64,
|
||||
"execution_model_version": "1.0.0",
|
||||
"execution_model_digest": "sha256:" + "7" * 64,
|
||||
"cost_model_version": "1.0.0",
|
||||
"cost_model_digest": "sha256:" + "8" * 64,
|
||||
"random_seed": 7,
|
||||
"code_revision": "d" * 40,
|
||||
"environment_lock_digest": "sha256:" + "9" * 64,
|
||||
"configuration_digest": _configuration_digest(),
|
||||
"evaluation_at": "2026-01-08T01:00:00Z",
|
||||
"computed_at": "2026-01-08T02:00:00Z",
|
||||
}
|
||||
arguments.update(overrides)
|
||||
return BacktestRunRef.create(**arguments)
|
||||
|
||||
|
||||
def _backtest_result() -> FactorBacktestResult:
|
||||
dates = pd.date_range("2026-01-05", periods=4, freq="B")
|
||||
scores = pd.DataFrame({"A": [2.0, 0.0], "B": [1.0, 3.0]}, index=dates[:2])
|
||||
opens = pd.DataFrame(
|
||||
{"A": [10.0, 10.0, 15.0, 15.0], "B": [20.0, 20.0, 20.0, 21.0]},
|
||||
index=dates,
|
||||
)
|
||||
closes = pd.DataFrame(
|
||||
{"A": [10.0, 12.0, 15.0, 15.0], "B": [20.0, 20.0, 18.0, 21.0]},
|
||||
index=dates,
|
||||
)
|
||||
return run_factor_backtest_research(
|
||||
scores,
|
||||
opens,
|
||||
closes,
|
||||
top_k=1,
|
||||
execution_price_field="open",
|
||||
valuation_price_field="close",
|
||||
initial_cash=1_000.0,
|
||||
config=ExecutionConfig(
|
||||
commission_bps=0,
|
||||
stamp_tax_bps=0,
|
||||
slippage_bps=0,
|
||||
min_trade_amount=0,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _artifact(
|
||||
run_ref: BacktestRunRef,
|
||||
benchmark_kind: str,
|
||||
) -> tuple[ResearchRunArtifact, FactorBacktestResult]:
|
||||
result = _backtest_result()
|
||||
benchmark_id: str | None
|
||||
benchmark_returns: pd.Series | None
|
||||
if benchmark_kind == "absent":
|
||||
benchmark_id = None
|
||||
benchmark_returns = None
|
||||
elif benchmark_kind == "estimable":
|
||||
benchmark_id = "000300.SH"
|
||||
benchmark_returns = pd.Series(
|
||||
[0.0, 0.01, -0.01, 0.02],
|
||||
index=result.returns.index,
|
||||
name="benchmark_return",
|
||||
)
|
||||
elif benchmark_kind == "zero_active_variance":
|
||||
benchmark_id = "000300.SH"
|
||||
benchmark_returns = result.returns.rename("benchmark_return")
|
||||
elif benchmark_kind == "zero_benchmark_variance":
|
||||
benchmark_id = "000300.SH"
|
||||
benchmark_returns = pd.Series(
|
||||
np.zeros(len(result.returns)),
|
||||
index=result.returns.index,
|
||||
name="benchmark_return",
|
||||
)
|
||||
else:
|
||||
raise AssertionError(f"unknown benchmark_kind: {benchmark_kind}")
|
||||
artifact = build_research_run_artifact(
|
||||
result,
|
||||
run_id=run_ref.run_id,
|
||||
strategy_id=run_ref.strategy_id,
|
||||
strategy_name="Alpha Top 1",
|
||||
strategy_version=run_ref.strategy_version,
|
||||
engine_version="1.2.0",
|
||||
code_revision=run_ref.code_revision,
|
||||
data_snapshot_id=run_ref.dataset_snapshot_id,
|
||||
calendar="CN-A",
|
||||
timezone="Asia/Shanghai",
|
||||
started_at="2026-01-08T10:00:00+08:00",
|
||||
finished_at="2026-01-08T10:01:00+08:00",
|
||||
parameters=PARAMETERS,
|
||||
benchmark_id=benchmark_id,
|
||||
benchmark_returns=benchmark_returns,
|
||||
)
|
||||
return artifact, result
|
||||
|
||||
|
||||
def _case(
|
||||
benchmark_kind: str,
|
||||
) -> tuple[PerformanceEvidenceV1, ResearchRunArtifact, BacktestRunRef, BacktestEvidenceManifest]:
|
||||
run_ref = _run_ref()
|
||||
artifact, _ = _artifact(run_ref, benchmark_kind)
|
||||
manifest = build_backtest_evidence_manifest(
|
||||
run_ref,
|
||||
artifact,
|
||||
artifact_available_at="2026-01-08T02:05:00Z",
|
||||
qualification=EvidenceQualification.CONTRACT_QUALIFIED,
|
||||
)
|
||||
return (
|
||||
build_performance_evidence(artifact, run_ref, manifest),
|
||||
artifact,
|
||||
run_ref,
|
||||
manifest,
|
||||
)
|
||||
|
||||
|
||||
def _metric_map(evidence: PerformanceEvidenceV1) -> dict[str, Any]:
|
||||
return {metric.key: metric for metric in evidence.metrics}
|
||||
|
||||
|
||||
def _mutate_frozen(value: Any, field: str, replacement: object) -> Any:
|
||||
changed = copy.copy(value)
|
||||
object.__setattr__(changed, field, replacement)
|
||||
return changed
|
||||
|
||||
|
||||
def _assert_error(
|
||||
error: pytest.ExceptionInfo[PerformanceEvidenceError],
|
||||
code: PerformanceEvidenceErrorCode,
|
||||
path: str,
|
||||
) -> None:
|
||||
assert error.value.code is code
|
||||
assert error.value.path == path
|
||||
|
||||
|
||||
def test_present_evidence_is_deterministic_content_addressed_and_three_party_closed() -> None:
|
||||
first, artifact, run_ref, manifest = _case("estimable")
|
||||
second = build_performance_evidence(artifact, run_ref, manifest)
|
||||
|
||||
assert first == second
|
||||
assert first.schema_version == PERFORMANCE_EVIDENCE_SCHEMA_VERSION
|
||||
assert first.performance_evidence_id.startswith("rhperformanceevidencev1:sha256:")
|
||||
assert first.document_sha256.startswith("sha256:")
|
||||
assert first.authority == "quant_engine"
|
||||
assert first.scope == "offline_research_only"
|
||||
assert first.run_id == first.backtest_run_ref_id == run_ref.run_id == manifest.run_id
|
||||
assert first.backtest_evidence_manifest_id == manifest.manifest_id
|
||||
assert first.backtest_evidence_manifest_evidence_digest == manifest.evidence_digest
|
||||
assert first.backtest_evidence_qualification == "contract_qualified"
|
||||
assert first.research_artifact_content_digest == f"sha256:{artifact.content_sha256}"
|
||||
assert first.performance_table_logical_name == "performance"
|
||||
assert first.performance_table_row_count == 1
|
||||
assert first.performance_row_digest.startswith("sha256:")
|
||||
assert first.benchmark_series_digest is not None
|
||||
assert first.canonical_bytes() == first.to_json().encode("utf-8")
|
||||
assert not first.canonical_bytes().endswith(b"\n")
|
||||
document_payload = first.to_dict()
|
||||
document_payload.pop("document_sha256")
|
||||
expected_document = json.dumps(
|
||||
document_payload,
|
||||
ensure_ascii=False,
|
||||
sort_keys=True,
|
||||
separators=(",", ":"),
|
||||
allow_nan=False,
|
||||
).encode("utf-8")
|
||||
assert _sha256(expected_document) == first.document_sha256
|
||||
assert PerformanceEvidenceV1.from_dict(
|
||||
first.to_dict(),
|
||||
artifact=artifact,
|
||||
run_ref=run_ref,
|
||||
evidence_manifest=manifest,
|
||||
) == first
|
||||
|
||||
|
||||
def test_methodology_and_metrics_bind_the_actual_artifact_builder_path() -> None:
|
||||
evidence, artifact, _, _ = _case("estimable")
|
||||
result = _backtest_result()
|
||||
expected_absolute = summary(result.returns, rf=0.0)
|
||||
benchmark = artifact.nav.set_index("trade_date")["benchmark_return"]
|
||||
benchmark.index = result.returns.index
|
||||
expected_relative = benchmark_summary(
|
||||
result.returns,
|
||||
benchmark,
|
||||
risk_free_daily=0.0,
|
||||
annualization=TRADING_DAYS_PER_YEAR,
|
||||
)
|
||||
metrics = _metric_map(evidence)
|
||||
|
||||
assert evidence.methodology.methodology_id == PERFORMANCE_METHODOLOGY_ID
|
||||
assert evidence.metric_schema_id == PERFORMANCE_METRIC_SCHEMA_ID
|
||||
assert evidence.methodology.return_type == "simple"
|
||||
assert evidence.methodology.source_frequency == "1d"
|
||||
assert evidence.methodology.periods_per_year == TRADING_DAYS_PER_YEAR == 252
|
||||
assert evidence.methodology.annual_risk_free == 0.0
|
||||
assert evidence.methodology.benchmark_risk_free_daily == 0.0
|
||||
assert evidence.methodology.benchmark_alignment == "exact_session_index"
|
||||
assert metrics["annualized_return"].value == pytest.approx(
|
||||
expected_absolute["ann_return"]
|
||||
)
|
||||
assert metrics["sharpe_ratio"].value == pytest.approx(expected_absolute["sharpe"])
|
||||
assert metrics["tracking_error"].value == pytest.approx(
|
||||
expected_relative["tracking_error"]
|
||||
)
|
||||
assert metrics["alpha"].value == pytest.approx(expected_relative["alpha"])
|
||||
assert all(metric.methodology_id == PERFORMANCE_METHODOLOGY_ID for metric in metrics.values())
|
||||
assert all(metric.metric_schema_id == PERFORMANCE_METRIC_SCHEMA_ID for metric in metrics.values())
|
||||
|
||||
|
||||
def test_relative_metric_availability_is_closed_for_present_absent_and_unestimable() -> None:
|
||||
present, *_ = _case("estimable")
|
||||
absent, *_ = _case("absent")
|
||||
zero_active, *_ = _case("zero_active_variance")
|
||||
zero_benchmark, *_ = _case("zero_benchmark_variance")
|
||||
|
||||
present_metrics = _metric_map(present)
|
||||
assert all(
|
||||
present_metrics[key].availability is PerformanceMetricAvailability.AVAILABLE
|
||||
for key in ("tracking_error", "information_ratio", "alpha", "beta")
|
||||
)
|
||||
absent_metrics = _metric_map(absent)
|
||||
assert absent.benchmark_series_digest is None
|
||||
assert absent.benchmark_id == ""
|
||||
assert absent.benchmark_alignment_policy == "none"
|
||||
assert all(
|
||||
absent_metrics[key].value is None
|
||||
and absent_metrics[key].availability
|
||||
is PerformanceMetricAvailability.BENCHMARK_ABSENT
|
||||
for key in ("tracking_error", "information_ratio", "alpha", "beta")
|
||||
)
|
||||
zero_active_metrics = _metric_map(zero_active)
|
||||
assert zero_active_metrics["tracking_error"].value == pytest.approx(0.0)
|
||||
assert (
|
||||
zero_active_metrics["information_ratio"].availability
|
||||
is PerformanceMetricAvailability.NOT_ESTIMABLE_ACTIVE_VARIANCE
|
||||
)
|
||||
assert zero_active_metrics["information_ratio"].value is None
|
||||
zero_benchmark_metrics = _metric_map(zero_benchmark)
|
||||
assert np.isfinite(zero_benchmark_metrics["tracking_error"].value)
|
||||
for key in ("alpha", "beta"):
|
||||
assert zero_benchmark_metrics[key].value is None
|
||||
assert (
|
||||
zero_benchmark_metrics[key].availability
|
||||
is PerformanceMetricAvailability.NOT_ESTIMABLE_BENCHMARK_VARIANCE
|
||||
)
|
||||
|
||||
|
||||
def test_golden_covers_present_absent_and_both_unestimable_states() -> None:
|
||||
expected = {
|
||||
"schema_version": 1,
|
||||
"source_commit": "a724e1e57a99d1304a932d01ee836bac56c5c15c",
|
||||
"source_tree": "4774e88442d25bf79a54eab3d7106ff4d0ba9603",
|
||||
"cases": {
|
||||
name: _case(name)[0].to_dict()
|
||||
for name in (
|
||||
"estimable",
|
||||
"zero_active_variance",
|
||||
"zero_benchmark_variance",
|
||||
"absent",
|
||||
)
|
||||
},
|
||||
}
|
||||
assert json.loads(PERFORMANCE_FIXTURE.read_text(encoding="utf-8")) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("owner", "field", "replacement", "code", "path"),
|
||||
[
|
||||
(
|
||||
"run_ref",
|
||||
"run_id",
|
||||
"rhbacktestrunv1:sha256:" + "0" * 64,
|
||||
PerformanceEvidenceErrorCode.IDENTITY_MISMATCH,
|
||||
"$.backtest_run_ref.run_id",
|
||||
),
|
||||
(
|
||||
"manifest",
|
||||
"manifest_id",
|
||||
"rhbacktestevidencev1:sha256:" + "0" * 64,
|
||||
PerformanceEvidenceErrorCode.EVIDENCE_MISMATCH,
|
||||
"$.backtest_evidence_manifest.manifest_id",
|
||||
),
|
||||
(
|
||||
"manifest",
|
||||
"qualification",
|
||||
EvidenceQualification.EXPLORATORY,
|
||||
PerformanceEvidenceErrorCode.AUTHORITY_REJECTED,
|
||||
"$.backtest_evidence_manifest.qualification",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_owner_identity_and_authority_mismatches_fail_closed(
|
||||
owner: str,
|
||||
field: str,
|
||||
replacement: object,
|
||||
code: PerformanceEvidenceErrorCode,
|
||||
path: str,
|
||||
) -> None:
|
||||
_, artifact, run_ref, manifest = _case("estimable")
|
||||
changed_run_ref = _mutate_frozen(run_ref, field, replacement) if owner == "run_ref" else run_ref
|
||||
changed_manifest = (
|
||||
_mutate_frozen(manifest, field, replacement) if owner == "manifest" else manifest
|
||||
)
|
||||
with pytest.raises(PerformanceEvidenceError) as rejected:
|
||||
build_performance_evidence(artifact, changed_run_ref, changed_manifest)
|
||||
_assert_error(rejected, code, path)
|
||||
|
||||
|
||||
def test_performance_table_row_and_benchmark_digest_mismatches_fail_closed() -> None:
|
||||
evidence, artifact, run_ref, manifest = _case("estimable")
|
||||
performance = artifact.performance
|
||||
performance.loc[0, "n_days"] += 1
|
||||
changed_artifact = replace(artifact, _performance=performance)
|
||||
with pytest.raises(PerformanceEvidenceError) as table_mismatch:
|
||||
build_performance_evidence(changed_artifact, run_ref, manifest)
|
||||
_assert_error(
|
||||
table_mismatch,
|
||||
PerformanceEvidenceErrorCode.EVIDENCE_MISMATCH,
|
||||
"$.artifact.tables.performance.content_digest",
|
||||
)
|
||||
|
||||
payload = evidence.to_dict()
|
||||
payload["benchmark_series_digest"] = "sha256:" + "0" * 64
|
||||
with pytest.raises(PerformanceEvidenceError) as benchmark_mismatch:
|
||||
PerformanceEvidenceV1.from_dict(
|
||||
payload,
|
||||
artifact=artifact,
|
||||
run_ref=run_ref,
|
||||
evidence_manifest=manifest,
|
||||
)
|
||||
_assert_error(
|
||||
benchmark_mismatch,
|
||||
PerformanceEvidenceErrorCode.BENCHMARK_INVALID,
|
||||
"$.benchmark_series_digest",
|
||||
)
|
||||
|
||||
|
||||
def test_manifest_closure_covers_non_performance_artifact_tables() -> None:
|
||||
_, artifact, run_ref, manifest = _case("estimable")
|
||||
nav = artifact.nav
|
||||
nav.loc[0, "nav"] += 0.01
|
||||
changed_artifact = replace(artifact, _nav=nav)
|
||||
|
||||
with pytest.raises(PerformanceEvidenceError) as rejected:
|
||||
build_performance_evidence(changed_artifact, run_ref, manifest)
|
||||
|
||||
_assert_error(
|
||||
rejected,
|
||||
PerformanceEvidenceErrorCode.EVIDENCE_MISMATCH,
|
||||
"$.artifact.tables.nav.content_digest",
|
||||
)
|
||||
|
||||
|
||||
def test_row_and_benchmark_mutations_change_their_digests_and_document_identity() -> None:
|
||||
original, artifact, run_ref, _ = _case("estimable")
|
||||
performance = artifact.performance
|
||||
performance.loc[0, "sharpe"] += 0.01
|
||||
changed_performance_artifact = replace(artifact, _performance=performance)
|
||||
changed_performance_manifest = build_backtest_evidence_manifest(
|
||||
run_ref,
|
||||
changed_performance_artifact,
|
||||
artifact_available_at="2026-01-08T02:05:00Z",
|
||||
)
|
||||
changed_performance = build_performance_evidence(
|
||||
changed_performance_artifact,
|
||||
run_ref,
|
||||
changed_performance_manifest,
|
||||
)
|
||||
assert changed_performance.performance_row_digest != original.performance_row_digest
|
||||
assert changed_performance.performance_evidence_id != original.performance_evidence_id
|
||||
|
||||
nav = artifact.nav
|
||||
nav.loc[0, "benchmark_nav"] += 0.01
|
||||
changed_benchmark_artifact = replace(artifact, _nav=nav)
|
||||
changed_benchmark_manifest = build_backtest_evidence_manifest(
|
||||
run_ref,
|
||||
changed_benchmark_artifact,
|
||||
artifact_available_at="2026-01-08T02:05:00Z",
|
||||
)
|
||||
changed_benchmark = build_performance_evidence(
|
||||
changed_benchmark_artifact,
|
||||
run_ref,
|
||||
changed_benchmark_manifest,
|
||||
)
|
||||
assert changed_benchmark.benchmark_series_digest != original.benchmark_series_digest
|
||||
assert changed_benchmark.performance_row_digest == original.performance_row_digest
|
||||
assert changed_benchmark.performance_evidence_id != original.performance_evidence_id
|
||||
|
||||
|
||||
def test_relative_metric_null_reasons_cannot_be_invented() -> None:
|
||||
_, artifact, run_ref, _ = _case("estimable")
|
||||
performance = artifact.performance
|
||||
performance.loc[0, "alpha"] = float("nan")
|
||||
changed_artifact = replace(artifact, _performance=performance)
|
||||
changed_manifest = build_backtest_evidence_manifest(
|
||||
run_ref,
|
||||
changed_artifact,
|
||||
artifact_available_at="2026-01-08T02:05:00Z",
|
||||
)
|
||||
with pytest.raises(PerformanceEvidenceError) as false_alpha_domain:
|
||||
build_performance_evidence(changed_artifact, run_ref, changed_manifest)
|
||||
_assert_error(
|
||||
false_alpha_domain,
|
||||
PerformanceEvidenceErrorCode.METRIC_INVALID,
|
||||
"$.metrics.alpha.value",
|
||||
)
|
||||
|
||||
_, absent_artifact, absent_run_ref, _ = _case("absent")
|
||||
absent_performance = absent_artifact.performance
|
||||
absent_performance.loc[0, "tracking_error"] = 0.0
|
||||
changed_absent = replace(absent_artifact, _performance=absent_performance)
|
||||
changed_absent_manifest = build_backtest_evidence_manifest(
|
||||
absent_run_ref,
|
||||
changed_absent,
|
||||
artifact_available_at="2026-01-08T02:05:00Z",
|
||||
)
|
||||
with pytest.raises(PerformanceEvidenceError) as false_absence:
|
||||
build_performance_evidence(
|
||||
changed_absent,
|
||||
absent_run_ref,
|
||||
changed_absent_manifest,
|
||||
)
|
||||
_assert_error(
|
||||
false_absence,
|
||||
PerformanceEvidenceErrorCode.BENCHMARK_INVALID,
|
||||
"$.metrics.tracking_error.availability",
|
||||
)
|
||||
|
||||
|
||||
def test_artifact_builder_enforces_strict_benchmark_session_alignment() -> None:
|
||||
run_ref = _run_ref()
|
||||
result = _backtest_result()
|
||||
misaligned = pd.Series(
|
||||
[0.0, 0.01, -0.01, 0.02],
|
||||
index=result.returns.index.shift(1, freq="B"),
|
||||
)
|
||||
with pytest.raises(ValueError, match="matching indexes"):
|
||||
build_research_run_artifact(
|
||||
result,
|
||||
run_id=run_ref.run_id,
|
||||
strategy_id=run_ref.strategy_id,
|
||||
strategy_name="Alpha Top 1",
|
||||
strategy_version=run_ref.strategy_version,
|
||||
engine_version="1.2.0",
|
||||
code_revision=run_ref.code_revision,
|
||||
data_snapshot_id=run_ref.dataset_snapshot_id,
|
||||
calendar="CN-A",
|
||||
timezone="Asia/Shanghai",
|
||||
started_at="2026-01-08T10:00:00+08:00",
|
||||
finished_at="2026-01-08T10:01:00+08:00",
|
||||
parameters=PARAMETERS,
|
||||
benchmark_id="000300.SH",
|
||||
benchmark_returns=misaligned,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("column", "value", "path"),
|
||||
[
|
||||
("total_ret", -1.01, "$.metrics.total_return.value"),
|
||||
("ann_ret", -1.01, "$.metrics.annualized_return.value"),
|
||||
("ann_volatility", -0.01, "$.metrics.annualized_volatility.value"),
|
||||
("max_dd", 0.01, "$.metrics.maximum_drawdown.value"),
|
||||
("win_rate", 1.01, "$.metrics.win_rate.value"),
|
||||
("tracking_error", -0.01, "$.metrics.tracking_error.value"),
|
||||
("n_trades", True, "$.metrics.trade_count.value"),
|
||||
],
|
||||
)
|
||||
def test_metric_domains_reject_invalid_source_values(
|
||||
column: str,
|
||||
value: object,
|
||||
path: str,
|
||||
) -> None:
|
||||
_, artifact, run_ref, _ = _case("estimable")
|
||||
performance = artifact.performance.astype(object)
|
||||
performance.at[0, column] = value
|
||||
changed_artifact = replace(artifact, _performance=performance)
|
||||
changed_manifest = build_backtest_evidence_manifest(
|
||||
run_ref,
|
||||
changed_artifact,
|
||||
artifact_available_at="2026-01-08T02:05:00Z",
|
||||
)
|
||||
with pytest.raises(PerformanceEvidenceError) as rejected:
|
||||
build_performance_evidence(changed_artifact, run_ref, changed_manifest)
|
||||
_assert_error(rejected, PerformanceEvidenceErrorCode.METRIC_INVALID, path)
|
||||
|
||||
|
||||
def test_closed_parser_rejects_unknown_non_ascii_non_finite_bool_and_unsafe_integer() -> None:
|
||||
evidence, artifact, run_ref, manifest = _case("estimable")
|
||||
|
||||
mutations: list[tuple[dict[str, Any], PerformanceEvidenceErrorCode, str]] = []
|
||||
unknown = evidence.to_dict()
|
||||
unknown["unexpected"] = "value"
|
||||
mutations.append((unknown, PerformanceEvidenceErrorCode.TYPE_ERROR, "$.unexpected"))
|
||||
non_ascii = evidence.to_dict()
|
||||
non_ascii["métric"] = "value"
|
||||
mutations.append((non_ascii, PerformanceEvidenceErrorCode.TYPE_ERROR, "$.métric"))
|
||||
non_finite = evidence.to_dict()
|
||||
non_finite["metrics"][0]["value"] = float("inf")
|
||||
mutations.append(
|
||||
(non_finite, PerformanceEvidenceErrorCode.METRIC_INVALID, "$.metrics[0].value")
|
||||
)
|
||||
bool_number = evidence.to_dict()
|
||||
bool_number["methodology"]["periods_per_year"] = True
|
||||
mutations.append(
|
||||
(
|
||||
bool_number,
|
||||
PerformanceEvidenceErrorCode.METHODOLOGY_MISMATCH,
|
||||
"$.methodology.periods_per_year",
|
||||
)
|
||||
)
|
||||
unsafe = evidence.to_dict()
|
||||
unsafe["performance_table_row_count"] = 2**53
|
||||
mutations.append(
|
||||
(
|
||||
unsafe,
|
||||
PerformanceEvidenceErrorCode.EVIDENCE_MISMATCH,
|
||||
"$.performance_table_row_count",
|
||||
)
|
||||
)
|
||||
|
||||
for payload, code, path in mutations:
|
||||
with pytest.raises(PerformanceEvidenceError) as rejected:
|
||||
PerformanceEvidenceV1.from_dict(
|
||||
payload,
|
||||
artifact=artifact,
|
||||
run_ref=run_ref,
|
||||
evidence_manifest=manifest,
|
||||
)
|
||||
_assert_error(rejected, code, path)
|
||||
|
||||
|
||||
def test_public_mapping_has_no_raw_inputs_storage_or_runtime_authority() -> None:
|
||||
evidence, *_ = _case("estimable")
|
||||
payload = evidence.to_dict()
|
||||
serialized = evidence.to_json().lower()
|
||||
forbidden_keys = {
|
||||
"parameters",
|
||||
"params_json",
|
||||
"returns",
|
||||
"nav",
|
||||
"benchmark_series",
|
||||
"table_bytes",
|
||||
"locator",
|
||||
"uri",
|
||||
"credential",
|
||||
"decision_eligible",
|
||||
"publication_eligible",
|
||||
"paper_trading",
|
||||
"live_trading",
|
||||
"investment_advice",
|
||||
}
|
||||
|
||||
def keys(value: object) -> set[str]:
|
||||
if isinstance(value, dict):
|
||||
return set(value) | {key for item in value.values() for key in keys(item)}
|
||||
if isinstance(value, list):
|
||||
return {key for item in value for key in keys(item)}
|
||||
return set()
|
||||
|
||||
assert not (keys(payload) & forbidden_keys)
|
||||
for token in ("postgres://", "mysql://", "s3://", "credential", "broker"):
|
||||
assert token not in serialized
|
||||
@@ -952,6 +952,37 @@ def test_architecture_dependency_no_copy_and_authority_boundaries() -> None:
|
||||
assert not any(name.startswith(("research_results", "research_platform")) for name in imports)
|
||||
for candidate in ("riskfolio", "pyp", "skfolio", "cvxportfolio"):
|
||||
assert candidate not in source.lower()
|
||||
artifact_source = (ROOT / "src" / "quant_engine" / "artifact.py").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
artifact_tree = ast.parse(artifact_source)
|
||||
forbidden_artifact_authority_symbols = {
|
||||
"PortfolioDecision",
|
||||
"RiskAssessment",
|
||||
"build_portfolio_decision",
|
||||
"assess_portfolio_risk",
|
||||
}
|
||||
artifact_imports = {
|
||||
alias.name
|
||||
for node in ast.walk(artifact_tree)
|
||||
if isinstance(node, ast.Import)
|
||||
for alias in node.names
|
||||
} | {
|
||||
node.module or ""
|
||||
for node in ast.walk(artifact_tree)
|
||||
if isinstance(node, ast.ImportFrom)
|
||||
}
|
||||
artifact_names = {
|
||||
node.id for node in ast.walk(artifact_tree) if isinstance(node, ast.Name)
|
||||
} | {
|
||||
node.attr for node in ast.walk(artifact_tree) if isinstance(node, ast.Attribute)
|
||||
}
|
||||
assert "quant_engine.portfolio_risk_contracts" not in artifact_imports
|
||||
assert not forbidden_artifact_authority_symbols & artifact_names
|
||||
assert all(
|
||||
token not in artifact_source
|
||||
for token in {"portfolio_risk_contracts", *forbidden_artifact_authority_symbols}
|
||||
)
|
||||
for owner_path in (
|
||||
ROOT / "src" / "quant_engine" / "governed_pipeline.py",
|
||||
ROOT / "src" / "quant_engine" / "artifact.py",
|
||||
@@ -989,7 +1020,6 @@ def test_architecture_dependency_no_copy_and_authority_boundaries() -> None:
|
||||
def test_read_only_owner_dependency_lock_and_ci_hashes_match_baseline() -> None:
|
||||
expected = {
|
||||
"src/quant_engine/governed_pipeline.py": "3b334f340898db78ed869375ab532f156e8c1fee595a8318f544bebdd391049d",
|
||||
"src/quant_engine/artifact.py": "e15feec412d3bfff10d8f21ca20813fc65cabc0703940371ed661896147bc379",
|
||||
"src/quant_engine/portfolio_construction.py": "e93d71da8d61b2047c19d4b99dace934a8cbc96d8d2b150ad62a9ceebd9163d4",
|
||||
"src/quant_engine/portfolio_decomp.py": "1a4f9f9aac2c46bf6ed2826d1b0d1723f06e3ce7c4b3f6479e098cbbd135bea6",
|
||||
"src/quant_engine/risk.py": "4a66c312d517d40f6f67bb71f438523e135645624ae78d49fa9c0fda2c02074e",
|
||||
|
||||
Reference in New Issue
Block a user