- 新增 LocalFactorEvaluator 类封装 FactorEngine,提供 (M,T) 矩阵输出 - evaluate_factors_with_evaluator() 支持新评估方式 - ValidationPipeline 优先使用 evaluator 计算信号 - 新增测试文件验证功能
118 lines
3.7 KiB
Python
118 lines
3.7 KiB
Python
"""端到端集成测试:验证 110 个 Paper Factors 能被本地引擎计算。
|
||
|
||
测试目标:
|
||
1. 加载 paper factors 库
|
||
2. 使用 LocalFactorEvaluator 计算每个因子
|
||
3. 验证输出形状为 (M, T) 且不含全 NaN
|
||
4. 统计成功率和跳过数
|
||
|
||
运行命令:uv run pytest tests/test_factorminer_e2e.py -v
|
||
"""
|
||
|
||
import pytest
|
||
|
||
|
||
def test_paper_factors_e2e():
|
||
"""端到端测试:验证 110 个 Paper Factors 能被本地引擎计算。
|
||
|
||
注意:此测试需要数据库中有 pro_bar 数据。
|
||
如果数据不足,会在计算时失败。
|
||
"""
|
||
from src.factorminer.core.library_io import import_from_paper
|
||
from src.factorminer.evaluation.local_engine import LocalFactorEvaluator
|
||
|
||
# 1. 加载 paper factors 库
|
||
library = import_from_paper()
|
||
factors = library.list_factors()
|
||
total = len(factors)
|
||
|
||
print(f"\n[e2e] 总计加载 {total} 个因子")
|
||
|
||
# 2. 过滤 unsupported 因子
|
||
supported_factors = [f for f in factors if not f.metadata.get("unsupported", False)]
|
||
unsupported_count = total - len(supported_factors)
|
||
|
||
print(f"[e2e] 可计算因子 {len(supported_factors)} 个")
|
||
print(f"[e2e] 跳过(未实现算子){unsupported_count} 个")
|
||
|
||
if not supported_factors:
|
||
print("[e2e] 没有可计算的因子,测试跳过")
|
||
pytest.skip("No supported factors to test")
|
||
|
||
# 3. 实例化 LocalFactorEvaluator
|
||
evaluator = LocalFactorEvaluator(
|
||
start_date="20200101",
|
||
end_date="20201231",
|
||
)
|
||
|
||
# 4. 批量计算每个因子
|
||
success_count = 0
|
||
fail_count = 0
|
||
skip_count = 0
|
||
|
||
specs = [(f.name, f.formula) for f in supported_factors]
|
||
|
||
print(f"[e2e] 开始批量计算 {len(specs)} 个因子...")
|
||
|
||
try:
|
||
results = evaluator.evaluate(specs)
|
||
except Exception as e:
|
||
print(f"[e2e] 批量计算失败: {e}")
|
||
pytest.fail(f"Batch evaluation failed: {e}")
|
||
|
||
# 5. 验证结果
|
||
for name, result in results.items():
|
||
if result is None:
|
||
fail_count += 1
|
||
continue
|
||
|
||
# 检查形状
|
||
if result.ndim != 2:
|
||
print(f"[e2e] 因子 {name} 形状错误: {result.ndim}D(期望 2D)")
|
||
fail_count += 1
|
||
continue
|
||
|
||
# 检查是否全 NaN
|
||
if np.isnan(result).all():
|
||
print(f"[e2e] 因子 {name} 输出全为 NaN")
|
||
fail_count += 1
|
||
continue
|
||
|
||
success_count += 1
|
||
|
||
print(
|
||
f"[e2e] 成功 {success_count}/{total},"
|
||
f"跳过 {skip_count} 个,失败 {fail_count} 个"
|
||
)
|
||
|
||
# 断言:至少 50% 的因子能成功计算(宽松标准)
|
||
success_rate = success_count / total
|
||
assert success_rate >= 0.5, f"成功率 {success_rate:.1%} 低于 50% 阈值"
|
||
|
||
|
||
if __name__ == "__main__":
|
||
import numpy as np
|
||
from src.factorminer.core.library_io import import_from_paper
|
||
from src.factorminer.evaluation.local_engine import LocalFactorEvaluator
|
||
|
||
# 手动运行测试
|
||
library = import_from_paper()
|
||
factors = library.list_factors()
|
||
total = len(factors)
|
||
print(f"\n[e2e] 总计加载 {total} 个因子")
|
||
|
||
supported_factors = [f for f in factors if not f.metadata.get("unsupported", False)]
|
||
unsupported_count = total - len(supported_factors)
|
||
print(f"[e2e] 可计算因子 {len(supported_factors)} 个")
|
||
print(f"[e2e] 跳过(未实现算子){unsupported_count} 个")
|
||
|
||
if supported_factors:
|
||
evaluator = LocalFactorEvaluator(
|
||
start_date="20200101",
|
||
end_date="20201231",
|
||
)
|
||
specs = [(f.name, f.formula) for f in supported_factors]
|
||
print(f"[e2e] 开始批量计算 {len(specs)} 个因子...")
|
||
results = evaluator.evaluate(specs)
|
||
print(f"[e2e] 批量计算完成,共 {len(results)} 个结果")
|