"""kr_backtest_engine: 원주가 불연속 역보정 + regime='AUTO' 거부.""" import asyncio from datetime import date, timedelta import pytest from app.core.config import settings from app.services.kr_backtest_engine import ( BarData, KrBacktestEngine, MeanReversionBacktest, back_adjust_price_events_v2, ) D0 = date(2026, 1, 5) def _b(i, px, vol=1000): return BarData(trade_date=D0 + timedelta(days=i), open_price=px, high_price=px, low_price=px, close_price=px, volume=vol, stock_code="A") def test_split_back_adjusted(): bars = [_b(0, 1000), _b(1, 1000), _b(2, 500), _b(3, 505)] n = back_adjust_price_events_v2(bars, [None, 0.0, 0.0, 0.01]) assert n == 1 assert [b.close_price for b in bars] == [500, 500, 500, 505] assert bars[0].volume == 2000 and bars[2].volume == 1000 def test_normal_move_untouched_and_no_rate_skipped(): bars = [_b(0, 1000), _b(1, 900)] assert back_adjust_price_events_v2(bars, [None, -0.10]) == 0 assert back_adjust_price_events_v2([_b(0, 1000), _b(1, 400)], [None, None]) == 0 assert bars[0].close_price == 1000 def test_genuine_crash_not_adjusted(): bars = [_b(0, 1000), _b(1, 700)] assert back_adjust_price_events_v2(bars, [None, -0.30]) == 0 def test_gap_not_event(): bars = [_b(0, 1000), _b(10, 400)] assert back_adjust_price_events_v2(bars, [None, 0.0]) == 0 def test_auto_regime_rejected(): with pytest.raises(ValueError): MeanReversionBacktest._get_regime({"regime": "auto"}) with pytest.raises(ValueError): MeanReversionBacktest._get_regime({"regime": "???"}) assert MeanReversionBacktest._get_regime({"regime": "bear"}).name == "BEAR" def test_run_rejects_auto_before_loading(monkeypatch): async def boom(*a, **k): raise AssertionError("데이터 로드까지 가면 안 됨") monkeypatch.setattr("app.services.kr_backtest_engine.load_daily_bars", boom) with pytest.raises(ValueError): asyncio.run(KrBacktestEngine().run(["A"], D0, D0, params={"regime": "AUTO"})) def test_default_regime_valid(): assert MeanReversionBacktest().default_params()["regime"] == "BULL" assert settings.MAX_POSITION_SIZE > 0