# tests/backtest/test_engine_sizing.py import pytest import sys from pathlib import Path import pandas as pd from datetime import datetime, timedelta # Añadir raíz del proyecto al path sys.path.insert(0, str(Path(__file__).parent.parent.parent)) from src.backtest.engine import BacktestEngine from src.backtest.strategy import Strategy, Signal from src.risk.sizing.fixed import FixedPositionSizer class BuyOnceStrategy(Strategy): """ Estrategia dummy: - BUY en la primera vela - SELL en la segunda """ def __init__(self): super().__init__(name="BuyOnce", params={}) def init_indicators(self, data: pd.DataFrame) -> pd.DataFrame: return data def generate_signal(self, idx: int) -> Signal: if idx == 0: return Signal.BUY if idx == 1: return Signal.SELL return Signal.HOLD def test_engine_uses_fixed_position_sizer(): """ El engine debe usar el PositionSizer y NO el position_size_fraction por defecto. """ # ------------------------- # Datos dummy # ------------------------- dates = [ datetime(2024, 1, 1), datetime(2024, 1, 2), datetime(2024, 1, 3), ] data = pd.DataFrame( { "open": [100, 100, 100], "high": [100, 100, 100], "low": [100, 100, 100], "close": [100, 100, 100], "volume": [1, 1, 1], }, index=dates, ) # ------------------------- # Engine + Sizer # ------------------------- strategy = BuyOnceStrategy() sizer = FixedPositionSizer(capital_fraction=0.5) engine = BacktestEngine( strategy=strategy, initial_capital=10000, commission=0.0, slippage=0.0, position_size=0.95, position_sizer=sizer ) results = engine.run(data) # ------------------------- # Validaciones # ------------------------- trades = results["trades"] assert len(trades) == 1 trade = trades[0] invested_value = trade.entry_price * trade.size # Esperamos ~50% del capital assert invested_value == pytest.approx(5000, rel=1e-3) # Sanity check assert invested_value < 9500 # NO debe usar 95%