---
id: "INTEGRACION-LLM-FINE-TUNING"
title: "Integracion LLM con Fine-Tuning para Trading Agent"
type: "Documentation"
project: "trading-platform"
version: "1.0.0"
created_date: "2026-01-04"
updated_date: "2026-01-04"
author: "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](#vision-general)
2. [Arquitectura Unificada](#arquitectura-unificada)
3. [Modelo LLM y Fine-Tuning](#modelo-llm-y-fine-tuning)
4. [Integracion MCP Servers](#integracion-mcp-servers)
5. [Gestion de Riesgo](#gestion-de-riesgo)
6. [Analisis de Predicciones ML](#analisis-de-predicciones-ml)
7. [API para Frontend](#api-para-frontend)
8. [Persistencia en PostgreSQL](#persistencia-en-postgresql)
9. [Pipeline de Fine-Tuning](#pipeline-de-fine-tuning)
10. [Implementacion](#implementacion)
11. [Testing y Validacion](#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:

```yaml
# 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

```jsonl
{"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

```python
# 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/`:

```typescript
// 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

```typescript
// 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

```python
# 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

```python
# 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

```python
# 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

```python
# 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

```python
# 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

```python
# 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

```sql
-- 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

```python
# 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

```python
# 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

```bash
#!/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

```yaml
# 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

```bash
#!/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

```python
# 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
