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>
12 KiB
| id | title | type | status | priority | epic | project | version | created_date | updated_date |
|---|---|---|---|---|---|---|---|---|---|
| RF-ML-004 | Pipeline de Entrenamiento | Requirement | Done | Alta | OQI-006 | trading-platform | 1.0.0 | 2025-12-05 | 2026-01-04 |
RF-ML-004: Pipeline de Entrenamiento
Versión: 1.0.0 Fecha: 2025-12-05 Épica: OQI-006 - Señales ML y Predicciones Prioridad: P1 Story Points: 8
Descripción
El sistema debe proporcionar un pipeline completo de entrenamiento de modelos XGBoost, incluyendo descarga de datos históricos, feature engineering, entrenamiento, validación y persistencia de modelos. El pipeline debe ser ejecutable bajo demanda y soportar entrenamiento programado.
Requisitos Funcionales
RF-ML-004.1: Descarga de Datos Históricos
El sistema debe:
- Descargar datos históricos de Binance API (velas de 5 minutos)
- Soportar cantidad configurable de samples (default: 500, máximo: 5000)
- Almacenar datos en formato OHLCV (Open, High, Low, Close, Volume)
- Validar integridad de datos (sin gaps temporales)
Configuración:
@dataclass
class TrainingConfig:
symbol: str # ej: "BTCUSDT"
samples: int = 500 # Número de velas históricas
interval: str = "5m" # Siempre 5 minutos
test_split: float = 0.2 # 20% para testing
validation_split: float = 0.1 # 10% para validación
RF-ML-004.2: Feature Engineering
El sistema debe:
- Calcular 30+ features técnicas (RF-ML-003)
- Eliminar filas con valores NaN/Inf
- Normalizar features si es necesario
- Crear features de rezagos (lags) si aplica
Features calculadas:
- Volatilidad: 8 features
- Momentum: 6 features
- Medias Móviles: 12 features
- Indicadores: 4 features (RSI, MACD, BB)
- Volumen: 1 feature
- High/Low: 6+ features
RF-ML-004.3: Generación de Targets
El sistema debe generar targets para cada horizonte:
# Para cada horizonte (scalping, intraday, swing, position)
horizons = {
'scalping': 6, # 30 min
'intraday': 18, # 90 min
'swing': 36, # 3 horas
'position': 72 # 6 horas
}
for horizon_name, n_candles in horizons.items():
# Calcular max/min futuro
future_high = max(high[i:i+n_candles])
future_low = min(low[i:i+n_candles])
# Calcular ratios
max_ratio = future_high / close[i] - 1
min_ratio = 1 - future_low / close[i]
# Asignar como target
y_high[i] = max_ratio
y_low[i] = min_ratio
RF-ML-004.4: División de Datos
El sistema debe dividir los datos en:
| Set | Porcentaje | Uso |
|---|---|---|
| Training | 70% | Entrenar el modelo |
| Validation | 10% | Ajustar hiperparámetros |
| Test | 20% | Evaluar performance final |
Importante: División temporal (no aleatoria) para evitar look-ahead bias.
# División temporal
total_samples = len(X)
train_end = int(total_samples * 0.7)
val_end = int(total_samples * 0.8)
X_train, y_train = X[:train_end], y[:train_end]
X_val, y_val = X[train_end:val_end], y[train_end:val_end]
X_test, y_test = X[val_end:], y[val_end:]
RF-ML-004.5: Entrenamiento XGBoost
El sistema debe entrenar dos modelos por horizonte:
- xgb_high: Predice
max_ratio - xgb_low: Predice
min_ratio
Configuración del modelo:
xgb_params = {
'objective': 'reg:squarederror',
'n_estimators': 100,
'max_depth': 6,
'learning_rate': 0.1,
'subsample': 0.8,
'colsample_bytree': 0.8,
'min_child_weight': 1,
'random_state': 42,
'n_jobs': -1 # Usar todos los cores
}
# Entrenar
model_high = XGBRegressor(**xgb_params)
model_high.fit(X_train, y_high_train)
model_low = XGBRegressor(**xgb_params)
model_low.fit(X_train, y_low_train)
RF-ML-004.6: Validación y Métricas
El sistema debe calcular métricas de performance:
Métricas principales:
- MAE (Mean Absolute Error): Error promedio absoluto
- RMSE (Root Mean Squared Error): Raíz del error cuadrático medio
- R² Score: Coeficiente de determinación
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
# Predecir en test set
y_pred_high = model_high.predict(X_test)
y_pred_low = model_low.predict(X_test)
# Calcular métricas
metrics = {
'high_mae': mean_absolute_error(y_test_high, y_pred_high),
'high_rmse': np.sqrt(mean_squared_error(y_test_high, y_pred_high)),
'high_r2': r2_score(y_test_high, y_pred_high),
'low_mae': mean_absolute_error(y_test_low, y_pred_low),
'low_rmse': np.sqrt(mean_squared_error(y_test_low, y_pred_low)),
'low_r2': r2_score(y_test_low, y_pred_low),
'train_samples': len(X_train),
'test_samples': len(X_test)
}
Umbrales de aceptación:
| Métrica | Umbral | Significado |
|---|---|---|
| high_mae | < 0.02 | Error < 2% |
| high_rmse | < 0.025 | RMSE < 2.5% |
| low_mae | < 0.02 | Error < 2% |
| low_rmse | < 0.03 | RMSE < 3% |
RF-ML-004.7: Persistencia de Modelos
El sistema debe:
- Guardar modelos entrenados en formato JSON (XGBoost nativo)
- Incluir metadata (símbolo, fecha, métricas, versión)
- Versionado de modelos
Estructura de archivos:
apps/ml-services/trained_models/
├── BTCUSDT/
│ ├── scalping/
│ │ ├── xgb_high_v1.0.0.json
│ │ ├── xgb_low_v1.0.0.json
│ │ └── metadata.json
│ ├── intraday/
│ ├── swing/
│ └── position/
└── ETHUSDT/
└── ...
Metadata.json:
{
"model_version": "1.0.0",
"symbol": "BTCUSDT",
"horizon": "scalping",
"trained_at": "2025-12-05T18:45:00.000Z",
"samples": 500,
"train_samples": 350,
"test_samples": 100,
"metrics": {
"high_mae": 0.00099,
"high_rmse": 0.00141,
"low_mae": 0.00173,
"low_rmse": 0.00284
},
"features": ["volatility_5", "rsi_14", ...],
"xgb_params": { ... }
}
RF-ML-004.8: Entrenamiento Programado
El sistema debe soportar:
- Entrenamiento manual vía API:
POST /api/train/{symbol} - Entrenamiento programado (cron job): semanal, cada domingo 2:00 AM
- Re-entrenamiento automático si MAE > umbral (degradación del modelo)
Datos de Entrada
| Campo | Tipo | Descripción | Requerido |
|---|---|---|---|
| symbol | string | Par de trading | Sí |
| samples | number | Cantidad de velas históricas | No (default: 500) |
| horizon | enum | Horizonte específico o "all" | No (default: "all") |
Datos de Salida
Inicio de Entrenamiento
interface TrainingStartResponse {
status: 'training_started' | 'already_training';
symbol: string;
samples: number;
horizon: string;
message: string;
}
Ejemplo:
{
"status": "training_started",
"symbol": "BTCUSDT",
"samples": 500,
"horizon": "all",
"message": "Model training started in background. Check /api/training/status for progress."
}
Estado de Entrenamiento
interface TrainingStatus {
training_in_progress: boolean;
is_trained: boolean;
current_symbol?: string;
progress_pct?: number;
last_training?: {
symbol: string;
timestamp: string;
samples: number;
metrics: {
high_mae: number;
high_rmse: number;
low_mae: number;
low_rmse: number;
train_samples: number;
test_samples: number;
};
};
}
Ejemplo:
{
"training_in_progress": false,
"is_trained": true,
"last_training": {
"symbol": "BTCUSDT",
"timestamp": "2025-12-05T18:45:23.123456Z",
"samples": 500,
"metrics": {
"high_mae": 0.00099,
"high_rmse": 0.00141,
"low_mae": 0.00173,
"low_rmse": 0.00284,
"train_samples": 355,
"test_samples": 89
}
}
}
Reglas de Negocio
- Un Entrenamiento a la Vez: Solo puede haber un proceso de entrenamiento activo
- Mínimo de Samples: Al menos 100 samples para entrenar
- Máximo de Samples: Máximo 5000 samples (limitación de API Binance)
- Sobrescritura: Un nuevo entrenamiento sobrescribe el modelo anterior
- Validación Pre-entrenamiento: Verificar disponibilidad de datos antes de iniciar
- Timeout: El entrenamiento tiene timeout de 30 minutos
Criterios de Aceptación
Escenario: Iniciar entrenamiento manual
DADO que el usuario es administrador
CUANDO hace POST /api/train/BTCUSDT?samples=500
ENTONCES el entrenamiento inicia en background
Y recibe status "training_started"
Y puede consultar el progreso en /api/training/status
Escenario: Entrenamiento completo exitoso
DADO que el entrenamiento ha finalizado
CUANDO consulta /api/training/status
ENTONCES training_in_progress = false
Y is_trained = true
Y last_training contiene métricas
Y high_mae < 0.02 y low_mae < 0.02
Escenario: Entrenamiento con datos insuficientes
DADO que solo existen 50 velas históricas
CUANDO intenta entrenar
ENTONCES recibe error 400 Bad Request
Y el mensaje indica "Insufficient data"
Escenario: Modelo entrenado está disponible
DADO que el entrenamiento finalizó exitosamente
CUANDO hace GET /api/predict/BTCUSDT
ENTONCES usa el modelo recién entrenado
Y is_trained = true en la respuesta
Dependencias
Técnicas:
- XGBoost 2.0+: Motor de ML
- scikit-learn: Métricas y validación
- Pandas/NumPy: Procesamiento de datos
- Binance API: Datos históricos
- Celery (opcional): Background tasks
Funcionales:
- RF-ML-003: Indicadores técnicos (features)
- Requiere infraestructura de almacenamiento para modelos
Notas Técnicas
Pipeline Completo
# apps/ml-services/src/models/training_pipeline.py
class TrainingPipeline:
"""
Pipeline completo de entrenamiento
"""
async def train(self, symbol: str, samples: int = 500) -> dict:
# 1. Descargar datos
logger.info(f"Downloading {samples} candles for {symbol}")
ohlcv = await market_data.fetch_ohlcv(symbol, limit=samples)
# 2. Calcular features
logger.info("Calculating technical indicators")
features = TechnicalIndicators.calculate_all(ohlcv)
# 3. Generar targets para cada horizonte
logger.info("Generating targets")
targets = self._generate_targets(ohlcv)
# 4. Entrenar modelos para cada horizonte
results = {}
for horizon in ['scalping', 'intraday', 'swing', 'position']:
logger.info(f"Training {horizon} models")
# Dividir datos
X_train, X_test, y_train, y_test = self._split_data(
features, targets[horizon]
)
# Entrenar
model_high = XGBRegressor(**xgb_params)
model_low = XGBRegressor(**xgb_params)
model_high.fit(X_train, y_train['high'])
model_low.fit(X_train, y_train['low'])
# Validar
metrics = self._calculate_metrics(
model_high, model_low, X_test, y_test
)
# Guardar
self._save_models(symbol, horizon, model_high, model_low, metrics)
results[horizon] = metrics
return results
Performance:
- Entrenamiento completo (4 horizontes): ~2-5 minutos con 500 samples
- Download de datos: ~10 segundos
- Feature engineering: ~5 segundos
- Entrenamiento XGBoost: ~30 segundos por horizonte
Referencias
Creado por: Requirements-Analyst Fecha: 2025-12-05 Última actualización: 2025-12-05