Gerenciamento de riscos (Parte 2): Implementação do cálculo de lotes na interface gráfica
- Introdução
- Aprimoramentos das funções de obtenção do lote e do stop-loss
- Estudo das bibliotecas de controles e painéis no MQL5
- Funções para criação dos componentes do painel (rótulos, botões etc.)
- Criação de objetos na área cliente
- Funções de atualização dinâmica do Combobox e de outros elementos
- Tratamento de eventos do teclado: implementação de OnCharEvent
- Inicialização do painel: uso do evento OnInit e configuração geral
- Teste e verificação do painel
- Considerações finais
Introdução
Bem-vindo. Neste artigo, que dá continuidade ao anterior, colocaremos em prática tudo o que foi previamente mencionado, além de aprimorarmos algumas funções do arquivo incluído. Para facilitar o trabalho, utilizaremos as robustas bibliotecas de controles gráficos do MQL5. O objetivo é aplicar de forma mais eficiente o que foi estudado, demonstrando como integrar interfaces gráficas às nossas funções de gerenciamento de riscos. Como resultado, você terá uma ferramenta confiável, capaz de calcular com precisão e eficiência o volume do lote e o stop-loss (SL).
Aprimoramentos das funções de obtenção do lote e do stop-loss
Iniciaremos este artigo aprimorando as funções criadas anteriormente, com foco na simplificação e na otimização do seu funcionamento. As principais alterações incluem a adição de mensagens de depuração (PrintFormat e Print), que auxiliam na identificação de erros em tempo real, e a criação de novas funções que tornam o cálculo do lote ideal e da distância até o stop-loss mais eficiente.
Aprimoramentos da função GetMaxLote
Essa função calcula o volume máximo de lote que pode ser negociado, com base na margem livre disponível e na especificação do símbolo.
//+----------------------------------------------------------------------------------------------+ //| Calculates the maximum lot size that can be traded based on free margin and symbol specifics | //+----------------------------------------------------------------------------------------------+ double GetMaxLote(ENUM_ORDER_TYPE type, double DEVIATION = 100, double STOP_LIMIT = 50) { 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 ResetLastError(); if(!OrderCalcMargin(new_type, _Symbol, VOLUME, price, margin)) { Print("OrderCalcMargin() failed. Error ", GetLastError()); return 0; // Exit the function if margin calculation fails } if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0) { PrintFormat("Free margin of %+.2f is invalid, you cannot open trades right now",AccountInfoDouble(ACCOUNT_MARGIN_FREE)); return 0; } double result = MathFloor((AccountInfoDouble(ACCOUNT_MARGIN_FREE) / margin) / volume_step) * volume_step; return result; // Return the calculated maximum lot size }
Melhorias implementadas:
- Mensagens de depuração: agora o usuário é informado caso o cálculo da margem falhe ou a margem livre seja insuficiente.
- Verificação da margem livre: foi adicionada uma verificação para evitar cálculos quando a margem livre é menor ou igual a zero.
Função GetIdealLot
Essa função calcula o volume ideal do lote com base no risco máximo permitido por operação e nas condições atuais de mercado.
//+---------------------------------------------------------------------+ //| Determine the optimal lot size based on risk and current conditions | //+---------------------------------------------------------------------+ void GetIdealLot(double& nlot, double glot, double max_risk_per_operation, double& new_risk_per_operation, long StopLoss) { 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; } Print("Max Lot: ", glot, " | RiskPerOperation: ", max_risk_per_operation); new_risk_per_operation = 0; long spread = (long)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); double rpo = (glot * (spread + 1 + (StopLoss * tick_value))); if(rpo > max_risk_per_operation) { if(max_risk_per_operation <= 0) return; 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; } else { new_risk_per_operation = rpo; nlot = glot; } if(nlot <= 0) PrintFormat("The lot %.2f is invalid, the risk %.2f increases or the sl %i decreases",nlot,max_risk_per_operation,StopLoss); }
Melhorias desta função:
- Verificação do stop-loss: antes da execução, é importante verificar se a distância até o stop-loss é válida.
- Mensagens claras: informam quando o lote calculado é inválido ou quando é necessário ajustar o risco.
Nova função GetLotByRiskPerOperation
Permite calcular o volume ótimo do lote com base exclusivamente no risco por operação (em dólares americanos) e no tipo de ordem, eliminando a necessidade de indicar a distância até o stop-loss.
//+--------------------------------------------------------------------+ //| Function to obtain the ideal lot based on your risk per operation | //+--------------------------------------------------------------------+ // risk_per_operation in USD, not % double GetLotByRiskPerOperation(double risk_per_operation, const ENUM_ORDER_TYPE order_type, double DEVIATION = 100, double STOP_LIMIT = 50) { double VOLUME = 1.0; // Initial volume size ENUM_ORDER_TYPE new_type = MarketOrderByOrderType(order_type); double price = PriceByOrderType(_Symbol, order_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 ResetLastError(); if(!OrderCalcMargin(new_type, _Symbol, VOLUME, price, margin)) { Print("OrderCalcMargin() failed. Error ", GetLastError()); return 0; // Exit the function if margin calculation fails } double result = MathFloor((risk_per_operation / margin) / volume_step) * volume_step; if(result <= 0) { PrintFormat("The lot %.2f is invalid, the risk %.2f increases",result,risk_per_operation); return 0; } PrintFormat("The ideal lot for %.2f risk per trade is %.2f lots",risk_per_operation,result); return result; // Return the calculated maximum lot size } //+------------------------------------------------------------------+
Principais características:
- Simplificação dos cálculos: utiliza apenas o risco em USD e remove a dependência de parâmetros adicionais.
- Depuração: mensagens claras sobre volumes de lote inválidos ou riscos excessivos.
Aprimoramento da função CalculateSL
Permite calcular a distância ideal até o stop-loss em pontos, de acordo com o risco por operação e o volume de lote selecionado.
//+-----------------------------------------------------------------------+ //| Calculate the stop loss distance in points based on risk and lot size | //+-----------------------------------------------------------------------+ long CalculateSL(const ENUM_ORDER_TYPE type, double risk_per_operation, double &chosen_lot, double DEVIATION = 100, double STOP_LIMIT = 50) { double lot = GetLotByRiskPerOperation(risk_per_operation,type,DEVIATION,STOP_LIMIT); chosen_lot = 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; long ideal_sl = long(MathRound(result)); if(ideal_sl <= 0) { PrintFormat("Stop loss %i invalid, please increase the risk per trade %.2f",ideal_sl,risk_per_operation); return 0; } return ideal_sl; }
Melhorias:
- Integração com GetLotByRiskPerOperation: é utilizado o volume de lote calculado para determinar o stop-loss.
- Cálculo aprimorado: garante que o stop-loss seja sempre positivo e correto.
Graças a essas melhorias, as funções se tornaram mais confiáveis, mais flexíveis e mais simples de depurar. As mensagens de erro permitem identificar rapidamente problemas, enquanto as novas implementações simplificam o processo de cálculo tanto do volume do lote quanto do stop-loss.
Estudo das bibliotecas de controles e painéis no MQL5
Antes de começarmos a nos aprofundar nas bibliotecas de elementos de controle do MQL5, é necessário compreender os princípios básicos do funcionamento dos objetos no MQL5.
Objetos gráficos no MQL5
No MQL5, objetos gráficos são elementos visuais que podem ser colocados no gráfico para exibir informações ou permitir interação com o usuário. Esses objetos são posicionados por meio de duas coordenadas principais:
- X (horizontal) — determina a posição do objeto no eixo horizontal.
- Y (vertical) — determina a posição no eixo vertical.
Essas coordenadas definem exatamente onde o objeto será exibido no gráfico ou no painel.
Principais propriedades dos objetos gráficos
Além de seu posicionamento, os objetos gráficos possuem características como:
- altura (height) — o espaço ocupado pelo objeto verticalmente,
- largura (width) — o espaço ocupado por ele horizontalmente.
Essas propriedades são importantes para garantir que o objeto não ultrapasse os limites da área em que deve ser exibido. Por exemplo, ao projetar uma interface, essas dimensões garantem que os elementos permaneçam organizados e dentro das fronteiras visíveis.
Ângulo de ancoragem
O ponto de ancoragem é o local no objeto que é utilizado como ponto de referência ao aplicar as coordenadas X e Y. Esse ponto determina como a posição do objeto será interpretada. Além disso, existem 4 tipos de pontos de ancoragem:
| Identificador | Descrição |
|---|---|
| CORNER_LEFT_UPPER | O centro das coordenadas está localizado no canto superior esquerdo do gráfico. |
| CORNER_LEFT_LOWER | O centro das coordenadas está localizado no canto inferior esquerdo do gráfico. |
| CORNER_RIGHT_LOWER | O centro das coordenadas está localizado no canto inferior direito do gráfico. |
| CORNER_RIGHT_UPPER | O centro das coordenadas está localizado no canto superior direito do gráfico. |
Esses 4 tipos de ancoragem podem ser representados na imagem da seguinte forma:

