En esta página
Volver a tutoriales

Crea tu Primer EA para MT5 (MQL5): Ejemplo Paso a Paso

Práctica: construye un EA de cruce de medias móviles desde cero. De la idea al código y luego a probar en el Probador. Reglas de entrada/salida claras y lógica que puedes ampliar.

📖 38 min read

📝 7,500 words

🏷️ MQL5 & Expert Advisors

Compartir este artículo:

¿Quieres crear una estrategia sin código ahora?

Crea tu cuenta gratis en segundos y empieza de inmediato.


Construye tu Primer Asesor Experto — Cruce de Medias Móviles Paso a Paso

Ya tienes el entorno configurado y la referencia MQL5 a mano. Ahora es momento de construir un EA real: una estrategia de cruce de medias móviles. Al final de este artículo tendrás un EA completo de cruce MA que compila, se ejecuta en el Probador de Estrategias y coloca órdenes de compra/venta reales — con reglas claras de entrada/salida y lógica que puedes ampliar. Este tutorial te lleva de la idea al código en ocho pasos concretos.

Todo el código sigue la documentación oficial de manejadores de eventos MQL5 y funciones de trading.


La Estrategia — Cruce de Medias Móviles

Idea: Usar dos medias móviles — una rápida (ej. 20 períodos) y una lenta (ej. 50 períodos).

  • Señal de compra: La MA rápida cruza por encima de la lenta
  • Señal de venta: La MA rápida cruza por debajo de la lenta

Es un enfoque clásico de seguimiento de tendencia. Operamos en la dirección del cruce.


Paso 1: Definir la Lógica

Antes de escribir una sola línea de MQL5, debes concretar tu lógica de trading. Una especificación clara evita errores y facilita el depurado. Para nuestro EA de cruce de MA, definimos cada elección explícitamente.

ComponenteElecciónPor qué esta elección
MA rápidaEMA 20 períodos, CierreLa EMA reacciona más rápido que la SMA a precios recientes — ideal para cruces. Cierre es estándar y muy usado en backtests.
MA lentaEMA 50 períodos, Cierre50 es un período "medio plazo" habitual; suaviza el ruido y confirma la dirección de la tendencia.
EntradaUna posición a la vez; señal nueva cierra la opuesta y abre nuevaMantiene el riesgo simple: sin posiciones superpuestas. Al nuevo cruce, cerramos la posición opuesta y abrimos la nueva.
SalidaCruce opuesto cierra la posiciónNo hay regla de salida aparte — el siguiente cruce opuesto cierra la operación actual y abre la contraria.

Consejo: Documenta así tu lógica en cada EA. Tu yo futuro (y Google) te lo agradecerán cuando revises el código meses después.


Paso 2: Crear el Archivo del EA

Crea un nuevo Asesor Experto desde la plantilla de MetaEditor para obtener la estructura correcta (OnInit, OnTick, OnDeinit) desde el inicio.

  1. En MetaEditor, ve a Archivo — Nuevo y elige Asesor Experto (plantilla).
  2. Cuando lo pida, nómbralo MACrossoverEA y deja los parámetros por defecto — el asistente crea el esqueleto.
  3. Guarda el archivo en MQL5\Experts. MetaTrader 5 busca los EAs en esta carpeta. Si guardas en otro sitio, el EA no aparecerá en el Navegador.

Estructura de carpetas: MetaTrader 5 se instala en una ruta como esta:

C:\Users\TuNombre\AppData\Roaming\MetaQuotes\Terminal\...\MQL5\Experts

Tu archivo MACrossoverEA.mq5 debe estar ahí (o en una subcarpeta) para compilarse y ejecutarse.


Paso 3: Entradas y Handles

Declaramos dos tipos de variables: inputs (configurables por el usuario) y handles (referencias a instancias del indicador).

Los inputs aparecen en el diálogo de propiedades del EA y en el Probador de Estrategias. El usuario puede cambiar períodos y lote sin tocar el código. La palabra clave input los hace persistentes.

