trading-platform/docs/01-arquitectura/INTEGRACION-LLM-FINE-TUNING.md
rckrdmrd c1b5081208 feat(ml): Complete FASE 11 - BTCUSD update and comprehensive documentation alignment
ML Engine Updates:
- Updated BTCUSD with Polygon API data (2024-2025): 215,699 new records
- Re-trained all ML models: Attention (R²: 0.223), Base, Metamodel (87.3% confidence)
- Backtest results: +176.71R profit with aggressive_filter strategy

Documentation Consolidation:
- Created docs/99-analisis/_MAP.md index with 13 new analysis documents
- Consolidated inventories: removed duplicates from orchestration/inventarios/
- Updated ML_INVENTORY.yml with BTCUSD metrics and training results
- Added execution reports: FASE11-BTCUSD, correction issues, alignment validation

Architecture & Integration:
- Updated all module documentation with NEXUS v3.4 frontmatter
- Fixed _MAP.md indexes across all folders
- Updated orchestration plans and traces

Files: 229 changed, 5064 insertions(+), 1872 deletions(-)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 09:31:29 -06:00

60 KiB

id title type project version created_date updated_date author
INTEGRACION-LLM-FINE-TUNING Integracion LLM con Fine-Tuning para Trading Agent Documentation trading-platform 1.0.0 2026-01-04 2026-01-04 Orquestador Agent - Trading Platform

Integracion LLM con Fine-Tuning para Trading Agent

Version: 1.0.0 Fecha: 2026-01-04 Modulo: OQI-010-llm-trading-integration Hardware: GPU NVIDIA 16GB VRAM


Tabla de Contenidos

  1. Vision General
  2. Arquitectura Unificada
  3. Modelo LLM y Fine-Tuning
  4. Integracion MCP Servers
  5. Gestion de Riesgo
  6. Analisis de Predicciones ML
  7. API para Frontend
  8. Persistencia en PostgreSQL
  9. Pipeline de Fine-Tuning
  10. Implementacion
  11. Testing y Validacion

Vision General

Objetivo

Crear un agente LLM inteligente que funcione como cerebro del sistema de trading, capaz de:

  1. Analizar y explicar predicciones de los modelos ML (AMD, ICT/SMC, Range Predictor)
  2. Gestionar riesgo de forma autonoma con reglas configurables
  3. Orquestar operaciones via MCP servers (MT4 y Binance)
  4. Tomar decisiones basadas en confluencia de senales
  5. Aprender y adaptarse mediante fine-tuning con datos de estrategias

Flujo de Alto Nivel

┌─────────────────────────────────────────────────────────────────────────────────┐
│                         LLM TRADING AGENT                                        │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                  │
│  ┌───────────────────────────────────────────────────────────────────────────┐  │
│  │                        LAYER 1: INPUT SOURCES                              │  │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐      │  │
│  │  │  ML Engine  │  │  Market     │  │  User       │  │  Portfolio  │      │  │
│  │  │  Signals    │  │  Data       │  │  Commands   │  │  State      │      │  │
│  │  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘      │  │
│  └─────────┼────────────────┼────────────────┼────────────────┼──────────────┘  │
│            │                │                │                │                  │
│            ▼                ▼                ▼                ▼                  │
│  ┌───────────────────────────────────────────────────────────────────────────┐  │
│  │                        LAYER 2: LLM CORE (Fine-Tuned)                      │  │
│  │  ┌─────────────────────────────────────────────────────────────────────┐  │  │
│  │  │                   chatgpt-oss / Llama 3 8B                          │  │  │
│  │  │                   Fine-tuned with Trading Strategies                │  │  │
│  │  │                   16GB VRAM Local GPU                               │  │  │
│  │  └──────────────────────────────┬──────────────────────────────────────┘  │  │
│  │                                 │                                          │  │
│  │  ┌──────────────────────────────▼──────────────────────────────────────┐  │  │
│  │  │                    REASONING ENGINE                                  │  │  │
│  │  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐                 │  │  │
│  │  │  │ AMD/ICT     │  │ Risk        │  │ Decision    │                 │  │  │
│  │  │  │ Analysis    │  │ Assessment  │  │ Making      │                 │  │  │
│  │  │  └─────────────┘  └─────────────┘  └─────────────┘                 │  │  │
│  │  └─────────────────────────────────────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────────────────────────────┘  │
│                                      │                                           │
│                                      ▼                                           │
│  ┌───────────────────────────────────────────────────────────────────────────┐  │
│  │                        LAYER 3: ACTION LAYER                              │  │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐      │  │
│  │  │MCP MT4      │  │MCP Binance  │  │  Risk       │  │  Alert      │      │  │
│  │  │Connector    │  │Connector    │  │  Manager    │  │  System     │      │  │
│  │  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘      │  │
│  └─────────┼────────────────┼────────────────┼────────────────┼──────────────┘  │
│            │                │                │                │                  │
│            ▼                ▼                ▼                ▼                  │
│  ┌───────────────────────────────────────────────────────────────────────────┐  │
│  │                        LAYER 4: PERSISTENCE                               │  │
│  │  ┌─────────────────────────────────────────────────────────────────────┐  │  │
│  │  │                    PostgreSQL (Schema: llm + ml)                     │  │  │
│  │  │  - Predictions    - Decisions    - Risk Events    - Trade History   │  │  │
│  │  └─────────────────────────────────────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────────────────────────────┘  │
│                                                                                  │
└─────────────────────────────────────────────────────────────────────────────────┘

Arquitectura Unificada

Componentes del Sistema

┌──────────────────────────────────────────────────────────────────────────────┐
│                         COMPONENTES PRINCIPALES                               │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│  1. LLM SERVICE (apps/llm-agent/)                                            │
│     ├── Core LLM Engine (chatgpt-oss fine-tuned)                             │
│     ├── Trading Tools (12+ herramientas)                                     │
│     ├── Context Manager (Redis)                                              │
│     ├── Risk Assessment Module (NUEVO)                                       │
│     └── MCP Orchestrator (NUEVO)                                             │
│                                                                               │
│  2. MCP SERVERS                                                              │
│     ├── mcp-mt4-connector/ (existente)                                       │
│     │   └── 6 tools: account, positions, quotes, trading                     │
│     └── mcp-binance-connector/ (NUEVO)                                       │
│         └── 8 tools: market, account, orders, positions                      │
│                                                                               │
│  3. ML ENGINE (apps/ml-engine/)                                              │
│     ├── AMD Detector                                                         │
│     ├── Range Predictor                                                      │
│     ├── Signal Generator                                                     │
│     ├── ICT/SMC Detector                                                     │
│     └── Predictions API                                                      │
│                                                                               │
│  4. RISK MANAGEMENT SERVICE (NUEVO)                                          │
│     ├── Position Sizing Calculator                                           │
│     ├── Drawdown Monitor                                                     │
│     ├── Exposure Tracker                                                     │
│     └── Circuit Breaker                                                      │
│                                                                               │
│  5. PERSISTENCE LAYER                                                        │
│     ├── PostgreSQL (predictions, decisions, risk_events)                     │
│     └── Redis (context, sessions, cache)                                     │
│                                                                               │
└──────────────────────────────────────────────────────────────────────────────┘