Observação: como regra geral, o tipo de ancoragem mais utilizado por padrão é o CORNER_LEFT_UPPER. A escolha pode variar conforme o objetivo ou conforme o que parecer mais simples. Nesta artigo trabalharemos apenas com esse tipo de ancoragem.
Biblioteca de elementos de controle MQL5
A biblioteca de elementos de controle MQL5 é uma biblioteca de classes destinada à criação de elementos de controle, painéis etc. Para consultar a descrição desses elementos, é possível recorrer à documentação oficial.
Para isso, abra a documentação pressionando a tecla F1 no MetaEditor. Uma vez lá, acesse a seção "Conteúdo" e clique no livro chamado «Manual de Referência do MQL5». Ao rolar a página até o final, você encontrará a seção denominada "Biblioteca Padrão". Ao clicar nela, verá uma lista de diversas bibliotecas para diferentes aplicações. A biblioteca que nos interessa é a Controls.
Ao selecionar essa biblioteca, você terá acesso a diferentes tabelas que descrevem o propósito e as funções de cada elemento de controle, além de sua estrutura de classificação por tipos. Para ajudá-lo a compreender melhor a estrutura e a relação entre as classes dos elementos de controle, preparei um esquema que mostra como funciona a herança e como se organizam as classes relacionadas a esses elementos.

Como podemos ver, a classe principal é CObject, da qual deriva a classe CWnd. A partir de CWnd derivam duas classes adicionais: CWndContainer, destinada a elementos de controle mais complexos, e CWndObj, utilizada para elementos mais simples. Em nossos painéis utilizaremos ambos os tipos de elementos de controle, dependendo das necessidades específicas de cada caso. Entretanto, neste momento focaremos no elemento de controle CAppDialog — a classe responsável pela criação da janela principal, semelhante à que estamos desenvolvendo.
Nesta painel começaremos a adicionar os principais elementos de controle, como rótulos para exibição de texto, listas suspensas para seleção do tipo de ordens, botões para salvar variáveis e obter resultados, além de campos de entrada para que o usuário possa editar valores, como o nível de risco por operação que deseja definir.
Planejamento da interface no MQL5: concepção e elementos gráficos
Antes de iniciar a programação da interface no MQL5, é necessário pensar cuidadosamente no seu design. A melhor maneira de fazer isso é criar um esboço visual, como mostrado na imagem anexada, utilizando ferramentas simples como Paint, Canva etc. Essa etapa garante que cada elemento necessário será incluído e corretamente posicionado, evitando omissões e facilitando a implementação. Foi exatamente assim que fiz:

