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>
14 KiB
14 KiB
| title | version | date | status | author | epic | tags | priority | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Especificación Técnica: Sistema de Atención con Factores Dinámicos | 1.0.0 | 2026-01-06 | Draft | ML-Specialist + Orquestador | OQI-006 |
|
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
# 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
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):
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
# 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
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)
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
# 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
# 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:
-
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
-
Filtros:
- Por símbolo (multi-select)
- Por confianza mínima
- Por attention weight mínimo
- Solo señales activas
-
Auto-refresh: 30 segundos
8.2 Página "ML Historical" (/ml/historical)
Propósito: Análisis de predicciones pasadas sin refresh constante
Componentes:
-
Date Range Picker (inicio - fin)
-
Selector de Símbolo
-
Selector de Modelo (o todos)
-
Tabla de Predicciones:
- Timestamp
- Símbolo
- Modelo
- Predicción (High/Low)
- Actual (High/Low)
- Error (MAE)
- Acierto (TP hit / SL hit / Neutral)
-
Gráfico de Equity Curve (basado en predicciones)
-
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
AttentionWeightedModelbase class - Tests unitarios
Fase 2: Migración de Modelos
- Refactorizar
RangePredictorpara 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