feat: integrate v2 backtest pipeline for triple barrier and walk-forward (#305)
This commit is contained in:
136
tests/test_backtest_pipeline_integration.py
Normal file
136
tests/test_backtest_pipeline_integration.py
Normal file
@@ -0,0 +1,136 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from src.analysis.backtest_cost_guard import BacktestCostModel
|
||||
from src.analysis.backtest_pipeline import (
|
||||
BacktestBar,
|
||||
WalkForwardConfig,
|
||||
fold_has_leakage,
|
||||
run_v2_backtest_pipeline,
|
||||
)
|
||||
from src.analysis.triple_barrier import TripleBarrierSpec
|
||||
from src.analysis.walk_forward_split import generate_walk_forward_splits
|
||||
|
||||
|
||||
def _bars() -> list[BacktestBar]:
|
||||
closes = [100.0, 101.0, 102.0, 101.5, 103.0, 102.5, 104.0, 103.5, 105.0, 104.5, 106.0, 105.5]
|
||||
bars: list[BacktestBar] = []
|
||||
for i, close in enumerate(closes):
|
||||
bars.append(
|
||||
BacktestBar(
|
||||
high=close + 1.0,
|
||||
low=close - 1.0,
|
||||
close=close,
|
||||
session_id="KRX_REG" if i % 2 == 0 else "US_PRE",
|
||||
)
|
||||
)
|
||||
return bars
|
||||
|
||||
|
||||
def _cost_model() -> BacktestCostModel:
|
||||
return BacktestCostModel(
|
||||
commission_bps=3.0,
|
||||
slippage_bps_by_session={"KRX_REG": 10.0, "US_PRE": 50.0},
|
||||
failure_rate_by_session={"KRX_REG": 0.01, "US_PRE": 0.08},
|
||||
unfavorable_fill_required=True,
|
||||
)
|
||||
|
||||
|
||||
def test_pipeline_happy_path_returns_fold_and_artifact_contract() -> None:
|
||||
out = run_v2_backtest_pipeline(
|
||||
bars=_bars(),
|
||||
entry_indices=[0, 1, 2, 3, 4, 5, 6, 7],
|
||||
side=1,
|
||||
triple_barrier_spec=TripleBarrierSpec(
|
||||
take_profit_pct=0.02,
|
||||
stop_loss_pct=0.01,
|
||||
max_holding_bars=3,
|
||||
),
|
||||
walk_forward=WalkForwardConfig(
|
||||
train_size=4,
|
||||
test_size=2,
|
||||
step_size=2,
|
||||
purge_size=1,
|
||||
embargo_size=1,
|
||||
min_train_size=3,
|
||||
),
|
||||
cost_model=_cost_model(),
|
||||
)
|
||||
|
||||
assert out.run_id.startswith("v2p-e8-f")
|
||||
assert out.n_bars == 12
|
||||
assert out.n_entries == 8
|
||||
assert out.required_sessions == ["KRX_REG", "US_PRE"]
|
||||
assert len(out.folds) > 0
|
||||
assert set(out.label_distribution) == {-1, 0, 1}
|
||||
for fold in out.folds:
|
||||
names = {score.name for score in fold.baseline_scores}
|
||||
assert names == {"B0", "B1", "M1"}
|
||||
for score in fold.baseline_scores:
|
||||
assert 0.0 <= score.accuracy <= 1.0
|
||||
|
||||
|
||||
def test_pipeline_cost_guard_fail_fast() -> None:
|
||||
bad = BacktestCostModel(
|
||||
commission_bps=3.0,
|
||||
slippage_bps_by_session={"KRX_REG": 10.0},
|
||||
failure_rate_by_session={"KRX_REG": 0.01},
|
||||
unfavorable_fill_required=True,
|
||||
)
|
||||
try:
|
||||
run_v2_backtest_pipeline(
|
||||
bars=_bars(),
|
||||
entry_indices=[0, 1, 2, 3],
|
||||
side=1,
|
||||
triple_barrier_spec=TripleBarrierSpec(
|
||||
take_profit_pct=0.02,
|
||||
stop_loss_pct=0.01,
|
||||
max_holding_bars=3,
|
||||
),
|
||||
walk_forward=WalkForwardConfig(train_size=2, test_size=1),
|
||||
cost_model=bad,
|
||||
required_sessions=["KRX_REG", "US_PRE"],
|
||||
)
|
||||
except ValueError as exc:
|
||||
assert "missing slippage_bps_by_session" in str(exc)
|
||||
else:
|
||||
raise AssertionError("expected cost guard validation error")
|
||||
|
||||
|
||||
def test_pipeline_fold_leakage_guard() -> None:
|
||||
folds = generate_walk_forward_splits(
|
||||
n_samples=12,
|
||||
train_size=6,
|
||||
test_size=2,
|
||||
step_size=2,
|
||||
purge_size=1,
|
||||
embargo_size=1,
|
||||
min_train_size=5,
|
||||
)
|
||||
assert folds
|
||||
for fold in folds:
|
||||
assert not fold_has_leakage(fold)
|
||||
|
||||
|
||||
def test_pipeline_deterministic_seed_free_deterministic_result() -> None:
|
||||
cfg = dict(
|
||||
bars=_bars(),
|
||||
entry_indices=[0, 1, 2, 3, 4, 5, 6, 7],
|
||||
side=1,
|
||||
triple_barrier_spec=TripleBarrierSpec(
|
||||
take_profit_pct=0.02,
|
||||
stop_loss_pct=0.01,
|
||||
max_holding_bars=3,
|
||||
),
|
||||
walk_forward=WalkForwardConfig(
|
||||
train_size=4,
|
||||
test_size=2,
|
||||
step_size=2,
|
||||
purge_size=1,
|
||||
embargo_size=1,
|
||||
min_train_size=3,
|
||||
),
|
||||
cost_model=_cost_model(),
|
||||
)
|
||||
out1 = run_v2_backtest_pipeline(**cfg)
|
||||
out2 = run_v2_backtest_pipeline(**cfg)
|
||||
assert out1 == out2
|
||||
Reference in New Issue
Block a user