trading-platform/docs/02-definicion-modulos/OQI-009-marketplace/especificaciones/ET-MT4-001-websocket-integration.md
Adrian Flores Cortes cea9ae85f1 docs: Add 8 ET specifications from TASK-002 audit gaps
Complete remaining ET specs identified in INTEGRATION-PLAN:
- ET-EDU-007: Video Player Advanced (554 LOC component)
- ET-MT4-001: WebSocket Integration (BLOCKER - 0% implemented)
- ET-ML-009: Ensemble Signal (Multi-strategy aggregation)
- ET-TRD-009: Risk-Based Position Sizer (391 LOC component)
- ET-TRD-010: Drawing Tools Persistence (backend + store)
- ET-TRD-011: Market Bias Indicator (multi-timeframe analysis)
- ET-PFM-009: Custom Charts (SVG AllocationChart + Canvas PerformanceChart)
- ET-ML-008: ICT Analysis Card (expanded - 294 LOC component)

All specs include:
- Architecture diagrams
- Complete code examples
- API contracts
- Implementation guides
- Testing scenarios

Related: TASK-2026-01-25-002-FRONTEND-COMPREHENSIVE-AUDIT
Priority: P1-P3 (mixed)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 14:20:53 -06:00

28 KiB

ET-MT4-001: WebSocket Integration for MT4 Gateway

Versión: 1.0.0 Fecha: 2026-01-25 Epic: OQI-009 - MT4 Trading Gateway Componente: Backend + Frontend WebSocket Estado: NO IMPLEMENTADO (0% - BLOCKER P0) Prioridad: P0 (Feature vendida sin implementar)


Metadata

Campo Valor
ID ET-MT4-001
Tipo Especificación Técnica
Epic OQI-009
Estado Actual BLOCKER - 0% funcional
Impacto 🔴 CRÍTICO - Feature vendida a clientes
Esfuerzo Estimado 180 horas (~1 mes, 2 devs)
Dependencias MT4 Expert Advisor, Backend FastAPI, Frontend React

1. Descripción General

MT4 WebSocket Integration es el sistema de comunicación en tiempo real entre terminales MetaTrader 4 (MT4) y la plataforma trading-platform. Permite visualizar posiciones activas, ejecutar trades, recibir cotizaciones, y sincronizar estado de cuenta en tiempo real.

Estado Actual: BLOCKER CRÍTICO

Frontend Components:
  - MT4ConnectionStatus.tsx        → 0% funcional (solo stub)
  - MT4LiveTradesPanel.tsx         → 0% NO EXISTE
  - MT4PositionsManager.tsx        → 0% NO EXISTE

Backend Services:
  - MT4 Gateway                    → 0% NO IMPLEMENTADO
  - WebSocket Server               → 0% NO IMPLEMENTADO
  - MT4 Expert Advisor (EA)        → 0% NO IMPLEMENTADO

Total Implementation: 0%

⚠️ IMPACTO COMERCIAL: Esta feature fue vendida a clientes pero NO está implementada. Es el gap más crítico identificado en la auditoría.


2. Arquitectura Propuesta

2.1 Visión General

