Gerenciamento de riscos (Parte 1): Fundamentos da construção de uma classe de gerenciamento de riscos
- Introdução
- O que é gerenciamento de riscos
- A importância da negociação automática
- Conceitos introdutórios de gerenciamento de riscos
- Criação do arquivo incluível e descrição do plano
- Criação de funções para calcular o tamanho do lote
- Criação de funções para obter o lucro
- Verificação na prática com a criação de um script simples com arquivo incluível
- Considerações finais
Introdução
Neste artigo, analisaremos o que é gerenciamento de riscos no trading e sua importância na negociação automática. Começaremos pelos conceitos básicos, estabelecendo a base para entender como um gerenciamento de riscos adequado pode determinar a diferença entre sucesso e fracasso nos mercados financeiros.
Em seguida, criaremos passo a passo uma classe em MQL5, que implementa um gerenciamento de riscos completo, permitindo controlar aspectos essenciais, como tamanho do lote, perdas máximas e lucro esperado.
O que é gerenciamento de riscos
O gerenciamento de riscos é um princípio fundamental de qualquer estratégia de negociação. Seu objetivo principal é monitorar e controlar as posições abertas, garantindo que as perdas não ultrapassem os limites definidos pelo trader, como limites diários, semanais ou gerais.
Além disso, o gerenciamento de riscos permite determinar o tamanho adequado do lote para cada operação com base nas regras e preferências do usuário. Isso não apenas protege o capital, mas também otimiza a eficiência da estratégia, garantindo que as operações estejam alinhadas ao perfil de risco estabelecido.
De modo geral, um gerenciamento de riscos competente não só reduz a probabilidade de perdas catastróficas, como também assegura uma abordagem disciplinada na tomada de decisões financeiras sensatas.
Importância para a negociação automática
O gerenciamento de riscos desempenha um papel decisivo na negociação automática, pois funciona como um sistema de controle que impede erros custosos, como excesso de operações ou exposição a riscos desnecessários. No contexto de bots de negociação, em que as decisões são tomadas de forma totalmente automática, um gerenciamento de riscos adequado garante uma execução disciplinada e eficiente das estratégias.
Isso é especialmente valioso em situações como a seleção para financiamento, pois o cumprimento rigoroso dos limites de perdas diárias, semanais ou gerais pode se tornar um fator decisivo para o sucesso ou o fracasso. O gerenciamento de riscos permite estabelecer com precisão essas barreiras, protegendo o capital do usuário e otimizando suas atividades em um ambiente competitivo.
Além disso, isso ajuda o bot a agir de maneira mais estratégica, estabelecendo limites claros para evitar excesso de operações ou a aceitação de riscos desproporcionais. Graças ao cálculo automático dos tamanhos de lote e à limitação das perdas por operação, o gerenciamento de riscos não apenas protege o capital, mas também proporciona ao trader tranquilidade, pois ele sabe que seu bot opera dentro de limites controlados e seguros.
Conceitos introdutórios de gerenciamento de riscos
Antes de iniciar a programação, é importante entender as principais variáveis e conceitos que utilizaremos no gerenciamento de riscos. Esses conceitos são fundamentais para desenvolver um sistema eficaz, que proteja o capital do usuário e garanta uma negociação controlada. A seguir, analisaremos cada um deles:
1. Perda máxima diária
É o limite máximo de perda que o bot pode acumular em um único dia (24 horas). Se esse limite for alcançado, o bot provavelmente fechará todas as posições abertas e suspenderá qualquer atividade de negociação até o próximo dia. Essa abordagem ajuda a evitar que uma sequência de operações malsucedidas cause impacto significativo no capital.
2. Perda máxima semanal
Funciona de forma semelhante à perda diária, mas aplicada ao intervalo de uma semana. Se o bot ultrapassar o limite definido, ele encerrará as operações até o início da semana seguinte. Esse parâmetro é ideal para impedir perdas significativas em horizontes temporais mais longos.
3. Perda máxima geral
É o limite absoluto de perdas, atingido o qual é ativada uma estratégia especial de recuperação. Ela pode incluir a reduçãao dos tamanhos dos lotes e uma negociação mais cautelosa, com o objetivo de recuperar gradualmente o capital perdido. Esse conceito permite controlar o risco total da conta.
4. Perda máxima por operação
Define a perda máxima que pode ser permitida em uma única operação. Esse valor é crucial, pois permite calcular automaticamente o tamanho ideal do lote para cada negociação conforme o nível de risco que o usuário está disposto a assumir.
5. Lucro diário, semanal e geral
Essas variáveis registram o lucro acumulado em diferentes períodos de tempo. Essas métricas são úteis para avaliar a eficiência do bot e ajustar estratégias com base nos resultados obtidos.
Criação do arquivo incluível e descrição do plano
Nesta seção, começaremos a programar o arquivo incluível.
1. Na parte superior da nossa plataforma Metatrader, encontramos o item "IDE" e clicamos nele:
![]()
2. No canto superior esquerdo do MetaEditor, selecionamos a aba "Arquivo", depois "Novo", e deve aparecer algo como o seguinte:

