Implementação do mecanismo de breakeven em MQL5 (Parte 1): Classe base e modo de breakeven por pontos fixos
- Introdução
- O que é o modo de breakeven
- Estruturas e enumerações necessárias
- Criação da classe base CBreakEvenBase
- Implementação das funções gerais da classe
- Desenvolvimento da primeira classe CBreakEvenSimple
- Testes do modo de breakeven
- Considerações finais
Introdução
Mover o Stop Loss para o breakeven (ponto de equilíbrio) é uma técnica usada no trading para um gerenciamento mais seguro de posições abertas. Ela consiste em deslocar o nível de Stop Loss para o preço de abertura da operação depois que ela avançou uma determinada quantidade de pontos no lucro. Assim, é possível proteger a posição e reduzir a perda em caso de um recuo inesperado.
A aplicação do mecanismo de breakeven inclui dois abordagens principais:
- permite garantir que, se o preço não atingir o nível-alvo de lucro do Take Profit, a operação não será encerrada com prejuízo;
- é usado para travar lucro ao definir o Stop Loss alguns pontos acima do preço de entrada.
Nesta série de artigos, desenvolveremos três variantes de implementação do mecanismo de breakeven. E agora, nesta primeira parte, criaremos a classe base do sistema e programaremos o primeiro tipo simples de breakeven, que servirá como modelo para futuras expansões.
O que é o modo de breakeven
Antes de integrar o conceito de breakeven ao nosso sistema, é importante entender o mecanismo básico do seu funcionamento.
De forma simplificada, o modo de breakeven pressupõe mover o nível de Stop Loss em uma quantidade fixa de pontos (pontos adicionais) depois que o preço se deslocou uma determinada distância a partir do preço de abertura da operação.

Figura 1. Posição de venda antes da modificação do nível de Stop Loss
Na Figura 1, vê-se que o Stop Loss inicialmente fica no nível do sinal gerado pelo indicador Order Blocks (um indicador desenvolvido em artigos anteriores de outra série).

Figura 2. Posição de venda após a modificação do nível de Stop Loss
Na Figura 2, é mostrado como, após o surgimento de um novo candle, o sistema move automaticamente o nível de Stop Loss para a distância definida, correspondente aos 150 pontos configurados. Esse comportamento reflete o processo de ativação que foi descrito acima.
Em seguida, analisaremos o que ocorreu após a ativação do modo de breakeven.