┌────────────────────────────────────────────────────────────────────┐
│               MT4 WebSocket Integration Architecture               │
├────────────────────────────────────────────────────────────────────┤
│                                                                    │
│  ┌──────────────┐                                                  │
│  │ MT4 Terminal │  (Usuario ejecuta trades en MT4)                │
│  │  + EA Plugin │                                                  │
│  └──────┬───────┘                                                  │
│         │                                                           │
│         │ 1. WebSocket Connection (Bidirectional)                 │
│         │    ws://localhost:8090/mt4/agent_1                      │
│         v                                                           │
│  ┌──────────────────────────────────────────────────────────────┐ │
│  │              MT4 Gateway (Python FastAPI)                     │ │
│  │                                                                │ │
│  │  ┌─────────────┐  ┌──────────────┐  ┌─────────────────────┐  │ │
│  │  │  WebSocket  │  │  MT4 Parser  │  │  Position Manager  │  │ │
│  │  │   Server    │  │              │  │                     │  │ │
│  │  └──────┬──────┘  └──────┬───────┘  └──────┬──────────────┘  │ │
│  │         │                │                  │                  │ │
│  │         └────────────────┴──────────────────┘                  │ │
│  │                          │                                      │ │
│  └──────────────────────────┼──────────────────────────────────────┘
│                             │                                       │
│                             │ 2. Forward via WebSocket              │
│                             │    ws://trading-platform:3082/mt4    │
│                             v                                       │
│  ┌──────────────────────────────────────────────────────────────┐ │
│  │          Backend WebSocket Server (Express/Socket.io)        │ │
│  │                                                                │ │
│  │  ┌─────────────┐  ┌──────────────┐  ┌─────────────────────┐  │ │
│  │  │  WS Router  │  │  Auth Layer  │  │   Broadcast Hub     │  │ │
│  │  └──────┬──────┘  └──────┬───────┘  └──────┬──────────────┘  │ │
│  │         │                │                  │                  │ │
│  │         └────────────────┴──────────────────┘                  │ │
│  │                          │                                      │ │
│  └──────────────────────────┼──────────────────────────────────────┘
│                             │                                       │
│                             │ 3. Real-time updates                 │
│                             │    WS event: mt4_position_update     │
│                             v                                       │
│  ┌──────────────────────────────────────────────────────────────┐ │
│  │              Frontend React App                              │ │
│  │                                                                │ │
│  │  ┌──────────────────┐  ┌─────────────────────────────────┐   │ │
│  │  │ useMT4WebSocket  │  │  MT4LiveTradesPanel.tsx         │   │ │
│  │  │   (custom hook)  │  │  MT4PositionsManager.tsx        │   │ │
│  │  │                  │  │  MT4ConnectionStatus.tsx        │   │ │
│  │  └────────┬─────────┘  └──────────┬──────────────────────┘   │ │
│  │           │                       │                           │ │
│  │           └───────────────────────┘                           │ │
│  │                    │                                           │ │
│  │                    v                                           │ │
│  │            ┌──────────────┐                                    │ │
│  │            │  mt4Store    │  (Zustand state)                  │ │
│  │            └──────────────┘                                    │ │
│  └──────────────────────────────────────────────────────────────┘ │
│                                                                    │
└────────────────────────────────────────────────────────────────────┘

2.2 Componentes del Sistema

Componente Tecnología Estado Esfuerzo
MT4 Expert Advisor MQL4 No existe 60h
MT4 Gateway (Python) FastAPI + WebSockets No existe 40h
Backend WS Server Express + Socket.io No existe 30h
Frontend Components React + WS hooks ⚠️ Stubs (0% funcional) 30h
Zustand Store (mt4Store) Zustand No existe 10h
Tests Pytest + Vitest No existe 10h

Total: 180 horas


3. MT4 Expert Advisor (MQL4)

3.1 Responsabilidades

  • Conectar a MT4 Gateway vía WebSocket
  • Enviar eventos de trades (open, close, modify)
  • Enviar heartbeat cada 5s (keep-alive)
  • Recibir comandos remotos (ejecutar trade desde plataforma)

3.2 Eventos Enviados al Gateway

// Event: position_opened
{
  "type": "position_opened",
  "timestamp": "2026-01-25T10:30:15Z",
  "data": {
    "ticket": 123456,
    "symbol": "BTCUSD",
    "type": "buy",
    "lots": 0.1,
    "open_price": 89450.00,
    "stop_loss": 89150.00,
    "take_profit": 89850.00,
    "magic_number": 42,
    "comment": "Manual trade"
  }
}

// Event: position_closed
{
  "type": "position_closed",
  "timestamp": "2026-01-25T11:45:20Z",
  "data": {
    "ticket": 123456,
    "close_price": 89650.00,
    "profit": 200.00,
    "commission": -2.50,
    "swap": 0.00,
    "net_profit": 197.50
  }
}

// Event: account_update
{
  "type": "account_update",
  "timestamp": "2026-01-25T10:30:15Z",
  "data": {
    "balance": 10000.00,
    "equity": 10197.50,
    "margin": 895.00,
    "margin_free": 9302.50,
    "margin_level": 1139.27
  }
}

// Event: heartbeat
{
  "type": "heartbeat",
  "timestamp": "2026-01-25T10:30:15Z",
  "agent_id": "agent_1",
  "status": "connected"
}

