Skip to content
All library documents

A Research Catalog for Comparing Model and Backtest Results

Code Machine Learning for Trading

Summary

This source module builds catalog tables from experiment registries for model predictions and backtests. It defines reserved fields for provenance, identity, completion status, artifact availability, and metrics. The catalog carries multiple measures that answer different questions, including fold-level and Newey-West-adjusted information coefficients, daily and pooled AUC, coverage counts, uncertainty intervals, ruin information, and risk-control trigger counts.

The code reads SQLite registries in read-only mode, adapts to optional tables and columns, and can distinguish released records from workspace or preview records. Catalog interfaces provide filtered lookups and can freeze an authoritative selection into a comparison set. These facilities help make results discoverable and comparable while preserving context about how they were produced. As infrastructure code, it does not present trading results or establish that any metric or strategy is valid; interpretation still depends on sound experimental design and comparable samples.

Key ideas

  • Catalogs should retain provenance, identity, completeness, and artifact availability alongside experiment metrics.
  • Fold-level diagnostic statistics and Newey-West-adjusted inferential statistics answer different questions.
  • Daily cross-sectional AUC can differ from pooled AUC when class prevalence changes over time.
  • Coverage counts help prevent partial metric availability from making a configuration look artificially strong.
  • Backtest catalogs can include uncertainty, ruin, trading-cost, and declared risk-control activity measures.
  • Freezing authoritative selections supports repeatable comparisons across research runs.

Tags

Full text
# catalog.py


