"""Deterministic diagnostics contracts with independently checkable samples.""" from __future__ import annotations import importlib from math import sqrt import numpy as np import pandas as pd import pytest def api(): return importlib.import_module("quant_engine.factor_diagnostics") def panel(values, *, days=1, assets=None): assets = assets or ["A", "B", "C"] index = pd.MultiIndex.from_product( [pd.date_range("2026-09-21", periods=days), assets], names=["date", "asset"] ) return pd.DataFrame(values, index=index) def test_pairwise_matrix_keeps_complete_security_date_pairs(): frame = panel({"pe": [None, 0, 1, 2, 3, 4, 5, 6], "pb": [1000, None, 1, 2, 3, 4, 5, 6]}, assets=list("ABCDEFGH")) original = frame.copy() result = api().correlation_matrix(frame) assert result["matrix"][0][1] == pytest.approx(1) assert result["n_pairs"] == [[7, 6], [6, 7]] assert result["status"][0][1] == "ok" assert result["method"]["aggregation"] == "pooled_asset_session_pairwise" assert result["method"]["positive_semidefinite_guaranteed"] is False pd.testing.assert_frame_equal(frame, original) def test_matrix_empty_constant_and_short_diagonals_are_undefined(): frame = panel({"empty": [None] * 3, "constant": [0.05] * 3, "short": [1, 2, None]}) result = api().correlation_matrix(frame) assert result["matrix"] == [[None] * 3 for _ in range(3)] assert result["n_pairs"][0][0] == 0 assert result["status"][0][0] == "no_pairs" assert result["status"][1][1] == "constant_both" assert result["status"][2][2] == "insufficient_pairs" def test_rank_correlation_uses_average_ties_after_pairing(): frame = panel({"f": [1, 1, 2, None], "r": [1, 2, 3, 999]}, assets=list("ABCD")) result = api().correlation_matrix(frame, method="spearman") assert result["matrix"][0][1] == pytest.approx(sqrt(3) / 2) assert result["n_pairs"][0][1] == 3 def test_daily_ic_is_not_one_pooled_correlation(): frame = panel({"f": [1, 2, 3, 101, 102, 103]}, days=2) returns = pd.Series([3, 2, 1, 101, 102, 103], index=frame.index) result = api().daily_ic(frame, returns) assert [r["ic"] for r in result["rows"]] == pytest.approx([-1, 1]) assert result["summary"][0]["ic_mean"] == pytest.approx(0) assert result["summary"][0]["ic_std"] == pytest.approx(sqrt(2)) assert result["summary"][0]["ic_ir"] == 0 assert result["summary"][0]["ic_t"] == 0 assert result["method"]["ir_annualized"] is False assert result["method"]["t_method"] == "naive_iid_unadjusted" assert result["method"]["serial_correlation_adjusted"] is False def test_daily_summary_matches_three_known_days_and_keeps_zero(): frame = panel({"f": [1, 2, 3] * 3}, days=3) returns = pd.Series([3, 2, 1, 1, 0, 1, 1, 2, 3], index=frame.index) result = api().daily_ic(frame, returns) assert [r["ic"] for r in result["rows"]] == pytest.approx([-1, 0, 1]) assert [r["rank_ic"] for r in result["rows"]] == pytest.approx([-1, 0, 1]) summary = result["summary"][0] assert (summary["n_days"], summary["ic_valid_days"], summary["rank_ic_valid_days"]) == (3, 3, 3) assert summary["ic_std"] == pytest.approx(1) assert summary["ic_ir"] == pytest.approx(0, abs=1e-15) assert summary["ic_t"] == pytest.approx(0, abs=1e-15) assert summary["rank_ic_ir"] == pytest.approx(0, abs=1e-15) assert summary["rank_ic_t"] == pytest.approx(0, abs=1e-15) def test_undefined_days_and_factor_observation_domain_survive_alignment(): frame = panel({"f": [1, 2, 3, None, None, None, 1, 2, 3]}, days=3) returns = pd.Series([1, 2, 3], index=frame.index[:3]) extra = pd.Series([100], index=pd.MultiIndex.from_tuples( [(pd.Timestamp("2026-09-25"), "D")], names=["date", "asset"])) result = api().daily_ic(frame, pd.concat([returns, extra])) assert len(result["rows"]) == 3 assert [r["n_pairs"] for r in result["rows"]] == [3, 0, 0] assert [r["n_observations"] for r in result["rows"]] == [3, 3, 3] assert [r["ic_status"] for r in result["rows"]] == ["ok", "no_pairs", "no_pairs"] summary = result["summary"][0] assert summary["ic_valid_days"] == 1 assert summary["ic_missing_days"] == 2 assert summary["ic_mean"] == pytest.approx(1) assert summary["ic_std"] is summary["ic_ir"] is summary["ic_t"] is None assert summary["ic_status"] == "insufficient_days" assert result["method"]["extra_return_keys"] == 1 def test_daily_summary_of_constant_ic_has_no_ratio_statistics(): frame = panel({"f": [1, 2, 3] * 3}, days=3) returns = pd.Series([1, 1, 2] * 3, index=frame.index) summary = api().daily_ic(frame, returns)["summary"][0] assert summary["ic_mean"] == pytest.approx(sqrt(3) / 2) assert summary["ic_std"] == 0 assert summary["ic_ir"] is summary["ic_t"] is None assert summary["ic_status"] == "constant_values" assert summary["rank_ic_ir"] is summary["rank_ic_t"] is None def test_permutation_and_input_copies_do_not_change_diagnostics(): frame = panel({"f": [1, 2, 3, 4, 5, 6]}, days=2) returns = pd.Series([1, 3, 2, 4, 6, 5], index=frame.index) original_frame, original_returns = frame.copy(), returns.copy() baseline = api().daily_ic(frame, returns) assert api().daily_ic(frame.iloc[::-1], returns.iloc[[2, 0, 4, 1, 5, 3]]) == baseline pd.testing.assert_frame_equal(frame, original_frame) pd.testing.assert_series_equal(returns, original_returns) def test_large_finite_inputs_reuse_scaled_correlation_without_overflow(): frame = panel({"a": [-1e308, 0, 1e308], "b": [-1e307, 0, 1e307]}) assert api().correlation_matrix(frame)["matrix"][0][1] == pytest.approx(1) @pytest.mark.parametrize("value", [True, "1.2", np.inf, -np.inf, object()]) def test_diagnostics_reject_invalid_numbers(value): frame = panel({"f": [value, 2, 3]}) with pytest.raises(ValueError, match=r"Values|Infinite"): api().correlation_matrix(frame) @pytest.mark.parametrize("kind", ["duplicate_keys", "bad_names", "bad_date", "timezone", "empty_asset", "duplicate_factors"]) def test_diagnostics_reject_ambiguous_keys(kind): frame = panel({"f": [1, 2, 3]}) if kind == "duplicate_keys": frame.index = pd.MultiIndex.from_tuples([frame.index[0]] * 3, names=["date", "asset"]) elif kind == "bad_names": frame.index.names = ["day", "asset"] elif kind == "bad_date": frame.index = pd.MultiIndex.from_product([["2026-09-21"], list("ABC")], names=["date", "asset"]) elif kind == "timezone": frame.index = pd.MultiIndex.from_product([pd.date_range("2026-09-21", periods=1, tz="UTC"), list("ABC")], names=["date", "asset"]) elif kind == "empty_asset": frame.index = pd.MultiIndex.from_product([pd.date_range("2026-09-21", periods=1), ["", "B", "C"]], names=["date", "asset"]) else: frame = pd.concat([frame, frame], axis=1) with pytest.raises(ValueError, match=r"keys|dates|identifiers"): api().correlation_matrix(frame) @pytest.mark.parametrize("minimum", [True, 0, 2, 3.1, "3"]) def test_minimum_pairs_is_explicit_and_at_least_three(minimum): with pytest.raises(ValueError, match="min_pairs"): api().correlation_matrix(panel({"f": [1, 2, 3]}), min_pairs=minimum) def test_all_missing_and_empty_inputs_remain_observed_not_identity_or_zero(): frame = panel({"f": [None] * 3}) result = api().daily_ic(frame, pd.Series([None] * 3, index=frame.index)) assert result["summary"][0]["ic_mean"] is None assert result["summary"][0]["ic_status"] == "no_valid_days" empty = frame.iloc[:0] assert api().correlation_matrix(empty)["matrix"] == [[None]] assert api().daily_ic(empty, pd.Series([], index=empty.index, dtype=float))["rows"] == [] def prices(values=(100, 110, 121, 133.1)): sessions = pd.DatetimeIndex(["2026-09-18", "2026-09-21", "2026-09-22", "2026-09-23"]) index = pd.MultiIndex.from_product([sessions, ["A"]], names=["date", "asset"]) return pd.Series(values, index=index), sessions def forward(series, sessions, **changes): options = {"entry_lag_sessions": 0, "holding_sessions": 2, "price_field": "close", "price_basis": "synthetic_comparable"} options.update(changes) return api().forward_returns(series, sessions=sessions, **options) def test_forward_returns_use_explicit_calendar_endpoints_and_compound_price_ratio(): series, sessions = prices() result = forward(series, sessions) assert result.returns.iloc[0] == pytest.approx(0.21) assert result.intervals.iloc[0]["entry_date"] == "2026-09-18" assert result.intervals.iloc[0]["exit_date"] == "2026-09-22" assert result.intervals.iloc[-1]["status"] == "insufficient_calendar" assert np.isnan(result.returns.iloc[-1]) assert result.metadata["formula"] == "exit_price / entry_price - 1" assert result.metadata["entry_lag_sessions"] == 0 assert result.metadata["holding_sessions"] == 2 assert result.metadata["execution_eligibility"] == "not_established" assert result.metadata["historical_availability"] == "not_established" def test_forward_entry_lag_changes_both_endpoints(): series, sessions = prices([10, 20, 30, 80]) result = forward(series, sessions, entry_lag_sessions=1) assert result.returns.iloc[0] == pytest.approx(3) assert result.intervals.iloc[0]["entry_date"] == "2026-09-21" assert result.intervals.iloc[0]["exit_date"] == "2026-09-23" def test_forward_does_not_skip_missing_prices_or_derive_calendar_from_rows(): series, sessions = prices([100, None, 121, 133.1]) result = forward(series.drop(index=(sessions[1], "A")), sessions, holding_sessions=1) assert len(result.returns) == 4 assert np.isnan(result.returns.iloc[0]) assert np.isnan(result.returns.iloc[1]) assert result.intervals.iloc[0]["status"] == "missing_price" assert result.intervals.iloc[0]["exit_date"] == "2026-09-21" assert result.returns.iloc[2] == pytest.approx(0.1) @pytest.mark.parametrize("options", [{"holding_sessions": 0}, {"holding_sessions": True}, {"holding_sessions": 1.5}, {"entry_lag_sessions": -1}, {"entry_lag_sessions": False}, {"price_field": ""}, {"price_basis": ""}]) def test_forward_rejects_ambiguous_interval_parameters(options): series, sessions = prices() with pytest.raises(ValueError, match=r"sessions|price field"): forward(series, sessions, **options) @pytest.mark.parametrize("kind", ["duplicate", "descending", "timezone", "intraday", "outside"]) def test_forward_rejects_invalid_calendar(kind): series, sessions = prices() if kind == "duplicate": sessions = sessions.insert(1, sessions[0]) elif kind == "descending": sessions = sessions[::-1] elif kind == "timezone": sessions = sessions.tz_localize("UTC") elif kind == "intraday": sessions = sessions + pd.Timedelta(hours=1) else: sessions = sessions[:-1] with pytest.raises(ValueError, match=r"sessions|dates|calendar"): forward(series, sessions) @pytest.mark.parametrize("value", [0, -1, True, "2", np.inf]) def test_forward_rejects_nonpositive_or_nonfinite_prices(value): series, sessions = prices([value, 110, 121, 133.1]) with pytest.raises(ValueError, match=r"positive|Values|Infinite"): forward(series, sessions) def test_result_version_and_parameters_do_not_grant_source_or_decision_admission(): frame = panel({"f": [1, 2, 3]}) result = api().daily_ic(frame, pd.Series([1, 2, 3], index=frame.index)) assert result["contract_version"] == api().FACTOR_DIAGNOSTICS_VERSION assert result["method"]["min_pairs"] == 3 assert result["method"]["min_days"] == 2 assert result["production_algorithm_version"] is None assert result["source_admission"] == result["historical_availability"] == "not_established" assert result["decision_eligible"] is False def test_nonzero_daily_summary_uses_three_days_not_nine_asset_rows(): frame = panel({"f": [1, 2, 3] * 3}, days=3) returns = pd.Series([.01, .02, .03, .02, .03, .01, .01, -.02, .01], index=frame.index) result = api().daily_ic(frame, returns) assert [point["ic"] for point in result["rows"]] == pytest.approx([1, -.5, 0], abs=1e-15) summary = result["summary"][0] for prefix in ("ic", "rank_ic"): assert summary[f"{prefix}_valid_days"] == 3 assert summary[f"{prefix}_mean"] == pytest.approx(1 / 6) assert summary[f"{prefix}_std"] == pytest.approx(sqrt(7 / 12)) assert summary[f"{prefix}_ir"] == pytest.approx(.2182178902359924) assert summary[f"{prefix}_t"] == pytest.approx(.3779644730092272) assert summary[f"{prefix}_p"] == pytest.approx(.7418011102528389) def test_each_side_has_three_values_but_only_two_common_pairs(): frame = panel({"left": [1, 2, 3, None], "right": [None, 2, 3, 4]}, assets=list("ABCD")) result = api().correlation_matrix(frame) assert result["n_pairs"][0][1] == 2 assert result["matrix"][0][1] is None assert result["status"][0][1] == "insufficient_pairs" def test_rank_preserves_distinct_tiny_values_beside_a_large_outlier(): frame = panel({"left": [1e-308, 2e-308, 1e308], "right": [1, 2, 3]}) assert api().correlation_matrix(frame, method="spearman")["matrix"][0][1] == pytest.approx(1) def test_daily_rank_ic_preserves_tiny_distinct_observations(): frame = panel({"f": [1e-200, 2e-200, 3e-200, 1e308]}, assets=list("ABCD")) returns = pd.Series([4, 3, 2, 1], index=frame.index) result = api().daily_ic(frame, returns) assert result["rows"][0]["rank_ic"] == pytest.approx(-1) assert result["rows"][0]["rank_ic_status"] == "ok" def test_affine_equivalent_daily_ic_does_not_turn_roundoff_into_extreme_significance(): frame = panel({"f": [1, 2, 3, 10, 20, 30, 101, 102, 103]}, days=3) returns = pd.Series([1, 1, 2, 101, 101, 102, 100001, 100001, 100002], index=frame.index) result = api().daily_ic(frame, returns) assert [point["ic"] for point in result["rows"]] == pytest.approx([sqrt(3) / 2] * 3) summary = result["summary"][0] assert summary["ic_mean"] == pytest.approx(sqrt(3) / 2) assert summary["ic_valid_days"] == 3 assert summary["ic_ir"] is summary["ic_t"] is summary["ic_p"] is None assert summary["ic_std"] <= result["method"]["minimum_ic_std_for_ratios"] assert summary["ic_status"] in {"constant_values", "below_resolution"} @pytest.mark.parametrize("offset,step", [(1e16, 2.0), (1e12, np.spacing(1e12))]) def test_pearson_preserves_representable_differences_beside_a_large_offset(offset, step): frame = panel({"f": offset + np.arange(5) * step}, assets=list("ABCDE")) returns = pd.Series([2, 5, 1, 4, 3], index=frame.index) matrix = api().correlation_matrix(frame.assign(other=returns)) assert matrix["matrix"][0][1] == pytest.approx(.1, abs=1e-14) daily = api().daily_ic(frame, returns) assert daily["rows"][0]["ic"] == pytest.approx(.1, abs=1e-14)