MQL5 — Ordens Avançadas e Gerenciamento de Posição
Trailing stop, fechamento parcial, ordens pendentes, OCO, gestão de múltiplas posições e padrões avançados de execução em MQL5.
MQL5 — Ordens Avançadas e Gerenciamento de Posição
Saber abrir uma ordem é só o começo. O diferencial de um EA profissional está em gerenciar posições abertas — trailing stops dinâmicos, fechamento parcial, cancelamento inteligente de pendentes e proteção contra condições adversas de mercado.
Fundamentos: MQL5 — Fundamentos | Gestão de risco: MQL5 — Gestão de Risco
Classe CTrade — Referência Rápida
A biblioteca <Trade/Trade.mqh> encapsula todas as operações de trading.
#include <Trade/Trade.mqh>
CTrade trade;
// Configurações recomendadas
trade.SetExpertMagicNumber(12345); // identificar ordens do EA
trade.SetSlippage(10); // slippage máximo em pontos
trade.SetTypeFilling(ORDER_FILLING_FOK); // Fill or Kill (alternativa: IOC, RETURN)
trade.SetDeviationInPoints(10); // desvio máximo de preço
// Abertura de ordens a mercado
trade.Buy(lots, _Symbol, ask, sl, tp, "comentário");
trade.Sell(lots, _Symbol, bid, sl, tp, "comentário");
// Ordens pendentes
trade.BuyLimit(lots, price, _Symbol, sl, tp, 0, 0, "comentário");
trade.SellLimit(lots, price, _Symbol, sl, tp, 0, 0, "comentário");
trade.BuyStop(lots, price, _Symbol, sl, tp, 0, 0, "comentário");
trade.SellStop(lots, price, _Symbol, sl, tp, 0, 0, "comentário");
// Modificar posição
trade.PositionModify(ticket, newSL, newTP);
// Fechar posição
trade.PositionClose(ticket);
trade.PositionClosePartial(ticket, lotsToClose);
// Cancelar ordem pendente
trade.OrderDelete(ticket);Trailing Stop
Trailing simples (n pontos atrás do preço)
input double TrailStart = 30.0; // pontos de lucro para ativar
input double TrailStep = 20.0; // distância do SL ao preço atual
input double TrailPoints = 5.0; // mover SL apenas quando ganhar X pontos
void TrailingStop()
{
CTrade trade;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket)) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
double tp = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(type == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double newSL = bid - TrailStep * _Point;
// Ativar apenas após TrailStart pontos de lucro
if(bid - openPrice < TrailStart * _Point) continue;
// Mover apenas se o novo SL for melhor que o atual E ganho mínimo
if(newSL > currentSL + TrailPoints * _Point)
trade.PositionModify(ticket, newSL, tp);
}
else if(type == POSITION_TYPE_SELL)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double newSL = ask + TrailStep * _Point;
if(openPrice - ask < TrailStart * _Point) continue;
if(newSL < currentSL - TrailPoints * _Point)
trade.PositionModify(ticket, newSL, tp);
}
}
}Trailing baseado em ATR
O trailing dinâmico com ATR se adapta à volatilidade atual — não persegue o preço de perto em mercados voláteis.
int atrHandle;
void TrailingStopATR(double atrMultiplier = 2.0)
{
double atr[];
ArraySetAsSeries(atr, true);
CopyBuffer(atrHandle, 0, 0, 3, atr);
double atrDist = atr[1] * atrMultiplier;
CTrade trade;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket)) continue;
double sl = PositionGetDouble(POSITION_SL);
double tp = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(type == POSITION_TYPE_BUY)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double newSL = NormalizeDouble(bid - atrDist, _Digits);
if(newSL > sl) trade.PositionModify(ticket, newSL, tp);
}
else
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double newSL = NormalizeDouble(ask + atrDist, _Digits);
if(newSL < sl || sl == 0) trade.PositionModify(ticket, newSL, tp);
}
}
}Trailing por estrutura (swing high/low)
void TrailingByStructure(int lookback = 20)
{
MqlRates rates[];
ArraySetAsSeries(rates, true);
CopyRates(_Symbol, PERIOD_CURRENT, 0, lookback + 1, rates);
double swingLow = rates[0].low;
double swingHigh = rates[0].high;
for(int i = 1; i <= lookback; i++)
{
if(rates[i].low < swingLow) swingLow = rates[i].low;
if(rates[i].high > swingHigh) swingHigh = rates[i].high;
}
CTrade trade;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket)) continue;
double sl = PositionGetDouble(POSITION_SL);
double tp = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(type == POSITION_TYPE_BUY && swingLow > sl)
trade.PositionModify(ticket, swingLow - _Point, tp);
else if(type == POSITION_TYPE_SELL && (swingHigh < sl || sl == 0))
trade.PositionModify(ticket, swingHigh + _Point, tp);
}
}Fechamento Parcial (Partial Take Profit)
Técnica profissional: fechar parte da posição no primeiro alvo e deixar o restante rodar com trailing.
input double PartialClosePercent = 50.0; // % do lote a fechar no 1º alvo
input double TP1_Points = 30.0; // 1º alvo em pontos
input double TP2_Points = 80.0; // 2º alvo (restante)
bool g_partialClosed = false;
void ManagePartialClose()
{
if(g_partialClosed) return;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket)) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double volume = PositionGetDouble(POSITION_VOLUME);
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
bool tp1Reached = (type == POSITION_TYPE_BUY && bid >= openPrice + TP1_Points * _Point) ||
(type == POSITION_TYPE_SELL && ask <= openPrice - TP1_Points * _Point);
if(tp1Reached)
{
double lotsToClose = NormalizeDouble(volume * PartialClosePercent / 100.0, 2);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
if(lotsToClose >= minLot)
{
CTrade trade;
if(trade.PositionClosePartial(ticket, lotsToClose))
{
g_partialClosed = true;
Print("Fechamento parcial: ", lotsToClose, " lotes no alvo 1");
// Mover SL para break even na posição restante
double newSL = (type == POSITION_TYPE_BUY)
? openPrice + 2 * _Point
: openPrice - 2 * _Point;
trade.PositionModify(ticket, newSL, 0);
}
}
}
}
}Gerenciamento de Ordens Pendentes
Cancelar ordens antigas
input int MaxOrderAgeMinutes = 60; // cancelar pendentes com mais de X minutos
void CancelOldPendingOrders()
{
CTrade trade;
datetime cutoff = TimeCurrent() - MaxOrderAgeMinutes * 60;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(!OrderSelect(ticket)) continue;
if(OrderGetString(ORDER_SYMBOL) != _Symbol) continue;
datetime orderTime = (datetime)OrderGetInteger(ORDER_TIME_SETUP);
if(orderTime < cutoff)
{
trade.OrderDelete(ticket);
Print("Ordem pendente cancelada por tempo: ticket ", ticket);
}
}
}Atualizar preço de ordem pendente (chasing)
void UpdatePendingOrderPrice(ulong ticket, double newPrice)
{
if(!OrderSelect(ticket)) return;
double sl = OrderGetDouble(ORDER_SL);
double tp = OrderGetDouble(ORDER_TP);
CTrade trade;
trade.OrderModify(ticket, newPrice, sl, tp, 0, 0);
}Iterar sobre posições com segurança
O loop deve ser de trás para frente (i--) para evitar índices inválidos ao fechar posições.
void CloseAllPositions()
{
CTrade trade;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket)) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
trade.PositionClose(ticket);
}
}
// Fechar apenas posições no lucro
void ClosePositionsInProfit()
{
CTrade trade;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket)) continue;
double profit = PositionGetDouble(POSITION_PROFIT);
if(profit > 0)
trade.PositionClose(ticket);
}
}OnTradeTransaction — reagir a eventos de execução
void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &request,
const MqlTradeResult &result)
{
// Posição aberta
if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
{
ulong dealTicket = trans.deal;
if(HistoryDealSelect(dealTicket))
{
ENUM_DEAL_ENTRY entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(dealTicket, DEAL_ENTRY);
if(entry == DEAL_ENTRY_IN)
Print("Posição aberta: ticket ", dealTicket);
else if(entry == DEAL_ENTRY_OUT)
Print("Posição fechada: profit = ",
HistoryDealGetDouble(dealTicket, DEAL_PROFIT));
}
}
// Ordem cancelada pelo servidor
if(trans.type == TRADE_TRANSACTION_ORDER_DELETE)
{
Print("Ordem deletada: ticket ", trans.order);
}
}Padrão OCO — One Cancels Other
MT5 não tem OCO nativo, mas é possível implementar: se um lado for executado, cancela o outro.
ulong g_buyStopTicket = 0;
ulong g_sellStopTicket = 0;
void PlaceOCO(double buyStopPrice, double sellStopPrice, double lots, double sl, double tp)
{
CTrade trade;
trade.BuyStop(lots, buyStopPrice, _Symbol, buyStopPrice - sl, buyStopPrice + tp);
g_buyStopTicket = trade.ResultOrder();
trade.SellStop(lots, sellStopPrice, _Symbol, sellStopPrice + sl, sellStopPrice - tp);
g_sellStopTicket = trade.ResultOrder();
}
void ManageOCO()
{
// Se compra foi executada, cancela venda
bool buyExecuted = PositionSelectByTicket(g_buyStopTicket);
bool sellExecuted = PositionSelectByTicket(g_sellStopTicket);
if(buyExecuted && OrderSelect(g_sellStopTicket))
{ CTrade t; t.OrderDelete(g_sellStopTicket); g_sellStopTicket = 0; }
if(sellExecuted && OrderSelect(g_buyStopTicket))
{ CTrade t; t.OrderDelete(g_buyStopTicket); g_buyStopTicket = 0; }
}Referências
- MQL5 — Fundamentos
- MQL5 — Gestão de Risco
- Análise Multi-Timeframe
- Stop Loss e Take Profit — conceito
- Trailing Drawdown — conceito
Referências externas
MQL5 — Gestão de Risco em Código
Como implementar position sizing, risco por trade, stops baseados em ATR, drawdown máximo e proteção de capital em Expert Advisors MQL5.
MQL5 — Análise Multi-Timeframe (MTF)
Como ler e combinar dados de múltiplos timeframes em MQL5: CopyRates, CopyBuffer com timeframe externo, sincronização de barras e padrões MTF em Expert Advisors.