llm-quant/tests/test_factor_portfolio.py

63 lines
1.9 KiB
Python

from __future__ import annotations
from datetime import date, timedelta
from app.features.evaluation import (
FactorPerformance,
evaluate_factor_portfolio,
optimize_factor_weights,
)
from app.features.factors import FactorSpec, compute_factor_range
from tests.factor_utils import populate_sample_data
def _seed_factor_history(codes, end_day):
specs = [
FactorSpec("mom_5", 5),
FactorSpec("mom_20", 20),
FactorSpec("turn_20", 20),
]
start_day = end_day - timedelta(days=5)
for code in codes:
populate_sample_data(code, end_day, days=180)
compute_factor_range(start_day, end_day, ts_codes=codes, factors=specs)
return specs, start_day
def test_optimize_factor_weights_returns_normalized_vector(isolated_db):
codes = [f"0000{i:02d}.SZ" for i in range(1, 4)]
end_day = date(2025, 2, 28)
specs, start_day = _seed_factor_history(codes, end_day)
factor_names = [spec.name for spec in specs]
weights, performances = optimize_factor_weights(
factor_names,
start_day,
end_day,
universe=codes,
)
assert set(weights.keys()) == set(factor_names)
assert abs(sum(weights.values()) - 1.0) < 1e-6
for perf in performances.values():
assert isinstance(perf, FactorPerformance)
def test_evaluate_factor_portfolio_returns_report(isolated_db):
codes = [f"0000{i:02d}.SZ" for i in range(1, 4)]
end_day = date(2025, 3, 10)
specs, start_day = _seed_factor_history(codes, end_day)
factor_names = [spec.name for spec in specs]
report = evaluate_factor_portfolio(
factor_names,
start_day,
end_day,
universe=codes,
)
assert set(report.weights.keys()) == set(factor_names)
assert isinstance(report.combined, FactorPerformance)
assert report.combined.sample_size >= 0
assert set(report.components.keys()) == set(factor_names)