3. Selecionamos "Include" e clicamos em "Avançar":

4. Configuramos o arquivo incluível, definindo para ele um nome e o autor:

Com isso, a criação do arquivo é concluída, embora este seja apenas o começo. Agora descreverei detalhadamente o plano de funcionamento do sistema de gerenciamento de riscos.
A seguir é apresentada uma representação esquemática de como o gerenciamento de riscos irá funcionar:

| Seção | Descrição | Frequência de execução |
|---|---|---|
| 1. Variáveis de configuração do cálculo | Nesse estágio inicial, executado apenas uma vez, são definidas todas as variáveis necessárias para calcular perdas e lucros. As principais tarefas incluem:
| Executado apenas uma vez ou ao configurar o EA. |
| 2. Cálculo de lucros e perdas | Neste estágio, calcula-se o estado atual de lucros e perdas da conta. Inclui:
| Executado diariamente, na abertura de uma operação, ou semanalmente — conforme as configurações. |
| 3. Verificação em tempo real | No modo em tempo real, o EA realiza uma verificação constante em cada tick, para garantir que as perdas atuais não ultrapassem os limites definidos. Se algum dos valores de perda for excedido, o EA fechará imediatamente todas as posições abertas, para impedir novas perdas. | A cada tick (processo em tempo real). |
Com tudo o que foi exposto, passamos à criação das primeiras funções.
Criação de funções para calcular o tamanho do lote
Antes de começar o desenvolvimento da classe, é necessário primeiro criar as funções que permitirão calcular o lote.
Cálculo do lote ideal
Para determinar o lote ideal, precisamos inicialmente calcular o tamanho total do lote, isto é, o máximo que nossa conta pode comprar ou vender. Esse cálculo é baseado no conhecimento da margem necessária para negociar um lote na moeda da conta. Depois de obter esse valor, dividimos a margem livre da conta pela margem inicial exigida, ajustamos o resultado por arredondamento e assim obtemos o lote máximo permitido para nossa conta.
Requisitos preliminares
Antes de iniciar os cálculos, é necessário determinar a margem exigida para um lote de qualquer símbolo. Neste exemplo, o símbolo utilizado será o ouro, porém esse processo se aplica a qualquer outro símbolo financeiro.
O objetivo principal é obter uma base confiável para o cálculo eficiente dos lotes, adaptada ao saldo e à margem disponível em nossa conta de negociação.
Como podemos ver, a margem inicial aproximada para comprar um lote de ouro é de 1 326 USD. Portanto, para calcular o lote máximo permitido, basta dividir a margem livre disponível na conta pela margem inicial exigida. Essa relação pode ser representada da seguinte forma:

