--- title: "Especificación Técnica: Sistema de Atención con Factores Dinámicos" version: "1.0.0" date: "2026-01-06" status: "Draft" author: "ML-Specialist + Orquestador" epic: "OQI-006" tags: ["ml", "attention", "factors", "dynamic", "specification"] priority: "HIGH" --- # ET-ML-FACTORES-ATENCION: Sistema de Atención con Factores Dinámicos ## 1. RESUMEN Este documento especifica la implementación de un sistema de atención basado en factores dinámicos calculados con ATR/mediana rolling, eliminando los factores hardcodeados actuales y permitiendo escalabilidad a 100+ activos. --- ## 2. PROBLEMA ACTUAL ### 2.1 Factores Hardcodeados **Ubicación actual**: `range_predictor_factor.py:598-601` ```python # PROBLEMA: Solo 2 activos, valores estáticos SYMBOLS = { 'XAUUSD': {'base': 2650.0, 'volatility': 0.0012, 'factor': 2.5}, 'EURUSD': {'base': 1.0420, 'volatility': 0.0004, 'factor': 0.0003}, } ``` ### 2.2 Impactos | Impacto | Descripción | Severidad | |---------|-------------|-----------| | Escalabilidad | No escala a 100+ activos | CRÍTICO | | Adaptabilidad | No se adapta a cambios de volatilidad | ALTO | | Mantenimiento | Requiere código nuevo por cada activo | MEDIO | | Precisión | Factores desactualizados degradan predicciones | ALTO | --- ## 3. SOLUCIÓN PROPUESTA ### 3.1 Cálculo Dinámico del Factor ```python def compute_factor_median_range( df: pd.DataFrame, window: int = 200, min_periods: int = 100 ) -> pd.Series: """ Factor dinámico = mediana rolling del rango de velas con shift(1). El shift(1) evita data leakage - solo usa información pasada. Args: df: DataFrame con High/Low window: Ventana rolling (default: 200 velas) min_periods: Períodos mínimos para calcular Returns: Serie con factor dinámico por timestamp """ range_col = df['High'] - df['Low'] factor = range_col.rolling(window=window, min_periods=min_periods).median().shift(1) return factor ``` ### 3.2 Mapeo de Pesos de Atención **Función smooth (softplus)**: ```python def weight_smooth(m: np.ndarray, w_max: float = 3.0, beta: float = 4.0) -> np.ndarray: """ Mapeo suave de multiplicador a peso de atención. Formula: w = log1p(exp(beta * (m - 1))) / beta Interpretación: - m < 1 → w ≈ 0 (ruido, ignorar) - m = 1 → w ≈ 0 (movimiento típico) - m = 2 → w ≈ 1 (2x normal, atención media) - m = 3 → w ≈ 2 (3x normal, atención alta) """ x = beta * (m - 1.0) w = np.where(x > 20, x / beta, np.log1p(np.exp(x)) / beta) return np.clip(w, 0.0, w_max) ``` ### 3.3 Ejemplo para XAUUSD | Variación Real | Factor Dinámico | Multiplicador | Peso de Atención | |----------------|-----------------|---------------|------------------| | 3.5 USD | 5.0 USD | 0.70 | **0.0** (ruido) | | 5.0 USD | 5.0 USD | 1.00 | **0.0** (normal) | | 7.5 USD | 5.0 USD | 1.50 | **~0.4** (interés) | | 10.0 USD | 5.0 USD | 2.00 | **~1.0** (atención) | | 15.0 USD | 5.0 USD | 3.00 | **~2.0** (alta atención) | | 20.0 USD | 5.0 USD | 4.00 | **3.0** (máximo) | --- ## 4. ARQUITECTURA DE IMPLEMENTACIÓN ### 4.1 Estructura de Archivos Propuesta ``` ml-engine/src/ ├── config/ │ ├── symbols_config.yaml # Configuración de símbolos (NEW) │ └── attention_config.yaml # Configuración de atención (NEW) ├── models/ │ ├── base/ │ │ └── attention_weighted_model.py # Base class (NEW) │ ├── trained/ │ │ ├── XAUUSD/ │ │ │ ├── 5m/ │ │ │ │ ├── range_predictor.joblib │ │ │ │ ├── movement_predictor.joblib │ │ │ │ └── config.yaml │ │ │ └── 15m/ │ │ │ └── ... │ │ ├── EURUSD/ │ │ │ └── ... │ │ └── BTCUSDT/ │ │ └── ... │ └── (existing models) └── training/ └── dynamic_factor_calculator.py # (NEW) ``` ### 4.2 symbols_config.yaml ```yaml # Configuración centralizada de símbolos # Factores iniciales para warm-start (se actualizan automáticamente) symbols: XAUUSD: category: "commodity" decimal_places: 2 pip_size: 0.01 initial_factor: 5.0 # Solo para warmup factor_window: 200 min_periods: 100 EURUSD: category: "forex" decimal_places: 5 pip_size: 0.0001 initial_factor: 0.0003 factor_window: 200 min_periods: 100 BTCUSDT: category: "crypto" decimal_places: 2 pip_size: 0.01 initial_factor: 200.0 factor_window: 200 min_periods: 100 # ... más símbolos ``` ### 4.3 DynamicFactorCalculator Class ```python class DynamicFactorCalculator: """ Calcula y mantiene factores dinámicos para todos los símbolos. Features: - Rolling median con shift(1) para evitar leakage - Cache de factores por símbolo - Actualización incremental (EMA) - Persistencia opcional en Redis/archivo """ def __init__(self, config_path: str = "config/symbols_config.yaml"): self.config = self._load_config(config_path) self._factors: Dict[str, float] = {} # Cache self._factor_history: Dict[str, List[float]] = {} def get_factor(self, symbol: str, df: pd.DataFrame = None) -> float: """Obtiene factor actual para un símbolo.""" if df is not None: return self._compute_factor(symbol, df) return self._factors.get(symbol, self.config['symbols'][symbol]['initial_factor']) def _compute_factor(self, symbol: str, df: pd.DataFrame) -> float: """Calcula factor dinámico basado en mediana rolling.""" cfg = self.config['symbols'].get(symbol, {}) window = cfg.get('factor_window', 200) min_periods = cfg.get('min_periods', 100) range_col = df['High'] - df['Low'] factor = range_col.rolling(window=window, min_periods=min_periods).median().iloc[-1] # Cache self._factors[symbol] = factor return factor def update_factor_incremental(self, symbol: str, new_range: float, alpha: float = 0.02): """Actualización incremental usando EMA.""" current = self._factors.get(symbol) if current is None: self._factors[symbol] = new_range else: self._factors[symbol] = alpha * new_range + (1 - alpha) * current ``` --- ## 5. INTEGRACIÓN CON MODELOS EXISTENTES ### 5.1 AttentionWeightedModel (Base Class) ```python class AttentionWeightedModel(ABC): """ Clase base para modelos con pesos de atención dinámicos. Subclases: - AttentionWeightedXGBoost - AttentionWeightedTransformer """ def __init__(self, symbol: str, timeframe: str): self.symbol = symbol self.timeframe = timeframe self.factor_calculator = DynamicFactorCalculator() self.attention_config = VolatilityAttentionConfig() @abstractmethod def train(self, X, y, df_ohlcv: pd.DataFrame): """Entrena modelo con pesos de atención.""" pass @abstractmethod def predict(self, X, df_ohlcv: pd.DataFrame): """Predice con factor dinámico actual.""" pass def compute_sample_weights(self, df: pd.DataFrame) -> np.ndarray: """Calcula pesos de muestra basados en volatilidad.""" factor = self.factor_calculator.get_factor(self.symbol, df) multiplier = compute_move_multiplier(df, factor) weights = weight_smooth(multiplier, w_max=3.0, beta=4.0) # Normalizar a mean=1 valid_mask = ~np.isnan(weights) if weights[valid_mask].mean() > 0: weights[valid_mask] /= weights[valid_mask].mean() return weights ``` ### 5.2 Modificación de RangePredictor ```python # En range_predictor.py - train() class RangePredictor(AttentionWeightedModel): def train(self, X, y_high, y_low, df_ohlcv: pd.DataFrame): # Calcular pesos de atención sample_weights = self.compute_sample_weights(df_ohlcv) # Entrenar modelo HIGH con pesos self.model_high = XGBRegressor(**self.config) self.model_high.fit(X, y_high, sample_weight=sample_weights) # Entrenar modelo LOW con pesos self.model_low = XGBRegressor(**self.config) self.model_low.fit(X, y_low, sample_weight=sample_weights) ``` --- ## 6. SEPARACIÓN POR ACTIVO Y TEMPORALIDAD ### 6.1 Estructura de Modelos Entrenados ``` trained/ ├── XAUUSD/ │ ├── 5m/ │ │ ├── config.yaml # Hyperparámetros específicos │ │ ├── factor_stats.json # Estadísticas del factor │ │ ├── range_predictor.joblib │ │ ├── movement_predictor.joblib │ │ └── attention_weights.npz │ └── 15m/ │ └── ... ├── EURUSD/ │ ├── 5m/ │ └── 15m/ └── BTCUSDT/ ├── 5m/ └── 15m/ ``` ### 6.2 Config.yaml por Modelo ```yaml # trained/XAUUSD/5m/config.yaml symbol: XAUUSD timeframe: 5m prediction_horizon: 15m # 3 velas de 5m factor: computed_at: "2026-01-06T10:00:00Z" value: 4.85 window: 200 method: "rolling_median_shift1" attention: w_max: 3.0 beta: 4.0 use_smooth: true xgboost: n_estimators: 300 max_depth: 6 learning_rate: 0.03 training: samples: 50000 train_period: "2021-01-01 to 2025-12-31" validation_split: 0.2 metrics: mae_high: 2.15 mae_low: 1.98 r2_high: 0.72 r2_low: 0.75 ``` --- ## 7. API PARA MODELOS ### 7.1 Endpoint Unificado (Recomendado) ``` GET /api/ml/predictions/{symbol} ?timeframe=15m &include_models=range,movement,amd,attention Response: { "symbol": "XAUUSD", "timeframe": "15m", "timestamp": "2026-01-06T10:30:00Z", "current_price": 2655.50, "dynamic_factor": 4.85, "attention_weight": 1.8, "models": { "range_predictor": { "pred_high": 2658.5, "pred_low": 2652.0, "confidence": 0.72, "multiplier_high": 0.62, "multiplier_low": 0.72 }, "movement_predictor": { "high_usd": 8.5, "low_usd": 3.0, "asymmetry_ratio": 2.83, "direction": "LONG" }, "amd_detector": { "phase": "ACCUMULATION", "confidence": 0.68, "next_phase_prob": {"manipulation": 0.25, "distribution": 0.07} }, "attention_model": { "pred_high": 2659.0, "pred_low": 2651.5, "attention_score": 1.8 } }, "metamodel": { "direction": "LONG", "confidence": 0.75, "entry": 2655.50, "tp": 2658.7, "sl": 2651.8, "rr_ratio": 2.83, "reasoning": ["High asymmetry", "Accumulation phase", "Attention > 1.5"] } } ``` ### 7.2 Endpoint por Modelo Individual ``` GET /api/ml/models/{model_type}/{symbol} ?timeframe=15m # model_type: range | movement | amd | attention | ensemble ``` --- ## 8. FRONTEND: 2 PÁGINAS REQUERIDAS ### 8.1 Página "ML Realtime" (`/ml/realtime`) **Propósito**: Visualización en tiempo real de predicciones ML **Componentes**: 1. **Cards por Activo** (grid responsive) - Símbolo + precio actual - Factor dinámico actual - Attention weight visual (barra de color) - Dirección predicha (LONG/SHORT/NEUTRAL) - Niveles TP/SL - Confianza 2. **Filtros**: - Por símbolo (multi-select) - Por confianza mínima - Por attention weight mínimo - Solo señales activas 3. **Auto-refresh**: 30 segundos ### 8.2 Página "ML Historical" (`/ml/historical`) **Propósito**: Análisis de predicciones pasadas sin refresh constante **Componentes**: 1. **Date Range Picker** (inicio - fin) 2. **Selector de Símbolo** 3. **Selector de Modelo** (o todos) 4. **Tabla de Predicciones**: - Timestamp - Símbolo - Modelo - Predicción (High/Low) - Actual (High/Low) - Error (MAE) - Acierto (TP hit / SL hit / Neutral) 5. **Gráfico de Equity Curve** (basado en predicciones) 6. **Métricas Agregadas**: - Win Rate por modelo - MAE promedio - R² por período - Factor promedio usado --- ## 9. PLAN DE IMPLEMENTACIÓN ### Fase 1: Infraestructura (Prioridad ALTA) - [ ] Crear `symbols_config.yaml` - [ ] Implementar `DynamicFactorCalculator` - [ ] Crear `AttentionWeightedModel` base class - [ ] Tests unitarios ### Fase 2: Migración de Modelos - [ ] Refactorizar `RangePredictor` para usar factores dinámicos - [ ] Refactorizar `MovementMagnitudePredictor` - [ ] Refactorizar `EnhancedRangePredictor` - [ ] Tests de regresión ### Fase 3: Separación por Activo/Timeframe - [ ] Crear estructura de directorios - [ ] Script de migración de modelos existentes - [ ] Pipeline de entrenamiento por activo - [ ] Documentación ### Fase 4: API y Frontend - [ ] Endpoint `/api/ml/predictions/{symbol}` - [ ] Página MLRealtime - [ ] Página MLHistorical - [ ] Tests de integración --- ## 10. MÉTRICAS DE ÉXITO | Métrica | Objetivo | Cómo medir | |---------|----------|------------| | Win Rate (movimientos fuertes) | ≥ 80% | Backtesting con attention > 1.5 | | R:R Ratio promedio | ≥ 2:1 | Promedio de trades ejecutados | | Tiempo de adaptación del factor | < 24h | Correlación factor vs volatilidad real | | Latencia de predicción | < 100ms | API response time | | Cobertura de activos | 100% de activos configurados | Símbolos con modelo entrenado | --- ## 11. RIESGOS Y MITIGACIONES | Riesgo | Probabilidad | Impacto | Mitigación | |--------|--------------|---------|------------| | Factor dinámico lag | Media | Alto | Usar EMA para actualización incremental | | Overfitting por activo | Media | Medio | Cross-validation + walk-forward | | Pérdida de modelos | Baja | Alto | Versionado + backups | | Incompatibilidad APIs | Baja | Medio | Tests de contrato | --- *Documento generado: 2026-01-06* *Pendiente de revisión: Vuelta 2*