Puertos y Servicios

Servicio Puerto Descripcion
Frontend 3080 React SPA
Backend API 3081 Express.js REST
WebSocket 3082 Real-time updates
ML Engine 3083 FastAPI predictions
Data Service 3084 Market data
LLM Agent 3085 Trading copilot
Trading Agents 3086 Atlas, Orion, Nova
MCP MT4 3605 MT4 connector
MCP Binance 3606 Binance connector (NUEVO)
Ollama 11434 LLM inference
PostgreSQL 5432 Database
Redis 6379 Cache

Modelo LLM y Fine-Tuning

Modelo Base Recomendado

Para 16GB de VRAM, las opciones son:

Modelo VRAM Context Ventajas Fine-Tuning
Llama 3 8B 10GB 8K Balance calidad/velocidad LoRA
Mistral 7B 8GB 8K Rapido, buena calidad LoRA
Qwen2 7B 9GB 32K Largo contexto LoRA
Phi-3 Medium 7GB 4K Eficiente, razonamiento LoRA

Recomendacion: Llama 3 8B con quantizacion Q5_K_M para fine-tuning con LoRA.

Dataset de Fine-Tuning

El fine-tuning se realizara con definiciones de estrategias de trading:

# Estructura del dataset de fine-tuning
dataset:
  name: trading_data_strategies_v1
  format: JSONL

  categories:
    - amd_phases:
        description: "Fases Accumulation-Manipulation-Distribution"
        samples: 5000+

    - ict_concepts:
        description: "Conceptos ICT/SMC (OTE, FVG, BOS, CHOCH)"
        samples: 3000+

    - risk_management:
        description: "Reglas de gestion de riesgo"
        samples: 2000+

    - trade_decisions:
        description: "Decisiones de trading con razonamiento"
        samples: 10000+

    - market_analysis:
        description: "Analisis de mercado y contexto"
        samples: 5000+

Formato del Dataset

{"instruction": "Analiza esta senal de trading y determina si es una buena entrada", "input": "Simbolo: XAUUSD, Fase AMD: Accumulation (78% confianza), Killzone: London Open, RSI: 42, MACD: positivo, Precio cerca de soporte", "output": "ANALISIS:\n\n1. FASE AMD: Accumulation (78%)\n   - Las instituciones estan acumulando posiciones largas\n   - Esta es una fase FAVORABLE para entradas LONG\n\n2. KILLZONE: London Open\n   - Alta liquidez y probabilidad de movimiento direccional\n   - Horario optimo para entradas\n\n3. INDICADORES:\n   - RSI 42: Zona neutral, no sobrecomprado\n   - MACD positivo: Momentum alcista\n\n4. ESTRUCTURA:\n   - Precio cerca de soporte: Punto de entrada con riesgo definido\n\nDECISION: LONG recomendado\n- Entry: Nivel de soporte actual\n- Stop Loss: 1-2% debajo del soporte\n- Take Profit: Siguiente resistencia (R:R minimo 1:2)\n\nRIESGO: 2% maximo del capital"}
{"instruction": "Que accion tomar cuando el mercado esta en fase de Manipulation?", "input": "Fase AMD actual: Manipulation (85%), precio haciendo spike hacia arriba, volume bajo", "output": "ALERTA: FASE DE MANIPULACION DETECTADA\n\n1. ACCION RECOMENDADA: NO OPERAR / ESPERAR\n\n2. RAZONAMIENTO:\n   - La fase de Manipulation (85% confianza) indica caza de stops\n   - Los spikes con bajo volumen son senales clasicas de manipulacion\n   - Las instituciones estan barriendo liquidez antes del movimiento real\n\n3. QUE ESPERAR:\n   - Transicion a fase de Distribution o Re-accumulation\n   - Confirmacion de direccion con volumen\n   - Nueva estructura de mercado clara\n\n4. REGLA DE RIESGO:\n   - NUNCA entrar durante Manipulation\n   - Si hay posiciones abiertas: ajustar stops, considerar cierre parcial\n\nESTADO: STANDBY - Esperar siguiente fase"}

Proceso de Fine-Tuning

# fine_tuning/train_lora.py

from datasets import load_dataset
from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM, AutoTokenizer, Trainer