Ao analisar a imagem da interface, podemos observar diversos elementos de controle que desempenham funções distintas. Esses elementos possibilitam ao usuário interagir com a interface de maneira clara e eficiente. A seguir, apresento uma descrição detalhada dos principais elementos e de como serão utilizados em nossa classe:
| Elemento de controle | Função | Exemplo na interface | Objeto base |
|---|---|---|---|
| CLabel | Utilizado para exibir texto estático na interface, como descrições ou instruções para o usuário. | Rótulos como "Risk Per Operation %", "Deviation (Points)" e "Stop Limit (Points)". | Este elemento de controle é derivado do objeto gráfico OBJ_LABEL. |
| CComboBox | Permite ao usuário escolher uma opção em uma lista suspensa. | "Get Lote" e "Get SL", que permitem selecionar o tipo de ordem para o lote correspondente e o cálculo do stop-loss. | Não é baseado em um único objeto, pois é um componente composto. |
| CButton | Botões interativos que executam ações ao serem clicados. | O botão ao lado de "SL Point", que executa uma ação como calcular ou confirmar o valor inserido. Também inclui o botão "Get ideal sl". | Este elemento de controle é derivado do objeto gráfico OBJ_BUTTON. |
| CEdit | Permite ao usuário inserir ou editar dados manualmente. | Campos como "Risk Per Operation %", "Deviation (Points)" e "Stop Limit (Points)", onde o usuário digita valores específicos. | Corresponde ao objeto gráfico OBJ_EDIT |
Funções para criação dos componentes do painel (rótulos, botões etc.)
Antes de iniciarmos a criação das funções, precisamos gerar um Expert Advisor (modelo) no MetaEditor. Esse arquivo inicialmente estará vazio, e assim que estiver pronto poderemos começar a incluir as bibliotecas necessárias para trabalhar com os componentes de nossa interface gráfica.
Para isso, adicione as seguintes linhas de código no início do arquivo:
#include <Controls\Dialog.mqh> #include <Controls\Edit.mqh> #include <Controls\Label.mqh> #include <Controls\Button.mqh> #include <Risk Management.mqh> #include <Controls\ComboBox.mqh>
Criação da classe principal
Começaremos definindo os parâmetros que especificam os tamanhos dos diversos elementos de controle. Esses valores permitirão ajustar as dimensões dos componentes da interface, como botões, campos de texto e listas suspensas.
//+------------------------------------------------------------------+ //| defines | //+------------------------------------------------------------------+ //--- for edits #define EDIT_HEIGHT (20) // edit height //--- for buttons #define BUTTON_WIDTH (80) // size by X coordinate #define BUTTON_HEIGHT (20) // size by Y coordinate //--- for combo box #define COMBO_BOX_WIDTH (200) // size by X coordinate #define COMBO_BOX_HEIGHT (20) // size by Y coordinate
Em seguida, criaremos um array do tipo string com capacidade para 8 elementos. Esse array será utilizado para armazenar diferentes tipos de ordens em formato textual.
string elements_order_type[8] = { "ORDER_TYPE_BUY", "ORDER_TYPE_SELL", "ORDER_TYPE_BUY_LIMIT", "ORDER_TYPE_SELL_LIMIT", "ORDER_TYPE_BUY_STOP", "ORDER_TYPE_SELL_STOP", "ORDER_TYPE_BUY_STOP_LIMIT", "ORDER_TYPE_SELL_STOP_LIMIT" };
Criação da classe para o painel de gerenciamento de riscos
Agora criaremos uma nova classe que herdará a parte pública da classe CAppDialog, que é o principal componente utilizado para a criação de painéis. Essa classe se tornará a base do nosso painel de gerenciamento de riscos.
//+-------------------------------------------------------------------+ //| Class CRiskManagementPanel | //| This class inherits from CAppDialog and will define the panel for | //| managing risk parameters. | //+-------------------------------------------------------------------+ class CRiskManagementPanel : public CAppDialog { // Declare private members here. };
Adição de métodos e atributos
Em seguida começaremos a adicionar métodos e atributos à nossa classe. Como já mencionado, precisaremos de vários elementos gráficos de controle e, conforme apresentado na imagem anterior, esses elementos serão adicionados à parte privada da classe.
private:
CLabel m_label_risk_per_operation;
CEdit m_edit_risk_per_operation;
CLabel m_label_deviaiton;
CEdit m_edit_deviaiton;
CLabel m_label_stop_limit;
CEdit m_edit_stop_limit;
CLabel m_label_get_lote;
CComboBox m_combobox_odertype_get_lot;
CLabel m_label_sl;
CEdit m_edit_sl;
CButton m_buttom_get_lote;
CLabel m_label_result_lote;
CLabel m_label_the_result_lote;
CLabel m_label_get_sl;
CComboBox m_combobox_odertype_get_sl;
CLabel m_label_lote;
CButton m_buttom_get_sl;
CLabel m_label_result_sl;
CLabel m_label_the_result_sl; Variáveis privadas para armazenamento de dados
Além dos elementos de controle, precisaremos de variáveis privadas que armazenarão os dados inseridos pelo usuário por meio dos elementos Edit e ComboBox. Essas variáveis conterão valores como risco por operação, magnitude da variação, tipo de ordem e valores de stop-loss.
// Variables to store the data entered by the user double deviation; // Stores deviation entered by the user double stop_limit; // Stores stop limit entered by the user double risk_per_operation; // Stores risk per operation entered by the user long sl; // Stores stop loss value entered by the user ENUM_ORDER_TYPE order_type_get_sl; // Stores the selected order type for stop loss ENUM_ORDER_TYPE order_type_get_lot; // Stores the selected order type for lot size
Para concluir a parte privada da nossa classe, declararemos todas as funções necessárias para gerenciar os elementos gráficos e a interação com o usuário. Essas funções incluirão: criação de objetos, funções executadas quando o usuário altera o item selecionado no ComboBox, além de funções para atualização dos valores exibidos em Labels, entre outras.
Declaração das funções privadas
//--- create labels and buttons bool CreateAreaClientPanel(); //--- functions to edit labels dynamically void EditLabelResultSL(string text); void EditLabelResultLote(string text); //--- create controls (buttons, labels, edits, combo boxes) bool CreateEdit(CEdit &m_edit, const string name, const int x1, const int y1, string initial_Text = ""); bool CreateLabel(CLabel &label, const string name, const string text, const int x1, const int y1); bool CreateButton(CButton &button, const string name, const string text, const int x1, const int y1, int x2_ = BUTTON_WIDTH, int y2_ = BUTTON_HEIGHT); bool CreateComboBox(CComboBox &combo_box, const string name, string &elements[], string initial_text, const int x1, const int y1); //--- combo box functions for handling user input void OnChangeComBoxOrderTypeGetLote(); void OnChangeComBoxOrderTypeGetSL();
Declaração dos métodos públicos
Na parte pública da classe adicionamos duas funções para criar o painel e os elementos de controle na área cliente. Também declaramos o evento OnEvent para tratar eventos do gráfico e uma função para converter strings em ENUM_ORDER_TYPE, além dos construtores e destrutores.
public: CRiskManagementPanel(void); ~CRiskManagementPanel(void); //--- create panel and controls virtual bool Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2); //--- chart event handler virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam); //--- function to convert string to ENUM_ORDER_TYPE static ENUM_ORDER_TYPE StringOrderTypeToEnum(const string OrderType);
Com essas declarações, temos tudo o que é necessário para criar o painel, seus elementos de controle e para processar os eventos gerados no gráfico.
Criação dos métodos para adicionar elementos de controle
Nesta seção analisaremos em detalhes os métodos necessários para criar os elementos básicos de controle, como rótulos (labels), botões (buttons), campos de entrada (edits) e listas suspensas (comboBoxes), que farão parte do nosso painel de gerenciamento de riscos. Além disso, será explicado o método principal que garante o correto funcionamento da interface por meio da vinculação dos elementos de controle à área cliente. Isso assegura que os elementos permaneçam alinhados ao painel quando este for movido ou tiver seu tamanho alterado.
Vinculação dos elementos de controle à área cliente
Para que os elementos de controle (como botões, rótulos etc.) se movimentem junto com o painel durante a interação, eles devem ser vinculados à área cliente do painel. Para isso, utiliza-se o método Add da classe CDialog. Esse método permite registrar os elementos de controle na lista de objetos da área cliente, garantindo sua sincronização com o painel. Abaixo está apresentada sua definição em CDialog:
//+------------------------------------------------------------------+ //| Add control to the client area (by pointer) | //+------------------------------------------------------------------+ bool CDialog::Add(CWnd *control) { return(m_client_area.Add(control)); } //+------------------------------------------------------------------+ //| Add control to the client area (by reference) | //+------------------------------------------------------------------+ bool CDialog::Add(CWnd &control) { return(m_client_area.Add(control)); }
O método Add possui duas versões: uma recebe um ponteiro para o elemento de controle, e a outra utiliza uma referência a ele. Ambas as versões adicionam o elemento ao contêiner m_client_area, responsável por gerenciar todos os elementos associados ao painel.
Se essa vinculação for omitida, os elementos de controle não acompanharão o movimento do painel, o que pode gerar inconsistências visuais ou dificuldades de interação.
Métodos para criação dos elementos básicos de controle
1. Função para criação de Labels
Os rótulos (Labels) são utilizados para exibir texto estático no painel, como títulos ou instruções. A função de criação de uma label envolve:
- definição das coordenadas do elemento de controle;
- criação do objeto label por meio do método Create;
- configuração do texto da label com Text;
- vinculação da label à área cliente usando Add.
Exemplo de código:
//+------------------------------------------------------------------+ //| Function to create labels | //+------------------------------------------------------------------+ bool CRiskManagementPanel::CreateLabel(CLabel &label,const string name, const string text, const int x1, const int y1) { //--- coordinates int x2=x1+50; int y2=y1+20; //--- create if(!label.Create(m_chart_id,name+"Label",m_subwin,x1,y1,x2,y2)) return(false); if(!label.Text(text)) return(false); if(!Add(label)) return(false); //--- succeed return(true); }
- label.Create — cria o elemento gráfico de controle nas coordenadas especificadas;
- label.Text — define o texto que será exibido no Label;
- Add — adiciona a label ao painel para que seja gerenciada junto com os demais elementos de controle.
2. Função de criação de botões:
Os botões permitem que o usuário interaja diretamente com o painel. A função inclui parâmetros para texto, dimensões e coordenadas iniciais. Além disso, permite configurar o tamanho dos botões utilizando valores predefinidos.
Exemplo de código:
//+------------------------------------------------------------------+ //| Function to create buttons | //+------------------------------------------------------------------+ bool CRiskManagementPanel::CreateButton(CButton &buttom,const string name, const string text, const int x1, const int y1,int x2_= BUTTON_WIDTH,int y2_ = BUTTON_HEIGHT) { int x2=x1+x2_; int y2=y1+y2_; //--- create if(!buttom.Create(m_chart_id,name,m_subwin,x1,y1,x2,y2)) return(false); if(!buttom.Text(text)) return(false); if(!Add(buttom)) return(false); //--- succeed return(true); }
Dimensões do botão:
- x2_ e y2_ — parâmetros opcionais que definem a largura e a altura do botão. Caso não sejam especificados, utilizam-se os valores padrão (BUTTON_WIDTH e BUTTON_HEIGHT).
Métodos utilizados:
- button.Create — cria o botão nas coordenadas especificadas;
- button.Text — define o texto que será exibido no botão;
- Add — registra o botão na lista de elementos do painel.
3.Função para criação de campos de entrada (edits)
Os campos editáveis permitem ao usuário inserir texto no painel. Essa função inclui um parâmetro opcional para definir o texto inicial.
Exemplo de código:
//+------------------------------------------------------------------+ //| Function to create edits | //+------------------------------------------------------------------+ bool CRiskManagementPanel::CreateEdit(CEdit &m_edit, const string name, const int x1, const int y1, string initial_Text = "") { //--- coordinates int y2=y1+EDIT_HEIGHT; int x2 =x1 +100; //--- create if(!m_edit.Create(m_chart_id,name+"Edit",m_subwin,x1,y1,x2,y2)) return(false); //--- allow editing the content if(!m_edit.ReadOnly(false)) return(false); if(!Add(m_edit)) return(false); m_edit.Text(initial_Text); //--- succeed return(true); }
Dimensões do campo de texto:
- A largura fixa de 100 e a altura definida por EDIT_HEIGHT determinam o tamanho do campo.
Métodos utilizados:
- m_edit.ReadOnly(false) — permite que o usuário edite o campo,
- m_edit.Text — define o texto inicial que será exibido no campo.
4. Função para criação de ComboBoxes
O ComboBox é um elemento de controle complexo que permite ao usuário selecionar um item a partir de uma lista suspensa. Essa função também possibilita definir o texto inicial e adicionar diversos elementos.
Exemplo de código:
//+-------------------------------------------------------------------+ //| Function to create the complex object: combo box | //| This function creates a combo box with multiple selectable items. | //+-------------------------------------------------------------------+ bool CRiskManagementPanel::CreateComboBox(CComboBox &combo_box, const string name, string &elements[], string initial_text, const int x1, const int y1) { //--- calculate coordinates for the combo box int x2 = x1 + COMBO_BOX_WIDTH; int y2 = y1 + COMBO_BOX_HEIGHT; //--- create the combo box control if (!combo_box.Create(m_chart_id, name, m_subwin, x1, y1, x2, y2)) return (false); //--- add items to the combo box for (int i = 0; i < ArraySize(elements); i++) { if (!combo_box.AddItem(elements[i], i)) return (false); } //--- select the initial text combo_box.SelectByText(initial_text); //--- add the combo box to the panel if (!Add(combo_box)) return (false); //--- successfully created the combo box return (true); }
Elementos do ComboBox:
- "elements[]" — array de strings contendo os itens que serão exibidos na lista suspensa,
- "combo_box.AddItem" — adiciona cada elemento ao ComboBox, associando-o a um índice exclusivo.
Seleção inicial:
- "combo_box.SelectByText(initial_text)" — determina qual item será exibido no ComboBox quando ele for criado.
Métodos utilizados:
- "combo_box.Create" — inicializa o ComboBox nas coordenadas especificadas,
- "Add" — adiciona o ComboBox ao painel.
Criação de objetos na área cliente
Nesta seção iniciamos o planejamento e a criação dos elementos da área cliente do nosso interface gráfico. O objetivo é fornecer ao usuário um espaço funcional para configurar e gerenciar parâmetros relacionados ao risco e às ordens de negociação. A seguir está a descrição de como essa área é dividida e como cada componente é criado.
A área cliente é dividida em três seções principais:

