Changes include: - Updated architecture documentation - Enhanced module definitions (OQI-001 to OQI-008) - ML integration documentation updates - Trading strategies documentation - Orchestration and inventory updates - Docker configuration updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
467 lines
9.8 KiB
Markdown
467 lines
9.8 KiB
Markdown
---
|
|
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
|