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)} 个结果")
|