-
Seção geral: nesta parte o usuário poderá editar parâmetros gerais, como risco por operação, variação e limite de stop.
-
Cálculo do lote: a segunda parte da interface é destinada ao cálculo do tamanho do lote com base no risco por operação e no stop-loss em pontos. Aqui ocorre a avaliação automática do nível de risco que o usuário está disposto a assumir.
-
Cálculo do stop-loss: na parte inferior do painel, é possível calcular o stop-loss com base no percentual de perda aceitável por operação.
bool CRiskManagementPanel::CreateAreaClientPanel(void) { int x1 = 11; // Initial X coordinate int y1 = 15; // Initial Y coordinate // --- General Section: Risk Per Operation Configuration --- if (!CreateLabel(m_label_risk_per_operation, "L-Risk-Per-operation", "Risk per operation %: ", x1, y1)) return false; // Create the label for risk per operation if (!CreateEdit(m_edit_risk_per_operation, "Risk-Per-operation", x1 + 150, y1)) return false; // Create the editable field for risk per operation y1 += 30; // Move the Y coordinate down for the next section if (!CreateLabel(m_label_deviation, "L-Deviation", "Deviation (Points):", x1, y1)) return false; // Create the label for deviation if (!CreateEdit(m_edit_deviation, "Deviation", x1 + 150, y1, "100")) return false; // Create the editable field for deviation this.deviation = 100; // Default value for deviation y1 += 30; if (!CreateLabel(m_label_stop_limit, "L-StopLimit", "Stop Limit (Points):", x1, y1)) return false; // Create the label for stop limit if (!CreateEdit(m_edit_stop_limit, "Stop Limit", x1 + 150, y1, "50")) return false; // Create the editable field for stop limit this.stop_limit = 50; // Default value for stop limit y1 += 50; // --- Lot Calculation Section --- if (!CreateLabel(m_label_get_lote, "L-Get-Lote-Title", "Get Lote", x1, y1)) return false; // Create the label for lot calculation section if (!CreateComboBox(m_combobox_order_type_get_lot, "ORDER_TYPE_LOT", elements_order_type, "ORDER_TYPE_BUY", x1 + 60, y1)) return false; // Create the combo box to select order type for lot calculation this.order_type_get_lot = ORDER_TYPE_BUY; // Default order type y1 += 30; if (!CreateLabel(m_label_sl, "L-SL", "SL Point: ", x1, y1)) return false; // Create the label for SL point if (!CreateEdit(m_edit_sl, "WRITE-SL", x1 + 60, y1)) return false; // Create the editable field for SL if (!CreateButton(m_button_get_lote, "GET-LOTE", "Save", x1 + 160 + 5, y1)) return false; // Create the button to save the lot calculation y1 += 25; if (!CreateLabel(m_label_result_lote, "L-Result-Lote", "Ideal Lot: ", x1, y1)) return false; // Create the label for displaying the ideal lot if (!CreateLabel(m_label_the_result_lote, "L-The-Result-lot", " ", x1 + 65, y1)) return false; // Create a label to display the calculated ideal lot y1 += 50; // --- Stop Loss Calculation Section --- if (!CreateLabel(m_label_get_sl, "L-Get-SL-Title", "Get SL", x1, y1)) return false; // Create the label for stop loss calculation section if (!CreateComboBox(m_combobox_order_type_get_sl, "ORDER_TYPE_SL", elements_order_type, "ORDER_TYPE_BUY", x1 + 50, y1)) return false; // Create the combo box to select order type for stop loss calculation this.order_type_get_sl = ORDER_TYPE_BUY; // Default order type y1 += 30; if (!CreateLabel(m_label_lote, "L-LOTE", "Get ideal sl:", x1, y1)) return false; // Create the label for getting the ideal stop loss if (!CreateButton(m_button_get_sl, "GET-SL", "Get", x1 + 90, y1)) return false; // Create the button to get the stop loss value y1 += 25; if (!CreateLabel(m_label_result_sl, "L-Result-sl", "Ideal SL:", x1, y1)) return false; // Create the label for displaying the ideal stop loss if (!CreateLabel(m_label_the_result_sl, "L-The-result-sl", " ", x1 + 65, y1)) return false; // Create a label to display the calculated ideal stop loss return true; // If all components are successfully created }
Função principal para criação do painel:
A função Create é o núcleo da inicialização da interface gráfica do usuário. Essa função reúne diversos componentes essenciais para configurar a interface, garantindo sua correta vinculação ao gráfico, assim como a definição adequada de suas dimensões e coordenadas.
Parâmetros de entrada
-
chart (long):
- representa o ID do gráfico no qual a interface será criada,
- por padrão, no MQL5, o gráfico atual possui identificador igual a 0.
-
name (string):
- nome que identifica o painel,
- ele é único e utilizado para fazer referência ao painel em outras operações.
-
subwin (int):
- número da subjanela do gráfico onde o painel será colocado,
- se o painel estiver na janela principal, esse valor será 0. Para subjanetas adicionais, são atribuídos valores sequenciais (1, 2 etc.).
-
x1 e y1 (int):
- coordenadas iniciais do painel (canto superior esquerdo) no gráfico.
-
x2 e y2 (int):
- definem as dimensões do painel:
- x2 — define a largura,
- y2 — define a altura.
- Essas dimensões são especificadas em pixels.
- definem as dimensões do painel:
//+------------------------------------------------------------------+ //| function to create the interface | //+------------------------------------------------------------------+ bool CRiskManagementPanel::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) { if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2)) return(false); if(!CreateAreaClientPanel()) return(false); //--- succeed return(true); }
Funções de atualização dinâmica do Combobox e de outros elementos
Agora, dando continuidade ao desenvolvimento das funções, concluiremos a implementação das funções dinâmicas que alteram os valores dos elementos da interface do usuário, como ComboBox e Label, que exibem os resultados dos cálculos do tamanho do lote (lot size) e do stop-loss (SL). Essas funções permitem alterar rapidamente o texto das labels com os resultados e reagir a mudanças feitas pelo usuário nos ComboBoxes.
1. Funções de edição de labels
Essas funções permitem modificar o texto das labels exibidas na interface, especificamente as labels do tamanho do lote (lot size) e do stop-loss (SL), com base nos cálculos realizados.
//+------------------------------------------------------------------+ //| Function to edit the text of the lot size label | //+------------------------------------------------------------------+ void CRiskManagementPanel::EditLabelResultLote(string text) { // This function updates the text of the label that shows the ideal lot size. this.m_label_the_result_lote.Text(text); // Set the new text to the label }Essa função atualiza o texto da label que exibe o tamanho do lote calculado. O valor textual é recebido como parâmetro e substitui o texto atualmente exibido na label correspondente.
//+------------------------------------------------------------------+ //| Function to edit the text of the stop loss label | //+------------------------------------------------------------------+ void CRiskManagementPanel::EditLabelResultSL(string text) { // This function updates the text of the label that shows the ideal stop loss value. this.m_label_the_result_sl.Text(text); // Set the new text to the stop loss label }
De forma análoga à função anterior, esta função atualiza o texto da label que exibe o valor ideal do stop-loss. Ela também recebe o texto como parâmetro e atualiza a label correspondente.
2. Funções para processamento de alterações no ComboBox
Essas funções são executadas sempre que o usuário seleciona um novo valor no ComboBox que controla o tipo de ordem (por exemplo, compra ou venda). Elas atualizam a variável interna onde é armazenado o tipo de ordem selecionado.
//+------------------------------------------------------------------+ //| Function to update the variable that stores | //| the order type to obtain the ideal sl | //+------------------------------------------------------------------+ void CRiskManagementPanel::OnChangeComBoxOrderTypeGetSL(void) { // Iterate through the order types array to find the selected type for(int i = 0; i < ArraySize(elements_order_type); i++) { // If the selected order type matches the one in the array if(m_combobox_order_type_get_sl.Select() == elements_order_type[i]) { // Update the order type variable for stop loss this.order_type_get_sl = StringOrderTypeToEnum(m_combobox_order_type_get_sl.Select()); Print("New order type for sl: ", EnumToString(this.order_type_get_sl)); // Log the selected order type break; } } }
A função é acionada quando o usuário altera o tipo de ordem no ComboBox utilizado para obter o stop-loss. Ela percorre o array de tipos de ordens e verifica qual deles foi selecionado no ComboBox. Em seguida, atualiza a variável interna order_type_get_sl com o tipo correspondente, utilizando a função StringOrderTypeToEnum.
//+------------------------------------------------------------------+ //| Function to update the variable that stores | //| the order type to obtain the ideal lot | //+------------------------------------------------------------------+ void CRiskManagementPanel::OnChangeComBoxOrderTypeGetLote(void) { // Iterate through the order types array to find the selected type for(int i = 0; i < ArraySize(elements_order_type); i++) { // If the selected order type matches the one in the array if(m_combobox_order_type_get_lot.Select() == elements_order_type[i]) { // Update the order type variable for lot size this.order_type_get_lot = StringOrderTypeToEnum(m_combobox_order_type_get_lot.Select()); Print("New order type for lot: ", EnumToString(this.order_type_get_lot)); // Log the selected order type break; } } }
De forma semelhante à função anterior, esta realiza a mesma tarefa, mas aplicada ao cálculo do tamanho ideal do lote. Quando o usuário altera o tipo de ordem no ComboBox para o cálculo do lote, a função atualiza a variável order_type_get_lot com o tipo selecionado.
3. Função adicional para converter uma string em ENUM_ORDER_TYPE
Por fim, essa função converte uma string que representa o tipo de ordem (por exemplo, "ORDER_TYPE_BUY") no valor enumerado correspondente do tipo ENUM_ORDER_TYPE.
//+------------------------------------------------------------------+ //| Function to convert a string into an order type | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE CRiskManagementPanel::StringOrderTypeToEnum(const string OrderType) { // Convert the string order type to its corresponding enum value if(OrderType == "ORDER_TYPE_BUY") return ORDER_TYPE_BUY; if(OrderType == "ORDER_TYPE_SELL") return ORDER_TYPE_SELL; if(OrderType == "ORDER_TYPE_BUY_LIMIT") return ORDER_TYPE_BUY_LIMIT; if(OrderType == "ORDER_TYPE_SELL_LIMIT") return ORDER_TYPE_SELL_LIMIT; if(OrderType == "ORDER_TYPE_BUY_STOP") return ORDER_TYPE_BUY_STOP; if(OrderType == "ORDER_TYPE_SELL_STOP") return ORDER_TYPE_SELL_STOP; if(OrderType == "ORDER_TYPE_BUY_STOP_LIMIT") return ORDER_TYPE_BUY_STOP_LIMIT; if(OrderType == "ORDER_TYPE_SELL_STOP_LIMIT") return ORDER_TYPE_SELL_STOP_LIMIT; // Return WRONG_VALUE if no match is found return WRONG_VALUE; }
Tratamento de eventos do teclado: implementação de OnCharEvent
Concluindo este artigo, definiremos o método OnChartEvent, previamente declarado em nossa classe. Esse método será responsável por executar as funções criadas na seção anterior, bem como por realizar o cálculo do tamanho do lote e do stop-loss ao pressionar os botões correspondentes, atualizar os campos de entrada e assim por diante.
Mas antes de começar a implementação, precisamos compreender o princípio de funcionamento dos eventos na biblioteca de elementos de controle do MQL5.
É importante esclarecer que, em vez de utilizar eventos predefinidos como CHARTEVENT_OBJECT_CLICK (que são comuns em muitas linguagens de programação), utilizamos eventos personalizados. Esses eventos são definidos em um arquivo especial chamado Defines.mqh, localizado na pasta includes\Controls\Defines.mqh do nosso projeto.
O arquivo Defines.mqh contém enumerações e definições necessárias para o funcionamento dos elementos de controle. Além dos valores predefinidos, como cores dos controles ou da interface, os mais importantes, no nosso caso, são as definições dos eventos, que se encontram no final do arquivo. A seguir explico como eles estão organizados e como os utilizamos.
Definição dos eventos
No arquivo Defines.mqh há diversos eventos personalizados que podem ser utilizados pelos elementos de controle.
//+------------------------------------------------------------------+ //| Events | //+------------------------------------------------------------------+ #define ON_CLICK (0) // clicking on control event #define ON_DBL_CLICK (1) // double clicking on control event #define ON_SHOW (2) // showing control event #define ON_HIDE (3) // hiding control event #define ON_CHANGE (4) // changing control event #define ON_START_EDIT (5) // start of editing event #define ON_END_EDIT (6) // end of editing event #define ON_SCROLL_INC (7) // increment of scrollbar event #define ON_SCROLL_DEC (8) // decrement of scrollbar event #define ON_MOUSE_FOCUS_SET (9) // the "mouse cursor entered the control" event #define ON_MOUSE_FOCUS_KILL (10) // the "mouse cursor exited the control" event #define ON_DRAG_START (11) // the "control dragging start" event #define ON_DRAG_PROCESS (12) // the "control is being dragged" event #define ON_DRAG_END (13) // the "control dragging end" event #define ON_BRING_TO_TOP (14) // the "mouse events priority increase" event #define ON_APP_CLOSE (100) // "closing the application" event //+------------------------------------------------------------------+
Uso de eventos no painel
No nosso caso específico, não precisamos utilizar todos os eventos disponíveis. Em vez disso, focaremos em três principais:
- ON_CLICK — o evento é acionado quando ocorre um clique em um elemento de controle, por exemplo, um botão;
- ON_CHANGE — usado quando o valor de um elemento de controle é alterado, como no caso de um ComboBox;
- ON_END_EDIT — executado quando o usuário finaliza a edição de um campo, por exemplo, um Edit.
Exemplo de implementação do evento em OnChartEvent
Os eventos definidos em Defines.mqh são atribuídos ao parâmetro ID dentro da função OnChartEvent. Essa função é responsável por processar os eventos que ocorrem no gráfico ou no painel. O ID é combinado com o valor CHARTEVENT_CUSTOM para criar um identificador único para cada evento.
Por exemplo, o evento ON_CLICK será utilizado da seguinte forma:
if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_lote.Id()) { // Acción a ejecutar cuando el botón de obtener lote es presionado // Aquí se llamaría a la función correspondiente para gestionar la acción del botón }
Criação da função OnEvent
1. Declaração da função
bool CRiskManagementPanel::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
A função OnEvent é um manipulador de eventos que recebe diversos parâmetros:
- id — identificador do evento ocorrido;
- lparam — parâmetro adicional que geralmente contém informações específicas de um determinado elemento de controle ou componente;
- dparam — parâmetro do tipo double, utilizado para transmitir valores numéricos;
- sparam — parâmetro do tipo string, utilizado para transmitir dados textuais.
O objetivo dessa função é processar eventos da interface, como alterações nos campos de texto ou cliques em botões.
2. Verificação de alteração no ComboBox responsável pelo tipo de ordem
Os elementos ComboBox permitem que o usuário selecione um valor em uma lista suspensa. Nesta etapa, verificamos se o usuário alterou o valor exibido no ComboBox.
Para o ComboBox responsável pelo lote:
if(id == ON_CHANGE + CHARTEVENT_CUSTOM && lparam == m_combobox_order_type_get_lot.Id()) { OnChangeComBoxOrderTypeGetLote(); }
Para o ComboBox responsável pelo stop-loss (SL):
if(id == ON_CHANGE + CHARTEVENT_CUSTOM && lparam == m_combobox_order_type_get_sl.Id()) { OnChangeComBoxOrderTypeGetSL(); }
- ON_CHANGE + CHARTEVENT_CUSTOM — evento que indica que o valor do elemento de controle foi modificado.
- Na condição "lparam == m_combobox_order_type_get_lot.Id()", verifica-se se o evento corresponde ao ComboBox apropriado, utilizando seu ID.
Nesse caso, são executadas as funções OnChangeComBoxOrderTypeGetLote e OnChangeComBoxOrderTypeGetSL, que atualizam a variável interna ENUM_ORDER_TYPE que representa o tipo de ordem selecionado.
3. Edits
Os campos de entrada (edits) permitem ao usuário inserir valores manualmente. Aqui são verificadas e atualizadas as variáveis associadas a cada campo editável.
Para o campo "Risco por operação":
if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_risk_per_operation.Id()) { this.risk_per_operation = StringToDouble(m_edit_risk_per_operation.Text()); this.risk_per_operation = NormalizeDouble((this.risk_per_operation / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE), 2); Print("Edit Risk Per Operation: ", this.risk_per_operation); }
Para o campo "SL":
if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_sl.Id()) { this.sl = StringToInteger(m_edit_sl.Text()); Print("Edit SL: ", this.sl); }
E para outros campos, como "Deviation" e "Stop Limit":
if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_deviation.Id()) { this.deviation = StringToDouble(m_edit_deviation.Text()); Print("Edit Deviation: ", this.deviation); } if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_stop_limit.Id()) { this.stop_limit = StringToDouble(m_edit_stop_limit.Text()); Print("Edit Stop Limit: ", this.stop_limit); }
- ON_END_EDIT + CHARTEVENT_CUSTOM — evento acionado quando o usuário finaliza a edição do campo.
- Cada instrução if verifica se lparam corresponde ao ID do elemento de controle (como um edit) e, caso corresponda, a variável interna apropriada é atualizada com o novo valor informado pelo usuário. Os valores são convertidos para os tipos adequados (double, int etc.) e normalizados quando necessário.
4. Obtenção do lote ideal e do stop-loss
Aqui verificamos se o usuário clicou nos botões para obter o lote ideal ou o stop-loss ideal. Dependendo do botão pressionado, são realizados os cálculos correspondentes.
Para o botão "Get Lot":
if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_lote.Id()) { Print("Risk Per operation: ", this.risk_per_operation); Print("SL in points: ", this.sl); Print("Order type get lot: ", EnumToString(this.order_type_get_lot)); double new_lot; double new_risk_per_operation; GetIdealLot(new_lot, GetMaxLote(this.order_type_get_lot), this.risk_per_operation, new_risk_per_operation, this.sl); PrintFormat("Loss in case the following operation fails, with the parameters: lot %.2f and stop loss of %i points will be %.2f ", new_lot, this.sl, new_risk_per_operation); EditLabelResultLote(DoubleToString(new_lot, 2)); m_button_get_lote.Pressed(false); }
Para o botão "Get SL":
if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_sl.Id()) { Print("Risk Per operation: ", this.risk_per_operation); Print("Order type get sl: ", EnumToString(this.order_type_get_lot)); double new_lot; long new_sl = CalculateSL(this.order_type_get_sl, this.risk_per_operation, new_lot, this.deviation, this.stop_limit); PrintFormat("For the risk per operation %.2f the chosen lot is %.2f and the ideal stop loss in points is %i", this.risk_per_operation, new_lot, new_sl); EditLabelResultSL(IntegerToString(new_sl)); m_button_get_sl.Pressed(false); }
- ON_CLICK + CHARTEVENT_CUSTOM — verifica-se se o evento de clique corresponde a um dos botões (m_button_get_lote ou m_button_get_sl).
- Os cálculos são executados com as funções GetIdealLot e CalculateSL para determinar o lote ideal e o stop-loss ideal.
- Os resultados são exibidos nas labels da interface (EditLabelResultLote e EditLabelResultSL).
- Por fim, o botão é desativado, definindo seu estado como falso (m_button_get_lote. Pressed(false)).
5. Retorno do evento OnChartEvent
Por fim, após o processamento dos eventos, a função retorna o evento para a classe base CAppDialog, permitindo que a função básica também trate quaisquer outros eventos que possam ter ocorrido:
return(CAppDialog::OnEvent(id, lparam, dparam, sparam));
Esse passo é importante para garantir que outros eventos relevantes, que não são tratados diretamente em OnEvent, não sejam ignorados.
Inicialização do painel: uso do evento OnInit e configuração geral
Nesta seção configuraremos a interface de gerenciamento de riscos por meio da classe personalizada CRiskManagementPanel. Essa interface será responsável por gerenciar os cálculos do lote, do stop-loss e outras funções importantes para o trader, apresentadas por meio do painel gráfico. Em seguida, veremos como declarar, inicializar e manipular essa interface no programa.
1. Declaração do objeto global
Primeiro declaramos um objeto da classe CRiskManagementPanel na área global do programa. Isso torna a interface acessível a partir de diferentes métodos do código, como OnInit, OnDeinit e OnChartEvent.
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ CRiskManagementPanel risk_panel; // Declare the panel object globally
2. Configuração da interface no evento OnInit
No evento OnInit configuramos a interface utilizando o método Create. Esse método inicializa a janela gráfica da interface e define sua posição e dimensões. Caso a criação da interface falhe, o programa retorna o status INIT_FAILED, indicando um erro de inicialização. Em seguida, ativamos a interface com o método run, que inicia o painel gráfico para interação com o usuário.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Create the risk management panel if(!risk_panel.Create(0, "Test Risk Management", 0, 40, 40, 380, 380)) return(INIT_FAILED); // If panel creation fails, return an error code //--- Run the panel risk_panel.Run(); // Start the graphical interface //--- Return success return(INIT_SUCCEEDED); }
- 0 (chart_id) — indica que a interface será criada na janela principal do gráfico.
- "Test Risk Management" (nome) — define o título que será exibido na janela da interface.
- 0 (subwin) — indica a subjanela em que a interface será exibida. Como a criamos no gráfico principal, utilizamos 0.
- 40, 40 (coordenadas) — definem a posição inicial da interface em pixels, nas coordenadas x(40) e y(40).
- 380, 380 (dimensões) — determinam a largura e a altura da interface em pixels.
3. Remoção da interface no evento OnDeinit
Quando o programa é encerrado ou fechado, é necessário liberar os recursos utilizados pela interface. Para isso, utilizamos o método Destroy dentro do evento OnDeinit. Isso garante que não permaneçam elementos gráficos residuais que possam interferir na execução de programas futuros.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Destroy the risk management panel risk_panel.Destroy(reason); // Free resources and remove the panel }
4. Processamento de eventos por meio de OnChartEvent
O método OnChartEvent é essencial para capturar e interpretar as interações do usuário com a interface, como cliques em botões, inserção de dados em campos de texto ou seleção de opções em listas suspensas. Esses eventos são tratados pelo método ChartEvent da classe CRiskManagementPanel.
//+------------------------------------------------------------------+ //| Expert chart event function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // Event ID const long& lparam, // Event parameter of type long const double& dparam, // Event parameter of type double const string& sparam) // Event parameter of type string { //--- Pass the chart event to the risk panel for processing risk_panel.ChartEvent(id, lparam, dparam, sparam); }
Testes e validação da interface
Agora executaremos o Expert Advisor com o nome que definimos, aplicando-o a um símbolo; no meu caso, executarei no símbolo XAUUSD, ou seja, ouro. O gráfico ficará aproximadamente assim:
Para que tudo fique mais claro e seja possível visualizar eventuais erros, o saldo da minha conta está da seguinte forma:
Como podemos ver, no momento não tenho nenhuma posição aberta.
Agora mostrarei como você pode utilizar isso:
1. É necessário preencher o campo "Risk Per Operation" com um valor percentual (ele será aplicado ao saldo atual). Na imagem a seguir escolherei 3% da minha conta.
Observação: caso deseje, é possível modificar os campos de entrada (edits) referentes ao desvio (deviation) e ao limite de stop (stop limit).
2. Selecione o que deseja calcular. Para começar, utilizarei a função de obtenção do lote ideal para minha operação com base no stop-loss em pontos do símbolo, no risco por operação e no tipo de ordem, selecionando um stop-loss de 500 pontos e uma ordem de compra (ORDER_TYPE_BUY).
Clicamos com o botão esquerdo do mouse no botão "Save" na linha "SL Point", e aqui está o resultado:
O resultado foi um lote de 0,03, como é possível ver na interface; além disso, isso também apareceu na área de mensagens da aba Experts:
Na mensagem está a informação sobre o tamanho máximo do lote, que será 0,45, o risco por operação, e também que, caso a operação seja encerrada com o lote e o stop-loss calculados, a perda será de 15.45 USD.
3. Agora vamos testar o método de cálculo do lote com base no risco por operação: clicamos com o botão esquerdo no botão "get" (ele está na mesma linha da label "Get ideal sl"):
Como resultado, obtivemos um stop-loss de 1856 pontos, além de:
Um tamanho de lote de 0,01, considerado ideal para um risco de 3,0%.
Considerações finais
Neste artigo analisamos a implementação do "calculador" de lote e stop-loss — uma ferramenta extremamente útil para qualquer trader. Além disso, exploramos como utilizar os classes e bibliotecas oferecidos pelo MQL5 para criar interfaces personalizadas. Também aprofundamos a estrutura de classes no MQL5, incluindo herança e o uso de objetos como campos de entrada (edits), listas suspensas (comboboxes) e botões (buttons), que nos permitem criar interfaces gráficas dinâmicas e funcionais.
Essa abordagem não apenas otimiza o fluxo de trabalho, mas também abre um vasto leque de possibilidades para o desenvolvimento de ferramentas personalizadas no MQL5. Na próxima parte da série, começaremos a trabalhar no classe principal, unindo os conceitos apresentados neste artigo. Espero que este guia tenha sido útil e que motive você a continuar explorando e criando interfaces gráficas avançadas para aprimorar sua experiência de trading.
Todos os arquivos criados e utilizados neste artigo estão apresentados na tabela a seguir:| Nome do arquivo | Descrição |
|---|---|
| "Risk_Management_Panel.mq5" | Arquivo do Expert Advisor que contém o painel de gerenciamento de riscos desenvolvido neste artigo |
| "Risk_Management.mqh" | Arquivo incluído que define as funções para cálculo do tamanho do lote, do stop-loss e outras funções de gerenciamento de riscos |
Traduzido do espanhol pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/es/articles/16985
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.
Do básico ao intermediário: Sobrecarga de operadores (II)
Desenvolvimento de um Kit de Ferramentas para Análise da Ação do Preço (Parte 6): Mean Reversion Signal Reaper
Simulação de mercado: A união faz a força (I)
Técnicas do MQL5 Wizard que você deve conhecer (Parte 51): Aprendizado por Reforço com SAC
- 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