Los handles son IDs enteros devueltos por iMA(). Apuntan a los buffers internos del indicador. Los creamos una vez en OnInit() y los reutilizamos en OnTick() con CopyBuffer(). Nunca crees handles dentro de OnTick() — eso asignaría nuevos indicadores en cada tick y dañaría el rendimiento.

Las directivas #property definen metadatos mostrados en MetaTrader (copyright, versión). Son opcionales pero buena práctica en EAs profesionales.

#property copyright "AlfaTactix Academy"
#property version   "1.00"

input int    InpFastPeriod = 20;     // Período MA rápida
input int    InpSlowPeriod = 50;     // Período MA lenta
input double InpLotSize    = 0.1;    // Tamaño del lote
input int    InpMagic      = 12345;  // Número mágico

int g_fastHandle = INVALID_HANDLE;
int g_slowHandle = INVALID_HANDLE;

Paso 4: OnInit — Crear Handles de Indicadores

OnInit() se ejecuta una vez cuando el EA se adjunta al gráfico (o cuando arranca el Probador de Estrategias). Según la documentación OnInit, aquí se crean los handles de indicadores, se validan los inputs y se preparan los recursos.

Validación de inputs: Rechazamos períodos inválidos (p.ej. rápida ≥ lenta, o cero/negativos) y devolvemos INIT_PARAMETERS_INCORRECT. Así el EA se detiene limpio y muestra un error en la pestaña Expertos en vez de fallar más tarde.

Parámetros de iMA: iMA(symbol, timeframe, period, shift, method, applied_price). Usamos PERIOD_CURRENT para que el EA siga el timeframe del gráfico, MODE_EMA para la media exponencial y PRICE_CLOSE para precios de cierre. Si iMA() devuelve INVALID_HANDLE, el terminal registra el error y devolvemos INIT_FAILED.

OnDeinit: Al quitar el EA (gráfico cerrado, probador terminado), OnDeinit() libera los handles con IndicatorRelease(). Así se libera memoria y se evitan fugas — imprescindible al ejecutar muchos backtests.

