- Implemented portfolio engine with risk-based allocation (50/50) - Added equity-based metrics for system-level evaluation - Validated portfolio against standalone strategies - Reduced max drawdown and volatility at system level - Quantitative decision closed before paper trading phase
96 lines
2.4 KiB
Python
96 lines
2.4 KiB
Python
# src/strategies/mean_reversion.py
|
|
|
|
import pandas as pd
|
|
import numpy as np
|
|
|
|
from src.strategies.base import Strategy
|
|
from src.core.strategy import Signal
|
|
|
|
|
|
class RSIMeanReversion(Strategy):
|
|
"""
|
|
Estrategia de reversión a la media basada en RSI.
|
|
|
|
Idea:
|
|
- Compra cuando el mercado está sobrevendido
|
|
- Vende cuando el precio rebota hacia la media
|
|
|
|
Señales:
|
|
- BUY: RSI cruza por debajo de oversold
|
|
- SELL: RSI cruza por encima de overbought
|
|
- HOLD: en cualquier otro caso
|
|
|
|
Parámetros:
|
|
period: periodo del RSI
|
|
oversold: nivel de sobreventa
|
|
overbought: nivel de sobrecompra
|
|
|
|
Valores típicos:
|
|
period = 14
|
|
oversold = 30
|
|
overbought = 70
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
period: int = 14,
|
|
oversold: float = 30.0,
|
|
overbought: float = 70.0,
|
|
):
|
|
super().__init__(
|
|
name="RSI_MeanReversion",
|
|
params={
|
|
"period": period,
|
|
"oversold": oversold,
|
|
"overbought": overbought,
|
|
},
|
|
)
|
|
|
|
self.period = period
|
|
self.oversold = oversold
|
|
self.overbought = overbought
|
|
|
|
# --------------------------------------------------
|
|
def init_indicators(self, data: pd.DataFrame) -> pd.DataFrame:
|
|
"""
|
|
Calcula el RSI clásico (Wilder).
|
|
|
|
Añade:
|
|
- data["rsi"]
|
|
"""
|
|
delta = data["close"].diff()
|
|
|
|
gain = delta.clip(lower=0)
|
|
loss = -delta.clip(upper=0)
|
|
|
|
avg_gain = gain.ewm(alpha=1 / self.period, adjust=False).mean()
|
|
avg_loss = loss.ewm(alpha=1 / self.period, adjust=False).mean()
|
|
|
|
rs = avg_gain / avg_loss
|
|
rsi = 100 - (100 / (1 + rs))
|
|
|
|
data["rsi"] = rsi
|
|
|
|
return data
|
|
|
|
# --------------------------------------------------
|
|
def generate_signal(self, idx: int) -> Signal:
|
|
"""
|
|
Genera señales de trading basadas en cruces del RSI.
|
|
"""
|
|
if idx == 0:
|
|
return Signal.HOLD
|
|
|
|
rsi_prev = self.data["rsi"].iloc[idx - 1]
|
|
rsi_curr = self.data["rsi"].iloc[idx]
|
|
|
|
# BUY → cruce hacia abajo de oversold
|
|
if rsi_prev > self.oversold and rsi_curr <= self.oversold:
|
|
return Signal.BUY
|
|
|
|
# SELL → cruce hacia arriba de overbought
|
|
if rsi_prev < self.overbought and rsi_curr >= self.overbought:
|
|
return Signal.SELL
|
|
|
|
return Signal.HOLD
|