--- title: "Análisis de Modelos ML - Vuelta 2" version: "1.0.0" date: "2026-01-06" status: "In Progress" author: "ML-Specialist + Orquestador" epic: "OQI-006" tags: ["ml", "analysis", "architecture", "integration", "refactoring"] --- # ANÁLISIS DE MODELOS ML - VUELTA 2 ## 1. RESUMEN DE HALLAZGOS VUELTA 1 ### 1.1 Brechas Críticas Identificadas | Brecha | Severidad | Estado Real | |--------|-----------|-------------| | Factores hardcodeados | CRÍTICA | **PARCIALMENTE RESUELTO** - `SYMBOL_CONFIGS` existe | | Sin separación activo/timeframe | ALTA | **RESUELTO** - `SymbolTimeframeTrainer` existe | | Atención no integrada | ALTA | **RESUELTO** - `use_dynamic_factor_weighting=True` | | Win rate bajo | CRÍTICA | Pendiente optimización | | R:R insuficiente | ALTA | Pendiente ajuste filtros | ### 1.2 Cambio de Diagnóstico **Vuelta 1**: Creíamos que faltaba infraestructura **Vuelta 2**: El problema es **integración y uso** de infraestructura existente --- ## 2. INFRAESTRUCTURA EXISTENTE DESCUBIERTA ### 2.1 Sistema de Configuración **Archivo**: `config/trading.yaml` ```yaml symbols: primary: ["XAUUSD", "EURUSD", "GBPUSD", "BTCUSD"] secondary: ["USDJPY", "GBPJPY", "AUDUSD", "NZDUSD"] timeframes: primary: 5 aggregations: [15, 30, 60, 240] ``` **Archivo**: `config/models.yaml` ```yaml xgboost: base: n_estimators: 200 max_depth: 5 learning_rate: 0.05 # ... configuración completa meta_model: type: "xgboost" # Soporte para metamodelo ya existe ``` ### 2.2 Trainer por Símbolo/Timeframe **Archivo**: `training/symbol_timeframe_trainer.py` ```python SYMBOL_CONFIGS = { 'XAUUSD': SymbolConfig(symbol='XAUUSD', base_factor=5.0, pip_value=0.01), 'BTCUSD': SymbolConfig(symbol='BTCUSD', base_factor=100.0, pip_value=0.01), 'EURUSD': SymbolConfig(symbol='EURUSD', base_factor=0.0005, pip_value=0.0001), 'GBPUSD': SymbolConfig(symbol='GBPUSD', base_factor=0.0006, pip_value=0.0001), 'USDJPY': SymbolConfig(symbol='USDJPY', base_factor=0.05, pip_value=0.01), } @dataclass class TrainerConfig: timeframes: List[str] = ['5m', '15m'] symbols: List[str] = ['XAUUSD', 'BTCUSD', 'EURUSD'] use_dynamic_factor_weighting: bool = True # ✅ Ya habilitado use_atr_weighting: bool = True # ✅ Ya habilitado factor_window: int = 200 softplus_beta: float = 4.0 softplus_w_max: float = 3.0 ``` ### 2.3 Modelos Ya Entrenados Separados **Ubicación**: `models/ml_first/` ``` ml_first/ └── XAUUSD/ └── movement_predictor/ ├── 5m_15min/ │ └── metadata.yaml └── 15m_60min/ └── metadata.yaml ``` --- ## 3. ANÁLISIS DE BRECHAS REAL ### 3.1 El Problema: Fragmentación de Código **Código Nuevo (Correcto)** - `symbol_timeframe_trainer.py`: ```python SYMBOL_CONFIGS = { 'XAUUSD': SymbolConfig(symbol='XAUUSD', base_factor=5.0, ...), # Factores dinámicos por símbolo } ``` **Código Legacy (Incorrecto)** - `range_predictor_factor.py:598-601`: ```python SYMBOLS = { 'XAUUSD': {'base': 2650.0, 'volatility': 0.0012, 'factor': 2.5}, 'EURUSD': {'base': 1.0420, 'volatility': 0.0004, 'factor': 0.0003}, } # Hardcoded, solo 2 símbolos, valores estáticos ``` ### 3.2 Dependencias No Conectadas ``` symbol_timeframe_trainer.py ──✅─→ DynamicFactorWeighter │ └── ❌ NO es usado por → range_predictor_factor.py enhanced_range_predictor.py prediction_service.py ``` ### 3.3 APIs No Usan Trainer Correcto **prediction_service.py** usa modelos legacy: - `RangePredictorFactor` (hardcoded) - `EnhancedRangePredictor` (parcialmente dinámico) **Debería usar**: - `SymbolTimeframeTrainer.predict()` - O modelos cargados desde `models/ml_first/{symbol}/{timeframe}/` --- ## 4. ARQUITECTURA PROPUESTA DE INTEGRACIÓN ### 4.1 Nuevo Flujo de Predicción ``` ┌─────────────────────────────────────────────────────────────────┐ │ PREDICTION SERVICE (Unificado) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Request: { symbol: "XAUUSD", timeframe: "15m" } │ │ │ │ 1. Load Config: SYMBOL_CONFIGS['XAUUSD'] │ │ → base_factor: 5.0, pip_value: 0.01 │ │ │ │ 2. Load Model: models/XAUUSD/15m/range_xgb.joblib │ │ │ │ 3. Apply Attention: DynamicFactorWeighter │ │ → compute_factor_median_range(df, window=200) │ │ → weight_smooth(m, w_max=3.0, beta=4.0) │ │ │ │ 4. Predict: model.predict(features, attention_weight) │ │ │ │ 5. Post-process: scale by base_factor │ │ │ │ Response: { delta_high, delta_low, confidence, attention } │ └─────────────────────────────────────────────────────────────────┘ ``` ### 4.2 Refactoring Requerido **Paso 1: Eliminar Factores Hardcodeados** ```python # ANTES (range_predictor_factor.py) class PriceDataGenerator: SYMBOLS = {...} # Hardcoded # DESPUÉS from training.symbol_timeframe_trainer import SYMBOL_CONFIGS class PriceDataGenerator: def __init__(self, symbol: str): self.config = SYMBOL_CONFIGS.get(symbol, self._default_config(symbol)) ``` **Paso 2: Unificar Carga de Modelos** ```python # prediction_service.py class UnifiedPredictionService: def __init__(self): self.trainer = SymbolTimeframeTrainer() self.trainer.load('models/ml_first/') # Cargar modelos entrenados def predict(self, symbol: str, timeframe: str, df: pd.DataFrame): return self.trainer.predict(df, symbol, timeframe) ``` **Paso 3: Integrar en API** ```python # api/main.py @app.post("/predict/range/{symbol}") async def predict_range(symbol: str, timeframe: str = "15m"): predictions = prediction_service.predict(symbol, timeframe, data) return predictions ``` --- ## 5. ANÁLISIS DE HYPERPARÁMETROS ### 5.1 XGBoost - Comparación de Configs | Parámetro | config/models.yaml | symbol_timeframe_trainer.py | Recomendación | |-----------|-------------------|----------------------------|---------------| | n_estimators | 200 | 300 | 300 (más estable) | | max_depth | 5 | 6 | 5 (evitar overfitting) | | learning_rate | 0.05 | 0.03 | 0.03 (más lento, mejor) | | subsample | 0.8 | 0.8 | 0.8 ✅ | | min_child_weight | 3 | 10 | 10 (más conservador) | ### 5.2 Attention Weighting - Config Óptima | Parámetro | Valor Actual | Propuesto | Razón | |-----------|--------------|-----------|-------| | factor_window | 200 | 200 ✅ | Suficiente contexto | | softplus_beta | 4.0 | 4.0 ✅ | Transición suave | | softplus_w_max | 3.0 | 3.0 ✅ | Cap razonable | ### 5.3 Configuración Óptima Propuesta ```yaml # config/unified_model.yaml training: use_dynamic_factor_weighting: true use_atr_weighting: true use_session_weighting: false # Deshabilitar, no aporta en backtests factor: window: 200 min_periods: 100 softplus: beta: 4.0 w_max: 3.0 xgboost: n_estimators: 300 max_depth: 5 learning_rate: 0.03 subsample: 0.8 colsample_bytree: 0.8 min_child_weight: 10 gamma: 0.1 reg_alpha: 0.1 reg_lambda: 1.0 ``` --- ## 6. ANÁLISIS DE MODELOS POR TIPO ### 6.1 XGBoost vs GRU vs Transformer | Criterio | XGBoost | GRU | Transformer | |----------|---------|-----|-------------| | **Velocidad inferencia** | ⚡ Rápido | 🔸 Medio | 🔸 Medio | | **Memoria** | ⚡ Bajo | 🔸 Medio | 🔴 Alto | | **Interpretabilidad** | ⚡ Alta | 🔴 Baja | 🔴 Baja | | **Dependencias temporales** | 🔴 Limitado | ⚡ Excelente | ⚡ Excelente | | **Volatility Attention** | 🔸 Via sample_weight | ⚡ Nativo | ⚡ Nativo | | **Backtests actuales** | 80% WR (scalping) | No probado | Implementado | **Recomendación**: 1. **Corto plazo**: XGBoost con attention via sample_weight (ya funciona) 2. **Mediano plazo**: Agregar GRU como modelo secundario 3. **Largo plazo**: Transformer con VolatilityBiasedSelfAttention ### 6.2 Arquitectura Híbrida Propuesta ``` ┌─────────────────────────────────────────────────────────────┐ │ HYBRID ENSEMBLE │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Level 1: Base Models │ │ ├── XGBoost (Range): 35% weight │ │ │ └── Sample weight: DynamicFactorWeighter │ │ │ │ │ ├── Transformer (Attention): 30% weight │ │ │ └── VolatilityBiasedSelfAttention │ │ │ │ │ ├── AMD Detector: 20% weight │ │ │ └── Phase-aware filtering │ │ │ │ │ └── ICT/SMC: 15% weight │ │ └── Order Blocks + FVG │ │ │ │ Level 2: Meta-Model │ │ └── XGBoost (stacking) │ │ └── Features: predictions + volatility + session │ │ │ │ Output: Unified Signal + Confidence │ └─────────────────────────────────────────────────────────────┘ ``` --- ## 7. FEATURES ÓPTIMOS POR MODELO ### 7.1 Features Críticos (de trading.yaml) **Set Mínimo (14 features)** - Mejor performance: ```yaml momentum: [macd_signal, macd_histogram, rsi] trend: [sma_10, sma_20, sar] volatility: [atr] volume: [obv, ad, cmf, mfi] patterns: [fractals_high, fractals_low, volume_zscore] ``` ### 7.2 Features por Modelo | Modelo | Features Prioritarios | Razón | |--------|----------------------|-------| | RangePredictor | atr, bollinger_width, volume_zscore | Volatilidad determina rango | | AMDDetector | volume, obv, cmf, rsi | Detección de fases | | ICT/SMC | support_levels, resistance_levels, sar | Estructura de mercado | | TPSLClassifier | atr, direction, momentum | Probabilidad de hit | ### 7.3 Feature Engineering Adicional ```python # Nuevos features propuestos new_features = { 'range_factor': (High - Low) / rolling_median(High - Low, 200).shift(1), 'atr_ratio': ATR / ATR.rolling(20).mean(), 'volume_momentum': Volume.pct_change(3), 'session_code': encode_session(timestamp), # 0=Asian, 1=London, 2=NY 'hour_sin': np.sin(2 * np.pi * hour / 24), 'hour_cos': np.cos(2 * np.pi * hour / 24), } ``` --- ## 8. OPTIMIZACIÓN DE FILTROS DIRECCIONALES ### 8.1 Hallazgo de Backtests > "100% de trades ganadores fueron SHORT en XAUUSD 5m" **Filtros que funcionaron**: ```python SHORT_FILTERS = { 'rsi': '> 55', # Sobreextensión alcista 'sar': 'above_price', # SAR bajista 'cmf': '< 0', # Flujo vendedor 'mfi': '> 55', # Money flow alto (distribución) } ``` ### 8.2 Filtros Propuestos por Dirección ```python class DirectionalFilters: """Filtros mejorados basados en backtests""" @staticmethod def short_signal_valid(indicators: dict) -> bool: """SHORT: 2+ confirmaciones requeridas""" confirmations = 0 if indicators['rsi'] > 55: confirmations += 1 if indicators['sar_above_price']: confirmations += 1 if indicators['cmf'] < 0: confirmations += 1 if indicators['mfi'] > 55: confirmations += 1 return confirmations >= 2 @staticmethod def long_signal_valid(indicators: dict) -> bool: """LONG: 3+ confirmaciones requeridas (más estricto)""" confirmations = 0 if indicators['rsi'] < 35: confirmations += 1 if not indicators['sar_above_price']: confirmations += 1 if indicators['cmf'] > 0.1: confirmations += 1 if indicators['mfi'] < 35: confirmations += 1 return confirmations >= 3 ``` --- ## 9. PLAN DE IMPLEMENTACIÓN VUELTA 2 ### Fase 2.1: Consolidación de Configuración - [ ] Migrar `SYMBOLS` de `range_predictor_factor.py` a `SYMBOL_CONFIGS` - [ ] Crear `config/symbols.yaml` centralizado - [ ] Actualizar todos los modelos para usar config unificada ### Fase 2.2: Integración de Trainer - [ ] Modificar `prediction_service.py` para usar `SymbolTimeframeTrainer` - [ ] Cargar modelos desde `models/ml_first/{symbol}/{timeframe}/` - [ ] Agregar fallback a legacy models durante transición ### Fase 2.3: Optimización de Filtros - [ ] Implementar `DirectionalFilters` en `signal_generator.py` - [ ] Ajustar thresholds según backtests - [ ] Agregar SHORT bias para XAUUSD ### Fase 2.4: Ensemble Mejorado - [ ] Actualizar pesos de `StrategyEnsemble` basado en performance - [ ] Agregar adaptive weighting basado en volatility regime - [ ] Integrar XGBoost meta-model --- ## 10. MÉTRICAS OBJETIVO ACTUALIZADAS ### 10.1 Con Infraestructura Existente | Métrica | Actual | Objetivo Vuelta 2 | Objetivo Final | |---------|--------|------------------|----------------| | Win Rate (fuertes) | 33-44% | 60% | 80% | | R:R Ratio | 1.2:1 | 1.8:1 | 2.5:1 | | Profit Factor | 1.07 | 1.3 | 1.8 | | Max Drawdown | 15% | 12% | 10% | ### 10.2 Quick Wins Identificados 1. **Usar filtros SHORT en XAUUSD**: +10% win rate estimado 2. **Integrar DynamicFactorWeighter**: Mejor selección de señales 3. **Cargar modelos separados**: Predicciones más precisas por símbolo 4. **Ajustar confidence threshold a 0.7**: Menos trades, mejor calidad --- ## 11. PRÓXIMOS PASOS ### Vuelta 2 - FASE 2: Análisis Profundo de Arquitectura 1. Revisar implementación de `VolatilityBiasedSelfAttention` 2. Analizar métricas de modelos entrenados existentes 3. Benchmark XGBoost vs Transformer en datos reales ### Vuelta 2 - FASE 3: Retroalimentación 1. Documentar decisiones arquitectónicas 2. Proponer refactoring mínimo viable 3. Definir tests de regresión --- ## 12. ANÁLISIS PROFUNDO DE ARQUITECTURA (FASE 2) ### 12.1 Métricas de Modelos Entrenados **XAUUSD Movement Predictor - Modelos Existentes:** | Modelo | Horizonte | R² High | R² Low | MAE High | MAE Low | Muestras | |--------|-----------|---------|--------|----------|---------|----------| | 5m→15min | 15 min | 38.85% | 40.24% | 0.76 USD | 0.78 USD | 135,199 | | 15m→60min | 60 min | 48.32% | 55.55% | 1.42 USD | 1.37 USD | 45,500 | **Observaciones:** - El modelo 15m→60min tiene mejor R² (48-56% vs 39-40%) - Más muestras no implica mejor modelo (5m tiene 3x muestras pero peor R²) - Horizonte más largo = más predecible (menos ruido) **Baseline Stats (5m→15min):** ``` mean_high: 1.52 USD mean_low: 1.64 USD total_range: 3.16 USD ``` ### 12.2 Features Usados por Modelos Entrenados **110 Features en MovementPredictor:** ``` Categorías: ├── Range Features (20): bar_range_usd, avg_range_usd_*, range_zscore_*, range_pctl_* ├── Momentum (16): momentum_*, momentum_abs_*, range_roc_* ├── ATR/Volatility (12): atr_*, atr_pct_*, vol_clustering_* ├── Price Position (12): price_position_*, dist_from_high_*, dist_from_low_* ├── Volume (12): volume_ma_*, volume_ratio_*, vol_range_* ├── High/Low Body (16): high_body, low_body, avg_high_move_*, high_low_ratio_* ├── Session (5): hour, day_of_week, is_london, is_ny, is_overlap └── Candlestick (17): body_size, upper_wick, lower_wick, body_to_range, avg_body_size_*, bullish_candles_* ``` **Feature Redundancy Identificada:** - 4 versiones de cada métrica (6, 12, 24, 48 períodos) = posible overfitting - Recomendación: Reducir a 2-3 períodos clave (6, 24) ### 12.3 Punto de Desconexión Identificado **prediction_service.py:157** ```python from ..models.range_predictor import RangePredictor # ← Legacy self._range_predictor = RangePredictor() # ← No usa modelos entrenados ``` **Debería ser:** ```python from ..training.symbol_timeframe_trainer import SymbolTimeframeTrainer self._trainer = SymbolTimeframeTrainer() self._trainer.load('models/ml_first/') # ← Cargar modelos separados ``` ### 12.4 Arquitectura de VolatilityBiasedSelfAttention **Componentes PyTorch implementados:** ```python VolatilityBiasedSelfAttention ├── Multi-head attention con bias de volatilidad ├── attn_weight: (B, T) aplicado a Q·K^T └── Softplus mapping para pesos suaves VolatilityAttentionBlock ├── LayerNorm + Attention + Residual └── LayerNorm + FeedForward + Residual VolatilityTransformerEncoder ├── Input projection + Positional encoding ├── N capas de VolatilityAttentionBlock └── Final LayerNorm VolatilityRangePredictor # ← Modelo completo ├── VolatilityTransformerEncoder └── Output heads para delta_high, delta_low ``` **Estado**: Implementado pero NO usado en producción (solo XGBoost activo) ### 12.5 Comparación de Pipelines | Aspecto | Pipeline Legacy | Pipeline Propuesto | |---------|-----------------|-------------------| | Modelos | `RangePredictor` genérico | `models/ml_first/{symbol}/{tf}/` | | Factores | Hardcoded en código | `SYMBOL_CONFIGS` + dinámico | | Atención | No usa | `DynamicFactorWeighter` | | Símbolo | Mezcla todos | Separado por símbolo | | Timeframe | Fijo | Separado por timeframe | | Entrenamiento | Manual | `SymbolTimeframeTrainer` | --- ## 13. QUICK WINS IDENTIFICADOS ### 13.1 Cambio de Bajo Riesgo - Alto Impacto **1. Cargar modelos entrenados en prediction_service.py** ```python # Línea ~157: Cambiar de from ..models.range_predictor import RangePredictor # A from ..models.movement_magnitude_predictor import MovementMagnitudePredictor # Y cargar modelo específico por símbolo ``` **2. Usar SYMBOL_CONFIGS en signal_generator.py** - Eliminar factores hardcodeados - Usar `base_factor` de configuración centralizada **3. Activar filtros direccionales en XAUUSD** - SHORT bias demostrado en backtests (+28 puntos porcentuales) - Implementar en `generate_signal()` ### 13.2 Impacto Estimado | Cambio | Esfuerzo | Impacto Win Rate | Riesgo | |--------|----------|------------------|--------| | Cargar modelos separados | Bajo | +5-10% | Bajo | | Filtros direccionales | Bajo | +10-15% | Bajo | | Integrar DynamicFactorWeighter | Medio | +5-10% | Medio | | Cambiar a Transformer | Alto | +10-20% | Alto | --- *Documento generado: 2026-01-06* *Estado: FASE 2 Vuelta 2 completada*