int OnInit()
{
   if(InpFastPeriod <= 0 || InpSlowPeriod <= 0 || InpFastPeriod >= InpSlowPeriod)
   {
      Print("Períodos de MA inválidos. Rápida debe ser < Lenta.");
      return(INIT_PARAMETERS_INCORRECT);
   }

   g_fastHandle = iMA(_Symbol, PERIOD_CURRENT, InpFastPeriod, 0, MODE_EMA, PRICE_CLOSE);
   g_slowHandle = iMA(_Symbol, PERIOD_CURRENT, InpSlowPeriod, 0, MODE_EMA, PRICE_CLOSE);

   if(g_fastHandle == INVALID_HANDLE || g_slowHandle == INVALID_HANDLE)
   {
      Print("Error al crear handles de MA. Error: ", GetLastError());
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   if(g_fastHandle != INVALID_HANDLE) IndicatorRelease(g_fastHandle);
   if(g_slowHandle != INVALID_HANDLE) IndicatorRelease(g_slowHandle);
}

Paso 5: OnTick — Detectar Cruce y Operar

OnTick() se llama en cada tick de precio. Eso puede ser cientos de veces por vela. Si evaluamos señales en cada tick, corremos el riesgo de órdenes duplicadas y múltiples entradas en la misma barra. La solución: procesar señales solo cuando se forma una nueva barra.

Comprobación de nueva barra: Comparamos iTime(_Symbol, PERIOD_CURRENT, 0) (tiempo de apertura de la barra actual) con una variable static lastBar. Cuando difieren, se ha formado una nueva barra. Actualizamos lastBar y seguimos; si no, salimos de inmediato. Es un patrón ligero y fiable usado en EAs profesionales.

¿Por qué tres barras? Para detectar un cruce necesitamos el estado antes y después del cruce. Con ArraySetAsSeries(buffer, true), índice 0 = barra actual, 1 = barra anterior (cerrada), 2 = barra anterior a esa. El cruce ocurre entre las barras 2 y 1:

  • Compra: En barra 1 la MA rápida está por encima de la lenta (fast[1] > slow[1]), pero en barra 2 estaba por debajo o igual (fast[2] <= slow[2]). Eso significa que la rápida cruzó por encima de la lenta.
  • Venta: Simétricamente, fast[1] < slow[1] y fast[2] >= slow[2] indica que la rápida cruzó por debajo.

CopyBuffer: Pedimos 3 elementos (índices 0, 1, 2). Si CopyBuffer devuelve menos de 3, aún no hay suficiente historial — salimos y esperamos al siguiente tick. En un gráfico reciente, las primeras barras pueden no tener datos suficientes; por eso este guarda es esencial.

Flujo: En señal de compra cerramos primero las ventas, luego abrimos compra si no tenemos ya una. Igual para venta. Así mantenemos la regla de "una posición por dirección".

void OnTick()
{
   static datetime lastBar = 0;
   datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(currentBar == lastBar) return;  // Esperar nueva barra
   lastBar = currentBar;

   double fast[], slow[];
   ArraySetAsSeries(fast, true);
   ArraySetAsSeries(slow, true);

   if(CopyBuffer(g_fastHandle, 0, 0, 3, fast) < 3) return;
   if(CopyBuffer(g_slowHandle, 0, 0, 3, slow) < 3) return;

   bool buySignal  = (fast[1] > slow[1]) && (fast[2] <= slow[2]);
   bool sellSignal = (fast[1] < slow[1]) && (fast[2] >= slow[2]);

   if(buySignal)
   {
      ClosePositions(POSITION_TYPE_SELL);
      if(CountPositions(POSITION_TYPE_BUY) == 0)
         OpenBuy();
   }
   else if(sellSignal)
   {
      ClosePositions(POSITION_TYPE_BUY);
      if(CountPositions(POSITION_TYPE_SELL) == 0)
         OpenSell();
   }
}

Paso 6: OpenBuy y OpenSell — OrderSend

OrderSend() envía una solicitud de trading al bróker. Rellenas la estructura MqlTradeRequest y opcionalmente recibes el resultado en MqlTradeResult.

Campos clave explicados:

  • action = TRADE_ACTION_DEAL — Ejecución inmediata a mercado (no orden pendiente).
  • symbol — El par de trading; _Symbol es el símbolo del gráfico.
  • volume — Tamaño del lote; debe cumplir SYMBOL_VOLUME_MIN y SYMBOL_VOLUME_STEP.
  • typeORDER_TYPE_BUY o ORDER_TYPE_SELL.
  • price — Para órdenes a mercado: usa Ask para compras (pagas el ask) y Bid para ventas (recibes el bid). SymbolInfoDouble(_Symbol, SYMBOL_ASK/BID) devuelve el precio actual.
  • deviation — Deslizamiento máximo permitido en puntos. 10 puntos es un valor por defecto habitual; aumenta en símbolos volátiles si ves requotes.
  • magic — Identificador de tu EA. Úsalo para filtrar posiciones y órdenes en PositionSelect, OrderSelect, etc.

Manejo de errores: Si OrderSend() devuelve false, GetLastError() da la razón (p.ej. margen insuficiente, mercado cerrado, requote). Siempre regístralo para depurar.

void OpenBuy()
{
   MqlTradeRequest req = {};
   MqlTradeResult  res = {};

   req.action   = TRADE_ACTION_DEAL;
   req.symbol   = _Symbol;
   req.volume   = InpLotSize;
   req.type     = ORDER_TYPE_BUY;
   req.price    = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   req.deviation = 10;
   req.magic    = InpMagic;

   if(!OrderSend(req, res))
      Print("OpenBuy falló: ", GetLastError());
}

void OpenSell()
{
   MqlTradeRequest req = {};
   MqlTradeResult  res = {};

   req.action   = TRADE_ACTION_DEAL;
   req.symbol   = _Symbol;
   req.volume   = InpLotSize;
   req.type     = ORDER_TYPE_SELL;
   req.price    = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   req.deviation = 10;
   req.magic    = InpMagic;

   if(!OrderSend(req, res))
      Print("OpenSell falló: ", GetLastError());
}

Paso 7: Cerrar Posiciones y Contar por Magic

En MetaTrader 5, una cuenta puede tener posiciones de varios EAs y operaciones manuales. Debemos filtrar por símbolo y número mágico para que nuestro EA gestione solo sus propias posiciones.

CountPositions: Recorre PositionsTotal() — el número de posiciones abiertas. PositionGetTicket(i) devuelve el ticket; PositionGetString(POSITION_SYMBOL), PositionGetInteger(POSITION_MAGIC) y PositionGetInteger(POSITION_TYPE) identifican cada posición. Contamos solo las que coinciden con nuestro símbolo, magic y tipo (BUY o SELL). Así evitamos abrir una segunda compra cuando ya tenemos una.

ClosePositions: Para cerrar una posición enviamos una contra-orden — venta para cerrar compra, o compra para cerrar venta. Configuramos req.action = TRADE_ACTION_DEAL, req.position = ticket (la posición a cerrar) y req.type / req.price al lado opuesto. req.volume debe igualar el volumen de la posición (cerramos toda). El magic asegura que la orden de cierre se atribuya a nuestro EA.

¿Por qué iterar hacia atrás? Al modificar la lista de posiciones (p.ej. cerrar una), los índices pueden desplazarse. Iterar de PositionsTotal()-1 a 0 evita saltar o procesar dos veces.

int CountPositions(ENUM_POSITION_TYPE type)
{
   int count = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != InpMagic) continue;
      if(PositionGetInteger(POSITION_TYPE) != type) continue;
      count++;
   }
   return count;
}

