mirror of
https://ghfast.top/https://github.com/aeroxw/tick-stock-panel.git
synced 2026-09-12 14:24:15 +08:00
Merge branch 'pr-219' into merge/pr-221-220-219
This commit is contained in:
@@ -158,7 +158,7 @@ def factor_run(req: FactorBacktestRequest, request: Request):
|
||||
svc = FactorBacktestService(engine)
|
||||
|
||||
end = req.end or date.today()
|
||||
start = _resolve_start(req, end, STRATEGY_DEFAULT_DAYS)
|
||||
start = _resolve_start(req, end, FACTOR_DEFAULT_DAYS)
|
||||
_guard_server_backtest_range(start, end)
|
||||
symbols = req.symbols if req.symbols else None
|
||||
if symbols is not None and len(symbols) > FACTOR_MAX_SYMBOLS:
|
||||
@@ -212,7 +212,7 @@ def factor_batch(req: FactorBatchRequest, request: Request):
|
||||
raise HTTPException(status_code=400, detail=f"不支持的因子: {', '.join(invalid)}")
|
||||
|
||||
end = req.end or date.today()
|
||||
start = _resolve_start(req, end, STRATEGY_DEFAULT_DAYS)
|
||||
start = _resolve_start(req, end, FACTOR_DEFAULT_DAYS)
|
||||
_guard_server_backtest_range(start, end)
|
||||
symbols = req.symbols if req.symbols else None
|
||||
if symbols is not None and len(symbols) > FACTOR_MAX_SYMBOLS:
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
"""因子回测默认区间回归测试。
|
||||
|
||||
覆盖 issue #202: `/api/backtest/factor/run` 与 `/api/backtest/factor/batch`
|
||||
在省略 `start` 时错误使用了策略默认区间(3 年)而非因子默认区间(180 天)。
|
||||
|
||||
只关注「起始日解析」这一关注点, 用替身 Service 捕获传入配置, 不做真实数据加载。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import date, timedelta
|
||||
from typing import ClassVar
|
||||
|
||||
import pytest
|
||||
|
||||
from app.api import backtest as bt
|
||||
|
||||
|
||||
@dataclass
|
||||
class _DummyResult:
|
||||
ok: bool = True
|
||||
|
||||
|
||||
class _CapturingService:
|
||||
"""替身 FactorBacktestService: 捕获配置, 不触真实引擎。"""
|
||||
|
||||
captured: ClassVar[list] = []
|
||||
|
||||
def __init__(self, engine):
|
||||
pass
|
||||
|
||||
def run(self, cfg):
|
||||
_CapturingService.captured.append(cfg)
|
||||
return _DummyResult()
|
||||
|
||||
def run_batch(self, cfg):
|
||||
_CapturingService.captured.append(cfg)
|
||||
return _DummyResult()
|
||||
|
||||
|
||||
class _Req:
|
||||
"""占位 request; _get_engine 已被 patch, 不会真正访问其属性。"""
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def patched(monkeypatch):
|
||||
_CapturingService.captured = []
|
||||
monkeypatch.setattr(bt, "_get_engine", lambda request: object())
|
||||
# factor_run / factor_batch 内部 `from app.backtest.factor import FactorBacktestService`
|
||||
# 读取的是模块属性, patch 源模块即可命中。
|
||||
import app.backtest.factor as factor_mod
|
||||
monkeypatch.setattr(factor_mod, "FactorBacktestService", _CapturingService)
|
||||
# 隔离守卫, 单测「起始日解析」。
|
||||
monkeypatch.setattr(bt, "_guard_server_backtest_range", lambda start, end: None)
|
||||
return _CapturingService
|
||||
|
||||
|
||||
def test_factor_run_omitted_start_uses_factor_default(patched):
|
||||
end = date(2026, 8, 24)
|
||||
bt.factor_run(bt.FactorBacktestRequest(factor_name="momentum_5d", end=end), _Req())
|
||||
cfg = patched.captured[-1]
|
||||
assert cfg.start == end - timedelta(days=bt.FACTOR_DEFAULT_DAYS)
|
||||
# 回归保护: 不得再退回策略默认的 3 年。
|
||||
assert cfg.start != end - timedelta(days=bt.STRATEGY_DEFAULT_DAYS)
|
||||
|
||||
|
||||
def test_factor_batch_omitted_start_uses_factor_default(patched):
|
||||
end = date(2026, 8, 24)
|
||||
bt.factor_batch(bt.FactorBatchRequest(factor_names=["momentum_5d"], end=end), _Req())
|
||||
cfg = patched.captured[-1]
|
||||
assert cfg.start == end - timedelta(days=bt.FACTOR_DEFAULT_DAYS)
|
||||
assert cfg.start != end - timedelta(days=bt.STRATEGY_DEFAULT_DAYS)
|
||||
|
||||
|
||||
def test_factor_run_explicit_null_start_means_all_history(patched):
|
||||
"""显式传 start=null 语义不变: 代表全部历史。"""
|
||||
end = date(2026, 8, 24)
|
||||
bt.factor_run(bt.FactorBacktestRequest(factor_name="momentum_5d", start=None, end=end), _Req())
|
||||
assert patched.captured[-1].start == date(1900, 1, 1)
|
||||
|
||||
|
||||
def test_factor_run_explicit_start_passthrough(patched):
|
||||
"""显式日期原样透传。"""
|
||||
end = date(2026, 8, 24)
|
||||
bt.factor_run(
|
||||
bt.FactorBacktestRequest(factor_name="momentum_5d", start=date(2026, 1, 1), end=end),
|
||||
_Req(),
|
||||
)
|
||||
assert patched.captured[-1].start == date(2026, 1, 1)
|
||||
Reference in New Issue
Block a user