# Configuracion LoRA para 16GB VRAM
lora_config = LoraConfig(
    r=16,                          # Rank de LoRA
    lora_alpha=32,                 # Scaling factor
    target_modules=[
        "q_proj", "k_proj", "v_proj",
        "o_proj", "gate_proj", "up_proj", "down_proj"
    ],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

# Training config optimizado para GPU 16GB
training_args = TrainingArguments(
    output_dir="./trading-llm-finetuned",
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    num_train_epochs=3,
    learning_rate=2e-4,
    fp16=True,                     # Mixed precision para ahorrar VRAM
    logging_steps=10,
    save_strategy="epoch",
    evaluation_strategy="epoch",
    warmup_ratio=0.1,
    optim="adamw_8bit",            # Optimizer de 8 bits
    max_grad_norm=0.3,
)

# Metricas de evaluacion
def compute_metrics(eval_preds):
    # Evaluar calidad de decisiones de trading
    pass

Integracion MCP Servers

MCP MT4 Connector (Existente)

Ya implementado en apps/mcp-mt4-connector/:

// Tools disponibles
const MT4_TOOLS = [
  "mt4_get_account",      // Balance, equity, margin
  "mt4_get_positions",    // Posiciones abiertas
  "mt4_get_quote",        // Precio bid/ask
  "mt4_execute_trade",    // Ejecutar orden
  "mt4_close_position",   // Cerrar posicion
  "mt4_modify_position"   // Modificar SL/TP
];

MCP Binance Connector (NUEVO)

Nueva app a crear en apps/mcp-binance-connector/:

mcp-binance-connector/
├── src/
│   ├── index.ts              # Entry point
│   ├── config.ts             # Configuration
│   ├── services/
│   │   └── binance-client.ts # CCXT wrapper
│   └── tools/
│       ├── index.ts          # Tool registry
│       ├── market.ts         # Market data tools
│       ├── account.ts        # Account info
│       ├── orders.ts         # Order management
│       └── positions.ts      # Position tracking
├── docs/
│   ├── ARCHITECTURE.md
│   └── MCP-TOOLS-SPEC.md
├── package.json
├── tsconfig.json
└── README.md

Tools del MCP Binance

// tools/index.ts

export const BINANCE_TOOLS = [
  // Market Data (Read-only, bajo riesgo)
  {
    name: "binance_get_ticker",
    description: "Obtiene precio actual de un simbolo",
    parameters: {
      symbol: { type: "string", required: true }
    },
    risk: "low"
  },
  {
    name: "binance_get_orderbook",
    description: "Obtiene order book con profundidad",
    parameters: {
      symbol: { type: "string", required: true },
      limit: { type: "number", default: 20 }
    },
    risk: "low"
  },
  {
    name: "binance_get_klines",
    description: "Obtiene velas historicas",
    parameters: {
      symbol: { type: "string", required: true },
      interval: { type: "string", enum: ["1m","5m","15m","1h","4h","1d"] },
      limit: { type: "number", default: 100 }
    },
    risk: "low"
  },

  // Account Info (Read-only, bajo riesgo)
  {
    name: "binance_get_account",
    description: "Obtiene balance y estado de cuenta",
    parameters: {},
    risk: "low"
  },
  {
    name: "binance_get_positions",
    description: "Obtiene posiciones abiertas (futures)",
    parameters: {},
    risk: "low"
  },

  // Order Management (Alto riesgo, requiere confirmacion)
  {
    name: "binance_create_order",
    description: "Crea orden de mercado o limite",
    parameters: {
      symbol: { type: "string", required: true },
      side: { type: "string", enum: ["BUY", "SELL"], required: true },
      type: { type: "string", enum: ["MARKET", "LIMIT", "STOP_MARKET"] },
      quantity: { type: "number", required: true },
      price: { type: "number" },
      stopPrice: { type: "number" }
    },
    risk: "high",
    requiresConfirmation: true
  },
  {
    name: "binance_cancel_order",
    description: "Cancela orden pendiente",
    parameters: {
      symbol: { type: "string", required: true },
      orderId: { type: "string", required: true }
    },
    risk: "medium"
  },
  {
    name: "binance_close_position",
    description: "Cierra posicion completa",
    parameters: {
      symbol: { type: "string", required: true }
    },
    risk: "high",
    requiresConfirmation: true
  }
];

Orquestacion de MCP desde LLM

# core/mcp_orchestrator.py

from typing import Dict, Any, List
import httpx

class MCPOrchestrator:
    """
    Orquesta llamadas a multiples MCP servers
    """

    def __init__(self, config: Dict):
        self.mt4_url = config.get('mcp_mt4_url', 'http://localhost:3605')
        self.binance_url = config.get('mcp_binance_url', 'http://localhost:3606')

    async def call_tool(
        self,
        server: str,   # "mt4" or "binance"
        tool: str,
        params: Dict
    ) -> Dict[str, Any]:
        """
        Llama a una herramienta MCP
        """
        url = self.mt4_url if server == "mt4" else self.binance_url

        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.post(
                f"{url}/tools/{tool}",
                json=params
            )
            return response.json()

    async def get_combined_portfolio(self) -> Dict[str, Any]:
        """
        Obtiene portfolio combinado de MT4 y Binance
        """
        mt4_account = await self.call_tool("mt4", "mt4_get_account", {})
        binance_account = await self.call_tool("binance", "binance_get_account", {})

        return {
            "mt4": mt4_account,
            "binance": binance_account,
            "total_equity": mt4_account.get("equity", 0) + binance_account.get("total_balance", 0)
        }

    async def execute_trade_on_best_venue(
        self,
        symbol: str,
        action: str,
        size: float,
        stop_loss: float,
        take_profit: float
    ) -> Dict[str, Any]:
        """
        Ejecuta trade en el mejor venue disponible
        """
        # Logica para determinar mejor venue
        # MT4 para forex/metales, Binance para crypto

        if symbol in ["XAUUSD", "EURUSD", "GBPUSD", "USDJPY"]:
            return await self.call_tool("mt4", "mt4_execute_trade", {
                "symbol": symbol,
                "action": action,
                "size": size,
                "stop_loss": stop_loss,
                "take_profit": take_profit
            })
        else:
            return await self.call_tool("binance", "binance_create_order", {
                "symbol": symbol,
                "side": action,
                "type": "MARKET",
                "quantity": size
            })

Gestion de Riesgo

Risk Manager Integrado

# services/risk_manager.py

from typing import Dict, Any, Optional
from dataclasses import dataclass
from enum import Enum

class RiskLevel(Enum):
    MINIMAL = "minimal"      # 0.5% max per trade
    CONSERVATIVE = "conservative"  # 1% max per trade
    MODERATE = "moderate"    # 2% max per trade
    AGGRESSIVE = "aggressive"  # 3% max per trade

@dataclass
class RiskLimits:
    max_position_size_pct: float    # % del capital por posicion
    max_daily_drawdown_pct: float   # % max perdida diaria
    max_total_exposure_pct: float   # % max exposicion total
    max_correlated_positions: int   # Max posiciones correlacionadas
    max_trades_per_day: int         # Max trades por dia

class RiskManager:
    """
    Gestor de riesgo integrado con el LLM Agent
    """

    # Limites por nivel de riesgo
    RISK_PROFILES = {
        RiskLevel.MINIMAL: RiskLimits(
            max_position_size_pct=0.5,
            max_daily_drawdown_pct=1.0,
            max_total_exposure_pct=5.0,
            max_correlated_positions=2,
            max_trades_per_day=3
        ),
        RiskLevel.CONSERVATIVE: RiskLimits(
            max_position_size_pct=1.0,
            max_daily_drawdown_pct=2.0,
            max_total_exposure_pct=10.0,
            max_correlated_positions=3,
            max_trades_per_day=5
        ),
        RiskLevel.MODERATE: RiskLimits(
            max_position_size_pct=2.0,
            max_daily_drawdown_pct=5.0,
            max_total_exposure_pct=20.0,
            max_correlated_positions=5,
            max_trades_per_day=10
        ),
        RiskLevel.AGGRESSIVE: RiskLimits(
            max_position_size_pct=3.0,
            max_daily_drawdown_pct=10.0,
            max_total_exposure_pct=30.0,
            max_correlated_positions=8,
            max_trades_per_day=20
        )
    }

    def __init__(self, risk_level: RiskLevel, capital: float):
        self.risk_level = risk_level
        self.limits = self.RISK_PROFILES[risk_level]
        self.capital = capital
        self.daily_pnl = 0.0
        self.current_exposure = 0.0
        self.trades_today = 0

    def can_open_position(
        self,
        position_size: float,
        stop_loss_pips: float,
        pip_value: float
    ) -> Dict[str, Any]:
        """
        Verifica si se puede abrir una nueva posicion
        """
        # Calcular riesgo de la posicion
        position_risk = stop_loss_pips * pip_value * position_size
        position_risk_pct = (position_risk / self.capital) * 100

        checks = {
            "position_size_ok": position_risk_pct <= self.limits.max_position_size_pct,
            "daily_drawdown_ok": abs(self.daily_pnl) < self.limits.max_daily_drawdown_pct,
            "exposure_ok": self.current_exposure + position_risk_pct <= self.limits.max_total_exposure_pct,
            "trades_limit_ok": self.trades_today < self.limits.max_trades_per_day
        }

        return {
            "allowed": all(checks.values()),
            "checks": checks,
            "position_risk_pct": position_risk_pct,
            "recommended_size": self._calculate_safe_size(stop_loss_pips, pip_value)
        }

    def _calculate_safe_size(
        self,
        stop_loss_pips: float,
        pip_value: float
    ) -> float:
        """
        Calcula tamano de posicion seguro basado en limites
        """
        max_risk = self.capital * (self.limits.max_position_size_pct / 100)
        safe_size = max_risk / (stop_loss_pips * pip_value)
        return round(safe_size, 2)

    def check_circuit_breaker(self) -> Dict[str, Any]:
        """
        Verifica si se activa el circuit breaker
        """
        daily_loss_pct = abs(self.daily_pnl)

        if daily_loss_pct >= self.limits.max_daily_drawdown_pct:
            return {
                "triggered": True,
                "reason": "daily_drawdown_limit",
                "message": f"Se alcanzo el limite de perdida diaria ({daily_loss_pct:.2f}%)",
                "action": "STOP_TRADING"
            }

        if daily_loss_pct >= self.limits.max_daily_drawdown_pct * 0.8:
            return {
                "triggered": False,
                "warning": True,
                "message": f"Cerca del limite de perdida diaria ({daily_loss_pct:.2f}%)",
                "action": "REDUCE_RISK"
            }

        return {"triggered": False, "warning": False}

Reglas de Riesgo para LLM

# prompts/risk_rules.py

RISK_RULES_PROMPT = """
## REGLAS DE GESTION DE RIESGO (OBLIGATORIAS)

Como Trading Agent, DEBES seguir estas reglas de riesgo SIEMPRE:

### 1. TAMANO DE POSICION
- NUNCA arriesgar mas del {max_position_pct}% del capital en una operacion
- Calcular tamano basado en distancia al stop loss
- Formula: Size = (Capital * Risk%) / (StopLoss_pips * Pip_value)

### 2. DRAWDOWN DIARIO
- Limite de perdida diaria: {max_daily_dd}%
- Si se alcanza, DETENER operaciones por el resto del dia
- Alertar al usuario cuando llegue al 80% del limite

### 3. EXPOSICION TOTAL
- Maximo {max_exposure}% del capital expuesto simultaneamente
- Incluye todas las posiciones abiertas
- Considerar correlacion entre pares

### 4. STOP LOSS OBLIGATORIO
- TODA operacion DEBE tener stop loss definido
- NUNCA mover stop loss hacia atras (aumentar riesgo)
- Solo mover a breakeven o trailing

### 5. CORRELACION
- Maximo {max_correlated} posiciones correlacionadas
- EUR/USD y GBP/USD se consideran correlacionados
- BTC y ETH se consideran correlacionados

### 6. CIRCUIT BREAKER
- Despues de {max_consecutive_losses} perdidas consecutivas: pausa 1 hora
- Despues de alcanzar daily DD: stop hasta manana
- KILL SWITCH manual siempre disponible

### FORMATO DE RESPUESTA CUANDO SE EJECUTA TRADE:

**VALIDACION DE RIESGO:**
- Tamano posicion: {size} lots = {risk_pct}% del capital
- Exposicion actual: {current_exp}% -> {new_exp}%
- Trades hoy: {trades_today} de {max_trades}
- Estado: [APROBADO/RECHAZADO]
"""

Analisis de Predicciones ML

Integracion ML Engine

# services/ml_analyzer.py

from typing import Dict, Any, List
import httpx

class MLAnalyzer:
    """
    Analiza y procesa predicciones del ML Engine
    """

    def __init__(self, ml_engine_url: str = "http://localhost:3083"):
        self.ml_engine_url = ml_engine_url

    async def get_full_analysis(self, symbol: str) -> Dict[str, Any]:
        """
        Obtiene analisis completo de todos los modelos ML
        """
        async with httpx.AsyncClient(timeout=30.0) as client:
            # Obtener predicciones de todos los modelos
            tasks = [
                client.get(f"{self.ml_engine_url}/api/amd_phase", params={"symbol": symbol}),
                client.get(f"{self.ml_engine_url}/api/range", params={"symbol": symbol}),
                client.get(f"{self.ml_engine_url}/api/signal", params={"symbol": symbol}),
                client.get(f"{self.ml_engine_url}/api/ict_context", params={"symbol": symbol}),
            ]

            responses = await asyncio.gather(*tasks)

            return {
                "symbol": symbol,
                "amd_phase": responses[0].json(),
                "range_prediction": responses[1].json(),
                "signal": responses[2].json(),
                "ict_context": responses[3].json(),
                "confluence_score": self._calculate_confluence(responses)
            }

    def _calculate_confluence(self, predictions: List) -> float:
        """
        Calcula score de confluencia entre modelos
        """
        amd = predictions[0].json()
        signal = predictions[2].json()
        ict = predictions[3].json()

        score = 0.0

        # AMD en fase favorable
        if amd.get("phase") in ["accumulation", "re_accumulation"]:
            if signal.get("direction") == "LONG":
                score += 0.3
        elif amd.get("phase") == "distribution":
            if signal.get("direction") == "SHORT":
                score += 0.3

        # Killzone activa
        if ict.get("killzone_active"):
            score += 0.2

        # OTE zone correcta
        if ict.get("ote_zone") == "discount" and signal.get("direction") == "LONG":
            score += 0.2
        elif ict.get("ote_zone") == "premium" and signal.get("direction") == "SHORT":
            score += 0.2

        # Confianza del modelo
        signal_confidence = signal.get("confidence", 0)
        score += signal_confidence * 0.3

        return min(score, 1.0)

    def generate_explanation(self, analysis: Dict[str, Any]) -> str:
        """
        Genera explicacion en lenguaje natural del analisis
        """
        amd = analysis["amd_phase"]
        signal = analysis["signal"]
        ict = analysis["ict_context"]
        confluence = analysis["confluence_score"]

        explanation = f"""
## Analisis ML para {analysis["symbol"]}

### 1. Fase AMD
- Fase actual: **{amd.get("phase", "N/A")}** ({amd.get("confidence", 0)*100:.1f}% confianza)
- Interpretacion: {self._explain_amd_phase(amd.get("phase"))}

### 2. Senal de Trading
- Direccion: **{signal.get("direction", "N/A")}**
- Confianza: {signal.get("confidence", 0)*100:.1f}%
- Entry: {signal.get("entry_price", "N/A")}
- Stop Loss: {signal.get("stop_loss", "N/A")}
- Take Profit: {signal.get("take_profit", "N/A")}

### 3. Contexto ICT/SMC
- Killzone: {ict.get("killzone", "None")} {"(ACTIVA)" if ict.get("killzone_active") else ""}
- Zona OTE: {ict.get("ote_zone", "N/A")}
- FVG detectado: {"Si" if ict.get("fvg_present") else "No"}

### 4. Score de Confluencia
**{confluence*100:.0f}%** - {"Alta confluencia, senal fuerte" if confluence > 0.7 else "Confluencia media" if confluence > 0.5 else "Baja confluencia, precaucion"}
"""
        return explanation

    def _explain_amd_phase(self, phase: str) -> str:
        explanations = {
            "accumulation": "Las instituciones estan acumulando. Favorable para LONG.",
            "manipulation": "Fase de caza de stops. EVITAR nuevas entradas.",
            "distribution": "Las instituciones estan distribuyendo. Favorable para SHORT.",
            "re_accumulation": "Consolidacion antes de continuacion alcista."
        }
        return explanations.get(phase, "Fase no identificada")

API para Frontend

Endpoints de Predicciones

# api/predictions.py

from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel
from typing import List, Optional

router = APIRouter(prefix="/api/v1/predictions", tags=["predictions"])

class PredictionRequest(BaseModel):
    symbol: str
    timeframe: str = "5m"

class PredictionResponse(BaseModel):
    symbol: str
    timestamp: str
    amd_phase: dict
    signal: dict
    range_prediction: dict
    ict_context: dict
    confluence_score: float
    explanation: str
    risk_assessment: dict

class HistoricalPrediction(BaseModel):
    id: str
    symbol: str
    timestamp: str
    prediction: dict
    outcome: Optional[dict] = None
    accuracy: Optional[float] = None

@router.post("/analyze", response_model=PredictionResponse)
async def analyze_symbol(request: PredictionRequest):
    """
    Analiza un simbolo con todos los modelos ML
    y retorna prediccion con explicacion
    """
    ml_analyzer = MLAnalyzer()
    analysis = await ml_analyzer.get_full_analysis(request.symbol)
    explanation = ml_analyzer.generate_explanation(analysis)

    return PredictionResponse(
        symbol=request.symbol,
        timestamp=datetime.utcnow().isoformat(),
        amd_phase=analysis["amd_phase"],
        signal=analysis["signal"],
        range_prediction=analysis["range_prediction"],
        ict_context=analysis["ict_context"],
        confluence_score=analysis["confluence_score"],
        explanation=explanation,
        risk_assessment=await get_risk_assessment(request.symbol, analysis)
    )

@router.get("/history/{symbol}", response_model=List[HistoricalPrediction])
async def get_prediction_history(
    symbol: str,
    limit: int = 50,
    include_outcomes: bool = True
):
    """
    Obtiene historial de predicciones para un simbolo
    """
    predictions = await prediction_repository.get_history(
        symbol=symbol,
        limit=limit,
        include_outcomes=include_outcomes
    )
    return predictions

@router.get("/accuracy/{symbol}")
async def get_model_accuracy(symbol: str, days: int = 30):
    """
    Obtiene metricas de accuracy del modelo para un simbolo
    """
    return await prediction_repository.get_accuracy_metrics(symbol, days)

@router.get("/active-signals")
async def get_active_signals():
    """
    Obtiene todas las senales activas con confluencia > 60%
    """
    return await prediction_repository.get_active_signals(min_confluence=0.6)

WebSocket para Predicciones en Tiempo Real

# api/websocket.py

from fastapi import WebSocket, WebSocketDisconnect
from typing import Dict, Set
import asyncio
import json

class PredictionWebSocketManager:
    def __init__(self):
        self.connections: Dict[str, Set[WebSocket]] = {}

    async def connect(self, websocket: WebSocket, symbol: str):
        await websocket.accept()
        if symbol not in self.connections:
            self.connections[symbol] = set()
        self.connections[symbol].add(websocket)

    def disconnect(self, websocket: WebSocket, symbol: str):
        if symbol in self.connections:
            self.connections[symbol].discard(websocket)

    async def broadcast_prediction(self, symbol: str, prediction: dict):
        if symbol in self.connections:
            message = json.dumps(prediction)
            for connection in self.connections[symbol]:
                try:
                    await connection.send_text(message)
                except:
                    self.disconnect(connection, symbol)

manager = PredictionWebSocketManager()

@router.websocket("/ws/predictions/{symbol}")
async def prediction_websocket(websocket: WebSocket, symbol: str):
    await manager.connect(websocket, symbol)
    try:
        while True:
            # Enviar prediccion cada 5 segundos
            prediction = await ml_analyzer.get_full_analysis(symbol)
            await websocket.send_json(prediction)
            await asyncio.sleep(5)
    except WebSocketDisconnect:
        manager.disconnect(websocket, symbol)

Persistencia en PostgreSQL

Nuevas Tablas

-- Schema: ml (agregar a existente)

-- Tabla de predicciones del LLM Agent
CREATE TABLE IF NOT EXISTS ml.llm_predictions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    symbol VARCHAR(20) NOT NULL,
    timeframe VARCHAR(10) NOT NULL,

    -- Prediccion
    amd_phase VARCHAR(50),
    amd_confidence DECIMAL(5,4),
    signal_direction VARCHAR(10),
    signal_confidence DECIMAL(5,4),
    entry_price DECIMAL(20,8),
    stop_loss DECIMAL(20,8),
    take_profit DECIMAL(20,8),

    -- Contexto ICT
    killzone VARCHAR(50),
    ote_zone VARCHAR(20),

    -- Confluencia
    confluence_score DECIMAL(5,4),

    -- Explicacion generada por LLM
    explanation TEXT,

    -- Metadata
    model_version VARCHAR(50),
    created_at TIMESTAMPTZ DEFAULT NOW(),

    CONSTRAINT chk_signal_direction CHECK (signal_direction IN ('LONG', 'SHORT', 'HOLD'))
);

