Gestión de Riesgo en EA — Protege tu Capital con MetaTrader 5
Tu EA puede encontrar entradas perfectas, pero sin gestión de riesgo una mala operación puede borrar semanas de ganancias. Según la documentación de funciones de trading MQL5, cada orden que envías debe incluir volumen, Stop Loss y Take Profit sensatos — o arriesgas reventar tu cuenta. Esta guía te enseña dimensionamiento, SL/TP, trailing stops y protecciones de cuenta usando la API oficial de MetaTrader 5, para que tu EA opere con seguridad en demo y en vivo.
Por qué Importa la Gestión de Riesgo
Una estrategia que gana el 60% del tiempo puede seguir perdiendo dinero si las pérdidas son mayores que las ganancias. La gestión de riesgo controla cuánto arriesgas por operación y dónde cortas pérdidas. Según MqlTradeRequest: volume, sl y tp son campos esenciales — úsalos con criterio.
Qué añadiremos a tu EA:
| Tema | Por qué importa |
|---|---|
| Dimensionamiento | Decidir tamaño del lote por operación — fijo, % de riesgo o basado en ATR |
| Stop Loss (SL) | Limitar pérdida por operación; el bróker ejecuta cuando el precio toca SL |
| Take Profit (TP) | Asegurar beneficios; salir cuando el precio alcanza TP |
| Trailing stop | Mover SL a favor del beneficio para proteger ganancias |
| Protecciones de cuenta | Máx. posiciones, comprobación de margen, límite de pérdida diaria |
Paso 1: Tamaño de Lote Fijo
¿Qué es? El tamaño del lote es cuántas unidades operas. Un lote fijo significa que siempre abres 0.1, 0.5 o 1.0 lotes — sin importar el tamaño de la cuenta ni la volatilidad del mercado. Es la forma más sencilla de empezar.
¿Por qué usarlo? Para backtesting y estrategias simples, el lote fijo mantiene la lógica clara. No necesitas calcular nada — solo defines un input y lo usas.
Importante: Cada bróker define tamaño mínimo y máximo del lote (ej. mín 0.01, máx 100). Si envías 0.001 o 500 lotes, el bróker rechaza la orden. Debes leer estos límites primero.
MqlTradeRequest.volume espera el volumen en lotes. Según SymbolInfoDouble:
double volMin = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double volMax = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double volStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double lot = MathMax(volMin, MathMin(volMax, InpLotSize));
lot = MathFloor(lot / volStep) * volStep;
lot = NormalizeDouble(lot, 2);
Aplica antes de OrderSend: request.volume = lot;
Paso 2: Riesgo por Operación (%)
¿Qué es? En lugar de un lote fijo, arriesgas un porcentaje fijo de tu cuenta por operación. Por ejemplo: "Nunca arriesgaré más del 1% de mi balance en una sola operación." Si tu balance es $10,000, cada operación arriesga como máximo $100. Si crece a $20,000, cada operación arriesga $200 — el tamaño del lote se ajusta solo.
¿Por qué usarlo? Un lote de 0.1 en una cuenta de $1,000 es enorme (puede acabarla en pocas operaciones malas). El mismo 0.1 lote en $50,000 es pequeño. El riesgo % mantiene tu riesgo proporcional a tu cuenta — así sobrevives a las rachas malas y creces de forma estable.
¿Cómo funciona? Decides: "Arriesgo 1% por operación." Luego calculas: "Si mi Stop Loss está a 50 pips, ¿cuántos lotes puedo abrir para que 50 pips = 1% de mi balance?" La respuesta te da el tamaño del lote. Usa AccountInfoDouble(ACCOUNT_BALANCE) y el valor del tick para el cálculo.
Fórmula:
- Riesgo = Balance × (Riesgo% / 100)
- Lote = Riesgo / (StopLoss en puntos × Valor por punto por lote)
Ejemplo — 1% riesgo, SL 50 pips en EURUSD:
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskPct = 1.0;
double riskAmount = balance * (riskPct / 100.0);
double slPoints = 50 * 10;
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double valuePerPoint = (tickValue / tickSize) * point;
double lot = riskAmount / (slPoints * point * valuePerPoint);
lot = MathMax(volMin, MathMin(volMax, lot));
lot = MathFloor(lot / volStep) * volStep;
lot = NormalizeDouble(lot, 2);
Paso 3: Lote Basado en ATR
¿Qué es? El ATR (Average True Range) es un indicador que mide cuánto se mueve el precio de media. En días tranquilos, el ATR es bajo (ej. 20 pips). En días volátiles, es alto (ej. 80 pips). Si usas un Stop Loss fijo de 50 pips, en días volátiles el precio puede alcanzar tu SL fácilmente (stop falso). En días tranquilos, 50 pips pueden ser demasiado justos y sacarte por ruido normal.
¿Por qué usarlo? Al basar la distancia del Stop Loss en el ATR (ej. 1.5× ATR), te adaptas a la volatilidad actual. Cuando el mercado está tranquilo, tu SL está más cerca. Cuando es volátil, tu SL es más amplio — así no te sacan por oscilaciones normales. Luego usas esa distancia en tu fórmula de riesgo %: SL más amplio → lote menor para el mismo riesgo en dinero.
¿Cómo funciona? Obtén el valor del ATR con iATR y CopyBuffer. Multiplica por un factor (ej. 1.5) para obtener la distancia del SL en precio. Convierte a puntos y úsalo en el cálculo del lote.
int atrHandle = iATR(_Symbol, PERIOD_CURRENT, 14);
double atrBuffer[];
ArraySetAsSeries(atrBuffer, true);
CopyBuffer(atrHandle, 0, 0, 1, atrBuffer);
double atr = atrBuffer[0];
double slDistance = atr * 1.5;
double slPoints = slDistance / SymbolInfoDouble(_Symbol, SYMBOL_POINT);
Paso 4: Stop Loss y Take Profit
¿Qué es? Stop Loss (SL) y Take Profit (TP) son niveles de precio que defines al abrir una operación. Stop Loss = "Si el precio va contra mí hasta aquí, cierra la posición automáticamente." Take Profit = "Si el precio se mueve a mi favor hasta aquí, cierra y asegura el beneficio." El bróker los ejecuta por ti — no necesitas mirar el gráfico.
¿Por qué usarlos? Sin SL, una mala operación puede vaciar tu cuenta. Sin TP, puede que nunca cierres ganadores. SL y TP eliminan la emoción — el EA y el bróker gestionan las salidas automáticamente.
Importante: En MqlTradeRequest, sl y tp deben ser precios (ej. 1.0850), no puntos. Calculas el precio a partir del precio de entrada ± (pips × point). Defínelos al abrir:
| Posición | SL | TP |
|---|---|---|
| Compra | Por debajo del precio de entrada | Por encima del precio de entrada |
| Venta | Por encima del precio de entrada | Por debajo del precio de entrada |
Ejemplo — Compra con SL 50 pips y TP 100 pips:
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
double sl = NormalizeDouble(ask - 50 * 10 * point, digits);
double tp = NormalizeDouble(ask + 100 * 10 * point, digits);
request.sl = sl;
request.tp = tp;
Paso 5: SYMBOL_TRADE_STOPS_LEVEL y NormalizeDouble
¿Qué es? Los brókers no permiten colocar SL/TP demasiado cerca del precio actual. Por ejemplo: no puedes poner un Stop Loss a 1 pip — los brókers exigen una distancia mínima (a menudo 10–50 puntos, según el símbolo). Si lo ignoras, OrderSend devuelve error 130 (invalid stops).
SYMBOL_TRADE_STOPS_LEVEL te indica la distancia mínima en puntos. NormalizeDouble redondea los precios al número correcto de decimales — los brókers rechazan precios como 1.0850123 cuando el símbolo espera 1.08501.
¿Por qué importa? Muchos principiantes reciben "invalid stops" porque ponen el SL demasiado cerca o envían precios con precisión incorrecta. Siempre comprueba SYMBOL_TRADE_STOPS_LEVEL y usa NormalizeDouble(precio, digits) antes de enviar. Según SymbolInfoInteger:
int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
if(stopLevel <= 0) stopLevel = 10;
double minDist = stopLevel * point;
double slDist = MathMax(minDist + 5 * point, 50 * 10 * point);
sl = NormalizeDouble(ask - slDist, digits);
Siempre usa NormalizeDouble(precio, digits) — los brókers rechazan precios con precisión incorrecta.
Paso 6: Trailing Stop
¿Qué es? Un trailing stop es un Stop Loss que se mueve cuando el precio se mueve a tu favor. Ejemplo: Compras en 1.0850 con SL en 1.0800. El precio sube a 1.0900. En lugar de dejar el SL en 1.0800, lo mueves a 1.0870 (30 pips por debajo del precio actual). Ahora, si el precio retrocede, aseguras 20 pips de beneficio en lugar de una pérdida. Si el precio sigue subiendo, sigues moviendo el SL — "siguiendo" al precio.
¿Por qué usarlo? Protege los beneficios. Sin trailing stop, una operación ganadora puede convertirse en perdedora si esperas demasiado. Con trailing stop, aseguras ganancias a medida que el mercado se mueve a tu favor.
¿Cómo funciona? En OnTick(), recorre tus posiciones. Para una Compra: si el Bid actual está lo bastante por encima del precio de apertura y por encima del SL actual, modifica el SL a un nuevo nivel (ej. Bid menos 30 pips). El bróker mantiene el nuevo SL — si el precio baja, cierra en ese nivel.
double trailPoints = 30 * 10;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket <= 0 || PositionGetInteger(POSITION_MAGIC) != InpMagic) continue;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double sl = PositionGetDouble(POSITION_SL);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
double newSL = NormalizeDouble(bid - trailPoints * point, digits);
if(newSL > sl && newSL < bid)
{
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_SLTP;
req.position = ticket;
req.symbol = _Symbol;
req.sl = newSL;
req.tp = PositionGetDouble(POSITION_TP);
OrderSend(req, res);
}
}
}
Paso 7: Protecciones de Cuenta
¿Qué es? Las protecciones de cuenta son comprobaciones extra que añades para que tu EA no haga algo peligroso. Por ejemplo: "Nunca abrir más de 2 posiciones a la vez" o "No abrir una nueva operación si el margen libre está por debajo de $500." Estas reglas te protegen de errores (ej. un EA que abre 100 posiciones) o condiciones de mercado extremas.
¿Por qué usarlas? Incluso con un buen SL/TP, un error o un suceso inesperado puede provocar sobreoperación o sobreapalancamiento. Las protecciones son una red de seguridad — detienen el EA antes de que dañe tu cuenta.
Protecciones habituales:
Máximo de posiciones:
int countMyPositions()
{
int n = 0;
for(int i = 0; i < PositionsTotal(); i++)
if(PositionGetInteger(POSITION_MAGIC) == InpMagic) n++;
return n;
}
if(countMyPositions() >= InpMaxPositions) return;
Comprobación de margen: Usa AccountInfoDouble(ACCOUNT_MARGIN_FREE) y OrderCalcMargin antes de abrir.
Resumen — Lo que Construiste
Añadiste: lote fijo y % de riesgo, SL basado en ATR, SL/TP en MqlTradeRequest, respeto a SYMBOL_TRADE_STOPS_LEVEL, trailing stop y protecciones de cuenta. Tu EA respeta los límites del bróker y protege el capital.
Consejo extra: ¿Quieres añadir estas reglas de riesgo sin programar? Prueba AlfaTactix Strategy Builder gratis — configura dimensionamiento (fijo, % riesgo, ATR), Stop Loss, Take Profit, trailing stops y protecciones de cuenta en una interfaz visual. Exporta MQL5 que respeta los límites del bróker. La misma protección, cero código.
Código Completo
bool OpenBuyWithSLTP(double lot, double slPips, double tpPips)
{
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
if(stopLevel <= 0) stopLevel = 10;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double sl = NormalizeDouble(ask - MathMax(stopLevel * point, slPips * 10 * point), digits);
double tp = NormalizeDouble(ask + MathMax(stopLevel * point, tpPips * 10 * point), digits);
MqlTradeRequest req = {};
MqlTradeResult res = {};
req.action = TRADE_ACTION_DEAL;
req.symbol = _Symbol;
req.volume = lot;
req.type = ORDER_TYPE_BUY;
req.price = ask;
req.sl = sl;
req.tp = tp;
req.deviation = 10;
req.magic = InpMagic;
return OrderSend(req, res);
}
Resolución de Problemas
| Síntoma | Causa | Solución |
|---|---|---|
Invalid stops (130) | SL/TP demasiado cerca o lado incorrecto | Usa SYMBOL_TRADE_STOPS_LEVEL, NormalizeDouble y dirección correcta. |
Not enough money (134) | Lote demasiado grande | Reduce lote, usa % de riesgo o OrderCalcMargin antes de enviar. |
| Lote redondeado a cero | Riesgo % muy bajo o SL muy amplio | Asegura lot >= SYMBOL_VOLUME_MIN; aumenta riesgo % o reduce SL. |
| Trailing stop no se mueve | Condición no cumplida o modificación fallida | Registra newSL vs sl; comprueba retcode de OrderSend. |
Consulta los códigos de retorno MQL5.