# src/risk/stops/trailing_stop.py import pandas as pd from src.risk.stops.base import StopLoss from src.core.trade import TradeType, Position class TrailingStop(StopLoss): """ Trailing Stop porcentual basado en el precio de cierre. - LONG: el stop solo sube - SHORT: el stop solo baja """ def __init__(self, trailing_fraction: float): """ Args: trailing_fraction: ej 0.05 = 5% """ if trailing_fraction <= 0 or trailing_fraction >= 1: raise ValueError("trailing_fraction debe estar entre 0 y 1") self.trailing_fraction = trailing_fraction def get_stop_price( self, *, data: pd.DataFrame, idx: int, entry_price: float, trade_type: TradeType ) -> float: """ Stop inicial al abrir la posición """ if trade_type == TradeType.LONG: return entry_price * (1 - self.trailing_fraction) elif trade_type == TradeType.SHORT: return entry_price * (1 + self.trailing_fraction) else: raise ValueError(f"TradeType no soportado: {trade_type}") def update_stop( self, *, data: pd.DataFrame, idx: int, position: Position ) -> float | None: """ Calcula un nuevo stop si el precio ha mejorado. Devuelve None si el stop no debe moverse. """ current_price = data.iloc[idx]["close"] if position.trade_type == TradeType.LONG: candidate = current_price * (1 - self.trailing_fraction) if position.stop_price is None or candidate > position.stop_price: return candidate elif position.trade_type == TradeType.SHORT: candidate = current_price * (1 + self.trailing_fraction) if position.stop_price is None or candidate < position.stop_price: return candidate return None