Trailing stop em MQL5: como programar no seu robô (com código comentado)
Código completo de trailing stop em MQL5 para MetaTrader 5, com início, distância e passo configuráveis, ajuste ao tick da B3 e os erros mais comuns que impedem o stop de ser movido.
Neste artigo
O trailing stop é um stop que “anda” junto com o preço quando a operação vai a seu favor. Ele protege o lucro já conquistado sem limitar o ganho caso o movimento continue.
O MetaTrader 5 até tem um trailing stop nativo (clique direito na posição → Trailing Stop), mas ele é limitado: usa distâncias fixas, só funciona com o terminal aberto e não conversa com a lógica do seu robô. Quando o trailing é programado dentro do Expert Advisor, você controla exatamente quando ele começa, a que distância fica e de quanto em quanto ele se move.
Neste artigo, você vai ver um código completo em MQL5, entender cada parte e conhecer os erros que mais aparecem na prática.
Os três parâmetros: início, distância e passo
Um trailing stop bem feito tem três configurações:
- Início: quanto de lucro, em pontos, a operação precisa ter para o trailing começar a agir.
- Distância: a quantos pontos do preço atual o stop vai ficar.
- Passo: o movimento mínimo para o stop ser ajustado. Evita mandar uma modificação a cada tick.
Veja um exemplo no mini índice (WIN), comprado em 130.000, com início de 200 pontos, distância de 150 e passo de 50:
| Preço atual | O que acontece | Stop |
|---|---|---|
| 130.150 | Lucro de 150 pontos, ainda abaixo do início | stop original |
| 130.200 | Trailing ativado | 130.050 |
| 130.230 | Novo stop seria 130.080, só 30 pontos acima: menor que o passo | 130.050 |
| 130.260 | Novo stop seria 130.110, 60 pontos acima: move | 130.110 |
O código completo
O código abaixo pode ser colado em um Expert Advisor existente. Ele filtra as posições pelo ativo e pelo magic number, calcula o novo stop e só modifica quando necessário.
#include <Trade\Trade.mqh>
input ulong InpMagic = 20260928; // Magic number das posições do robô
input int InpTrailingInicio = 200; // Lucro (em pontos) para ativar o trailing
input int InpTrailingDist = 150; // Distância do stop até o preço (em pontos)
input int InpTrailingPasso = 50; // Movimento mínimo (em pontos) para ajustar
CTrade trade;
int OnInit()
{
trade.SetExpertMagicNumber(InpMagic);
return(INIT_SUCCEEDED);
}
void OnTick()
{
// ... sua lógica de entrada aqui ...
AplicarTrailingStop();
}
//--- Arredonda o preço para um múltiplo do tick (essencial na B3)
double AjustarAoTick(double preco)
{
double tick = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
if(tick <= 0)
return NormalizeDouble(preco, _Digits);
return NormalizeDouble(MathRound(preco / tick) * tick, _Digits);
}
void AplicarTrailingStop()
{
double nivelMin = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i); // também seleciona a posição
if(ticket == 0)
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
continue;
if((ulong)PositionGetInteger(POSITION_MAGIC) != InpMagic)
continue;
double abertura = PositionGetDouble(POSITION_PRICE_OPEN);
double slAtual = PositionGetDouble(POSITION_SL);
double tpAtual = PositionGetDouble(POSITION_TP);
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(bid <= 0 || bid - abertura < InpTrailingInicio * _Point)
continue; // ainda sem o lucro mínimo
double novoSL = AjustarAoTick(bid - InpTrailingDist * _Point);
if(bid - novoSL < nivelMin)
continue; // perto demais: seria recusado
if(slAtual == 0 || novoSL - slAtual >= InpTrailingPasso * _Point)
{
if(!trade.PositionModify(ticket, novoSL, tpAtual))
Print("Falha ao mover o stop: ", trade.ResultRetcode(), " ", trade.ResultRetcodeDescription());
}
}
else // POSITION_TYPE_SELL
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(ask <= 0 || abertura - ask < InpTrailingInicio * _Point)
continue;
double novoSL = AjustarAoTick(ask + InpTrailingDist * _Point);
if(novoSL - ask < nivelMin)
continue;
if(slAtual == 0 || slAtual - novoSL >= InpTrailingPasso * _Point)
{
if(!trade.PositionModify(ticket, novoSL, tpAtual))
Print("Falha ao mover o stop: ", trade.ResultRetcode(), " ", trade.ResultRetcodeDescription());
}
}
}
}
Entendendo as partes importantes
O laço de trás para frente
O for percorre as posições do fim para o começo. É um hábito seguro em MQL5: se alguma posição for fechada durante o laço, a contagem não pula nenhuma outra.
O filtro por magic number
Sem esse filtro, o robô vai mexer no stop de qualquer posição do ativo, inclusive operações manuais ou de outros robôs. É um dos erros mais comuns em códigos copiados da internet.
Compra usa Bid, venda usa Ask
Uma posição comprada é encerrada pelo preço de venda (Bid). Uma vendida é encerrada pelo preço de compra (Ask). Por isso o cálculo usa preços diferentes para cada lado. Usar o preço errado faz o stop ficar alguns pontos fora do lugar, o que pesa em ativos com spread maior.
Ajuste ao tick
Na B3, o preço precisa respeitar o tamanho mínimo de variação do ativo. No WIN, esse tamanho é de 5 pontos: um stop em 130.112 é inválido, e a corretora recusa a ordem. A função AjustarAoTick arredonda o preço para o múltiplo válido mais próximo. No Forex, o tick costuma ser igual ao ponto, então a função não atrapalha.
Nível mínimo de stop
Algumas corretoras exigem uma distância mínima entre o preço atual e o stop (SYMBOL_TRADE_STOPS_LEVEL). Na B3 esse valor geralmente é zero, mas em contas de Forex é comum ter um mínimo. Se o novo stop ficar perto demais, a modificação seria recusada, então o código nem tenta.
O passo
Sem o passo, o robô enviaria uma modificação a cada tick de lucro. Isso gera centenas de requisições desnecessárias, polui o diário e, em algumas corretoras, pode até causar bloqueio temporário por excesso de pedidos.
Variações úteis
Trailing pela volatilidade (ATR)
Em vez de uma distância fixa, use um múltiplo do ATR. Assim o stop fica mais largo em dias agitados e mais curto em dias calmos:
int handleATR;
int OnInit()
{
handleATR = iATR(_Symbol, PERIOD_CURRENT, 14);
if(handleATR == INVALID_HANDLE)
return(INIT_FAILED);
trade.SetExpertMagicNumber(InpMagic);
return(INIT_SUCCEEDED);
}
double DistanciaATR(double multiplicador)
{
double atr[];
if(CopyBuffer(handleATR, 0, 1, 1, atr) != 1)
return 0;
return atr[0] * multiplicador; // já em preço, não em pontos
}
Repare que, em MQL5, iATR devolve um handle, e o valor é lido com CopyBuffer. Quem vem do MQL4 costuma esquecer isso.
Breakeven antes do trailing
Uma combinação popular: ao atingir X pontos de lucro, o stop vai para o preço de entrada (mais uma pequena margem para cobrir custos). Só depois de um lucro maior o trailing assume.
Trailing pelo candle anterior
Na compra, o stop acompanha a mínima do candle anterior (iLow(_Symbol, PERIOD_CURRENT, 1)). Na venda, a máxima (iHigh). É simples e respeita a estrutura do gráfico.
Como testar no Testador de Estratégia
O trailing stop depende do caminho que o preço faz dentro do candle. Por isso:
- Use a modelagem “Cada tick baseado em ticks reais”. Com “Apenas preços de abertura”, o resultado do trailing não significa nada.
- Rode pelo menos uma vez no modo visual e acompanhe o stop se movendo no gráfico.
- Compare o resultado com e sem trailing. Nem toda estratégia melhora com ele: em estratégias de alvo curto, o trailing pode apenas reduzir o ganho médio.
Se nunca fez um backtest, veja o passo a passo de backtest no MetaTrader 5.
Erros comuns e o que significam
| Mensagem no diário | Causa provável |
|---|---|
10016 Invalid stops | Preço fora do tick do ativo ou stop perto demais do preço |
10025 No changes | O robô enviou o mesmo stop que já estava na posição |
| Stop mexendo em operações manuais | Falta o filtro por magic number |
| Trailing não funciona no backtest | Modelagem por preços de abertura em vez de ticks |
Resumo
- Trailing stop programado no robô dá controle total sobre início, distância e passo.
- Filtre sempre pelo ativo e pelo magic number.
- Na B3, ajuste o preço ao tick do ativo para não ter ordens recusadas.
- Teste com ticks reais e compare o resultado com e sem trailing.
O código deste artigo é um ponto de partida para estudo. Antes de usar com dinheiro real, teste em conta demo e adapte às regras da sua estratégia.