Solución definitiva a Invalid stops (10016/130) en MT5/MT4
Contenido
- Qué es Invalid stops (diferencia entre 10016 y 130)
- ① TRADE_RETCODE_INVALID_STOPS = 10016 (MT5 / código de retorno de `OrderSend()`)
- ② ERR_INVALID_STOPS = 130 (MT4 / `GetLastError()`)
- Diagnóstico rápido en 30 segundos
- Causas y soluciones (6 patrones)
- ① El SL/TP está demasiado cerca del precio actual (por debajo del stops level)
- ② Dirección del SL/TP invertida (confusión entre BUY/SELL)
- ③ Modificación de orden o posición dentro del freeze level
- ④ Confusión entre precio y puntos (distancia)
- ⑤ Brókeres de ejecución a mercado que no aceptan SL/TP en la apertura
- ⑥ Particularidades propias de cada instrumento (oro e índices con stops level mayor)
- Diferencias entre brókeres (puntos a tener en cuenta)
- Código MQL5 para prevenir este error (para desarrolladores de EAs)
- Validar y ajustar el SL/TP antes de enviar la orden
- Gestionar de forma específica el retcode 10016
- Comprobar el freeze level antes de modificar el trailing
- Lista de verificación por prioridad
- Resumen
- Preguntas frecuentes
- P: Mis cálculos de SL/TP deberían ser correctos, pero aun así aparece Invalid stops. ¿Por qué?
- P: ¿Cuál es la diferencia entre 10016 y 130?
- P: Normalmente no aparece, pero durante la publicación de datos económicos empieza a darme Invalid stops.
- P: Sin SL/TP la orden pasa, pero con SL/TP es rechazada.
- P: Un EA que funciona en EURUSD genera Invalid stops repetidamente en el oro.
Solución definitiva a Invalid stops (10016/130)
Cuando tu EA está funcionando y de repente aparece Invalid stops u OrderSend error 130 en la pestaña Expert, es fácil pensar "¿el valor del SL está mal? Pero mis cálculos deberían ser correctos...". Sin embargo, la gran mayoría de estos errores no son fallos de cálculo del SL/TP, sino violaciones de la "distancia mínima" que impone el bróker. El valor en sí puede ser correcto y aun así ser rechazado si está demasiado cerca del precio actual, si tiene la dirección invertida, o si cae dentro de una zona donde la modificación está prohibida.
Este artículo está dirigido tanto a quienes usan EAs en MT5/MT4 como a quienes programan EAs en MQL5, y reúne en un solo lugar la naturaleza del error Invalid stops, sus 6 causas, un diagnóstico de 30 segundos y la prevención permanente a nivel de código. Para un listado general de todos los códigos de error, consulta la guía completa de códigos de error de MQL5/MT5.
Este artículo está basado en MT5 (serie build 4xxx) de julio de 2026. Los valores concretos del stops level varían según el bróker y el instrumento.
Qué es Invalid stops (diferencia entre 10016 y 130)
El valor que indica "stop inválido" existe en dos versiones, según se trate de MT5 o MT4 (generación anterior). Identificar en qué plataforma y en qué etapa aparece acelera enormemente el diagnóstico.
① TRADE_RETCODE_INVALID_STOPS = 10016 (MT5 / código de retorno de OrderSend())
El resultado de OrderSend() en MQL5 se guarda en MqlTradeResult.retcode. Si el SL/TP incluido en la solicitud (o su relación con el precio de una orden pendiente) no cumple las reglas del servidor, se rechaza con 10016 (TRADE_RETCODE_INVALID_STOPS). Se trata de una notificación de rechazo procedente del servidor de trading.
Significado: El stop (SL/TP) de la solicitud es inválido (Invalid stops in the request)
Constante : TRADE_RETCODE_INVALID_STOPS
Valor : 10016
// Ejemplo típico de salida en el log
2026.07.07 09:15:32.441 EA_NAME XAUUSD,M5: OrderSend error: retcode=10016 (invalid stops)
② ERR_INVALID_STOPS = 130 (MT4 / GetLastError())
En la generación MT4 (MQL4), tras fallar OrderSend() / OrderModify(), GetLastError() devuelve 130 (ERR_INVALID_STOPS). El OrderSend error 130 que aparece en la pestaña Expert corresponde a esto. Si además usas un EA de MT4 en paralelo, es aquí donde lo verás.
Significado: Stop inválido (invalid stops)
Constante : ERR_INVALID_STOPS
Valor : 130
Uso práctico según la plataforma:
| Plataforma | Origen | Valor | Cuándo aparece |
|---|---|---|---|
| MT5 | MqlTradeResult.retcode | 10016 (TRADE_RETCODE_INVALID_STOPS) | El servidor rechaza OrderSend / PositionModify |
| MT5 | CTrade.ResultRetcode() | 10016 | Rechazo de envío/modificación vía CTrade |
| MT4 | GetLastError() | 130 (ERR_INVALID_STOPS) | Tras un fallo de OrderSend / OrderModify |
Aunque el número cambia, el significado y las causas son prácticamente idénticos, y la solución es común a ambos. Cabe aclarar que el código 10015 (TRADE_RETCODE_INVALID_PRICE), con el que suele confundirse en MT5, es un problema distinto: el propio precio de la orden es inválido. El 10016, en cambio, es siempre un problema de "posición del SL/TP (stop)".
Diagnóstico rápido en 30 segundos
Abre en MT5 "Cotizaciones → clic derecho sobre el símbolo → Especificación" y revisa estos dos valores.
Stops level (Nivel de stops) : distancia mínima (en puntos) a la que debe estar el SL/TP respecto al precio actual
Freeze level (Nivel de congelación): distancia (en puntos) dentro de la cual se prohíbe modificar/cancelar una orden a punto de ejecutarse
A continuación, revisa en el log, en el instante exacto en que apareció el error, el valor de SL/TP que intentabas enviar junto con el Bid/Ask actual.
- El SL o TP está a una distancia del precio actual menor que el stops level → esta es la causa más probable (causa ①).
- En una compra (BUY), el SL está por encima del Bid o el TP por debajo del Bid (en una venta, al revés) → dirección invertida (causa ②).
- Solo falla al modificar una posición ya abierta → puede ser el freeze level o el requisito de fijar el SL/TP después de la apertura (causas ③ y ⑤).
- El valor del SL es un número que claramente no es un precio, como "50" → confusión entre precio y puntos (causa ④).
Para verificarlo desde el código basta con una línea.
Print("StopsLevel=", SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL),
" FreezeLevel=", SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL));
Causas y soluciones (6 patrones)
① El SL/TP está demasiado cerca del precio actual (por debajo del stops level)
Síntoma: frecuente en EAs de scalping con SL muy ajustado o con trailing de rango pequeño. También ocurre manualmente: si intentas colocar el SL justo al lado del precio, el botón de envío se bloquea o la orden es rechazada.
Causa: cada bróker define, por instrumento, un SYMBOL_TRADE_STOPS_LEVEL (distancia mínima de stop en puntos), y cualquier SL/TP o precio de orden pendiente situado a menos de esa distancia del precio actual es rechazado automáticamente por el servidor. El precio de referencia para la validación es el Bid para el SL/TP de una posición BUY, y el Ask para una SELL. Cuando el spread se amplía, la distancia entre Bid y Ask crece, por lo que una distancia que normalmente pasa la validación puede ser rechazada de repente durante publicaciones de datos económicos o en las primeras horas de la sesión.
Solución:
- Comprueba el stops level en la ventana de especificación y amplía la configuración del SL/TP o del trailing del EA para que supere ese valor.
- En el EA, aplica un "clamp" (ajuste al mínimo permitido) antes de enviar la orden (código más abajo).
- Si necesitas imperativamente un SL muy ajustado, considera un bróker o tipo de cuenta con un stops level menor.
② Dirección del SL/TP invertida (confusión entre BUY/SELL)
Síntoma: siempre aparece Invalid stops en una dirección concreta (solo en compras o solo en ventas). Es el patrón más frecuente en las primeras pruebas de un EA propio.
Causa: la regla es simple: en BUY, el SL debe estar por debajo del precio actual (Bid) y el TP por encima; en SELL, el SL debe estar por encima del precio actual (Ask) y el TP por debajo. Errores típicos como copiar la fórmula de BUY para SELL sin corregir el signo, o confundir price - sl con price + sl, provocan que el servidor devuelva de inmediato 10016/130.
Solución:
- Cuando ocurra el error, imprime los valores reales con
PrintFormat("type=%s sl=%.5f tp=%.5f bid=%.5f ask=%.5f", ...)y verifica visualmente la dirección. - Unifica el cálculo de SL/TP para BUY/SELL en una función común, concentrando la bifurcación de signos en un único punto (elimina las ramas duplicadas por copy-paste).
③ Modificación de orden o posición dentro del freeze level
Síntoma: las nuevas órdenes no tienen problema, pero solo se rechazan las modificaciones/cancelaciones justo antes de alcanzar el TP, el SL o el disparo de una pendiente.
Causa: en los instrumentos donde está configurado SYMBOL_TRADE_FREEZE_LEVEL, cuando el precio actual se acerca lo suficiente al precio de activación (TP/SL o el precio disparador de una pendiente), se congela la modificación/cancelación de esa orden. Es una especificación del servidor pensada para evitar conflictos entre la ejecución y una solicitud de modificación simultánea, y suele manifestarse como un trailing del EA que "intenta actualizar un paso más justo antes del TP y es rechazado".
Solución:
- Antes de modificar, lee
SYMBOL_TRADE_FREEZE_LEVELy, si la distancia hasta el precio de activación es menor o igual al freeze level, omite la modificación en ese ciclo. - Amplía el intervalo y el paso de actualización del trailing para reducir solicitudes de modificación innecesarias justo antes de la activación.
- Como el rechazo no es crítico (estar cerca de la activación significa que la ejecución es inminente), es válido diseñar el EA para que simplemente ignore el error y solo lo registre en el log.
④ Confusión entre precio y puntos (distancia)
Síntoma: en el SL aparece directamente un "número pensado como distancia", como 50 o 0.0050. En el log se ve algo como sl=50.00000, que evidentemente no es un precio.
Causa: los campos MqlTradeRequest.sl / .tp deben contener un precio absoluto (no "50 puntos por debajo de la entrada"). Un EA que gestiona la distancia internamente debe convertirla a entry ± distance * _Point antes de asignarla. Por el contrario, si se pasa una distancia donde se espera un precio absoluto —un hábito heredado de ciertas funciones de la época de MT4—, el valor no constituye un precio válido y provoca 10016/130.
Otro error clásico es la confusión entre pips y puntos. En brókeres con 5 dígitos (por ejemplo, USDJPY con 3 dígitos y EURUSD con 5), 1 pip = 10 puntos. Si "SL 50" se interpreta como pips cuando en realidad se calculó en puntos (o viceversa), la distancia real queda multiplicada o dividida por 10, pudiendo caer por debajo del stops level y ser rechazada.
Solución:
- Construye siempre el SL/TP con la forma
NormalizeDouble(price ± dist * _Point, _Digits). - Indica explícitamente en comentarios la unidad de los parámetros de entrada (pips / points) y centraliza la conversión a
_Pointen un único lugar del código.
⑤ Brókeres de ejecución a mercado que no aceptan SL/TP en la apertura
Síntoma: la orden se ejecuta sin problema si no lleva SL/TP, pero es rechazada con Invalid stops en cuanto se incluyen. Ocurre especialmente en cuentas ECN/Market Execution.
Causa: en la ejecución a mercado, el "precio solicitado" y el "precio de ejecución real" pueden diferir, por lo que algunos servidores no aceptan el SL/TP dentro de la solicitud de una nueva orden y exigen fijarlos mediante una modificación de posición después de la ejecución. Es una especificación clásica de las cuentas ECN de la era MT4, donde el error 130 era muy frecuente, y todavía hay servidores en MT5 que se comportan igual.
Solución:
- Cambia a un esquema de dos pasos: primero envía la orden sin SL/TP → tras confirmar la ejecución, añade el SL/TP con
PositionModify()(otrade.PositionModify()si usas CTrade). - Con este método existe un instante en el que "la orden se ejecutó pero falló la fijación del SL", así que es imprescindible incluir una protección que reintente hasta que el SL se establezca correctamente y cierre la posición de inmediato si se supera un número determinado de intentos fallidos (dejar una posición sin SL es el peor escenario posible).
- Puedes comprobar el modo de ejecución con
SymbolInfoInteger(_Symbol, SYMBOL_TRADE_EXEMODE).
⑥ Particularidades propias de cada instrumento (oro e índices con stops level mayor)
Síntoma: el mismo EA funciona en EURUSD, pero en cuanto se aplica a XAUUSD (oro) o a un CFD de índice bursátil, empieza a generar Invalid stops repetidamente.
Causa: el stops level se configura de forma independiente para cada instrumento, y es habitual que el oro, los índices y las divisas exóticas tengan un valor mucho mayor que las divisas principales. Si se reutiliza sin cambios un SL/trailing ajustado y pensado para forex de las principales, no alcanza la distancia mínima del nuevo instrumento y es rechazado. El número de dígitos también varía según el instrumento (el oro suele tener 2 o 3 dígitos), por lo que un código que asume _Digits de forma fija también se rompe.
Solución:
- Al cambiar de instrumento, revisa siempre el stops level y el número de dígitos en la ventana de especificación.
- Define el ancho de SL/TP en función de la volatilidad (por ejemplo, con ATR) en lugar de un valor fijo en puntos, para que se adapte mejor entre instrumentos.
- Obtén siempre
_Point/_Digits/SYMBOL_TRADE_STOPS_LEVELde forma dinámica en el código (evita cualquier valor fijo/hardcodeado).
Diferencias entre brókeres (puntos a tener en cuenta)
El stops level y el freeze level varían por completo según la combinación de bróker e instrumento. Es habitual que, con el mismo EA y la misma configuración, un error que nunca aparece con el Bróker A se produzca a diario con el Bróker B.
Un punto adicional a vigilar es el de los brókeres donde el stops level se muestra como "0". Ese 0 no suele significar "sin límite", sino "determinación dinámica": en condiciones normales acepta un SL muy próximo al precio, pero es rechazado justo en el momento en que el spread se amplía, como durante publicaciones de datos económicos o en las primeras horas de la sesión. La causa de un Invalid stops "que aparece solo de vez en cuando" suele ser exactamente esta.
Verifica siempre estos valores en una cuenta real.
long stops = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL); // en puntos
long freeze = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL); // en puntos
Los valores concretos dependen del tipo de cuenta y de la especificación de cada instrumento en cada bróker, por lo que este artículo no da cifras específicas. El único valor correcto es el que obtengas ejecutando el código anterior en tu propia cuenta.
Código MQL5 para prevenir este error (para desarrolladores de EAs)
El diseño correcto no es "corregir cuando aparece el error", sino ajustar (clamp) el SL/TP a la distancia mínima del bróker antes de enviar la orden, de modo que el 10016 nunca llegue a producirse.
Validar y ajustar el SL/TP antes de enviar la orden
// Ajusta el SL/TP a una distancia igual o mayor al stops level antes de enviar la orden
// Valor de retorno false = dirección invertida (error de diseño), no enviar la orden
bool ClampStops(ENUM_ORDER_TYPE type, double &sl, double &tp)
{
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
long stopsPt = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
double spread = SymbolInfoDouble(_Symbol, SYMBOL_ASK)
- SymbolInfoDouble(_Symbol, SYMBOL_BID);
// Margen adicional = stops level + spread (protección para brókeres con stops=0 dinámico)
double minDist = stopsPt * point + spread;
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(type == ORDER_TYPE_BUY)
{
// El SL/TP de un BUY se valida contra el Bid
if(sl > 0 && sl >= bid) return false; // dirección invertida
if(tp > 0 && tp <= bid) return false;
if(sl > 0 && (bid - sl) < minDist) sl = bid - minDist;
if(tp > 0 && (tp - bid) < minDist) tp = bid + minDist;
}
else if(type == ORDER_TYPE_SELL)
{
// El SL/TP de un SELL se valida contra el Ask
if(sl > 0 && sl <= ask) return false; // dirección invertida
if(tp > 0 && tp >= ask) return false;
if(sl > 0 && (sl - ask) < minDist) sl = ask + minDist;
if(tp > 0 && (ask - tp) < minDist) tp = ask - minDist;
}
sl = NormalizeDouble(sl, digits);
tp = NormalizeDouble(tp, digits);
return true;
}
Tres puntos clave:
- Obtén siempre
SYMBOL_TRADE_STOPS_LEVELySYMBOL_POINTde forma dinámica (así el código funciona con independencia del instrumento o el bróker). - Añade un margen equivalente al spread (facilita que la orden pase incluso en brókeres con stops level 0 y determinación dinámica).
- Termina siempre ajustando los decimales con
NormalizeDouble(precio, _Digits)(un exceso de decimales también puede provocar el rechazo).
Gestionar de forma específica el retcode 10016
Registrar en el log, en el momento del rechazo, "qué se envió y cuál era la distancia en ese instante" permite identificar de un vistazo cuál de las causas ①–⑥ es la responsable.
MqlTradeRequest req; MqlTradeResult res;
ZeroMemory(req); ZeroMemory(res);
// ... se construye req (sl/tp ya pasados por ClampStops) ...
if(!OrderSend(req, res) || res.retcode != TRADE_RETCODE_DONE)
{
if(res.retcode == TRADE_RETCODE_INVALID_STOPS) // 10016
PrintFormat("Invalid stops: sl=%s tp=%s bid=%s stopsLevel=%d",
DoubleToString(req.sl, _Digits),
DoubleToString(req.tp, _Digits),
DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits),
(int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL));
else
PrintFormat("OrderSend failed: retcode=%d, lastError=%d", res.retcode, GetLastError());
}
Comprobar el freeze level antes de modificar el trailing
// Antes de modificar una posición, comprueba si el precio de activación está en la zona de congelación
bool CanModify(double triggerPrice)
{
long freeze = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL);
if(freeze <= 0) return true;
double dist = MathAbs(SymbolInfoDouble(_Symbol, SYMBOL_BID) - triggerPrice);
return (dist > freeze * SymbolInfoDouble(_Symbol, SYMBOL_POINT));
}
En un EA de MT4, puedes obtener los mismos valores con MarketInfo(Symbol(), MODE_STOPLEVEL) / MODE_FREEZELEVEL. El planteamiento es exactamente el mismo.
Los EAs distribuidos por FXEA365 implementan como estándar esta validación del SL/TP antes de enviar la orden, el ajuste (clamp) al stops level y la obtención dinámica independiente del instrumento, por lo que su diseño evita quedar bloqueado por Invalid stops incluso al cambiar de bróker o de instrumento.
Lista de verificación por prioridad
| Prioridad | Comprobación | Solución |
|---|---|---|
| 🚨 Primero | ¿La distancia entre el SL/TP y el precio actual es menor que el stops level? | Ampliar el ancho del SL/TP / implementar el ajuste (clamp) |
| 🚨 Primero | ¿El SL/TP tiene la dirección invertida en BUY/SELL? | Mostrar los valores reales en el log y verificarlos visualmente |
| ⚠️ Después | Solo falla la modificación → ¿está dentro del freeze level? | Omitir la modificación cuando esté cerca de la activación |
| ⚠️ Después | ¿Se está pasando una distancia (en puntos) en sl/tp? | Convertir a precio absoluto price ± dist*_Point |
| ✅ Verificar | ¿Es una cuenta con ejecución a mercado que exige fijar el SL/TP después? | Esquema de dos pasos: enviar orden → PositionModify |
| 🛠 Desarrollo | ¿Se obtiene dinámicamente la especificación del instrumento? | Implementar el ClampStops mostrado arriba |
Resumen
Invalid stopscorresponde a 10016 (TRADE_RETCODE_INVALID_STOPS) en MT5 y a 130 (ERR_INVALID_STOPS) en MT4. El número cambia, pero el significado y la solución son comunes.- La mayoría de las causas no son errores de cálculo, sino uno de estos seis factores: distancia menor al stops level, dirección invertida, freeze level, confusión entre precio y puntos, exigencia de fijar el SL/TP después de la apertura, y particularidades propias del instrumento.
- Los desarrolladores pueden solucionarlo de forma permanente leyendo
SYMBOL_TRADE_STOPS_LEVELantes de enviar la orden, aplicando el ajuste (clamp) y usandoNormalizeDouble. Incluso en brókeres con stops level 0, un margen equivalente al spread absorbe el problema.
Para el conjunto completo de códigos de error, consulta la guía completa de códigos de error de MQL5/MT5. Sobre otro error clásico de rechazo de órdenes, "fondos insuficientes", puedes consultar el artículo dedicado a ERR_NO_MONEY (134/10019).
Preguntas frecuentes
P: Mis cálculos de SL/TP deberían ser correctos, pero aun así aparece Invalid stops. ¿Por qué?
Es muy probable que el rechazo no se deba a que el valor sea incorrecto, sino a la distancia respecto al precio actual. Un SL/TP situado por debajo de la distancia mínima de stop (stops level) del bróker se rechaza automáticamente aunque el valor sea exacto. Comprueba la distancia mínima en la ventana de especificación o con SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL).
P: ¿Cuál es la diferencia entre 10016 y 130?
10016 (TRADE_RETCODE_INVALID_STOPS) es el código de resultado de OrderSend() / PositionModify() en MT5, mientras que 130 (ERR_INVALID_STOPS) es el valor que devuelve GetLastError() en MT4. Solo cambia la plataforma: el significado (posición inválida del SL/TP) y la solución son los mismos.
P: Normalmente no aparece, pero durante la publicación de datos económicos empieza a darme Invalid stops.
La causa es la ampliación del spread. Como el SL/TP de un BUY se valida contra el Bid y el de un SELL contra el Ask, en el momento en que el spread se amplía la distancia deja de ser suficiente. Incluso en brókeres con stops level en 0, puede haber un comportamiento que rechaza dinámicamente las órdenes solo durante movimientos bruscos. Añade un margen equivalente al spread a la distancia del SL/TP (consulta el código incluido en el artículo).
P: Sin SL/TP la orden pasa, pero con SL/TP es rechazada.
Es posible que estés en una cuenta de ejecución a mercado (Market Execution) cuyo servidor no acepta el SL/TP dentro de la solicitud de una nueva orden. Cambia a un esquema de dos pasos: envía la orden sin SL/TP y, tras la ejecución, fíjalo con PositionModify(). Eso sí, asegúrate de incluir reintentos si falla la fijación del SL, y una protección de cierre inmediato si los fallos persisten.
P: Un EA que funciona en EURUSD genera Invalid stops repetidamente en el oro.
Es habitual que el oro y los índices bursátiles tengan un stops level mayor que las divisas principales, además de un número de dígitos distinto. Amplía el ancho del SL/trailing según la especificación del instrumento, o cámbialo a un ancho vinculado a la volatilidad basado en ATR.
Relacionado
📧 Avisos antes de subidas de precio + curso gratuito de 5 días por email
Todos los EAs están a precio de lanzamiento y suben por escalones según las ventas. Reciba aviso antes de cada subida, más un email diario sobre trading algorítmico, cómo leer backtests y elegir bróker.
* Privacidad estrictamente protegida. Puedes darte de baja en cualquier momento.