3.3 Comandos Recibidos del Gateway

// Command: execute_trade
{
  "command": "execute_trade",
  "request_id": "uuid-1234",
  "data": {
    "symbol": "BTCUSD",
    "type": "buy",
    "lots": 0.1,
    "stop_loss": 89150.00,
    "take_profit": 89850.00,
    "comment": "From trading-platform"
  }
}

// Response: trade_executed
{
  "type": "trade_executed",
  "request_id": "uuid-1234",
  "success": true,
  "data": {
    "ticket": 123457,
    "open_price": 89450.00
  }
}

// Command: modify_position
{
  "command": "modify_position",
  "request_id": "uuid-1235",
  "data": {
    "ticket": 123456,
    "stop_loss": 89200.00,
    "take_profit": 89900.00
  }
}

// Command: close_position
{
  "command": "close_position",
  "request_id": "uuid-1236",
  "data": {
    "ticket": 123456
  }
}

3.4 Código MQL4 (Pseudocódigo)

//+------------------------------------------------------------------+
//|                                      TradingPlatform_EA.mq4     |
//|                              Copyright 2026, Trading Platform   |
//+------------------------------------------------------------------+

#property strict

// WebSocket library (external)
#include <WebSocket.mqh>

// Configuration
input string GatewayURL = "ws://localhost:8090/mt4/agent_1";
input string AgentID = "agent_1";
input int HeartbeatInterval = 5000; // 5 seconds

WebSocket ws;
datetime lastHeartbeat = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
  // Connect to MT4 Gateway
  if (!ws.Connect(GatewayURL)) {
    Print("Failed to connect to MT4 Gateway");
    return INIT_FAILED;
  }

  Print("Connected to MT4 Gateway: ", GatewayURL);
  return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
  // Send heartbeat every 5 seconds
  if (TimeCurrent() - lastHeartbeat > HeartbeatInterval / 1000) {
    SendHeartbeat();
    lastHeartbeat = TimeCurrent();
  }

  // Process incoming commands from gateway
  string command = ws.Receive();
  if (StringLen(command) > 0) {
    ProcessCommand(command);
  }
}

//+------------------------------------------------------------------+
//| Trade transaction event                                          |
//+------------------------------------------------------------------+
void OnTrade() {
  // Detect new positions
  for (int i = 0; i < OrdersTotal(); i++) {
    if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
      if (OrderOpenTime() > lastPositionCheck) {
        SendPositionOpened(OrderTicket());
      }
    }
  }

  // Detect closed positions
  for (int i = 0; i < OrdersHistoryTotal(); i++) {
    if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) {
      if (OrderCloseTime() > lastPositionCheck) {
        SendPositionClosed(OrderTicket());
      }
    }
  }

  lastPositionCheck = TimeCurrent();
}

//+------------------------------------------------------------------+
//| Send position opened event                                       |
//+------------------------------------------------------------------+
void SendPositionOpened(int ticket) {
  if (!OrderSelect(ticket, SELECT_BY_TICKET)) return;

  string json = StringFormat(
    "{\"type\":\"position_opened\",\"timestamp\":\"%s\",\"data\":{\"ticket\":%d,\"symbol\":\"%s\",\"type\":\"%s\",\"lots\":%.2f,\"open_price\":%.5f,\"stop_loss\":%.5f,\"take_profit\":%.5f}}",
    TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS),
    ticket,
    OrderSymbol(),
    OrderType() == OP_BUY ? "buy" : "sell",
    OrderLots(),
    OrderOpenPrice(),
    OrderStopLoss(),
    OrderTakeProfit()
  );

  ws.Send(json);
}

//+------------------------------------------------------------------+
//| Process command from gateway                                     |
//+------------------------------------------------------------------+
void ProcessCommand(string command) {
  // Parse JSON command
  string cmdType = ParseJSONString(command, "command");

  if (cmdType == "execute_trade") {
    ExecuteTradeCommand(command);
  }
  else if (cmdType == "modify_position") {
    ModifyPositionCommand(command);
  }
  else if (cmdType == "close_position") {
    ClosePositionCommand(command);
  }
}

//+------------------------------------------------------------------+

4. MT4 Gateway (Python FastAPI)

