feat: finalize portfolio system and quantitative validation- Finalized MA_Crossover(30,100) and TrendFiltered_MA(30,100,ADX=15)
- 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
This commit is contained in:
95
src/strategies/mean_reversion.py
Normal file
95
src/strategies/mean_reversion.py
Normal file
@@ -0,0 +1,95 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user