Add GPU parity and feature gap analysis

This commit is contained in:
Daniel Maddern 2026-08-18 20:45:09 +07:00
parent c38f3d7efb
commit 203bc7917a
48 changed files with 95351 additions and 0 deletions

32
Dockerfile.gpu-feature-v1 Normal file
View file

@ -0,0 +1,32 @@
FROM --platform=linux/arm64 nvidia/cuda:12.8.1-cudnn-runtime-ubuntu22.04
ARG TARGETARCH
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends python3 python3-pip \
&& rm -rf /var/lib/apt/lists/* \
&& python3 -m pip install --no-cache-dir --upgrade pip \
&& python3 -m pip install --no-cache-dir \
--index-url https://download.pytorch.org/whl/cu128 \
--extra-index-url https://pypi.org/simple \
torch==2.7.1+cu128 numpy
# Fail the ARM64 image build if it selected an incompatible interpreter or PyTorch wheel.
RUN test "$TARGETARCH" = arm64 \
&& python3 -c "import platform, torch; assert platform.machine() == 'aarch64'; assert torch.version.cuda == '12.8'; print(f'{platform.machine()} torch={torch.__version__} cuda={torch.version.cuda}')"
WORKDIR /opt/gpu-feature
COPY gpu_feature_engine_v1.py /opt/gpu-feature/gpu_feature_engine_v1.py
COPY gpu_batch01_v1_1_runner.py gpu_feature_parity_contract_v1_1.py /opt/gpu-feature/
COPY control_plane/trading_studio/indicators/historical_band_channel.py /opt/gpu-feature/control_plane/trading_studio/indicators/historical_band_channel.py
# Oracle and market data are intentionally supplied as read-only runtime mounts.
ENV PYTHONUNBUFFERED=1 \
CUDA_DEVICE_ORDER=PCI_BUS_ID \
GPU_FEATURE_DATA_CSV=/data/binance_btcusdt_spot_2m_180d.csv \
GPU_FEATURE_ORACLE_NPZ=/oracle/batch01_oracle_outputs.npz \
GPU_FEATURE_REQUEST_JSON=/oracle/batch01_oracle_request.json \
GPU_FEATURE_CACHE_DIR=/cache
ENTRYPOINT ["python3", "/opt/gpu-feature/gpu_feature_engine_v1.py"]
CMD ["--mode", "smoke"]

View file

@ -0,0 +1,337 @@
#!/usr/bin/env python3
"""Read-only Cohort001 entry-to-frozen-oracle failure mining.
Associations in this report are observational and must not be interpreted as
causal feature effects. The runner never replays a strategy or alters inputs.
"""
from __future__ import annotations
import argparse
import hashlib
import json
from collections import Counter, defaultdict
from pathlib import Path
from typing import Any
import numpy as np
ARTIFACT = "COHORT001_FEATURE_FAILURE_MINING_V1"
GOOD_LABEL = "GOOD_ENTRY"
FAILURE_LABELS = ("WRONG_DIRECTION", "HIGH_MAE_ENTRY", "LATE_SIGNAL", "RECOVERY_DEPENDENT")
ENTRY_FIELDS = ("entry_bar", "entry_index", "entry_bar_index", "entry_idx", "bar_index")
def file_hash(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as source:
for block in iter(lambda: source.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def directory_hash(path: Path) -> str:
"""Hash names and contents without loading the oracle into memory."""
digest = hashlib.sha256()
for item in sorted(path.glob("*.npy")):
digest.update(item.name.encode("utf-8"))
digest.update(b"\0")
digest.update(bytes.fromhex(file_hash(item)))
return digest.hexdigest()
def json_compatible(value: Any) -> Any:
"""Convert NumPy values to strict JSON-compatible Python values."""
if isinstance(value, np.ndarray):
return json_compatible(value.tolist())
if isinstance(value, np.bool_):
return bool(value)
if isinstance(value, np.integer):
return int(value)
if isinstance(value, np.floating):
value = float(value)
if isinstance(value, float):
return value if np.isfinite(value) else None
if isinstance(value, dict):
return {json_compatible(key): json_compatible(item) for key, item in value.items()}
if isinstance(value, (list, tuple)):
return [json_compatible(item) for item in value]
return value
def load_engineering_map(path: Path) -> dict[str, dict[str, Any]]:
payload = json.loads(path.read_text(encoding="utf-8"))
rows = payload.get("primitives", payload.get("requests", []))
if not isinstance(rows, list):
raise ValueError("engineering map must contain primitives or requests")
result = {}
for row in rows:
try:
key = feature_key(row)
except (KeyError, TypeError, ValueError):
continue
if key in result:
raise ValueError(f"duplicate engineering primitive: {key}")
result[key] = dict(row, feature_key=key)
if not result:
raise ValueError("engineering map contains no usable primitives")
return result
def feature_key(row: dict[str, Any]) -> str:
return f"{int(row['indicator_id'])}:{int(row['period'])}:{float(row['p1']):g}"
def load_semantics(path: Path, engineering: dict[str, dict[str, Any]]) -> dict[str, dict[str, str]]:
try:
import pyarrow.parquet as pq
except ImportError as error:
raise ValueError("pyarrow is required to read semantic-map parquet") from error
result = {key: {"domain": "unclassified", "output_type": "continuous"} for key in engineering}
for row in pq.read_table(path).to_pylist():
try:
key = str(row.get("feature_key") or feature_key(row))
except (KeyError, TypeError, ValueError):
continue
if key not in result:
continue
raw = str(row.get("output_type", row.get("semantic_type", row.get("value_type", "continuous")))).lower()
kind = "event" if raw in {"event", "detection", "binary_event"} else "state" if raw in {"state", "categorical", "boolean"} else "continuous"
result[key] = {"domain": str(row.get("domain", row.get("semantic_domain", row.get("engineering_family", row.get("family", "unclassified"))))), "output_type": kind}
return result
def feature_keys_from_row(row: dict[str, Any]) -> list[str]:
"""Read explicit triples first, then the persisted flat five-triple genome."""
for field in ("feature_triples_json", "feature_triples"):
value = row.get(field)
if isinstance(value, str):
try:
value = json.loads(value)
except json.JSONDecodeError:
continue
if isinstance(value, list):
keys = []
for triple in value:
if isinstance(triple, dict):
try:
keys.append(feature_key(triple))
except (KeyError, TypeError, ValueError):
pass
if keys:
return keys
value = row.get("genome_json", row.get("genome"))
if isinstance(value, str):
try:
value = json.loads(value)
except json.JSONDecodeError:
return []
combo = value.get("combo") if isinstance(value, dict) else None
if isinstance(combo, str):
try:
combo = json.loads(combo)
except json.JSONDecodeError:
return []
if not isinstance(combo, list) or len(combo) < 15:
return []
try:
return [f"{int(combo[index])}:{int(combo[index + 1])}:{float(combo[index + 2]):g}" for index in range(0, 15, 3)]
except (TypeError, ValueError):
return []
def entry_bar(row: dict[str, Any]) -> tuple[int | None, str | None]:
"""Resolve only an integer bar offset; timestamps are intentionally not guessed."""
containers = [("column", row)]
for field in ("raw_ledger_json", "context_json"):
value = row.get(field)
if isinstance(value, str):
try:
value = json.loads(value)
except json.JSONDecodeError:
continue
if isinstance(value, dict):
containers.append((field, value))
for source, values in containers:
for field in ENTRY_FIELDS:
value = values.get(field)
if isinstance(value, bool) or value is None:
continue
try:
numeric = float(value)
except (TypeError, ValueError):
continue
if np.isfinite(numeric) and numeric.is_integer() and numeric >= 0:
return int(numeric), f"{source}.{field}"
return None, None
def checkpoint_paths(directory: Path, engineering: dict[str, dict[str, Any]]) -> dict[str, Path]:
paths = {}
for path in directory.glob("*.npy"):
parts = path.stem.split("_")
if len(parts) != 4 or parts[0] != "hs22":
continue
try:
key = f"{int(parts[1])}:{int(parts[2])}:{float(parts[3]):g}"
except ValueError:
continue
if key in engineering and key not in paths:
paths[key] = path
return paths
def ks_statistic(good: np.ndarray, failure: np.ndarray) -> tuple[float, str]:
try:
from scipy.stats import ks_2samp
return float(ks_2samp(good, failure, method="auto").statistic), "scipy_ks_2samp"
except ImportError:
# Exact empirical CDF distance evaluated at every observed rank boundary.
values = np.sort(np.concatenate((good, failure)))
left = np.searchsorted(np.sort(good), values, side="right") / good.size
right = np.searchsorted(np.sort(failure), values, side="right") / failure.size
return float(np.max(np.abs(left - right))), "exact_rank_approx"
def odds_ratio(good: np.ndarray, failure: np.ndarray) -> float | None:
if not np.all(np.isin(np.concatenate((good, failure)), (0, 1))):
return None
# Haldane-Anscombe correction keeps complete separation reportable.
good_on, failure_on = good.sum(), failure.sum()
return float(((failure_on + 0.5) / (failure.size - failure_on + 0.5)) / ((good_on + 0.5) / (good.size - good_on + 0.5)))
def comparison(good: list[tuple[float, str]], failure: list[tuple[float, str]]) -> dict[str, Any]:
good_values, failure_values = np.array([item[0] for item in good]), np.array([item[0] for item in failure])
median_difference = float(np.median(failure_values) - np.median(good_values))
pooled = np.concatenate((good_values, failure_values))
good_variance = np.var(good_values, ddof=1) if good_values.size > 1 else 0.0
failure_variance = np.var(failure_values, ddof=1) if failure_values.size > 1 else 0.0
degrees_of_freedom = good_values.size + failure_values.size - 2
scale = float(np.sqrt(((good_values.size - 1) * good_variance + (failure_values.size - 1) * failure_variance) / degrees_of_freedom)) if degrees_of_freedom else 0.0
effect = median_difference / scale if scale else None
pooled_sorted = np.sort(pooled)
quantile_separation = float(
np.searchsorted(pooled_sorted, np.median(failure_values), side="right") / pooled.size
- np.searchsorted(pooled_sorted, np.median(good_values), side="right") / pooled.size
)
ks, ks_method = ks_statistic(good_values, failure_values)
direction = np.sign(median_difference)
folds = []
for fold in sorted(set(item[1] for item in good) & set(item[1] for item in failure)):
fold_difference = np.median([item[0] for item in failure if item[1] == fold]) - np.median([item[0] for item in good if item[1] == fold])
folds.append(float(fold_difference))
consistent = sum(np.sign(item) == direction for item in folds) if direction else sum(item == 0 for item in folds)
return {
"good_samples": int(good_values.size), "failure_samples": int(failure_values.size),
"median_difference_failure_minus_good": median_difference,
"standardized_effect_size": effect, "standardized_effect_size_method": "median_difference_over_pooled_within_group_standard_deviation", "quantile_separation": quantile_separation,
"ks_statistic": ks, "ks_method": ks_method,
"folds_compared": len(folds), "fold_direction_consistent": consistent,
"fold_consistency": consistent / len(folds) if folds else None,
}
def mine(rows: list[dict[str, Any]], engineering: dict[str, dict[str, Any]], semantics: dict[str, dict[str, str]], paths: dict[str, Path]) -> tuple[list[dict[str, Any]], dict[str, int], list[str]]:
samples: dict[str, dict[str, list[tuple[float, str]]]] = defaultdict(lambda: defaultdict(list))
skipped = Counter()
schemas = set()
arrays: dict[str, np.ndarray] = {}
for row in rows:
label = row.get("failure_label")
if label not in (GOOD_LABEL, *FAILURE_LABELS):
continue
bar, schema = entry_bar(row)
keys = feature_keys_from_row(row)
if bar is None:
skipped["unmappable_entry_bar"] += 1
continue
if not keys:
skipped["unmappable_feature_triples"] += 1
continue
schemas.add(schema)
fold = str(row.get("fold", "unknown"))
for key in set(keys):
if key not in engineering or key not in paths:
skipped["feature_missing_from_oracle"] += 1
continue
values = arrays.setdefault(key, np.load(paths[key], allow_pickle=False, mmap_mode="r").reshape(-1))
if bar >= values.size:
skipped["entry_bar_outside_oracle"] += 1
continue
value = float(values[bar])
if not np.isfinite(value):
skipped["nonfinite_oracle_value"] += 1
continue
samples[key][label].append((value, fold))
output = []
for key in sorted(samples):
good = samples[key][GOOD_LABEL]
if not good:
continue
for label in FAILURE_LABELS:
failure = samples[key][label]
if not failure:
continue
result = {"feature_key": key, "failure_label": label, "attribution": "ASSOCIATIVE_NOT_CAUSAL", **engineering[key], **semantics[key], **comparison(good, failure)}
if semantics[key]["output_type"] in {"state", "event"}:
result["state_event_odds_ratio_failure_vs_good"] = odds_ratio(np.array([x[0] for x in good]), np.array([x[0] for x in failure]))
output.append(result)
return output, dict(sorted(skipped.items())), sorted(schema for schema in schemas if schema)
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--failure-context", type=Path, required=True)
parser.add_argument("--semantic-map", type=Path, required=True)
parser.add_argument("--oracle-checkpoint-dir", type=Path, required=True)
parser.add_argument("--engineering-map", type=Path, required=True)
parser.add_argument("--output-dir", type=Path, required=True)
args = parser.parse_args()
if not args.oracle_checkpoint_dir.is_dir():
parser.error("--oracle-checkpoint-dir must be a directory")
for path in (args.failure_context, args.semantic_map, args.engineering_map):
if not path.is_file():
parser.error(f"input is not a file: {path}")
try:
import pyarrow as pa
import pyarrow.parquet as pq
except ImportError as error:
raise SystemExit("pyarrow is required for Cohort001 failure mining") from error
engineering = load_engineering_map(args.engineering_map)
semantics = load_semantics(args.semantic_map, engineering)
paths = checkpoint_paths(args.oracle_checkpoint_dir, engineering)
rows = pq.read_table(args.failure_context).to_pylist()
features, skipped, entry_schemas = mine(rows, engineering, semantics, paths)
domains: dict[tuple[str, str], list[dict[str, Any]]] = defaultdict(list)
for item in features:
domains[(item["domain"], item["failure_label"])].append(item)
domain_rows = [{
"domain": domain, "failure_label": label, "feature_comparisons": len(items),
"median_standardized_effect_size": float(np.median([x["standardized_effect_size"] for x in items if x["standardized_effect_size"] is not None])) if any(x["standardized_effect_size"] is not None for x in items) else None,
"median_ks_statistic": float(np.median([x["ks_statistic"] for x in items])),
"median_fold_consistency": float(np.median([x["fold_consistency"] for x in items if x["fold_consistency"] is not None])) if any(x["fold_consistency"] is not None for x in items) else None,
"attribution": "ASSOCIATIVE_NOT_CAUSAL",
} for (domain, label), items in sorted(domains.items())]
result = {
"schema_version": 1, "artifact": ARTIFACT, "read_only": True,
"attribution": "ASSOCIATIVE_NOT_CAUSAL",
"attribution_note": "Entry-time associations do not establish causal feature effects.",
"inputs": {
str(path): file_hash(path) for path in (args.failure_context, args.semantic_map, args.engineering_map)
} | {str(args.oracle_checkpoint_dir): directory_hash(args.oracle_checkpoint_dir)},
"entry_bar_schema_detected": entry_schemas,
"failure_context_rows": len(rows), "oracle_features_available": len(paths),
"skipped": skipped, "feature_failure_comparisons": features, "domain_aggregates": domain_rows,
}
args.output_dir.mkdir(parents=True, exist_ok=True)
pq.write_table(pa.Table.from_pylist(features), args.output_dir / "cohort001_feature_failure_mining_v1.parquet", compression="zstd")
output = args.output_dir / "cohort001_feature_failure_mining_v1.json"
output.write_text(json.dumps(json_compatible(result), indent=2, sort_keys=True, allow_nan=False) + "\n", encoding="utf-8")
print(output)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,177 @@
from __future__ import annotations
import json
from dataclasses import fields
from pathlib import Path
from uuid import UUID
from django.core.management.base import BaseCommand, CommandError
from django.db import connection, transaction
from control_plane.trading_studio.indicators.feature_catalog import feature_definition
from control_plane.trading_studio.indicators.schema import HS22SchemaError, parse_hs22
from control_plane.trading_studio.management.commands.cohort_qualification_hyperscalper_001 import (
RUNNER_NAME as QUALIFICATION_RUNNER,
)
from control_plane.trading_studio.management.commands.materialize_hyperscalper_cohort_001 import (
DATASET_SHA256,
DATASET_VERSION_ID,
)
from control_plane.trading_studio.management.commands.specimen_hyperscalper_cohort_001 import (
RUNNER_NAME as SPECIMEN_RUNNER,
SCENARIO_NAMES,
)
from control_plane.trading_studio.models import QualificationReplayRun, TradingCohort
from control_plane.trading_studio.qualification import DEQPrimitives, ReplayMode
ALLOWED_RUNNERS = frozenset((SPECIMEN_RUNNER, QUALIFICATION_RUNNER))
class Command(BaseCommand):
help = "Read-only JSON export of completed Cohort001 CANARY_ONLY DEQ ledger data."
def add_arguments(self, parser):
parser.add_argument("--cohort-id", required=True, type=UUID)
parser.add_argument("--output", required=True, help="JSON output path.")
def handle(self, *args, **options):
output = Path(options["output"])
try:
with transaction.atomic():
# Must be the first statement in this transaction; PostgreSQL rejects writes thereafter.
with connection.cursor() as cursor:
cursor.execute("SET TRANSACTION READ ONLY")
payload = self._payload(options["cohort_id"])
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(
json.dumps(payload, indent=2, sort_keys=True, ensure_ascii=True) + "\n",
encoding="utf-8",
)
except (OSError, ValueError, TradingCohort.DoesNotExist) as error:
raise CommandError(str(error)) from error
self.stdout.write(json.dumps({"cohort_id": str(options["cohort_id"]), "output": str(output)}))
@classmethod
def _payload(cls, cohort_id: UUID) -> dict:
cohort = TradingCohort.objects.select_related("dataset_version").get(pk=cohort_id)
cls._validate_cohort(cohort)
memberships = list(
cohort.memberships.select_related("strategy_version").order_by("ordinal")
)
if len(memberships) != 20 or [member.ordinal for member in memberships] != list(range(1, 21)):
raise ValueError("Cohort001 must contain exactly 20 ordered memberships.")
runs = list(
QualificationReplayRun.objects.filter(
strategy_version_id__in=[member.strategy_version_id for member in memberships],
dataset_version_id=cohort.dataset_version_id,
replay_mode=ReplayMode.CANARY_ONLY,
runner_name__in=ALLOWED_RUNNERS,
)
.prefetch_related("ledger_rows")
.order_by("strategy_version_id", "fold", "scenario_name", "runner_name")
)
run_index = cls._validated_run_index(memberships, runs)
folds = cohort.policy_snapshot["reconstruction_v1"]["folds"]
strategies = []
for member in memberships:
runner = SPECIMEN_RUNNER if member.ordinal == 1 else QUALIFICATION_RUNNER
strategy = {
"ordinal": member.ordinal,
"membership_id": str(member.id),
"strategy_version_id": str(member.strategy_version_id),
"runner_name": runner,
"genome": member.strategy_version.genome,
"feature_triples": cls._feature_triples(member.strategy_version.genome),
"folds": [],
}
for fold in folds:
fold_name = fold["fold"]
scenarios = []
for scenario_name in SCENARIO_NAMES:
run = run_index[(member.strategy_version_id, fold_name, scenario_name)]
ledger = [row.result for row in sorted(run.ledger_rows.all(), key=lambda row: row.sequence)]
scenarios.append(
{
"qualification_run_id": str(run.id),
"scenario": scenario_name,
"summary": run.summary,
"deq_samples": ledger,
"deq_aggregate": cls._deq_aggregate(ledger),
}
)
strategy["folds"].append({"fold": fold_name, "scenarios": scenarios})
strategies.append(strategy)
return {
"contract": "Cohort001-DEQ-ledger-export-v1",
"cohort_id": str(cohort.id),
"dataset_version_id": str(cohort.dataset_version_id),
"dataset_sha256": cohort.dataset_version.content_hash,
"replay_mode": ReplayMode.CANARY_ONLY,
"allowed_runner_names": sorted(ALLOWED_RUNNERS),
"strategies": strategies,
}
@staticmethod
def _validate_cohort(cohort) -> None:
reconstruction = cohort.policy_snapshot.get("reconstruction_v1")
if (
cohort.dataset_version_id != DATASET_VERSION_ID
or cohort.dataset_version.content_hash != DATASET_SHA256
or not isinstance(reconstruction, dict)
or reconstruction.get("provenance") != "RECONSTRUCTED_FROM_FROZEN_SPEC_V1"
or not isinstance(reconstruction.get("folds"), list)
or len(reconstruction["folds"]) != 4
):
raise ValueError("Cohort or dataset is not the validated frozen Cohort001 canary dataset.")
@classmethod
def _validated_run_index(cls, memberships, runs) -> dict:
member_by_strategy = {member.strategy_version_id: member for member in memberships}
expected = {
(member.strategy_version_id, fold["fold"], scenario)
for member in memberships
for fold in member.cohort.policy_snapshot["reconstruction_v1"]["folds"]
for scenario in SCENARIO_NAMES
}
indexed = {}
for run in runs:
member = member_by_strategy[run.strategy_version_id]
expected_runner = SPECIMEN_RUNNER if member.ordinal == 1 else QUALIFICATION_RUNNER
key = (run.strategy_version_id, run.fold, run.scenario_name)
if run.runner_name != expected_runner or key not in expected or key in indexed:
raise ValueError("Cohort001 contains an unexpected or duplicate CANARY_ONLY replay.")
indexed[key] = run
if set(indexed) != expected:
raise ValueError("Cohort001 DEQ export requires a complete 20 x 4 x 13 replay matrix.")
return indexed
@staticmethod
def _feature_triples(genome: dict) -> list[dict] | None:
try:
state = parse_hs22(genome.get("combo"))
except (AttributeError, HS22SchemaError, TypeError, ValueError):
return None
return [feature_definition(variant).artifact() for variant in state.variants()]
@staticmethod
def _deq_aggregate(ledger: list[dict]) -> dict:
result = {"trade_count": len(ledger), "fields": {}}
for field in fields(DEQPrimitives):
values = [row.get("deq", {}).get(field.name) for row in ledger]
available = [value for value in values if value is not None]
aggregate = {
"available_count": len(available),
"null_count": len(values) - len(available),
}
if available and all(isinstance(value, bool) for value in available):
aggregate.update(true_count=sum(available), false_count=len(available) - sum(available))
elif available:
aggregate.update(min=min(available), max=max(available), mean=sum(available) / len(available))
else:
aggregate["mean"] = None
result["fields"][field.name] = aggregate
return result

View file

@ -0,0 +1,235 @@
from __future__ import annotations
import hashlib
import json
from collections import Counter
from pathlib import Path
from uuid import UUID
from django.core.management.base import BaseCommand, CommandError
from django.db import connection, transaction
from control_plane.trading_studio.management.commands.export_cohort_deq_v1 import Command as DEQExport
CONTRACT = "Cohort001-failure-context-export-v1"
LABEL_VERSION = "v1"
# All signed DEQ values are already expressed in the trade direction.
THRESHOLDS = {
"good_entry_return_1_bars_bps_min": 5.0,
"wrong_direction_return_1_bars_bps_max": -5.0,
"late_signal_return_1_bars_bps_max": 0.0,
"late_signal_return_5_bars_bps_min": 5.0,
"high_mae_entry_bps_max": -25.0,
"recovery_dependent_bars_min": 3,
}
LABEL_PRECEDENCE = (
"GOOD_SIGNAL_BAD_MONETIZATION",
"RECOVERY_DEPENDENT",
"HIGH_MAE_ENTRY",
"WRONG_DIRECTION",
"LATE_SIGNAL",
"GOOD_ENTRY",
"NO_EDGE",
)
class Command(BaseCommand):
help = "Read-only Parquet export of Cohort001 DEQ failure context and analytical labels."
def add_arguments(self, parser):
parser.add_argument("--cohort-id", required=True, type=UUID)
parser.add_argument(
"--output-dir",
required=True,
help="Directory for cohort_failure_context_v1.parquet and its manifest.",
)
parser.add_argument("--parquet-output", help="Optional explicit Parquet output path.")
parser.add_argument("--manifest-output", help="Optional explicit manifest output path.")
def handle(self, *args, **options):
output_dir = Path(options["output_dir"])
parquet_path = Path(options["parquet_output"] or output_dir / "cohort_failure_context_v1.parquet")
manifest_path = Path(
options["manifest_output"] or output_dir / "cohort_failure_context_v1.manifest.json"
)
try:
with transaction.atomic():
self._set_read_only_transaction()
payload = DEQExport._payload(options["cohort_id"])
rows = self._rows(payload)
self._write_parquet(rows, parquet_path)
manifest = self._manifest(payload, rows, parquet_path)
manifest_path.parent.mkdir(parents=True, exist_ok=True)
manifest_path.write_text(
json.dumps(manifest, indent=2, sort_keys=True, ensure_ascii=True) + "\n",
encoding="utf-8",
)
except (ImportError, OSError, ValueError) as error:
raise CommandError(str(error)) from error
self.stdout.write(
json.dumps(
{
"cohort_id": str(options["cohort_id"]),
"parquet": str(parquet_path),
"manifest": str(manifest_path),
"database_writes": False,
},
sort_keys=True,
)
)
@staticmethod
def _set_read_only_transaction() -> None:
"""Use the database's explicit read-only mode where it is supported."""
if connection.vendor == "postgresql":
# This must be the first database statement in the atomic block.
with connection.cursor() as cursor:
cursor.execute("SET TRANSACTION READ ONLY")
@classmethod
def _rows(cls, payload: dict) -> list[dict]:
rows = []
for strategy in payload["strategies"]:
for fold in strategy["folds"]:
for scenario in fold["scenarios"]:
rescue = scenario["summary"].get("rescue")
for sequence, ledger in enumerate(scenario["deq_samples"]):
deq = ledger.get("deq", {})
label, rationale = cls._label(ledger, deq)
rows.append(
{
"cohort_id": payload["cohort_id"],
"dataset_version_id": payload["dataset_version_id"],
"dataset_sha256": payload["dataset_sha256"],
"ordinal": strategy["ordinal"],
"membership_id": strategy["membership_id"],
"strategy_version_id": strategy["strategy_version_id"],
"runner_name": strategy["runner_name"],
"qualification_run_id": scenario["qualification_run_id"],
"fold": fold["fold"],
"scenario": scenario["scenario"],
"ledger_sequence": sequence,
"ledger_identity": ledger.get("identity"),
"direction": cls._direction(ledger),
"signal_bar": ledger.get("signal_bar"),
"entry_bar": ledger.get("entry_bar"),
"entry_at": ledger.get("entry_at"),
"entry_reference_price": ledger.get("entry_reference_price"),
"entry_execution_price": ledger.get("entry_execution_price"),
"net_pnl": ledger.get("net_pnl"),
"gross_pnl": ledger.get("gross_pnl"),
"failure_label": label,
"label_rationale": rationale,
"label_version": LABEL_VERSION,
"return_1_bars_bps": deq.get("return_1_bars_bps"),
"return_5_bars_bps": deq.get("return_5_bars_bps"),
"mfe_bps": deq.get("mfe_bps"),
"mae_bps": deq.get("mae_bps"),
"winner_negative_first": deq.get("winner_negative_first"),
"recovery_bars": deq.get("recovery_bars"),
# JSON columns preserve every raw ledger, genome, context, and rescue field.
"raw_deq_json": cls._canonical(deq),
"raw_ledger_json": cls._canonical(ledger),
"genome_json": cls._canonical(strategy["genome"]),
"feature_triples_json": cls._canonical(strategy["feature_triples"]),
"run_summary_json": cls._canonical(scenario["summary"]),
"rescue_json": cls._canonical(rescue),
"context_json": cls._canonical(ledger.get("context")),
}
)
return rows
@staticmethod
def _direction(ledger: dict) -> str | None:
take_profit = ledger.get("take_profit_price")
entry = ledger.get("entry_execution_price")
if take_profit is None or entry is None:
return None
return "LONG" if take_profit > entry else "SHORT" if take_profit < entry else None
@staticmethod
def _label(ledger: dict, deq: dict) -> tuple[str, str]:
r1 = deq.get("return_1_bars_bps")
r5 = deq.get("return_5_bars_bps")
mae = deq.get("mae_bps")
recovery = deq.get("recovery_bars")
net_pnl = ledger.get("net_pnl")
good_signal = r1 is not None and r1 >= THRESHOLDS["good_entry_return_1_bars_bps_min"]
if good_signal and net_pnl is not None and net_pnl <= 0:
return "GOOD_SIGNAL_BAD_MONETIZATION", "return_1_bars_bps >= 5 and net_pnl <= 0"
if net_pnl is not None and net_pnl > 0 and (
deq.get("winner_negative_first") is True
or (recovery is not None and recovery >= THRESHOLDS["recovery_dependent_bars_min"])
):
return "RECOVERY_DEPENDENT", "profitable after negative-first path or recovery >= 3 bars"
if mae is not None and mae <= THRESHOLDS["high_mae_entry_bps_max"]:
return "HIGH_MAE_ENTRY", "mae_bps <= -25"
if r1 is not None and r1 <= THRESHOLDS["wrong_direction_return_1_bars_bps_max"]:
return "WRONG_DIRECTION", "return_1_bars_bps <= -5"
if (
r1 is not None
and r5 is not None
and r1 <= THRESHOLDS["late_signal_return_1_bars_bps_max"]
and r5 >= THRESHOLDS["late_signal_return_5_bars_bps_min"]
):
return "LATE_SIGNAL", "return_1_bars_bps <= 0 and return_5_bars_bps >= 5"
if good_signal:
return "GOOD_ENTRY", "return_1_bars_bps >= 5"
return "NO_EDGE", "no preceding label rule matched"
@staticmethod
def _canonical(value) -> str:
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
@classmethod
def _write_parquet(cls, rows: list[dict], path: Path) -> None:
try:
import pyarrow as pa
import pyarrow.parquet as pq
except ImportError as error:
raise ImportError("pyarrow is required to write the Parquet export.") from error
path.parent.mkdir(parents=True, exist_ok=True)
pq.write_table(pa.Table.from_pylist(rows), path, compression="zstd")
@classmethod
def _manifest(cls, payload: dict, rows: list[dict], parquet_path: Path) -> dict:
labels = Counter(row["failure_label"] for row in rows)
return {
"contract": CONTRACT,
"cohort_id": payload["cohort_id"],
"dataset_version_id": payload["dataset_version_id"],
"dataset_sha256": payload["dataset_sha256"],
"source_contract": payload["contract"],
"allowed_runner_names": payload["allowed_runner_names"],
"row_count": len(rows),
"label_counts": {label: labels.get(label, 0) for label in LABEL_PRECEDENCE},
"label_version": LABEL_VERSION,
"label_precedence": list(LABEL_PRECEDENCE),
"thresholds": THRESHOLDS,
"parquet": {
"path": str(parquet_path),
"sha256": cls._file_hash(parquet_path),
},
"provenance": {
"source": "persisted QualificationReplayRun and QualificationReplayLedger records",
"replay_mode": payload["replay_mode"],
"raw_fields": [
"raw_deq_json", "raw_ledger_json", "genome_json", "feature_triples_json",
"run_summary_json", "rescue_json", "context_json",
],
},
"no_write_confirmation": {
"database_writes": False,
"read_only_transaction": True,
"note": "Only the requested Parquet and manifest artifact paths were written.",
},
}
@staticmethod
def _file_hash(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as artifact:
for block in iter(lambda: artifact.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,21 @@
# GPU Batch01 Independent Review V1.2
Decision: `REJECT`
## Exact Blockers
1. All 97 features do not pass validation: calibration 94/97, holdout 46/97, and adversarial 67/97.
2. Supertrend ATR limits were frozen as zero by the runner, not at the approved bounded values. The period-10 ATR calibration drift is `7.105427357601002e-15`; the holdout drift is `1.4210854715202004e-14`.
3. Calibration-derived continuous limits do not generalize: holdout fails 48 Bollinger/Keltner requests plus three Supertrend ATR requests; adversarial fails all 30 Keltner requests.
4. V1.2 contains no performance artifact or complete runtime provenance.
5. ATR validation does not require ATR shape and NaN-mask equality before measuring finite errors.
## Satisfied Checks
- The contract contains 97 unique requests and was frozen before holdout/adversarial validation.
- Reported finite numeric limits are mechanically enforced.
- Donchian and PSAR outputs pass exact checks on supplied evidence.
- Supertrend bands, predicates, direction, transitions, and final outputs pass exact checks.
- Historical role semantics are honestly marked `UNVERIFIABLE_FROM_RECOVERED_SOURCE` and non-blocking.
`GPU_FEATURE_PARITY_CONTRACT_V1_2_VALIDATED` must not be issued.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
{"artifact":"GPU_BATCH01_PERFORMANCE_METRICS_V1_1","families":[{"cold":{"compute_seconds":0.003065667988266796,"device_to_host_seconds":0.0006752620101906359,"host_to_device_seconds":0.00010705000022426248,"max_gpu_memory_bytes":48369152},"cold_values_per_second":760931715.0220335,"family":"bollinger","values":2332764,"variants":36,"warm":{"compute_seconds":0.0030614440329372883,"device_to_host_seconds":0.0006707339780405164,"host_to_device_seconds":7.96440290287137e-05,"max_gpu_memory_bytes":48369152},"warm_values_per_second":761981592.6413786},{"cold":{"compute_seconds":0.00028294604271650314,"device_to_host_seconds":0.0001819260069169104,"host_to_device_seconds":7.823598571121693e-05,"max_gpu_memory_bytes":7779840},"cold_values_per_second":2290153959.316022,"family":"donchian","values":647990,"variants":10,"warm":{"compute_seconds":0.0002682899939827621,"device_to_host_seconds":0.00018216704484075308,"host_to_device_seconds":7.761199958622456e-05,"max_gpu_memory_bytes":7779840},"warm_values_per_second":2415259661.311238},{"cold":{"compute_seconds":30.859290324966423,"device_to_host_seconds":0.0008443809929303825,"host_to_device_seconds":7.618800736963749e-05,"max_gpu_memory_bytes":27707904},"cold_values_per_second":75593.57248448124,"family":"keltner","values":2332764,"variants":36,"warm":{"compute_seconds":30.80078762996709,"device_to_host_seconds":0.0007362319738604128,"host_to_device_seconds":0.00010204297723248601,"max_gpu_memory_bytes":27707904},"warm_values_per_second":75737.1541281749},{"cold":{"compute_seconds":9.649663921969477,"device_to_host_seconds":0.00011956499656662345,"host_to_device_seconds":0.00010370800737291574,"max_gpu_memory_bytes":4148736},"cold_values_per_second":20145.468440347915,"family":"psar","values":194397,"variants":3,"warm":{"compute_seconds":9.652134977979586,"device_to_host_seconds":0.00010578898945823312,"host_to_device_seconds":9.908498032018542e-05,"max_gpu_memory_bytes":4148736},"warm_values_per_second":20140.310971976458},{"cold":{"compute_seconds":34.126689486030955,"device_to_host_seconds":0.00030309800058603287,"host_to_device_seconds":9.794998914003372e-05,"max_gpu_memory_bytes":12444672},"cold_values_per_second":22785.333465125277,"family":"supertrend","values":777588,"variants":12,"warm":{"compute_seconds":34.110405425017234,"device_to_host_seconds":0.0002850039745680988,"host_to_device_seconds":0.00011255801655352116,"max_gpu_memory_bytes":12444672},"warm_values_per_second":22796.211018638372}],"measurement":"CUDA synchronize before each interval; cold is first family invocation, warm is second"}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"artifact":"GPU_BATCH01_V1_1_EVIDENCE_MANIFEST","calibration_gpu_timing":{"compute_seconds":149.54003936197842,"device_to_host_seconds":0.7904343790141866,"host_to_device_seconds":0.1820402479788754,"max_gpu_memory_bytes":114601472},"cuda":"12.8","data_hashes":{"adversarial_csv":"20036fe14201f5e68f932edeb17189e54e875a13f948057cbe06900cadd7dc1c","calibration_csv":"7fb56bb05b0edc2348407765cd693efadf334307c25877dfb52acfe4221e5d00","holdout_csv":"2eef64de206e9ec908b8d7042f58cd4fcf22bfa0b99b856d27246e2432281904","request_json":"7ca0c8578dd81c3ae60898d83e6e73f9bf8889a39a873b451c7cdaf036cbaf24"},"gpu":"NVIDIA GB10","image_env":"artifex/gpu-feature-v1:v1_1","implementation_hashes":{"gpu_batch01_v1_1_runner.py":"c105954199d753aa4f404ca595b1d106e3f2647aad3725fad8b9274b61a881f7","gpu_feature_engine_v1.py":"1d5033b3ae078f9cd5ebe7e51f6ea4bd82d39737229425137370388fb6a205a9","gpu_feature_parity_contract_v1_1.py":"ad78e5f6200393540a45293535937ebfb2769a3474e6f207b867045c42ce477a"},"platform":"Linux-6.17.0-1026-nvidia-aarch64-with-glibc2.35","python":"3.10.12 (main, Jun 22 2026, 18:55:27) [GCC 11.4.0]","role_semantics":{"reason":"Batch01 request records contain no strategy-role assignment; provide --role-usage-artifact generated from historical lineage.","status":"not_reconstructable"},"schema_version":"1.1","source_hashes":{"historical_band_channel.py":"1f019058049ef4717edce14e8a9af11e74ec1f1ab2d12d26a938449017930d91"},"torch":"2.7.1+cu128"}

372
gpu_batch01_v1_1_runner.py Normal file
View file

@ -0,0 +1,372 @@
"""Reproducible GPU Batch01 V1.1 evidence runner for the ARM64 CUDA image.
The runner deliberately evaluates the CPU historical port and the CUDA engine
separately. CUDA unavailability is an error: GPU results are never replaced by
CPU results.
"""
from __future__ import annotations
import argparse
import csv
import hashlib
import json
import os
import platform
import sys
import time
from collections.abc import Mapping
from pathlib import Path
from typing import Any
import numpy as np
import torch
from gpu_feature_engine_v1 import evaluate_batch, psar_trace, supertrend_trace
from gpu_feature_parity_contract_v1_1 import (
calibrate,
canonical_bytes,
compare_stateful_trace,
corpus_manifest,
cpu_psar_trace,
cpu_supertrend_trace,
deterministic_adversarial_ohlcv,
freeze_contract,
historical_cpu_oracle,
nan_gap_semantics_manifest,
validate_frozen_contract,
)
ARTIFACT_PREFIX = "gpu_batch01_v1_1"
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def write_json(path: Path, payload: Mapping[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(canonical_bytes(dict(payload)) + b"\n")
def read_ohlcv(path: Path) -> dict[str, np.ndarray]:
with path.open(newline="", encoding="utf-8") as handle:
rows = list(csv.DictReader(handle))
required = ("close", "high", "low", "volume")
if not rows or not set(required).issubset(rows[0]):
raise ValueError("CSV must contain non-empty close, high, low, volume columns")
values = {
name: np.asarray([float(row[name]) for row in rows], dtype=np.float64) for name in required
}
if not all(np.isfinite(value).all() for value in values.values()):
raise ValueError(
"historical NaN/gap semantics are undefined; non-finite OHLCV inputs are rejected"
)
return values
def write_ohlcv_csv(path: Path, ohlcv: Mapping[str, np.ndarray]) -> None:
with path.open("w", newline="", encoding="utf-8") as handle:
writer = csv.DictWriter(handle, fieldnames=["close", "high", "low", "volume"])
writer.writeheader()
writer.writerows(
{name: float(ohlcv[name][index]) for name in writer.fieldnames}
for index in range(len(ohlcv["close"]))
)
def gpu_outputs(
request: Mapping[str, Any], ohlcv: Mapping[str, np.ndarray], device: torch.device
) -> tuple[dict[str, np.ndarray], dict[str, float]]:
torch.cuda.reset_peak_memory_stats(device)
transfer_start = time.perf_counter()
tensors = tuple(
torch.as_tensor(ohlcv[name], dtype=torch.float64, device=device)
for name in ("close", "high", "low", "volume")
)
torch.cuda.synchronize(device)
transfer_seconds = time.perf_counter() - transfer_start
compute_start = time.perf_counter()
outputs = evaluate_batch(dict(request), *tensors)
torch.cuda.synchronize(device)
compute_seconds = time.perf_counter() - compute_start
host_start = time.perf_counter()
result = {key: value.detach().cpu().numpy() for key, value in outputs.items()}
torch.cuda.synchronize(device)
return result, {
"host_to_device_seconds": transfer_seconds,
"compute_seconds": compute_seconds,
"device_to_host_seconds": time.perf_counter() - host_start,
"max_gpu_memory_bytes": int(torch.cuda.max_memory_allocated(device)),
}
def traces(
requests: list[Mapping[str, Any]], ohlcv: Mapping[str, np.ndarray], device: torch.device
) -> dict[str, Any]:
close, high, low = (ohlcv[name] for name in ("close", "high", "low"))
gpu_values = tuple(
torch.as_tensor(value, dtype=torch.float64, device=device) for value in (close, high, low)
)
records = []
for item in requests:
indicator = int(item["indicator_id"])
if indicator == 19:
expected = cpu_supertrend_trace(
close, high, low, int(item["period"]), float(item["p1"])
)
actual = supertrend_trace(*gpu_values, int(item["period"]), float(item["p1"]))
trace_type = "supertrend"
elif indicator == 28:
expected = cpu_psar_trace(close, high, low, float(item["p1"]))
actual = psar_trace(*gpu_values, float(item["p1"]))
trace_type = "psar"
else:
continue
torch.cuda.synchronize(device)
compared = compare_stateful_trace(
expected,
{key: value.detach().cpu().numpy() for key, value in actual.items()},
trace_type=trace_type,
)
records.append({"request_id": str(item["request_id"]), "family": trace_type, **compared})
return {
"artifact": "GPU_BATCH01_STATEFUL_TRACE_COMPARISON_V1_1",
"passed": all(item["exact"] for item in records),
"records": records,
}
def performance(
request: Mapping[str, Any], ohlcv: Mapping[str, np.ndarray], device: torch.device
) -> dict[str, Any]:
# Each family is measured through the same CUDA evaluate_batch entry point.
families: dict[str, list[Mapping[str, Any]]] = {}
names = {
17: "bollinger",
18: "bollinger",
19: "supertrend",
20: "donchian",
21: "donchian",
23: "keltner",
24: "keltner",
28: "psar",
}
for item in request["requests"]:
families.setdefault(names[int(item["indicator_id"])], []).append(item)
results = []
for family, items in sorted(families.items()):
family_request = {"requests": items}
_, cold = gpu_outputs(family_request, ohlcv, device)
_, warm = gpu_outputs(family_request, ohlcv, device)
values = len(ohlcv["close"]) * len(items)
results.append(
{
"family": family,
"variants": len(items),
"values": values,
"cold": cold,
"warm": warm,
"cold_values_per_second": values / cold["compute_seconds"]
if cold["compute_seconds"]
else None,
"warm_values_per_second": values / warm["compute_seconds"]
if warm["compute_seconds"]
else None,
}
)
return {
"artifact": "GPU_BATCH01_PERFORMANCE_METRICS_V1_1",
"measurement": (
"CUDA synchronize before each interval; cold is first family invocation, warm is second"
),
"families": results,
}
def role_semantics(path: Path | None) -> dict[str, Any]:
if path is None:
return {
"status": "not_reconstructable",
"reason": "Batch01 request records contain no strategy-role assignment; provide "
"--role-usage-artifact generated from historical lineage.",
}
if not path.is_file():
raise ValueError(f"role usage artifact does not exist: {path}")
return {"status": "source_artifact_recorded", "path": str(path), "sha256": sha256_file(path)}
def implementation_hashes() -> dict[str, str]:
root = Path(__file__).resolve().parent
return {
path.name: sha256_file(path)
for path in (
root / "gpu_batch01_v1_1_runner.py",
root / "gpu_feature_engine_v1.py",
root / "gpu_feature_parity_contract_v1_1.py",
)
}
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--calibration-csv", type=Path, required=True)
parser.add_argument("--holdout-csv", type=Path, required=True)
parser.add_argument("--request-json", type=Path, required=True)
parser.add_argument("--output-dir", type=Path, required=True)
parser.add_argument("--adversarial-length", type=int, default=256)
parser.add_argument("--seed", type=int, default=0)
parser.add_argument("--role-usage-artifact", type=Path)
parser.add_argument("--image-env", default=os.getenv("GPU_BATCH01_IMAGE_ENV"))
args = parser.parse_args()
if not torch.cuda.is_available():
raise SystemExit("CUDA GPU is required; no CPU fallback is available")
args.output_dir.mkdir(parents=True, exist_ok=True)
request = json.loads(args.request_json.read_text(encoding="utf-8"))
requests = request.get("requests")
if not isinstance(requests, list) or not requests:
raise ValueError("request JSON must contain a non-empty requests list")
device = torch.device("cuda")
# Calibration is the only stage allowed to derive and freeze acceptance limits.
calibration_ohlcv = read_ohlcv(args.calibration_csv)
calibration_cpu = historical_cpu_oracle(args.calibration_csv, requests)
calibration_gpu, calibration_timing = gpu_outputs(request, calibration_ohlcv, device)
calibration_manifest = corpus_manifest(
{"calibration": calibration_ohlcv},
{
"source_csv": str(args.calibration_csv),
"source_sha256": sha256_file(args.calibration_csv),
},
)
calibration_evidence = calibrate(
calibration_gpu,
calibration_cpu["outputs"],
requests,
calibration_ohlcv["close"],
calibration_manifest,
)
contract = freeze_contract(calibration_evidence)
write_json(args.output_dir / "gpu_feature_parity_calibration_v1_1.json", calibration_evidence)
write_json(args.output_dir / "gpu_feature_parity_contract_v1_1.json", contract)
np.savez_compressed(
args.output_dir / f"{ARTIFACT_PREFIX}_calibration_cpu_oracle.npz",
**calibration_cpu["outputs"],
)
np.savez_compressed(
args.output_dir / f"{ARTIFACT_PREFIX}_calibration_gpu_outputs.npz", **calibration_gpu
)
adversarial = deterministic_adversarial_ohlcv(length=args.adversarial_length, seed=args.seed)
adversarial_csv = args.output_dir / f"{ARTIFACT_PREFIX}_adversarial.csv"
write_ohlcv_csv(adversarial_csv, adversarial)
corpora = {
"calibration": calibration_ohlcv,
"holdout": read_ohlcv(args.holdout_csv),
"adversarial": adversarial,
}
corpus = corpus_manifest(
corpora,
{
"adversarial_generator": "deterministic_adversarial_ohlcv",
"seed": args.seed,
"role_semantics": role_semantics(args.role_usage_artifact),
},
)
write_json(args.output_dir / "gpu_feature_parity_corpus_manifest_v1_1.json", corpus)
write_json(
args.output_dir / "gpu_feature_parity_nan_gap_semantics_v1_1.json",
nan_gap_semantics_manifest(),
)
validations, trace_reports = [], []
for name in ("calibration", "holdout", "adversarial"):
if name == "calibration":
csv_path, cpu, gpu, timing = (
args.calibration_csv,
calibration_cpu,
calibration_gpu,
calibration_timing,
)
else:
csv_path = args.holdout_csv if name == "holdout" else adversarial_csv
cpu = historical_cpu_oracle(csv_path, requests)
gpu, timing = gpu_outputs(request, corpora[name], device)
validation = validate_frozen_contract(contract, gpu, cpu["outputs"], corpora[name]["close"])
validation.update(
{
"corpus": name,
"cpu_oracle": {key: value for key, value in cpu.items() if key != "outputs"},
"gpu_timing": timing,
}
)
validations.append(validation)
trace_reports.append({"corpus": name, **traces(requests, corpora[name], device)})
if name != "calibration":
np.savez_compressed(
args.output_dir / f"{ARTIFACT_PREFIX}_{name}_cpu_oracle.npz", **cpu["outputs"]
)
np.savez_compressed(
args.output_dir / f"{ARTIFACT_PREFIX}_{name}_gpu_outputs.npz", **gpu
)
validation_payload = {
"artifact": "GPU_FEATURE_PARITY_VALIDATION_REPORT_V1_1",
"contract_sha256": hashlib.sha256(canonical_bytes(contract)).hexdigest(),
"passed": all(value["passed"] for value in validations),
"corpora": validations,
}
trace_payload = {
"artifact": "GPU_BATCH01_STATEFUL_TRACE_REPORT_V1_1",
"passed": all(value["passed"] for value in trace_reports),
"corpora": trace_reports,
}
write_json(args.output_dir / "gpu_feature_parity_validation_v1_1.json", validation_payload)
write_json(args.output_dir / "gpu_batch01_stateful_trace_comparison_v1_1.json", trace_payload)
write_json(
args.output_dir / "gpu_batch01_performance_metrics_v1_1.json",
performance(request, corpora["holdout"], device),
)
root = Path(__file__).resolve().parent
manifest = {
"artifact": "GPU_BATCH01_V1_1_EVIDENCE_MANIFEST",
"schema_version": "1.1",
"python": sys.version,
"platform": platform.platform(),
"torch": torch.__version__,
"cuda": torch.version.cuda,
"gpu": torch.cuda.get_device_properties(device).name,
"image_env": args.image_env,
"source_hashes": {
"historical_band_channel.py": sha256_file(
root / "control_plane/trading_studio/indicators/historical_band_channel.py"
)
},
"implementation_hashes": implementation_hashes(),
"data_hashes": {
"request_json": sha256_file(args.request_json),
"calibration_csv": sha256_file(args.calibration_csv),
"holdout_csv": sha256_file(args.holdout_csv),
"adversarial_csv": sha256_file(adversarial_csv),
},
"calibration_gpu_timing": calibration_timing,
"role_semantics": role_semantics(args.role_usage_artifact),
}
write_json(args.output_dir / "gpu_batch01_v1_1_evidence_manifest.json", manifest)
print(
json.dumps(
{
"output_dir": str(args.output_dir),
"validation_passed": validation_payload["passed"],
"trace_passed": trace_payload["passed"],
},
allow_nan=False,
)
)
if __name__ == "__main__":
main()

View file

@ -0,0 +1 @@
{"artifact":"GPU_BATCH01_V1_2_EVIDENCE_MANIFEST","artifacts":["gpu_feature_parity_calibration_v1_2.json","gpu_feature_parity_contract_v1_2.json","gpu_feature_parity_contract_v1_2.md","gpu_feature_parity_validation_calibration_v1_2.json","gpu_feature_parity_validation_holdout_v1_2.json","gpu_feature_parity_validation_adversarial_v1_2.json","gpu_feature_parity_role_surface_v1_2.json","gpu_feature_parity_review_template_v1_2.json"],"atr_limits":{"mae":0.0,"max_absolute_error":0.0},"contract_sha256":"8f8ee3459b54d7674b5f946c8030ca21ad5b2b2691b0446f33616da3917df47f","feature_count":97,"schema_version":"1.2","status":"evidence_generated_not_validated","validation_passed":false}

203
gpu_batch01_v1_2_runner.py Normal file
View file

@ -0,0 +1,203 @@
"""GPU Batch01 V1.2 full-family, frozen-contract evidence runner."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
import platform
import sys
import time
from pathlib import Path
from typing import Any, Mapping
import numpy as np
import torch
from control_plane.trading_studio.indicators.historical_band_channel import evaluate_band_channel
from gpu_batch01_v1_1_runner import read_ohlcv, write_ohlcv_csv
from gpu_feature_engine_v1 import evaluate_batch as evaluate_v1_batch
from gpu_feature_engine_v1_2 import supertrend_trace
from gpu_feature_parity_contract_v1_1 import deterministic_adversarial_ohlcv
from gpu_feature_parity_contract_v1_2 import (
CALIBRATION_ARTIFACT,
calibrate_output,
calibrate_supertrend,
canonical_bytes,
freeze_contract,
review_template,
role_surface,
validate_all_frozen_contract,
)
ARTIFACT_PREFIX = "gpu_batch01_v1_2"
ATR_LIMIT_KEYS = ("max_absolute_error", "mae")
DEFAULT_ATR_LIMITS = {"max_absolute_error": 1.5e-14, "mae": 1.5e-14}
def write_json(path: Path, payload: Mapping[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(canonical_bytes(dict(payload)) + b"\n")
def _sha256_file(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def _atr_limits(atr_limits: Mapping[str, float] | None) -> dict[str, float]:
limits = dict(DEFAULT_ATR_LIMITS if atr_limits is None else atr_limits)
if set(limits) != set(ATR_LIMIT_KEYS) or any(not np.isfinite(value) or value < 0 for value in limits.values()):
raise ValueError("ATR limits must contain finite, non-negative max_absolute_error and mae")
return limits
def _cpu_outputs(requests: list[Mapping[str, Any]], ohlcv: Mapping[str, np.ndarray]) -> dict[str, np.ndarray]:
return {
str(item["request_id"]): evaluate_band_channel(
int(item["indicator_id"]), ohlcv["close"], ohlcv["high"], ohlcv["low"], ohlcv["volume"], int(item["period"]), float(item["p1"])
)
for item in requests
}
def _gpu_outputs(requests: list[Mapping[str, Any]], ohlcv: Mapping[str, np.ndarray], device: torch.device) -> tuple[dict[str, np.ndarray], dict[str, tuple[dict[str, np.ndarray], dict[str, np.ndarray]]]]:
tensors = tuple(torch.as_tensor(ohlcv[name], dtype=torch.float64, device=device) for name in ("close", "high", "low", "volume"))
other = [item for item in requests if int(item["indicator_id"]) != 19]
outputs = evaluate_v1_batch({"requests": other}, *tensors) if other else {}
traces = {}
for item in requests:
if int(item["indicator_id"]) != 19:
continue
key = str(item["request_id"])
actual = supertrend_trace(*tensors[:3], int(item["period"]), float(item["p1"]))
# The CPU trace is imported lazily to keep historical output generation explicit.
from gpu_feature_parity_contract_v1_2 import cpu_supertrend_trace
expected = cpu_supertrend_trace(ohlcv["close"], ohlcv["high"], ohlcv["low"], int(item["period"]), float(item["p1"]))
traces[key] = (expected, {name: value.detach().cpu().numpy() for name, value in actual.items()})
outputs[key] = actual["output"]
torch.cuda.synchronize(device)
return {key: value.detach().cpu().numpy() for key, value in outputs.items()}, traces
def _gpu_workload(requests: list[Mapping[str, Any]], ohlcv: Mapping[str, np.ndarray], device: torch.device) -> None:
tensors = tuple(torch.as_tensor(ohlcv[name], dtype=torch.float64, device=device) for name in ("close", "high", "low", "volume"))
other = [item for item in requests if int(item["indicator_id"]) != 19]
if other:
evaluate_v1_batch({"requests": other}, *tensors)
for item in requests:
if int(item["indicator_id"]) == 19:
supertrend_trace(*tensors[:3], int(item["period"]), float(item["p1"]))
def performance(requests: list[Mapping[str, Any]], ohlcv: Mapping[str, np.ndarray], device: torch.device) -> dict[str, Any]:
"""Measure V1.2 GPU evaluation with synchronized intervals."""
families: dict[str, list[Mapping[str, Any]]] = {}
for item in requests:
family = {17: "bollinger", 18: "bollinger", 19: "supertrend", 20: "donchian", 21: "donchian", 23: "keltner", 24: "keltner", 28: "psar"}[int(item["indicator_id"])]
families.setdefault(family, []).append(item)
results = []
for family, items in sorted(families.items()):
timings = []
for _ in range(2):
torch.cuda.synchronize(device)
started = time.perf_counter()
_gpu_workload(items, ohlcv, device)
torch.cuda.synchronize(device)
timings.append(time.perf_counter() - started)
values = len(ohlcv["close"]) * len(items)
results.append({"family": family, "variants": len(items), "values": values, "cold_seconds": timings[0], "warm_seconds": timings[1], "cold_values_per_second": values / timings[0] if timings[0] else None, "warm_values_per_second": values / timings[1] if timings[1] else None})
return {"artifact": "GPU_BATCH01_PERFORMANCE_METRICS_V1_2", "schema_version": "1.2", "measurement": "CUDA synchronize before and after each interval; cold is the first family invocation and warm is the second", "families": results}
def _markdown(contract: Mapping[str, Any], roles: Mapping[str, Any]) -> str:
counts: dict[str, int] = {}
for limit in contract["feature_limits"].values():
counts[limit["family"]] = counts.get(limit["family"], 0) + 1
return "\n".join((
"# GPU Batch01 V1.2 Frozen Validation Contract", "",
"All 97 observed Batch01 outputs are evaluated against `historical_band_channel`.",
"", "## Evidence Classes", "",
"- Donchian and PSAR outputs: exact.",
"- Bollinger and Keltner outputs: bounded by calibration-only limits.",
"- Supertrend final output, bands, direction, transitions, and branch predicates: exact; ATR uses supplied fixed bounds.",
"", "## Frozen Coverage", "",
*[f"- `{family}`: {count}" for family, count in sorted(counts.items())],
"", "## Role Status", "",
"Unrecovered strategy roles are `UNVERIFIABLE_FROM_RECOVERED_SOURCE`; they are recorded but are not parity blockers.",
f"Role records: {len(roles['records'])}.", "",
))
def run(calibration_csv: Path, holdout_csv: Path, request_json: Path, output_dir: Path, *, adversarial_length: int = 256, seed: int = 0, atr_limits: Mapping[str, float] | None = None, device: torch.device | None = None) -> dict[str, Any]:
if device is None:
if not torch.cuda.is_available():
raise RuntimeError("CUDA GPU is required; no CPU fallback is available")
device = torch.device("cuda")
atr_limits = _atr_limits(atr_limits)
request = json.loads(request_json.read_text(encoding="utf-8"))
requests = request.get("requests")
if not isinstance(requests, list) or len(requests) != 97:
raise ValueError("V1.2 requires the complete 97-request Batch01 manifest")
if len({str(item["request_id"]) for item in requests}) != 97:
raise ValueError("Batch01 request IDs must be unique")
output_dir.mkdir(parents=True, exist_ok=True)
calibration, holdout = read_ohlcv(calibration_csv), read_ohlcv(holdout_csv)
adversarial = deterministic_adversarial_ohlcv(length=adversarial_length, seed=seed)
write_ohlcv_csv(output_dir / f"{ARTIFACT_PREFIX}_adversarial.csv", adversarial)
roles = role_surface(requests)
write_json(output_dir / "gpu_feature_parity_role_surface_v1_2.json", roles)
# Calibration is the sole point at which continuous-family limits are derived.
calibration_cpu = _cpu_outputs(requests, calibration)
calibration_gpu, calibration_traces = _gpu_outputs(requests, calibration, device)
evidence = {"artifact": CALIBRATION_ARTIFACT, "schema_version": "1.2", "feature_count": 97, "records": [
calibrate_supertrend(str(item["request_id"]), *calibration_traces[str(item["request_id"])], atr_limits=atr_limits)
if int(item["indicator_id"]) == 19 else calibrate_output(str(item["request_id"]), int(item["indicator_id"]), calibration_cpu[str(item["request_id"])], calibration_gpu[str(item["request_id"])] )
for item in requests
]}
contract = freeze_contract(evidence) # Frozen before holdout/adversarial are evaluated.
write_json(output_dir / "gpu_feature_parity_calibration_v1_2.json", evidence)
write_json(output_dir / "gpu_feature_parity_contract_v1_2.json", contract)
(output_dir / "gpu_feature_parity_contract_v1_2.md").write_text(_markdown(contract, roles), encoding="utf-8")
np.savez_compressed(output_dir / f"{ARTIFACT_PREFIX}_calibration_cpu_oracle.npz", **calibration_cpu)
np.savez_compressed(output_dir / f"{ARTIFACT_PREFIX}_calibration_gpu_outputs.npz", **calibration_gpu)
reports = {}
for name, corpus in (("calibration", calibration), ("holdout", holdout), ("adversarial", adversarial)):
expected = calibration_cpu if name == "calibration" else _cpu_outputs(requests, corpus)
actual, traces = (calibration_gpu, calibration_traces) if name == "calibration" else _gpu_outputs(requests, corpus, device)
report = validate_all_frozen_contract(contract, expected, actual, traces)
report.update({"corpus": name, "feature_count": 97})
reports[name] = report
write_json(output_dir / f"gpu_feature_parity_validation_{name}_v1_2.json", report)
if name != "calibration":
np.savez_compressed(output_dir / f"{ARTIFACT_PREFIX}_{name}_cpu_oracle.npz", **expected)
np.savez_compressed(output_dir / f"{ARTIFACT_PREFIX}_{name}_gpu_outputs.npz", **actual)
review = review_template()
review["required_review"].extend(["all 97 Batch01 requests are represented", "role status remains UNVERIFIABLE_FROM_RECOVERED_SOURCE where lineage is unrecovered"])
write_json(output_dir / "gpu_feature_parity_review_template_v1_2.json", review)
write_json(output_dir / "gpu_batch01_performance_metrics_v1_2.json", performance(requests, holdout, device))
provenance = {"artifact": "GPU_BATCH01_RUNTIME_PROVENANCE_V1_2", "schema_version": "1.2", "python": sys.version, "platform": platform.platform(), "torch": torch.__version__, "cuda": torch.version.cuda, "gpu": torch.cuda.get_device_properties(device).name, "image_env": os.getenv("GPU_BATCH01_IMAGE_ENV"), "source_hashes": {path.name: _sha256_file(path) for path in (Path(__file__), Path(__file__).with_name("gpu_batch01_v1_1_runner.py"), Path(__file__).with_name("gpu_feature_engine_v1.py"), Path(__file__).with_name("gpu_feature_engine_v1_2.py"), Path(__file__).with_name("gpu_feature_parity_contract_v1_1.py"), Path(__file__).with_name("gpu_feature_parity_contract_v1_2.py"), Path(__file__).parent / "control_plane/trading_studio/indicators/historical_band_channel.py")}, "data_hashes": {"request_json": _sha256_file(request_json), "calibration_csv": _sha256_file(calibration_csv), "holdout_csv": _sha256_file(holdout_csv), "adversarial_csv": _sha256_file(output_dir / f"{ARTIFACT_PREFIX}_adversarial.csv")}, "adversarial": {"generator": "deterministic_adversarial_ohlcv", "length": adversarial_length, "seed": seed}}
write_json(output_dir / "gpu_batch01_runtime_provenance_v1_2.json", provenance)
artifacts = ["gpu_feature_parity_calibration_v1_2.json", "gpu_feature_parity_contract_v1_2.json", "gpu_feature_parity_contract_v1_2.md", "gpu_feature_parity_validation_calibration_v1_2.json", "gpu_feature_parity_validation_holdout_v1_2.json", "gpu_feature_parity_validation_adversarial_v1_2.json", "gpu_feature_parity_role_surface_v1_2.json", "gpu_feature_parity_review_template_v1_2.json", "gpu_batch01_performance_metrics_v1_2.json", "gpu_batch01_runtime_provenance_v1_2.json"]
manifest = {"artifact": "GPU_BATCH01_V1_2_EVIDENCE_MANIFEST", "schema_version": "1.2", "status": "evidence_generated_not_validated", "feature_count": 97, "contract_sha256": hashlib.sha256(canonical_bytes(contract)).hexdigest(), "atr_limits": atr_limits, "validation_passed": all(report["passed"] for report in reports.values()), "artifacts": artifacts}
write_json(output_dir / "gpu_batch01_v1_2_evidence_manifest.json", manifest)
return manifest
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--calibration-csv", type=Path, required=True)
parser.add_argument("--holdout-csv", type=Path, required=True)
parser.add_argument("--request-json", type=Path, required=True)
parser.add_argument("--output-dir", type=Path, required=True)
parser.add_argument("--adversarial-length", type=int, default=256)
parser.add_argument("--seed", type=int, default=0)
parser.add_argument("--supertrend-atr-max-absolute-error", type=float, default=DEFAULT_ATR_LIMITS["max_absolute_error"])
parser.add_argument("--supertrend-atr-mae", type=float, default=DEFAULT_ATR_LIMITS["mae"])
args = parser.parse_args()
print(json.dumps(run(args.calibration_csv, args.holdout_csv, args.request_json, args.output_dir, adversarial_length=args.adversarial_length, seed=args.seed, atr_limits={"max_absolute_error": args.supertrend_atr_max_absolute_error, "mae": args.supertrend_atr_mae}), allow_nan=False))
if __name__ == "__main__":
main()

430
gpu_feature_engine_v1.py Normal file
View file

@ -0,0 +1,430 @@
"""Standalone CUDA Batch01 band/channel prototype.
This module deliberately has no Artifex imports. It consumes frozen oracle
artifacts and OHLCV CSV files mounted read-only, writes only its output cache,
and records measured parity rather than asserting it.
"""
from __future__ import annotations
import argparse
import csv
import hashlib
import json
import os
import time
from pathlib import Path
from typing import Any
import numpy as np
import torch
def _sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def _load_ohlcv(path: Path, device: torch.device) -> tuple[torch.Tensor, ...]:
with path.open(newline="", encoding="utf-8") as handle:
rows = list(csv.DictReader(handle))
if not rows or not {"close", "high", "low", "volume"}.issubset(rows[0]):
raise ValueError("CSV must contain close, high, low, and volume columns")
return tuple(
torch.as_tensor([float(row[name]) for row in rows], dtype=torch.float64, device=device)
for name in ("close", "high", "low", "volume")
)
def _rolling_windows(values: torch.Tensor, period: int) -> torch.Tensor:
return values.unfold(0, period, 1)
def _empty(values: torch.Tensor) -> torch.Tensor:
return torch.full_like(values, float("nan"))
def _sma(values: torch.Tensor, period: int) -> torch.Tensor:
out = _empty(values)
if values.numel() < period:
return out
out[period - 1 :] = _rolling_windows(values, period).mean(dim=1)
return out
def _ema(values: torch.Tensor, period: int) -> torch.Tensor:
out = _empty(values)
if values.numel() < period:
return out
alpha = 2.0 / (period + 1)
out[period - 1] = values[:period].sum() / period
for index in range(period, values.numel()):
out[index] = alpha * values[index] + (1.0 - alpha) * out[index - 1]
return out
def _rma(values: torch.Tensor, period: int) -> torch.Tensor:
out = _empty(values)
valid = torch.nonzero(~torch.isnan(values), as_tuple=False).flatten()
if valid.numel() < period:
return out
start = int(valid[period - 1].item())
out[start] = values[valid[:period]].sum() / period
alpha = 1.0 / period
for index in range(start + 1, values.numel()):
out[index] = out[index - 1] if torch.isnan(values[index]) else alpha * values[index] + (1.0 - alpha) * out[index - 1]
return out
def _true_range(close: torch.Tensor, high: torch.Tensor, low: torch.Tensor) -> torch.Tensor:
out = torch.empty_like(close)
out[0] = high[0] - low[0]
out[1:] = torch.stack((high[1:] - low[1:], (high[1:] - close[:-1]).abs(), (low[1:] - close[:-1]).abs())).amax(dim=0)
return out
def _bollinger(close: torch.Tensor, period: int, multipliers: list[float]) -> tuple[torch.Tensor, torch.Tensor]:
upper, lower = _empty(close).repeat(len(multipliers), 1), _empty(close).repeat(len(multipliers), 1)
if close.numel() < period:
return upper, lower
windows = _rolling_windows(close, period)
mean = windows.mean(dim=1)
# Keep reductions on-device and vectorized; sample variance matches the oracle formula.
standard_deviation = ((windows - mean[:, None]).square().sum(dim=1) / (period - 1)).sqrt()
multiplier = torch.tensor(multipliers, dtype=torch.float64, device=close.device)
upper[:, period - 1 :] = mean[None, :] + multiplier[:, None] * standard_deviation[None, :]
lower[:, period - 1 :] = mean[None, :] - multiplier[:, None] * standard_deviation[None, :]
return upper, lower
def _donchian(values: torch.Tensor, period: int, upper: bool) -> torch.Tensor:
out = _empty(values)
if values.numel() >= period:
windows = _rolling_windows(values, period)
out[period - 1 :] = windows.amax(dim=1) if upper else windows.amin(dim=1)
return out
def _keltner(close: torch.Tensor, high: torch.Tensor, low: torch.Tensor, period: int, multipliers: list[float]) -> tuple[torch.Tensor, torch.Tensor]:
basis, atr = _ema(close, period), _rma(_true_range(close, high, low), period)
upper, lower = _empty(close).repeat(len(multipliers), 1), _empty(close).repeat(len(multipliers), 1)
valid = ~torch.isnan(basis) & ~torch.isnan(atr)
multiplier = torch.tensor(multipliers, dtype=torch.float64, device=close.device)
upper[:, valid] = basis[valid] + multiplier[:, None] * atr[valid]
lower[:, valid] = basis[valid] - multiplier[:, None] * atr[valid]
return upper, lower
def _supertrend(close: torch.Tensor, high: torch.Tensor, low: torch.Tensor, period: int, multipliers: list[float]) -> torch.Tensor:
count, length = len(multipliers), close.numel()
out = _empty(close).repeat(count, 1)
atr, upper, lower = _rma(_true_range(close, high, low), period), _empty(close).repeat(count), _empty(close).repeat(count)
multiplier = torch.tensor(multipliers, dtype=torch.float64, device=close.device)
direction = torch.ones(count, dtype=torch.float64, device=close.device)
for index in range(period, length):
midpoint, basic_upper, basic_lower = (high[index] + low[index]) / 2.0, None, None
basic_upper, basic_lower = midpoint + multiplier * atr[index], midpoint - multiplier * atr[index]
if index == period:
upper, lower = basic_upper, basic_lower
out[:, index] = torch.where(close[index] > lower, lower, upper)
direction = torch.where(close[index] > out[:, index], 1.0, -1.0)
continue
upper = torch.where((close[index - 1] <= upper) & ~torch.isnan(upper), torch.minimum(basic_upper, upper), basic_upper)
lower = torch.where((close[index - 1] >= lower) & ~torch.isnan(lower), torch.maximum(basic_lower, lower), basic_lower)
out[:, index] = torch.where(direction == 1.0, torch.where(close[index] >= lower, lower, upper), torch.where(close[index] <= upper, upper, lower))
direction = torch.where(close[index] > out[:, index], 1.0, -1.0)
return out
def _psar(close: torch.Tensor, high: torch.Tensor, low: torch.Tensor, steps: list[float]) -> torch.Tensor:
out = _empty(close).repeat(len(steps), 1)
if close.numel() < 2:
return out
step = torch.tensor(steps, dtype=torch.float64, device=close.device)
bullish = torch.ones(len(steps), dtype=torch.bool, device=close.device)
acceleration = torch.full_like(step, 0.02)
extreme, sar = high[0].repeat(len(steps)), low[0].repeat(len(steps))
for index in range(1, close.numel()):
sar = sar + acceleration * (extreme - sar)
bull_sar = torch.minimum(sar, low[index - 1])
bear_sar = torch.maximum(sar, high[index - 1])
if index >= 2:
bull_sar, bear_sar = torch.minimum(bull_sar, low[index - 2]), torch.maximum(bear_sar, high[index - 2])
reversal_down, reversal_up = bullish & (low[index] < bull_sar), ~bullish & (high[index] > bear_sar)
candidate = torch.where(bullish, bull_sar, bear_sar)
sar = torch.where(reversal_down | reversal_up, extreme, candidate)
new_bullish = torch.where(reversal_down, False, torch.where(reversal_up, True, bullish))
new_extreme = torch.where(reversal_down, low[index], torch.where(reversal_up, high[index], extreme))
rising = new_bullish & ~reversal_up & (high[index] > extreme)
falling = ~new_bullish & ~reversal_down & (low[index] < extreme)
new_extreme = torch.where(rising, high[index], torch.where(falling, low[index], new_extreme))
acceleration = torch.where(reversal_down | reversal_up, 0.02, torch.where(rising | falling, torch.minimum(acceleration + step, torch.full_like(step, 0.2)), acceleration))
bullish, extreme = new_bullish, new_extreme
out[:, index] = sar
return out
def supertrend_trace(close: torch.Tensor, high: torch.Tensor, low: torch.Tensor, period: int, multiplier: float) -> dict[str, torch.Tensor]:
"""Return the GPU state-machine trace for one Supertrend variant.
The bar loop advances state, while every state operation remains a CUDA
tensor operation; this is not a CPU or per-variant fallback.
"""
output = _supertrend(close, high, low, period, [multiplier])[0]
atr = _rma(_true_range(close, high, low), period)
upper, lower = _empty(close), _empty(close)
direction, initialized = torch.zeros_like(close, dtype=torch.int8), torch.zeros_like(close, dtype=torch.bool)
for index in range(period, close.numel()):
midpoint = (high[index] + low[index]) / 2.0
basic_upper, basic_lower = midpoint + multiplier * atr[index], midpoint - multiplier * atr[index]
if index == period:
upper[index], lower[index] = basic_upper, basic_lower
else:
upper[index] = torch.minimum(basic_upper, upper[index - 1]) if close[index - 1] <= upper[index - 1] else basic_upper
lower[index] = torch.maximum(basic_lower, lower[index - 1]) if close[index - 1] >= lower[index - 1] else basic_lower
direction[index] = torch.where(close[index] > output[index], 1, -1)
initialized[index] = True
return {"output": output, "atr": atr, "upper": upper, "lower": lower, "direction": direction, "initialized": initialized}
def psar_trace(close: torch.Tensor, high: torch.Tensor, low: torch.Tensor, step: float) -> dict[str, torch.Tensor]:
"""Return the GPU state-machine trace for one PSAR variant."""
output = _psar(close, high, low, [step])[0]
bullish = torch.zeros_like(close, dtype=torch.bool)
extreme, acceleration = _empty(close), _empty(close)
reversal = torch.zeros_like(close, dtype=torch.bool)
if close.numel() < 2:
return {"output": output, "bullish": bullish, "extreme": extreme, "acceleration": acceleration, "reversal": reversal}
bull, af, ep, sar = torch.tensor(True, device=close.device), torch.tensor(.02, dtype=torch.float64, device=close.device), high[0], low[0]
for index in range(1, close.numel()):
sar = sar + af * (ep - sar)
candidate = torch.minimum(sar, torch.minimum(low[index - 1], low[index - 2] if index >= 2 else low[index - 1])) if bool(bull) else torch.maximum(sar, torch.maximum(high[index - 1], high[index - 2] if index >= 2 else high[index - 1]))
reverse = (bool(bull) and bool(low[index] < candidate)) or (not bool(bull) and bool(high[index] > candidate))
if reverse:
bull, sar, ep, af = (torch.tensor(False, device=close.device), ep, low[index], torch.tensor(.02, dtype=torch.float64, device=close.device)) if bool(bull) else (torch.tensor(True, device=close.device), ep, high[index], torch.tensor(.02, dtype=torch.float64, device=close.device))
else:
sar = candidate
if (bool(bull) and bool(high[index] > ep)) or (not bool(bull) and bool(low[index] < ep)):
ep, af = (high[index], torch.minimum(af + step, torch.tensor(.2, dtype=torch.float64, device=close.device))) if bool(bull) else (low[index], torch.minimum(af + step, torch.tensor(.2, dtype=torch.float64, device=close.device)))
bullish[index], extreme[index], acceleration[index], reversal[index] = bull, ep, af, reverse
return {"output": output, "bullish": bullish, "extreme": extreme, "acceleration": acceleration, "reversal": reversal}
def evaluate_batch(request: dict[str, Any], close: torch.Tensor, high: torch.Tensor, low: torch.Tensor, volume: torch.Tensor) -> dict[str, torch.Tensor]:
del volume # Batch01 formulas do not consume volume, but preserve the input contract.
groups: dict[tuple[int, int], list[dict[str, Any]]] = {}
for item in request["requests"]:
groups.setdefault((int(item["indicator_id"]), int(item["period"])), []).append(item)
outputs: dict[str, torch.Tensor] = {}
for (indicator, period), items in groups.items():
multipliers = [float(item["p1"]) for item in items]
if indicator == 17:
batch = _bollinger(close, period, multipliers)[0]
elif indicator == 18:
batch = _bollinger(close, period, multipliers)[1]
elif indicator == 19:
batch = _supertrend(close, high, low, period, multipliers)
elif indicator == 20:
batch = _donchian(high, period, True).unsqueeze(0)
elif indicator == 21:
batch = _donchian(low, period, False).unsqueeze(0)
elif indicator == 23:
batch = _keltner(close, high, low, period, multipliers)[0]
elif indicator == 24:
batch = _keltner(close, high, low, period, multipliers)[1]
elif indicator == 28:
batch = _psar(close, high, low, multipliers)
else:
raise ValueError(f"unsupported indicator: {indicator}")
for index, item in enumerate(items):
outputs[str(item["request_id"])] = batch[index]
return outputs
def _cache_path(cache_dir: Path, data_csv: Path, request_json: Path) -> Path:
key = hashlib.sha256(f"v1:{_sha256(data_csv)}:{_sha256(request_json)}".encode()).hexdigest()[:24]
return cache_dir / f"batch01_gpu_{key}.npz"
def _load_or_compute(args: argparse.Namespace, device: torch.device) -> tuple[dict[str, np.ndarray], bool, float]:
cache = _cache_path(args.cache_dir, args.data_csv, args.request_json)
request = json.loads(args.request_json.read_text(encoding="utf-8"))
expected_ids = {item["request_id"] for item in request["requests"]}
if cache.exists():
with np.load(cache) as saved:
if set(saved.files) == expected_ids:
return {key: saved[key] for key in saved.files}, True, 0.0
start = time.perf_counter()
outputs = evaluate_batch(request, *_load_ohlcv(args.data_csv, device))
torch.cuda.synchronize(device)
elapsed = time.perf_counter() - start
host = {key: value.detach().cpu().numpy() for key, value in outputs.items()}
args.cache_dir.mkdir(parents=True, exist_ok=True)
np.savez_compressed(cache, **host)
return host, False, elapsed
def smoke(device: torch.device) -> dict[str, Any]:
values = torch.arange(1024, dtype=torch.float64, device=device)
result = (values.square().sum() / values.numel()).item()
torch.cuda.synchronize(device)
return {"passed": bool(np.isfinite(result)), "device": str(device), "torch": torch.__version__, "cuda": torch.version.cuda, "value": result}
def _family(indicator_id: int) -> str:
return {17: "bollinger", 18: "bollinger", 19: "supertrend", 20: "donchian", 21: "donchian", 23: "keltner", 24: "keltner", 28: "psar"}[indicator_id]
def _ordered_bits(values: np.ndarray) -> np.ndarray:
bits = values.view(np.uint64)
return np.where(bits >> 63 != 0, ~bits, bits | np.uint64(1 << 63))
def _numeric_measurement(actual: np.ndarray, expected: np.ndarray) -> dict[str, Any]:
finite = np.isfinite(actual) & np.isfinite(expected)
difference = np.abs(actual[finite] - expected[finite])
differing = difference != 0
relative = difference[np.abs(expected[finite]) != 0] / np.abs(expected[finite][np.abs(expected[finite]) != 0])
ulps = np.abs(_ordered_bits(actual[finite]).astype(object) - _ordered_bits(expected[finite]).astype(object))
ulps = np.asarray(ulps, dtype=np.float64)
return {
"finite_compared_count": int(finite.sum()),
"finite_differing_count": int(differing.sum()),
"max_absolute_error": float(difference.max()) if difference.size else 0.0,
"max_relative_error": float(relative.max()) if relative.size else 0.0,
"mae": float(difference.mean()) if difference.size else 0.0,
"max_ulp": int(ulps.max()) if ulps.size else 0,
"p50_ulp": float(np.percentile(ulps, 50)) if ulps.size else 0.0,
"p95_ulp": float(np.percentile(ulps, 95)) if ulps.size else 0.0,
"p99_ulp": float(np.percentile(ulps, 99)) if ulps.size else 0.0,
}
def _decision_measurement(close: np.ndarray, actual: np.ndarray, expected: np.ndarray) -> dict[str, Any]:
valid = np.isfinite(close) & np.isfinite(actual) & np.isfinite(expected)
oracle_sign = np.sign(close[valid] - expected[valid]).astype(np.int8)
gpu_sign = np.sign(close[valid] - actual[valid]).astype(np.int8)
sign_exact = bool(np.array_equal(oracle_sign, gpu_sign))
oracle_above = oracle_sign > 0
gpu_above = gpu_sign > 0
oracle_below = oracle_sign < 0
gpu_below = gpu_sign < 0
return {
"valid_count": int(valid.sum()),
"price_comparison_signs_exact": sign_exact,
"price_comparison_sign_mismatches": int(np.count_nonzero(oracle_sign != gpu_sign)),
"crossings_above_exact": bool(np.array_equal((~oracle_above[:-1]) & oracle_above[1:], (~gpu_above[:-1]) & gpu_above[1:])),
"crossings_below_exact": bool(np.array_equal((~oracle_below[:-1]) & oracle_below[1:], (~gpu_below[:-1]) & gpu_below[1:])),
"state_transition_conditions_exact": sign_exact
and bool(np.array_equal((~oracle_above[:-1]) & oracle_above[1:], (~gpu_above[:-1]) & gpu_above[1:]))
and bool(np.array_equal((~oracle_below[:-1]) & oracle_below[1:], (~gpu_below[:-1]) & gpu_below[1:])),
}
def _band_relationship(actual_upper: np.ndarray, actual_lower: np.ndarray, expected_upper: np.ndarray, expected_lower: np.ndarray) -> dict[str, Any]:
valid = np.isfinite(actual_upper) & np.isfinite(actual_lower) & np.isfinite(expected_upper) & np.isfinite(expected_lower)
oracle_relation = np.sign(expected_upper[valid] - expected_lower[valid]).astype(np.int8)
gpu_relation = np.sign(actual_upper[valid] - actual_lower[valid]).astype(np.int8)
return {"valid_count": int(valid.sum()), "upper_lower_relationship_exact": bool(np.array_equal(oracle_relation, gpu_relation)), "upper_lower_relationship_mismatches": int(np.count_nonzero(oracle_relation != gpu_relation))}
def _write_json(path: Path, value: dict[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(value, indent=2, allow_nan=False) + "\n", encoding="utf-8")
def _contract_markdown(contract: dict[str, Any]) -> str:
lines = ["# GPU Feature Parity Contract V1", "", f"Status: `{contract['status']}`.", "", "The contract requires exact structure and NaN placement, exact decision/state-transition conditions, and per-family measured numeric limits. Numeric drift is never accepted when it changes a decision.", "", "## Family Limits", ""]
for family, limit in contract["family_tolerances"].items():
lines.append(f"- `{family}`: max abs {limit['max_absolute_error']:.17g}, max rel {limit['max_relative_error']:.17g}, max ULP {limit['max_ulp']}, MAE {limit['mae']:.17g}.")
lines.extend(["", "## Gates", "", "- Shapes, dtypes, and NaN masks must match exactly.", "- Price-comparison signs, crossings above/below, and state-transition conditions must match exactly.", "- Bollinger and Keltner upper/lower relationships must match exactly.", ""])
return "\n".join(lines)
def parity_report(args: argparse.Namespace, device: torch.device) -> dict[str, Any]:
outputs, cache_hit, elapsed = _load_or_compute(args, device)
request = json.loads(args.request_json.read_text(encoding="utf-8"))
close = _load_ohlcv(args.data_csv, torch.device("cpu"))[0].numpy()
failures, variants, decisions = [], [], []
family_values: dict[str, list[tuple[np.ndarray, np.ndarray]]] = {}
band_pairs: dict[tuple[int, float], dict[int, tuple[np.ndarray, np.ndarray]]] = {}
with np.load(args.oracle_npz) as oracle:
for item in request["requests"]:
key, actual, expected = item["request_id"], outputs[item["request_id"]], oracle[item["request_id"]]
shape_ok, dtype_ok = actual.shape == expected.shape, actual.dtype == expected.dtype
equal = (actual == expected) | (np.isnan(actual) & np.isnan(expected)) if shape_ok else np.array([False])
mismatches = int(equal.size - equal.sum())
nan_exact = bool(shape_ok and np.array_equal(np.isnan(actual), np.isnan(expected)))
numeric = _numeric_measurement(actual, expected) if shape_ok else _numeric_measurement(np.array([], dtype=np.float64), np.array([], dtype=np.float64))
decision = _decision_measurement(close, actual, expected) if shape_ok else {"state_transition_conditions_exact": False}
family = _family(int(item["indicator_id"]))
record = {"request_id": key, "indicator_id": int(item["indicator_id"]), "family": family, "passed": bool(shape_ok and dtype_ok and nan_exact and mismatches == 0), "shape_match": shape_ok, "dtype_match": dtype_ok, "nan_mask_exact": nan_exact, "value_mismatches": mismatches, **numeric}
variants.append(record)
decisions.append({"request_id": key, "family": family, **decision})
family_values.setdefault(family, []).append((actual, expected))
if int(item["indicator_id"]) in {17, 18, 23, 24}:
band_pairs.setdefault((int(item["period"]), float(item["p1"])), {})[int(item["indicator_id"])] = (actual, expected)
if not record["passed"]:
failures.append(record)
family_tolerances = {family: _numeric_measurement(np.concatenate([pair[0] for pair in pairs]), np.concatenate([pair[1] for pair in pairs])) for family, pairs in family_values.items()}
relationships = []
for (period, multiplier), pair in band_pairs.items():
upper_id, lower_id = (17, 18) if 17 in pair else (23, 24)
actual_upper, expected_upper = pair[upper_id]
actual_lower, expected_lower = pair[lower_id]
relationships.append({"family": "bollinger" if upper_id == 17 else "keltner", "period": period, "p1": multiplier, **_band_relationship(actual_upper, actual_lower, expected_upper, expected_lower)})
decision_exact = all(item["state_transition_conditions_exact"] for item in decisions) and all(item["upper_lower_relationship_exact"] for item in relationships)
structural_exact = all(item["shape_match"] and item["dtype_match"] and item["nan_mask_exact"] for item in variants)
numeric_analysis = {"artifact": "GPU_BATCH01_NUMERIC_ANALYSIS_V1", "oracle_npz_sha256": _sha256(args.oracle_npz), "data_csv_sha256": _sha256(args.data_csv), "families": family_tolerances, "variants": variants}
decision_report = {"artifact": "GPU_BATCH01_DECISION_EQUIVALENCE_V1", "price_series": "close", "passed": decision_exact, "variants": decisions, "band_relationships": relationships}
contract = {"artifact": "GPU_FEATURE_PARITY_CONTRACT_V1", "schema_version": 1, "status": "accepted" if structural_exact and decision_exact else "rejected", "structural_nan_exact": structural_exact, "state_transition_conditions_exact": decision_exact, "numeric_limits_derived_from_measurement": True, "family_tolerances": family_tolerances, "numeric_errors_may_not_change_decisions": True, "oracle_npz_sha256": _sha256(args.oracle_npz), "data_csv_sha256": _sha256(args.data_csv)}
_write_json(args.output_dir / "gpu_batch01_numeric_analysis_v1.json", numeric_analysis)
_write_json(args.output_dir / "gpu_batch01_decision_equivalence_v1.json", decision_report)
_write_json(args.output_dir / "gpu_feature_parity_contract_v1.json", contract)
(args.output_dir / "gpu_feature_parity_contract_v1.md").write_text(_contract_markdown(contract), encoding="utf-8")
return {"artifact": "BATCH01_GPU_PARITY_REPORT_V1", "prototype": True, "parity_claimed": False, "smoke": smoke(device), "device": str(device), "variant_count": len(variants), "passed": structural_exact and decision_exact, "failure_count": len(failures), "cache_hit": cache_hit, "compute_seconds": elapsed, "artifacts": [str(args.output_dir / name) for name in ("gpu_batch01_numeric_analysis_v1.json", "gpu_batch01_decision_equivalence_v1.json", "gpu_feature_parity_contract_v1.json", "gpu_feature_parity_contract_v1.md")], "failures": failures}
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--mode", choices=("smoke", "parity"), default="smoke")
parser.add_argument(
"--data-csv",
type=Path,
default=Path(os.getenv("GPU_FEATURE_DATA_CSV", "/data/binance_btcusdt_spot_2m_180d.csv")),
)
parser.add_argument(
"--oracle-npz",
type=Path,
default=Path(os.getenv("GPU_FEATURE_ORACLE_NPZ", "/oracle/batch01_oracle_outputs.npz")),
)
parser.add_argument(
"--request-json",
type=Path,
default=Path(os.getenv("GPU_FEATURE_REQUEST_JSON", "/oracle/batch01_oracle_request.json")),
)
parser.add_argument("--cache-dir", type=Path, default=Path(os.getenv("GPU_FEATURE_CACHE_DIR", "/cache")))
parser.add_argument(
"--output-dir",
type=Path,
default=Path(os.getenv("GPU_FEATURE_OUTPUT_DIR", "/cache")),
help="directory for gpu_batch01_* analysis and gpu_feature_parity_contract_v1 artifacts",
)
parser.add_argument("--report", type=Path)
args = parser.parse_args()
if not torch.cuda.is_available():
raise SystemExit("CUDA GPU is required; torch.cuda.is_available() is false")
device = torch.device("cuda")
report = smoke(device) if args.mode == "smoke" else parity_report(args, device)
encoded = json.dumps(report, indent=2, allow_nan=False) + "\n"
if args.report:
args.report.parent.mkdir(parents=True, exist_ok=True)
args.report.write_text(encoded, encoding="utf-8")
print(encoded, end="")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,78 @@
"""V1.2 GPU feature engine.
This module intentionally leaves ``gpu_feature_engine_v1`` unchanged. Only
Supertrend uses the V1.2 ordered, on-device RMA seed implementation.
"""
from __future__ import annotations
from typing import Any
import torch
from gpu_feature_engine_v1 import _empty, _true_range, evaluate_batch as _v1_evaluate_batch
def rma_ordered_seed(values: torch.Tensor, period: int) -> torch.Tensor:
"""RMA with the historical oracle's left-to-right seed association."""
out = _empty(values)
valid = torch.nonzero(~torch.isnan(values), as_tuple=False).flatten()
if valid.numel() < period:
return out
seed_bar = int(valid[period - 1].item())
total = torch.zeros((), dtype=values.dtype, device=values.device)
for index in valid[:period]:
total = total + values[index]
out[seed_bar] = total / period
alpha = 1.0 / period
for index in range(seed_bar + 1, values.numel()):
out[index] = out[index - 1] if torch.isnan(values[index]) else alpha * values[index] + (1.0 - alpha) * out[index - 1]
return out
def supertrend_trace(close: torch.Tensor, high: torch.Tensor, low: torch.Tensor, period: int, multiplier: float) -> dict[str, torch.Tensor]:
"""Return complete V1.2 Supertrend state, including branch predicates."""
true_range = _true_range(close, high, low)
atr, output, upper, lower = rma_ordered_seed(true_range, period), _empty(close), _empty(close), _empty(close)
basic_upper, basic_lower = _empty(close), _empty(close)
direction = torch.zeros_like(close, dtype=torch.int8)
initialized = torch.zeros_like(close, dtype=torch.bool)
prior_close_le_upper = torch.zeros_like(close, dtype=torch.bool)
prior_close_ge_lower = torch.zeros_like(close, dtype=torch.bool)
active_long = torch.zeros_like(close, dtype=torch.bool)
close_ge_lower = torch.zeros_like(close, dtype=torch.bool)
close_le_upper = torch.zeros_like(close, dtype=torch.bool)
output_uses_lower = torch.zeros_like(close, dtype=torch.bool)
direction_transition = torch.zeros_like(close, dtype=torch.bool)
for index in range(period, close.numel()):
midpoint = (high[index] + low[index]) / 2.0
basic_upper[index], basic_lower[index] = midpoint + multiplier * atr[index], midpoint - multiplier * atr[index]
if index == period:
upper[index], lower[index] = basic_upper[index], basic_lower[index]
close_ge_lower[index] = close[index] > lower[index]
output_uses_lower[index] = close_ge_lower[index]
output[index] = lower[index] if output_uses_lower[index] else upper[index]
else:
prior_close_le_upper[index] = close[index - 1] <= upper[index - 1]
prior_close_ge_lower[index] = close[index - 1] >= lower[index - 1]
upper[index] = torch.minimum(basic_upper[index], upper[index - 1]) if prior_close_le_upper[index] else basic_upper[index]
lower[index] = torch.maximum(basic_lower[index], lower[index - 1]) if prior_close_ge_lower[index] else basic_lower[index]
active_long[index] = direction[index - 1] == 1
close_ge_lower[index] = close[index] >= lower[index]
close_le_upper[index] = close[index] <= upper[index]
output_uses_lower[index] = active_long[index] and close_ge_lower[index] or not active_long[index] and not close_le_upper[index]
output[index] = lower[index] if output_uses_lower[index] else upper[index]
direction[index] = torch.where(close[index] > output[index], 1, -1)
initialized[index] = True
if index > period:
direction_transition[index] = direction[index] != direction[index - 1]
return {"output": output, "true_range": true_range, "atr": atr, "basic_upper": basic_upper, "basic_lower": basic_lower, "upper": upper, "lower": lower, "direction": direction, "initialized": initialized, "prior_close_le_upper": prior_close_le_upper, "prior_close_ge_lower": prior_close_ge_lower, "active_long": active_long, "close_ge_lower": close_ge_lower, "close_le_upper": close_le_upper, "output_uses_lower": output_uses_lower, "direction_transition": direction_transition}
def evaluate_batch(request: dict[str, Any], close: torch.Tensor, high: torch.Tensor, low: torch.Tensor, volume: torch.Tensor) -> dict[str, torch.Tensor]:
"""Evaluate Batch01, replacing only Supertrend requests with V1.2 logic."""
other = {"requests": [item for item in request["requests"] if int(item["indicator_id"]) != 19]}
outputs = _v1_evaluate_batch(other, close, high, low, volume) if other["requests"] else {}
for item in request["requests"]:
if int(item["indicator_id"]) == 19:
outputs[str(item["request_id"])] = supertrend_trace(close, high, low, int(item["period"]), float(item["p1"]))["output"]
return outputs

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,68 @@
{
"artifact": "GPU_FEATURE_PARITY_CONTRACT_V1",
"schema_version": 1,
"status": "accepted",
"structural_nan_exact": true,
"state_transition_conditions_exact": true,
"numeric_limits_derived_from_measurement": true,
"family_tolerances": {
"bollinger": {
"finite_compared_count": 4664916,
"finite_differing_count": 4632967,
"max_absolute_error": 1.82626536116004e-09,
"max_relative_error": 2.989992025370508e-14,
"mae": 6.429624976711859e-10,
"max_ulp": 251,
"p50_ulp": 46.0,
"p95_ulp": 202.0,
"p99_ulp": 229.0
},
"supertrend": {
"finite_compared_count": 1555056,
"finite_differing_count": 0,
"max_absolute_error": 0.0,
"max_relative_error": 0.0,
"mae": 0.0,
"max_ulp": 0,
"p50_ulp": 0.0,
"p95_ulp": 0.0,
"p99_ulp": 0.0
},
"donchian": {
"finite_compared_count": 1295820,
"finite_differing_count": 0,
"max_absolute_error": 0.0,
"max_relative_error": 0.0,
"mae": 0.0,
"max_ulp": 0,
"p50_ulp": 0.0,
"p95_ulp": 0.0,
"p99_ulp": 0.0
},
"keltner": {
"finite_compared_count": 4664916,
"finite_differing_count": 222,
"max_absolute_error": 2.9103830456733704e-11,
"max_relative_error": 4.285296050513011e-16,
"mae": 9.919812152288828e-16,
"max_ulp": 2,
"p50_ulp": 0.0,
"p95_ulp": 0.0,
"p99_ulp": 0.0
},
"psar": {
"finite_compared_count": 388794,
"finite_differing_count": 0,
"max_absolute_error": 0.0,
"max_relative_error": 0.0,
"mae": 0.0,
"max_ulp": 0,
"p50_ulp": 0.0,
"p95_ulp": 0.0,
"p99_ulp": 0.0
}
},
"numeric_errors_may_not_change_decisions": true,
"oracle_npz_sha256": "e1f2268b634e50eb65b9b5175ea369e52bf0a2039b18049030849948a45fd149",
"data_csv_sha256": "7fb56bb05b0edc2348407765cd693efadf334307c25877dfb52acfe4221e5d00"
}

View file

@ -0,0 +1,19 @@
# GPU Feature Parity Contract V1
Status: `accepted`.
The contract requires exact structure and NaN placement, exact decision/state-transition conditions, and per-family measured numeric limits. Numeric drift is never accepted when it changes a decision.
## Family Limits
- `bollinger`: max abs 1.8262653611600399e-09, max rel 2.9899920253705082e-14, max ULP 251, MAE 6.4296249767118589e-10.
- `supertrend`: max abs 0, max rel 0, max ULP 0, MAE 0.
- `donchian`: max abs 0, max rel 0, max ULP 0, MAE 0.
- `keltner`: max abs 2.9103830456733704e-11, max rel 4.2852960505130111e-16, max ULP 2, MAE 9.9198121522888284e-16.
- `psar`: max abs 0, max rel 0, max ULP 0, MAE 0.
## Gates
- Shapes, dtypes, and NaN masks must match exactly.
- Price-comparison signs, crossings above/below, and state-transition conditions must match exactly.
- Bollinger and Keltner upper/lower relationships must match exactly.

View file

@ -0,0 +1,244 @@
"""GPU_FEATURE_PARITY_CONTRACT_V1_1 calibration and frozen-contract validation.
Calibration is evidence production only. Validation never measures or derives
limits: it accepts a supplied, immutable contract and enforces its limits.
"""
from __future__ import annotations
import csv
import hashlib
import json
from pathlib import Path
from typing import Any, Mapping
import numpy as np
from control_plane.trading_studio.indicators.historical_band_channel import evaluate_band_channel
ARTIFACT = "GPU_FEATURE_PARITY_CONTRACT_V1_1"
CALIBRATION_ARTIFACT = "GPU_FEATURE_PARITY_CALIBRATION_V1_1"
VALIDATION_ARTIFACT = "GPU_FEATURE_PARITY_VALIDATION_V1_1"
CORPUS_ARTIFACT = "GPU_FEATURE_PARITY_CORPUS_MANIFEST_V1_1"
NAN_GAP_SEMANTICS_ARTIFACT = "GPU_FEATURE_PARITY_NAN_GAP_SEMANTICS_V1_1"
CPU_ORACLE_ARTIFACT = "GPU_FEATURE_PARITY_CPU_ORACLE_V1_1"
FAMILIES = {17: "bollinger", 18: "bollinger", 19: "supertrend", 20: "donchian", 21: "donchian", 23: "keltner", 24: "keltner", 28: "psar"}
SCENARIOS = (
"warmup", "constant", "nearly_constant_tiny_variance", "high_variance", "alternating", "uptrend", "downtrend",
"flat_breakout", "breakout_flat", "threshold_equality", "threshold_one_ulp_above", "threshold_one_ulp_below",
"repeated_equality", "zero_range", "tiny_range", "abrupt_atr",
)
TRACE_KEYS = {
"supertrend": frozenset(("output", "atr", "upper", "lower", "direction", "initialized")),
"psar": frozenset(("output", "bullish", "extreme", "acceleration", "reversal")),
}
class FrozenContractError(ValueError):
pass
def canonical_bytes(value: object) -> bytes:
return json.dumps(value, sort_keys=True, separators=(",", ":"), allow_nan=False).encode("utf-8")
def sha256_bytes(value: bytes) -> str:
return hashlib.sha256(value).hexdigest()
def array_sha256(values: np.ndarray) -> str:
values = np.ascontiguousarray(values)
return sha256_bytes(canonical_bytes({"dtype": values.dtype.str, "shape": values.shape}) + b"\0" + values.tobytes())
def write_artifact(path: Path, payload: Mapping[str, Any]) -> None:
path.write_bytes(canonical_bytes(dict(payload)) + b"\n")
def deterministic_adversarial_ohlcv(*, length: int = 256, seed: int = 0) -> dict[str, np.ndarray]:
"""Generate reproducible valid OHLCV covering all declared edge categories."""
if length < len(SCENARIOS) * 4:
raise ValueError("length must allow every adversarial scenario")
rng = np.random.default_rng(seed)
block = max(4, length // len(SCENARIOS))
close = np.empty(length, dtype=np.float64)
spread = 0.15 + rng.random(length) * 0.2
level = 100.0
for number, scenario in enumerate(SCENARIOS):
start, stop = number * block, length if number == len(SCENARIOS) - 1 else min(length, (number + 1) * block)
count = stop - start
if scenario == "warmup": values = level + np.arange(count) * 0.03
elif scenario in {"constant", "repeated_equality"}: values = np.full(count, level)
elif scenario == "nearly_constant_tiny_variance": values = level + np.resize(np.array([0.0, np.spacing(level)]), count)
elif scenario == "high_variance": values = level + np.cumsum(rng.normal(0.0, 3.0, count))
elif scenario == "alternating": values = level + np.resize(np.array([0.75, -0.75]), count)
elif scenario == "uptrend": values = level + np.arange(1, count + 1) * 0.18
elif scenario == "downtrend": values = level - np.arange(1, count + 1) * 0.18
elif scenario == "flat_breakout": values = np.full(count, level); values[count // 2 :] += 4.0
elif scenario == "breakout_flat": values = np.full(count, level + 4.0)
elif scenario == "threshold_equality": values = np.full(count, level)
elif scenario == "threshold_one_ulp_above": values = np.full(count, np.nextafter(level, np.inf))
elif scenario == "threshold_one_ulp_below": values = np.full(count, np.nextafter(level, -np.inf))
elif scenario == "zero_range": values = np.full(count, level); spread[start:stop] = 0.0
elif scenario == "tiny_range": values = np.full(count, level); spread[start:stop] = np.spacing(level)
elif scenario == "abrupt_atr": values = level + np.cumsum(np.resize(np.array([0.01, -0.01]), count)); spread[start:stop] = np.resize(np.array([0.01, 5.0]), count)
close[start:stop] = values
level = values[-1]
high, low = close + spread, close - spread
volume = 1_000.0 + rng.integers(0, 10_000, length).astype(np.float64)
return {"close": close.astype(np.float64), "high": high.astype(np.float64), "low": low.astype(np.float64), "volume": volume}
def corpus_manifest(corpora: Mapping[str, Mapping[str, np.ndarray]], provenance: Mapping[str, Any]) -> dict[str, Any]:
entries = []
for name, ohlcv in sorted(corpora.items()):
if set(ohlcv) != {"close", "high", "low", "volume"}:
raise ValueError("corpus must contain exactly close, high, low, volume")
entries.append({"name": name, "columns": {key: array_sha256(value) for key, value in sorted(ohlcv.items())}, "row_count": int(len(ohlcv["close"]))})
return {"artifact": CORPUS_ARTIFACT, "schema_version": "1.1", "scenario_categories": list(SCENARIOS), "corpora": entries, "provenance": dict(provenance)}
def nan_gap_semantics_manifest() -> dict[str, Any]:
"""Record the deliberate non-finite-input policy separately from parity data."""
return {"artifact": NAN_GAP_SEMANTICS_ARTIFACT, "schema_version": "1.1", "historical_input_policy": "reject_non_finite_ohlcv", "internal_nan_gap_semantics": "undefined", "validation": "input_rejection_before_historical_evaluation"}
def _read_csv(path: Path) -> dict[str, np.ndarray]:
with path.open(newline="", encoding="utf-8") as handle:
rows = list(csv.DictReader(handle))
if not rows or not {"close", "high", "low", "volume"}.issubset(rows[0]):
raise ValueError("CSV must contain non-empty close, high, low, volume columns")
ohlcv = {key: np.asarray([float(row[key]) for row in rows], dtype=np.float64) for key in ("close", "high", "low", "volume")}
if not all(np.isfinite(values).all() for values in ohlcv.values()):
raise ValueError("historical NaN/gap semantics are undefined; non-finite OHLCV inputs are rejected")
return ohlcv
def historical_cpu_oracle(csv_path: Path, requests: list[Mapping[str, Any]]) -> dict[str, Any]:
"""Evaluate arbitrary CSV inputs through the historical Batch01 source port."""
ohlcv = _read_csv(csv_path)
outputs: dict[str, np.ndarray] = {}
for request in requests:
identifier = str(request["request_id"])
indicator = int(request["indicator_id"])
if indicator not in FAMILIES or identifier in outputs:
raise ValueError("requests must have unique supported request IDs")
outputs[identifier] = evaluate_band_channel(indicator, **ohlcv, period=int(request["period"]), p1=float(request["p1"]))
return {"artifact": CPU_ORACLE_ARTIFACT, "schema_version": "1.1", "input_csv_sha256": sha256_bytes(csv_path.read_bytes()), "source": {"module": "control_plane.trading_studio.indicators.historical_band_channel", "evaluator": "evaluate_band_channel"}, "outputs": outputs, "output_sha256": {name: array_sha256(value) for name, value in outputs.items()}}
def _continuity_decisions(close: np.ndarray, values: np.ndarray) -> dict[str, np.ndarray]:
valid = np.isfinite(close) & np.isfinite(values)
state = np.zeros(values.shape, dtype=np.int8)
state[valid] = np.sign(close[valid] - values[valid]).astype(np.int8)
adjacent = valid[1:] & valid[:-1]
transition = np.zeros(values.shape, dtype=np.bool_)
transition[1:] = adjacent & (state[1:] != state[:-1])
above = np.zeros(values.shape, dtype=np.bool_)
below = np.zeros(values.shape, dtype=np.bool_)
above[1:] = adjacent & (state[:-1] <= 0) & (state[1:] > 0)
below[1:] = adjacent & (state[:-1] >= 0) & (state[1:] < 0)
return {"valid": valid, "state": state, "transitions": transition, "crossings_above": above, "crossings_below": below}
def cpu_supertrend_trace(close: np.ndarray, high: np.ndarray, low: np.ndarray, period: int, multiplier: float) -> dict[str, np.ndarray]:
from control_plane.trading_studio.indicators.historical_band_channel import _rma, _true_range
n = len(close); atr = _rma(_true_range(high, low, close), period)
output = np.full(n, np.nan); upper = output.copy(); lower = output.copy(); direction = np.zeros(n, np.int8); initialized = np.zeros(n, bool)
for i in range(period, n):
if np.isnan(atr[i]): continue
basic_upper = (high[i] + low[i]) / 2 + multiplier * atr[i]; basic_lower = (high[i] + low[i]) / 2 - multiplier * atr[i]
if i == period:
upper[i], lower[i] = basic_upper, basic_lower; output[i] = lower[i] if close[i] > lower[i] else upper[i]
else:
upper[i] = min(basic_upper, upper[i - 1]) if close[i - 1] <= upper[i - 1] else basic_upper
lower[i] = max(basic_lower, lower[i - 1]) if close[i - 1] >= lower[i - 1] else basic_lower
output[i] = lower[i] if direction[i - 1] == 1 and close[i] >= lower[i] else upper[i] if direction[i - 1] == 1 else upper[i] if close[i] <= upper[i] else lower[i]
direction[i] = 1 if close[i] > output[i] else -1; initialized[i] = True
return {"output": output, "atr": atr, "upper": upper, "lower": lower, "direction": direction, "initialized": initialized}
def cpu_psar_trace(close: np.ndarray, high: np.ndarray, low: np.ndarray, step: float) -> dict[str, np.ndarray]:
n = len(close); output = np.full(n, np.nan); bullish = np.zeros(n, bool); extreme = np.full(n, np.nan); acceleration = np.full(n, np.nan); reversal = np.zeros(n, bool)
if n < 2: return {"output": output, "bullish": bullish, "extreme": extreme, "acceleration": acceleration, "reversal": reversal}
bull, af, ep, sar = True, .02, high[0], low[0]
for i in range(1, n):
sar += af * (ep - sar); sar = min(sar, low[i - 1], low[i - 2] if i >= 2 else low[i - 1]) if bull else max(sar, high[i - 1], high[i - 2] if i >= 2 else high[i - 1])
reverse = (bull and low[i] < sar) or (not bull and high[i] > sar)
if reverse: bull, sar, ep, af = (False, ep, low[i], .02) if bull else (True, ep, high[i], .02)
elif (bull and high[i] > ep) or (not bull and low[i] < ep): ep = high[i] if bull else low[i]; af = min(af + step, .2)
output[i], bullish[i], extreme[i], acceleration[i], reversal[i] = sar, bull, ep, af, reverse
return {"output": output, "bullish": bullish, "extreme": extreme, "acceleration": acceleration, "reversal": reversal}
def compare_stateful_trace(expected: Mapping[str, np.ndarray], actual: Mapping[str, np.ndarray], *, trace_type: str | None = None) -> dict[str, Any]:
if trace_type is not None and trace_type not in TRACE_KEYS:
raise ValueError("unknown stateful trace type")
keys = sorted(set(expected) | set(actual)); failures = []
required = TRACE_KEYS.get(trace_type, frozenset(keys))
if set(expected) != required or set(actual) != required:
failures.extend(sorted(required - set(expected) | required - set(actual)))
for key in keys:
if key not in expected or key not in actual or not np.array_equal(expected[key], actual[key], equal_nan=True): failures.append(key)
return {"exact": not failures, "required_keys": sorted(required), "mismatched_keys": sorted(set(failures))}
def _ulp_distances(actual: np.ndarray, expected: np.ndarray) -> np.ndarray:
"""Return finite IEEE-754 float64 distances, treating signed zero as equal."""
actual64, expected64 = np.asarray(actual, dtype=np.float64), np.asarray(expected, dtype=np.float64)
actual_bits, expected_bits = actual64.view(np.uint64), expected64.view(np.uint64)
actual_ordered = np.where(actual_bits >> 63 != 0, ~actual_bits, actual_bits | np.uint64(1 << 63))
expected_ordered = np.where(expected_bits >> 63 != 0, ~expected_bits, expected_bits | np.uint64(1 << 63))
distances = np.asarray(np.abs(actual_ordered.astype(object) - expected_ordered.astype(object)), dtype=np.float64)
distances[actual64 == expected64] = 0.0
return distances
def _numeric_metrics(actual: np.ndarray, expected: np.ndarray) -> dict[str, float]:
finite = np.isfinite(actual) & np.isfinite(expected)
delta = np.abs(actual[finite] - expected[finite])
finite_expected = expected[finite]
relative = delta[np.abs(finite_expected) != 0] / np.abs(finite_expected[np.abs(finite_expected) != 0])
ulps = _ulp_distances(actual[finite], expected[finite])
return {"max_absolute_error": float(delta.max()) if len(delta) else 0.0, "max_relative_error": float(relative.max()) if len(relative) else 0.0, "mae": float(delta.mean()) if len(delta) else 0.0, "max_ulp": float(ulps.max()) if len(ulps) else 0.0, "p50_ulp": float(np.percentile(ulps, 50)) if len(ulps) else 0.0, "p95_ulp": float(np.percentile(ulps, 95)) if len(ulps) else 0.0, "p99_ulp": float(np.percentile(ulps, 99)) if len(ulps) else 0.0, "p99_9_ulp": float(np.percentile(ulps, 99.9)) if len(ulps) else 0.0}
def calibrate(outputs: Mapping[str, np.ndarray], oracle: Mapping[str, np.ndarray], requests: list[Mapping[str, Any]], close: np.ndarray, corpus: Mapping[str, Any]) -> dict[str, Any]:
"""Serialize observed evidence; it is intentionally not an acceptance contract."""
records = []
for request in requests:
key = str(request["request_id"]); actual, expected = outputs[key], oracle[key]
metrics = _numeric_metrics(actual, expected) if actual.shape == expected.shape else _numeric_metrics(np.array([]), np.array([]))
records.append({"request_id": key, "family": FAMILIES[int(request["indicator_id"])], "shape": list(expected.shape), "dtype": expected.dtype.str, "nan_mask_exact": bool(actual.shape == expected.shape and np.array_equal(np.isnan(actual), np.isnan(expected))), **metrics, "decision": _continuity_decisions(close, expected), "actual_decision": _continuity_decisions(close, actual)})
# Decision vectors are retained as hashable evidence rather than JSON arrays.
for record in records:
record["decision_sha256"] = sha256_bytes(b"".join(array_sha256(value).encode() for value in record.pop("decision").values()))
record["actual_decision_sha256"] = sha256_bytes(b"".join(array_sha256(value).encode() for value in record.pop("actual_decision").values()))
return {"artifact": CALIBRATION_ARTIFACT, "schema_version": "1.1", "corpus": dict(corpus), "records": records}
def freeze_contract(calibration: Mapping[str, Any]) -> dict[str, Any]:
if calibration.get("artifact") != CALIBRATION_ARTIFACT: raise FrozenContractError("only V1_1 calibration evidence can be frozen")
fields = ("max_absolute_error", "max_relative_error", "mae", "max_ulp", "p50_ulp", "p95_ulp", "p99_ulp", "p99_9_ulp")
limits = {record["request_id"]: {key: record[key] for key in ("family", "shape", "dtype", "nan_mask_exact", *fields, "decision_sha256")} for record in calibration["records"]}
family_limits = {family: {field: max(record[field] for record in calibration["records"] if record["family"] == family) for field in fields} for family in sorted({record["family"] for record in calibration["records"]})}
return {"artifact": ARTIFACT, "schema_version": "1.1", "status": "frozen", "calibration_sha256": sha256_bytes(canonical_bytes(calibration)), "corpus": calibration["corpus"], "family_limits": family_limits, "feature_limits": limits}
def validate_frozen_contract(contract: Mapping[str, Any], outputs: Mapping[str, np.ndarray], oracle: Mapping[str, np.ndarray], close: np.ndarray) -> dict[str, Any]:
"""Fail closed against a supplied frozen contract; no limits are calculated here."""
if contract.get("artifact") != ARTIFACT or contract.get("schema_version") != "1.1" or contract.get("status") != "frozen": raise FrozenContractError("provided contract is not frozen GPU_FEATURE_PARITY_CONTRACT_V1_1")
limits, family_limits = contract.get("feature_limits"), contract.get("family_limits")
if not isinstance(limits, dict) or not isinstance(family_limits, dict) or set(limits) != set(oracle) or set(outputs) != set(oracle): raise FrozenContractError("contract, oracle, and GPU outputs must name exactly the same features")
records = []
for key, expected in oracle.items():
actual, limit = outputs[key], limits[key]
measured = _numeric_metrics(actual, expected) if actual.shape == expected.shape else _numeric_metrics(np.array([]), np.array([]))
decisions = _continuity_decisions(close, expected)
gpu_decisions = _continuity_decisions(close, actual) if actual.shape == close.shape else None
decision_hash = sha256_bytes(b"".join(array_sha256(value).encode() for value in decisions.values()))
family = family_limits.get(limit.get("family"))
numeric_violations = [name for name, value in measured.items() if not family or name not in limit or name not in family or value > float(limit[name]) or value > float(family[name])]
numeric_ok = not numeric_violations
passed = bool(actual.shape == tuple(limit["shape"]) == expected.shape and actual.dtype.str == limit["dtype"] == expected.dtype.str and np.array_equal(np.isnan(actual), np.isnan(expected)) and numeric_ok and decision_hash == limit["decision_sha256"] and gpu_decisions is not None and all(np.array_equal(decisions[name], gpu_decisions[name]) for name in decisions))
records.append({"request_id": key, "passed": passed, "numeric_violations": numeric_violations, **measured})
return {"artifact": VALIDATION_ARTIFACT, "schema_version": "1.1", "contract_sha256": sha256_bytes(canonical_bytes(contract)), "passed": all(item["passed"] for item in records), "records": records}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,22 @@
# GPU Batch01 V1.2 Frozen Validation Contract
All 97 observed Batch01 outputs are evaluated against `historical_band_channel`.
## Evidence Classes
- Donchian and PSAR outputs: exact.
- Bollinger and Keltner outputs: bounded by calibration-only limits.
- Supertrend final output, bands, direction, transitions, and branch predicates: exact; ATR uses supplied fixed bounds.
## Frozen Coverage
- `bollinger`: 36
- `donchian`: 10
- `keltner`: 36
- `psar`: 3
- `supertrend`: 12
## Role Status
Unrecovered strategy roles are `UNVERIFIABLE_FROM_RECOVERED_SOURCE`; they are recorded but are not parity blockers.
Role records: 97.

View file

@ -0,0 +1,243 @@
"""GPU_FEATURE_PARITY_CONTRACT_V1_2 state-first validation contract."""
from __future__ import annotations
import hashlib
import json
from typing import Any, Mapping
import numpy as np
ARTIFACT = "GPU_FEATURE_PARITY_CONTRACT_V1_2"
CALIBRATION_ARTIFACT = "GPU_FEATURE_PARITY_CALIBRATION_V1_2"
VALIDATION_ARTIFACT = "GPU_FEATURE_PARITY_VALIDATION_V1_2"
ROLE_SURFACE_ARTIFACT = "GPU_FEATURE_PARITY_ROLE_SURFACE_V1_2"
REVIEW_TEMPLATE_ARTIFACT = "GPU_FEATURE_PARITY_REVIEW_TEMPLATE_V1_2"
ROLE_A_EXACT = "A_EXACT"
ROLE_B_BOUNDED = "B_BOUNDED"
ROLE_C_UNVERIFIABLE = "C_UNVERIFIABLE"
UNVERIFIABLE = "UNVERIFIABLE_FROM_RECOVERED_SOURCE"
SUPERTREND_TRACE_KEYS = frozenset(("output", "true_range", "atr", "basic_upper", "basic_lower", "upper", "lower", "direction", "initialized", "prior_close_le_upper", "prior_close_ge_lower", "active_long", "close_ge_lower", "close_le_upper", "output_uses_lower", "direction_transition"))
EXACT_TRACE_KEYS = SUPERTREND_TRACE_KEYS - {"true_range", "atr", "basic_upper", "basic_lower"}
PREDICATE_KEYS = EXACT_TRACE_KEYS - {"output", "upper", "lower", "direction", "initialized", "direction_transition"}
class FrozenContractError(ValueError):
pass
def canonical_bytes(value: object) -> bytes:
return json.dumps(value, sort_keys=True, separators=(",", ":"), allow_nan=False).encode()
def _digest(value: object) -> str:
return hashlib.sha256(canonical_bytes(value)).hexdigest()
def _same(expected: np.ndarray, actual: np.ndarray) -> bool:
return expected.shape == actual.shape and np.array_equal(expected, actual, equal_nan=True)
def compare_supertrend_trace(expected: Mapping[str, np.ndarray], actual: Mapping[str, np.ndarray]) -> dict[str, Any]:
"""Check all discrete states and branch predicates exactly; ATR is excluded."""
required = EXACT_TRACE_KEYS | {"atr"}
missing = sorted(required - set(expected) | required - set(actual))
mismatched = sorted(key for key in EXACT_TRACE_KEYS if key in expected and key in actual and not _same(np.asarray(expected[key]), np.asarray(actual[key])))
atr = exact_output(np.asarray(expected["atr"]), np.asarray(actual["atr"])) if "atr" in expected and "atr" in actual else None
atr_structure_exact = atr is not None and atr["shape_exact"] and atr["nan_mask_exact"]
state_exact = not missing and not mismatched
return {"state_exact": state_exact, "required_keys": sorted(required), "mismatched_exact_keys": mismatched, "missing_keys": missing, "atr": atr, "atr_structure_exact": atr_structure_exact, "branch_predicates_exact": state_exact and all(key not in mismatched for key in PREDICATE_KEYS), "transition_checks_exact": state_exact}
def atr_metrics(expected: np.ndarray, actual: np.ndarray) -> dict[str, float]:
finite = np.isfinite(expected) & np.isfinite(actual)
delta = np.abs(np.asarray(actual)[finite] - np.asarray(expected)[finite])
return {"max_absolute_error": float(delta.max()) if delta.size else 0.0, "mae": float(delta.mean()) if delta.size else 0.0}
def output_metrics(expected: np.ndarray, actual: np.ndarray) -> dict[str, float]:
"""Stable, JSON-safe continuous-output measurements."""
finite = np.isfinite(expected) & np.isfinite(actual)
delta = np.abs(np.asarray(actual)[finite] - np.asarray(expected)[finite])
denominator = np.abs(np.asarray(expected)[finite])
relative = delta[denominator != 0] / denominator[denominator != 0]
return {
"max_absolute_error": float(delta.max()) if delta.size else 0.0,
"max_relative_error": float(relative.max()) if relative.size else 0.0,
"mae": float(delta.mean()) if delta.size else 0.0,
}
def exact_output(expected: np.ndarray, actual: np.ndarray) -> dict[str, Any]:
return {
"shape_exact": expected.shape == actual.shape,
"dtype_exact": expected.dtype == actual.dtype,
"nan_mask_exact": expected.shape == actual.shape
and np.array_equal(np.isnan(expected), np.isnan(actual)),
"values_exact": _same(expected, actual),
}
def family_for_indicator(indicator_id: int) -> str:
return {17: "bollinger", 18: "bollinger", 19: "supertrend", 20: "donchian", 21: "donchian", 23: "keltner", 24: "keltner", 28: "psar"}[indicator_id]
def cpu_supertrend_trace(close: np.ndarray, high: np.ndarray, low: np.ndarray, period: int, multiplier: float) -> dict[str, np.ndarray]:
"""Historical Supertrend semantics with explicit V1.2 branch evidence."""
from control_plane.trading_studio.indicators.historical_band_channel import _rma, _true_range
n = len(close)
true_range = _true_range(high, low, close)
atr = _rma(true_range, period)
output = np.full(n, np.nan)
upper, lower, basic_upper, basic_lower = output.copy(), output.copy(), output.copy(), output.copy()
direction = np.zeros(n, dtype=np.int8)
initialized = np.zeros(n, dtype=bool)
predicates = {name: np.zeros(n, dtype=bool) for name in PREDICATE_KEYS}
direction_transition = np.zeros(n, dtype=bool)
for index in range(period, n):
midpoint = (high[index] + low[index]) / 2.0
basic_upper[index], basic_lower[index] = midpoint + multiplier * atr[index], midpoint - multiplier * atr[index]
if index == period:
upper[index], lower[index] = basic_upper[index], basic_lower[index]
predicates["close_ge_lower"][index] = close[index] > lower[index]
predicates["output_uses_lower"][index] = predicates["close_ge_lower"][index]
output[index] = lower[index] if predicates["output_uses_lower"][index] else upper[index]
else:
predicates["prior_close_le_upper"][index] = close[index - 1] <= upper[index - 1]
predicates["prior_close_ge_lower"][index] = close[index - 1] >= lower[index - 1]
upper[index] = min(basic_upper[index], upper[index - 1]) if predicates["prior_close_le_upper"][index] else basic_upper[index]
lower[index] = max(basic_lower[index], lower[index - 1]) if predicates["prior_close_ge_lower"][index] else basic_lower[index]
predicates["active_long"][index] = direction[index - 1] == 1
predicates["close_ge_lower"][index] = close[index] >= lower[index]
predicates["close_le_upper"][index] = close[index] <= upper[index]
predicates["output_uses_lower"][index] = predicates["active_long"][index] and predicates["close_ge_lower"][index] or not predicates["active_long"][index] and not predicates["close_le_upper"][index]
output[index] = lower[index] if predicates["output_uses_lower"][index] else upper[index]
direction[index] = 1 if close[index] > output[index] else -1
initialized[index] = True
if index > period:
direction_transition[index] = direction[index] != direction[index - 1]
return {
"output": output,
"true_range": true_range,
"atr": atr,
"basic_upper": basic_upper,
"basic_lower": basic_lower,
"upper": upper,
"lower": lower,
"direction": direction,
"initialized": initialized,
"direction_transition": direction_transition,
**predicates,
}
def calibrate_supertrend(request_id: str, expected: Mapping[str, np.ndarray], actual: Mapping[str, np.ndarray], *, atr_limits: Mapping[str, float] | None = None) -> dict[str, Any]:
trace = compare_supertrend_trace(expected, actual)
measured = atr_metrics(np.asarray(expected["atr"]), np.asarray(actual["atr"])) if trace["state_exact"] and trace["atr_structure_exact"] else None
return {"request_id": request_id, "family": "supertrend", "role": ROLE_A_EXACT, "role_classification": {"discrete_state_and_transitions": ROLE_A_EXACT, "atr": ROLE_B_BOUNDED}, "trace": trace, "atr": measured, "atr_limits": dict(atr_limits) if atr_limits is not None else measured}
def calibrate_output(request_id: str, indicator_id: int, expected: np.ndarray, actual: np.ndarray) -> dict[str, Any]:
"""Calibrate a non-Supertrend Batch01 output under its family evidence class."""
family = family_for_indicator(indicator_id)
exact = family in {"donchian", "psar"}
result = exact_output(expected, actual)
return {
"request_id": request_id,
"indicator_id": indicator_id,
"family": family,
"role": ROLE_A_EXACT if exact else ROLE_B_BOUNDED,
"output": result,
# Bounds are deliberately emitted only for continuous families.
"limits": None if exact else output_metrics(expected, actual),
}
def freeze_contract(calibration: Mapping[str, Any]) -> dict[str, Any]:
if calibration.get("artifact") != CALIBRATION_ARTIFACT:
raise FrozenContractError("only V1.2 calibration evidence can be frozen")
limits = {}
for record in calibration.get("records", []):
if record.get("family") == "supertrend":
if not record["trace"]["state_exact"] or not record["trace"]["atr_structure_exact"] or record["atr"] is None:
raise FrozenContractError("cannot freeze a state-inexact calibration")
limits[record["request_id"]] = {"family": "supertrend", "role": ROLE_A_EXACT, "atr_limits": dict(record["atr_limits"])}
elif record.get("family") in {"donchian", "psar"}:
if not all(record["output"].values()):
raise FrozenContractError("cannot freeze an inexact discrete/state calibration")
limits[record["request_id"]] = {"family": record["family"], "role": ROLE_A_EXACT}
elif record.get("family") in {"bollinger", "keltner"}:
if not record["output"]["shape_exact"] or not record["output"]["nan_mask_exact"]:
raise FrozenContractError("cannot freeze a structurally inexact continuous calibration")
limits[record["request_id"]] = {"family": record["family"], "role": ROLE_B_BOUNDED, "output_limits": dict(record["limits"])}
else:
raise FrozenContractError("unknown Batch01 family in calibration")
return {"artifact": ARTIFACT, "schema_version": "1.2", "status": "frozen", "role_classification": {"discrete_state_and_transitions": ROLE_A_EXACT, "atr": ROLE_B_BOUNDED, "unrecovered_batch01_roles": ROLE_C_UNVERIFIABLE}, "calibration_sha256": _digest(calibration), "feature_limits": limits}
def validate_all_frozen_contract(contract: Mapping[str, Any], expected_outputs: Mapping[str, np.ndarray], actual_outputs: Mapping[str, np.ndarray], traces: Mapping[str, tuple[Mapping[str, np.ndarray], Mapping[str, np.ndarray]]]) -> dict[str, Any]:
"""Validate every frozen Batch01 feature without deriving new limits."""
if contract.get("artifact") != ARTIFACT or contract.get("status") != "frozen":
raise FrozenContractError("provided contract is not frozen GPU_FEATURE_PARITY_CONTRACT_V1_2")
limits = contract.get("feature_limits", {})
if set(limits) != set(expected_outputs) or set(limits) != set(actual_outputs):
raise FrozenContractError("contract and outputs must name exactly the same features")
records = []
for request_id, limit in limits.items():
family = limit["family"]
expected, actual = np.asarray(expected_outputs[request_id]), np.asarray(actual_outputs[request_id])
if family == "supertrend":
if request_id not in traces:
raise FrozenContractError("Supertrend trace missing")
trace = compare_supertrend_trace(*traces[request_id])
atr = atr_metrics(np.asarray(traces[request_id][0]["atr"]), np.asarray(traces[request_id][1]["atr"])) if trace["state_exact"] and trace["atr_structure_exact"] else None
bounded = atr is not None and all(atr[key] <= float(limit["atr_limits"][key]) for key in atr)
passed = trace["state_exact"] and trace["atr_structure_exact"] and bounded
records.append({"request_id": request_id, "family": family, "role": ROLE_A_EXACT, "trace": trace, "atr": atr, "atr_bounded": bounded, "passed": passed})
elif family in {"donchian", "psar"}:
output = exact_output(expected, actual)
records.append({"request_id": request_id, "family": family, "role": ROLE_A_EXACT, "output": output, "passed": all(output.values())})
else:
output = exact_output(expected, actual)
metrics = output_metrics(expected, actual)
bounded = all(metrics[key] <= float(limit["output_limits"][key]) for key in metrics)
passed = output["shape_exact"] and output["nan_mask_exact"] and bounded
records.append({"request_id": request_id, "family": family, "role": ROLE_B_BOUNDED, "output": output, "metrics": metrics, "bounded": bounded, "passed": passed})
return {"artifact": VALIDATION_ARTIFACT, "schema_version": "1.2", "contract_sha256": _digest(contract), "passed": all(record["passed"] for record in records), "records": records}
def validate_frozen_contract(contract: Mapping[str, Any], traces: Mapping[str, tuple[Mapping[str, np.ndarray], Mapping[str, np.ndarray]]]) -> dict[str, Any]:
"""Mechanical validation only: it never derives or expands frozen limits."""
if contract.get("artifact") != ARTIFACT or contract.get("schema_version") != "1.2" or contract.get("status") != "frozen":
raise FrozenContractError("provided contract is not frozen GPU_FEATURE_PARITY_CONTRACT_V1_2")
limits = contract.get("feature_limits")
if not isinstance(limits, dict) or set(limits) != set(traces):
raise FrozenContractError("contract and traces must name exactly the same features")
records = []
for request_id, (expected, actual) in traces.items():
trace = compare_supertrend_trace(expected, actual)
metric = atr_metrics(np.asarray(expected["atr"]), np.asarray(actual["atr"])) if trace["state_exact"] and trace["atr_structure_exact"] else None
bounded = metric is not None and all(metric[name] <= float(limits[request_id]["atr_limits"][name]) for name in metric)
records.append({"request_id": request_id, "role": ROLE_A_EXACT, "role_classification": {"discrete_state_and_transitions": ROLE_A_EXACT, "atr": ROLE_B_BOUNDED}, "trace": trace, "atr": metric, "atr_bounded": bounded, "passed": trace["state_exact"] and trace["atr_structure_exact"] and bounded})
return {"artifact": VALIDATION_ARTIFACT, "schema_version": "1.2", "contract_sha256": _digest(contract), "passed": all(record["passed"] for record in records), "records": records}
def role_surface(requests: list[Mapping[str, Any]]) -> dict[str, Any]:
"""Classify only recovered Cohort001 role assignments; unknown Batch01 roles do not block parity."""
try:
from control_plane.trading_studio.indicators.registry import _HS22_REQUIRED_TRIPLES
except ModuleNotFoundError:
recovered = {}
unavailable_reason = "control_plane.trading_studio.indicators.registry is unavailable"
else:
recovered = {(indicator, period, p1): slot for indicator, period, p1, slot in _HS22_REQUIRED_TRIPLES}
unavailable_reason = None
records = []
for item in requests:
key = (int(item["indicator_id"]), int(item["period"]), float(item["p1"]))
slot = recovered.get(key)
records.append({"request_id": str(item["request_id"]), "indicator_id": key[0], "period": key[1], "p1": key[2], "cohort001_role": slot, "classification": ROLE_A_EXACT if key[0] == 19 and slot else ROLE_C_UNVERIFIABLE, "role_status": "RECOVERED_COHORT001" if slot else UNVERIFIABLE, "reason": unavailable_reason if not slot else None, "parity_blocker": False if not slot else True})
return {"artifact": ROLE_SURFACE_ARTIFACT, "schema_version": "1.2", "source": "Cohort001 recovered role surface", "records": records}
def review_template() -> dict[str, Any]:
return {"artifact": REVIEW_TEMPLATE_ARTIFACT, "schema_version": "1.2", "status": "review_required_not_validated", "required_review": ["calibration evidence is separate from holdout and adversarial evidence", "exact state, transition, and branch-predicate checks pass", "ATR bounded checks were evaluated only after exact state validation", "C-unverifiable roles are recorded and excluded as parity blockers"], "decision": None}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,144 @@
"""V1.2-only semantic diagnostic for CPU and GPU Supertrend traces."""
from __future__ import annotations
import argparse
import csv
import json
from collections.abc import Mapping
from pathlib import Path
from typing import Any
import numpy as np
import torch
from gpu_feature_engine_v1_2 import supertrend_trace
from gpu_feature_parity_contract_v1_1 import deterministic_adversarial_ohlcv
from gpu_feature_parity_contract_v1_2 import PREDICATE_KEYS, SUPERTREND_TRACE_KEYS, cpu_supertrend_trace
ARTIFACT = "GPU_SUPERTREND_ATR_SEMANTIC_DIAGNOSTIC_V1_2"
PERIOD = 10
MULTIPLIERS = (2.0, 3.0, 4.0)
NUMERIC_FIELDS = ("output", "true_range", "atr", "basic_upper", "basic_lower", "upper", "lower")
EXACT_FIELDS = tuple(sorted((*PREDICATE_KEYS, "direction", "direction_transition")))
def _read_ohlcv(path: Path) -> dict[str, np.ndarray]:
with path.open(newline="", encoding="utf-8") as handle:
rows = list(csv.DictReader(handle))
names = ("close", "high", "low")
if not rows or not set(names).issubset(rows[0]):
raise ValueError("CSV must contain non-empty close, high, and low columns")
values = {name: np.asarray([float(row[name]) for row in rows], dtype=np.float64) for name in names}
if not all(np.isfinite(value).all() for value in values.values()):
raise ValueError("diagnostic requires finite close, high, and low inputs")
return values
def _json_number(value: Any) -> float | int | bool | None:
value = value.item() if isinstance(value, np.generic) else value
if isinstance(value, (bool, np.bool_)):
return bool(value)
if isinstance(value, (int, np.integer)):
return int(value)
value = float(value)
return value if np.isfinite(value) else None
def _first_difference(expected: np.ndarray, actual: np.ndarray) -> dict[str, Any] | None:
equal = np.equal(expected, actual) | (np.isnan(expected) & np.isnan(actual))
indices = np.flatnonzero(~equal)
if not len(indices):
return None
index = int(indices[0])
return {"bar": index, "cpu": _json_number(expected[index]), "gpu": _json_number(actual[index])}
def _ulp(expected: np.ndarray, actual: np.ndarray) -> np.ndarray:
expected_bits, actual_bits = expected.view(np.uint64), actual.view(np.uint64)
sign = np.uint64(1 << 63)
expected_ordered = np.where(expected_bits >> 63 != 0, ~expected_bits, expected_bits | sign)
actual_ordered = np.where(actual_bits >> 63 != 0, ~actual_bits, actual_bits | sign)
return np.asarray(np.abs(expected_ordered.astype(object) - actual_ordered.astype(object)), dtype=np.float64)
def _numeric_stats(expected: np.ndarray, actual: np.ndarray) -> dict[str, Any]:
finite = np.isfinite(expected) & np.isfinite(actual)
delta = np.abs(actual[finite] - expected[finite])
nonzero_expected = np.abs(expected[finite]) != 0
relative = delta[nonzero_expected] / np.abs(expected[finite][nonzero_expected])
zero_reference_divergence = bool(np.any((expected[finite] == 0) & (actual[finite] != 0)))
return {
"first_divergence": _first_difference(expected, actual),
"finite_pairs": int(finite.sum()),
"nonfinite_mismatches": int(np.count_nonzero(~(np.equal(expected, actual) | (np.isnan(expected) & np.isnan(actual))) & ~finite)),
"max_abs": float(delta.max()) if delta.size else 0.0,
"max_rel": float(relative.max()) if relative.size else 0.0,
"max_rel_unbounded_at_zero_cpu": zero_reference_divergence,
"max_ulp": float(_ulp(expected[finite], actual[finite]).max()) if delta.size else 0.0,
}
def _exact_stats(expected: np.ndarray, actual: np.ndarray) -> dict[str, Any]:
return {"exact": _first_difference(expected, actual) is None, "first_divergence": _first_difference(expected, actual)}
def _diagnose_one(ohlcv: Mapping[str, np.ndarray], multiplier: float, device: torch.device) -> dict[str, Any]:
tensors = {name: torch.as_tensor(ohlcv[name], dtype=torch.float64, device=device) for name in ("close", "high", "low")}
cpu = cpu_supertrend_trace(ohlcv["close"], ohlcv["high"], ohlcv["low"], PERIOD, multiplier)
gpu_trace = supertrend_trace(tensors["close"], tensors["high"], tensors["low"], PERIOD, multiplier)
if device.type == "cuda":
torch.cuda.synchronize(device)
gpu = {name: value.detach().cpu().numpy() for name, value in gpu_trace.items()}
missing_cpu = sorted(SUPERTREND_TRACE_KEYS - cpu.keys())
missing_gpu = sorted(SUPERTREND_TRACE_KEYS - gpu.keys())
if missing_cpu or missing_gpu:
raise RuntimeError(f"incomplete V1.2 Supertrend trace: CPU missing {missing_cpu}; GPU missing {missing_gpu}")
return {
"period": PERIOD,
"multiplier": multiplier,
"numeric": {name: _numeric_stats(cpu[name], gpu[name]) for name in NUMERIC_FIELDS},
"branch_predicates": {name: _exact_stats(cpu[name], gpu[name]) for name in sorted(PREDICATE_KEYS)},
"direction": _exact_stats(cpu["direction"], gpu["direction"]),
"transitions": {"direction_transition": _exact_stats(cpu["direction_transition"], gpu["direction_transition"])},
}
def diagnose(ohlcv: Mapping[str, np.ndarray], periods: list[int] | None = None, multiplier: float | None = None, device: torch.device | None = None) -> dict[str, Any]:
"""Diagnose one corpus; V1.2 semantics are fixed to period 10 and 2/3/4 multipliers."""
if periods not in (None, [PERIOD]) or multiplier not in (None, *MULTIPLIERS):
raise ValueError("V1.2 semantic diagnostic supports only period 10 and multipliers 2, 3, and 4")
if device is None:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
return {"artifact": ARTIFACT, "schema_version": "1.2", "device": str(device), "bars": len(ohlcv["close"]), "period": PERIOD, "records": [_diagnose_one(ohlcv, value, device) for value in MULTIPLIERS]}
def run(calibration_csv: Path, holdout_csv: Path, *, adversarial_length: int = 256, seed: int = 0, device: torch.device | None = None) -> dict[str, Any]:
"""Produce calibration, holdout, and deterministic adversarial diagnostic corpora."""
if adversarial_length < PERIOD + 1:
raise ValueError("adversarial length must be at least 11")
if device is None:
if not torch.cuda.is_available():
raise RuntimeError("CUDA GPU is required; pass device=torch.device('cpu') only for tests")
device = torch.device("cuda")
return {"artifact": ARTIFACT, "schema_version": "1.2", "device": str(device), "adversarial": {"generator": "deterministic_adversarial_ohlcv", "length": adversarial_length, "seed": seed}, "corpora": {"calibration": diagnose(_read_ohlcv(calibration_csv), device=device), "holdout": diagnose(_read_ohlcv(holdout_csv), device=device), "adversarial": diagnose(deterministic_adversarial_ohlcv(length=adversarial_length, seed=seed), device=device)}}
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--calibration-csv", type=Path, required=True)
parser.add_argument("--holdout-csv", type=Path, required=True)
parser.add_argument("--adversarial-length", type=int, default=256)
parser.add_argument("--seed", type=int, default=0)
parser.add_argument("--output", type=Path)
args = parser.parse_args()
payload = run(args.calibration_csv, args.holdout_csv, adversarial_length=args.adversarial_length, seed=args.seed)
encoded = json.dumps(payload, sort_keys=True, allow_nan=False) + "\n"
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(encoded, encoding="utf-8")
else:
print(encoded, end="")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,32 @@
{
"schema_version": 1,
"artifact": "HYPERSCALPER_CANDIDATE_TAXONOMY_V1",
"purpose": "Static, non-executable candidate taxonomy for feature-gap triage. Entries are hypotheses, not strategies or approvals.",
"evidence_labels": ["source_mapped", "historical_usage", "lineage_bias", "qualification_failure", "external_candidate", "requires_oracle", "requires_deq"],
"candidate_registry": [
{"candidate_id": "spot_perp_basis", "market": "crypto", "venue_scope": "spot_perpetual", "family": "cross_market_basis", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "perp_funding_pressure", "market": "crypto", "venue_scope": "perpetual", "family": "derivatives_positioning", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "options_implied_realized_vol", "market": "crypto", "venue_scope": "options_spot", "family": "cross_market_volatility", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "exchange_orderbook_imbalance", "market": "crypto", "venue_scope": "single_exchange", "family": "microstructure", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "cross_exchange_dislocation", "market": "crypto", "venue_scope": "multi_exchange", "family": "cross_venue_execution", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "stablecoin_flow_stress", "market": "crypto", "venue_scope": "onchain_cex", "family": "liquidity_flow", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "equity_index_futures_cash_basis", "market": "equity", "venue_scope": "cash_futures", "family": "cross_market_basis", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "etf_constituent_dispersion", "market": "equity", "venue_scope": "etf_cash", "family": "cross_asset_dispersion", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "adr_local_listing_spread", "market": "equity", "venue_scope": "cross_listing", "family": "cross_venue_basis", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "index_option_skew", "market": "equity", "venue_scope": "options_cash", "family": "implied_distribution", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "treasury_curve_slope", "market": "rates", "venue_scope": "cash_futures", "family": "curve_relative_value", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "swap_spread", "market": "rates", "venue_scope": "otc_futures", "family": "cross_market_basis", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "fx_spot_forward_carry", "market": "fx", "venue_scope": "spot_forwards", "family": "carry_basis", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "fx_cross_pair_relative_strength", "market": "fx", "venue_scope": "cross_pair", "family": "cross_asset_momentum", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "commodity_calendar_spread", "market": "commodity", "venue_scope": "futures_curve", "family": "term_structure", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "commodity_crack_or_crush_spread", "market": "commodity", "venue_scope": "intercommodity", "family": "processing_margin", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "volatility_term_structure", "market": "volatility", "venue_scope": "spot_futures_options", "family": "term_structure", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]},
{"candidate_id": "cross_asset_risk_regime", "market": "multi_asset", "venue_scope": "equity_rates_fx_commodity_crypto", "family": "regime", "evidence": ["external_candidate", "requires_oracle", "requires_deq"]}
],
"expansion_waves": [
{"wave": 0, "name": "evidence_reconciliation", "admission_evidence": ["source_mapped", "historical_usage", "lineage_bias"], "not_a_strategy": true},
{"wave": 1, "name": "oracle_redundancy_gaps", "admission_evidence": ["requires_oracle", "qualification_failure"], "not_a_strategy": true},
{"wave": 2, "name": "cross_market_data_contracts", "admission_evidence": ["external_candidate", "requires_oracle"], "not_a_strategy": true},
{"wave": 3, "name": "decision_equivalence_gate", "admission_evidence": ["requires_deq"], "not_a_strategy": true}
]
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
# HYPERSCALPER Feature Gap Analysis V1
Read-only: yes. Strategy execution: not performed.
- Information map: 711/711 features
- Acceptance: not_proven
- Redundancy clusters: 39
- DEQ: AVAILABLE (supplied Cohort001 ledger; multi-feature attribution is associative, not causal)
- Blockers: 0
## Blockers

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,310 @@
#!/usr/bin/env python3
"""Synthesize evidence-backed, OHLCV-only Wave 1 feature candidates.
This is a read-only reporting tool. It does not evaluate formulas, search
strategies, infer data provenance, or turn associative evidence into causality.
"""
from __future__ import annotations
import argparse
import hashlib
import json
from collections import defaultdict
from pathlib import Path
from typing import Any
ARTIFACT = "HYPERSCALPER_WAVE1_FEATURE_SYNTHESIS_V1"
MIN_CANDIDATES = 20
MAX_CANDIDATES = 50
ASSOCIATIVE_LIMITATION = (
"Evidence is observational and multi-feature attribution is associative, not causal."
)
def load_json(path: Path) -> dict[str, Any]:
value = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(value, dict):
raise ValueError(f"{path} must contain a JSON object")
return value
def source_record(path: Path | None) -> dict[str, Any]:
if path is None:
return {"status": "UNAVAILABLE", "reason": "not supplied"}
return {
"status": "AVAILABLE",
"path": str(path),
"sha256": hashlib.sha256(path.read_bytes()).hexdigest(),
}
def rows(value: dict[str, Any], *names: str) -> list[dict[str, Any]]:
for name in names:
found = value.get(name)
if isinstance(found, list):
return [item for item in found if isinstance(item, dict)]
return []
def feature_key(row: dict[str, Any]) -> str | None:
value = row.get("feature_key")
if value is not None:
return str(value)
try:
return f"{int(row['indicator_id'])}:{int(row['period'])}:{float(row['p1']):g}"
except (KeyError, TypeError, ValueError):
return None
def is_ohlcv_only(row: dict[str, Any]) -> bool:
"""Accept only an explicit, finite OHLCV dependency declaration."""
scope = str(row.get("input_scope", row.get("data_scope", ""))).upper()
if scope in {"OHLCV", "OHLCV_ONLY"}:
return True
dependencies = row.get("data_dependencies", row.get("inputs"))
if not isinstance(dependencies, list) or not dependencies:
return False
return {str(item).lower() for item in dependencies} <= {
"open",
"high",
"low",
"close",
"volume",
}
def numeric(value: Any) -> float:
return float(value) if isinstance(value, (int, float)) and not isinstance(value, bool) else 0.0
def failure_evidence(payload: dict[str, Any]) -> dict[str, list[dict[str, Any]]]:
result: dict[str, list[dict[str, Any]]] = defaultdict(list)
for row in rows(payload, "feature_failure_comparisons", "features", "comparisons"):
key = feature_key(row)
if key:
result[key].append(row)
return result
def deq_evidence(payload: dict[str, Any]) -> dict[str, dict[str, Any]]:
deq = payload.get("decision_equivalence", payload)
if not isinstance(deq, dict):
return {}
return {
str(row["feature_key"]): row
for row in rows(deq, "feature_summaries", "features")
if row.get("feature_key") is not None
}
def redundancy_evidence(payload: dict[str, Any]) -> dict[str, dict[str, Any]]:
result: dict[str, dict[str, Any]] = {}
for cluster in rows(payload.get("redundancy", payload), "clusters"):
for key in cluster.get("members", []):
result[str(key)] = {
"cluster_id": cluster.get("cluster_id"),
"cluster_size": cluster.get("size"),
"representative_feature_key": cluster.get("representative_feature_key"),
}
return result
def synthesis(
semantic: dict[str, Any],
redundancy: dict[str, Any],
failure: dict[str, Any] | None,
deq: dict[str, Any],
lineage_gap: dict[str, Any],
) -> tuple[list[dict[str, Any]], list[str], int]:
feature_rows = rows(semantic, "features", "primitives", "rows", "semantic_map")
if not feature_rows:
feature_rows = rows(lineage_gap.get("information_map", {}), "features")
failures = failure_evidence(failure) if failure else {}
deq_by_feature = deq_evidence(deq)
clusters = redundancy_evidence(redundancy)
lineage_by_feature = {
str(row.get("feature_key")): row
for row in rows(lineage_gap.get("information_map", {}), "features")
if row.get("feature_key") is not None
}
excluded_non_ohlcv = 0
candidates = []
for row in feature_rows:
key = feature_key(row)
if key is None:
continue
if not is_ohlcv_only(row):
excluded_non_ohlcv += 1
continue
lineage = lineage_by_feature.get(key, row)
feature_failures = failures.get(key, [])
candidates.append(
{
"candidate_id": f"ohlcv:{key}",
"feature_key": key,
"indicator_id": row.get("indicator_id"),
"period": row.get("period"),
"p1": row.get("p1"),
"domain": row.get("domain", row.get("semantic_domain", "unclassified")),
"output_type": row.get("output_type", row.get("semantic_type", "unclassified")),
"input_scope": "OHLCV_ONLY",
"evidence": {
"semantic": {"feature_key": key, "explicit_ohlcv_only": True},
"lineage_gap": {
"historical_usage": lineage.get("historical_usage"),
"lineage_mentions": lineage.get("lineage_mentions"),
"max_redundancy": lineage.get("max_redundancy"),
"redundancy_status": lineage.get("redundancy_status"),
},
"redundancy_cluster": clusters.get(key),
"failure_mining": feature_failures or None,
"decision_equivalence": deq_by_feature.get(key),
},
"limitations": [
ASSOCIATIVE_LIMITATION,
"Not a strategy approval or causal feature claim.",
],
}
)
# Evidence ordering only: failure contrast, DEQ observations, lineage use, then redundancy.
def rank(candidate: dict[str, Any]) -> tuple[float, float, float, float, str]:
failure_rows = candidate["evidence"]["failure_mining"] or []
failure_strength = max(
(numeric(item.get("ks_statistic")) for item in failure_rows), default=0.0
)
deq_row = candidate["evidence"]["decision_equivalence"] or {}
deq_support = numeric(deq_row.get("trade_count"))
lineage = candidate["evidence"]["lineage_gap"]
usage = numeric(lineage.get("historical_usage")) + numeric(lineage.get("lineage_mentions"))
redundancy_value = lineage.get("max_redundancy")
redundancy = numeric(redundancy_value) if redundancy_value is not None else 1.0
return (-failure_strength, -deq_support, -usage, redundancy, candidate["feature_key"])
return sorted(candidates, key=rank), [], excluded_non_ohlcv
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--semantic-summary", type=Path, required=True)
parser.add_argument("--redundancy-clusters", type=Path, required=True)
parser.add_argument("--deq-summary", type=Path, required=True)
parser.add_argument("--lineage-gap-analysis", type=Path, required=True)
parser.add_argument(
"--failure-mining", type=Path, help="Copied Spark failure-mining JSON when available"
)
parser.add_argument("--output-dir", type=Path, required=True)
args = parser.parse_args()
required = (
args.semantic_summary,
args.redundancy_clusters,
args.deq_summary,
args.lineage_gap_analysis,
)
if any(not path.is_file() for path in required) or (
args.failure_mining and not args.failure_mining.is_file()
):
parser.error("all supplied artifact paths must be files")
semantic, redundancy, deq, lineage = (load_json(path) for path in required)
failure = load_json(args.failure_mining) if args.failure_mining else None
candidates, blockers, excluded_non_ohlcv = synthesis(
semantic, redundancy, failure, deq, lineage
)
selected = candidates[:MAX_CANDIDATES]
if args.failure_mining is None:
blockers.append(
"Spark failure-mining result is unavailable; copy its JSON before relying on "
"failure evidence."
)
if len(selected) < MIN_CANDIDATES:
blockers.append(
f"Only {len(selected)} explicitly OHLCV-only candidates are evidenced; refusing to "
f"invent the required {MIN_CANDIDATES}-{MAX_CANDIDATES} selection."
)
status = "READY" if not blockers else "BLOCKED"
sources = {
"semantic_summary": source_record(args.semantic_summary),
"redundancy_clusters": source_record(args.redundancy_clusters),
"failure_mining": source_record(args.failure_mining),
"deq_summary": source_record(args.deq_summary),
"lineage_gap_analysis": source_record(args.lineage_gap_analysis),
}
common = {
"schema_version": 1,
"artifact": ARTIFACT,
"read_only": True,
"strategy_search": "not performed",
"associative_limitations": ASSOCIATIVE_LIMITATION,
"status": status,
"sources": sources,
"blockers": blockers,
}
taxonomy = {
**common,
"artifact": "FEATURE_GAP_TAXONOMY_V1",
"candidates": selected,
"excluded_without_explicit_ohlcv_only_declaration": excluded_non_ohlcv,
}
plan = {
**common,
"artifact": "FEATURE_EXPANSION_PLAN_V1",
"wave": 1,
"admission_rule": "Explicit OHLCV-only declaration and evidence-ranked source records.",
"candidate_count": len(selected),
"candidates": [item["candidate_id"] for item in selected],
}
registry = {**common, "artifact": "NEW_FEATURE_CANDIDATE_REGISTRY_V1", "candidates": selected}
spec = {
**common,
"artifact": "HYPERSCALPER_WAVE1_FEATURE_SPEC_V1",
"features": selected,
"implementation_constraint": (
"Implement features from OHLCV inputs only; no strategy search or causal claims."
),
}
args.output_dir.mkdir(parents=True, exist_ok=True)
outputs = {
"feature_gap_taxonomy_v1.json": taxonomy,
"feature_expansion_plan_v1.json": plan,
"new_feature_candidate_registry_v1.json": registry,
"hyperscalper_wave1_feature_spec_v1.json": spec,
}
for name, payload in outputs.items():
(args.output_dir / name).write_text(
json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
markdown = (
"# HYPERSCALPER Wave 1 Feature Spec V1\n\n"
f"Status: {status}. Read-only: yes. Strategy search: not performed.\n\n"
f"Selected candidates: {len(selected)}. Explicitly excluded without OHLCV-only evidence: "
f"{excluded_non_ohlcv}.\n\n"
"## Limitations\n\n"
f"- {ASSOCIATIVE_LIMITATION}\n"
"- No feature is a strategy, approval, or causal claim.\n\n"
"## Candidates\n\n"
+ "\n".join(
f"- `{item['candidate_id']}`: {item['domain']} / {item['output_type']}"
for item in selected
)
+ "\n\n## Blockers\n\n"
+ "\n".join(f"- {item}" for item in blockers)
+ "\n"
)
(args.output_dir / "hyperscalper_wave1_feature_spec_v1.md").write_text(
markdown, encoding="utf-8"
)
executive = (
f"Wave 1 synthesis is {status}: {len(selected)} evidence-ranked, explicitly OHLCV-only "
"candidates. "
f"Failure mining is {'available' if failure else 'unavailable'}. {ASSOCIATIVE_LIMITATION}\n"
)
(args.output_dir / "hyperscalper_wave1_executive_summary_v1.md").write_text(
executive, encoding="utf-8"
)
print(args.output_dir / "feature_gap_taxonomy_v1.json")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail
# Usage: ./run_gpu_feature_batch01_v1.sh [cache-directory]
# The source mounts are read-only; only the cache directory is writable.
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CACHE_DIR="${1:-${ROOT}/.gpu-feature-cache}"
mkdir -p "${CACHE_DIR}"
docker build -f "${ROOT}/Dockerfile.gpu-feature-v1" -t artifex-gpu-feature-v1 "${ROOT}"
docker run --rm --gpus all \
--mount "type=bind,src=${ROOT}/hs22_oracle_v1,dst=/data,readonly" \
--mount "type=bind,src=${ROOT},dst=/oracle,readonly" \
--mount "type=bind,src=${CACHE_DIR},dst=/cache" \
artifex-gpu-feature-v1 --mode parity --report /cache/batch01_gpu_parity_report.json
echo "Wrote ${CACHE_DIR}/batch01_gpu_parity_report.json"

View file

@ -0,0 +1,230 @@
"""Generate a read-only semantic map for the 711 historical primitives.
The generator consumes engineering/source metadata only. It never imports or
evaluates a historical formula, and optional checkpoints are inspected solely
to count already-materialized finite observations.
"""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
from typing import Any
import numpy as np
import pyarrow as pa
import pyarrow.parquet as pq
EXPECTED_PRIMITIVES = 711
def digest(path: Path) -> str:
if path.is_file():
return hashlib.sha256(path.read_bytes()).hexdigest()
hasher = hashlib.sha256()
for item in sorted(path.glob("*.npy")):
hasher.update(item.name.encode("utf-8"))
hasher.update(b"\0")
hasher.update(bytes.fromhex(digest(item)))
return hasher.hexdigest()
def key_for(row: dict[str, Any]) -> tuple[int, int, float]:
return int(row["indicator_id"]), int(row["period"]), float(row["p1"])
def key_text(key: tuple[int, int, float]) -> str:
return f"{key[0]}:{key[1]}:{key[2]:g}"
def composite_key(value: Any) -> tuple[int, int, float]:
"""Decode canonical primitive keys stored as strings or Parquet structs."""
if isinstance(value, dict):
return key_for(value)
if isinstance(value, (list, tuple)) and len(value) == 3:
return int(value[0]), int(value[1]), float(value[2])
if not isinstance(value, str):
raise ValueError(f"invalid composite primitive key: {value!r}")
parts = value.strip().split(":")
if len(parts) != 3:
parts = value.strip().split("_")
if len(parts) == 4 and parts[0] == "hs22":
parts = parts[1:]
if len(parts) != 3:
raise ValueError(f"invalid composite primitive key: {value!r}")
try:
return int(parts[0]), int(parts[1]), float(parts[2])
except ValueError as error:
raise ValueError(f"invalid composite primitive key: {value!r}") from error
def source_name(row: dict[str, Any]) -> str:
return str(row.get("source_function", "")).lower()
def semantic_mapping(row: dict[str, Any]) -> tuple[str, str, str, str, str]:
"""Return domain, subdomain, type, provenance, and deterministic rule id."""
name = source_name(row)
family = str(row.get("engineering_family", ""))
rules = (
(("cross_", "crossover", "crossunder", "breakout", "divergence"), "price_action", "crossing_or_breakout", "event", "source_derived", "explicit_event_name"),
(("regime_", "entropy_", "chop", "fractal_dimension"), "market_regime", "regime_detection", "state", "source_derived", "explicit_regime_name"),
(("time_", "session", "weekday", "hour"), "time_context", "session_or_calendar", "state", "source_derived", "explicit_time_name"),
(("micro_", "orderflow", "vwap", "obv", "mfi", "cmf", "volume"), "volume_and_flow", "volume_flow", "continuous", "source_derived", "explicit_volume_name"),
(("osc_", "rsi", "stoch", "cci", "williams", "roc", "momentum"), "momentum", "oscillator_or_rate", "continuous", "source_derived", "explicit_momentum_name"),
(("bb_", "kelt_", "donch_", "ichimoku_", "supertrend", "psar", "channel"), "volatility", "bands_and_channels", "state", "source_derived", "explicit_band_name"),
(("vol_", "atr", "true_range", "variance", "std", "range"), "volatility", "range_and_dispersion", "continuous", "source_derived", "explicit_volatility_name"),
(("ma_", "ema", "sma", "wma", "tema", "dema"), "trend", "moving_average", "continuous", "source_derived", "explicit_average_name"),
(("adx", "aroon_", "linreg_", "trend_"), "trend", "direction_and_strength", "continuous", "source_derived", "explicit_trend_name"),
(("pivot_", "prev_", "rolling_", "quantile_"), "price_structure", "reference_level", "state", "source_derived", "explicit_reference_name"),
(("norm_", "zscore", "percentile"), "normalization", "scaled_price_or_signal", "continuous", "source_derived", "explicit_normalization_name"),
)
for prefixes, domain, subdomain, value_type, provenance, rule in rules:
if any(prefix in name for prefix in prefixes):
return domain, subdomain, value_type, provenance, rule
family_fallbacks = {
"moving_average": ("trend", "moving_average", "continuous"),
"oscillator": ("momentum", "oscillator_or_rate", "continuous"),
"volatility": ("volatility", "range_and_dispersion", "continuous"),
"regime": ("market_regime", "regime_detection", "state"),
"microstructure": ("market_microstructure", "price_volume_structure", "continuous"),
"momentum": ("momentum", "oscillator_or_rate", "continuous"),
"trend_structure": ("trend", "direction_and_strength", "continuous"),
"normalization": ("normalization", "scaled_price_or_signal", "continuous"),
"cross_indicator": ("price_action", "crossing_or_breakout", "event"),
"time_session": ("time_context", "session_or_calendar", "state"),
"reference_level": ("price_structure", "reference_level", "state"),
"band_channel": ("volatility", "bands_and_channels", "state"),
}
domain, subdomain, value_type = family_fallbacks.get(
family, ("specialized", "unclassified_source_function", "continuous")
)
return domain, subdomain, value_type, "heuristic", "engineering_family_fallback"
def load_engineering_map(path: Path) -> list[dict[str, Any]]:
payload = json.loads(path.read_text(encoding="utf-8"))
if payload.get("artifact") != "ENGINEERING_FAMILY_MAP_V1":
raise ValueError("--engineering-map is not ENGINEERING_FAMILY_MAP_V1")
rows = payload.get("primitives")
if not isinstance(rows, list):
raise ValueError("engineering map has no primitives list")
keys = [key_for(row) for row in rows]
if len(rows) != EXPECTED_PRIMITIVES or len(set(keys)) != EXPECTED_PRIMITIVES:
raise ValueError("engineering map must contain exactly 711 unique primitives")
return rows
def usage_counts(path: Path, expected_indicator_ids: set[int]) -> dict[int, int]:
"""Load indicator-level usage; it cannot distinguish parameterized primitives."""
counts: dict[int, int] = {}
source = pq.ParquetFile(path)
columns = set(source.schema.names)
required = {"entity_type", "indicator_id", "usage_count"}
if missing := required - columns:
raise ValueError(f"usage parquet is missing required columns: {', '.join(sorted(missing))}")
for batch in source.iter_batches(columns=["entity_type", "indicator_id", "usage_count"]):
for row in batch.to_pylist():
if row["entity_type"] != "indicator_id":
continue
indicator_id = int(row["indicator_id"])
if indicator_id not in expected_indicator_ids:
continue
if indicator_id in counts:
raise ValueError(f"usage parquet has duplicate indicator row: {indicator_id}")
counts[indicator_id] = int(row["usage_count"])
if set(counts) != expected_indicator_ids:
raise ValueError("usage parquet does not cover every engineering indicator ID")
return counts
def checkpoint_counts(directory: Path, expected: set[tuple[int, int, float]]) -> tuple[dict[tuple[int, int, float], int], dict[tuple[int, int, float], str]]:
counts: dict[tuple[int, int, float], int] = {}
names: dict[tuple[int, int, float], str] = {}
for path in sorted(directory.glob("*.npy")):
parts = path.stem.split("_")
if len(parts) != 4 or parts[0] != "hs22":
raise ValueError(f"invalid checkpoint filename: {path.name}")
try:
key = int(parts[1]), int(parts[2]), float(parts[3])
except ValueError as error:
raise ValueError(f"invalid checkpoint filename: {path.name}") from error
if key not in expected or key in counts:
raise ValueError(f"checkpoint is not a unique engineering primitive: {path.name}")
counts[key] = int(np.isfinite(np.load(path, allow_pickle=False)).sum())
names[key] = path.name
if set(counts) != expected:
raise ValueError("checkpoint directory does not cover every engineering primitive")
return counts, names
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--engineering-map", type=Path, required=True)
parser.add_argument("--usage-parquet", type=Path)
parser.add_argument("--checkpoint-dir", type=Path)
parser.add_argument("--output-dir", type=Path, required=True)
args = parser.parse_args()
if args.usage_parquet and not args.usage_parquet.is_file():
parser.error("--usage-parquet must be a file")
if args.checkpoint_dir and not args.checkpoint_dir.is_dir():
parser.error("--checkpoint-dir must be a directory")
engineering = load_engineering_map(args.engineering_map)
expected = {key_for(row) for row in engineering}
expected_indicator_ids = {key[0] for key in expected}
usage = usage_counts(args.usage_parquet, expected_indicator_ids) if args.usage_parquet else {}
finite, checkpoint_names = checkpoint_counts(args.checkpoint_dir, expected) if args.checkpoint_dir else ({}, {})
rows = []
for engineering_row in engineering:
key = key_for(engineering_row)
domain, subdomain, value_type, provenance, rule = semantic_mapping(engineering_row)
rows.append({
"feature_key": key_text(key), "indicator_id": key[0], "period": key[1], "p1": key[2],
"source_function": engineering_row.get("source_function"),
"source_expression": engineering_row.get("source_expression"),
"engineering_family": engineering_row.get("engineering_family"),
"semantic_domain": domain, "semantic_subdomain": subdomain, "semantic_type": value_type,
"semantic_mapping_provenance": provenance, "semantic_mapping_rule": rule,
"usage_count": usage.get(key[0]),
"usage_grain": "indicator_id_replicated_not_parameter_specific" if args.usage_parquet else None,
"finite_observation_count": finite.get(key),
"checkpoint_file": checkpoint_names.get(key),
})
args.output_dir.mkdir(parents=True, exist_ok=True)
parquet_path = args.output_dir / "historical_feature_semantic_domain_map_v1.parquet"
pq.write_table(pa.Table.from_pylist(rows), parquet_path, compression="zstd")
sources: dict[str, dict[str, str]] = {"engineering_map": {"path": str(args.engineering_map), "sha256": digest(args.engineering_map)}}
if args.usage_parquet:
sources["usage_parquet"] = {"path": str(args.usage_parquet), "sha256": digest(args.usage_parquet)}
if args.checkpoint_dir:
sources["checkpoint_dir"] = {"path": str(args.checkpoint_dir), "sha256": digest(args.checkpoint_dir)}
summary = {
"schema_version": 1,
"artifact": "HISTORICAL_FEATURE_SEMANTIC_DOMAIN_MAP_V1_SUMMARY",
"method": "read-only source-function semantic classification; explicit name rules are source-derived and family fallbacks are heuristic; no historical formulas were imported or evaluated",
"source_artifacts": sources,
"counts": {
"primitives": len(rows),
"semantic_domains": len({row["semantic_domain"] for row in rows}),
"semantic_subdomains": len({row["semantic_subdomain"] for row in rows}),
"source_derived_mappings": sum(row["semantic_mapping_provenance"] == "source_derived" for row in rows),
"heuristic_mappings": sum(row["semantic_mapping_provenance"] == "heuristic" for row in rows),
"usage_count_total_replicated_across_primitives": sum(row["usage_count"] or 0 for row in rows),
"finite_observation_count_total": sum(row["finite_observation_count"] or 0 for row in rows),
},
"semantic_type_counts": {kind: sum(row["semantic_type"] == kind for row in rows) for kind in ("continuous", "state", "event")},
"usage_grain": "indicator_id_replicated_not_parameter_specific" if args.usage_parquet else None,
"artifacts": {"semantic_domain_map_parquet": str(parquet_path), "semantic_domain_map_parquet_sha256": digest(parquet_path)},
}
(args.output_dir / "historical_feature_semantic_domain_map_v1_summary.json").write_text(
json.dumps(summary, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
if __name__ == "__main__":
main()

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,61 @@
from __future__ import annotations
import json
import sys
import numpy as np
import pytest
import cohort001_feature_failure_mining_v1 as runner
def test_entry_bar_uses_exported_column_then_raw_ledger_and_rejects_timestamps():
assert runner.entry_bar({"entry_bar": 4}) == (4, "column.entry_bar")
assert runner.entry_bar({"raw_ledger_json": '{"entry_index":3}'}) == (3, "raw_ledger_json.entry_index")
assert runner.entry_bar({"entry_at": "2026-01-01T00:00:00Z"}) == (None, None)
def test_json_compatible_normalizes_numpy_scalars_and_nonfinite_floats():
payload = runner.json_compatible({
"integer": np.int64(7),
"float": np.float32(1.25),
"boolean": np.bool_(True),
"nested": [np.float64(np.nan), np.float64(np.inf), float("-inf")],
})
assert payload == {
"integer": 7,
"float": pytest.approx(1.25),
"boolean": True,
"nested": [None, None, None],
}
assert json.loads(json.dumps(payload, allow_nan=False))["nested"] == [None, None, None]
def test_runner_mines_entry_values_and_skips_unmappable_rows(tmp_path, monkeypatch):
pa = pytest.importorskip("pyarrow")
pq = pytest.importorskip("pyarrow.parquet")
engineering = tmp_path / "engineering.json"
engineering.write_text(json.dumps({"primitives": [{"indicator_id": 1, "period": 10, "p1": 0}]}))
semantic = tmp_path / "semantic.parquet"
pq.write_table(pa.Table.from_pylist([{"feature_key": "1:10:0", "domain": "trend", "output_type": "continuous"}]), semantic)
checkpoints = tmp_path / "checkpoints"
checkpoints.mkdir()
np.save(checkpoints / "hs22_1_10_0.npy", np.array([1.0, 2.0, 8.0, 9.0]))
context = tmp_path / "context.parquet"
pq.write_table(pa.Table.from_pylist([
{"failure_label": "GOOD_ENTRY", "entry_bar": 0, "fold": "F1", "feature_triples_json": '[{"indicator_id":1,"period":10,"p1":0}]'},
{"failure_label": "GOOD_ENTRY", "entry_bar": 1, "fold": "F2", "feature_triples_json": '[{"indicator_id":1,"period":10,"p1":0}]'},
{"failure_label": "WRONG_DIRECTION", "entry_bar": 2, "fold": "F1", "feature_triples_json": '[{"indicator_id":1,"period":10,"p1":0}]'},
{"failure_label": "WRONG_DIRECTION", "entry_bar": 3, "fold": "F2", "feature_triples_json": '[{"indicator_id":1,"period":10,"p1":0}]'},
{"failure_label": "WRONG_DIRECTION", "entry_at": "not a bar", "feature_triples_json": '[]'},
]), context)
output = tmp_path / "output"
monkeypatch.setattr(sys, "argv", ["runner", "--failure-context", str(context), "--semantic-map", str(semantic), "--oracle-checkpoint-dir", str(checkpoints), "--engineering-map", str(engineering), "--output-dir", str(output)])
runner.main()
result = json.loads((output / "cohort001_feature_failure_mining_v1.json").read_text())
item = result["feature_failure_comparisons"][0]
assert item["median_difference_failure_minus_good"] == pytest.approx(7.0)
assert item["fold_consistency"] == 1.0
assert result["skipped"]["unmappable_entry_bar"] == 1
assert item["attribution"] == "ASSOCIATIVE_NOT_CAUSAL"

View file

@ -0,0 +1,41 @@
from control_plane.trading_studio.management.commands.export_cohort_failure_context_v1 import Command
def test_failure_labels_are_explicit_and_precedence_is_deterministic():
cases = [
({"net_pnl": -1}, {"return_1_bars_bps": 5}, "GOOD_SIGNAL_BAD_MONETIZATION"),
({"net_pnl": 1}, {"winner_negative_first": True}, "RECOVERY_DEPENDENT"),
({}, {"mae_bps": -25}, "HIGH_MAE_ENTRY"),
({}, {"return_1_bars_bps": -5}, "WRONG_DIRECTION"),
({}, {"return_1_bars_bps": 0, "return_5_bars_bps": 5}, "LATE_SIGNAL"),
({"net_pnl": 1}, {"return_1_bars_bps": 5}, "GOOD_ENTRY"),
({}, {}, "NO_EDGE"),
]
for ledger, deq, expected in cases:
assert Command._label(ledger, deq)[0] == expected
def test_rows_preserve_raw_ledger_deq_genome_context_and_rescue():
payload = {
"cohort_id": "cohort", "dataset_version_id": "dataset", "dataset_sha256": "hash",
"strategies": [{
"ordinal": 1, "membership_id": "member", "strategy_version_id": "strategy",
"runner_name": "runner", "genome": {"combo": "g"}, "feature_triples": [],
"folds": [{"fold": "F1", "scenarios": [{
"qualification_run_id": "run", "scenario": "BASE",
"summary": {"rescue": {"recovered": True}},
"deq_samples": [{"identity": "trade", "entry_bar": 2, "entry_execution_price": 10,
"take_profit_price": 11, "context": {"regime": "x"},
"deq": {"return_1_bars_bps": 5}}],
}]}],
}],
}
row = Command._rows(payload)[0]
assert row["direction"] == "LONG"
assert '"return_1_bars_bps":5' in row["raw_deq_json"]
assert '"combo":"g"' in row["genome_json"]
assert '"regime":"x"' in row["context_json"]
assert '"recovered":true' in row["rescue_json"]

View file

@ -0,0 +1,92 @@
from __future__ import annotations
import json
import sys
import numpy as np
import pytest
pytest.importorskip("pyarrow")
from scripts import generate_historical_feature_semantic_domain_map_v1 as generator
def engineering_rows() -> list[dict[str, object]]:
names = ["ma_fast", "cross_over", "regime_entropy", "pivot_high", "mystery"]
return [
{
"indicator_id": index,
"period": 10,
"p1": float(index % 2),
"source_function": names[index % len(names)],
"source_expression": names[index % len(names)] + "(close)",
"engineering_family": "specialized" if index % len(names) == 4 else "moving_average",
}
for index in range(711)
]
def test_generator_writes_711_semantic_rows_with_optional_evidence(tmp_path, monkeypatch):
import pyarrow as pa
import pyarrow.parquet as pq
rows = engineering_rows()
# The map may contain multiple parameterized primitives for one indicator.
rows[-1] = {**rows[-1], "indicator_id": 0, "period": 11, "p1": 0.0}
map_path = tmp_path / "engineering.json"
map_path.write_text(json.dumps({"artifact": "ENGINEERING_FAMILY_MAP_V1", "primitives": rows}), encoding="utf-8")
usage_path = tmp_path / "usage.parquet"
pq.write_table(pa.Table.from_pylist([
{"entity_type": "indicator_id", "indicator_id": indicator_id, "usage_count": indicator_id + 1}
for indicator_id in sorted({int(row["indicator_id"]) for row in rows})
]), usage_path)
checkpoints = tmp_path / "checkpoints"
checkpoints.mkdir()
for row in rows:
values = np.array([1.0, np.nan]) if row["indicator_id"] else np.array([np.nan, np.nan])
np.save(
checkpoints / f"hs22_{row['indicator_id']}_{row['period']}_{row['p1']:g}.npy",
values,
allow_pickle=False,
)
output = tmp_path / "output"
monkeypatch.setattr(sys, "argv", ["generator", "--engineering-map", str(map_path), "--usage-parquet", str(usage_path), "--checkpoint-dir", str(checkpoints), "--output-dir", str(output)])
generator.main()
result = pq.read_table(output / "historical_feature_semantic_domain_map_v1.parquet").to_pylist()
summary = json.loads((output / "historical_feature_semantic_domain_map_v1_summary.json").read_text())
assert len(result) == 711
assert result[0]["semantic_domain"] == "trend"
assert result[1]["semantic_type"] == "event"
assert result[2]["semantic_type"] == "state"
assert result[4]["semantic_mapping_provenance"] == "heuristic"
assert result[0]["usage_count"] == 1
assert result[0]["usage_grain"] == "indicator_id_replicated_not_parameter_specific"
assert result[-1]["usage_count"] == 1
assert result[-1]["usage_grain"] == "indicator_id_replicated_not_parameter_specific"
assert result[0]["finite_observation_count"] == 0
assert result[1]["finite_observation_count"] == 1
assert summary["counts"]["primitives"] == 711
assert summary["usage_grain"] == "indicator_id_replicated_not_parameter_specific"
def test_usage_counts_requires_all_engineering_indicator_ids(tmp_path):
import pyarrow as pa
import pyarrow.parquet as pq
path = tmp_path / "usage.parquet"
pq.write_table(pa.Table.from_pylist([{"entity_type": "indicator_id", "indicator_id": 1, "usage_count": 1}]), path)
with pytest.raises(ValueError, match="does not cover every"):
generator.usage_counts(path, {1, 2})
def test_usage_counts_replicates_indicator_count_for_parameterized_primitives(tmp_path):
import pyarrow as pa
import pyarrow.parquet as pq
path = tmp_path / "usage.parquet"
pq.write_table(pa.Table.from_pylist([
{"entity_type": "indicator_id", "indicator_id": 1, "usage_count": 7},
{"entity_type": "other", "indicator_id": 1, "usage_count": 3},
]), path)
assert generator.usage_counts(path, {1}) == {1: 7}

View file

@ -0,0 +1,23 @@
from __future__ import annotations
import numpy as np
import pytest
pytest.importorskip("torch")
from gpu_batch01_v1_1_runner import read_ohlcv, role_semantics, write_ohlcv_csv
def test_runner_csv_round_trip_and_explicit_missing_role_semantics(tmp_path):
path = tmp_path / "ohlcv.csv"
expected = {
"close": np.array([100.0, 101.0]),
"high": np.array([101.0, 102.0]),
"low": np.array([99.0, 100.0]),
"volume": np.array([1000.0, 1001.0]),
}
write_ohlcv_csv(path, expected)
actual = read_ohlcv(path)
assert all(np.array_equal(actual[name], values) for name, values in expected.items())
semantics = role_semantics(None)
assert semantics["status"] == "not_reconstructable"
assert "no strategy-role assignment" in semantics["reason"]

View file

@ -0,0 +1,22 @@
from __future__ import annotations
import pytest
pytest.importorskip("torch")
from gpu_batch01_v1_2_runner import DEFAULT_ATR_LIMITS, _atr_limits
def test_v1_2_uses_fixed_atr_defaults_covering_documented_drift():
assert _atr_limits(None) == DEFAULT_ATR_LIMITS
assert DEFAULT_ATR_LIMITS["max_absolute_error"] >= 1.4210854715202004e-14
@pytest.mark.parametrize("limits", [
{"max_absolute_error": -1.0, "mae": 0.0},
{"max_absolute_error": float("nan"), "mae": 0.0},
{"max_absolute_error": 0.0},
])
def test_v1_2_rejects_invalid_atr_limits(limits):
with pytest.raises(ValueError, match="finite, non-negative"):
_atr_limits(limits)

View file

@ -0,0 +1,111 @@
from __future__ import annotations
import csv
import numpy as np
import pytest
from gpu_feature_parity_contract_v1_1 import (
ARTIFACT,
FrozenContractError,
SCENARIOS,
calibrate,
compare_stateful_trace,
corpus_manifest,
cpu_psar_trace,
cpu_supertrend_trace,
deterministic_adversarial_ohlcv,
freeze_contract,
historical_cpu_oracle,
nan_gap_semantics_manifest,
validate_frozen_contract,
)
def _evidence():
ohlcv = deterministic_adversarial_ohlcv(length=96, seed=7)
requests = [{"request_id": "super", "indicator_id": 19, "period": 8, "p1": 2.0}]
oracle = {"super": cpu_supertrend_trace(ohlcv["close"], ohlcv["high"], ohlcv["low"], 8, 2.0)["output"]}
corpus = corpus_manifest({"adversarial": ohlcv}, {"generator": "deterministic_adversarial_ohlcv", "seed": 7})
calibration = calibrate(oracle, oracle, requests, ohlcv["close"], corpus)
return ohlcv, oracle, freeze_contract(calibration)
def test_adversarial_generator_is_deterministic_and_manifest_has_provenance():
first = deterministic_adversarial_ohlcv(length=96, seed=3)
second = deterministic_adversarial_ohlcv(length=96, seed=3)
assert all(np.array_equal(first[key], second[key]) for key in first)
manifest = corpus_manifest({"edge": first}, {"source": "test"})
assert manifest["scenario_categories"] == list(SCENARIOS)
assert manifest["provenance"]["source"] == "test"
assert {"constant", "nearly_constant_tiny_variance", "high_variance", "alternating", "uptrend", "downtrend", "flat_breakout", "breakout_flat", "threshold_equality", "threshold_one_ulp_above", "threshold_one_ulp_below", "repeated_equality", "zero_range", "tiny_range", "abrupt_atr", "warmup"} == set(SCENARIOS)
def test_frozen_contract_validates_and_rejects_injected_numeric_nan_and_decision_violations():
ohlcv, oracle, contract = _evidence()
assert contract["artifact"] == ARTIFACT
assert {"max_ulp", "p50_ulp", "p95_ulp", "p99_ulp", "p99_9_ulp"}.issubset(contract["feature_limits"]["super"])
assert {"max_ulp", "p50_ulp", "p95_ulp", "p99_ulp", "p99_9_ulp"}.issubset(contract["family_limits"]["supertrend"])
assert validate_frozen_contract(contract, oracle, oracle, ohlcv["close"])["passed"]
numeric = {"super": oracle["super"].copy()}; numeric["super"][-1] += 1.0
assert not validate_frozen_contract(contract, numeric, oracle, ohlcv["close"])["passed"]
nan = {"super": oracle["super"].copy()}; nan["super"][-1] = np.nan
assert not validate_frozen_contract(contract, nan, oracle, ohlcv["close"])["passed"]
decision = {"super": oracle["super"].copy()}; index = np.flatnonzero(np.isfinite(decision["super"]))[-1]; decision["super"][index] = ohlcv["close"][index] + 1.0
assert not validate_frozen_contract(contract, decision, oracle, ohlcv["close"])["passed"]
def test_frozen_contract_rejects_a_deliberate_ulp_breach():
ohlcv, oracle, contract = _evidence()
breach = {"super": oracle["super"].copy()}
index = np.flatnonzero(np.isfinite(breach["super"]))[-1]
breach["super"][index] = np.nextafter(breach["super"][index], np.inf)
result = validate_frozen_contract(contract, breach, oracle, ohlcv["close"])
assert not result["passed"]
assert result["records"][0]["max_ulp"] > contract["feature_limits"]["super"]["max_ulp"]
assert "max_ulp" in result["records"][0]["numeric_violations"]
def test_validation_refuses_unfrozen_or_derived_contracts():
ohlcv, oracle, contract = _evidence()
contract["status"] = "accepted"
with pytest.raises(FrozenContractError):
validate_frozen_contract(contract, oracle, oracle, ohlcv["close"])
def test_trace_comparer_requires_every_discrete_state_key_and_value():
ohlcv = deterministic_adversarial_ohlcv(length=96)
trace = cpu_psar_trace(ohlcv["close"], ohlcv["high"], ohlcv["low"], .02)
assert compare_stateful_trace(trace, trace, trace_type="psar")["exact"]
changed = dict(trace); changed["reversal"] = trace["reversal"].copy(); changed["reversal"][-1] = ~changed["reversal"][-1]
assert compare_stateful_trace(trace, changed, trace_type="psar")["mismatched_keys"] == ["reversal"]
assert not compare_stateful_trace(trace, {"output": trace["output"]}, trace_type="psar")["exact"]
def test_decisions_do_not_cross_nan_gaps():
close = np.array([1., 2., 3.])
oracle = {"super": np.array([0., np.nan, 4.])}
calibration = calibrate(oracle, oracle, [{"request_id": "super", "indicator_id": 19}], close, corpus_manifest({"edge": deterministic_adversarial_ohlcv(length=96)}, {}))
contract = freeze_contract(calibration)
assert validate_frozen_contract(contract, oracle, oracle, close)["passed"]
def test_historical_cpu_oracle_accepts_arbitrary_csv(tmp_path):
ohlcv = deterministic_adversarial_ohlcv(length=96)
path = tmp_path / "input.csv"
with path.open("w", newline="", encoding="utf-8") as handle:
writer = csv.DictWriter(handle, fieldnames=["close", "high", "low", "volume"]); writer.writeheader()
writer.writerows({key: ohlcv[key][index] for key in ohlcv} for index in range(96))
result = historical_cpu_oracle(path, [{"request_id": "psar", "indicator_id": 28, "period": 1, "p1": .02}])
assert result["outputs"]["psar"].shape == (96,)
assert result["source"]["evaluator"] == "evaluate_band_channel"
def test_nan_gap_semantics_are_manifested_and_rejected_before_historical_evaluation(tmp_path):
manifest = nan_gap_semantics_manifest()
assert manifest["historical_input_policy"] == "reject_non_finite_ohlcv"
assert manifest["internal_nan_gap_semantics"] == "undefined"
path = tmp_path / "nan_gap.csv"
path.write_text("close,high,low,volume\n100,101,99,1000\nnan,101,99,1000\n", encoding="utf-8")
with pytest.raises(ValueError, match="NaN/gap semantics are undefined"):
historical_cpu_oracle(path, [{"request_id": "psar", "indicator_id": 28, "period": 1, "p1": .02}])

View file

@ -0,0 +1,122 @@
from __future__ import annotations
import builtins
import numpy as np
import pytest
from gpu_feature_parity_contract_v1_1 import deterministic_adversarial_ohlcv
from gpu_feature_parity_contract_v1_2 import (
ARTIFACT,
CALIBRATION_ARTIFACT,
ROLE_C_UNVERIFIABLE,
UNVERIFIABLE,
calibrate_supertrend,
calibrate_output,
cpu_supertrend_trace,
freeze_contract,
role_surface,
validate_all_frozen_contract,
validate_frozen_contract,
)
def _traces():
ohlcv = deterministic_adversarial_ohlcv(length=96, seed=19)
expected = cpu_supertrend_trace(ohlcv["close"], ohlcv["high"], ohlcv["low"], 8, 3.0)
actual = {name: value.copy() for name, value in expected.items()}
return expected, actual
def test_ordered_on_device_seed_matches_historical_left_to_right_seed():
torch = pytest.importorskip("torch")
from gpu_feature_engine_v1_2 import rma_ordered_seed
values = torch.tensor([1e16, 1.0, -1e16, 3.0], dtype=torch.float64)
result = rma_ordered_seed(values, 4)
total = 0.0
for value in values.numpy():
total += value
assert result[-1].item() == total / 4
def test_v1_2_requires_exact_branch_state_before_atr_bounds():
expected, actual = _traces()
calibration = {"artifact": CALIBRATION_ARTIFACT, "records": [calibrate_supertrend("super", expected, actual)]}
contract = freeze_contract(calibration)
assert contract["artifact"] == ARTIFACT
assert validate_frozen_contract(contract, {"super": (expected, actual)})["passed"]
changed = dict(actual)
changed["active_long"] = actual["active_long"].copy()
changed["active_long"][-1] = ~changed["active_long"][-1]
result = validate_frozen_contract(contract, {"super": (expected, changed)})
assert not result["passed"]
assert result["records"][0]["atr"] is None
@pytest.mark.parametrize("mutation", ["shape", "nan_mask"])
def test_v1_2_requires_atr_structure_before_measuring_bounds(mutation):
expected, actual = _traces()
calibration = {"artifact": CALIBRATION_ARTIFACT, "records": [calibrate_supertrend("super", expected, actual)]}
contract = freeze_contract(calibration)
changed = dict(actual)
changed["atr"] = actual["atr"][:-1].copy() if mutation == "shape" else actual["atr"].copy()
if mutation == "nan_mask":
changed["atr"][-1] = np.nan
result = validate_frozen_contract(contract, {"super": (expected, changed)})
assert not result["passed"]
assert result["records"][0]["atr"] is None
assert not result["records"][0]["trace"]["atr_structure_exact"]
def test_unrecorded_batch01_roles_are_unverifiable_and_not_parity_blockers():
surface = role_surface([
{"request_id": "known", "indicator_id": 19, "period": 10, "p1": 2.0},
{"request_id": "unknown", "indicator_id": 17, "period": 999, "p1": 2.0},
])
unknown = next(item for item in surface["records"] if item["request_id"] == "unknown")
assert unknown["classification"] == ROLE_C_UNVERIFIABLE
assert unknown["role_status"] == UNVERIFIABLE
assert not unknown["parity_blocker"]
def test_role_surface_is_unverifiable_when_recovered_registry_is_absent(monkeypatch):
original_import = builtins.__import__
def missing_registry(name, *args, **kwargs):
if name == "control_plane.trading_studio.indicators.registry":
raise ModuleNotFoundError(name)
return original_import(name, *args, **kwargs)
monkeypatch.setattr(builtins, "__import__", missing_registry)
surface = role_surface([{"request_id": "known", "indicator_id": 19, "period": 10, "p1": 2.0}])
record = surface["records"][0]
assert record["role_status"] == UNVERIFIABLE
assert record["reason"] == "control_plane.trading_studio.indicators.registry is unavailable"
assert not record["parity_blocker"]
def test_full_batch_contract_keeps_exact_and_calibration_bounded_classes_separate():
expected, actual = _traces()
values = np.array([np.nan, 2.0, 3.0])
calibration = {
"artifact": CALIBRATION_ARTIFACT,
"records": [
calibrate_supertrend("super", expected, actual, atr_limits={"max_absolute_error": 0.0, "mae": 0.0}),
calibrate_output("donch", 20, values, values.copy()),
calibrate_output("psar", 28, values, values.copy()),
calibrate_output("bb", 17, values, values.copy()),
calibrate_output("kc", 23, values, values.copy()),
],
}
contract = freeze_contract(calibration)
result = validate_all_frozen_contract(
contract,
{"super": expected["output"], "donch": values, "psar": values, "bb": values, "kc": values},
{"super": actual["output"], "donch": values.copy(), "psar": values.copy(), "bb": values.copy(), "kc": values.copy()},
{"super": (expected, actual)},
)
assert result["passed"]
assert contract["feature_limits"]["donch"]["role"] == "A_EXACT"
assert contract["feature_limits"]["bb"]["role"] == "B_BOUNDED"

View file

@ -0,0 +1,38 @@
from __future__ import annotations
import pytest
torch = pytest.importorskip("torch")
import json
from gpu_feature_parity_contract_v1_1 import deterministic_adversarial_ohlcv # noqa: E402
from gpu_feature_engine_v1_2 import supertrend_trace # noqa: E402
from gpu_feature_parity_contract_v1_2 import cpu_supertrend_trace # noqa: E402
from gpu_supertrend_v1_2_diagnostic import diagnose # noqa: E402
def test_period_10_semantic_diagnostic_has_serializable_cpu_gpu_trace_statistics():
ohlcv = deterministic_adversarial_ohlcv(length=96, seed=11)
report = diagnose(ohlcv, device=torch.device("cpu"))
assert json.dumps(report, allow_nan=False)
assert [record["multiplier"] for record in report["records"]] == [2.0, 3.0, 4.0]
for record in report["records"]:
assert set(record["numeric"]) == {"output", "true_range", "atr", "basic_upper", "basic_lower", "upper", "lower"}
assert record["numeric"]["atr"]["first_divergence"] is None
assert record["direction"]["exact"]
assert record["transitions"]["direction_transition"]["exact"]
assert all(item["exact"] for item in record["branch_predicates"].values())
def test_cpu_trace_exposes_the_complete_gpu_trace_surface():
ohlcv = deterministic_adversarial_ohlcv(length=96, seed=11)
cpu = cpu_supertrend_trace(ohlcv["close"], ohlcv["high"], ohlcv["low"], 10, 3.0)
gpu = supertrend_trace(
torch.as_tensor(ohlcv["close"], dtype=torch.float64),
torch.as_tensor(ohlcv["high"], dtype=torch.float64),
torch.as_tensor(ohlcv["low"], dtype=torch.float64),
10,
3.0,
)
assert set(cpu) == set(gpu)

View file

@ -0,0 +1,367 @@
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"),
]

View file

@ -0,0 +1,60 @@
from __future__ import annotations
import json
import sys
import pytest
import hyperscalper_wave1_feature_synthesis_v1 as synthesis
pytestmark = pytest.mark.django_db
def test_synthesis_requires_explicit_ohlcv_and_reports_missing_failure_artifact(
tmp_path, monkeypatch
):
features = [
{
"indicator_id": index,
"period": 10,
"p1": 0,
"domain": "trend",
"output_type": "continuous",
"input_scope": "OHLCV_ONLY",
}
for index in range(20)
] + [{"indicator_id": 99, "period": 10, "p1": 0, "input_scope": "EXTERNAL"}]
semantic = tmp_path / "semantic.json"
semantic.write_text(json.dumps({"features": features}), encoding="utf-8")
redundancy = tmp_path / "redundancy.json"
redundancy.write_text(json.dumps({"clusters": []}), encoding="utf-8")
deq = tmp_path / "deq.json"
deq.write_text(json.dumps({"feature_summaries": []}), encoding="utf-8")
gap = tmp_path / "gap.json"
gap.write_text(json.dumps({"information_map": {"features": []}}), encoding="utf-8")
output = tmp_path / "out"
monkeypatch.setattr(
sys,
"argv",
[
"runner",
"--semantic-summary",
str(semantic),
"--redundancy-clusters",
str(redundancy),
"--deq-summary",
str(deq),
"--lineage-gap-analysis",
str(gap),
"--output-dir",
str(output),
],
)
synthesis.main()
result = json.loads((output / "new_feature_candidate_registry_v1.json").read_text())
assert len(result["candidates"]) == 20
assert result["status"] == "BLOCKED"
assert result["sources"]["failure_mining"]["status"] == "UNAVAILABLE"
assert all(item["input_scope"] == "OHLCV_ONLY" for item in result["candidates"])

View file

@ -0,0 +1,66 @@
from __future__ import annotations
import json
import numpy as np
import pytest
from validate_historical_oracle_acceptance import array_digest, validate
pytestmark = pytest.mark.django_db
def test_validate_uses_runner_semantic_array_digest(tmp_path):
output_dir = tmp_path / "run"
checkpoints = output_dir / "checkpoints"
checkpoints.mkdir(parents=True)
values = np.array([1.0, 2.0], dtype=np.float64)
status = {
"artifact": "HISTORICAL_FEATURE_ORACLE_FULL_STATUS_V1",
"status": "PASS",
"completed_features": 711,
"checkpoints": [
{
"request_id": f"feature-{index}",
"path": f"feature-{index}.npy",
"dtype": "<f8",
"shape": [2],
"sha256": array_digest(values),
}
for index in range(711)
],
}
for checkpoint in status["checkpoints"]:
np.save(checkpoints / checkpoint["path"], values, allow_pickle=False)
(output_dir / "final_status.json").write_text(json.dumps(status), encoding="utf-8")
assert validate(output_dir)["status"] == "PASS"
def test_validate_rejects_raw_npy_file_digest(tmp_path):
output_dir = tmp_path / "run"
checkpoints = output_dir / "checkpoints"
checkpoints.mkdir(parents=True)
values = np.array([1.0], dtype=np.float64)
np.save(checkpoints / "feature.npy", values, allow_pickle=False)
status = {
"artifact": "HISTORICAL_FEATURE_ORACLE_FULL_STATUS_V1",
"status": "PASS",
"completed_features": 711,
"checkpoints": [
{
"request_id": f"feature-{index}",
"path": f"feature-{index}.npy",
"dtype": "<f8",
"shape": [1],
"sha256": "0" * 64,
}
for index in range(711)
],
}
for checkpoint in status["checkpoints"]:
np.save(checkpoints / checkpoint["path"], values, allow_pickle=False)
(output_dir / "final_status.json").write_text(json.dumps(status), encoding="utf-8")
with pytest.raises(ValueError, match="semantic digest mismatch"):
validate(output_dir)

View file

@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""Validate a completed 711-feature historical-oracle run without modifying it."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
from pathlib import Path
from typing import Any
import numpy as np
def canonical(value: object) -> bytes:
return json.dumps(value, sort_keys=True, separators=(",", ":"), allow_nan=False).encode("utf-8")
def array_digest(values: np.ndarray) -> str:
values = np.ascontiguousarray(values)
return hashlib.sha256(
canonical({"dtype": values.dtype.str, "shape": values.shape}) + b"\0" + values.tobytes()
).hexdigest()
def validate(output_dir: Path) -> dict[str, Any]:
status_path = output_dir / "final_status.json"
status = json.loads(status_path.read_text(encoding="utf-8"))
checkpoints = status.get("checkpoints")
if status.get("artifact") != "HISTORICAL_FEATURE_ORACLE_FULL_STATUS_V1":
raise ValueError("invalid full-status artifact")
if status.get("status") != "PASS" or status.get("completed_features") != 711:
raise ValueError("full-status artifact does not attest to 711 passing features")
if not isinstance(checkpoints, list) or len(checkpoints) != 711:
raise ValueError("full-status artifact must contain exactly 711 checkpoints")
request_ids = set()
for checkpoint in checkpoints:
request_id = checkpoint.get("request_id")
if not isinstance(request_id, str) or request_id in request_ids:
raise ValueError("checkpoint request IDs must be unique strings")
request_ids.add(request_id)
path = output_dir / "checkpoints" / str(checkpoint.get("path", ""))
if path.name != f"{request_id}.npy" or not path.is_file():
raise ValueError(f"missing checkpoint: {request_id}")
values = np.load(path, allow_pickle=False)
if (
values.dtype.str != checkpoint.get("dtype")
or list(values.shape) != checkpoint.get("shape")
):
raise ValueError(f"checkpoint dtype or shape mismatch: {request_id}")
if array_digest(values) != checkpoint.get("sha256"):
raise ValueError(f"checkpoint semantic digest mismatch: {request_id}")
return {
"artifact": "HISTORICAL_FEATURE_ORACLE_ACCEPTANCE_VALIDATION_V1",
"status": "PASS",
"completed_features": len(checkpoints),
"status_sha256": hashlib.sha256(status_path.read_bytes()).hexdigest(),
}
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--output-dir", type=Path, required=True)
parser.add_argument("--manifest", type=Path)
args = parser.parse_args()
manifest = validate(args.output_dir)
if args.manifest:
args.manifest.write_bytes(canonical(manifest) + b"\n")
os.chmod(args.manifest, 0o444)
print(canonical(manifest).decode("utf-8"))
if __name__ == "__main__":
main()