-- Indice para busquedas rapidas
CREATE INDEX idx_llm_predictions_symbol_time ON ml.llm_predictions(symbol, created_at DESC);

-- Tabla de outcomes para tracking de accuracy
CREATE TABLE IF NOT EXISTS ml.prediction_outcomes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    prediction_id UUID REFERENCES ml.llm_predictions(id),

    -- Resultado real
    actual_direction VARCHAR(10),
    actual_high DECIMAL(20,8),
    actual_low DECIMAL(20,8),

    -- Metricas de precision
    direction_correct BOOLEAN,
    target_reached BOOLEAN,
    stop_hit BOOLEAN,
    pnl_pips DECIMAL(10,2),
    pnl_percentage DECIMAL(10,4),

    -- Tiempo de resolucion
    resolved_at TIMESTAMPTZ,
    resolution_candles INT,

    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Tabla de decisiones del LLM Agent
CREATE TABLE IF NOT EXISTS ml.llm_decisions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    prediction_id UUID REFERENCES ml.llm_predictions(id),

    -- Decision
    decision_type VARCHAR(50), -- 'TRADE', 'ALERT', 'WAIT', 'CLOSE'
    action_taken VARCHAR(50),
    reasoning TEXT,

    -- Risk assessment
    risk_level VARCHAR(20),
    position_size DECIMAL(10,4),
    risk_pct DECIMAL(5,4),

    -- Execution
    executed BOOLEAN DEFAULT FALSE,
    execution_venue VARCHAR(20), -- 'MT4', 'BINANCE'
    order_id VARCHAR(100),

    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Tabla de eventos de riesgo
