61 lines
2.0 KiB
Python
61 lines
2.0 KiB
Python
"""事件回测纯函数:入场/出场索引语义、汇总统计(含空样本与分年)。"""
|
||
from __future__ import annotations
|
||
|
||
from datetime import datetime
|
||
|
||
import pytest
|
||
|
||
from app.backtest.events import _entry_exit_indices, _stats_block
|
||
from app.schemas import EventBacktestSpec
|
||
|
||
|
||
def _spec(holding_days: int = 5) -> EventBacktestSpec:
|
||
return EventBacktestSpec.model_validate({
|
||
"holding_days": holding_days,
|
||
"entry": {"indicator": []},
|
||
})
|
||
|
||
|
||
def test_entry_exit_next_day_and_hold():
|
||
spec = _spec(holding_days=5)
|
||
assert _entry_exit_indices(10, spec, n=100) == (11, 16) # 次日入,持有 5 日出
|
||
|
||
|
||
def test_entry_exit_out_of_range_none():
|
||
spec = _spec(holding_days=5)
|
||
# 出场索引越界(exit_i >= n)
|
||
assert _entry_exit_indices(94, spec, n=100) is None
|
||
assert _entry_exit_indices(99, spec, n=100) is None
|
||
|
||
|
||
def test_entry_exit_boundary_exact_fit():
|
||
spec = _spec(holding_days=5)
|
||
# exit_i == n-1 恰好可用
|
||
assert _entry_exit_indices(93, spec, n=100) == (94, 99)
|
||
|
||
|
||
def test_stats_block_empty():
|
||
s = _stats_block([])
|
||
assert s["samples"] == 0 and s["stocks"] == 0
|
||
assert s["by_year"] == []
|
||
assert s["win_rate"] == 0.0
|
||
|
||
|
||
def test_stats_block_aggregates():
|
||
trades = [
|
||
{"ts_code": "000001.SZ", "ret_pct": 10.0,
|
||
"entry_date": datetime(2024, 1, 5), "entry_price": 10, "exit_price": 11},
|
||
{"ts_code": "000001.SZ", "ret_pct": -4.0,
|
||
"entry_date": datetime(2024, 3, 6), "entry_price": 10, "exit_price": 9.6},
|
||
{"ts_code": "600519.SH", "ret_pct": 2.0,
|
||
"entry_date": datetime(2023, 5, 10), "entry_price": 10, "exit_price": 10.2},
|
||
]
|
||
s = _stats_block(trades)
|
||
assert s["samples"] == 3
|
||
assert s["stocks"] == 2
|
||
assert s["mean_pct"] == pytest.approx((10.0 - 4.0 + 2.0) / 3, abs=1e-3) # 统计块保留 3 位小数
|
||
assert s["win_rate"] == round(2 / 3 * 100, 2)
|
||
assert [y["year"] for y in s["by_year"]] == [2023, 2024] # 分年升序
|
||
assert s["by_year"][1]["samples"] == 2
|
||
assert s["max_pct"] == 10.0 and s["min_pct"] == -4.0
|