Figura 3: Posição de venda após o fechamento no nível de breakeven
Como pode ser visto na Figura 3, a posição foi encerrada no nível de breakeven com um pequeno lucro graças ao ajuste previamente configurado. Neste caso específico, foi possível evitar uma perda total (Stop Loss), pois o preço não se afastou significativamente do ponto de entrada.
Essa é uma das vantagens do mecanismo de breakeven: ele permite garantir um lucro potencial ou evitar perdas totais.
No entanto, é importante observar que a garantia de lucro depende de quantos pontos adicionais o nível de breakeven foi configurado:
Por outro lado, também existem algumas desvantagens na aplicação desse método. Em alguns casos, se o Stop Loss não tivesse sido movido, a operação poderia ter alcançado o nível-alvo de lucro (Take Profit). Essa situação depende em grande parte da estratégia utilizada.
No nosso caso, ao trabalhar com sinais de Order Blocks, a aplicação do modo de breakeven geralmente é justificável, pois há situações em que os blocos de ordens não se confirmam, e a proteção das posições abertas se torna criticamente importante.
Modo de breakeven baseado em ATR
Outra opção de gerenciamento é a conversão dinâmica do Stop Loss para breakeven com base no ATR (Média de Amplitude de Variação).
Esse método é semelhante ao anterior, mas em vez de usar uma quantidade fixa de pontos, utiliza-se o parâmetro multiplier. O multiplier permite calcular a distância ideal para o ajuste do Stop Loss levando em consideração a volatilidade atual do mercado. Essa abordagem se mostra mais flexível ao trabalhar com ativos de alta volatilidade, como o ouro.
Por exemplo, se estiver configurado um nível fixo de breakeven em 150 pontos (equivalente a 1,50 USD no ouro), em períodos que antecedem notícias econômicas, o preço do ouro pode facilmente subir para 3,00 USD ou mais. Nessas situações, o uso do ajuste dinâmico baseado em ATR ajuda a se adaptar melhor ao comportamento do mercado, impedindo que oscilações comuns fechem a operação prematuramente.
Dessa forma, o método baseado em ATR não utiliza distâncias fixas, mas se adapta dinamicamente às condições atuais.
Modo de breakeven baseado na relação Risk-Reward Ratio (RRR)
Por fim, é possível mover o Stop Loss para breakeven com base na relação entre risco e retorno (Risk-Reward Ratio, RRR). O RRR representa a proporção entre o risco assumido e o lucro esperado da operação. Ele é calculado dividindo-se o valor do Take Profit pelo valor do Stop Loss.
A compreensão do RRR é importante para um gerenciamento de posições mais eficiente:
- quando o RRR é mais alto, a porcentagem de operações bem-sucedidas geralmente diminui, pois o preço precisa percorrer uma distância maior para atingir o objetivo;
- quando o RRR é mais baixo, a probabilidade de alcançar o Take Profit aumenta, mas as perdas potenciais podem ser maiores se o mercado se mover contra a posição.
A lógica de aplicação do modo de breakeven com base no RRR é simples. Suponhamos que uma posição seja aberta com um Take Profit duas vezes maior que o Stop Loss, ou seja, com um RRR de 1:2.
Ao utilizar esse método, o Stop Loss pode ser configurado para se mover até o preço de entrada quando a operação atingir a relação de 1:1 em relação ao risco inicial. Isso significa que, assim que o mercado avançar o suficiente para equilibrar o risco assumido, a operação será automaticamente protegida.
Por exemplo:
- ao atingir a relação de 1:1, o Stop Loss pode ser movido para proteger a posição;
- ele também pode ser configurado para avançar ainda mais, por exemplo, para as relações 1:2, 1:3 e assim por diante, dependendo do tipo de estratégia utilizada.
Essa abordagem permite proteger as operações depois que o mercado demonstrou um movimento suficiente em uma direção favorável, com base em uma determinada relação entre risco e retorno.
Estruturas e enumerações necessárias
Nesta seção, começaremos a programar as bases do mecanismo de breakeven. Antes de criar a classe principal, definiremos as estruturas e enumerações necessárias. O processo será claro, simples e direto. Não utilizaremos todas as enumerações que criamos para o gerenciamento de riscos, pois, para a implementação do breakeven, precisaremos apenas de algumas delas. Vamos começar.
Criaremos uma estrutura simples para armazenar as informações principais, que permitirá aplicar o modo de breakeven. Essa estrutura deve conter:
- o ticket da operação,
- price_to_beat, ou seja, o preço que o mercado deve superar para mover o Stop Loss para breakeven_price,
- breakeven_price, que se tornará o novo nível no qual o Stop Loss será definido após o cumprimento da condição,
- o tipo da operação para a correta determinação dos níveis.
A estrutura terá a seguinte forma:
struct position_be { ulong ticket; //Position Ticket double breakeven_price; //Be price double price_to_beat; //Price to exceed to reach break even ENUM_POSITION_TYPE type; //Position type };
Enumeração para o tipo de breakeven
Criamos uma enumeração que permite escolher um dos três tipos de ativação do modo de breakeven mencionados anteriormente, desenvolvidos para otimizar o gerenciamento de cada operação:
enum ENUM_BREAKEVEN_TYPE { BREAKEVEN_TYPE_RR = 0, //By RR BREAKEVEN_TYPE_FIXED_POINTS = 1, //By FixedPoints BREAKEVEN_TYPE_ATR = 2 //By Atr };
Enumeração para o modo de breakeven
Nem todas as estratégias de trading funcionam da mesma forma. Por isso, criamos uma enumeração que permite escolher de maneira inequívoca como exatamente o modo de breakeven será aplicado às operações abertas.
enum ENUM_BREAKEVEN_MODE
{
BREAKEVEN_MODE_AUTOMATIC,
BREAKEVEN_MODE_MANUAL
}; - O modo BREAKEVEN_MODE_AUTOMATIC fixa automaticamente todas as novas operações por meio do OnTradeTransaction. O trader não precisa fazer nada adicional. O sistema detecta, protege e gerencia as operações instantaneamente.
- O modo BREAKEVEN_MODE_MANUAL oferece máxima flexibilidade de configuração. O trader escolhe quais tickets serão protegidos pelo mecanismo de breakeven e em que momento a revisão das condições terá início. Esse modo é ideal para estratégias que exigem seleção manual, como scalping discricionário ou setups de trading de alta precisão.
Criação da classe base CBreakEvenBase
Antes de iniciar a definição da classe base, conectaremos o gerenciamento de riscos desenvolvido nos artigos anteriores.
#include <Risk_Management.mqh> Variáveis protegidas de CBreakEvenBase
A seguir, é apresentada a definição da classe e de suas variáveis internas. Para cada variável, também é fornecida uma explicação.
//+------------------------------------------------------------------+ //| Main class to apply break even | //+------------------------------------------------------------------+ class CBreakEvenBase { protected: CTrade obj_trade; //CTrade object MqlTick tick; //tick structure string symbol; //current symbol double point_value; //value of the set symbol point position_be PositionsBe[]; //array of positions of type Positions ulong magic; //magic number of positions to make break even bool pause; //Boolean variable to activate the pause of the review, this is used to prevent the array from going out of range int num_params; //Number of parameters the class needs ENUM_BREAKEVEN_MODE breakeven_mode; //Break even mode, manual or automatic bool allow_extra_logs; . . };
Variável que envia e modifica ordens.
CTrade obj_trade; //CTrade object Estrutura MqlTick, na qual será armazenada a informação sobre o último tick registrado.
MqlTick tick; //tick structure
Nome do instrumento analisado.
string symbol; //current symbol
Valor de um ponto do símbolo selecionado.
double point_value; //value of the set symbol point
Array de posições para aplicação do modo de breakeven.
position_be PositionsBe[]; //array of positions of type Positions Identificador único das operações.
ulong magic; //magic number of positions to make break even
Indicador que suspende temporariamente a verificação.
bool pause; //Boolean variable to activate the pause of the review, this is used to prevent the array from going out of range
Quantidade de parâmetros necessários para a classe.
int num_params; //Number of parameters the class needs
Modo de aplicação do breakeven (manual ou automático).
ENUM_BREAKEVEN_MODE breakeven_mode; //Break even mode, manual or automatic Possibilidade de criação de registros adicionais (logs).
bool allow_extra_logs; Funções de CBreakEvenBase
Nesta seção, começamos a definir as funções que serão herdadas pelas diferentes classes implementadas posteriormente.
Construtor
O construtor é uma parte fundamental de qualquer classe. Na classe CBreakEvenBase, sua tarefa é inicializar as variáveis internas e atribuir a elas os valores necessários para que a classe funcione corretamente.
Esse construtor recebe três parâmetros: symbol, a partir do qual serão obtidos os dados bid e ask; magic, que permite identificar ordens específicas; e mode, que indica o tipo selecionado de gerenciamento de breakeven, que pode ser automático ou manual.
CBreakEvenBase(string symbol_, ulong magic_, ENUM_BREAKEVEN_MODE mode_);
Definição do construtor
Durante a execução do construtor, as variáveis pause e allow_extra_logs são inicializadas com o valor false. Em seguida, é verificado se o valor recebido em mode é válido. Caso não seja, uma mensagem de erro é exibida e a função ExpertRemove é chamada para remover o EA do gráfico, a fim de evitar a continuação da execução do código com uma configuração incorreta.
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CBreakEvenBase::CBreakEvenBase(string symbol_, ulong magic_, ENUM_BREAKEVEN_MODE mode_) : pause(false), allow_extra_logs(false) { if(magic_ != NOT_MAGIC_NUMBER) obj_trade.SetExpertMagicNumber(magic_); if(mode_ != BREAKEVEN_MODE_MANUAL && mode_ != BREAKEVEN_MODE_AUTOMATIC) { printf("%s:: Error critico el modo del break even %s, es invalido", __FUNCTION__, EnumToString(mode_)); ExpertRemove(); } this.symbol = symbol_; this.num_params = 0; this.magic = magic_; this.breakeven_mode = mode_; this.point_value = SymbolInfoDouble(symbol_, SYMBOL_POINT); }
Os valores correspondentes também são atribuídos aos parâmetros symbol, magic e breakeven_mode utilizando os valores recebidos como argumentos.
O valor do ponto do símbolo é armazenado na variável point_value, e a quantidade de parâmetros é inicializada com o valor "0". Cada classe que herdar de CBreakEvenBase definirá sua própria quantidade de parâmetros em seu próprio construtor.
Além disso, se a variável magic_ for igual a NOT_MAGIC_NUMBER, não executaremos a função-membro da classe CTrade destinada a definir o número mágico.
Observação: NOT_MAGIC_NUMBER — é um define criado dentro do arquivo include de gerenciamento de riscos. Esse define serve para indicar que o número mágico não será utilizado, portanto, no caso de uso do mecanismo de breakeven, ele será aplicado a todas as operações, mas somente se o modo de breakeven for automático.
Destrutor
O destrutor é usado para liberar a memória utilizada pelo array no qual as posições gerenciadas são armazenadas. Essa função é executada automaticamente quando o objeto é destruído.
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CBreakEvenBase::~CBreakEvenBase() { ArrayFree(PositionsBe); }
Implementação das funções gerais da classe
Em seguida, definiremos as funções que fazem parte da classe base CBreakEvenBase. Essas funções serão utilizadas e estendidas pelas classes derivadas que serão implementadas posteriormente.
Primeiramente, declaramos a função que retorna a quantidade de parâmetros necessários para a classe. Essa função simplesmente retorna o valor da variável protegida num_params. Além disso, ela é marcada como const, para evitar a modificação do estado do objeto, e como final, para impedir que seja sobrescrita por classes filhas.
virtual inline int GetNumParams() const final { return num_params; }
Em seguida, declaramos a função Add, que será utilizada para adicionar uma nova estrutura position_be ao array PositionsBe. Essa função receberá os dados necessários da operação, como ticket, preço de abertura, Stop Loss e tipo da posição.
virtual bool Add(ulong post_ticket, double open_price, double sl_price, ENUM_POSITION_TYPE position_type) = 0;
Cada classe que herdar de CBreakEvenBase deve implementar sua própria versão dessa função, pois ela é declarada como pura e virtual.
A principal função dessa classe é garantir o breakeven. Ela é responsável por aplicar o ajuste de breakeven a todas as posições armazenadas no array PositionsBe. Para isso, o array é percorrido por meio de um laço for.
Antes de aplicar o ajuste, é verificado se o tamanho do array é maior que zero e se a variável pause possui o valor false. Caso essas condições não sejam atendidas, a função é encerrada imediatamente.
Como a estrutura position_be já contém tanto o nível-alvo quanto o preço no qual o ajuste deve ser iniciado, é necessário apenas verificar se a condição de mercado é atendida:
- se a posição for de compra, verifica-se se o preço ask é maior ou igual ao valor price_to_beat;
- se for uma venda, verifica-se se o preço bid é menor ou igual a esse mesmo valor.
Após a confirmação da condição, a posição é identificada por meio do ticket correspondente, e o valor atual do Take Profit é calculado. Em seguida, a modificação é executada utilizando o método PositionModify da classe CTrade. Ao final, essa posição é marcada para remoção do array.
//+------------------------------------------------------------------+ //| Function to make break even | //+------------------------------------------------------------------+ void CBreakEvenBase::BreakEven(void) { if(this.PositionsBe.Size() < 1 || pause) return; SymbolInfoTick(this.symbol, tick); int indices_to_remove[]; for(int i = 0 ; i < ArraySize(this.PositionsBe) ; i++) { if((this.PositionsBe[i].type == POSITION_TYPE_BUY && tick.ask >= this.PositionsBe[i].price_to_beat) || (this.PositionsBe[i].type == POSITION_TYPE_SELL && tick.bid <= this.PositionsBe[i].price_to_beat)) { if(!PositionSelectByTicket(this.PositionsBe[i].ticket)) { printf("%s:: Error al seleccionar el ticket %I64u",__FUNCTION__,this.PositionsBe[i].ticket); ExtraFunctions::AddArrayNoVerification(indices_to_remove, i); continue; } double position_tp = PositionGetDouble(POSITION_TP); obj_trade.PositionModify(this.PositionsBe[i].ticket, this.PositionsBe[i].breakeven_price, position_tp); ExtraFunctions::AddArrayNoVerification(indices_to_remove, i); } } ExtraFunctions::RemoveMultipleIndexes(this.PositionsBe, indices_to_remove); }
Para adicionar a estrutura ao array PositionsBe no momento da abertura de uma nova operação, definimos uma função que será chamada dentro do evento OnTradeTransaction. Essa função se chama OnTradeTransactionEvent e será executada sempre que uma transação de trading ocorrer.
virtual void OnTradeTransactionEvent(const MqlTradeTransaction& trans) final;
Antes de adicionar a posição ao array PositionsBe, é necessário selecionar o ticket correspondente por meio da função HistoryDealSelect. Em seguida, o tipo de entrada na operação é determinado com base no valor entry.
Se entry corresponder à entrada no mercado, ou seja, DEAL_ENTRY_IN, verifica-se que a operação está totalmente aberta, que o modo de breakeven é automático, BREAKEVEN_MODE_AUTOMATIC, e que o número mágico da operação coincide com a variável interna magic, ou que esta última possui o valor NOT_MAGIC_NUMBER.
Quando todas as condições são atendidas, a posição é adicionada ao array PositionsBe por meio da função Add. Se allow_extra_logs estiver ativada, é exibida uma mensagem indicando o registro da posição.
No caso em que entry corresponde à saída, ou seja, DEAL_ENTRY_OUT, e a posição está completamente encerrada, o ticket correspondente é removido do array PositionsBe. Para evitar conflitos potenciais durante essa remoção, a variável pause recebe temporariamente o valor true.
O código da função é estruturado da seguinte forma:
//+------------------------------------------------------------------+ //| OnTradeTransactionEvent | //+------------------------------------------------------------------+ void CBreakEvenBase::OnTradeTransactionEvent(const MqlTradeTransaction &trans) { if(trans.type != TRADE_TRANSACTION_DEAL_ADD) return; HistoryDealSelect(trans.deal); ENUM_DEAL_ENTRY entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(trans.deal, DEAL_ENTRY); bool pos = PositionSelectByTicket(trans.position); if(breakeven_mode == BREAKEVEN_MODE_AUTOMATIC) { ulong position_magic = (ulong)HistoryDealGetInteger(trans.deal, DEAL_MAGIC); if(entry == DEAL_ENTRY_IN && pos && (this.magic == position_magic || this.magic == NOT_MAGIC_NUMBER)) { if(Add(trans.position, PositionGetDouble(POSITION_PRICE_OPEN), PositionGetDouble(POSITION_SL), (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE))) if(this.allow_extra_logs) printf("%s:: Se a añadido el ticket %I64u del array de posiciones", __FUNCTION__, trans.position); return; } } if(entry == DEAL_ENTRY_OUT && pos == false) { this.pause = true; if(ExtraFunctions::RemoveIndexFromAnArrayOfPositions(PositionsBe, trans.position)) if(this.allow_extra_logs) printf("%s:: Se a eliminado el ticket %I64u del array de posiciones", __FUNCTION__, trans.position); this.pause = false; } }
Algumas classes derivadas precisarão de configuração de diferentes parâmetros. Por exemplo, o ajuste simples necessita de dois valores inteiros, enquanto outros métodos, como o método baseado em ATR, exigem dados como período, timeframe e multiplier.
Como no classe base não é possível definir múltiplas variações de uma mesma função virtual com parâmetros diferentes, propõe-se o uso de um array do tipo MqlParam para armazenar a configuração necessária. Dessa forma, cada classe herdada poderá interpretar e atribuir internamente os valores recebidos.
virtual void Set(MqlParam ¶ms[]) = 0;
Por fim, é adicionada uma função que permite ativar ou desativar mensagens informativas adicionais. Isso é útil quando se deseja registrar ações como a adição ou remoção de tickets durante a execução do programa.
virtual void SetExtraLogs(bool allow_extra_logs_) final { this.allow_extra_logs = allow_extra_logs_; }
Desenvolvimento da primeira classe CBreakEvenSimple
Variáveis internas
Agora que a classe base está pronta, iniciaremos o desenvolvimento da classe CBreakEvenSimple. Essa classe aplica o método de breakeven utilizando uma quantidade fixa de pontos.
Para isso, são declaradas duas variáveis:
int extra_points_be, points_be; - a variável extra_points_be indica a distância adicional para a qual o nível de breakeven será movido após a ativação,
- a variável points_be define quantos pontos no lucro o preço deve percorrer antes que o ajuste seja executado.
Construtor
No construtor, é necessário definir os valores iniciais. Também é chamado o construtor da classe base para atribuir os parâmetros herdados. Dentro do corpo do construtor, as variáveis internas são inicializadas com zero, e é definida a quantidade de parâmetros necessários para a classe, que neste caso é dois.
CBreakEvenSimple(string symbol_, ulong magic_, ENUM_BREAKEVEN_MODE mode_) : CBreakEvenBase(symbol_, magic_, mode_) { this.extra_points_be = 0; this.points_be = 0; this.num_params = 2;}
Função para definir os parâmetros
Para atribuir valores às variáveis internas extra_points_be e points_be, será utilizado um array do tipo MqlParam. Essa estrutura faz parte da linguagem MQL5 e normalmente é aplicada ao adicionar indicadores por meio da função ChartIndicatorAdd().
Neste caso, ela será utilizada para passar valores para a classe. A função Set será sobrescrita em relação à classe base. Para isso, utiliza-se a palavra-chave "override".
void Set(MqlParam ¶ms[]) override;
Dentro da função, verifica-se se o tamanho do array params não é menor que dois. Se essa condição não for atendida, será exibida uma mensagem informativa de erro. Caso contrário, será chamada a função SetSimple para atribuir os valores correspondentes.
//+------------------------------------------------------------------+ //| Set attributes of CBreakEvenSimple class with MqlParam array | //+------------------------------------------------------------------+ void CBreakEvenSimple::Set(MqlParam ¶ms[]) { if(params.Size() < 2) { printf("%s:: Error setting simple break-even, the size of the params array %I32u to less than 2", __FUNCTION__, params.Size()); return; } SetSimple(int(params[0].integer_value), int(params[1].integer_value)); }
Função SetSimple
A função SetSimple permite definir diretamente os valores de points_be e extra_points_be sem o uso de um array de parâmetros. Nesse caso, os valores inteiros são recebidos diretamente na chamada da função.
//+------------------------------------------------------------------+ //| Function to set member variables without using MalParams | //+------------------------------------------------------------------+ void CBreakEvenSimple::SetSimple(int points_be_, int extra_points_be_) { if(points_be_ <= 0) { printf("%s:: Error when setting the break even value for fixed points, be points %I32d are invalid.", __FUNCTION__, extra_points_be_); ExpertRemove(); return; } if(extra_points_be_ < 0) { printf("%s:: Error when setting the break even value for fixed points, extra points %I32d are invalid.", __FUNCTION__, extra_points_be_); ExpertRemove(); return; } if(extra_points_be_ >= points_be_) { printf("%s:: Warning: The break even points (breakeven_price) is greater than the breakeven points (price_to_beat)\nTherefore the value of the extra breakeven points will be modified 0.", __FUNCTION__); this.points_be = points_be_; //0 this.extra_points_be = 0; //1 return; } this.points_be = points_be_; //0 this.extra_points_be = extra_points_be_; //1 }
Esse método pode ser útil quando os valores são definidos diretamente no código, ou quando, em alguns casos específicos, é necessário evitar o uso do tipo MqlParam.
Função para adicionar os dados das posições ao array PositionsBe
Para dar continuidade ao desenvolvimento da classe CBreakEvenSimple, é necessário sobrescrever a função Add. Essa função permite calcular e registrar os valores principais da estrutura position_be, que posteriormente será armazenada no array PositionsBe.
O parâmetro open_price é utilizado como base para o cálculo de dois valores: breakeven_price e price_to_beat. Essas variáveis definem o nível para o qual o Stop Loss será movido e o nível que o preço deve alcançar para que o ajuste seja ativado.
Para calcular o valor de breakeven_price, são utilizadas as seguintes fórmulas:
- Posições de compra:
break_even_price = open_price + (point_value * extra_points_be)
- Posições de venda:
break_even_price = open_price - (point_value * extra_points_be)
Por outro lado, para o cálculo de price_to_beat, são utilizadas as mesmas fórmulas, porém a variável extra_points_be é substituída por points_be.
Após a definição desses valores, a estrutura position_be é preenchida com os dados necessários. Em seguida, essa estrutura é adicionada ao array PositionsBe por meio da função AddArrayNoVerification, que já foi utilizada na seção anterior dedicada ao gerenciamento de riscos.
O código de implementação tem a seguinte forma:
//+----------------------------------------------------------------------------------------------+ //| Create a new structure and add it to the main array using the 'AddToArrayBe' function | //+----------------------------------------------------------------------------------------------+ bool CBreakEvenSimple::Add(ulong post_ticket, double open_price, double sl_price, ENUM_POSITION_TYPE position_type) { position_be new_pos; new_pos.breakeven_price = position_type == POSITION_TYPE_BUY ? open_price + (point_value * extra_points_be) : open_price - (point_value * extra_points_be); new_pos.type = position_type; new_pos.price_to_beat = position_type == POSITION_TYPE_BUY ? open_price + (point_value * points_be) : open_price - (point_value * points_be) ; new_pos.ticket = post_ticket; ExtraFunctions::AddArrayNoVerification(this.PositionsBe,new_pos); return true; }
Código completo da classe:
//+------------------------------------------------------------------+ //| class CBreakEvenSimple | //+------------------------------------------------------------------+ class CBreakEvenSimple : public CBreakEvenBase { private: int extra_points_be, points_be; public: CBreakEvenSimple(string symbol_, ulong magic_, ENUM_BREAKEVEN_MODE mode_) : CBreakEvenBase(symbol_, magic_, mode_) { this.extra_points_be = 0; this.points_be = 0; this.num_params = 2;} bool Add(ulong post_ticket, double open_price, double sl_price, ENUM_POSITION_TYPE position_type) override; void Set(MqlParam ¶ms[]) override; void SetSimple(int points_be_, int extra_points_be_); }; //+----------------------------------------------------------------------------------------------+ //| Create a new structure and add it to the main array using the 'AddToArrayBe' function | //+----------------------------------------------------------------------------------------------+ bool CBreakEvenSimple::Add(ulong post_ticket, double open_price, double sl_price, ENUM_POSITION_TYPE position_type) { position_be new_pos; new_pos.breakeven_price = position_type == POSITION_TYPE_BUY ? open_price + (point_value * extra_points_be) : open_price - (point_value * extra_points_be); new_pos.type = position_type; new_pos.price_to_beat = position_type == POSITION_TYPE_BUY ? open_price + (point_value * points_be) : open_price - (point_value * points_be) ; new_pos.ticket = post_ticket; ExtraFunctions::AddArrayNoVerification(this.PositionsBe, new_pos); return true; } //+------------------------------------------------------------------+ //| Set attributes of CBreakEvenSimple class with MqlParam array | //+------------------------------------------------------------------+ void CBreakEvenSimple::Set(MqlParam ¶ms[]) { if(params.Size() < 2) { printf("%s:: Error setting simple break-even, the size of the params array %I32u to less than 2", __FUNCTION__, params.Size()); return; } SetSimple(int(params[0].integer_value), int(params[1].integer_value)); } //+------------------------------------------------------------------+ //| Function to set member variables without using MalParams | //+------------------------------------------------------------------+ void CBreakEvenSimple::SetSimple(int points_be_, int extra_points_be_) { if(points_be_ <= 0) { printf("%s:: Error when setting the break even value for fixed points, be points %I32d are invalid.", __FUNCTION__, extra_points_be_); ExpertRemove(); return; } if(extra_points_be_ < 0) { printf("%s:: Error when setting the break even value for fixed points, extra points %I32d are invalid.", __FUNCTION__, extra_points_be_); ExpertRemove(); return; } if(extra_points_be_ >= points_be_) { printf("%s:: Warning: The break even points (breakeven_price) is greater than the breakeven points (price_to_beat)\nTherefore the value of the extra breakeven points will be modified 0.", __FUNCTION__); this.points_be = points_be_; //0 this.extra_points_be = 0; //1 return; } this.points_be = points_be_; //0 this.extra_points_be = extra_points_be_; //1 } //+------------------------------------------------------------------+
Com isso, a implementação básica do método de breakeven por pontos fixos é concluída. Na próxima seção, a funcionalidade dessa classe será testada utilizando o EA Order Blocks, desenvolvido no último artigo sobre gerenciamento de riscos.
Testes do modo de breakeven
Nesta seção, adaptamos o EA Order Blocks para integrar a aplicação do modo de breakeven por pontos fixos. O objetivo é verificar o funcionamento do sistema e avaliar o comportamento das posições sob esse tipo de gerenciamento ativo.
Primeiramente, alteramos os parâmetros do EA, adicionando uma nova seção especializada:
sinput group "-----| Break Even |----"
Nessa seção, serão reunidos todos os parâmetros relacionados ao mecanismo de breakeven. Como até o momento foi desenvolvido apenas o modo por pontos fixos, adiciona-se um parâmetro geral do tipo bool para sua ativação ou desativação.
input bool use_be = true; // Enable Break Even usage
Em seguida, será definido um subseção para os parâmetros específicos desse método.
sinput group "- BreakEven based on Fixed Points -" input int be_fixed_points_to_put_be = 200; // Points traveled needed to activate Break Even input int be_fixed_points_extra = 100; // Extra adjustment points when activating Break Even
Para integrar a funcionalidade, é criado um objeto do tipo CBreakEvenSimple. Posteriormente, ele será substituído por um objeto mais flexível do tipo manager, que permitirá gerenciar diferentes variantes a partir de uma interface única.
CBreakEvenSimple break_even(_Symbol, Magic, BREAKEVEN_MODE_AUTOMATIC); Na função OnInit, se o parâmetro use_be estiver habilitado, serão atribuídos os valores configurados por meio da função SetSimple.
//--- if(use_be) break_even.SetSimple(be_fixed_points_to_put_be, be_fixed_points_extra);
Para que o gerenciamento de breakeven funcione corretamente, é necessário que a função OnTradeTransactionEvent do objeto break_even seja executada dentro da função OnTradeTransaction. Ela será responsável pelo registro ou remoção das operações no array correspondente.
//+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { risk.OnTradeTransactionEvent(trans); break_even.OnTradeTransactionEvent(trans); }
Por fim, dentro de OnTick, a função de breakeven será chamada apenas se o uso desse mecanismo estiver habilitado pelo usuário:
if(use_be) break_even.BreakEven(); Graças a essas alterações, o robô agora pode aplicar automaticamente o mecanismo de breakeven quando as condições definidas forem atendidas.
Backtest
O backtest do EA Order Blocks com o modo de breakeven foi realizado sem restrições de gerenciamento de capital. Não foram definidos limites nem de lucro nem de prejuízo. As operações podiam ser encerradas apenas ao atingir os níveis de Stop Loss ou Take Profit. Foi utilizada a relação Risk:Reward de 1:2, em que o Take Profit é duas vezes maior que o Stop Loss.
1. Configurações gerais:

Figura 4. Configurações gerais para o backtest
2. Gráfico do backtest do funcionamento da estratégia sem o mecanismo de breakeven.

Figura 5. Gráfico do backtest sem o uso do mecanismo de breakeven
Na Figura 5 é mostrado o comportamento do EA sem o uso do mecanismo de breakeven. Durante esse teste, o saldo inicial de 15 000 USD caiu para 8 700 USD, o que resultou em uma perda de aproximadamente 7 000 USD. Esse resultado serve como referência para avaliar as consequências de ativar ou desativar essa funcionalidade.
3. Gráfico do backtest do funcionamento da estratégia com o mecanismo de breakeven.

Figura 6. Gráfico do backtest com o uso do mecanismo de breakeven
O segundo teste foi realizado com o modo de breakeven ativado. O parâmetro be_fixed_points_extra foi definido em 100 pontos, e o parâmetro be_fixed_points_to_put_be em 600 pontos.
Durante esse teste, o gráfico apresentou um desenvolvimento mais lento. Em séries de operações perdedoras, a redução do saldo foi menor. O saldo caiu de 10 900 USD para 7 900 USD, o que representa uma perda de 3 000 USD. Essa diferença de 3 000 USD em comparação com o primeiro cenário indica que o uso do mecanismo de breakeven pode ajudar a reduzir o impacto de sequências negativas.
No entanto, também foram identificadas limitações. Em ambos os testes, as operações começaram com perdas até aproximadamente março de 2024. A partir de 01.03.2024, foram observadas mudanças: surgiu uma sequência de operações lucrativas. No primeiro backtest, o saldo ultrapassou 16 000 USD, enquanto no segundo, com o modo de breakeven ativado, ele atingiu apenas 10 900 USD.
Uma das possíveis explicações são os fechamentos antecipados causados pelo modo de breakeven. Se, após a abertura da posição, o preço recua, mas não atinge o nível de Stop Loss, na ausência do mecanismo de breakeven ele pode retornar à direção esperada e ser fechado com lucro. Em contrapartida, com o modo de breakeven ativado, a posição será encerrada no zero, eliminando a possibilidade de obter esse lucro.
Isso permite supor que, no primeiro caso, muitas operações permaneceram em saldo negativo até atingirem o Take Profit. Por esse motivo, ao aplicar o modo de breakeven por pontos fixos, o resultado pode ser limitado em cenários nos quais os recuos de preço ocorrem com frequência.
Em condições de alta volatilidade, essa configuração pode ser ativada rapidamente, protegendo o capital, mas ao mesmo tempo fechando operações de forma prematura. Em períodos de menor atividade, ela pode não ser acionada. Assim, o uso do modo de breakeven por pontos fixos é mais adequado para traders que priorizam a proteção do saldo, e não a obtenção de um lucro maior por operação.
Considerações finais
Neste artigo, analisamos o conceito de breakeven, sua implementação em MQL5 e algumas variações possíveis. Tudo o que foi desenvolvido foi aplicado ao EA Order Blocks, criado no último artigo sobre gerenciamento de riscos.
Ao final da análise, foi realizada uma comparação do comportamento do sistema com o modo de breakeven ativado e sem ele. Observou-se que, em séries de operações lucrativas, seu uso pode limitar o crescimento do saldo. Por outro lado, durante séries de perdas, o modo de breakeven reduz a exposição a prejuízos maiores. Essa dualidade indica a existência de um certo equilíbrio entre risco e resultado, embora a eficácia real dependa dos resultados estatísticos da estratégia base sem restrições externas, como limites de lucro ou de perda.
Também chegamos à conclusão de que o uso do modo de breakeven é mais adequado para traders que não buscam uma abordagem agressiva, mas preferem um desenvolvimento mais estável dos resultados, com maior controle sobre as saídas das operações.
Arquivos utilizados/atualizados neste artigo:
| Nome do arquivo | Tipo | Descrição |
|---|---|---|
| Risk_Management.mqh | .mqh (arquivo de cabeçalho) | Contém a classe de gerenciamento de risco desenvolvida no último artigo da série sobre gerenciamento de riscos. |
| Order_Block_Indicador_New_Part_2.mq5 | .mq5 (indicador) | Contém o código do indicador Order Block. |
| Order Block EA MetaTrader 5.mq5 | .mq5 (EA) | Código do robô Order Block com o modo de breakeven integrado. |
| OB_SET_WITHOUT_BREAKEVEN.set | .set (arquivo de configuração) | Configurações do primeiro backtest, sem o mecanismo de breakeven. |
| OB_SET_WITH_BREAKEVEN.set | .set (arquivo de configuração) | Configurações do segundo backtest com o modo de breakeven ativado. |
| PositionManagement.mqh | .mqh (arquivo de cabeçalho) | Arquivo mqh que contém o código do mecanismo de breakeven. |
Traduzido do espanhol pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/es/articles/17957
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.
MQL5 Trading Toolkit (Parte 7): Expandindo a Biblioteca EX5 de Gerenciamento de Histórico com as Funções da Última Ordem Pendente Cancelada
Simulação de mercado: A união faz a força (III)
Redes neurais em trading: Previsão de séries temporais com o auxílio da decomposição modal adaptativa (ACEFormer)
Do básico ao intermediário: Sobrecarga de operadores (IV)
- 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