Desenvolvendo um EA Dinâmico para Múltiplos Pares (Parte 2): Diversificação e Otimização de Portfólio
Introdução
Um dos desafios mais persistentes no trading profissional está em manter a consistência do portfólio e protocolos robustos de gerenciamento de risco. Os traders frequentemente apresentam uma dependência excessiva de ativos ou estratégias individuais, aumentando a exposição a drawdowns substanciais durante mudanças abruptas nos regimes de mercado. Agravando esse risco está a tendência predominante de utilizar alavancagem excessiva em instrumentos correlacionados, o que aumenta a probabilidade de perdas simultâneas e compromete a estabilidade dos retornos. Na ausência de um portfólio rigorosamente diversificado e otimizado, os traders enfrentam resultados de desempenho irregulares, frequentemente levando a decisões motivadas pela emoção e a uma rentabilidade volátil. Portanto, uma estrutura sistemática que equilibre estrategicamente os retornos ajustados ao risco em um conjunto de ativos não correlacionados é indispensável para um desempenho sustentável no longo prazo.
Para mitigar esses desafios, uma metodologia quantitativa que integre otimização de portfólio, diversificação de múltiplos ativos e uma estratégia de trading de breakout aprimorada pela confirmação baseada em oscilador oferece uma solução robusta. Ao implementar uma estratégia de breakout em múltiplos pares de moedas, os traders podem aproveitar movimentos de preço impulsionados por momentum e de alta probabilidade, ao mesmo tempo em que distribuem o risco por meio da exposição a mercados não correlacionados. A integração de um indicador oscilador serve para validar os sinais de entrada, minimizando a participação em falsos breakouts e reduzindo operações pouco eficientes. Essa abordagem não apenas aumenta o potencial de lucro, mas também fortalece a estabilidade do portfólio ao explorar sistematicamente oportunidades em diferentes fases do mercado. A estratégia resultante demonstra maior resiliência à volatilidade, garantindo um desempenho consistentemente alinhado às condições macroeconômicas e técnicas em constante evolução.
Lógica do Expert Advisor
Modelo de Compra:
O Expert Advisor começa calculando o intervalo de preços entre 10:00 e 12:00 UTC+2, identificando a máxima mais alta e a mínima mais baixa dentro desse período. Às 12:00 ou a qualquer momento posterior, se o preço romper acima da máxima previamente estabelecida, uma potencial oportunidade de compra é acionada. No entanto, para confirmação, o Stochastic Oscillator deve estar no nível 20 ou abaixo dele, indicando uma condição de mercado sobrevendido. Isso garante que o breakout não esteja excessivamente estendido e ainda tenha espaço para momentum de alta. Quando ambas as condições são atendidas, o EA executa uma ordem de compra, buscando aproveitar o breakout enquanto minimiza sinais falsos por meio da filtragem baseada no oscilador.

Modelo de Venda:
Para o modelo de venda, o Expert Advisor segue o mesmo processo de cálculo do intervalo entre 10:00 e 12:00 UTC+2, identificando a máxima e a mínima da sessão. Se, às 12:00 ou posteriormente, o preço romper abaixo da mínima estabelecida, uma oportunidade de venda é considerada. No entanto, o trade somente é executado se o Stochastic Oscillator estiver no nível 80 ou acima dele, indicando uma condição de mercado sobrecomprado. Isso garante que o breakout de baixa seja sustentado por uma potencial pressão vendedora, em vez de ser um movimento falso. Quando ambas as condições estão alinhadas, o EA abre uma posição vendida, aproveitando o momentum de baixa enquanto evita entradas prematuras.