4.1 Responsabilidades

  • Recibir conexiones WebSocket de MT4 Expert Advisors
  • Parsear eventos MQL4 a JSON estructurado
  • Reenviar eventos a Backend WebSocket Server
  • Recibir comandos desde Backend y enviarlos a MT4

4.2 Estructura de Proyecto

apps/mt4-gateway/
├── main.py                      # FastAPI app
├── websocket/
│   ├── __init__.py
│   ├── mt4_handler.py           # Maneja conexiones MT4
│   ├── backend_forwarder.py     # Reenvía a backend
│   └── parser.py                # Parse MQL4 JSON
├── models/
│   ├── events.py                # Pydantic models para eventos
│   └── commands.py              # Pydantic models para comandos
├── config.py
├── requirements.txt
└── tests/
    └── test_websocket.py

4.3 Código Python (FastAPI)

# main.py
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from typing import Dict
import asyncio
import websockets
import json

app = FastAPI(title="MT4 Gateway")

# Active MT4 connections
active_connections: Dict[str, WebSocket] = {}

# Backend WebSocket URL
BACKEND_WS_URL = "ws://localhost:3082/mt4"
backend_ws = None

@app.on_event("startup")
async def startup():
    """Connect to backend WebSocket on startup"""
    global backend_ws
    try:
        backend_ws = await websockets.connect(BACKEND_WS_URL)
        print(f"Connected to backend: {BACKEND_WS_URL}")
    except Exception as e:
        print(f"Failed to connect to backend: {e}")

@app.websocket("/mt4/{agent_id}")
async def mt4_websocket(websocket: WebSocket, agent_id: str):
    """
    Handle WebSocket connection from MT4 Expert Advisor
    """
    await websocket.accept()
    active_connections[agent_id] = websocket

    print(f"MT4 Agent {agent_id} connected")

    try:
        while True:
            # Receive event from MT4
            data = await websocket.receive_text()
            event = json.loads(data)

            print(f"Received from MT4 {agent_id}: {event['type']}")

            # Add agent_id to event
            event['agent_id'] = agent_id

            # Forward to backend
            if backend_ws:
                await backend_ws.send(json.dumps(event))

    except WebSocketDisconnect:
        print(f"MT4 Agent {agent_id} disconnected")
        del active_connections[agent_id]

@app.websocket("/commands")
async def commands_websocket(websocket: WebSocket):
    """
    Receive commands from backend and forward to MT4
    """
    await websocket.accept()

    try:
        while True:
            # Receive command from backend
            data = await websocket.receive_text()
            command = json.loads(data)

            agent_id = command.get('agent_id')
            if agent_id in active_connections:
                # Forward command to MT4
                mt4_ws = active_connections[agent_id]
                await mt4_ws.send_text(json.dumps(command))
            else:
                print(f"Agent {agent_id} not connected")

    except WebSocketDisconnect:
        print("Backend commands channel disconnected")

# Health check
@app.get("/health")
async def health():
    return {
        "status": "ok",
        "active_agents": len(active_connections),
        "backend_connected": backend_ws is not None
    }

5. Backend WebSocket Server (Express)

5.1 Estructura

apps/backend/src/websocket/
├── server.ts                    # WebSocket server setup
├── handlers/
│   ├── mt4Handler.ts            # Handle MT4 events
│   ├── authHandler.ts           # Authenticate connections
│   └── broadcastHandler.ts      # Broadcast to clients
├── middleware/
│   └── authMiddleware.ts        # JWT validation
└── types/
    └── mt4Events.ts             # TypeScript interfaces

5.2 Código TypeScript (Backend)

// server.ts
import { Server } from 'socket.io'
import { createServer } from 'http'
import express from 'express'
import { verifyJWT } from './middleware/authMiddleware'

const app = express()
const httpServer = createServer(app)
const io = new Server(httpServer, {
  cors: { origin: 'http://localhost:3000' }
})

// Namespace for MT4 events
const mt4Namespace = io.of('/mt4')

mt4Namespace.use((socket, next) => {
  // Authenticate client (frontend users)
  const token = socket.handshake.auth.token
  try {
    const user = verifyJWT(token)
    socket.data.user = user
    next()
  } catch (error) {
    next(new Error('Authentication failed'))
  }
})