Margem livre:
- Este é o capital disponível em sua conta que pode ser utilizado para abrir novas operações. No MetaTrader, ele é calculado da seguinte maneira:
![]()
Cálculo do preço para qualquer tipo de ordem
Agora que sabemos como calcular o tamanho máximo do lote, o próximo passo será implementar essa funcionalidade programaticamente. No entanto, antes de fazer isso, precisamos determinar o preço pelo qual a ordem será executada. Para isso, criaremos uma função chamada PriceByOrderType, que calculará e retornará o preço correspondente conforme o tipo de ordem.
double PriceByOrderType(const string symbol, const ENUM_ORDER_TYPE order_type, double DEVIATION = 100, double STOP_LIMIT = 50)
Parâmetros:
- symbol — o símbolo de negociação (por exemplo, "EURUSD") no qual a ordem será executada,
- order_type — o tipo de ordem, baseado no enumerador ENUM_ORDER_TYPE,
- DEVIATION — o desvio permitido em pontos,
- STOP_LIMIT — a distância em pontos para ordens do tipo STOP_LIMIT.
Passo 1. Criação das variáveis necessárias
Primeiro, declararemos as variáveis que armazenarão a quantidade de dígitos do símbolo, o valor de um ponto e os preços atuais (bid e ask) na estrutura MqlTick.
int digits=0; double point=0; MqlTick tick={};
Passo 2. Atribuição de valores às variáveis
Utilizamos funções para obter informações sobre o símbolo, como quantidade de dígitos, valor do ponto e preços atuais.
Obtenção do valor SYMBOL_POINT:ResetLastError(); if(!SymbolInfoDouble(symbol, SYMBOL_POINT, point)) { Print("SymbolInfoDouble() failed. Error ", GetLastError()); return 0; }
Obtenção do valor SYMBOL_DIGITS:
long value=0; if(!SymbolInfoInteger(symbol, SYMBOL_DIGITS, value)) { Print("SymbolInfoInteger() failed. Error ", GetLastError()); return 0; } digits=(int)value;
Obtenção dos preços atuais do símbolo:
if(!SymbolInfoTick(symbol, tick)) { Print("SymbolInfoTick() failed. Error ", GetLastError()); return 0; }
Passo 3. Cálculo do preço com base no tipo de ordem
Dependendo do tipo de ordem, retornamos o preço correspondente, utilizando a construção switch:
switch(order_type) { case ORDER_TYPE_BUY : return(tick.ask); case ORDER_TYPE_SELL : return(tick.bid); case ORDER_TYPE_BUY_LIMIT : return(NormalizeDouble(tick.ask - DEVIATION * point, digits)); case ORDER_TYPE_SELL_LIMIT : return(NormalizeDouble(tick.bid + DEVIATION * point, digits)); case ORDER_TYPE_BUY_STOP : return(NormalizeDouble(tick.ask + DEVIATION * point, digits)); case ORDER_TYPE_SELL_STOP : return(NormalizeDouble(tick.bid - DEVIATION * point, digits)); case ORDER_TYPE_BUY_STOP_LIMIT : return(NormalizeDouble(tick.ask + DEVIATION * point - STOP_LIMIT * point, digits)); case ORDER_TYPE_SELL_STOP_LIMIT : return(NormalizeDouble(tick.bid - DEVIATION * point + STOP_LIMIT * point, digits)); default : return(0); }
Aqui está a implementação final da função:
double PriceByOrderType(const string symbol, const ENUM_ORDER_TYPE order_type, double DEVIATION = 100, double STOP_LIMIT = 50) { int digits=0; double point=0; MqlTick tick={}; //--- we get the Point value of the symbol ResetLastError(); if(!SymbolInfoDouble(symbol, SYMBOL_POINT, point)) { Print("SymbolInfoDouble() failed. Error ", GetLastError()); return 0; } //--- we get the Digits value of the symbol long value=0; if(!SymbolInfoInteger(symbol, SYMBOL_DIGITS, value)) { Print("SymbolInfoInteger() failed. Error ", GetLastError()); return 0; } digits=(int)value; //--- we get the latest prices of the symbol if(!SymbolInfoTick(symbol, tick)) { Print("SymbolInfoTick() failed. Error ", GetLastError()); return 0; } //--- Depending on the type of order, we return the price switch(order_type) { case ORDER_TYPE_BUY : return(tick.ask); case ORDER_TYPE_SELL : return(tick.bid); case ORDER_TYPE_BUY_LIMIT : return(NormalizeDouble(tick.ask - DEVIATION * point, digits)); case ORDER_TYPE_SELL_LIMIT : return(NormalizeDouble(tick.bid + DEVIATION * point, digits)); case ORDER_TYPE_BUY_STOP : return(NormalizeDouble(tick.ask + DEVIATION * point, digits)); case ORDER_TYPE_SELL_STOP : return(NormalizeDouble(tick.bid - DEVIATION * point, digits)); case ORDER_TYPE_BUY_STOP_LIMIT : return(NormalizeDouble(tick.ask + DEVIATION * point - STOP_LIMIT * point, digits)); case ORDER_TYPE_SELL_STOP_LIMIT : return(NormalizeDouble(tick.bid - DEVIATION * point + STOP_LIMIT * point, digits)); default : return(0); } }
Além disso, será necessária uma função para obter o tipo de ordem a mercado com base no tipo de ordem, algo que é feito de forma simples:
ENUM_ORDER_TYPE MarketOrderByOrderType(ENUM_ORDER_TYPE type) { switch(type) { case ORDER_TYPE_BUY : case ORDER_TYPE_BUY_LIMIT : case ORDER_TYPE_BUY_STOP : case ORDER_TYPE_BUY_STOP_LIMIT : return(ORDER_TYPE_BUY); case ORDER_TYPE_SELL : case ORDER_TYPE_SELL_LIMIT : case ORDER_TYPE_SELL_STOP : case ORDER_TYPE_SELL_STOP_LIMIT : return(ORDER_TYPE_SELL); } return(WRONG_VALUE); }
Cálculo do lote máximo
A função GetMaxLot calcula o tamanho máximo do lote que pode ser aberto com base na margem livre disponível e no tipo de ordem especificado. Trata-se de uma ferramenta fundamental para o gerenciamento de riscos, pois garante que as operações respeitem os requisitos de margem estabelecidos pela corretora.
1. Criação dos parâmetros da função
A função recebe os seguintes parâmetros:
double GetMaxLote(ENUM_ORDER_TYPE type, double DEVIATION = 100, double STOP_LIMIT = 50)
- Type — define o tipo de ordem, como ORDER_TYPE_BUY ou ORDER_TYPE_SELL. Esse parâmetro é essencial para o cálculo correto do preço e da margem.
- DEVIATION — indica o desvio permitido em pontos para ordens pendentes. O valor padrão é 100.
- STOP_LIMIT — representa a distância em pontos para ordens do tipo STOP_LIMIT. O valor padrão é 50.
2. Inicialização das variáveis necessárias
São declaradas quatro variáveis do tipo double e um enumerador ORDER_TYPE, que serão utilizados nos cálculos:
//--- Set variables double VOLUME = 1.0; //Initial volume size ENUM_ORDER_TYPE new_type = MarketOrderByOrderType(type); double price = PriceByOrderType(_Symbol, type, DEVIATION, STOP_LIMIT); // Price for the given order type double volume_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Volume step for the symbol double margin = EMPTY_VALUE; // Required margin, initialized as empty
3. Cálculo da margem necessária para um lote
Para determinar a margem exigida para abrir um lote nas condições de mercado atuais, utiliza-se a função OrderCalcMargin. Em caso de falha, uma mensagem de erro é exibida e retorna-se 0:
ResetLastError(); if (!OrderCalcMargin(new_type, _Symbol, VOLUME, price, margin)) { Print("OrderCalcMargin() failed. Error ", GetLastError()); return 0; // Exit the function if margin calculation fails }
4. Cálculo do tamanho máximo do lote
Para calcular o tamanho máximo do lote, utiliza-se a fórmula apresentada acima. Isso inclui dividir a margem livre pela margem exigida, normalizar o resultado de acordo com o passo de volume permitido e arredondar para baixo, evitando erros:
double result = MathFloor((AccountInfoDouble(ACCOUNT_MARGIN_FREE) / margin) / volume_step) * volume_step;
5. Retorno do resultado
Por fim, é retornado o tamanho máximo de lote calculado:
return result; // Return the maximum lot size
Função completa:
double GetMaxLote(ENUM_ORDER_TYPE type, double DEVIATION = 100, double STOP_LIMIT = 50) { //--- Set variables double VOLUME = 1.0; // Initial volume size ENUM_ORDER_TYPE new_type = MarketOrderByOrderType(type); double price = PriceByOrderType(_Symbol, type, DEVIATION, STOP_LIMIT); // Price for the given order type double volume_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Volume step for the symbol double margin = EMPTY_VALUE; // Required margin, initialized as empty //--- Get margin for one lot ResetLastError(); if (!OrderCalcMargin(new_type, _Symbol, VOLUME, price, margin)) { Print("OrderCalcMargin() failed. Error ", GetLastError()); return 0; // Exit the function if margin calculation fails } //--- Calculate the maximum lot size double result = MathFloor((AccountInfoDouble(ACCOUNT_MARGIN_FREE) / margin) / volume_step) * volume_step; return result; // Return the maximum lot size }
Criação de funções para obter o lucro
Após concluir o desenvolvimento das funções para calcular o tamanho máximo do lote, o próximo passo é desenvolver funções que calculem o lucro desde uma data específica até o momento atual. Isso é fundamental, pois, na avaliação a cada tick, precisamos determinar se a variável de perda máxima foi ultrapassada. Para isso, dependemos das variáveis que armazenam informações sobre o lucro. Por exemplo, para verificar se a perda máxima diária foi excedida, é necessário conhecer o lucro acumulado do dia e também a equity atual.
O cálculo do lucro atual será realizado utilizando as funções disponíveis para trabalhar com o histórico de ordens e transações. Isso nos permitirá obter dados precisos e atualizados sobre lucros e perdas em um determinado período.
Descrição detalhada da função
1. Inicialização de variáveis e limpeza de erros:
double total_net_profit = 0.0; // Initialize the total net profit ResetLastError(); // Reset any previous errors
- total_net_profit — é inicializada com o valor 0,0, indicando que o lucro líquido ainda não foi calculado.
- ResetLastError — garante a limpeza de quaisquer erros anteriores que possam ter ocorrido no código antes do início da execução.
2. Verificação da data inicial (start_date):
if((start_date > 0 || start_date != D'1971.01.01 00:00'))Esta linha verifica se a data indicada como start_date é válida, pois ela não pode ser uma data inválida padrão, como 1971.01.01, ou uma data nula. Se a data for válida, o código prossegue com a seleção do histórico de negociações.
3. Seleção do histórico de negociações:
if(!HistorySelect(start_date, TimeCurrent())) { Print("Error when selecting orders: ", _LastError); return 0.0; // Exit if unable to select the history }
- HistorySelect — seleciona o histórico de negociações desde a data indicada (start_date) até o momento atual (TimeCurrent).
- Se a seleção do histórico falhar, uma mensagem de erro é exibida e a função termina retornando 0.
4. Obtenção do número total de negociações:
int total_deals = HistoryDealsTotal(); // Get the total number of deals in history
- HistoryDealsTotal — retorna a quantidade total de negociações no histórico, permitindo determinar quantas negociações precisam ser processadas.
5. Iteração por todas as negociações:
for(int i = 0; i < total_deals; i++) { ulong deal_ticket = HistoryDealGetTicket(i); // Retrieve the deal ticket
- Nesta etapa, é iniciado um laço for que percorre todas as negociações do histórico.
- HistoryDealGetTicket — obtém o ticket único da negociação na posição i, necessário para acessar os detalhes completos daquela negociação.
6. Filtragem das negociações do tipo "balance":
if(HistoryDealGetInteger(deal_ticket, DEAL_TYPE) == DEAL_TYPE_BALANCE) continue;
Se o tipo da negociação for balance (ajuste de balanço, não sendo uma operação real), ela é ignorada, e o laço continua para a próxima.
7. Obtenção dos detalhes da negociação:
ENUM_DEAL_ENTRY deal_entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal_ticket, DEAL_ENTRY); // Get deal entry type long deal_close_time_long = HistoryDealGetInteger(deal_ticket, DEAL_TIME); // Get deal close time (as long) datetime deal_close_time = (datetime)deal_close_time_long; // Explicit conversion to datetime ulong position_id = HistoryDealGetInteger(deal_ticket, DEAL_POSITION_ID); // Get the position ID
- deal_entry — determina se a negociação foi uma entrada ou saída, útil para identificar se a operação se refere à abertura ou ao fechamento.
- deal_close_time — o horário de fechamento da negociação. Para facilitar o processamento, ele é convertido para o tipo datetime.
- position_id — o identificador da posição associada à negociação, usado para verificar o magic number.
8. Filtragem por data e tipo de negociação:
if(deal_close_time >= start_date && (deal_entry == DEAL_ENTRY_OUT || deal_entry == DEAL_ENTRY_IN))A condição garante que apenas negociações cujo horário de fechamento seja maior ou igual a start_date, e que sejam negociações de entrada ou saída válidas, sejam consideradas.
9. Filtragem de negociações por Magic Number e tipo de inclusão:
if((HistoryDealGetInteger(deal_ticket, DEAL_MAGIC) == specific_magic || specific_magic == GetMagic(position_id)) || include_all_magic == true)
- HistoryDealGetInteger — obtém o magic number da negociação.
- Se o magic number da operação corresponde ao especificado (specific_magic), ou se todas as negociações foram autorizadas para inclusão (quando include_all_magic é true), então o cálculo do lucro líquido da negociação é realizado.
10. Cálculo do lucro líquido por negociação:
double deal_profit = HistoryDealGetDouble(deal_ticket, DEAL_PROFIT); // Retrieve profit from the deal double deal_commission = HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION); // Retrieve commission double deal_swap = HistoryDealGetDouble(deal_ticket, DEAL_SWAP); // Retrieve swap fees double deal_net_profit = deal_profit + deal_commission + deal_swap; // Calculate net profit for the deal total_net_profit += deal_net_profit; // Add to the total net profit
- deal_profit — obtém o lucro da negociação.
- deal_commission — obtém a comissão da negociação.
- deal_swap — obtém o swap da negociação, isto é, a taxa de juros ou custo pelo carregamento da posição para o dia seguinte.
O lucro líquido da negociação é calculado como a soma desses três valores e adicionado a total_net_profit.
11. Retorno do lucro líquido total:
return NormalizeDouble(total_net_profit, 2); // Return the total net profit rounded to 2 decimalsAo final, é retornado o lucro líquido total, arredondado para 2 casas decimais com a ajuda de NormalizeDouble, garantindo que o valor possua o formato correto para uso posterior.
Função completa:
double GetNetProfitSince(bool include_all_magic, ulong specific_magic, datetime start_date) { double total_net_profit = 0.0; // Initialize the total net profit ResetLastError(); // Reset any previous errors // Check if the start date is valid if((start_date > 0 || start_date != D'1971.01.01 00:00')) { // Select the order history from the given start date to the current time if(!HistorySelect(start_date, TimeCurrent())) { Print("Error when selecting orders: ", _LastError); return 0.0; // Exit if unable to select the history } int total_deals = HistoryDealsTotal(); // Get the total number of deals in history // Iterate through all deals for(int i = 0; i < total_deals; i++) { ulong deal_ticket = HistoryDealGetTicket(i); // Retrieve the deal ticket // Skip balance-type deals if(HistoryDealGetInteger(deal_ticket, DEAL_TYPE) == DEAL_TYPE_BALANCE) continue; ENUM_DEAL_ENTRY deal_entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal_ticket, DEAL_ENTRY); // Get deal entry type long deal_close_time_long = HistoryDealGetInteger(deal_ticket, DEAL_TIME); // Get deal close time (as long) datetime deal_close_time = (datetime)deal_close_time_long; // Explicit conversion to datetime ulong position_id = HistoryDealGetInteger(deal_ticket, DEAL_POSITION_ID); // Get the position ID // Check if the deal is within the specified date range and is a valid entry/exit deal if(deal_close_time >= start_date && (deal_entry == DEAL_ENTRY_OUT || deal_entry == DEAL_ENTRY_IN)) { // Check if the deal matches the specified magic number or if all deals are to be included if((HistoryDealGetInteger(deal_ticket, DEAL_MAGIC) == specific_magic || specific_magic == GetMagic(position_id)) || include_all_magic == true) { double deal_profit = HistoryDealGetDouble(deal_ticket, DEAL_PROFIT); // Retrieve profit from the deal double deal_commission = HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION); // Retrieve commission double deal_swap = HistoryDealGetDouble(deal_ticket, DEAL_SWAP); // Retrieve swap fees double deal_net_profit = deal_profit + deal_commission + deal_swap; // Calculate net profit for the deal total_net_profit += deal_net_profit; // Add to the total net profit } } } } return NormalizeDouble(total_net_profit, 2); // Return the total net profit rounded to 2 decimals }
Função adicional para obter o magic number de uma ordem:
ulong GetMagic(const ulong ticket) { HistoryOrderSelect(ticket); return HistoryOrderGetInteger(ticket,ORDER_MAGIC); }
Verificação na prática com a criação de um script simples com arquivo incluível
Agora vamos criar uma função que converte uma distância absoluta em unidades de pontos do símbolo atual. Essa conversão é essencial no trading, pois os pontos são a unidade padrão utilizada para calcular níveis de preço, stops e objetivos.
Fórmula matemática
A fórmula para calcular a distância em pontos é simples:

Onde:
- dist — é a distância absoluta que desejamos converter,
- pointSize — é o tamanho de um ponto do instrumento financeiro (por exemplo, 0,0001 para EUR/USD).
Representação da fórmula no código
Para implementar essa fórmula em MQL5, precisamos realizar as seguintes etapas:
-
Obter o tamanho do ponto (pointSize).
Utilizamos a função SymbolInfoDouble para obter o valor do ponto do símbolo atual. O parâmetro _Symbol representa o símbolo no qual o script está sendo executado, e SYMBOL_POINT retorna o tamanho do ponto.
- Dividir a distância pelo tamanho do ponto e converter para um número inteiro.
Para calcular a quantidade de pontos, dividimos a distância (dist) pelo tamanho do ponto (pointSize). Em seguida, convertemos o resultado para um valor inteiro usando int, pois pontos são sempre números inteiros.
double pointSize = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
return (int)(dist / pointSize);
Função completa
Abaixo apresentamos a função em sua forma final:
int DistanceToPoint(double dist) { double pointSize = SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Get the point size for the current symbol return (int)(dist / pointSize); // Calculate and return the distance in points }
Para aplicar na prática tudo o que foi exposto neste artigo, criaremos dois scripts.
Agora criaremos duas funções importantes: uma para calcular o lote ideal com base na perda permitida por operação, e outra para calcular o stop loss ideal em pontos do símbolo com base no lote especificado e no risco por operação.
Função para calcular o lote ideal com base no risco por operação
A função GetIdealLot calcula o tamanho ideal do lote (nlot) levando em consideração a perda máxima permitida por operação e a distância até o stop loss (StopLoss). Isso garante que cada operação cumpra o limite de risco definido pelo usuário.
void GetIdealLot( double& nlot, // Calculated ideal lot double glot, // Gross Lot (max lot accorsing to the balance) double max_risk_per_operation, // Maximum allowed risk per trade (in account currency) double& new_risk_per_operation, // Calculated risk for the adjusted lot (in account currency) long StopLoss // Stop Loss distance (in points) )
Descrição dos parâmetros:
- nlot — este será o lote ideal, ajustado pela função.
- glot — este é o lote máximo possível que pode ser aberto utilizando todos os fundos disponíveis na conta.
- max_risk_per_operation — representa o risco máximo permitido por operação, expresso na moeda da conta.
- new_risk_per_operation — indica o risco real da operação ajustada com base no lote calculado (nlot), mostrando quanto será perdido se o preço atingir o stop loss.
- StopLoss — a distância até o stop loss em pontos.
1. Verificação inicial
A função verifica que o valor de "StopLoss" é maior que 0, pois um stop loss inválido tornaria impossível o cálculo correto do risco.
if(StopLoss <= 0) { Print("[ERROR SL] Stop Loss distance is less than or equal to zero, now correct the stoploss distance: ", StopLoss); nlot = 0.0; return; }
2. Inicialização das variáveis
São inicializados os valores necessários para os cálculos posteriores:
- spread — o spread atual do símbolo.
- tick_value — o valor de um tick, que indica quanto vale a menor variação de preço na moeda da conta.
- step — o incremento mínimo permitido no tamanho do lote.
new_risk_per_operation = 0; // Initialize the new risk long spread = (long)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
3. Cálculo do risco atual (rpo)
O risco atual por operação (rpo) é calculado utilizando a fórmula:
![]()
No código:
double rpo = (glot * (spread + 1 + (StopLoss * tick_value)));
4. Verificação do risco máximo
A função avalia se o risco atual (rpo) ultrapassa o risco máximo permitido por operação (max_risk_per_operation):
Caso 1. O risco excede o valor máximo permitido
- O tamanho do lote é ajustado proporcionalmente ao risco máximo permitido.
- O lote ajustado é arredondado para o incremento permitido mais próximo (step).
- Um novo risco é calculado de acordo com esse lote corrigido.
if(rpo > max_risk_per_operation) { double new_lot = (max_risk_per_operation / rpo) * glot; new_lot = MathFloor(new_lot / step) * step; new_risk_per_operation = new_lot * (spread + 1 + (StopLoss * tick_value)); nlot = new_lot; }
Caso 2. O risco está dentro do limite permitido
- Se o risco atual não ultrapassar o limite estabelecido, os valores originais são mantidos:
else { new_risk_per_operation = rpo; // Current risk nlot = glot; // Gross lot }
Por fim, criaremos a função final para calcular o stop loss com base no prejuízo máximo permitido por operação e no volume de lote definido pelo usuário:
long GetSL(const ENUM_ORDER_TYPE type , double risk_per_operation , double lot) { long spread = (long)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double result = ((risk_per_operation/lot)-spread-1)/tick_value; return long(MathRound(result)); }
Descrição dos parâmetros:
- type — o tipo de ordem (compra ou venda), embora nesta função ele não seja usado diretamente.
- risk_per_operation — a perda máxima permitida por operação, expressa na moeda da conta.
- lot — o tamanho do lote definido pelo usuário.
Lógica passo a passo
1. Fórmula básica:
A fórmula original de cálculo do risco por operação (rpo) é a seguinte:
![]()
Nesta função, expressaremos o stop loss de forma a calcular seu valor com base em rpo, no tamanho do lote e em outros fatores.
2. Expressando o stop loss:
- Ambos os lados da equação são divididos pelo valor do lote:
![]()
- Subtraímos o spread e 1 de ambos os lados da equação:

- Dividimos por tick_value para isolar o StopLoss:

Implementação no código
A fórmula acima é aplicada diretamente nos cálculos dentro da função:
double result = ((risk_per_operation / lot) - spread - 1) / tick_value;
- risk_per_operation/lot — calcula o risco por unidade de lote,
- - spread - 1 — subtrai o spread e uma margem adicional,
- /tick_value — converte o resultado para pontos dividindo pelo valor de um tick.
O resultado é arredondado e convertido para o tipo long, para corresponder ao formato exigido:
return long(MathRound(result));
Finalmente, criaremos dois scripts para calcular o lote ideal e o stop loss (sl) ideal de acordo com o risco definido para a operação. Ambos os scripts utilizam uma lógica simples, porém eficaz, para automatizar esses cálculos com base no saldo da conta e nos parâmetros fornecidos pelo usuário.
Primeiro script: cálculo do lote ideal
Esse script calculará o tamanho ideal do lote com base no percentual de risco por operação, no stop loss (sl) especificado em pontos e no tipo de ordem.
-
Propriedades do script:
- #property strict garante que o código siga regras rígidas de compilação.
- #property script_show_inputs permite que o usuário insira parâmetros por meio da interface gráfica.
- Parâmetros de entrada (Inputs):
input double percentage_risk_per_operation = 1.0; //Risk per operation in % input long sl = 600; //Stops Loss in points input ENUM_ORDER_TYPE Order_Type = ORDER_TYPE_BUY; //Order Type
Cálculo do risco por operação:
A fórmula calcula o valor em dólares que pode ser colocado em risco em uma única operação, com base no percentual definido:
double risk_per_operation = ((percentage_risk_per_operation/100.0) * AccountInfoDouble(ACCOUNT_BALANCE));
Chamada da função de cálculo do lote ideal:
GetIdealLot(new_lot, GetMaxLote(Order_Type), risk_per_operation, new_risk_per_operation, sl);
Mensagens ao usuário: informações detalhadas sobre os valores calculados, como o lote ideal e o risco ajustado, são exibidas no console e no gráfico.
//+------------------------------------------------------------------+ //| Get Lot By Risk Per Trade and SL.mq5 | //| Your name | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Your name" #property link "https://www.mql5.com" #property version "1.00" #property strict #property script_show_inputs input double percentage_risk_per_operation = 1.0; // Risk per operation in % input long sl = 600; // Stop Loss in points input ENUM_ORDER_TYPE Order_Type = ORDER_TYPE_BUY; // Order Type #include <Risk Management.mqh> //+------------------------------------------------------------------+ //| Main script function | //+------------------------------------------------------------------+ void OnStart() { // Calculate the maximum allowable risk per operation in account currency double risk_per_operation = ((percentage_risk_per_operation / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE)); // Print input and calculated risk details Print("Risk Per operation: ", risk_per_operation); Print("SL in points: ", sl); Print("Order type: ", EnumToString(Order_Type)); double new_lot; double new_risk_per_operation; // Calculate the ideal lot size GetIdealLot(new_lot, GetMaxLote(Order_Type), risk_per_operation, new_risk_per_operation, sl); // Check if the lot size is valid if (new_lot <= 0) { Print("The stop loss is too large or the risk per operation is low. Increase the risk or decrease the stop loss."); } else { // Display calculated values Print("Ideal Lot: ", new_lot); Print("Maximum loss with SL: ", sl, " | Lot: ", new_lot, " is: ", new_risk_per_operation); Comment("Ideal Lot: ", new_lot); } Sleep(1000); Comment(" "); } //+------------------------------------------------------------------+
Segundo script: cálculo do SL ideal
Esse script calcula o stop loss em pontos com base no lote especificado pelo usuário e no risco máximo permitido por operação.
input double percentage_risk_per_operation = 1.0; //Risk per operation in % input double Lot = 0.01; //lot input ENUM_ORDER_TYPE Order_Type = ORDER_TYPE_BUY; //Order Type
Cálculo do sl ideal: a função get sl é usada para determinar o stop loss em pontos:
long new_sl = GetSL(Order_Type, risk_per_operation, Lot);
Verificação do resultado: se o valor calculado de sl for inválido (new_sl menor ou igual a 0), o usuário recebe uma notificação correspondente.
//+------------------------------------------------------------------+ //| Get Sl by risk per operation and lot.mq5 | //| Your name | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Your name" #property link "https://www.mql5.com" #property version "1.00" #property strict #property script_show_inputs input double percentage_risk_per_operation = 1.0; // Risk per operation in % input double Lot = 0.01; // Lot size input ENUM_ORDER_TYPE Order_Type = ORDER_TYPE_BUY; // Order Type #include <Risk Management.mqh> //+------------------------------------------------------------------+ //| Main script function | //+------------------------------------------------------------------+ void OnStart() { // Calculate the maximum allowable risk per operation in account currency double risk_per_operation = ((percentage_risk_per_operation / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE)); // Print input and calculated risk details Print("Risk Per operation: ", risk_per_operation); Print("Lot size: ", Lot); Print("Order type: ", EnumToString(Order_Type)); // Calculate the ideal stop loss long new_sl = GetSL(Order_Type, risk_per_operation, Lot); // Check if the SL is valid if (new_sl <= 0) { Print("The lot size is too high or the risk per operation is too low. Increase the risk or decrease the lot size."); } else { // Display calculated values Print("For lot: ", Lot, ", and risk: ", risk_per_operation, ", the ideal SL is: ", new_sl); Comment("Ideal SL: ", new_sl); } Sleep(1000); Comment(" "); } //+------------------------------------------------------------------+Agora, para aplicar o script na prática, utilizaremos ele para obter o tamanho ideal do lote com base no risco definido por operação. O teste será realizado no símbolo XAUUSD, que corresponde ao ouro.
Com parâmetros como um stop loss de 200 pontos, risco por operação de 1,0% do saldo da conta e tipo de ordem definido como ORDER_TYPE_BUY, o resultado será o seguinte:
O resultado exibido na aba "Experts" corresponde a um tamanho de lote de 0,01 com um stop loss de 200 pontos e um risco por operação de 3,81, que equivale a 1% do saldo da conta.
Considerações finais
Concluímos a primeira parte desta série, na qual nos concentramos no desenvolvimento das funções principais que serão utilizadas na construção da classe gerenciamento de riscos. Essas funções são fundamentais para obter lucro e realizar cálculos adicionais. Na próxima parte, veremos como integrar tudo o que aprendemos à interface gráfica, utilizando as bibliotecas de elementos de controle do MQL5.
Traduzido do espanhol pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/es/articles/16820
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.
Desenvolvimento de sistemas de trading avançados ICT: Implementação de sinais no indicador Order Blocks
MQL5 Trading Toolkit (Parte 5): Expandindo a Biblioteca EX5 de Gerenciamento de Histórico com Funções de Posição
Técnicas do MQL5 Wizard que você deve conhecer (Parte 51): Aprendizado por Reforço com SAC
Construindo um Modelo de Restrição de Tendência com Candlestick (Parte 10): Golden Cross e Death Cross Estratégicos (EA)
- 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