#!/usr/bin/env python3 """Standalone, credential-free inference for the Numerai weekly v4 bundle.""" from __future__ import annotations import argparse from pathlib import Path from typing import Any import joblib import numpy as np import pandas as pd from scipy.stats import norm, rankdata REQUIRED_COMPONENTS = ( "benchmark_era_boost", "multi_target", "residual", "catboost", ) def load_bundle(path: str | Path) -> dict[str, Any]: """Load a trusted model bundle and validate its public inference contract.""" bundle = joblib.load(Path(path)) if not isinstance(bundle, dict): raise TypeError("expected a dictionary model bundle") missing = [name for name in REQUIRED_COMPONENTS if name not in bundle] if missing: raise ValueError(f"bundle is missing components: {', '.join(missing)}") if "calibrated_weights" not in bundle or "config" not in bundle: raise ValueError("bundle is missing calibrated_weights or config") return bundle def _columns(bundle: dict[str, Any]) -> tuple[list[str], list[str], list[str]]: config = bundle["config"] features = config.get("features") benchmark_columns = config.get("bench_cols") all_columns = config.get("all_feature_cols") if not all(isinstance(value, list) for value in (features, benchmark_columns, all_columns)): raise ValueError("bundle config does not contain serialized feature-name lists") return features, benchmark_columns, all_columns def _gaussianize(values: np.ndarray) -> np.ndarray: ranked = rankdata(values, method="average") / (len(values) + 1) return norm.ppf(ranked) def predict(frame: pd.DataFrame, bundle: dict[str, Any]) -> np.ndarray: """Return the exact pre-neutralization Gaussian v4 ensemble prediction.""" features, _, all_columns = _columns(bundle) missing = sorted(set(all_columns) - set(frame.columns)) if missing: preview = ", ".join(missing[:8]) raise ValueError(f"input is missing {len(missing)} columns; first missing: {preview}") n_rows = len(frame) if n_rows < 2: raise ValueError("at least two rows are required for cross-sectional ranking") x_full = frame[all_columns].to_numpy() x_features = frame[features].to_numpy() components: dict[str, np.ndarray] = {} models = bundle["benchmark_era_boost"] components["benchmark_era_boost"] = np.mean( [model.predict(x_full) for model in models], axis=0 ) target_predictions = [ rankdata(model.predict(x_full), method="average") / n_rows for model in bundle["multi_target"].values() ] components["multi_target"] = np.mean(target_predictions, axis=0) models = bundle["residual"] components["residual"] = np.mean( [model.predict(x_features) for model in models], axis=0 ) models = bundle["catboost"] components["catboost"] = np.mean( [model.predict(x_full) for model in models], axis=0 ) for optional_name in ("xgboost", "lgb_dart"): models = bundle.get(optional_name) if models: components[optional_name] = np.mean( [model.predict(x_full) for model in models], axis=0 ) horizon_models = bundle.get("horizon60") if horizon_models: components["horizon60"] = np.mean( [ rankdata(model.predict(x_full), method="average") / n_rows for model in horizon_models.values() ], axis=0, ) weights = { name: float(weight) for name, weight in bundle["calibrated_weights"].items() if name in components } total_weight = sum(weights.values()) if total_weight <= 0: raise ValueError("bundle has no positively weighted active components") ensemble = np.zeros(n_rows, dtype=np.float64) for name, weight in weights.items(): component_rank = rankdata(components[name], method="average") / (n_rows + 1) ensemble += (weight / total_weight) * component_rank return _gaussianize(ensemble) def predict_ranked(frame: pd.DataFrame, bundle: dict[str, Any]) -> np.ndarray: """Return submission-shaped predictions strictly between zero and one.""" gaussian_prediction = predict(frame, bundle) return rankdata(gaussian_prediction, method="average") / (len(frame) + 1) def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--model", type=Path, required=True) parser.add_argument("--live", type=Path, required=True) parser.add_argument("--benchmarks", type=Path, required=True) parser.add_argument("--output", type=Path, required=True) args = parser.parse_args() bundle = load_bundle(args.model) live = pd.read_parquet(args.live) benchmarks = pd.read_parquet(args.benchmarks) benchmark_columns = [column for column in benchmarks.columns if column != "era"] live = live.join(benchmarks[benchmark_columns], how="left") predictions = predict_ranked(live, bundle) output = pd.DataFrame({"prediction": predictions}, index=live.index) output.index.name = "id" args.output.parent.mkdir(parents=True, exist_ok=True) output.to_csv(args.output) print(f"wrote {len(output):,} predictions to {args.output}") return 0 if __name__ == "__main__": raise SystemExit(main())