mt4Namespace.on('connection', (socket) => {
  console.log(`Client connected: ${socket.data.user.id}`)

  // Join room based on user ID (to receive only their MT4 events)
  socket.join(`user_${socket.data.user.id}`)

  socket.on('disconnect', () => {
    console.log(`Client disconnected: ${socket.data.user.id}`)
  })
})

// Receive events from MT4 Gateway and broadcast to frontend
const WebSocket = require('ws')
const wss = new WebSocket.Server({ port: 3082 })

wss.on('connection', (ws) => {
  console.log('MT4 Gateway connected')

  ws.on('message', (message: string) => {
    const event = JSON.parse(message)

    console.log(`MT4 Event: ${event.type}`)

    // Determine which user to send to (based on agent_id → userId mapping)
    const userId = getU serIdByAgentId(event.agent_id)

    if (userId) {
      // Broadcast to specific user's room
      mt4Namespace.to(`user_${userId}`).emit('mt4_event', event)
    }
  })

  ws.on('close', () => {
    console.log('MT4 Gateway disconnected')
  })
})

httpServer.listen(3082, () => {
  console.log('WebSocket server running on port 3082')
})

6. Frontend Components (React)

6.1 Custom Hook: useMT4WebSocket

// hooks/useMT4WebSocket.ts
import { useEffect, useRef } from 'react'
import { io, Socket } from 'socket.io-client'
import { useMT4Store } from '@/stores/mt4.store'
import { useAuthStore } from '@/stores/auth.store'

export const useMT4WebSocket = () => {
  const socketRef = useRef<Socket | null>(null)
  const token = useAuthStore(state => state.token)
  const { addPosition, updatePosition, removePosition, updateAccount } = useMT4Store()

  useEffect(() => {
    if (!token) return

    // Connect to backend WebSocket
    const socket = io('ws://localhost:3082/mt4', {
      auth: { token }
    })

    socketRef.current = socket

    socket.on('connect', () => {
      console.log('MT4 WebSocket connected')
    })

    socket.on('mt4_event', (event) => {
      console.log('MT4 Event received:', event)

      switch (event.type) {
        case 'position_opened':
          addPosition(event.data)
          break

        case 'position_closed':
          removePosition(event.data.ticket)
          break

        case 'position_modified':
          updatePosition(event.data.ticket, event.data)
          break

        case 'account_update':
          updateAccount(event.data)
          break

        case 'heartbeat':
          // Update connection status
          useMT4Store.setState({ lastHeartbeat: new Date(event.timestamp) })
          break
      }
    })

    socket.on('disconnect', () => {
      console.log('MT4 WebSocket disconnected')
    })

    return () => {
      socket.disconnect()
    }
  }, [token])

  return socketRef.current
}

6.2 Zustand Store: mt4Store

// stores/mt4.store.ts
import { create } from 'zustand'

interface MT4Position {
  ticket: number
  symbol: string
  type: 'buy' | 'sell'
  lots: number
  open_price: number
  stop_loss: number
  take_profit: number
  profit: number
}

interface MT4Account {
  balance: number
  equity: number
  margin: number
  margin_free: number
  margin_level: number
}

interface MT4Store {
  // State
  positions: MT4Position[]
  account: MT4Account | null
  isConnected: boolean
  lastHeartbeat: Date | null

  // Actions
  addPosition: (position: MT4Position) => void
  updatePosition: (ticket: number, updates: Partial<MT4Position>) => void
  removePosition: (ticket: number) => void
  updateAccount: (account: MT4Account) => void
}

export const useMT4Store = create<MT4Store>((set) => ({
  positions: [],
  account: null,
  isConnected: false,
  lastHeartbeat: null,

  addPosition: (position) =>
    set((state) => ({
      positions: [...state.positions, position]
    })),

  updatePosition: (ticket, updates) =>
    set((state) => ({
      positions: state.positions.map((p) =>
        p.ticket === ticket ? { ...p, ...updates } : p
      )
    })),

  removePosition: (ticket) =>
    set((state) => ({
      positions: state.positions.filter((p) => p.ticket !== ticket)
    })),

  updateAccount: (account) => set({ account })
}))

6.3 Component: MT4LiveTradesPanel

