This commit is contained in:
@@ -0,0 +1,311 @@
|
||||
"""Fresh synthetic artifact assembly; no reuse or relabelling of real outputs."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from dataclasses import replace
|
||||
from typing import Any
|
||||
|
||||
import pandas as pd
|
||||
import pytest
|
||||
|
||||
from quant_engine.artifact import (
|
||||
EvidenceQualification,
|
||||
PerformanceEvidenceError,
|
||||
ResearchRunArtifact,
|
||||
build_research_run_artifact,
|
||||
build_backtest_evidence_manifest,
|
||||
build_performance_evidence,
|
||||
)
|
||||
from quant_engine.execution import ExecutionConfig
|
||||
from quant_engine.factor_contracts import FactorContractError
|
||||
from quant_engine.governed_pipeline import BacktestContractError
|
||||
from quant_engine.research_pipeline import run_factor_backtest_research
|
||||
from quant_engine.retrospective_artifact_contracts import (
|
||||
build_retrospective_backtest_evidence_manifest,
|
||||
build_retrospective_performance_evidence,
|
||||
RetrospectiveBacktestEvidenceManifest,
|
||||
RetrospectivePerformanceEvidence,
|
||||
)
|
||||
from quant_engine.retrospective_backtest_contracts import RetrospectiveBacktestRunRef
|
||||
from test_retrospective_backtest_contracts import run_arguments
|
||||
from test_retrospective_data_contracts import identify, replace_at
|
||||
|
||||
CONTRACT_ERRORS = (FactorContractError, BacktestContractError, PerformanceEvidenceError)
|
||||
|
||||
|
||||
def synthetic_artifact(run: RetrospectiveBacktestRunRef) -> ResearchRunArtifact:
|
||||
# Artifact-envelope tests, not an end-to-end proof of factor/source authenticity.
|
||||
# The existing financial methods receive new, in-memory synthetic matrices.
|
||||
dates = pd.date_range("2018-01-02", periods=4, freq="B")
|
||||
scores = pd.DataFrame({"SIM0": [2.0, 0.0], "SIM1": [1.0, 3.0]}, index=dates[:2])
|
||||
opens = pd.DataFrame(
|
||||
{"SIM0": [10.0, 10.0, 15.0, 15.0], "SIM1": [20.0, 20.0, 20.0, 21.0]}, index=dates
|
||||
)
|
||||
closes = pd.DataFrame(
|
||||
{"SIM0": [10.0, 12.0, 15.0, 15.0], "SIM1": [20.0, 20.0, 18.0, 21.0]}, index=dates
|
||||
)
|
||||
result = run_factor_backtest_research(
|
||||
scores,
|
||||
opens,
|
||||
closes,
|
||||
top_k=1,
|
||||
execution_price_field="open",
|
||||
valuation_price_field="close",
|
||||
initial_cash=1000.0,
|
||||
config=ExecutionConfig(
|
||||
commission_bps=0, stamp_tax_bps=0, slippage_bps=0, min_trade_amount=0
|
||||
),
|
||||
)
|
||||
benchmark = pd.Series(
|
||||
[0.0, 0.01, -0.01, 0.02], index=result.returns.index, name="benchmark_return"
|
||||
)
|
||||
return build_research_run_artifact(
|
||||
result,
|
||||
run_id=run.run_id,
|
||||
strategy_id=run.strategy_id,
|
||||
strategy_name="Synthetic Top 1",
|
||||
strategy_version=run.strategy_version,
|
||||
engine_version="0.1.0",
|
||||
code_revision=run.code_revision,
|
||||
data_snapshot_id=run.dataset_snapshot_id,
|
||||
calendar="CN-A",
|
||||
timezone="Asia/Shanghai",
|
||||
started_at=run.evaluation_at,
|
||||
finished_at=run.computed_at,
|
||||
parameters={"lag_sessions": 1, "top_k": 1},
|
||||
benchmark_id="synthetic.benchmark",
|
||||
benchmark_returns=benchmark,
|
||||
)
|
||||
|
||||
|
||||
def test_manifest_closes_all_nine_existing_tables_without_changing_their_schema() -> None:
|
||||
run = RetrospectiveBacktestRunRef.create(**run_arguments())
|
||||
artifact = synthetic_artifact(run)
|
||||
manifest = build_retrospective_backtest_evidence_manifest(
|
||||
run, artifact, artifact_available_at="2026-09-08T01:11:00Z"
|
||||
)
|
||||
wire = manifest.to_dict()
|
||||
assert wire["schema_version"] == "2.0.0"
|
||||
assert wire["artifact_schema_version"] == "1.1.0"
|
||||
assert wire["run_id"] == run.run_id
|
||||
assert wire["usage"] == "retrospective_research"
|
||||
assert wire["historical_availability"] == "not_established"
|
||||
assert wire["execution_validation"] == "not_validated"
|
||||
assert wire["decision_eligible"] is False
|
||||
assert manifest.manifest_id.startswith("rhbacktestevidencev2:sha256:")
|
||||
assert len({table.logical_name for item in manifest.evidence for table in item.tables}) == 9
|
||||
|
||||
|
||||
def test_performance_v2_keeps_existing_metric_methods_and_binds_all_upstreams() -> None:
|
||||
run = RetrospectiveBacktestRunRef.create(**run_arguments())
|
||||
artifact = synthetic_artifact(run)
|
||||
manifest = build_retrospective_backtest_evidence_manifest(
|
||||
run, artifact, artifact_available_at="2026-09-08T01:11:00Z"
|
||||
)
|
||||
evidence = build_retrospective_performance_evidence(artifact, run, manifest)
|
||||
wire = evidence.to_dict()
|
||||
assert wire["schema_version"] == "researchhub.performance-evidence.v2"
|
||||
assert wire["methodology_id"] == "researchhub.quant-performance-methodology.v1"
|
||||
assert wire["metric_schema_id"] == "researchhub.quant-performance-metrics.v1"
|
||||
assert wire["research_artifact_schema_version"] == "1.1.0"
|
||||
assert wire["backtest_run_ref_id"] == run.run_id
|
||||
assert wire["backtest_evidence_manifest_id"] == manifest.manifest_id
|
||||
assert wire["historical_availability"] == "not_established"
|
||||
assert wire["usage"] == "retrospective_research"
|
||||
assert wire["start_date"] == "2018-01-02"
|
||||
assert wire["end_date"] == "2018-01-05"
|
||||
assert wire["artifact_available_at"] == "2026-09-08T01:11:00Z"
|
||||
assert evidence.run_id == run.run_id
|
||||
assert evidence.performance_evidence_id.startswith("rhperformancev2:sha256:")
|
||||
for metric in evidence.metrics:
|
||||
if metric.value is not None:
|
||||
assert metric.value == artifact.performance.iloc[0][metric.source_column]
|
||||
assert (
|
||||
RetrospectivePerformanceEvidence.from_json(
|
||||
evidence.to_json(), artifact=artifact, run_ref=run, evidence_manifest=manifest
|
||||
)
|
||||
== evidence
|
||||
)
|
||||
assert (
|
||||
RetrospectiveBacktestEvidenceManifest.from_json(
|
||||
manifest.to_json(), artifact=artifact, backtest_run_ref=run
|
||||
)
|
||||
== manifest
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("path", "value"),
|
||||
[
|
||||
("schema_version", "1.0.0"),
|
||||
("run_id", "rhbacktestrunv2:sha256:" + "0" * 64),
|
||||
("profile", "offline_research_v1"),
|
||||
("historical_availability", "established"),
|
||||
("decision_eligible", True),
|
||||
("execution_validation", "validated"),
|
||||
("evidence_scope", "real_data"),
|
||||
("artifact_available_at", "2026-09-08T01:09:00Z"),
|
||||
("artifact_schema_version", "2.0.0"),
|
||||
("qualification", "legacy_exploratory"),
|
||||
("evidence_digest", "sha256:" + "0" * 64),
|
||||
("evidence.0.tables.0.row_count", True),
|
||||
("evidence.0.tables.0.content_digest", "sha256:" + "0" * 64),
|
||||
("run_reference.value.factor_set_id", "rhfactorsetv2:sha256:" + "0" * 64),
|
||||
],
|
||||
)
|
||||
def test_manifest_rejects_reidentified_claims_without_actual_table_closure(
|
||||
path: str, value: Any
|
||||
) -> None:
|
||||
run = RetrospectiveBacktestRunRef.create(**run_arguments())
|
||||
artifact = synthetic_artifact(run)
|
||||
row = build_retrospective_backtest_evidence_manifest(
|
||||
run, artifact, artifact_available_at="2026-09-08T01:11:00Z"
|
||||
).to_dict()
|
||||
replace_at(row, path, value)
|
||||
identify(row, "manifest_id", "rhbacktestevidencev2:")
|
||||
with pytest.raises(CONTRACT_ERRORS):
|
||||
RetrospectiveBacktestEvidenceManifest.from_dict(
|
||||
row, artifact=artifact, backtest_run_ref=run
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("table", "column", "value"),
|
||||
[
|
||||
("run", "data_snapshot_id", "rhdsv2:sha256:" + "0" * 64),
|
||||
("run", "config_hash", "0" * 64),
|
||||
("run", "code_revision", "0" * 40),
|
||||
("run", "started_at", "2018-01-02T07:00:00Z"),
|
||||
("run", "finished_at", "2026-09-08T01:12:00Z"),
|
||||
("signals", "asset_id", "/private/data.csv"),
|
||||
("nav", "run_id", "old.run"),
|
||||
("performance", "run_id", "old.run"),
|
||||
],
|
||||
)
|
||||
def test_manifest_rejects_artifact_identity_time_or_private_data_mismatch(
|
||||
table: str, column: str, value: Any
|
||||
) -> None:
|
||||
run = RetrospectiveBacktestRunRef.create(**run_arguments())
|
||||
artifact = synthetic_artifact(run)
|
||||
frame = getattr(artifact, table)
|
||||
frame.loc[frame.index[0], column] = value
|
||||
forged = replace(artifact, **{"_" + table: frame})
|
||||
with pytest.raises(CONTRACT_ERRORS):
|
||||
build_retrospective_backtest_evidence_manifest(
|
||||
run, forged, artifact_available_at="2026-09-08T01:13:00Z"
|
||||
)
|
||||
|
||||
|
||||
def test_each_table_is_reconciled_and_legacy_apis_cannot_accept_v2() -> None:
|
||||
run = RetrospectiveBacktestRunRef.create(**run_arguments())
|
||||
artifact = synthetic_artifact(run)
|
||||
manifest = build_retrospective_backtest_evidence_manifest(
|
||||
run, artifact, artifact_available_at="2026-09-08T01:11:00Z"
|
||||
)
|
||||
for item in manifest.evidence:
|
||||
for table in item.tables:
|
||||
with pytest.raises(CONTRACT_ERRORS):
|
||||
build_retrospective_backtest_evidence_manifest(
|
||||
run,
|
||||
artifact,
|
||||
artifact_available_at="2026-09-08T01:11:00Z",
|
||||
expected_table_digests={table.logical_name: "sha256:" + "0" * 64},
|
||||
)
|
||||
with pytest.raises(CONTRACT_ERRORS):
|
||||
build_backtest_evidence_manifest(
|
||||
run, artifact, artifact_available_at="2026-09-08T01:11:00Z"
|
||||
)
|
||||
with pytest.raises(CONTRACT_ERRORS):
|
||||
build_performance_evidence(artifact, run, manifest)
|
||||
with pytest.raises(CONTRACT_ERRORS):
|
||||
build_retrospective_backtest_evidence_manifest(
|
||||
run,
|
||||
artifact,
|
||||
artifact_available_at="2026-09-08T01:11:00Z",
|
||||
qualification=EvidenceQualification.LEGACY_EXPLORATORY,
|
||||
)
|
||||
|
||||
|
||||
def seal_performance(row: dict[str, Any]) -> None:
|
||||
def sha(document: Any) -> str:
|
||||
return (
|
||||
"sha256:"
|
||||
+ hashlib.sha256(
|
||||
json.dumps(
|
||||
document,
|
||||
ensure_ascii=False,
|
||||
sort_keys=True,
|
||||
separators=(",", ":"),
|
||||
allow_nan=False,
|
||||
).encode()
|
||||
).hexdigest()
|
||||
)
|
||||
|
||||
row.pop("document_sha256", None)
|
||||
row.pop("performance_evidence_id", None)
|
||||
row["performance_evidence_id"] = "rhperformancev2:" + sha(row)
|
||||
row["document_sha256"] = sha(row)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("path", "value"),
|
||||
[
|
||||
("schema_version", "researchhub.performance-evidence.v1"),
|
||||
("scope", "live"),
|
||||
("historical_availability", "established"),
|
||||
("decision_eligible", True),
|
||||
("evidence_scope", "real_data"),
|
||||
("dataset_snapshot_id", "rhdsv2:sha256:" + "0" * 64),
|
||||
("backtest_run_ref_document_sha256", "sha256:" + "0" * 64),
|
||||
("backtest_evidence_manifest_id", "rhbacktestevidencev2:sha256:" + "0" * 64),
|
||||
("methodology.periods_per_year", 365),
|
||||
("metric_schema_id", "new.metric"),
|
||||
("metrics.0.value", 0.0),
|
||||
("metrics.0.nullable", True),
|
||||
("start_date", "2017-01-01"),
|
||||
("artifact_available_at", "2018-01-02T07:00:00Z"),
|
||||
],
|
||||
)
|
||||
def test_performance_never_accepts_reidentified_changed_facts(path: str, value: Any) -> None:
|
||||
run = RetrospectiveBacktestRunRef.create(**run_arguments())
|
||||
artifact = synthetic_artifact(run)
|
||||
manifest = build_retrospective_backtest_evidence_manifest(
|
||||
run, artifact, artifact_available_at="2026-09-08T01:11:00Z"
|
||||
)
|
||||
row = build_retrospective_performance_evidence(artifact, run, manifest).to_dict()
|
||||
replace_at(row, path, value)
|
||||
seal_performance(row)
|
||||
with pytest.raises(CONTRACT_ERRORS):
|
||||
RetrospectivePerformanceEvidence.from_dict(
|
||||
row, artifact=artifact, run_ref=run, evidence_manifest=manifest
|
||||
)
|
||||
|
||||
|
||||
def test_performance_has_immutable_finite_canonical_payload_and_current_tables() -> None:
|
||||
run = RetrospectiveBacktestRunRef.create(**run_arguments())
|
||||
artifact = synthetic_artifact(run)
|
||||
manifest = build_retrospective_backtest_evidence_manifest(
|
||||
run, artifact, artifact_available_at="2026-09-08T01:11:00Z"
|
||||
)
|
||||
evidence = build_retrospective_performance_evidence(artifact, run, manifest)
|
||||
assert evidence.document_sha256.startswith("sha256:")
|
||||
exported = evidence.to_dict()
|
||||
exported["metrics"][0]["value"] = 9.0
|
||||
assert evidence.to_dict()["metrics"][0]["value"] != 9.0
|
||||
for data in (
|
||||
evidence.to_json() + "\n",
|
||||
'{"schema_version":"x",' + evidence.to_json()[1:],
|
||||
"null",
|
||||
"{bad",
|
||||
):
|
||||
with pytest.raises(CONTRACT_ERRORS):
|
||||
RetrospectivePerformanceEvidence.from_json(
|
||||
data, artifact=artifact, run_ref=run, evidence_manifest=manifest
|
||||
)
|
||||
frame = artifact.performance
|
||||
frame.loc[0, "total_ret"] = 0.0
|
||||
forged = replace(artifact, _performance=frame)
|
||||
with pytest.raises(CONTRACT_ERRORS):
|
||||
build_retrospective_performance_evidence(forged, run, manifest)
|
||||
Reference in New Issue
Block a user