Primeiros passos:
//+------------------------------------------------------------------+ //| Dyna MP.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include <Trade/Trade.mqh> CTrade trade; enum Signal_Breakout{ Normal_Signal, Reversed_Signal, };
`#include <Trade/Trade.mqh>` Esta linha inclui a biblioteca de negociação do MQL5 (\Trade.mqh`). O arquivo de cabeçalho `Trade.mqh` define classes e funções que simplificam tarefas de trading, como iniciar, ajustar ou fechar ordens. Aqui, um objeto denominado `trade` é instanciado a partir da classe `CTrade`. Essa classe contém métodos para executar operações de trading (por exemplo, enviar solicitações de compra/venda, modificar trades abertos ou fechar posições) de maneira estruturada.
input group "--------------General Inputs--------------" input string Symbols = "XAUUSD, GBPUSD, USDCAD, USDJPY"; input Signal_Breakout BreakOutMode = Reversed_Signal; input double In_Lot = 0.01; input int TakeProfit = 500; double StopLoss = 500; input bool TrailYourStop = false; input int TrailingStop = 50; // Stochastic input int KPeriod = 21; input int upprer_level = 80;
`Symbols` especifica os ativos a serem negociados (por exemplo, "XAUUSD, GBPUSD"), `In_Lot` define o tamanho fixo do trade (0,01 lote), `TakeProfit` (500 pontos) e `StopLoss` (1000 pontos) definem os limites de realização de lucro e de risco, enquanto `TrailYourStop` e `Trailing Stop` (70 pontos) ativam e ajustam dinamicamente o trailing do stop-loss. Para o Stochastic Oscillator, `KPeriod` (20) determina o período de cálculo da linha percentual, e `upper_level` (80) define o limite de sobrecompra. Esses parâmetros de entrada, em conjunto, equilibram as regras de execução de trades, o gerenciamento de risco e a geração de sinais técnicos.
//+------------------------------------------------------------------+ //| Global vars | //+------------------------------------------------------------------+ int handles[]; double bufferM[]; int RangeStart = 600; int RangeDuration = 120; int RangeClose = 1200; int Num_symbs = 0; string symb_List[]; string Formatted_Symbs[];
Esta seção define as variáveis globais. O array `handles` armazena handles para o Stochastic Oscillator, permitindo que o EA gerencie múltiplos ativos de forma eficiente. O array `bufferM` armazena valores calculados, como leituras de indicadores ou níveis históricos de preço. A variável `RangeStart` é definida como 600 minutos (10:00 UTC+2), marcando o início da medição do intervalo, enquanto `RangeDuration` é definida como 120 minutos, estabelecendo um período de 2 horas para capturar a máxima e a mínima. Quando o parâmetro `RangeClose` é definido como 1200 (representando 12:00 UTC+2 na lógica adotada), momento em que o EA do intervalo e começa a monitorar as condições de breakout.
Para o gerenciamento de símbolos, o array `symb_List` contém a lista bruta de símbolos a serem processados. O array \Formatted_Symbs` armazena os símbolos formatados resultantes do processamento da entrada `Symbols`, ou seja, a lista final de ativos a ser utilizada. Essas variáveis, em conjunto, permitem que o EA execute trades dinamicamente em múltiplos ativos, garantindo simultaneamente cálculos precisos do intervalo para a detecção de breakouts.
//+------------------------------------------------------------------+ //| Ranger Global Vars | //+------------------------------------------------------------------+ struct RANGER{ datetime start_time; datetime end_time; datetime close_time; double high; double low; bool b_entry; bool b_high_breakout; bool b_low_breakout; RANGER() : start_time(0), end_time(0), close_time(0), high(0), low(999999), b_entry(false), b_high_breakout(false), b_low_breakout(false) {}; };
A struct `Ranger` armazena e gerencia os principais pontos de dados da estratégia de breakout de intervalo. Ela define uma maneira estruturada de acompanhar os limites temporais do intervalo, os níveis de preço e as condições de breakout. As variáveis `start_time`, `end_time` e `close_time` representam os horários de início, fim e fechamento do cálculo do intervalo, garantindo que o EA identifique corretamente a janela de breakout. As variáveis `high` e `low` armazenam, respectivamente, os valores máximo e mínimo de preço registrados no intervalo, sendo `low` inicializada com um valor muito alto (999999) para garantir atualizações precisas dos preços. O `low (999999)` é um padrão comum para localizar valores mínimos em séries de preços.
Além disso, temos três flags booleanas. A `b_entry` indica se uma entrada de trade já foi executada, evitando múltiplas operações dentro do mesmo evento de breakout. `b_high_breakout` sinaliza se o preço rompeu acima da máxima do intervalo, confirmando um potencial sinal de compra, e `b_low_breakout` indica se o preço rompeu abaixo da mínima do intervalo, confirmando um possível setup de compra. O construtor `Ranger()` inicializa todos os valores com estados padrão, garantindo que a struct comece com dados limpos antes de ser atualizada dinamicamente durante o trading em tempo real.
RANGER rangeArray[];
MqlTick prevTick[], currTick[]; Essas variáveis desempenham funções cruciais no acompanhamento da ação do preço e no gerenciamento da lógica de breakout de intervalo no Expert Advisor. O rangeArray é um array de estruturas `RANGER`, que armazena múltiplas instâncias dos parâmetros de breakout de intervalo definidos. Isso permite que o EA acompanhe múltiplos símbolos simultaneamente, garantindo que cada símbolo tenha seus próprios dados de intervalo, incluindo horários de início e fim, preços máximos e mínimos e condições de breakout.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ string separator = ","; ushort usprtr; usprtr = StringGetCharacter(separator, 0); StringSplit(Symbols, usprtr, symb_List); Num_symbs = ArraySize(symb_List); ArrayResize(Formatted_Symbs, Num_symbs); for(int i = 0; i < Num_symbs; i++){ Formatted_Symbs[i] = symb_List[i]; } ArrayResize(rangeArray, Num_symbs); ArrayResize(prevTick, Num_symbs); ArrayResize(currTick, Num_symbs); ArrayResize(handles, Num_symbs); ArraySetAsSeries(bufferM, true); // Calculate initial ranges for each symbol for (int i = 0; i < ArraySize(Formatted_Symbs); i++) { CalculateRange(i, Formatted_Symbs[i]); // Pass the symbol index handles[i] = iStochastic(Formatted_Symbs[i], PERIOD_CURRENT, KPeriod, 1, 3, MODE_SMA, STO_LOWHIGH); if(handles[i] == INVALID_HANDLE){ Alert("Failed to create indicator handle"); return INIT_FAILED; } StopLoss = SymbolInfoDouble(Formatted_Symbs[i], SYMBOL_POINT)*TrailingStop; } return(INIT_SUCCEEDED); }
A função `OnInit` é responsável por inicializar e configurar as principais variáveis, gerenciar os símbolos e preparar as estruturas de dados necessárias. Continuamos utilizando o separador , para dividir a lista de símbolos em ativos individuais negociáveis, que são armazenados em `symb_List`. Determinamos o número total de símbolos `Num_symbs` e, em seguida, redimensionamos o array `Formatted_Symbs` de acordo, garantindo que cada símbolo esteja devidamente formatado e acessível para negociação.
Em seguida, o EA redimensiona dinamicamente diversos arrays, incluindo `rangeArray` para armazenar os dados de breakout de intervalo, `prevTick` e `currTick` para acompanhar as atualizações de preço, `handles` para o Stochastic Oscillator e `bufferM` para os valores do indicador, garantindo que todas as estruturas de dados necessárias sejam devidamente alocadas antes que o EA inicie sua execução.
Depois que os arrays são configurados, o EA percorre cada símbolo em `Formatted_Symbs` para calcular o intervalo inicial utilizando a função `CalculateRange()`. Ele também cria um handle do Stochastic Oscillator (istochastic) para cada símbolo, que utilizaremos posteriormente para confirmar breakouts verificando as condições de sobrecompra e sobrevenda. Se o handle do indicador não for inicializado corretamente (INVALID_HANDLE), o EA dispara um alerta e encerra com o status INIT_FAILED. Além disso, calculamos o Stop Loss para cada símbolo com base em seu valor do ponto e em um parâmetro de trailing stop. Se tudo for inicializado com sucesso, a função retorna INIT_SUCCEEDED, permitindo que o EA prossiga com o monitoramento dos movimentos de preço e a execução dos trades.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ for(int i = 0; i < ArraySize(Formatted_Symbs); i++){ if(handles[i] != INVALID_HANDLE){ IndicatorRelease(handles[i]); } } }
A função OnDeinit() é a função de desinicialização do Expert Advisor (EA), responsável por liberar adequadamente os recursos quando o EA é removido do gráfico ou interrompe sua execução.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ for(int i = 0; i < ArraySize(Formatted_Symbs); i++){ string symbol = Formatted_Symbs[i]; prevTick[i] = currTick[i]; SymbolInfoTick(symbol, currTick[i]); // Range Cal if(currTick[i].time > rangeArray[i].start_time && currTick[i].time < rangeArray[i].end_time){ // flag rangeArray[i].b_entry = true; // high if(currTick[i].ask > rangeArray[i].high){ rangeArray[i].high = currTick[i].ask; } // low if(currTick[i].bid < rangeArray[i].low){ rangeArray[i].low = currTick[i].bid; } } // now calculate range if(((RangeClose >= 0 && currTick[i].time >= rangeArray[i].close_time) || (rangeArray[i].b_high_breakout && rangeArray[i].b_low_breakout) || (rangeArray[i].end_time == 0) || (rangeArray[i].end_time != 0 && currTick[i].time > rangeArray[i].end_time && !rangeArray[i].b_entry))){ CalculateRange(i, Formatted_Symbs[i]); } checkBreak(i, Formatted_Symbs[i]); } }
A função `OnTick` é o principal loop de execução do Expert Advisor (EA), sendo executada a cada nova atualização de tick. Ela gerencia a análise de preços em tempo real, ajusta os parâmetros do intervalo e avalia oportunidades de breakout em todos os instrumentos de negociação configurados. O processo começa avaliando sequencialmente cada símbolo da lista `Formatted_Symbs`. Para cada símbolo, ela copia os dados do tick atual em um buffer histórico (prevTick) e recupera o estado mais recente do mercado por meio de `SymbolInfoTick`.
Durante a janela de negociação ativa predefinida (start_time a end_time), o sistema acompanha dinamicamente os extremos de preço. Ele eleva a máxima da sessão utilizando o maior valor de ask e reduz a mínima da sessão utilizando o menor valor de bid. Se o horário atual estiver dentro desse intervalo, a flag b_entry é ativada, indicando que o intervalo é válido para negociação.
Após a janela de negociação, a lógica avalia três gatilhos de renovação:
- Fechamento programado da sessão `(close_time)`
- Ocorrências simultâneas de breakout da máxima e da mínima
- Timestamps de `end_time` inválidos ou expirados
//+------------------------------------------------------------------+ //| Range Calculation function | //+------------------------------------------------------------------+ void CalculateRange(int index, string symbol) { for(index = 0; index < ArraySize(Formatted_Symbs); index++){ symbol = Formatted_Symbs[index]; // Reset all the range variables rangeArray[index].start_time = 0; rangeArray[index].end_time = 0; rangeArray[index].close_time = 0; rangeArray[index].high = 0.0; rangeArray[index].low = 999999; rangeArray[index].b_entry = false; rangeArray[index].b_high_breakout = false; rangeArray[index].b_low_breakout = false; // Calculate range start time int time_cycle = 86400; rangeArray[index].start_time = (currTick[index].time - (currTick[index].time % time_cycle)) + RangeStart * 60; for(int i = 0; i < 8; i++){ MqlDateTime tmp; TimeToStruct(rangeArray[index].start_time, tmp); int dotw = tmp.day_of_week; if(currTick[index].time >= rangeArray[index].start_time || dotw == 6 || dotw == 0){ rangeArray[index].start_time += time_cycle; } } // Calculate range end time rangeArray[index].end_time = rangeArray[index].start_time + RangeDuration * 60; for(int i = 0 ; i < 2; i++){ MqlDateTime tmp; TimeToStruct(rangeArray[index].end_time, tmp); int dotw = tmp.day_of_week; if(dotw == 6 || dotw == 0){ rangeArray[index].end_time += time_cycle; } } // Calculate range close rangeArray[index].close_time = (rangeArray[index].end_time - (rangeArray[index].end_time % time_cycle)) + RangeClose * 60; for(int i = 0; i < 3; i++){ MqlDateTime tmp; TimeToStruct(rangeArray[index].close_time, tmp); int dotw = tmp.day_of_week; if(rangeArray[index].close_time <= rangeArray[index].end_time || dotw == 6 || dotw == 0){ rangeArray[index].close_time += time_cycle; } } } }
A função `CalculateRange` inicializa e configura intervalos de negociação baseados em tempo para múltiplos símbolos. Para cada símbolo no array `Formatted_Symbs`, ela primeiro redefine os parâmetros críticos do intervalo — horários de início/fim/fechamento, limites de preço máximo/mínimo e flags de breakout — para seus valores padrão. O `start_time` é calculado alinhando o horário do tick atual a um limite diário (utilizando um time_cycle de 24 horas) e, em seguida, deslocando-o pelo `RangeStart` definido pelo usuário (em minutos). Um loop garante que o horário de início evite finais de semana (sábado/domingo) e permaneça cronologicamente válido, incrementando o timestamp em dias completos caso ocorram conflitos. Isso cria uma referência para a janela de negociação, respeitando os períodos de fechamento do mercado.
Após definir o horário de início do intervalo, a função calcula o horário de término do intervalo adicionando `RangeDuration` ao `start_time`. De forma semelhante ao cálculo do horário de início, ela garante que o horário de término não caia em um final de semana, percorrendo um loop de validação. Por fim, a função determina o horário de fechamento do intervalo, que marca o ponto em que o EA deixa de monitorar breakouts. Esse horário é derivado de `RangeClose` e ajustado para evitar finais de semana. Ao manter esses cálculos dinâmicos, a função garante que o EA configure com precisão as condições de negociação por intervalo em diferentes símbolos, evitando negociações durante horários sem negociação e finais de semana, ao mesmo tempo em que assegura uma detecção precisa de breakouts.
bool CLots(double sl, double &lots){ lots = In_Lot; if(!CHLots(lots)){return false;} return true; }
A função `CLots()` define e valida o tamanho do lote antes da execução de um trade; ela recebe dois parâmetros, que são o seu stop loss (sl) e lots (uma variável de referência que armazena o tamanho final do lote).
bool CHLots(double &lots){ for(int i = 0; i < ArraySize(Formatted_Symbs); i++){ string symbol = Formatted_Symbs[i]; double min = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); double max = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX); double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); if(lots < min){ lots = min; return true; } if(lots > max){ return false; } lots = (int)MathFloor(lots / step) * step; } return true; }
A função `CHLots()` valida os tamanhos dos lotes para que estejam em conformidade com as regras de negociação específicas da corretora para cada instrumento. Ela percorre a lista de símbolos em `Formatted_Symbs`, extraindo as restrições definidas pela corretora — tamanho mínimo do lote, tamanho máximo do lote e incremento permitido. Esses parâmetros estabelecem os limites operacionais para os volumes das ordens. Quando o valor de lote proposto fica abaixo do limite mínimo, a função o corrige automaticamente para o volume mínimo permitido e confirma sua validade retornando true. Por outro lado, se o tamanho de lote solicitado exceder o limite máximo da corretora, a função interrompe a solicitação retornando false, bloqueando assim trades que não estejam em conformidade.
Para garantir a precisão, a função impõe o alinhamento ao incremento permitido arredondando o tamanho do lote para baixo por meio da fórmula `MathFloor(lots/step) * step`. Isso elimina incrementos fracionários ou irregulares que poderiam provocar rejeições pela corretora. Se o tamanho do lote atender a todas as restrições sem necessidade de ajustes, a função retorna true, confirmando sua validade. Ao aplicar rigorosamente essas verificações, `CHLots()` atua como uma proteção essencial, evitando rejeições de ordens devido a violações de volume e reforçando a confiabilidade operacional do EA em ambientes de trading em tempo real.
//+------------------------------------------------------------------+ //| Check for Breakout | //+------------------------------------------------------------------+ void checkBreak(int i, string symbol) { for (i = 0; i < ArraySize(Formatted_Symbs); i++) { symbol = Formatted_Symbs[i]; //get indicator vals if(CopyBuffer(handles[i], 0, 1, 2, bufferM) != 2){ Print("Failed to get indicator values"); return; } int stopLevel = (int)SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL); int spread = (int)SymbolInfoInteger(symbol, SYMBOL_SPREAD); double Bid = SymbolInfoDouble(symbol, SYMBOL_BID); double Ask = SymbolInfoDouble(symbol, SYMBOL_ASK); if (currTick[i].time >= rangeArray[i].end_time && rangeArray[i].end_time > 0 && rangeArray[i].b_entry) { double rangeSize = rangeArray[i].high - rangeArray[i].low; // High Breakout (BUY/SELL) bool upperBreak = bufferM[0] >= upprer_level && bufferM[1] < upprer_level; bool lowerBreak = bufferM[0] <= (100 - upprer_level) && bufferM[1] > (100 - upprer_level); bool HighSigType,LowSigType; if(BreakOutMode == Normal_Signal){ HighSigType = upperBreak; }else{HighSigType = lowerBreak;} if (!rangeArray[i].b_high_breakout && currTick[i].ask >= rangeArray[i].high && HighSigType) { rangeArray[i].b_high_breakout = true; double entry = NormalizeDouble(Ask + 100 * _Point, _Digits); double sl = rangeArray[i].low; //sl = NormalizeDouble(sl, true); double tp = entry + TakeProfit * _Point; double lots; if (!CLots(entry - sl, lots)) continue; if (!trade.PositionOpen(symbol, ORDER_TYPE_BUY, lots, currTick[i].ask, sl, tp, "High Breakout")) Print("Buy Order Failed: ", GetLastError()); } if(BreakOutMode == Normal_Signal){ LowSigType = upperBreak; }else{LowSigType = lowerBreak; } // Low Breakout (SELL) if (!rangeArray[i].b_low_breakout && currTick[i].bid <= rangeArray[i].low && LowSigType) { rangeArray[i].b_low_breakout = true; double entry = NormalizeDouble(Bid - 100 * _Point, _Digits); double sl = rangeArray[i].high; //sl = NormalizeDouble(sl,true); double tp = entry - TakeProfit * _Point; double lots; if (!CLots(sl - entry, lots)) continue; if (!trade.PositionOpen(symbol, ORDER_TYPE_SELL, lots, currTick[i].bid, sl, tp, "Low Breakout")) Print("Sell Order Failed: ", GetLastError()); } } } }
Esta função monitora breakouts de preço e gerencia a execução de trades combinando movimentos de preço com sinais do Stochastic Oscillator. Ela percorre cada símbolo em `Formatted_Symbs`, tentando primeiro recuperar os valores do oscilador por meio de `CopyBuffer()`. Se essa recuperação de dados falhar, a função registra um erro e é encerrada para evitar decisões incorretas. Para cada símbolo, são obtidos parâmetros críticos, como níveis de stop, spread e preços atuais de bid/ask. As avaliações de breakout ocorrem somente após a conclusão do período do intervalo, verificada ao confirmar se o horário atual excede o `end_time` do intervalo e se a flag `b_entry` está ativa, garantindo que a análise fique restrita às janelas de negociação válidas.
Para breakouts da máxima (sinal de compra), a função confirma dois critérios: o preço ask deve ultrapassar a máxima da sessão e o oscilador deve indicar condições de sobrevenda (abaixo de 100 - upper_level). Após a validação, ela aciona uma ordem de compra — calculando o preço de entrada, o stop-loss (com base na volatilidade do intervalo) e os níveis de take-profit — e, em seguida, verifica a conformidade do tamanho do lote por meio de `CLots()`.
Por outro lado, para um breakout da mínima (sinal de venda), é necessário que o preço bid caia abaixo da mínima da sessão enquanto o oscilador indica uma condição de sobrecompra (acima de upper_level). Se essas condições forem atendidas, uma ordem de venda é gerada com parâmetros de risco equivalentes. Ambos os cenários incluem o registro de erros para ordens que não forem executadas com sucesso, garantindo transparência. Ao sincronizar os limites de preço com a confirmação baseada no oscilador, a função assegura uma execução disciplinada dos trades, orientada por critérios definidos.
//+------------------------------------------------------------------+ //| Trailing Stoploss | //+------------------------------------------------------------------+ void Trailler(){ if(!TrailYourStop) return; for(int i = PositionsTotal()-1; i >= 0; i--){ ulong ticket = PositionGetTicket(i); if(ticket <= 0) continue; if(!PositionSelectByTicket(ticket)) continue; // Get position details string symbol = PositionGetString(POSITION_SYMBOL); long magic; if(!PositionGetInteger(POSITION_MAGIC, magic)) continue; if(magic != MagicNumber) continue; // Get current prices MqlTick latestTick; if(!SymbolInfoTick(symbol, latestTick)) continue; long type; double openPrice, currentSl, currentTp; PositionGetInteger(POSITION_TYPE, type); PositionGetDouble(POSITION_PRICE_OPEN, openPrice); PositionGetDouble(POSITION_SL, currentSl); PositionGetDouble(POSITION_TP, currentTp); // Calculate pip values double pipSize = 10 * SymbolInfoDouble(symbol, SYMBOL_POINT); double currentPrice = type == POSITION_TYPE_BUY ? latestTick.bid : latestTick.ask; double priceMove = MathAbs(currentPrice - openPrice); // Calculate required moves double requiredMove = 70 * pipSize; // 20 pips double trailAmount = 10 * pipSize; // 10 pips // Calculate new stop loss double newSl = currentSl; bool inProfit = type == POSITION_TYPE_BUY ? (currentPrice > openPrice) : (currentPrice < openPrice); if(inProfit && priceMove >= requiredMove){ int steps = int(priceMove / requiredMove); if(type == POSITION_TYPE_BUY){ newSl = openPrice + (steps * trailAmount); newSl = MathMax(newSl, currentSl + trailAmount); } else{ newSl = openPrice - (steps * trailAmount); newSl = MathMin(newSl, currentSl - trailAmount); } } // Validate and modify SL if(newSl != currentSl){ // Check stop levels double minDist = SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point; newSl = NormalizeDouble(newSl, _Digits); if(type == POSITION_TYPE_BUY && (currentPrice - newSl) >= minDist){ if(!trade.PositionModify(ticket, newSl, currentTp)) Print("Buy Trailing Failed: ", GetLastError()); } else if(type == POSITION_TYPE_SELL && (newSl - currentPrice) >= minDist){ if(!trade.PositionModify(ticket, newSl, currentTp)) Print("Sell Trailing Failed: ", GetLastError()); } } } }
A função `Trailing Stop Loss` (TSL) no trading garante que, quando um trade se move favoravelmente, o stop-loss seja ajustado automaticamente para proteger os lucros enquanto minimiza os riscos. A função `Trailer()` recalcula e atualiza o nível do stop-loss com base no movimento do preço e em uma porcentagem do intervalo entre a máxima e a mínima, evitando saídas prematuras enquanto protege os ganhos.
Conclusão
Em resumo, a otimização e a diversificação de portfólio são estratégias essenciais no trading que buscam maximizar os retornos enquanto minimizam o risco por meio da distribuição dos investimentos entre múltiplos ativos. Os métodos tradicionais de trading frequentemente se concentram em estratégias de um único par, expondo os traders a maior volatilidade e a riscos específicos do mercado. A diversificação fortalece a resiliência do portfólio, enquanto os métodos de otimização alocam estrategicamente o capital analisando padrões históricos de desempenho, métricas de volatilidade dos ativos e relações entre mercados para alcançar um equilíbrio eficiente entre risco e retorno.
Em conclusão, incorporar a otimização e a diversificação de portfólio a uma estratégia de trading proporciona uma abordagem mais resiliente e adaptável às flutuações do mercado. Ao combinar uma estratégia de trading de breakout com um indicador oscilador, os traders podem identificar configurações de alta probabilidade enquanto gerenciam o risco dinamicamente. Essa abordagem aumenta as chances de rentabilidade consistente e também melhora a sustentabilidade no longo prazo ao mitigar drawdowns.
Para obter os resultados a seguir, o Expert Advisor foi testado utilizando o EURUSD como símbolo base, com atrasos de latência zero, execução ideal e modelagem de cada tick para obter a máxima precisão. O período de teste foi de 2022.02.01 a 2022.03.22. Nas configurações de entrada, o BreakoutMode foi definido como Reversed_signal, e o TrailYourStop foi habilitado (definido como true) para permitir ajustes dinâmicos do stop-loss. Todos os demais parâmetros de entrada foram mantidos em seus valores padrão.


Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/16089
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Algoritmo dos Macacos-Azuis — Blue Monkey (BM) Algorithm
Como criamos a plataforma de trading mais poderosa com Machine Learning: a crônica da evolução do MQL e do MetaTrader com base em arquivos, fóruns e releases
Redes neurais em trading: Treinamento de modelos spiking profundos (Integração de spikes)
Engenharia de Atributos com Python e MQL5 (Parte IV): Reconhecimento de Padrões de Candlestick com Regressão UMAP
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso
Ok, digitei EURUSD em “Entrada/Símbolos” e agora está funcionando.
Obrigado
Ótimo artigo! Vou tentar isso amanhã. Estou curioso para saber por que você usou um período tão estranho no testador de estratégias. Eu esperava meses inteiros em 2024. Gosto do seu conceito de stop loss móvel; estou usando a mesma técnica. Uma adaptação que fiz foi tentar também minimizar a perda caso a operação fique negativa depois de quase atingir o ponto de equilíbrio.
Um abraço e continue publicando artigos, eles são ótimos
CapeCoddah
Você poderia me ajudar? Obrigado
Cada moeda de entrada deve ser separada apenas por uma vírgula. Não coloque espaço entre as moedas
Olá novamente,
Tentei usar seu sistema em um gráfico ativo e identifiquei algumas melhorias
O problema do Alberto provavelmente foi que ele não tinha todos os pares na lista de símbolos da janela de acompanhamento de mercado, ctlM. Também recebi esse erro com o XAUUSD
Em vez de ArraySize(... nas instruções For, use Num_symbls, que é um pouco mais rápido. Além disso, percebi que escrever os nomes por extenso ajuda outras pessoas a entenderem melhor seu código e também evita muitos erros de sintaxe; por exemplo, Number_Symbols é melhor, na minha opinião, do que Num_symbls.
DisplayObjects não estava no código; eu o adicionei.
Nos objetos de exibição, adicionei uma condição para selecionar apenas o símbolo do gráfico. Enumerar os demais não é necessário e vai sobrecarregar a tela. Mas talvez eu esteja deixando passar alguma coisa.
Por fim, há um problema com o cálculo de intervalo. Em um gráfico ativo, e não no Testador de Estratégias, ao iniciar o EA é gerado um raio que se estende além da data atual, no futuro. Por exemplo, ao iniciar em 30/4, gera-se um raio que começa às 10h do dia 30/4 e termina em 1º/5. Isso resulta em um raio invisível que não aparece no gráfico, mas aparece na Lista de Objetos. Vou deixar que você corrija isso.
Estou anexando meu código para você usar
Atenciosamente, CapeCoddah