Artifex/tests/test_hyperscalper_feature_gap_analysis_v1.py
2026-08-18 20:45:09 +07:00

367 lines
12 KiB
Python

from __future__ import annotations
import json
import sys
import time
import numpy as np
import pytest
import hyperscalper_feature_gap_analysis_v1 as runner
pytestmark = pytest.mark.django_db
def test_read_only_runner_writes_711_map_and_honest_deq_blocker(tmp_path, monkeypatch):
rows = [
{"request_id": f"r{index}", "indicator_id": index, "period": 10, "p1": 0.0}
for index in range(711)
]
request = tmp_path / "request.json"
request.write_text(json.dumps({"requests": rows}), encoding="utf-8")
checkpoint = tmp_path / "checkpoint.npz"
values = {f"r{index}": np.arange(8, dtype=float) + index for index in range(711)}
values["r710"] = np.full(8, np.nan)
np.savez(checkpoint, **values)
manifest = tmp_path / "manifest.json"
manifest.write_text(
json.dumps({"coverage": {"feature_versions": [f"r{index}" for index in range(711)]}}),
encoding="utf-8",
)
for name in ("usage.parquet", "lineage.parquet"):
(tmp_path / name).write_bytes(b"not-read-without-pyarrow")
output = tmp_path / "out"
monkeypatch.setattr(
sys,
"argv",
[
"runner",
"--oracle-request",
str(request),
"--oracle-checkpoint",
str(checkpoint),
"--acceptance-manifest",
str(manifest),
"--historical-usage",
str(tmp_path / "usage.parquet"),
"--lineage",
str(tmp_path / "lineage.parquet"),
"--output-dir",
str(output),
"--sample-rows",
"8",
],
)
runner.main()
result = json.loads((output / "hyperscalper_feature_gap_analysis_v1.json").read_text())
assert result["read_only"] is True
assert len(result["information_map"]["features"]) == 711
assert result["information_map"]["features"][-1]["redundancy_status"] == (
"unusable_no_finite_observations"
)
assert result["redundancy"]["map"]["format"] == "parquet"
assert result["decision_equivalence"]["status"] == "BLOCKED"
def test_checkpoint_directory_maps_hs22_filenames_to_engineering_primitives(tmp_path):
rows = [
{"indicator_id": index, "period": index + 10, "p1": float(index % 3)}
for index in range(711)
]
checkpoint_dir = tmp_path / "checkpoints"
checkpoint_dir.mkdir()
for row in reversed(rows):
np.save(
checkpoint_dir
/ f"hs22_{row['indicator_id']}_{row['period']}_{row['p1']:g}.npy",
np.array([row["indicator_id"], row["period"]], dtype=float),
allow_pickle=False,
)
matrix, names = runner.checkpoint_directory_columns(
checkpoint_dir,
[
{
**row,
"feature_key": f"{row['indicator_id']}:{row['period']}:{row['p1']:g}",
"request_id": None,
}
for row in rows
],
)
assert matrix.shape == (2, 711)
assert np.array_equal(matrix[:, 0], [0.0, 10.0])
assert np.array_equal(matrix[:, -1], [710.0, 720.0])
assert names[0] == "hs22_0_10_0.npy"
def test_pairwise_redundancy_uses_staggered_finite_observations_for_clustering():
sample = np.array(
[
[0.0, 0.0, np.nan, np.nan],
[1.0, 2.0, np.nan, np.nan],
[2.0, 4.0, np.nan, np.nan],
[np.nan, np.nan, 5.0, np.nan],
[np.nan, np.nan, 6.0, np.nan],
]
)
pearson, spearman, counts = runner.pairwise_redundancy(sample, min_pair_samples=3)
assert counts[0, 1] == 3
assert pearson[0, 1] == pytest.approx(1.0)
assert spearman[0, 1] == pytest.approx(1.0)
assert np.isnan(pearson[0, 2])
assert runner.clusters(pearson) == [[0, 1]]
def test_pairwise_redundancy_precomputes_ranks_once_per_column_with_bounded_runtime(monkeypatch):
sample = np.arange(512 * 64, dtype=float).reshape(512, 64)
sample[::17, ::7] = np.nan
calls = 0
original = runner.rank_finite_columns
def counted_ranks(values):
nonlocal calls
calls += 1
return original(values)
monkeypatch.setattr(runner, "rank_finite_columns", counted_ranks)
started = time.perf_counter()
pearson, spearman, counts = runner.pairwise_redundancy(sample, min_pair_samples=2)
assert time.perf_counter() - started < 3.0
assert calls == 1
assert counts[0, 1] > 2
assert pearson[0, 1] == pytest.approx(1.0)
assert spearman[0, 1] == pytest.approx(1.0, abs=1e-5)
def test_type_aware_redundancy_uses_agreement_and_jaccard_for_detection_outputs():
sample = np.array(
[
[0.0, 1.0, 1.0, 0.0],
[1.0, 1.0, 1.0, 1.0],
[1.0, 0.0, 0.0, 1.0],
[0.0, 0.0, 0.0, 0.0],
]
)
pearson, spearman, agreement, jaccard, counts = runner.type_aware_redundancy(
sample, ["state", "state", "event", "event"], min_pair_samples=2
)
primary = runner.primary_redundancy_matrix(
pearson, agreement, jaccard, ["state", "state", "event", "event"]
)
assert counts[0, 1] == 4
assert agreement[0, 1] == pytest.approx(0.5)
assert jaccard[2, 3] == pytest.approx(1 / 3)
assert np.isnan(pearson[0, 1])
assert primary[0, 1] == pytest.approx(0.5)
assert primary[2, 3] == pytest.approx(1 / 3)
def test_semantic_types_derives_type_and_domain_from_primitive_map(tmp_path):
semantic_map = tmp_path / "semantic-map.json"
semantic_map.write_text(
json.dumps(
{
"primitives": [
{"indicator_id": 1, "period": 10, "p1": 0, "output_type": "boolean", "engineering_family": "regime"},
{"indicator_id": 2, "period": 20, "p1": 1, "output_type": "event", "domain": "trigger"},
]
}
),
encoding="utf-8",
)
rows = [
{"feature_key": "1:10:0", "indicator_id": 1, "period": 10, "p1": 0.0},
{"feature_key": "2:20:1", "indicator_id": 2, "period": 20, "p1": 1.0},
]
types, blocker = runner.semantic_types(semantic_map, rows)
assert blocker is None
assert types == [
{"output_type": "state", "domain": "regime"},
{"output_type": "event", "domain": "trigger"},
]
def test_lineage_combo_rejects_incomplete_data():
with pytest.raises(ValueError, match="at least five triples"):
runner.lineage_combo([1, 10, 0.0])
with pytest.raises(ValueError, match="at least five triples"):
runner.lineage_combo([[1, 10, 0.0]] * 5)
def test_lineage_combo_reads_only_the_five_primitives_from_real_flat_encoding():
# Flat lineage rows append tp, sl, confirmation bounds, and volume threshold.
combo = [
17, 20, 2.0,
31, 14, 0.0,
47, 9, 1.5,
62, 30, 0.0,
70, 20, 0.0,
0.015, 0.01, 5, 20, 5, 20, 75,
]
assert runner.lineage_combo(json.dumps(combo)) == (
(17, 20, 2.0),
(31, 14, 0.0),
(47, 9, 1.5),
(62, 30, 0.0),
(70, 20, 0.0),
)
def test_lineage_usage_parses_combo_json_role_triples(tmp_path):
pa = pytest.importorskip("pyarrow")
pq = pytest.importorskip("pyarrow.parquet")
lineage = tmp_path / "lineage.parquet"
combo = [1, 10, 0.0, 2, 11, 1.5, 1, 10, 0.0, 4, 13, 2.0, 5, 14, 3.0]
pq.write_table(
pa.Table.from_pylist(
[
{"strategy_id": 100, "combo_json": json.dumps(combo)},
{"strategy_id": 101, "combo_json": json.dumps(combo + [0.01, 0.02, 3, 6, 3, 6, 75])},
]
),
lineage,
)
counts, evidence, blocker = runner.lineage_usage(lineage)
assert blocker is None
assert counts == {"1:10:0": 4, "2:11:1.5": 2, "4:13:2": 2, "5:14:3": 2}
assert evidence["role_counts"] == {
"confirm": 2,
"signal": 2,
"trend": 2,
"trigger": 2,
"vol": 2,
}
assert evidence["strategy_ids_observed"] == 2
assert evidence["combo_counts"][0]["usage_count"] == 2
def test_cohort_deq_evidence_aggregates_exported_ledger_associatively(tmp_path):
deq_path = tmp_path / "cohort-deq.json"
sample = {
"deq": {
"return_1_bars_bps": 10.0,
"return_2_bars_bps": -5.0,
"return_3_bars_bps": None,
"return_5_bars_bps": 0.0,
"return_10_bars_bps": 20.0,
"mfe_bps": 25.0,
"mae_bps": -4.0,
"time_to_positive_bars": 2,
}
}
deq_path.write_text(
json.dumps(
{
"contract": "Cohort001-DEQ-ledger-export-v1",
"strategies": [
{
"strategy_version_id": "strategy-a",
"feature_triples": [
{"indicator_id": 7, "period": 10, "p1": 1.0},
{"indicator_id": 7, "period": 20, "p1": 2.0},
],
"folds": [
{
"fold": "Fold 1",
"scenarios": [
{"scenario": "BASE", "deq_samples": [sample]},
{"scenario": "STRESS", "deq_samples": [sample]},
],
}
],
}
],
}
),
encoding="utf-8",
)
evidence, parquet_rows, blocker = runner.cohort_deq_evidence(deq_path)
assert blocker is None
assert evidence["status"] == "AVAILABLE"
assert evidence["attribution"] == "ASSOCIATIVE_NOT_CAUSAL"
assert evidence["strategy_summaries"][0]["trade_count"] == 2
assert evidence["strategy_summaries"][0]["scenario_coverage"] == ["BASE", "STRESS"]
returns = evidence["feature_summaries"][0]["returns"]
assert returns["return_1_bars_bps"]["mean"] == 10.0
assert returns["return_2_bars_bps"]["directional_incorrect_count"] == 2
assert evidence["family_summaries"][0]["trade_count"] == 4
assert {row["entity_type"] for row in parquet_rows} == {"strategy", "feature", "family"}
def test_cohort_deq_evidence_uses_genome_combo_when_exported_triples_are_null(tmp_path):
deq_path = tmp_path / "cohort-deq.json"
deq_path.write_text(
json.dumps(
{
"contract": "Cohort001-DEQ-ledger-export-v1",
"strategies": [
{
"strategy_version_id": "strategy-a",
"feature_triples": None,
"genome": {
"combo": [
7, 10, 1.0,
8, 11, 2.0,
9, 12, 3.0,
10, 13, 4.0,
11, 14, 5.0,
0.01, 0.02, 3, 6, 3, 6, 75,
]
},
"folds": [
{
"fold": "Fold 1",
"scenarios": [
{
"scenario": "BASE",
"deq_samples": [{"deq": {"return_1_bars_bps": 10.0}}],
}
],
}
],
}
],
}
),
encoding="utf-8",
)
evidence, _, blocker = runner.cohort_deq_evidence(deq_path)
assert blocker is None
assert [item["feature_key"] for item in evidence["feature_summaries"]] == [
"10:13:4",
"11:14:5",
"7:10:1",
"8:11:2",
"9:12:3",
]
def test_deq_strategy_feature_keys_falls_back_when_exported_triples_are_invalid():
strategy = {
"feature_triples": [{"indicator_id": "not-an-id"}],
"genome": {"combo": [7, 10, 1.0, 8, 11, 2.0, 9, 12, 3.0, 10, 13, 4.0, 11, 14, 5.0]},
}
assert runner.deq_strategy_feature_keys(strategy) == [
("7:10:1", "7"),
("8:11:2", "8"),
("9:12:3", "9"),
("10:13:4", "10"),
("11:14:5", "11"),
]