// components/MT4LiveTradesPanel.tsx
import React from 'react'
import { useMT4WebSocket } from '@/hooks/useMT4WebSocket'
import { useMT4Store } from '@/stores/mt4.store'

export const MT4LiveTradesPanel: React.FC = () => {
  useMT4WebSocket() // Initialize WebSocket connection

  const positions = useMT4Store(state => state.positions)
  const account = useMT4Store(state => state.account)

  return (
    <div className="bg-gray-900 rounded-lg p-4">
      <h2 className="text-xl font-bold mb-4">MT4 Live Trades</h2>

      {/* Account Summary */}
      {account && (
        <div className="grid grid-cols-4 gap-4 mb-6">
          <div>
            <span className="text-gray-400">Balance</span>
            <p className="text-2xl font-bold">${account.balance.toFixed(2)}</p>
          </div>
          <div>
            <span className="text-gray-400">Equity</span>
            <p className="text-2xl font-bold">${account.equity.toFixed(2)}</p>
          </div>
          <div>
            <span className="text-gray-400">Margin</span>
            <p className="text-2xl">${account.margin.toFixed(2)}</p>
          </div>
          <div>
            <span className="text-gray-400">Free Margin</span>
            <p className="text-2xl">${account.margin_free.toFixed(2)}</p>
          </div>
        </div>
      )}

      {/* Positions Table */}
      <table className="w-full">
        <thead>
          <tr className="text-left border-b border-gray-700">
            <th>Ticket</th>
            <th>Symbol</th>
            <th>Type</th>
            <th>Lots</th>
            <th>Open Price</th>
            <th>S/L</th>
            <th>T/P</th>
            <th>Profit</th>
          </tr>
        </thead>
        <tbody>
          {positions.map((position) => (
            <tr key={position.ticket} className="border-b border-gray-800">
              <td>{position.ticket}</td>
              <td className="font-mono">{position.symbol}</td>
              <td>
                <span className={position.type === 'buy' ? 'text-green-500' : 'text-red-500'}>
                  {position.type.toUpperCase()}
                </span>
              </td>
              <td>{position.lots.toFixed(2)}</td>
              <td>{position.open_price.toFixed(5)}</td>
              <td>{position.stop_loss.toFixed(5)}</td>
              <td>{position.take_profit.toFixed(5)}</td>
              <td className={position.profit >= 0 ? 'text-green-500' : 'text-red-500'}>
                ${position.profit.toFixed(2)}
              </td>
            </tr>
          ))}

          {positions.length === 0 && (
            <tr>
              <td colSpan={8} className="text-center py-8 text-gray-500">
                No active positions
              </td>
            </tr>
          )}
        </tbody>
      </table>
    </div>
  )
}

7. Plan de Implementación (180h)

Fase 1: MT4 Expert Advisor (60h)

  • Investigar librerías WebSocket para MQL4 (10h)
  • Implementar conexión WebSocket (15h)
  • Implementar eventos (position_opened, closed, etc.) (20h)
  • Implementar comandos remotos (execute_trade, etc.) (10h)
  • Testing en demo account (5h)

Fase 2: MT4 Gateway (Python) (40h)

  • Setup FastAPI project (5h)
  • Implementar WebSocket handler para MT4 (10h)
  • Implementar forwarder a backend (10h)
  • Parsers y validación de eventos (5h)
  • Testing + error handling (10h)

Fase 3: Backend WebSocket Server (30h)

  • Setup Socket.io en Express (5h)
  • Implementar MT4 namespace (10h)
  • Implementar auth middleware JWT (5h)
  • Implementar broadcast logic (5h)
  • Testing + integración (5h)

Fase 4: Frontend Components (30h)

  • Crear mt4Store (Zustand) (5h)
  • Implementar useMT4WebSocket hook (8h)
  • Implementar MT4LiveTradesPanel (8h)
  • Implementar MT4PositionsManager (5h)
  • Implementar MT4ConnectionStatus (4h)

Fase 5: Testing E2E (20h)

  • Setup test environment (5h)
  • Tests MT4 → Gateway (5h)
  • Tests Gateway → Backend (5h)
  • Tests Backend → Frontend (5h)

8. Referencias


Última actualización: 2026-01-25 Estado: BLOCKER P0 - Requiere implementación urgente Responsable: Backend Lead + MT4 Specialist