import sys from pathlib import Path import pandas as pd from datetime import datetime # 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.backtest.trade import TradeStatus from src.risk.stops.fixed_stop import FixedStop class AlwaysBuyStrategy(Strategy): """ Estrategia dummy para testing: - Compra en la primera vela - Nunca vende """ def __init__(self): super().__init__(name="AlwaysBuy", params={}) def init_indicators(self, data): return data def generate_signal(self, idx: int): if idx == 0: return Signal.BUY return Signal.HOLD def _build_test_data(): timestamps = pd.date_range( start=datetime(2024, 1, 1), periods=5, freq="1h" ) return pd.DataFrame( { "open": [100, 100, 100, 100, 100], "high": [101, 101, 101, 101, 101], "low": [99, 98, 95, 90, 85], "close": [100, 99, 96, 91, 86], "volume": [1, 1, 1, 1, 1], }, index=timestamps ) def test_engine_closes_position_on_stop_hit(): """ Con stop activo → debe cerrarse por Stop Loss """ data = _build_test_data() strategy = AlwaysBuyStrategy() engine = BacktestEngine( strategy=strategy, initial_capital=10_000, commission=0.0, slippage=0.0, position_size=1.0, stop_loss=FixedStop(0.03) ) engine.run(data) assert len(engine.trades) == 1 trade = engine.trades[0] assert trade.status == TradeStatus.CLOSED assert trade.exit_reason == "Stop Loss" def test_engine_closes_position_at_end_without_stop(): """ Sin stop → la posición debe cerrarse al final del backtest """ data = _build_test_data() strategy = AlwaysBuyStrategy() engine = BacktestEngine( strategy=strategy, initial_capital=10_000, commission=0.0, slippage=0.0, position_size=1.0, stop_loss=None ) engine.run(data) assert len(engine.trades) == 1 trade = engine.trades[0] assert trade.status == TradeStatus.CLOSED assert trade.exit_reason == "End of backtest"