54 lines
2.0 KiB
Python
54 lines
2.0 KiB
Python
"""Tests for cross-sectional IC evaluation."""
|
|
import numpy as np
|
|
import pandas as pd
|
|
|
|
from eval.metrics import evaluate_cross_sectional
|
|
|
|
|
|
def test_cross_sectional_keys_present():
|
|
dates = pd.date_range("2024-01-01", periods=10)
|
|
cols = ["a", "b", "c"]
|
|
rng = np.random.default_rng(0)
|
|
signals = pd.DataFrame(rng.standard_normal((10, 3)), index=dates, columns=cols)
|
|
returns = pd.DataFrame(rng.standard_normal((10, 3)), index=dates, columns=cols)
|
|
res = evaluate_cross_sectional(signals, returns)
|
|
for key in (
|
|
"ic_mean", "ic_std", "ir", "rank_ic_mean", "rank_ic_std",
|
|
"rank_ir", "hit_rate", "n_periods",
|
|
):
|
|
assert key in res
|
|
|
|
|
|
def test_perfect_signal_has_positive_rank_ic():
|
|
# When the signal equals next-period returns, rank IC should be ~1 each day.
|
|
dates = pd.date_range("2024-01-01", periods=8)
|
|
cols = ["a", "b", "c"]
|
|
rng = np.random.default_rng(42)
|
|
returns = pd.DataFrame(rng.standard_normal((8, 3)), index=dates, columns=cols)
|
|
signals = returns.copy() # perfect foresight
|
|
res = evaluate_cross_sectional(signals, returns)
|
|
assert res["rank_ic_mean"] > 0.99
|
|
assert res["hit_rate"] == 1.0
|
|
assert res["n_periods"] == 8
|
|
|
|
|
|
def test_inverted_signal_has_negative_rank_ic():
|
|
dates = pd.date_range("2024-01-01", periods=6)
|
|
cols = ["a", "b", "c"]
|
|
rng = np.random.default_rng(7)
|
|
returns = pd.DataFrame(rng.standard_normal((6, 3)), index=dates, columns=cols)
|
|
signals = -returns # perfectly wrong
|
|
res = evaluate_cross_sectional(signals, returns)
|
|
assert res["rank_ic_mean"] < -0.99
|
|
|
|
|
|
def test_single_stock_falls_back_to_rolling():
|
|
dates = pd.date_range("2024-01-01", periods=40)
|
|
rng = np.random.default_rng(1)
|
|
signals = pd.DataFrame({"a": rng.standard_normal(40)}, index=dates)
|
|
returns = pd.DataFrame({"a": rng.standard_normal(40)}, index=dates)
|
|
res = evaluate_cross_sectional(signals, returns)
|
|
# Rolling fallback still yields the standard metric keys.
|
|
assert "rank_ic_mean" in res
|
|
assert res["n_periods"] > 0
|