```py
from __future__ import annotations

import json
import sqlite3
from contextlib import closing
from pathlib import Path
from typing import TYPE_CHECKING, Any

import polars as pl

from case_studies.utils.registry.specs import IDENTITY_VERSION, canonical_json

if TYPE_CHECKING:
    from .comparison import CandidateSet
    from .workspace import Study


CATALOG_VERSION = 1
_METRIC_COLUMNS = (
    "ic_mean",
    "ic_std",
    # Two different statistics, and the difference is the difference between ten numbers and a
    # hundred. `ic_t` is computed over the fold-level mean ICs - ten of them for a ten-fold run -
    # and `registry/metrics.py` calls it a diagnostic in terms. `ic_t_hac` is the inferential one:
    # Newey-West on the per-date IC series, which is what a reader means by a t-statistic on an IC.
    # `auc_t_hac` was already carried and its IC counterpart was not, so a notebook wanting the
    # inferential statistic had only the diagnostic to reach for, and reaching for it while calling
    # it Newey-West is a mistake this column exists to stop.
    "ic_t",
    "ic_t_hac",
    # Validation dates that produced a defined cross-sectional IC. A configuration whose
    # predictions collapse to near-constant on some folds yields no IC on those dates, so its
    # ic_mean is measured over fewer of them and is not comparable to a full-coverage one.
    # Ranking without this column reports the partial-coverage artifact as the leader.
    "ic_n_days",
    "n_folds",
    "pct_positive",
    "accuracy",
    "balanced_accuracy",
    # `auc_roc` pools every (entity, date) row in a fold into one ROC, so it pays a model for
    # the base rate moving through the year as well as for ranking within a date.
    # `auc_mean_daily` is the cross-sectional reading, computed within each date and averaged,
    # which is the same shape as `ic_mean` and the one to compare against it. Both are carried:
    # they agree where the cross-section is balanced and diverge where it is not.
    "auc_roc",
    "auc_mean_daily",
    "auc_n_days",
    "auc_t_hac",
    "auc_pr",
    "log_loss",
    "brier_score",
)
RESERVED_COLUMNS: dict[str, Any] = {
    "catalog_version": pl.Int64,
    "origin": pl.String,
    "identity_status": pl.String,
    "family": pl.String,
    "config_name": pl.String,
    "label": pl.String,
    "task": pl.String,
    "direction_label": pl.String,
    "split": pl.String,
    "checkpoint_kind": pl.String,
    "checkpoint_value": pl.Int64,
    "checkpoint_spec_json": pl.String,
    "cv_identity": pl.String,
    "execution_tier": pl.String,
    "approval": pl.String,
    "complete": pl.Boolean,
    "decision_key_digest": pl.String,
    "created_at": pl.String,
    "metrics_computed_at": pl.String,
    "artifact_available": pl.Boolean,
    **{metric: pl.Float64 for metric in _METRIC_COLUMNS},
    "diagnostic_metrics_json": pl.String,
    "provenance_json": pl.String,
    "training_hash": pl.String,
    "prediction_hash": pl.String,
    "spec_json": pl.String,
}

_BACKTEST_METRIC_COLUMNS = (
    "sharpe",
    "sortino",
    "total_return",
    "max_drawdown",
    "cagr",
    "volatility",
    "calmar",
    "omega",
    "stability",
    "tail_ratio",
    "win_rate",
    "kurtosis",
    "skewness",
    "var_95",
    "cvar_95",
    "n_periods",
    "num_trades",
    "total_commission",
    "total_slippage",
    "avg_turnover",
    # The block-bootstrap Sharpe interval is registered on every backtest by
    # ``compute_backtest_uncertainty``. Without it here, a catalog reader can
    # only report point estimates and has to drop to raw SQL to say whether a
    # Sharpe clears zero.
    "sharpe_ci95_lo",
    "sharpe_ci95_hi",
    # Whether the path ended, and where. Without these a catalog reader can only
    # infer a bankrupt run from `max_drawdown`, and cannot infer it at all once
    # the engine floors the drawdown at -1.0.
    "ruin",
    "ruin_period",
    # How often each declared risk control acted. NULL means no control of that
    # kind was declared, which is what separates an overlay that never fired from
    # one that was never installed.
    "risk_triggers",
    "risk_triggers_stop_loss",
    "risk_triggers_trailing_stop",
    "risk_triggers_time_exit",
    "risk_triggers_max_drawdown",
    "risk_triggers_daily_loss",
)
BACKTEST_RESERVED_COLUMNS: dict[str, Any] = {
    "catalog_version": pl.Int64,
    "origin": pl.String,
    "identity_status": pl.String,
    "family": pl.String,
    "config_name": pl.String,
    "label": pl.String,
    "split": pl.String,
    "checkpoint_kind": pl.String,
    "checkpoint_value": pl.Int64,
    "stage": pl.String,
    "execution_tier": pl.String,
    "approval": pl.String,
    "completion_state": pl.String,
    "complete": pl.Boolean,
    "created_at": pl.String,
    "metrics_computed_at": pl.String,
    "artifact_available": pl.Boolean,
    "signal_method": pl.String,
    "allocation_method": pl.String,
    "risk_method": pl.String,
    "decision_artifact_hash": pl.String,
    **{metric: pl.Float64 for metric in _BACKTEST_METRIC_COLUMNS},
    "metrics_json": pl.String,
    "training_hash": pl.String,
    "prediction_hash": pl.String,
    "backtest_hash": pl.String,
    "training_spec_json": pl.String,
    "spec_json": pl.String,
}


def _tables(db: sqlite3.Connection) -> set[str]:
    return {
        row[0] for row in db.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()
    }


def _columns(db: sqlite3.Connection, table: str) -> set[str]:
    return {row[1] for row in db.execute(f"PRAGMA table_info({table})").fetchall()}


def _select(column: str, columns: set[str], alias: str, default: str = "NULL") -> str:
    return (
        f"{alias}.{column} AS {alias}_{column}"
        if column in columns
        else f"{default} AS {alias}_{column}"
    )


def _nested(spec: dict[str, Any], *path: str) -> Any:
    value: Any = spec
    for part in path:
        if not isinstance(value, dict):
            return None
        value = value.get(part)
    return value


def _computation(spec: dict[str, Any]) -> dict[str, Any]:
    value = spec.get("computation")
    return value if isinstance(value, dict) else spec


def _flatten(prefix: str, value: Any, output: dict[str, Any]) -> None:
    if isinstance(value, dict):
        for key in sorted(value):
            _flatten(f"{prefix}__{key}" if prefix else key, value[key], output)
    else:
        output[prefix] = value


def _open_value(value: Any) -> Any:
    if value is None or isinstance(value, (bool, int, float, str)):
        return value
    return canonical_json({"value": value})[9:-1]


def _registry_rows(root: Path, origin: str, *, immutable: bool = False) -> list[dict[str, Any]]:
    db_path = root / "run_log" / "registry.db"
    if not db_path.is_file() or db_path.stat().st_size == 0:
        return []
    query = f"file:{db_path}?mode=ro"
    if immutable:
        # `immutable=1` promises SQLite the file cannot change while open, which lets it skip
        # locking and WAL recovery. That is true of a released case directory and false of any
        # root a `Study.at` handle was pointed at - a fixture, an output tree, a live case
        # directory another notebook is writing. Deciding it from the caller rather than from
        # the `origin` label keeps the two questions apart: `origin` records where a row came
        # from, this records whether the file can still move underneath the read.
        query += "&immutable=1"
    with closing(sqlite3.connect(query, uri=True)) as db:
        tables = _tables(db)
        if not {"training_runs", "prediction_sets"} <= tables:
            return []
        training_columns = _columns(db, "training_runs")
        prediction_columns = _columns(db, "prediction_sets")
        coverage_columns = (
            _columns(db, "prediction_coverage") if "prediction_coverage" in tables else set()
        )
        metric_columns = (
            _columns(db, "prediction_metrics") if "prediction_metrics" in tables else set()
        )
        expressions = [
            _select("training_hash", training_columns, "t"),
            _select("family", training_columns, "t"),
            _select("label", training_columns, "t"),
            _select("config_name", training_columns, "t"),
            _select("spec_json", training_columns, "t", "'{}'"),
            _select("identity_version", training_columns, "t"),
            _select("execution_tier", training_columns, "t"),
            _select("git_commit", training_columns, "t"),
            _select("entry_point", training_columns, "t"),
            _select("started_at", training_columns, "t"),
            _select("elapsed_s", training_columns, "t"),
            _select("runtime_json", training_columns, "t", "'{}'"),
            _select("prediction_hash", prediction_columns, "p"),
            _select("checkpoint_kind", prediction_columns, "p"),
            _select("checkpoint_value", prediction_columns, "p"),
            _select("split", prediction_columns, "p"),
            _select("created_at", prediction_columns, "p"),
            _select("status", coverage_columns, "c"),
            _select("n_folds_expected", coverage_columns, "c"),
            _select("actual_key_digest", coverage_columns, "c"),
            _select("prediction_hash", metric_columns, "m"),
            _select("computed_at", metric_columns, "m"),
            _select("task_type", metric_columns, "m"),
            # Which sibling label an `auc_*` block was scored against. A classification row
            # scores its own label and leaves this null; a regression row has no classes, so
            # the AUC it carries is against a declared direction sibling and is meaningless
            # without knowing which. A regression label with no sibling carries no AUC.
            _select("direction_label", metric_columns, "m"),
            *[_select(metric, metric_columns, "m") for metric in _METRIC_COLUMNS],
        ]
        fold_metric_count = (
            "(SELECT COUNT(*) FROM fold_metrics fm "
            "WHERE fm.prediction_hash = p.prediction_hash) AS fold_metric_count"
            if "fold_metrics" in tables
            else "0 AS fold_metric_count"
        )
        expressions.append(fold_metric_count)
        coverage_join = (
            "LEFT JOIN prediction_coverage c ON c.prediction_hash = p.prediction_hash"
            if coverage_columns
            else "LEFT JOIN (SELECT NULL AS prediction_hash) c ON 0"
        )
        metrics_join = (
            "LEFT JOIN prediction_metrics m ON m.prediction_hash = p.prediction_hash"
            if metric_columns
            else "LEFT JOIN (SELECT NULL AS prediction_hash) m ON 0"
        )
        cursor = db.execute(
            f"SELECT {', '.join(expressions)} FROM prediction_sets p "
            "JOIN training_runs t ON t.training_hash = p.training_hash "
            f"{coverage_join} {metrics_join}"
        )
        columns = [description[0] for description in cursor.description]
        records = [dict(zip(columns, row, strict=True)) for row in cursor.fetchall()]
        overlay_roots = (
            {
                row[0]: Path(row[1])
                for row in db.execute(
                    "SELECT result_hash, source_root FROM overlay_references "
                    "WHERE result_kind = 'prediction'"
                ).fetchall()
            }
            if "overlay_references" in tables
            else {}
        )

    rows: list[dict[str, Any]] = []
    for record in records:
        try:
            spec = json.loads(record["t_spec_json"] or "{}")
        except json.JSONDecodeError:
            spec = {}
        computation = _computation(spec)
        model_value = computation.get("model")
        model: dict[str, Any] = dict(model_value) if isinstance(model_value, dict) else {}
        cv_value = computation.get("cv")
        cv: dict[str, Any] = dict(cv_value) if isinstance(cv_value, dict) else {}
        task = computation.get("task")
        if isinstance(task, dict):
            task = task.get("type")
        task = task or record["m_task_type"]
        identity_version = record["t_identity_version"]
        identity_status = (
            "current"
            if identity_version == IDENTITY_VERSION
            else ("legacy-v2" if identity_version == 2 else "legacy")
        )
        artifact_root = overlay_roots.get(record["p_prediction_hash"], root)
        row_origin = "released" if record["p_prediction_hash"] in overlay_roots else origin
        artifact = (
            artifact_root
            / "run_log"
            / "predictions"
            / record["p_prediction_hash"]
            / "predictions.parquet"
        )
        metrics = {
            metric: record[f"m_{metric}"]
            for metric in _METRIC_COLUMNS
            if record[f"m_{metric}"] is not None
        }
        spec_provenance = spec.get("provenance")
        provenance = dict(spec_provenance) if isinstance(spec_provenance, dict) else {}
        provenance.update(
            {
                key: value
                for key, value in {
                    "git_commit": record["t_git_commit"],
                    "entry_point": record["t_entry_point"],
                    "started_at": record["t_started_at"],
                    "elapsed_s": record["t_elapsed_s"],
                    "runtime": json.loads(record["t_runtime_json"] or "{}"),
                }.items()
                if value not in (None, {}, "")
            }
        )
        complete = (
            identity_status == "current"
            and record["c_status"] == "complete"
            and record["m_prediction_hash"] is not None
            and record["fold_metric_count"] == record["c_n_folds_expected"]
            and artifact.is_file()
        )
        row: dict[str, Any] = {
            "catalog_version": CATALOG_VERSION,
            "origin": row_origin,
            "identity_status": identity_status,
            "family": record["t_family"],
            "config_name": record["t_config_name"],
            "label": record["t_label"],
            "task": task,
            "direction_label": record["m_direction_label"],
            "split": record["p_split"],
            "checkpoint_kind": record["p_checkpoint_kind"],
            "checkpoint_value": record["p_checkpoint_value"],
            "checkpoint_spec_json": canonical_json(
                {
                    "kind": record["p_checkpoint_kind"],
                    "value": record["p_checkpoint_value"],
                }
            ),
            "cv_identity": cv.get("identity"),
            "execution_tier": record["t_execution_tier"] or "canonical",
            "approval": "unapproved",
            "complete": complete,
            "decision_key_digest": record["c_actual_key_digest"],
            "created_at": record["p_created_at"],
            "metrics_computed_at": record["m_computed_at"],
            "artifact_available": artifact.is_file(),
            **{metric: record[f"m_{metric}"] for metric in _METRIC_COLUMNS},
            "diagnostic_metrics_json": canonical_json(metrics),
            "provenance_json": canonical_json(provenance),
            "training_hash": record["t_training_hash"],
            "prediction_hash": record["p_prediction_hash"],
            "spec_json": canonical_json(spec),
        }
        open_fields: dict[str, Any] = {}
        _flatten("model", model, open_fields)
        _flatten("preprocessing", computation.get("preprocessing", {}), open_fields)
        _flatten("cv", cv.get("request", {}), open_fields)
        row.update({key: _open_value(value) for key, value in open_fields.items()})
        rows.append(row)
    return rows


def _dtype(values: list[Any]) -> Any:
    concrete = [value for value in values if value is not None]
    if not concrete:
        return pl.String
    types = {type(value) for value in concrete}
    if types <= {bool}:
        return pl.Boolean
    if types <= {int}:
        return pl.Int64
    if types <= {int, float}:
        return pl.Float64
    return pl.String


def _frame(
    rows: list[dict[str, Any]],
    reserved_columns: dict[str, Any] = RESERVED_COLUMNS,
) -> pl.DataFrame:
    if not rows:
        return pl.DataFrame(schema=reserved_columns)
    columns = list(reserved_columns)
    columns.extend(sorted(set().union(*(row.keys() for row in rows)) - set(columns)))
    schema = dict(reserved_columns)
    for column in columns:
        if column not in schema:
            schema[column] = _dtype([row.get(column) for row in rows])
    normalized = []
    for row in rows:
        values = {}
        for column in columns:
            value = row.get(column)
            if (
                schema[column] == pl.Float64
                and isinstance(value, int)
                and not isinstance(value, bool)
            ):
                value = float(value)
            elif schema[column] == pl.String and value is not None and not isinstance(value, str):
                value = canonical_json({"value": value})[9:-1]
            values[column] = value
        normalized.append(values)
    return pl.DataFrame(normalized, schema=schema).select(columns)


def _backtest_registry_rows(
    root: Path, origin: str, *, immutable: bool = False
) -> list[dict[str, Any]]:
    db_path = root / "run_log" / "registry.db"
    if not db_path.is_file() or db_path.stat().st_size == 0:
        return []
    query = f"file:{db_path}?mode=ro"
    if immutable:
        # `immutable=1` promises SQLite the file cannot change while open, which lets it skip
        # locking and WAL recovery. That is true of a released case directory and false of any
        # root a `Study.at` handle was pointed at - a fixture, an output tree, a live case
        # directory another notebook is writing. Deciding it from the caller rather than from
        # the `origin` label keeps the two questions apart: `origin` records where a row came
        # from, this records whether the file can still move underneath the read.
        query += "&immutable=1"
    with closing(sqlite3.connect(query, uri=True)) as db:
        tables = _tables(db)
        if not {"training_runs", "prediction_sets", "backtest_runs"} <= tables:
            return []
        training_columns = _columns(db, "training_runs")
        prediction_columns = _columns(db, "prediction_sets")
        backtest_columns = _columns(db, "backtest_runs")
        coverage_columns = (
            _columns(db, "prediction_coverage") if "prediction_coverage" in tables else set()
        )
        prediction_metric_columns = (
            _columns(db, "prediction_metrics") if "prediction_metrics" in tables else set()
        )
        backtest_metric_columns = (
            _columns(db, "backtest_metrics") if "backtest_metrics" in tables else set()
        )
        expressions = [
            _select("training_hash", training_columns, "t"),
            _select("family", training_columns, "t"),
            _select("label", training_columns, "t"),
            _select("config_name", training_columns, "t"),
            _select("spec_json", training_columns, "t", "'{}'"),
            _select("identity_version", training_columns, "t"),
            _select("execution_tier", training_columns, "t"),
            _select("prediction_hash", prediction_columns, "p"),
            _select("checkpoint_kind", prediction_columns, "p"),
            _select("checkpoint_value", prediction_columns, "p"),
            _select("split", prediction_columns, "p"),
            _select("backtest_hash", backtest_columns, "b"),
            _select("spec_json", backtest_columns, "b", "'{}'"),
            _select("stage", backtest_columns, "b"),
            _select("created_at", backtest_columns, "b"),
            _select("status", coverage_columns, "c"),
            _select("n_folds_expected", coverage_columns, "c"),
            _select("prediction_hash", prediction_metric_columns, "pm"),
            _select("backtest_hash", backtest_metric_columns, "bm"),
            _select("computed_at", backtest_metric_columns, "bm"),
            *[
                _select(metric, backtest_metric_columns, "bm")
                for metric in _BACKTEST_METRIC_COLUMNS
            ],
        ]
        expressions.append(
            "(SELECT COUNT(*) FROM fold_metrics fm "
            "WHERE fm.prediction_hash = p.prediction_hash) AS prediction_fold_metric_count"
            if "fold_metrics" in tables
            else "0 AS prediction_fold_metric_count"
        )
        coverage_join = (
            "LEFT JOIN prediction_coverage c ON c.prediction_hash = p.prediction_hash"
            if coverage_columns
            else "LEFT JOIN (SELECT NULL AS prediction_hash) c ON 0"
        )
        prediction_metrics_join = (
            "LEFT JOIN prediction_metrics pm ON pm.prediction_hash = p.prediction_hash"
            if prediction_metric_columns
            else "LEFT JOIN (SELECT NULL AS prediction_hash) pm ON 0"
        )
        backtest_metrics_join = (
            "LEFT JOIN backtest_metrics bm ON bm.backtest_hash = b.backtest_hash"
            if backtest_metric_columns
            else "LEFT JOIN (SELECT NULL AS backtest_hash) bm ON 0"
        )
        cursor = db.execute(
            f"SELECT {', '.join(expressions)} FROM backtest_runs b "
            "JOIN prediction_sets p ON p.prediction_hash = b.prediction_hash "
            "JOIN training_runs t ON t.training_hash = p.training_hash "
            f"{coverage_join} {prediction_metrics_join} {backtest_metrics_join}"
        )
        columns = [description[0] for description in cursor.description]
        records = [dict(zip(columns, row, strict=True)) for row in cursor.fetchall()]
        prediction_roots = (
            {
                row[0]: Path(row[1])
                for row in db.execute(
                    "SELECT result_hash, source_root FROM overlay_references "
                    "WHERE result_kind = 'prediction'"
                ).fetchall()
            }
            if "overlay_references" in tables
            else {}
        )

    rows: list[dict[str, Any]] = []
    for record in records:
        try:
            training_spec = json.loads(record["t_spec_json"] or "{}")
        except json.JSONDecodeError:
            training_spec = {}
        try:
            backtest_spec = json.loads(record["b_spec_json"] or "{}")
        except json.JSONDecodeError:
            backtest_spec = {}
        computation = _computation(training_spec)
        model_value = computation.get("model")
        model = dict(model_value) if isinstance(model_value, dict) else {}
        cv_value = computation.get("cv")
        cv = dict(cv_value) if isinstance(cv_value, dict) else {}
        strategy_value = backtest_spec.get("strategy")
        strategy = dict(strategy_value) if isinstance(strategy_value, dict) else {}
        identity_version = record["t_identity_version"]
        identity_status = (
            "current"
            if identity_version == IDENTITY_VERSION
            else ("legacy-v2" if identity_version == 2 else "legacy")
        )
        prediction_root = prediction_roots.get(record["p_prediction_hash"], root)
        prediction_artifact = (
            prediction_root
            / "run_log"
            / "predictions"
            / record["p_prediction_hash"]
            / "predictions.parquet"
        )
        returns_artifact = (
            root / "run_log" / "backtest" / record["b_backtest_hash"] / "daily_returns.parquet"
        )
        complete = (
            identity_status == "current"
            and record["c_status"] == "complete"
            and record["pm_prediction_hash"] is not None
            and record["prediction_fold_metric_count"] == record["c_n_folds_expected"]
            and prediction_artifact.is_file()
            and record["bm_backtest_hash"] is not None
            and returns_artifact.is_file()
        )
        metrics = {
            metric: record[f"bm_{metric}"]
            for metric in _BACKTEST_METRIC_COLUMNS
            if record[f"bm_{metric}"] is not None
        }
        signal_value = strategy.get("signal")
        signal: dict[str, Any] = dict(signal_value) if isinstance(signal_value, dict) else {}
        allocation_value = strategy.get("allocation")
        allocation: dict[str, Any] = (
            dict(allocation_value) if isinstance(allocation_value, dict) else {}
        )
        risk_value = strategy.get("risk")
        risk: dict[str, Any] = dict(risk_value) if isinstance(risk_value, dict) else {}
        decision_value = backtest_spec.get("decision_artifact")
        decision: dict[str, Any] = dict(decision_value) if isinstance(decision_value, dict) else {}
        row: dict[str, Any] = {
            "catalog_version": CATALOG_VERSION,
            "origin": origin,
            "identity_status": identity_status,
            "family": record["t_family"],
            "config_name": record["t_config_name"],
            "label": record["t_label"],
            "split": record["p_split"],
            "checkpoint_kind": record["p_checkpoint_kind"],
            "checkpoint_value": record["p_checkpoint_value"],
            "stage": record["b_stage"],
            "execution_tier": record["t_execution_tier"]
            or backtest_spec.get("execution_tier")
            or "canonical",
            "approval": "unapproved",
            "completion_state": "complete" if complete else "partial",
            "complete": complete,
            "created_at": record["b_created_at"],
            "metrics_computed_at": record["bm_computed_at"],
            "artifact_available": returns_artifact.is_file(),
            "signal_method": signal.get("method"),
            "allocation_method": allocation.get("method"),
            # `name`, not `method`: a risk control is declared under `strategy.risk.name`,
            # unlike signal and allocation which spell theirs `method`. Reading `method`
            # here left the column NULL for every backtest ever registered, so a risk
            # overlay was indistinguishable from an unprotected book in any catalog read.
            "risk_method": risk.get("name"),
            "decision_artifact_hash": decision.get("hash"),
            **{metric: record[f"bm_{metric}"] for metric in _BACKTEST_METRIC_COLUMNS},
            "metrics_json": canonical_json(metrics),
            "training_hash": record["t_training_hash"],
            "prediction_hash": record["p_prediction_hash"],
            "backtest_hash": record["b_backtest_hash"],
            "training_spec_json": canonical_json(training_spec),
            "spec_json": canonical_json(backtest_spec),
        }
        open_fields: dict[str, Any] = {}
        _flatten("model", model, open_fields)
        _flatten("preprocessing", computation.get("preprocessing", {}), open_fields)
        _flatten("cv", cv.get("request", {}), open_fields)
        _flatten("strategy", strategy, open_fields)
        row.update({key: _open_value(value) for key, value in open_fields.items()})
        rows.append(row)
    return rows


def _resolve_authoritative_selection(
    study: Study,
    selection: pl.DataFrame,
    *,
    kind: str,
    canonical: bool = True,
) -> tuple[Any, ...]:
    if not isinstance(selection, pl.DataFrame):
        raise TypeError(f"{kind} selection must be a Polars DataFrame")
    identity_columns = (
        ("training_hash", "prediction_hash")
        if kind == "prediction"
        else ("training_hash", "prediction_hash", "backtest_hash")
    )
    missing = set(identity_columns) - set(selection.columns)
    if missing:
        raise ValueError(
            f"{kind} catalog selection is missing required identity columns: {sorted(missing)}"
        )
    if selection.is_empty():
        raise ValueError(f"{kind} catalog selection is empty")
    result_column = identity_columns[-1]
    if selection.get_column(result_column).n_unique() != selection.height:
        raise ValueError(f"duplicate {kind} identities make the selection ambiguous")
    authoritative = (
        study.predictions.table(include_preview=True)
        if kind == "prediction"
        else study.backtests.table(include_preview=True)
    )
    from .results import Result

    resolved = []
    for selected in selection.select(*identity_columns).iter_rows(named=True):
        match = authoritative.filter(pl.col(result_column) == selected[result_column])
        if match.height != 1:
            raise ValueError(
                f"{kind} identity {selected[result_column]!r} resolved to {match.height} rows"
            )
        row = match.row(0, named=True)
        altered = {
            column: (selected[column], row[column])
            for column in identity_columns
            if selected[column] != row[column]
        }
        if altered:
            details = ", ".join(
                f"{column}={actual!r}, not {supplied!r}"
                for column, (supplied, actual) in altered.items()
            )
            raise ValueError(f"{kind} selection has altered lineage: {details}")
        if canonical and row["execution_tier"] == "preview":
            raise ValueError(f"preview {kind} results cannot enter a canonical candidate set")
        if not row["complete"]:
            raise ValueError(f"{kind} {selected[result_column]} is partial")
        result = Result.open(study, str(selected[result_column]), include_preview=True)
        if result.kind != kind:
            raise ValueError(f"catalog identity {selected[result_column]} is not a {kind}")
        resolved.append(result)
    return tuple(resolved)


def prediction_rows_at(case_dir: str | Path) -> pl.DataFrame:
    """The prediction catalog for a case-study directory the caller has already resolved.

    A notebook that resolved its case directory through ``get_case_study_dir`` cannot open a
    ``Study`` to ask which of its predictions are admissible. Every ``Study.open`` branch ends
    in ``activate()``, which pops ``ML4T_OUTPUT_DIR`` on the read-only branch and rewrites it
    otherwise, then clears the root-sensitive caches (``workspace.py:264-292``). Two things go
    wrong at once: the catalog answers for whichever registry the activation selected rather
    than the one the notebook read its predictions from, so a join between them drops every
    row and reports a healthy population as inadmissible; and every later resolution in that
    notebook follows the activated root, which for the canonical no-workspace path is the
    published case study, so an isolated run registers its results into the real registry.

    This reads the registry under ``case_dir`` and changes no process state.
    """
    return _frame(_registry_rows(Path(case_dir), "workspace")).sort("prediction_hash")


class PredictionCatalog:
    def __init__(self, study: Study) -> None:
        self.study = study

    def table(self, *, include_preview: bool = False) -> pl.DataFrame:
        released = _registry_rows(
            self.study.release_case_root,
            "released",
            immutable=self.study.release_root_is_immutable,
        )
        if self.study.read_only:
            return _frame(released).sort("prediction_hash")
        workspace = _registry_rows(self.study.root, "workspace")
        preview: list[dict[str, Any]] = []
        if include_preview and self.study.output_root is not None:
            preview = _registry_rows(
                self.study.output_root / ".preview" / self.study.case_study,
                "workspace",
            )
        seen = {row["prediction_hash"] for row in [*workspace, *preview]}
        overlaid = [
            *workspace,
            *preview,
            *(row for row in released if row["prediction_hash"] not in seen),
        ]
        return _frame(overlaid).sort("prediction_hash")

    def one(self, **filters: Any) -> dict[str, Any]:
        table = self.table()
        for field, value in filters.items():
            if field not in table.columns:
                raise ValueError(f"unknown prediction catalog field {field!r}")
            predicate = pl.col(field).is_null() if value is None else pl.col(field) == value
            table = table.filter(predicate)
        if table.height != 1:
            varying = [
                column
                for column in (
                    "label",
                    "cv_identity",
                    "split",
                    "checkpoint_kind",
                    "checkpoint_value",
                    "training_hash",
                    "prediction_hash",
                )
                if column in table.columns and table.get_column(column).n_unique() > 1
            ]
            raise ValueError(
                f"prediction selection matched {table.height} rows; disambiguate with {varying}"
            )
        return table.row(0, named=True)

    def freeze(
        self,
        selection: pl.DataFrame,
        *,
        name: str,
        comparison_contract: dict[str, Any] | None = None,
        supersedes: str | None = None,
    ) -> CandidateSet:
        """Freeze exact authoritative prediction members selected with Polars."""
        from .comparison import CandidateSet

        members = _resolve_authoritative_selection(
            self.study,
            selection,
            kind="prediction",
        )
        return CandidateSet.create(
            self.study,
            name,
            members,
            comparison_contract=comparison_contract,
            supersedes=supersedes,
        )


class BacktestCatalog:
    def __init__(self, study: Study) -> None:
        self.study = study

    def table(self, *, include_preview: bool = False) -> pl.DataFrame:
        released = _backtest_registry_rows(
            self.study.release_case_root,
            "released",
            immutable=self.study.release_root_is_immutable,
        )
        if self.study.read_only:
            return _frame(released, BACKTEST_RESERVED_COLUMNS).sort("backtest_hash")
        workspace = _backtest_registry_rows(self.study.root, "workspace")
        preview: list[dict[str, Any]] = []
        if include_preview and self.study.output_root is not None:
            preview = _backtest_registry_rows(
                self.study.output_root / ".preview" / self.study.case_study,
                "workspace",
            )
        seen = {row["backtest_hash"] for row in [*workspace, *preview]}
        overlaid = [
            *workspace,
            *preview,
            *(row for row in released if row["backtest_hash"] not in seen),
        ]
        return _frame(overlaid, BACKTEST_RESERVED_COLUMNS).sort("backtest_hash")

    def one(self, **filters: Any) -> dict[str, Any]:
        table = self.table()
        for field, value in filters.items():
            if field not in table.columns:
                raise ValueError(f"unknown backtest catalog field {field!r}")
            predicate = pl.col(field).is_null() if value is None else pl.col(field) == value
            table = table.filter(predicate)
        if table.height != 1:
            varying = [
                column
                for column in (
                    "label",
                    "split",
                    "checkpoint_kind",
                    "checkpoint_value",
                    "stage",
                    "signal_method",
                    "allocation_method",
                    "risk_method",
                    "training_hash",
                    "prediction_hash",
                    "backtest_hash",
                )
                if column in table.columns and table.get_column(column).n_unique() > 1
            ]
            raise ValueError(
                f"backtest selection matched {table.height} rows; disambiguate with {varying}"
            )
        return table.row(0, named=True)

    def freeze(
        self,
        selection: pl.DataFrame,
        *,
        name: str,
        comparison_contract: dict[str, Any] | None = None,
        supersedes: str | None = None,
    ) -> CandidateSet:
        """Freeze exact authoritative backtest members selected with Polars."""
        from .comparison import CandidateSet

        members = _resolve_authoritative_selection(
            self.study,
            selection,
            kind="backtest",
        )
        return CandidateSet.create(
            self.study,
            name,
            members,
            comparison_contract=comparison_contract,
            supersedes=supersedes,
        )

```

Shown in full with attribution under the source's licence. Licence: MIT

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.