--- 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 - OrbiQuant IA" --- # 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 ``` ┌─────────────────────────────────────────────────────────────────────────────────┐ │ ORBIQUANT 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: orbiquant_trading_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="./orbiquant-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="./orbiquant-llm-finetuned" DATASET_PATH="./data/trading_strategies.jsonl" # Verificar GPU nvidia-smi # Activar ambiente source activate orbiquant-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: orbiquant-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: orbiquant-llm-agent ports: - "3085:3085" environment: - OLLAMA_URL=http://ollama:11434 - LLM_MODEL=orbiquant-trading:latest # Modelo fine-tuned - REDIS_URL=redis://redis:6379 - DATABASE_URL=postgresql://user:pass@postgres:5432/orbiquant - 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: orbiquant-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: orbiquant-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: orbiquant-ml-engine ports: - "3083:3083" environment: - DATABASE_URL=postgresql://user:pass@postgres:5432/orbiquant - REDIS_URL=redis://redis:6379 volumes: - ml_models:/app/models restart: unless-stopped # Redis redis: image: redis:7-alpine container_name: orbiquant-redis ports: - "6379:6379" volumes: - redis_data:/data restart: unless-stopped # PostgreSQL postgres: image: postgres:16-alpine container_name: orbiquant-postgres ports: - "5432:5432" environment: - POSTGRES_USER=user - POSTGRES_PASSWORD=pass - POSTGRES_DB=orbiquant 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 "=== OrbiQuant 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 orbiquant-postgres pg_isready # 4. Ejecutar migraciones echo "[4/6] Ejecutando migraciones SQL..." docker exec -i orbiquant-postgres psql -U user -d orbiquant < ./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/orbiquant-trading.gguf" ]; then echo "Cargando modelo fine-tuned..." docker exec orbiquant-ollama ollama create orbiquant-trading -f /models/Modelfile else echo "Modelo fine-tuned no encontrado, usando Llama 3..." docker exec orbiquant-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 - OrbiQuant IA **Version:** 1.0.0