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>
20 KiB
| title | version | date | status | author | epic | tags | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Análisis de Modelos ML - Vuelta 2 | 1.0.0 | 2026-01-06 | In Progress | ML-Specialist + Orquestador | OQI-006 |
|
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
symbols:
primary: ["XAUUSD", "EURUSD", "GBPUSD", "BTCUSD"]
secondary: ["USDJPY", "GBPJPY", "AUDUSD", "NZDUSD"]
timeframes:
primary: 5
aggregations: [15, 30, 60, 240]
Archivo: config/models.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
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:
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:
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
# 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
# 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
# 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
# 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:
- Corto plazo: XGBoost con attention via sample_weight (ya funciona)
- Mediano plazo: Agregar GRU como modelo secundario
- 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:
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
# 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:
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
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
SYMBOLSderange_predictor_factor.pyaSYMBOL_CONFIGS - Crear
config/symbols.yamlcentralizado - Actualizar todos los modelos para usar config unificada
Fase 2.2: Integración de Trainer
- Modificar
prediction_service.pypara usarSymbolTimeframeTrainer - Cargar modelos desde
models/ml_first/{symbol}/{timeframe}/ - Agregar fallback a legacy models durante transición
Fase 2.3: Optimización de Filtros
- Implementar
DirectionalFiltersensignal_generator.py - Ajustar thresholds según backtests
- Agregar SHORT bias para XAUUSD
Fase 2.4: Ensemble Mejorado
- Actualizar pesos de
StrategyEnsemblebasado 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
- Usar filtros SHORT en XAUUSD: +10% win rate estimado
- Integrar DynamicFactorWeighter: Mejor selección de señales
- Cargar modelos separados: Predicciones más precisas por símbolo
- Ajustar confidence threshold a 0.7: Menos trades, mejor calidad
11. PRÓXIMOS PASOS
Vuelta 2 - FASE 2: Análisis Profundo de Arquitectura
- Revisar implementación de
VolatilityBiasedSelfAttention - Analizar métricas de modelos entrenados existentes
- Benchmark XGBoost vs Transformer en datos reales
Vuelta 2 - FASE 3: Retroalimentación
- Documentar decisiones arquitectónicas
- Proponer refactoring mínimo viable
- 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
from ..models.range_predictor import RangePredictor # ← Legacy
self._range_predictor = RangePredictor() # ← No usa modelos entrenados
Debería ser:
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:
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
# 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_factorde 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