void ClosePositions(ENUM_POSITION_TYPE type)
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != InpMagic) continue;
      if(PositionGetInteger(POSITION_TYPE) != type) continue;

      MqlTradeRequest req = {};
      MqlTradeResult  res = {};
      req.action   = TRADE_ACTION_DEAL;
      req.position = ticket;
      req.symbol   = _Symbol;
      req.volume   = PositionGetDouble(POSITION_VOLUME);
      req.deviation = 10;
      req.magic    = InpMagic;
      req.type     = (type == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
      req.price    = (type == POSITION_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK);

      OrderSend(req, res);
   }
}

Paso 8: Compilar y Probar

1. Compilar. En MetaEditor, pulsa F7 (o Compilar en el menú Compilar). El panel de salida muestra errores y advertencias. Corrige los errores antes de seguir. Las advertencias (p.ej. variables no usadas) son opcionales pero conviene limpiarlas para código de producción.

2. Abrir el Probador de Estrategias. En MetaTrader 5, ve a Ver — Probador de Estrategias (o pulsa Ctrl+R). El panel aparece en la parte inferior.

3. Configurar la prueba. Selecciona tu EA (MACrossoverEA) en el desplegable. Elige símbolo (p.ej. EURUSD), timeframe (H1 recomendado para cruces) y rango de fechas (p.ej. últimos 12 meses). Ajusta depósito y apalancamiento si hace falta. Activa Modo visual si quieres ver el EA operar barra a barra.

4. Elegir calidad de modelado. "Cada tick" es lo más preciso pero más lento. "1 minuto OHLC" usa un tick por minuto y ofrece buen equilibrio velocidad/realismo para estrategias diarias/horarias. "Solo precios de apertura" es lo más rápido pero menos preciso — úsalo solo para comprobaciones rápidas.

5. Ejecutar la prueba. Haz clic en Iniciar. Al terminar, revisa la pestaña Resultados (beneficio, drawdown, operaciones) y la Diario (salida de Print() y errores). La pestaña Gráfico muestra la curva de equity. Usa esto para validar que tu EA se comporta como esperas antes de plantear Demo o Live.


Resumen — Lo Que Construiste

ParteFunción
OnInitCrear handles iMA, validar inputs
OnDeinitLiberar handles
OnTickComprobar nueva barra, lógica de cruce, abrir/cerrar
OpenBuy/OpenSellOrderSend con MqlTradeRequest
ClosePositionsCerrar por símbolo + magic + tipo
CountPositionsEvitar posiciones duplicadas

Código Completo