CREATE TABLE IF NOT EXISTS ml.risk_events (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID,

    event_type VARCHAR(50), -- 'CIRCUIT_BREAKER', 'DAILY_LIMIT', 'EXPOSURE_LIMIT'
    severity VARCHAR(20),
    details JSONB,

    action_taken VARCHAR(100),
    resolved BOOLEAN DEFAULT FALSE,
    resolved_at TIMESTAMPTZ,

    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Funcion para calcular accuracy
CREATE OR REPLACE FUNCTION ml.calculate_prediction_accuracy(
    p_symbol VARCHAR,
    p_days INT DEFAULT 30
)
RETURNS TABLE(
    total_predictions INT,
    direction_accuracy DECIMAL,
    target_hit_rate DECIMAL,
    avg_pnl_pips DECIMAL,
    profit_factor DECIMAL
) AS $$
BEGIN
    RETURN QUERY
    SELECT
        COUNT(*)::INT as total_predictions,
        AVG(CASE WHEN o.direction_correct THEN 1 ELSE 0 END)::DECIMAL as direction_accuracy,
        AVG(CASE WHEN o.target_reached THEN 1 ELSE 0 END)::DECIMAL as target_hit_rate,
        AVG(o.pnl_pips)::DECIMAL as avg_pnl_pips,
        CASE
            WHEN SUM(CASE WHEN o.pnl_pips < 0 THEN ABS(o.pnl_pips) ELSE 0 END) > 0
            THEN SUM(CASE WHEN o.pnl_pips > 0 THEN o.pnl_pips ELSE 0 END) /
                 SUM(CASE WHEN o.pnl_pips < 0 THEN ABS(o.pnl_pips) ELSE 0 END)
            ELSE 0
        END::DECIMAL as profit_factor
    FROM ml.llm_predictions p
    JOIN ml.prediction_outcomes o ON p.id = o.prediction_id
    WHERE p.symbol = p_symbol
    AND p.created_at >= NOW() - (p_days || ' days')::INTERVAL;
END;
$$ LANGUAGE plpgsql;

COMMENT ON TABLE ml.llm_predictions IS 'Predicciones generadas por el LLM Trading Agent';
COMMENT ON TABLE ml.prediction_outcomes IS 'Resultados reales de las predicciones para tracking de accuracy';
COMMENT ON TABLE ml.llm_decisions IS 'Decisiones tomadas por el LLM Agent';
COMMENT ON TABLE ml.risk_events IS 'Eventos de gestion de riesgo';

Repository Pattern

# repositories/prediction_repository.py

from typing import List, Optional, Dict, Any
import asyncpg
from datetime import datetime, timedelta

class PredictionRepository:
    def __init__(self, pool: asyncpg.Pool):
        self.pool = pool

    async def save_prediction(self, prediction: Dict[str, Any]) -> str:
        """Guarda una nueva prediccion"""
        query = """
            INSERT INTO ml.llm_predictions (
                symbol, timeframe, amd_phase, amd_confidence,
                signal_direction, signal_confidence, entry_price,
                stop_loss, take_profit, killzone, ote_zone,
                confluence_score, explanation, model_version
            ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
            RETURNING id
        """
        async with self.pool.acquire() as conn:
            row = await conn.fetchrow(query,
                prediction["symbol"],
                prediction["timeframe"],
                prediction.get("amd_phase", {}).get("phase"),
                prediction.get("amd_phase", {}).get("confidence"),
                prediction.get("signal", {}).get("direction"),
                prediction.get("signal", {}).get("confidence"),
                prediction.get("signal", {}).get("entry_price"),
                prediction.get("signal", {}).get("stop_loss"),
                prediction.get("signal", {}).get("take_profit"),
                prediction.get("ict_context", {}).get("killzone"),
                prediction.get("ict_context", {}).get("ote_zone"),
                prediction.get("confluence_score"),
                prediction.get("explanation"),
                prediction.get("model_version", "v1.0")
            )
            return str(row["id"])

    async def save_outcome(self, prediction_id: str, outcome: Dict[str, Any]):
        """Guarda el resultado de una prediccion"""
        query = """
            INSERT INTO ml.prediction_outcomes (
                prediction_id, actual_direction, actual_high, actual_low,
                direction_correct, target_reached, stop_hit,
                pnl_pips, pnl_percentage, resolved_at, resolution_candles
            ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
        """
        async with self.pool.acquire() as conn:
            await conn.execute(query,
                prediction_id,
                outcome["actual_direction"],
                outcome["actual_high"],
                outcome["actual_low"],
                outcome["direction_correct"],
                outcome["target_reached"],
                outcome["stop_hit"],
                outcome["pnl_pips"],
                outcome["pnl_percentage"],
                outcome.get("resolved_at", datetime.utcnow()),
                outcome.get("resolution_candles", 0)
            )

    async def get_history(
        self,
        symbol: str,
        limit: int = 50,
        include_outcomes: bool = True
    ) -> List[Dict]:
        """Obtiene historial de predicciones"""
        query = """
            SELECT
                p.*,
                o.direction_correct,
                o.target_reached,
                o.pnl_pips
            FROM ml.llm_predictions p
            LEFT JOIN ml.prediction_outcomes o ON p.id = o.prediction_id
            WHERE p.symbol = $1
            ORDER BY p.created_at DESC
            LIMIT $2
        """
        async with self.pool.acquire() as conn:
            rows = await conn.fetch(query, symbol, limit)
            return [dict(row) for row in rows]

    async def get_accuracy_metrics(
        self,
        symbol: str,
        days: int = 30
    ) -> Dict[str, Any]:
        """Obtiene metricas de accuracy"""
        async with self.pool.acquire() as conn:
            row = await conn.fetchrow(
                "SELECT * FROM ml.calculate_prediction_accuracy($1, $2)",
                symbol, days
            )
            return dict(row) if row else {}

Pipeline de Fine-Tuning

Generacion de Dataset

# fine_tuning/generate_dataset.py

import json
from typing import List, Dict
import asyncpg

class DatasetGenerator:
    """
    Genera dataset de fine-tuning a partir de:
    1. Predicciones historicas con outcomes
    2. Decisiones correctas del sistema
    3. Reglas de trading documentadas
    """

    async def generate_from_predictions(
        self,
        pool: asyncpg.Pool,
        min_accuracy: float = 0.7
    ) -> List[Dict]:
        """
        Genera ejemplos de predicciones exitosas
        """
        query = """
            SELECT
                p.symbol, p.amd_phase, p.amd_confidence,
                p.signal_direction, p.signal_confidence,
                p.killzone, p.ote_zone, p.confluence_score,
                o.direction_correct, o.target_reached, o.pnl_pips
            FROM ml.llm_predictions p
            JOIN ml.prediction_outcomes o ON p.id = o.prediction_id
            WHERE o.direction_correct = true
            AND p.confluence_score >= $1
        """

        async with pool.acquire() as conn:
            rows = await conn.fetch(query, min_accuracy)

        examples = []
        for row in rows:
            example = self._create_training_example(dict(row))
            examples.append(example)

        return examples

    def _create_training_example(self, prediction: Dict) -> Dict:
        """Crea ejemplo de entrenamiento en formato instruction/input/output"""

        instruction = "Analiza esta senal de trading y proporciona tu recomendacion"

        input_text = f"""
Simbolo: {prediction['symbol']}
Fase AMD: {prediction['amd_phase']} ({prediction['amd_confidence']*100:.0f}% confianza)
Senal: {prediction['signal_direction']} ({prediction['signal_confidence']*100:.0f}%)
Killzone: {prediction['killzone']}
Zona OTE: {prediction['ote_zone']}
Confluencia: {prediction['confluence_score']*100:.0f}%
"""

        output_text = self._generate_analysis_output(prediction)

        return {
            "instruction": instruction,
            "input": input_text.strip(),
            "output": output_text
        }

    def generate_from_strategy_docs(self, docs_path: str) -> List[Dict]:
        """
        Genera ejemplos a partir de documentacion de estrategias
        """
        examples = []

        # AMD Phase examples
        examples.extend(self._amd_examples())

        # ICT/SMC examples
        examples.extend(self._ict_examples())

        # Risk management examples
        examples.extend(self._risk_examples())

        return examples

    def _amd_examples(self) -> List[Dict]:
        return [
            {
                "instruction": "Explica que es la fase de Accumulation en AMD",
                "input": "",
                "output": """# Fase de Accumulation (Acumulacion)

## Definicion
La fase de Accumulation es cuando las instituciones (smart money) estan comprando activamente mientras el precio se mantiene en un rango. Es la primera fase del ciclo AMD.

## Caracteristicas
1. **Precio**: Se mueve lateralmente o con leve tendencia alcista
2. **Volumen**: Incrementa en impulsos alcistas, decrece en retrocesos
3. **Estructura**: Minimos mas altos dentro del rango
4. **Duracion**: Puede durar dias o semanas

## Como Tradear
- **Direccion preferida**: LONG
- **Entrada**: En retrocesos hacia la parte baja del rango
- **Stop Loss**: Debajo del ultimo minimo significativo
- **Take Profit**: Parte alta del rango o ruptura

## Senales de Confirmacion
- Rechazo de precios bajos con volumen
- Velas de absorcion en soporte
- RSI formando divergencias alcistas
- MACD cruzando hacia arriba

## Transicion
La fase de Accumulation termina cuando:
1. El precio rompe la resistencia con volumen
2. Comienza la fase de Markup (tendencia alcista)
3. O inicia fase de Manipulation (falsa ruptura)"""
            },
            {
                "instruction": "Que hacer cuando se detecta fase de Manipulation",
                "input": "El mercado muestra fase de Manipulation con 85% de confianza",
                "output": """# ALERTA: Fase de Manipulation Detectada

## Accion Recomendada: NO OPERAR

La fase de Manipulation es la mas peligrosa para traders retail. Las instituciones estan barriendo liquidez (stops) antes del movimiento real.

## Porque NO Operar
1. **Alta probabilidad de stops**: Las instituciones buscan liquidez en zonas obvias
2. **Movimientos erraticos**: El precio puede ir en ambas direcciones rapidamente
3. **Senales falsas**: Los indicadores tecnicos generan senales contradictorias
4. **Volumen enganoso**: Spikes de volumen que no indican direccion real

## Que Esperar
- Transicion a Distribution (movimiento bajista)
- O transicion a Re-accumulation (continuacion alcista)
- Tiempo estimado: 1-4 horas en timeframes de 5min

## Si Tienes Posiciones Abiertas
1. Mover stop loss a breakeven si es posible
2. Considerar cierre parcial (50%)
3. NO agregar a la posicion
4. Estar listo para cierre manual si es necesario

## Siguiente Paso
Esperar confirmacion de nueva fase con:
- Cambio en estructura de mercado
- Volumen direccional claro
- Confluencia de indicadores"""
            }
        ]

    def save_dataset(self, examples: List[Dict], output_path: str):
        """Guarda dataset en formato JSONL"""
        with open(output_path, 'w') as f:
            for example in examples:
                f.write(json.dumps(example, ensure_ascii=False) + '\n')

Script de Entrenamiento

#!/bin/bash
# fine_tuning/train.sh

# Configuracion
MODEL_NAME="meta-llama/Meta-Llama-3-8B-Instruct"
OUTPUT_DIR="./trading-llm-finetuned"
DATASET_PATH="./data/trading_strategies.jsonl"

# Verificar GPU
nvidia-smi

# Activar ambiente
source activate trading-llm

# Ejecutar entrenamiento
python train_lora.py \
    --model_name $MODEL_NAME \
    --dataset_path $DATASET_PATH \
    --output_dir $OUTPUT_DIR \
    --num_epochs 3 \
    --batch_size 4 \
    --learning_rate 2e-4 \
    --lora_r 16 \
    --lora_alpha 32 \
    --max_length 2048 \
    --fp16 \
    --gradient_checkpointing

# Merge LoRA weights
python merge_lora.py \
    --base_model $MODEL_NAME \
    --lora_weights $OUTPUT_DIR \
    --output_dir "${OUTPUT_DIR}-merged"

# Convertir a GGUF para Ollama
python convert_to_gguf.py \
    --model_path "${OUTPUT_DIR}-merged" \
    --output_path "${OUTPUT_DIR}.gguf" \
    --quantization q5_K_M

echo "Fine-tuning completado. Modelo en: ${OUTPUT_DIR}.gguf"

Implementacion

Docker Compose Completo

# docker-compose.llm-advanced.yaml

version: '3.8'

services:
  # Ollama con modelo fine-tuned
  ollama:
    image: ollama/ollama:latest
    container_name: trading-ollama
    ports:
      - "11434:11434"
    volumes:
      - ollama_data:/root/.ollama
      - ./models:/models  # Para modelos custom
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    environment:
      - OLLAMA_MODELS=/models
    restart: unless-stopped

  # LLM Agent Service
  llm-agent:
    build:
      context: ./apps/llm-agent
      dockerfile: Dockerfile
    container_name: trading-llm-agent
    ports:
      - "3085:3085"
    environment:
      - OLLAMA_URL=http://ollama:11434
      - LLM_MODEL=trading-trading:latest  # Modelo fine-tuned
      - REDIS_URL=redis://redis:6379
      - DATABASE_URL=postgresql://user:pass@postgres:5432/trading
      - ML_ENGINE_URL=http://ml-engine:3083
      - MCP_MT4_URL=http://mcp-mt4:3605
      - MCP_BINANCE_URL=http://mcp-binance:3606
    depends_on:
      - ollama
      - redis
      - postgres
    restart: unless-stopped

  # MCP MT4 Connector
  mcp-mt4:
    build:
      context: ./apps/mcp-mt4-connector
      dockerfile: Dockerfile
    container_name: trading-mcp-mt4
    ports:
      - "3605:3605"
    environment:
      - MT4_GATEWAY_HOST=${MT4_GATEWAY_HOST}
      - MT4_GATEWAY_TOKEN=${MT4_GATEWAY_TOKEN}
    restart: unless-stopped

  # MCP Binance Connector (NUEVO)
  mcp-binance:
    build:
      context: ./apps/mcp-binance-connector
      dockerfile: Dockerfile
    container_name: trading-mcp-binance
    ports:
      - "3606:3606"
    environment:
      - BINANCE_API_KEY=${BINANCE_API_KEY}
      - BINANCE_API_SECRET=${BINANCE_API_SECRET}
      - BINANCE_TESTNET=${BINANCE_TESTNET:-true}
    restart: unless-stopped

  # ML Engine
  ml-engine:
    build:
      context: ./apps/ml-engine
      dockerfile: Dockerfile
    container_name: trading-ml-engine
    ports:
      - "3083:3083"
    environment:
      - DATABASE_URL=postgresql://user:pass@postgres:5432/trading
      - REDIS_URL=redis://redis:6379
    volumes:
      - ml_models:/app/models
    restart: unless-stopped

  # Redis
  redis:
    image: redis:7-alpine
    container_name: trading-redis
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    restart: unless-stopped

  # PostgreSQL
  postgres:
    image: postgres:16-alpine
    container_name: trading-postgres
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=trading
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  ollama_data:
  redis_data:
  postgres_data:
  ml_models:

Script de Inicializacion

#!/bin/bash
# scripts/init_llm_system.sh

echo "=== Trading Platform LLM Trading System Setup ==="

# 1. Verificar GPU
echo "[1/6] Verificando GPU..."
nvidia-smi || { echo "Error: GPU no detectada"; exit 1; }

# 2. Iniciar servicios base
echo "[2/6] Iniciando servicios base..."
docker-compose -f docker-compose.llm-advanced.yaml up -d postgres redis

# 3. Esperar a que postgres este listo
echo "[3/6] Esperando PostgreSQL..."
sleep 10
docker exec trading-postgres pg_isready

# 4. Ejecutar migraciones
echo "[4/6] Ejecutando migraciones SQL..."
docker exec -i trading-postgres psql -U user -d trading < ./apps/database/ddl/schemas/ml/llm_predictions.sql

# 5. Iniciar Ollama y cargar modelo
echo "[5/6] Iniciando Ollama..."
docker-compose -f docker-compose.llm-advanced.yaml up -d ollama
sleep 10

# Verificar si existe modelo fine-tuned
if [ -f "./models/trading-trading.gguf" ]; then
    echo "Cargando modelo fine-tuned..."
    docker exec trading-ollama ollama create trading-trading -f /models/Modelfile
else
    echo "Modelo fine-tuned no encontrado, usando Llama 3..."
    docker exec trading-ollama ollama pull llama3:8b-instruct-q5_K_M
fi

# 6. Iniciar resto de servicios
echo "[6/6] Iniciando servicios de aplicacion..."
docker-compose -f docker-compose.llm-advanced.yaml up -d

echo ""
echo "=== Setup Completado ==="
echo "LLM Agent:     http://localhost:3085"
echo "ML Engine:     http://localhost:3083"
echo "MCP MT4:       http://localhost:3605"
echo "MCP Binance:   http://localhost:3606"
echo ""

Testing y Validacion

Test Cases

# tests/test_llm_trading.py

import pytest
import httpx
from datetime import datetime

LLM_URL = "http://localhost:3085"
ML_URL = "http://localhost:3083"

@pytest.mark.asyncio
async def test_llm_analyze_with_confluence():
    """Test analisis con confluencia de modelos"""
    async with httpx.AsyncClient(timeout=60.0) as client:
        response = await client.post(
            f"{LLM_URL}/api/v1/analyze",
            json={"symbol": "XAUUSD", "timeframe": "5m"}
        )
        assert response.status_code == 200
        data = response.json()

        # Verificar campos requeridos
        assert "amd_phase" in data
        assert "signal" in data
        assert "confluence_score" in data
        assert "explanation" in data
        assert "risk_assessment" in data

@pytest.mark.asyncio
async def test_risk_management_validation():
    """Test validacion de riesgo antes de trade"""
    async with httpx.AsyncClient(timeout=30.0) as client:
        response = await client.post(
            f"{LLM_URL}/api/v1/validate-trade",
            json={
                "symbol": "XAUUSD",
                "direction": "LONG",
                "size": 0.1,
                "stop_loss_pips": 50
            }
        )
        assert response.status_code == 200
        data = response.json()

        assert "allowed" in data
        assert "checks" in data
        assert "recommended_size" in data

@pytest.mark.asyncio
async def test_mcp_binance_connection():
    """Test conexion MCP Binance"""
    async with httpx.AsyncClient(timeout=10.0) as client:
        response = await client.get("http://localhost:3606/health")
        assert response.status_code == 200

@pytest.mark.asyncio
async def test_prediction_persistence():
    """Test persistencia de predicciones"""
    # 1. Generar prediccion
    async with httpx.AsyncClient(timeout=60.0) as client:
        response = await client.post(
            f"{LLM_URL}/api/v1/predictions/analyze",
            json={"symbol": "BTCUSDT"}
        )
        assert response.status_code == 200

        # 2. Verificar en historial
        history_response = await client.get(
            f"{LLM_URL}/api/v1/predictions/history/BTCUSDT?limit=1"
        )
        assert history_response.status_code == 200
        assert len(history_response.json()) > 0

@pytest.mark.asyncio
async def test_fine_tuned_trading_knowledge():
    """Test conocimiento de trading del modelo fine-tuned"""
    async with httpx.AsyncClient(timeout=60.0) as client:
        response = await client.post(
            f"{LLM_URL}/api/v1/chat",
            json={
                "message": "Explica que es la fase de Accumulation en AMD y como tradearla",
                "session_id": "test_knowledge"
            }
        )
        assert response.status_code == 200
        content = response.json()["response"]

        # Verificar conocimiento especifico
        assert "acumulacion" in content.lower() or "accumulation" in content.lower()
        assert "instituciones" in content.lower() or "smart money" in content.lower()

Metricas de Validacion

Metrica Target Como Medir
Response Time <5s pytest benchmark
Direction Accuracy >65% Historical outcomes
Confluence Score Reliability >70% Correlation with outcomes
Risk Limit Adherence 100% Audit logs
MCP Uptime >99% Health checks
Fine-tuning Quality Perplexity <3.0 Eval dataset

Proximos Pasos

Fase 1: Infraestructura (1-2 semanas)

  1. Crear MCP Binance Connector
  2. Implementar DDL nuevas tablas
  3. Configurar Docker Compose completo
  4. Setup pipeline de CI/CD

Fase 2: Core LLM (2-3 semanas)

  1. Generar dataset de fine-tuning
  2. Entrenar modelo con LoRA
  3. Integrar modelo en Ollama
  4. Implementar Risk Manager

Fase 3: Integracion (1-2 semanas)

  1. Conectar ML Engine con LLM
  2. Implementar MCP Orchestrator
  3. API de predicciones para frontend
  4. WebSocket real-time

Fase 4: Testing y Deployment (1 semana)

  1. Tests de integracion
  2. Backtesting de decisiones
  3. Documentacion final
  4. Deployment a produccion

Documento Generado: 2026-01-04 Autor: Orquestador Agent - Trading Platform Version: 1.0.0