--- id: "RF-ML-003" title: "Indicadores Tecnicos del ML" type: "Requirement" status: "Done" priority: "Alta" epic: "OQI-006" project: "trading-platform" version: "1.0.0" created_date: "2025-12-05" updated_date: "2026-01-04" --- # RF-ML-003: Indicadores Técnicos del ML **Versión:** 1.0.0 **Fecha:** 2025-12-05 **Épica:** OQI-006 - Señales ML y Predicciones **Prioridad:** P1 **Story Points:** 5 --- ## Descripción El sistema debe calcular y proporcionar indicadores técnicos avanzados utilizados como features del modelo ML. Estos indicadores deben estar disponibles tanto para el entrenamiento del modelo como para visualización en la interfaz de usuario. --- ## Requisitos Funcionales ### RF-ML-003.1: Indicadores de Volatilidad El sistema debe calcular: **Volatilidad Estándar:** ```python volatility_N = std(returns[-N:]) Períodos: 5, 10, 20, 50 ``` **Average True Range (ATR):** ```python true_range = max( high - low, abs(high - close_prev), abs(low - close_prev) ) atr_N = sma(true_range, N) Períodos: 5, 10, 20, 50 ``` **Bollinger Bands Position:** ```python bb_middle = sma(close, 20) bb_std = std(close, 20) bb_upper = bb_middle + (2 * bb_std) bb_lower = bb_middle - (2 * bb_std) bb_position = (close - bb_lower) / (bb_upper - bb_lower) // Rango: 0 (en banda inferior) a 1 (en banda superior) ``` ### RF-ML-003.2: Indicadores de Momentum El sistema debe calcular: **Momentum Simple:** ```python momentum_N = (close / close[-N]) - 1 Períodos: 5, 10, 20 ``` **Rate of Change (ROC):** ```python roc_N = ((close - close[-N]) / close[-N]) * 100 Períodos: 5, 10, 20 ``` **Relative Strength Index (RSI):** ```python # RSI de 14 períodos gains = max(0, close - close_prev) losses = max(0, close_prev - close) avg_gain = ema(gains, 14) avg_loss = ema(losses, 14) rs = avg_gain / avg_loss rsi_14 = 100 - (100 / (1 + rs)) // Rango: 0 (sobreventa extrema) a 100 (sobrecompra extrema) // Zonas: < 30 sobreventa, > 70 sobrecompra ``` ### RF-ML-003.3: Medias Móviles El sistema debe calcular: **Simple Moving Average (SMA):** ```python sma_N = mean(close[-N:]) Períodos: 5, 10, 20, 50 ``` **Exponential Moving Average (EMA):** ```python multiplier = 2 / (N + 1) ema_N = (close * multiplier) + (ema_prev * (1 - multiplier)) Períodos: 5, 10, 20, 50 ``` **SMA Ratios:** ```python sma_ratio_N = close / sma_N - 1 // Indica distancia del precio actual respecto a la SMA // Positivo: precio sobre SMA (alcista) // Negativo: precio bajo SMA (bajista) Períodos: 5, 10, 20, 50 ``` ### RF-ML-003.4: MACD (Moving Average Convergence Divergence) El sistema debe calcular: ```python # MACD estándar (12, 26, 9) ema_12 = ema(close, 12) ema_26 = ema(close, 26) macd_line = ema_12 - ema_26 macd_signal = ema(macd_line, 9) macd_histogram = macd_line - macd_signal // Interpretación: // macd > signal: momento alcista // macd < signal: momento bajista // histogram > 0: aceleración alcista // histogram < 0: aceleración bajista ``` ### RF-ML-003.5: Indicadores de Volumen El sistema debe calcular: **Volume Ratio:** ```python volume_sma = sma(volume, 20) volume_ratio = volume / volume_sma // Rango típico: 0.5 - 2.0 // > 1.5: volumen alto (confirmación de movimiento) // < 0.7: volumen bajo (falta de interés) ``` ### RF-ML-003.6: Indicadores de High/Low El sistema debe calcular: **High-Low Range Percentage:** ```python hl_range_pct = (high - low) / close * 100 // Mide la volatilidad intravela ``` **Distance to High/Low:** ```python high_distance = (high - close) / high low_distance = (close - low) / low // Indica posición del cierre dentro de la vela ``` **Historical Max/Min Ratios:** ```python hist_max_N = max(high[-N:]) hist_min_N = min(low[-N:]) hist_max_ratio_N = close / hist_max_N - 1 hist_min_ratio_N = close / hist_min_N - 1 Períodos: 10, 20, 50, 100 ``` --- ## Datos de Entrada | Campo | Tipo | Descripción | Requerido | |-------|------|-------------|-----------| | symbol | string | Par de trading | Sí | | limit | number | Número de velas históricas | No (default: 100) | --- ## Datos de Salida ```typescript interface TechnicalIndicators { symbol: string; timestamp: string; price: number; // Volatilidad (9 indicators) volatility: { vol_5: number; vol_10: number; vol_20: number; vol_50: number; atr_5: number; atr_10: number; atr_20: number; atr_50: number; bb_position: number; // 0-1 }; // Momentum (7 indicators) momentum: { mom_5: number; mom_10: number; mom_20: number; roc_5: number; roc_10: number; roc_20: number; rsi_14: number; // 0-100 }; // Medias Móviles (12 indicators) moving_averages: { sma_5: number; sma_10: number; sma_20: number; sma_50: number; ema_5: number; ema_10: number; ema_20: number; ema_50: number; sma_ratio_5: number; sma_ratio_10: number; sma_ratio_20: number; sma_ratio_50: number; }; // MACD (3 indicators) macd: { macd_line: number; macd_signal: number; macd_histogram: number; }; // Volumen (1 indicator) volume: { volume_ratio: number; }; // High/Low (7 indicators) high_low: { hl_range_pct: number; high_distance: number; low_distance: number; hist_max_ratio_10: number; hist_max_ratio_20: number; hist_min_ratio_10: number; hist_min_ratio_20: number; }; } ``` **Ejemplo:** ```json { "symbol": "BTCUSDT", "timestamp": "2025-12-05T18:35:00.000Z", "price": 89450.00, "volatility": { "vol_5": 0.0012, "vol_10": 0.0015, "vol_20": 0.0018, "vol_50": 0.0022, "atr_5": 250.5, "atr_10": 280.3, "atr_20": 310.8, "atr_50": 345.2, "bb_position": 0.65 }, "momentum": { "mom_5": 0.0025, "mom_10": 0.0045, "mom_20": 0.0082, "roc_5": 0.25, "roc_10": 0.45, "roc_20": 0.82, "rsi_14": 58.3 }, "moving_averages": { "sma_5": 89380.5, "sma_10": 89320.2, "sma_20": 89250.8, "sma_50": 89100.4, "ema_5": 89400.1, "ema_10": 89350.6, "ema_20": 89280.3, "ema_50": 89150.7, "sma_ratio_5": 0.0008, "sma_ratio_10": 0.0014, "sma_ratio_20": 0.0022, "sma_ratio_50": 0.0039 }, "macd": { "macd_line": 45.2, "macd_signal": 38.7, "macd_histogram": 6.5 }, "volume": { "volume_ratio": 1.25 }, "high_low": { "hl_range_pct": 0.35, "high_distance": 0.0002, "low_distance": 0.0032, "hist_max_ratio_10": -0.0015, "hist_max_ratio_20": -0.0028, "hist_min_ratio_10": 0.0042, "hist_min_ratio_20": 0.0078 } } ``` --- ## Reglas de Negocio 1. **Datos Mínimos:** Requiere al menos 100 velas para indicadores de período 50 2. **Actualización:** Los indicadores se recalculan cada 5 minutos (nueva vela) 3. **Caché:** Resultados se cachean por 1 minuto 4. **Precisión:** Cálculos con precisión de 8 decimales 5. **NaN Handling:** Valores NaN/Inf se reemplazan con 0 o último valor válido --- ## Criterios de Aceptación ```gherkin Escenario: Calcular indicadores para BTCUSDT DADO que existen al menos 100 velas históricas CUANDO hace GET /api/indicators/BTCUSDT ENTONCES recibe 39 indicadores técnicos Y todos los valores son números válidos (no NaN) Y rsi_14 está entre 0 y 100 Y bb_position está entre 0 y 1 Escenario: Indicadores en tiempo real vía WebSocket DADO que el usuario está conectado a /ws/BTCUSDT CUANDO se cierra una nueva vela de 5 minutos ENTONCES recibe indicadores actualizados Y el timestamp coincide con el cierre de la vela Escenario: Datos insuficientes DADO que solo existen 30 velas históricas CUANDO solicita indicadores ENTONCES recibe error 400 Bad Request Y el mensaje indica "Insufficient data: need 100+ candles" ``` --- ## Dependencias ### Técnicas: - **NumPy:** Cálculos vectorizados - **Pandas:** Series de tiempo - **TA-Lib (opcional):** Librería de indicadores técnicos - **Binance API:** Datos OHLCV ### Funcionales: - Usado por RF-ML-001 (como features del modelo) - Usado por RF-ML-002 (para generar señales) - Usado por RF-TRD-002 (visualización en charts) --- ## Notas Técnicas ### Implementación - Feature Calculator ```python # apps/ml-services/src/models/indicators.py import numpy as np import pandas as pd class TechnicalIndicators: """ Calcula indicadores técnicos para ML features """ @staticmethod def calculate_all(ohlcv: pd.DataFrame) -> dict: """ Calcula todos los indicadores Args: ohlcv: DataFrame con columnas [timestamp, open, high, low, close, volume] Returns: dict con 39 indicadores """ close = ohlcv['close'].values high = ohlcv['high'].values low = ohlcv['low'].values volume = ohlcv['volume'].values indicators = {} # Volatilidad (9) indicators.update(calculate_volatility(close, high, low)) # Momentum (7) indicators.update(calculate_momentum(close)) # Medias Móviles (12) indicators.update(calculate_moving_averages(close)) # MACD (3) indicators.update(calculate_macd(close)) # Volumen (1) indicators.update(calculate_volume(volume)) # High/Low (7) indicators.update(calculate_high_low(close, high, low)) return indicators ``` ### Optimizaciones: - Uso de NumPy vectorizado (100x más rápido que loops) - Cálculo incremental para nuevas velas - Caché de medias móviles intermedias ### Performance: - Cálculo de 39 indicadores: < 10ms para 500 velas - Memoria: ~50KB por símbolo en caché --- ## Referencias - [TA-Lib Documentation](https://ta-lib.org/) - [Investopedia - Technical Indicators](https://www.investopedia.com/terms/t/technicalindicator.asp) - [NumPy Performance Tips](https://numpy.org/doc/stable/user/c-info.html) --- **Creado por:** Requirements-Analyst **Fecha:** 2025-12-05 **Última actualización:** 2025-12-05