Aquí tienes el EA completo en un solo bloque para copiar y verificar. Ensámblalo en MetaEditor y guárdalo como MACrossoverEA.mq5 en MQL5\Experts.

//+------------------------------------------------------------------+
//|                                                MACrossoverEA.mq5 |
//|                                    EA de Cruce de Medias Móviles |
//+------------------------------------------------------------------+
#property copyright "AlfaTactix Academy"
#property version   "1.00"

input int    InpFastPeriod = 20;     // Período MA rápida
input int    InpSlowPeriod = 50;     // Período MA lenta
input double InpLotSize    = 0.1;    // Tamaño del lote
input int    InpMagic      = 12345;  // Número mágico

int g_fastHandle = INVALID_HANDLE;
int g_slowHandle = INVALID_HANDLE;

int OnInit()
{
   if(InpFastPeriod <= 0 || InpSlowPeriod <= 0 || InpFastPeriod >= InpSlowPeriod)
   {
      Print("Períodos de MA inválidos. Rápida debe ser < Lenta.");
      return(INIT_PARAMETERS_INCORRECT);
   }
   g_fastHandle = iMA(_Symbol, PERIOD_CURRENT, InpFastPeriod, 0, MODE_EMA, PRICE_CLOSE);
   g_slowHandle = iMA(_Symbol, PERIOD_CURRENT, InpSlowPeriod, 0, MODE_EMA, PRICE_CLOSE);
   if(g_fastHandle == INVALID_HANDLE || g_slowHandle == INVALID_HANDLE)
   {
      Print("Error al crear handles de MA. Error: ", GetLastError());
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   if(g_fastHandle != INVALID_HANDLE) IndicatorRelease(g_fastHandle);
   if(g_slowHandle != INVALID_HANDLE) IndicatorRelease(g_slowHandle);
}

void OnTick()
{
   static datetime lastBar = 0;
   datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(currentBar == lastBar) return;
   lastBar = currentBar;

   double fast[], slow[];
   ArraySetAsSeries(fast, true);
   ArraySetAsSeries(slow, true);
   if(CopyBuffer(g_fastHandle, 0, 0, 3, fast) < 3) return;
   if(CopyBuffer(g_slowHandle, 0, 0, 3, slow) < 3) return;

   bool buySignal  = (fast[1] > slow[1]) && (fast[2] <= slow[2]);
   bool sellSignal = (fast[1] < slow[1]) && (fast[2] >= slow[2]);

   if(buySignal)
   {
      ClosePositions(POSITION_TYPE_SELL);
      if(CountPositions(POSITION_TYPE_BUY) == 0) OpenBuy();
   }
   else if(sellSignal)
   {
      ClosePositions(POSITION_TYPE_BUY);
      if(CountPositions(POSITION_TYPE_SELL) == 0) OpenSell();
   }
}

void OpenBuy()
{
   MqlTradeRequest req = {};
   MqlTradeResult  res = {};
   req.action   = TRADE_ACTION_DEAL;
   req.symbol   = _Symbol;
   req.volume   = InpLotSize;
   req.type     = ORDER_TYPE_BUY;
   req.price    = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   req.deviation = 10;
   req.magic    = InpMagic;
   if(!OrderSend(req, res)) Print("OpenBuy falló: ", GetLastError());
}

void OpenSell()
{
   MqlTradeRequest req = {};
   MqlTradeResult  res = {};
   req.action   = TRADE_ACTION_DEAL;
   req.symbol   = _Symbol;
   req.volume   = InpLotSize;
   req.type     = ORDER_TYPE_SELL;
   req.price    = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   req.deviation = 10;
   req.magic    = InpMagic;
   if(!OrderSend(req, res)) Print("OpenSell falló: ", GetLastError());
}

int CountPositions(ENUM_POSITION_TYPE type)
{
   int count = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != InpMagic) continue;
      if(PositionGetInteger(POSITION_TYPE) != type) continue;
      count++;
   }
   return count;
}

void ClosePositions(ENUM_POSITION_TYPE type)
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != InpMagic) continue;
      if(PositionGetInteger(POSITION_TYPE) != type) continue;

      MqlTradeRequest req = {};
      MqlTradeResult  res = {};
      req.action   = TRADE_ACTION_DEAL;
      req.position = ticket;
      req.symbol   = _Symbol;
      req.volume   = PositionGetDouble(POSITION_VOLUME);
      req.deviation = 10;
      req.magic    = InpMagic;
      req.type     = (type == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
      req.price    = (type == POSITION_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      OrderSend(req, res);
   }
}

Resolución de Problemas

SíntomaCausaSolución
INVALID_HANDLE de iMASímbolo o timeframe no disponible, o parámetros incorrectosAsegúrate de que el símbolo esté en Market Watch, el gráfico abierto y los períodos válidos (rápida < lenta, ambos > 0).
Not enough money (134)Margen insuficiente para el loteReduce InpLotSize, aumenta el depósito en el Probador o revisa el apalancamiento.
Requote (10004)El precio cambió antes de ejecutarAumenta req.deviation (p.ej. 20–50 puntos) o acepta requotes y reintenta.
El EA no abre operacionesAutoTrading desactivado o "Permitir live trading" sin marcarHaz clic en AutoTrading en la barra de MT5 (Ctrl+E) y en Propiedades del EA activa "Permitir Algo Trading".
Trade not allowed (64)Restricciones del bróker o cuentaVerifica que el símbolo permita trading, el mercado esté abierto y la cuenta permita trading automatizado.
Órdenes duplicadas en la misma barraSin comprobación de nueva barra en OnTickAñade la comprobación lastBar / iTime del Paso 5 — evalúa señales solo una vez por vela.

Revisa la pestaña Expertos en MetaTrader 5 para códigos de error y mensajes Print(). GetLastError() devuelve el código; consulta las constantes de error MQL5.


Próximos Pasos — Añadir Gestión de Riesgo

Este EA no tiene Stop Loss ni Take Profit. En Gestión de riesgo en EA añadirás dimensionamiento, SL/TP, trailing stops y buenas prácticas para proteger tu cuenta.

Consejo extra: ¿Quieres crear el mismo EA de cruce MA sin escribir código? Prueba AlfaTactix Strategy Builder gratis — diseña cruces de medias móviles, filtros RSI y reglas de riesgo de forma visual, y exporta MQL5 listo para producción en minutos. Compara variantes antes de refinar en MetaEditor. La misma lógica, cero tecleo.

Crea tu estrategia de trading sin código ahora — gratis

Crea tu cuenta y empieza ahora mismo a construir una estrategia sin código. Añade indicadores, filtros y reglas de riesgo, y exporta MQL5 en minutos.

Preguntas Frecuentes

Se trazan una MA rápida (ej. 20 períodos) y una MA lenta (ej. 50 períodos). Compra cuando la rápida cruza por encima de la lenta; vende cuando cruza por debajo. Es un enfoque clásico de seguimiento de tendencia.

OnTick() se ejecuta en cada tick. Comprobar nueva barra (ej. comparando tiempo de barra) asegura que evalúes señales solo una vez por vela — evitando órdenes duplicadas.

Comprueba PositionsTotal() y filtra por magic number. Abre nueva posición solo si no tienes ninguna (o tu lógica lo permite). Usa PositionGetInteger(POSITION_MAGIC) para identificar las posiciones de tu EA.

Verifica que AutoTrading esté activo, margen suficiente, símbolo en Market Watch y que el EA tenga "Permitir live trading" marcado en Propiedades. Revisa la pestaña Expertos para mensajes de error.

Un número mágico (ulong) identifica las órdenes y posiciones de tu EA. Úsalo para filtrar — p.ej. cerrar solo posiciones abiertas por este EA. Configúralo en MqlTradeRequest.magic.

Establece request.sl y request.tp en MqlTradeRequest antes de OrderSend(). Usa NormalizeDouble() y respeta SYMBOL_TRADE_STOPS_LEVEL. Ver Gestión de riesgo en EA.

Crea tu estrategia sin código ahora — gratis

Crear cuenta gratis