Monitoramento de sinais de negociação multimoeda (Parte 2): Implementação da parte visual do aplicativo
Índice
- Introdução
- Etapa 1 da instalação: Símbolos
- Etapa 2 da instalação: Período gráfico
- Etapa 3 da instalação: Adição de sinais
- Janela de criação e edição de sinais de negociação
- Monitoramento dos sinais de negociação
- Conclusão
Introdução
No estágio anterior, nós desenvolvemos a estrutura geral do monitor de sinais de negociação multimoeda. Nesta parte, nós implementaremos sequencialmente os estágios passo a passo relacionados à configuração inicial do aplicativo e criaremos a interação básica dos elementos que compõem a interface.
Etapa 1 da instalação: Símbolos
De acordo com a estrutura do aplicativo, a primeira etapa de configuração do aplicativo durante o primeiro lançamento implica a criação de uma interface para a seleção de símbolos que serão utilizados posteriormente para procurar os sinais de negociação criados. No final do artigo anterior, nós criamos uma estrutura do aplicativo, que se baseia em todo o nosso trabalho seguinte. Vamos prosseguir para o desenvolvimento do aplicativo. Primeiro, nós definiremos os principais grupos de elementos necessários para a implementação desta parte do aplicativo:
- Janela do aplicativo.
- Seleção rápida dos símbolos.
- Campo de entrada do grupo.
- Botões Save e Load para os grupos de símbolo.
- A lista completa de todos os símbolos disponíveis como uma caixa de seleção com um rótulo de texto para o nome do símbolo.
- O botão Next para alternar para a segunda etapa da configuração: Seleção do período gráfico.
A estrutura de arquivos criada anteriormente deve ficar assim:
Fig. 1 Estrutura de arquivos do aplicativo.
Primeiro, abrimos o aplicativo SignalMonitor.mq5 e adicionamos os parâmetros de entrada a ele. Você poderá configurar os parâmetros ao executar o aplicativo diretamente da plataforma MetaTrader 5. Além disso, declaramos a instância do arquivo da classe CProgram criada anteriormente e inicializamos algumas das variáveis. Editamos o arquivo da seguinte maneira:
//+------------------------------------------------------------------+ //| SignalMonitor.mq5 | //| Copyright 2019, Alexander Fedosov | //| https://www.mql5.com/en/users/alex2356 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, Alexander Fedosov" #property link "https://www.mql5.com/en/users/alex2356" #property version "1.00" //--- Include application class #include "Program.mqh" //+------------------------------------------------------------------+ //| Expert Advisor input parameters | //+------------------------------------------------------------------+ input int Inp_BaseFont = 10; // Basic font input color Caption = C'0,130,225'; // Caption color input color Background = clrWhiteSmoke; // Background color //--- CProgram program; ulong tick_counter; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(void) { //--- tick_counter=GetTickCount(); //--- Initialize class variables program.OnInitEvent(); program.m_base_font_size=Inp_BaseFont; program.m_background=Background; program.m_caption=Caption; //--- Set up the trading panel if(!program.CreateGUI()) { Print(__FUNCTION__," > Failed to create graphical interface!"); return(INIT_FAILED); } //--- Initialization completed successfully return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { program.OnDeinitEvent(reason); } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer(void) { program.OnTimerEvent(); } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { program.ChartEvent(id,lparam,dparam,sparam); //--- if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI) { Print("End in ",GetTickCount()-tick_counter," ms"); } } //+------------------------------------------------------------------+
Como podemos ver no código acima, foram adicionados três parâmetros de entrada:
- Tamanho da fonte.
- A cor do cabeçalho para as janelas do aplicativo.
- A cor de fundo das janelas e dos elementos do aplicativo.
Em seguida, declaramos a instância de classe CProgram denominada program e a variável tick_counter (ela é necessária apenas para exibir as informações sobre o tempo de inicialização do aplicativo). Além disso, no método OnInit() nós inicializamos as variáveis de instância de classe atribuindo a elas os valores dos parâmetros de entrada do aplicativo. Além disso, nós chamamos o método CreateGUI() que iniciará o app.
No entanto, se você tentar compilar o arquivo aberto agora, você receberá erros de compilação, informando que as variáveis m_base_font_size, m_background, m_caption e o método CreateGUI() não foram encontrados no método da classe CProgram. Portanto, vamos abrir o arquivo Program.mqh para implementar a classe CProgram. Primeiro, adicionamos as variáveis mencionadas acima e o método, além de outros métodos necessários para a inicialização correta do aplicativo. Após adicionarmos todos os elementos necessários, a classe CProgram ficará da seguinte maneira:
//+------------------------------------------------------------------+ //| Class for creating an application | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { public: //--- int m_base_font_size; //--- string m_base_font; //--- color m_background; color m_caption; public: CProgram(void); ~CProgram(void); //--- Initialization/deinitialization void OnInitEvent(void); void OnDeinitEvent(const int reason); //--- Timer void OnTimerEvent(void); //--- Chart event handler virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- Create the graphical interface of the program bool CreateGUI(void); };
A implementação do método que cria a interface ainda está vazia:
//+------------------------------------------------------------------+ //| Creates the graphical interface of the program | //+------------------------------------------------------------------+ bool CProgram::CreateGUI(void) { //--- //--- Finish the creation of GUI CWndEvents::CompletedGUI(); return(true); } //+------------------------------------------------------------------+
Preste atenção que também nós adicionamos a variável do tipo string m_base_font que é responsável pelo nome da fonte no aplicativo. Ela é inicializada em nosso construtor de classe:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CProgram::CProgram(void) { m_base_font="Trebuchet MS"; }
Agora vamos continuar criando a primeira janela do aplicativo. Para esse fim, nós declaramos na classe a nova variável m_step_window que é uma instância de classe CWindow. Declare também um método que criará a primeira janela e nomeamos ela de CreateStepWindow(). O código da classe terá o seguinte aspecto:
class CProgram : public CWndEvents { public: //--- Application windows CWindow m_step_window; ... protected: //--- forms bool CreateStepWindow(const string caption_text);
Nós determinamos anteriormente que a implementação da parte da interface responsável pela configuração passo a passo do lançamento inicial estará localizada no arquivo de inclusão StepWindow.mqh. Então, abrimos e damos início à implementação do método CreateStepWindow():
#include "Program.mqh" //+------------------------------------------------------------------+ //| Creates a form for the selection of symbols | //+------------------------------------------------------------------+ bool CProgram::CreateStepWindow(const string text) { //--- Add the pointer to the window array CWndContainer::AddWindow(m_step_window); //--- Properties m_step_window.XSize(600); m_step_window.YSize(200); //--- Coordinates int x=int(ChartGetInteger(m_chart_id,CHART_WIDTH_IN_PIXELS)-m_step_window.XSize())/2; int y=10; m_step_window.CaptionHeight(22); m_step_window.IsMovable(true); m_step_window.CaptionColor(m_caption); m_step_window.CaptionColorLocked(m_caption); m_step_window.CaptionColorHover(m_caption); m_step_window.BackColor(m_background); m_step_window.FontSize(m_base_font_size); m_step_window.Font(m_base_font); //--- Creating a form if(!m_step_window.CreateWindow(m_chart_id,m_subwin,text,x,y)) return(false); //--- return(true); } //+------------------------------------------------------------------+
Não se esqueça de adicionar o seguinte código ao método CreateGUI():
//+------------------------------------------------------------------+ //| Creates the graphical interface of the program | //+------------------------------------------------------------------+ bool CProgram::CreateGUI(void) { //--- Step 1-3 if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols")) return(false); //--- Finish the creation of GUI CWndEvents::CompletedGUI(); return(true); } //+------------------------------------------------------------------+
Se a sequência de ações estiver correta, você verá o formulário recém-criado depois de compilar e executar o arquivo SignalMonitor.mq5 na plataforma:
Fig. 2 A primeira janela do aplicativo
Os primeiros elementos da janela criada incluem um grupo de botões que permitem a seleção rápida de conjuntos de símbolos predefinidos na plataforma: forex.all, forex.crosses, forex.major. No arquivo Program.mqh, nós adicionamos um array das instâncias da classe CButton com o valor da dimensão igual a três, bem como um método universal CreateSymbolSet() para a criação dos botões:
//+------------------------------------------------------------------+ //| Class for creating an application | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { public: //--- Application windows CWindow m_step_window; //--- Simple buttons CButton m_currency_set[3]; ... //--- Buttons bool CreateSymbolSet(CButton &button,string text,const int x_gap,const int y_gap);
Agora, abrimos o arquivo StepWindow.mqh e adicionamos a implementação do método acima.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateSymbolSet(CButton &button,string text,const int x_gap,const int y_gap) { //--- color baseclr=C'220,225,235'; color pressed=C'55,160,250'; //--- Save the window pointer button.MainPointer(m_step_window); //--- Set properties before creation button.TwoState(true); button.XSize(80); button.YSize(30); button.LabelXGap(19); button.LabelYGap(2); button.Font(m_base_font); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(pressed); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(pressed); button.LabelColor(clrBlack); button.LabelColorPressed(clrWhite); button.IsCenterText(true); //--- Create a control if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add a pointer to element to the base CWndContainer::AddToElementsArray(0,button); return(true); } //+------------------------------------------------------------------+
Agora, nós precisamos apenas adicionar os três botões usando esse método com os diferentes valores de coordenadas e rótulos de texto no método básico CreateStepWindow() da janela após a criação do formulário:
... //--- Creating a form if(!m_step_window.CreateWindow(m_chart_id,m_subwin,text,x,y)) return(false); //--- if(!CreateSymbolSet(m_currency_set[0],"ALL",10,30)) return(false); if(!CreateSymbolSet(m_currency_set[1],"Major",10+100,30)) return(false); if(!CreateSymbolSet(m_currency_set[2],"Crosses",10+2*(100),30)) return(false); ...
Após a compilação, o resultado será o seguinte:
Fig. 3 Adicionando os botões para a seleção rápida de grupos de símbolos.
Em seguida, nós adicionamos um campo de entrada para o nome do grupo de símbolos selecionado, que pode ser salvo e carregado usando dois botões: Save e Load. Para fazer isso nós adicionamos uma instância de classe para criar o campo de entrada CTextEdit e mais duas instâncias de classe para a criação dos botões CButton. Como os botões Save e Load diferem apenas em seus nomes, criamos o método universal CreateButton1() e, para o campo de entrada, incluímos o método CreateEditValue() à classe CProgram:
//+------------------------------------------------------------------+ //| Class for creating an application | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { public: //--- Application windows CWindow m_step_window; //--- Simple buttons CButton m_currency_set[3]; CButton m_load_button; CButton m_save_button; //--- Input fields CTextEdit m_text_edit; ... bool CreateButton1(CButton &button,string text,const int x_gap,const int y_gap); //--- Input field bool CreateEditValue(CTextEdit &text_edit,const int x_gap,const int y_gap);
Voltamos para o arquivo StepWindow.mqh e adicionamos a implementação dos métodos criados no final do arquivo.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateEditValue(CTextEdit &text_edit,const int x_gap,const int y_gap) { //--- Store the pointer to the main control text_edit.MainPointer(m_step_window); //--- Properties text_edit.XSize(110); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(110); text_edit.GetTextBoxPointer().DefaultTextColor(clrSilver); text_edit.GetTextBoxPointer().DefaultText("Template name"); //--- Create a control if(!text_edit.CreateTextEdit("",x_gap,y_gap)) return(false); //--- Add an object to the common array of object groups CWndContainer::AddToElementsArray(0,text_edit); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateButton1(CButton &button,string text,const int x_gap,const int y_gap) { //--- color baseclr=C'70,180,70'; color pressed=C'70,170,70'; //--- Save the window pointer button.MainPointer(m_step_window); //--- Set properties before creation button.XSize(80); button.YSize(30); button.Font(m_base_font); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(pressed); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(pressed); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); //--- Create a control if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add a pointer to element to the base CWndContainer::AddToElementsArray(0,button); return(true); }
Então, voltamos para a classe CreateStepWindow() para adicionar os dois botões e um campo de entrada na janela do aplicativo.
//--- if(!CreateEditValue(m_text_edit,300,m_step_window.CaptionHeight()+10)) return(false); //--- if(!CreateButton1(m_load_button,"Load(L)",m_step_window.XSize()-2*(80+10),m_step_window.CaptionHeight()+10)) return(false); if(!CreateButton1(m_save_button,"Save(S)",m_step_window.XSize()-(80+10),m_step_window.CaptionHeight()+10)) return(false);
Compilamos novamente o arquivo SignalMonitor.mq5. Aqui está o resultado:
Fig.4 Adicionamos um campo de entrada para os grupos de símbolos e os botões Save/Load.
Agora, vamos prosseguir com a visualização e a capacidade de selecionar todos os símbolos disponíveis para a conta selecionada na plataforma MetaTrader 5. Observamos que, se você exibir todos os símbolos disponíveis, a altura da janela do aplicativo não será suficiente. Uma boa solução é permitir o ajuste automático da altura da janela, dependendo dos dados. A adição do número total de símbolos é semelhante: adicionamos um array de instâncias de classe para criar as caixas de seleção CCheckBox e os métodos universais que os criam (pois eles diferem apenas no nome).
... //--- Checkboxes CCheckBox m_checkbox[]; ... //--- Checkboxes bool CreateCheckBox(CCheckBox &checkbox,const int x_gap,const int y_gap,const string text);
A dimensão do array m_checkbox[] não é especificada porque não se sabe com antecedência quantos símbolos estão presentes na conta selecionada na plataforma. Portanto, vamos criar duas variáveis na seção private da classe CProgram e atribuir a eles o número total de símbolos disponíveis e o número de símbolos atualmente selecionados na Observação do Mercado.
private: //--- int m_symbol_total; int m_all_symbols;
No construtor da classe, atribuímos a eles os valores necessários e definimos a dimensão apropriada para o array m_checkbox[]:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CProgram::CProgram(void) { m_base_font="Trebuchet MS"; m_symbol_total=SymbolsTotal(true); m_all_symbols=SymbolsTotal(false); ArrayResize(m_checkbox,m_all_symbols); }
Adicionamos esta implementação do método ao final do arquivo StepWindow.mqh:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateCheckBox(CCheckBox &checkbox,const int x_gap,const int y_gap,const string text) { //--- Store the pointer to the main control checkbox.MainPointer(m_step_window); //--- Properties checkbox.GreenCheckBox(true); checkbox.IsPressed(false); checkbox.Font(m_base_font); checkbox.FontSize(m_base_font_size); checkbox.BackColor(m_background); checkbox.LabelColorHover(C'55,160,250'); //--- Create a control if(!checkbox.CreateCheckBox(text,x_gap,y_gap)) return(false); //--- Add a pointer to element to the base CWndContainer::AddToElementsArray(0,checkbox); return(true); }
Adicionamos as caixas de seleção no método CreateStepWindow(). No código abaixo, a lista inteira de símbolos disponíveis possui 7 colunas. Além disso, a altura da janela é alterada de acordo com o número de linhas recebidas.
//--- Checkboxes int k=0; for(int j=0; j<=MathCeil(m_all_symbols/7); j++) { for(int i=0; i<7; i++) { if(k<m_all_symbols) if(!CreateCheckBox(m_checkbox[k],10+80*i,m_step_window.CaptionHeight()+70+j*25,SymbolName(k,false))) return(false); k++; } } m_step_window.ChangeWindowHeight(m_checkbox[m_all_symbols-1].YGap()+30+30);
Compilamos as adições obtidas
Fig.5 Adição das caixas de seleção com todos os símbolos disponíveis.
O último elemento desta parte do aplicativo inclui os botões de navegação para alternar entre as etapas de configuração. Eles podem ser facilmente adicionados: adicionamos duas instâncias da classe CButton nomeada m_next_button e m_back_button, usamos o método de criação CreateButton1(), que foi criado anteriormente. Adicionamos o seguinte no método de criação da janela CreateStepWindow():
//--- if(!CreateButton1(m_back_button,"Back",m_step_window.XSize()-2*(80+10),m_step_window.YSize()-(30+10))) return(false); if(!CreateButton1(m_next_button,"Next",m_step_window.XSize()-(80+10),m_step_window.YSize()-(30+10))) return(false);
Agora nós só precisamos configurar a operação dos botões usando os conjuntos de símbolos predefinidos que podem ser selecionados. Vamos ao arquivo Program.mqh, encontramos o OnEvent() e adicionamos o seguinte código:
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Pressing the button event if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { //--- All if(lparam==m_currency_set[0].Id() && m_currency_set[0].IsPressed()) { m_currency_set[1].IsPressed(false); m_currency_set[2].IsPressed(false); m_currency_set[1].Update(true); m_currency_set[2].Update(true); //--- for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsPressed(true); m_checkbox[i].Update(true); } } //--- Majors else if(lparam==m_currency_set[1].Id() && m_currency_set[1].IsPressed()) { m_currency_set[0].IsPressed(false); m_currency_set[2].IsPressed(false); m_currency_set[0].Update(true); m_currency_set[2].Update(true); //--- string pairs[4]= {"EURUSD","GBPUSD","USDCHF","USDJPY"}; //--- Clear the selection for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsPressed(false); m_checkbox[i].Update(true); } //--- for(int i=0; i<m_all_symbols; i++) { for(int j=0; j<4; j++) if(m_checkbox[i].LabelText()==pairs[j]) { m_checkbox[i].IsPressed(true); m_checkbox[i].Update(true); } } } //--- Crosses else if(lparam==m_currency_set[2].Id() && m_currency_set[2].IsPressed()) { m_currency_set[0].IsPressed(false); m_currency_set[1].IsPressed(false); m_currency_set[0].Update(true); m_currency_set[1].Update(true); //--- string pairs[20]= { "EURUSD","GBPUSD","USDCHF","USDJPY","USDCAD","AUDUSD","AUDNZD","AUDCAD","AUDCHF","AUDJPY", "CHFJPY","EURGBP","EURAUD","EURCHF","EURJPY","EURCAD","EURNZD","GBPCHF","GBPJPY","CADCHF" }; //--- Clear the selection for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsPressed(false); m_checkbox[i].Update(true); } //--- for(int i=0; i<m_all_symbols; i++) { for(int j=0; j<20; j++) if(m_checkbox[i].LabelText()==pairs[j]) { m_checkbox[i].IsPressed(true); m_checkbox[i].Update(true); } } } //--- if((lparam==m_currency_set[0].Id() && !m_currency_set[0].IsPressed()) || (lparam==m_currency_set[1].Id() && !m_currency_set[1].IsPressed()) || (lparam==m_currency_set[2].Id() && !m_currency_set[2].IsPressed()) ) { //--- Clear the selection for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsPressed(false); m_checkbox[i].Update(true); } } } }
A ideia desta implementação é a seguinte:
- Um clique em ALL seleciona todos os símbolos.
- Um clique em Major remove a seleção anterior e define um conjunto de símbolos correspondentes a forex.major na plataforma.
- Um clique em Crosses remove a seleção anterior e define um conjunto de símbolos correspondentes a crosses.major na plataforma.
- Quando todos os três botões são pressionados, qualquer seleção é cancelada.
É assim que ele se parece:
Fig.6 Implementação da interação básica dos elementos.
São necessárias duas pequenas adições para concluir a implementação visual. Você pode ver na figura 5 que a janela contém o botão Back criado anteriormente. Mas este é o passo 1, portanto, não deve haver esse botão. Ele deve estar oculto e ser exibido apenas nas etapas 2 e 3. Adicionamos a seguinte linha ao método CreateGUI():
bool CProgram::CreateGUI(void) { //--- Step 1-3 if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols")) return(false); //--- Finish the creation of GUI CWndEvents::CompletedGUI(); m_back_button.Hide(); return(true); }
Além disso, nós precisamos monitorar a seleção do usuário. A mudança para a etapa 2 não deve ser permitida se o usuário não tiver selecionado pelo menos um símbolo. A alternância entre as etapas é realizada usando os botões Back e Next. Portanto, para resolver a tarefa, adicione três novos métodos à seção private da classe CProgram. Os métodos processarão as informações selecionadas durante cada uma das três etapas e, assim, executarão a configuração inicial do aplicativo. Além disso, adicionamos também a variável m_current_step: ao clicar em Back/Next, o aplicativo saberá em que etapa estamos atualmente.
private: //--- int m_symbol_total; int m_all_symbols; int m_current_step; //--- void ToStep_1(void); void ToStep_2(void); void ToStep_3(void);
Depois disso, no construtor da classe, definimos o valor do primeiro passo para a variável criada, ou seja, 1. Para definir a navegação entre as três etapas de configuração, adicionamos o código abaixo no evento de clique no botão no OnEvent():
//--- Navigation if(lparam==m_back_button.Id()) { //--- Return to Step 1 if(m_current_step==2) ToStep_1(); //--- Return to Step 2 else if(m_current_step==3) ToStep_2(); } //--- Go to Step 2 if(lparam==m_next_button.Id()) { //--- Go to Step 2 if(m_current_step==1) ToStep_2(); //--- Go to Step 3 else if(m_current_step==2) ToStep_3(); }
Se você tentar compilar o projeto nesta etapa, o compilador retornará um erro de que os três métodos foram criados, são usados, mas não têm implementação:
function 'CProgram::ToStep_1' must have a body Program.mqh 60 22
Para corrigi-lo, criamos a implementação dessas classes no arquivo Program.mqh. No entanto, vamos deixá-lo em branco por enquanto para os métodos ToStep_1() e ToStep_3(). Eles serão preenchidos mais tarde. Agora nós estamos interessados no método de alternar para o segundo passo ToStep_2(). Adicionamos uma verificação se pelo menos um símbolo está selecionado:
//+------------------------------------------------------------------+ //| Go to Step 1 | //+------------------------------------------------------------------+ void CProgram::ToStep_1(void) { //--- } //+------------------------------------------------------------------+ //| Go to Step 2 | //+------------------------------------------------------------------+ void CProgram::ToStep_2(void) { //--- Check whether at least one symbol is selected int cnt=0; for(int i=0; i<m_all_symbols; i++) { if(m_checkbox[i].IsPressed()) cnt++; } if(cnt<1) { MessageBox("No symbols selected!","Warning"); return; } } //+------------------------------------------------------------------+ //| Move to Step 3 3 | //+------------------------------------------------------------------+ void CProgram::ToStep_3(void) { //--- }
Se o usuário pressionar Next acidentalmente sem selecionar um símbolo, será exibido um aviso de que pelo menos um símbolo deve ser selecionado.
Etapa 2 da instalação: Período gráfico
Na segunda etapa de configuração do aplicativo, o usuário deve selecionar os períodos nos quais os sinais de negociação serão pesquisados. Nós mencionamos os elementos de interface do usuário necessários no primeiro artigo:
- Um grupo de botões para a seleção rápida de períodos gráficos.
- Lista de períodos gráficos na forma de caixas de seleção.
- O botão Back para retornar à Etapa 1.
Vamos usar os objetos existentes na implementação visual da Etapa 1 e ajustá-la para a seleção de períodos gráficos. Vamos para o corpo do ToStep_2() que nós editamos recentemente e adicionamos as funcionalidades adicionais a ele. Primeiramente, lembre-se de selecionar os símbolos na Etapa 1 e de exibí-los no Observador de Mercado na MetaTrader 5:
//--- Set selected symbols in Market Watch for(int i=0; i<m_all_symbols; i++) { if(m_checkbox[i].IsPressed()) SymbolSelect(m_checkbox[i].LabelText(),true); else SymbolSelect(m_checkbox[i].LabelText(),false); }
Em seguida, transformamos a interface da Etapa 1 na segunda:
//--- Change header m_step_window.LabelText("Signal Monitor Step 2: Choose Timeframes"); m_step_window.Update(true); //--- Hide elements of Step 1 for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsLocked(false); m_checkbox[i].IsPressed(false); m_checkbox[i].Hide(); } string names[3]= {"All","Junior","Senior"}; //--- Change names of selection buttons for(int i=0; i<3; i++) { m_currency_set[i].LabelText(names[i]); m_currency_set[i].IsPressed(false); if(m_current_step==3) m_currency_set[i].Show(); m_currency_set[i].Update(true); } //--- Hide block for working with templates m_text_edit.Hide(); m_load_button.Hide(); m_save_button.Hide(); //--- Show all timeframes string timeframe_names[21]= { "M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30", "H1","H2","H3","H4","H6","H8","H12","D1","W1","MN" }; for(int i=0; i<21; i++) { m_checkbox[i].LabelText(timeframe_names[i]); m_checkbox[i].Show(); m_checkbox[i].Update(true); } //--- Show Back button m_back_button.Show(); //--- m_current_step=2;
A implementação é bem simples. No final, atribuímos o valor 2 à variável m_current_step (Etapa 2). Agora, nós precisamos fornecer a exibição correta dos conjuntos do período gráfico selecionados All, Junior, Senior pela interface alterada. Abrimos o Program.mqh e alteramos o código no método OnEvent(). A modificação é necessária na seção do evento "button click". Do ponto de vista do objeto, os botões de seleção rápida nas etapas 1 e 2 são semelhantes. Por isso, é necessário definir a etapa de configuração atual no evento de clique no botão:
//--- Step 1 if(m_current_step==1) { ... } //--- Step 2 else if(m_current_step==2) { //--- All if(lparam==m_currency_set[0].Id() && m_currency_set[0].IsPressed()) { m_currency_set[1].IsPressed(false); m_currency_set[2].IsPressed(false); m_currency_set[1].Update(true); m_currency_set[2].Update(true); //--- for(int i=0; i<21; i++) { m_checkbox[i].IsPressed(true); m_checkbox[i].Update(true); } } //--- Junior Timeframes else if(lparam==m_currency_set[1].Id() && m_currency_set[1].IsPressed()) { m_currency_set[0].IsPressed(false); m_currency_set[2].IsPressed(false); m_currency_set[0].Update(true); m_currency_set[2].Update(true); //--- string pairs[11]= { "M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30" }; //--- Clear the selection for(int i=0; i<21; i++) { m_checkbox[i].IsPressed(false); m_checkbox[i].Update(true); } //--- for(int i=0; i<21; i++) { for(int j=0; j<11; j++) if(m_checkbox[i].LabelText()==pairs[j]) { m_checkbox[i].IsPressed(true); m_checkbox[i].Update(true); } } } //--- Senior Timeframes else if(lparam==m_currency_set[2].Id() && m_currency_set[2].IsPressed()) { m_currency_set[0].IsPressed(false); m_currency_set[1].IsPressed(false); m_currency_set[0].Update(true); m_currency_set[1].Update(true); //--- string pairs[10]= { "H1","H2","H3","H4","H6","H8","H12","D1","W1","MN" }; //--- Clear the selection for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsPressed(false); m_checkbox[i].Update(true); } //--- for(int i=0; i<m_all_symbols; i++) { for(int j=0; j<10; j++) if(m_checkbox[i].LabelText()==pairs[j]) { m_checkbox[i].IsPressed(true); m_checkbox[i].Update(true); } } }
O último elemento da interface do usuário a ser implementado na segunda etapa da configuração é o botão Back, que retorna à Etapa 1. Isso é feito pela etapa criada, mas ainda vazia ToStep_1(). Retornamos a interface anterior e configuramos o wrapper anterior para processar os eventos de clique no botão de seleção dos conjuntos.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProgram::ToStep_1(void) { //--- Change header m_step_window.LabelText("Signal Monitor Step 1: Choose Symbols"); m_step_window.Update(true); //--- Hide the Back button m_back_button.Hide(); //--- Clear selection for(int i=0; i<21; i++) { m_checkbox[i].IsPressed(false); m_checkbox[i].Update(true); } //--- Show elements of Step 1 for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].Show(); m_checkbox[i].LabelText(SymbolName(i,false)); m_checkbox[i].Update(true); } string names[3]= {"All","Majors","Crosses"}; //--- Change names of selection buttons for(int i=0; i<3; i++) { m_currency_set[i].IsPressed(false); m_currency_set[i].LabelText(names[i]); m_currency_set[i].Update(true); } //--- Show block for working with templates m_text_edit.Show(); m_load_button.Show(); m_save_button.Show(); //--- Set the current setup step m_current_step=1; }
Agora, compilamos o projeto. Se tudo for adicionado corretamente, o resultado será como na Fig.7.
Fig.7 Implementação da Etapa 2 da configuração do aplicativo.
Etapa 3 da instalação: Adição de sinais
A próxima etapa é a Etapa 3: Interface de adição de sinais. Ela é bem simples e consiste em um botão de adição de sinal e um cabeçalho para a lista de sinais adicionados. Abrimos o Program.mqh e declaramos duas novas variáveis na СPrograma:
CButton m_add_signal;
//---
CTextLabel m_signal_header;
Métodos que implementam as variáveis:
bool CreateIconButton(CButton &button,string text,const int x_gap,const int y_gap); //--- Text label bool CreateLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text);
Adicionamos sua implementação no final do arquivo StepWindow.mqh.
//+------------------------------------------------------------------+ //| Creates a button with an image | //+------------------------------------------------------------------+ #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\plus.bmp" bool CProgram::CreateIconButton(CButton &button,string text,const int x_gap,const int y_gap) { //--- color baseclr=C'70,180,70'; color pressed=C'70,170,70'; //--- Save the window pointer button.MainPointer(m_step_window); //--- Set properties before creation button.XSize(110); button.YSize(30); button.Font(m_base_font); button.FontSize(m_base_font_size); button.IconXGap(3); button.IconYGap(7); button.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\plus.bmp"); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(pressed); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(pressed); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); //--- Create a control if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add a pointer to element to the base CWndContainer::AddToElementsArray(0,button); return(true); } //+------------------------------------------------------------------+ //| Creates the text label | //+------------------------------------------------------------------+ bool CProgram::CreateLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text) { //--- Save the window pointer text_label.MainPointer(m_step_window); //--- text_label.Font(m_base_font); text_label.FontSize(m_base_font_size); text_label.XSize(120); text_label.BackColor(m_background); //--- Create the button if(!text_label.CreateTextLabel(label_text,x_gap,y_gap)) return(false); //--- Add a pointer to element to the base CWndContainer::AddToElementsArray(0,text_label); return(true); } //+------------------------------------------------------------------+
Adicionamos o seguinte à janela CreateStepWindow() para que sejam criados no lançamento do aplicativo.
//--- if(!CreateIconButton(m_add_signal,"Add Signal",10,30)) return(false); if(!CreateLabel(m_signal_header,10,30+30+10,"Signal List")) return(false);
Agora, para desativar sua exibição no lançamento, ou seja, na primeira etapa, imediatamente após a criação da interface pela chamada de CreateGUI(), adicionamos duas linhas ocultando os elementos no final do corpo do método.
//+------------------------------------------------------------------+ //| Creates the graphical interface of the program | //+------------------------------------------------------------------+ bool CProgram::CreateGUI(void) { //--- Step 1-3 if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols")) return(false); //--- Finish the creation of GUI CWndEvents::CompletedGUI(); m_back_button.Hide(); m_add_signal.Hide(); m_signal_header.Hide(); return(true); }
Agora, implementamos o método ToStep_3(), que foi adicionado anteriormente, e que limpará a visualização na etapa anterior e exibirá os elementos que criamos:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProgram::ToStep_3(void) { //--- Check whether at least one timeframe is selected int cnt=0; for(int i=0; i<21; i++) { if(m_checkbox[i].IsPressed()) cnt++; } if(cnt<1) { MessageBox("No timeframes selected!","Warning"); return; } //--- m_step_window.LabelText("Signal Monitor Step 3: Create Signals"); m_step_window.Update(true); m_next_button.LabelText("Create"); m_next_button.Update(true); //--- Hide elements of Step 2 for(int i=0; i<21; i++) { if(i<3) m_currency_set[i].Hide(); m_checkbox[i].Hide(); } //--- m_add_signal.Show(); m_signal_header.Show(); //--- m_current_step=3; }
Compilamos novamente o projeto e vamos para a Etapa 3 clicando duas vezes no botão Next. Não se esqueça de selecionar os elementos nas duas primeiras etapas, caso contrário, o aplicativo não nos permitirá ir para a terceira etapa.
Fig.8 Implementação da Etapa 3 da configuração do aplicativo.
Janela de criação e edição de sinais de negociação
O componente visual relacionado ao trabalho com os sinais de negociação estará localizado no arquivo SetWindow.mqh, então vamos abri-lo. Agora ele possui apenas o arquivo de inclusão Program.mqh que é conectado via linha de comando #include. Primeiro, criamos uma janela separada, que será básica para todos os outros elementos de criação e configuração. Abrimos o Program.mqh e declaramos na classe a variável m_set_window que é uma instância da classe CWindow. Adicionamos também o método CreateSetWindow() para criar a janela:
CWindow m_set_window; bool CreateSetWindow(const string caption_text);
Depois disso, voltamos para o SetWindow.mqh e implementamos o método criado.
//+------------------------------------------------------------------+ //| Creates a window for creating and editing trading signals | //+------------------------------------------------------------------+ bool CProgram::CreateSetWindow(const string text) { //--- Add the pointer to the window array CWndContainer::AddWindow(m_set_window); //--- Properties m_set_window.XSize(568); m_set_window.YSize(555); //--- Coordinates int x=int(ChartGetInteger(m_chart_id,CHART_WIDTH_IN_PIXELS)-m_set_window.XSize())/2; int y=30; //--- m_set_window.CaptionHeight(22); m_set_window.IsMovable(true); m_set_window.CaptionColor(m_caption); m_set_window.CaptionColorLocked(m_caption); m_set_window.CaptionColorHover(m_caption); m_set_window.BackColor(m_background); m_set_window.FontSize(m_base_font_size); m_set_window.Font(m_base_font); m_set_window.WindowType(W_DIALOG); //--- Creating a form if(!m_set_window.CreateWindow(m_chart_id,m_subwin,text,x,y)) return(false); return(true); } //+------------------------------------------------------------------+
Agora vamos vincular a janela recém-criada aos elementos já disponíveis. Primeiramente, adicionamos a chamada do método na criação de interface CreateGUI(). A janela deve abrir com um clique no botão "Add Signal" na etapa 3.
//+------------------------------------------------------------------+ //| Creates the graphical interface of the program | //+------------------------------------------------------------------+ bool CProgram::CreateGUI(void) { //--- Step 1-3 if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols")) return(false); //--- Creation and editing window if(!CreateSetWindow("Signal Monitor Edit Signal")) return(false); //--- Finish the creation of GUI CWndEvents::CompletedGUI(); m_back_button.Hide(); m_add_signal.Hide(); m_signal_header.Hide(); return(true); }
No evento de clique em OnEvent():
//--- Click on the "Add Signal" button if(lparam==m_add_signal.Id()) { m_set_window.OpenWindow(); }
Compilamos o projeto e verificamos o resultado: Ao clicar no botão "Add Signal" na etapa 3, uma janela adicional de criação e edição é aberta.
Fig. 9 Implementação da janela de criação e edição do sinal de negociação.
O primeiro elemento da janela é a seleção do tipo de indicador que será usado na geração de um sinal de negociação. O procedimento de adição do elemento é o mesmo: criamos uma instância de classe e criamos um método implementando a instância.
//--- Drop-down menu CComboBox m_indicator_type; //--- Creates a drop-down method bool CreateIndicatorType(const int x_gap,const int y_gap);
A implementação do método estará localizada no mesmo arquivo que se encontra a janela criada anteriormente.
//+------------------------------------------------------------------+ //| Creates a drop-down menu with indicator types | //+------------------------------------------------------------------+ bool CProgram::CreateIndicatorType(const int x_gap,const int y_gap) { //--- Pass the object to the panel m_indicator_type.MainPointer(m_set_window); //--- Array of the item values in the list view string pattern_names[7]= { "ATR","CCI","DeMarker","Force Ind","WPR","RSI","Momentum" }; //--- Set properties before creation m_indicator_type.XSize(200); m_indicator_type.YSize(26); m_indicator_type.LabelYGap(4); m_indicator_type.ItemsTotal(7); m_indicator_type.Font(m_base_font); m_indicator_type.FontSize(m_base_font_size); m_indicator_type.BackColor(m_background); m_indicator_type.GetButtonPointer().Font(m_base_font); m_indicator_type.GetButtonPointer().FontSize(m_base_font_size); m_indicator_type.GetButtonPointer().BackColor(clrWhite); m_indicator_type.GetButtonPointer().XGap(100); m_indicator_type.GetButtonPointer().XSize(100); m_indicator_type.GetListViewPointer().Font(m_base_font); m_indicator_type.GetListViewPointer().FontSize(m_base_font_size); m_indicator_type.GetListViewPointer().ItemYSize(26); //--- Store the item values in the combo box list view for(int i=0; i<7; i++) m_indicator_type.SetValue(i,pattern_names[i]); //--- Get the list pointer CListView *lv=m_indicator_type.GetListViewPointer(); //--- Set the list view properties lv.LightsHover(true); m_indicator_type.SelectItem(5); //--- Create a control if(!m_indicator_type.CreateComboBox("Indicator Type",x_gap,y_gap)) return(false); //--- Add an object to the common array of object groups CWndContainer::AddToElementsArray(1,m_indicator_type); return(true); }
A única adição aqui é que, no final do corpo do método CreateSetWindow(), nós chamamos um método para criar a opção de seleção do tipo de indicador CreateIndicatorType().
... //--- Creating a form if(!m_set_window.CreateWindow(m_chart_id,m_subwin,text,x,y)) return(false); //--- Indicator type if(!CreateIndicatorType(10,22+10)) return(false);
O resultado é o elemento da interface do usuário que permite selecionar entre 7 indicadores padrão do tipo oscilador.
Fig. 10 Elemento para selecionar o tipo de indicador.
A seguir, vamos considerar os conjuntos de elementos agrupados em duas seções: Configurações do Indicador (Indicator Settings) e Configurações do Sinal (Signal Settings). Todos os indicadores selecionados do conjunto padrão têm configurações comuns, como Período e Preço Aplicado. Portanto, é necessário o seguinte para a primeira seção: um rótulo de texto, um campo de entrada para o período e um menu suspenso para selecionar o preço usado para o cálculo do indicador. Adicionamos a variável necessária e seus métodos de criação na classe CProgram.
//--- Text label CTextLabel m_set_header[5]; //--- Input fields CTextEdit m_period_edit; //--- Drop-down menu CComboBox m_applied_price; ... bool CreateSetLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text); bool CreatePeriodEdit(const int x_gap,const int y_gap); bool CreateAppliedPrice(const int x_gap,const int y_gap);
Implementamos os métodos adicionados e chamamos eles no final do corpo do método CreateSetWindow(). Agora vamos adicionar um mecanismo devido ao qual os elementos criados mudarão o conjunto de configurações disponíveis, dependendo do tipo de indicador selecionado. Para fazer isso, adicionamos OnEvent() uma seção com um evento de clicar no item do menu suspenso e definir o conjunto individual de configurações para cada um dos indicadores:
//--- Item selection in the combobox list if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM) { int index=m_indicator_type.GetListViewPointer().SelectedItemIndex(); switch(index) { case 0: m_period_edit.LabelText("ATR Period"); m_applied_price.Hide(); break; case 1: m_period_edit.LabelText("CCI Period"); m_applied_price.Show(); break; case 2: m_period_edit.LabelText("DeMarker Period"); m_applied_price.Hide(); break; case 3: m_period_edit.LabelText("Force Index Period"); m_applied_price.Show(); break; case 4: m_period_edit.LabelText("WPR Period"); m_applied_price.Hide(); break; case 5: m_period_edit.LabelText("RSI Period"); m_applied_price.Show(); break; case 6: m_period_edit.LabelText("Momentum Period"); m_applied_price.Hide(); break; default: m_period_edit.LabelText("RSI Period"); m_applied_price.Hide(); break; } m_period_edit.Update(true); }
Compilamos o projeto e vemos o resultado:
Fig. 11 Implementação das configurações do indicador.
Em seguida, vamos para a segunda seção da edição de sinais. Ela é composta pelo cabeçalho e oito configurações:
- Regra do sinal.
- Valor do rótulo de texto no bloco de sinal.
- Cor da etiqueta de texto.
- Uso e cor do plano de fundo.
- Uso e cor da borda.
- Uso, cor e valor da dica de ferramenta acima do bloco de sinal.
- O uso de rótulos gráficos e sua aparência no bloco de sinal.
- Seleção dos períodos gráficos disponíveis para procurar um determinado sinal.
Para adicionar um cabeçalho para esta seção, adicionamos o seguinte código no final do corpo do CreateSetWindow() (nós criamos anteriormente um método para visualizar o cabeçalho, que pode ser usado novamente com diferentes valores de argumento):
//--- Signal settings if(!CreateSetLabel(m_set_header[1],10,22+10+4*(25+10),"2.Signal Settings")) return(false);
A regra do sinal consiste em dois elementos: um menu suspenso e um campo de entrada para um valor numérico. Adicionamos as instâncias de classe e os métodos de implementação à classe CProgram:
CTextEdit m_rule_value; CComboBox m_rule_type; ... bool CreateRuleValue(const int x_gap,const int y_gap); bool CreateRule(const int x_gap,const int y_gap);
Adicionamos a implementação deles ao SetWindow.mqh e chamamos eles no corpo do método CreateSetWindow().
//--- Condition settings if(!CreateRuleValue(130,22+10+5*(25+10))) return(false); if(!CreateRule(10,22+10+5*(25+10))) return(false);
Além disso, adicionamos cada uma das configurações da mesma maneira. Segue abaixo a implementação completa do método CreateSetWindow():
//+------------------------------------------------------------------+ //| Creates a window for creating and editing trading signals | //+------------------------------------------------------------------+ bool CProgram::CreateSetWindow(const string text) { //--- Add the pointer to the window array CWndContainer::AddWindow(m_set_window); //--- Properties m_set_window.XSize(568); m_set_window.YSize(575); //--- Coordinates int x=int(ChartGetInteger(m_chart_id,CHART_WIDTH_IN_PIXELS)-m_set_window.XSize())/2; int y=30; //--- m_set_window.CaptionHeight(22); m_set_window.IsMovable(true); m_set_window.CaptionColor(m_caption); m_set_window.CaptionColorLocked(m_caption); m_set_window.CaptionColorHover(m_caption); m_set_window.BackColor(m_background); m_set_window.FontSize(m_base_font_size); m_set_window.Font(m_base_font); m_set_window.WindowType(W_DIALOG); //--- Creating a form if(!m_set_window.CreateWindow(m_chart_id,m_subwin,text,x,y)) return(false); //--- Indicator type if(!CreateIndicatorType(10,22+10)) return(false); //--- Settings of the selected indicator if(!CreateSetLabel(m_set_header[0],10,22+10+26+10,"1.Indicator Settings")) return(false); if(!CreatePeriodEdit(10,22+10+2*(25+10))) return(false); if(!CreateAppliedPrice(10,22+10+3*(25+10))) return(false); //--- Signal settings if(!CreateSetLabel(m_set_header[1],10,22+10+4*(25+10),"2.Signal Settings")) return(false); //--- Condition settings if(!CreateRuleValue(130,22+10+5*(25+10))) return(false); if(!CreateRule(10,22+10+5*(25+10))) return(false); //--- Label display settings if(!CreateSetLabel(m_set_header[2],10,22+10+6*(25+10),"Label")) return(false); if(!CreateButton2(m_label_button[0],"Value",100,22+7+6*(25+10))) return(false); if(!CreateButton2(m_label_button[1],"Text",100+80,22+7+6*(25+10))) return(false); //--- Label color display settings if(!CreateColorButton(m_color_button[0],10,22+10+7*(25+10),"Label Color")) return(false); if(!CreateTextBox(180+80+10,22+7+6*(25+10))) return(false); //--- if(!CreateColorButton(m_color_button[1],25,22+10+8*(25+10),"")) return(false); if(!CreateSetCheckBox(m_set_param[0],10,22+10+8*(25+10),"Use Background")) return(false); if(!CreateColorButton(m_color_button[2],25,22+10+9*(25+10),"")) return(false); if(!CreateSetCheckBox(m_set_param[1],10,22+10+9*(25+10),"Use Border")) return(false); if(!CreateColorButton(m_color_button[3],25,22+10+10*(25+10),"")) return(false); if(!CreateSetCheckBox(m_set_param[2],10,22+10+10*(25+10),"Use Tooltip")) return(false); if(!CreateTooltipText(240,22+10+10*(25+10))) return(false); if(!CreateSetCheckBox(m_set_param[3],10,22+10+11*(25+10),"Use Image")) return(false); if(!CreateImageSlider(125,22+10+11*(25+10))) return(false); //--- Timeframe selection if(!CreateSetLabel(m_set_header[4],10,22+10+12*(25+10),"Timeframes")) return(false); //--- y=22+10+13*(25+10); int k=0; for(int i=0; i<21; i++) { if(i==11) { y=22+20+14*(25+10); k=0; } if(!CreateTfButton(m_tf_button[i],40*k+10,y)) return(false); k++; } return(true); }
A lista completa de adições e sua implementação estão disponíveis nos anexos abaixo. Depois de adicionar todas as partes necessárias, a janela de criação e edição fica assim:
Fig. 12 Implementação dos elementos da interface do usuário da janela de edição do sinal.
Como você pode ver na figura, os botões de seleção do período gráfico estão vazios. Nós também precisamos configurar as interações básicas dos elementos:
- Os botões do período gráfico devem mostrar apenas o número selecionado na Etapa 2.
- Quando o botão Value é selecionado, o botão Text deve se soltar e o campo de entrada do rótulo de texto deve estar oculto.
- Ao clicar no botão de seleção de cores deve abrir uma janela com a paleta de cores.
- Quando desmarcada, a seleção da paleta, o campo de entrada da dica de ferramenta e a seleção do rótulo gráfico devem ficar inativos.
Para implementar a saída dos períodos gráficos selecionados, vamos criar o método RebulidTimeframes() na seção privada da classe básica e implementar este método:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProgram::RebuildTimeframes(void) { //--- Count the number of selected timeframes int cnt=0; for(int i=0; i<21; i++) { if(m_checkbox[i].IsPressed()) cnt++; } ArrayResize(m_timeframes,cnt); cnt=0; //--- Remember the selected timeframe to the array for(int i=0; i<21; i++) { if(m_checkbox[i].IsPressed()) { m_timeframes[cnt]=m_checkbox[i].LabelText(); cnt++; } } //--- for(int i=0; i<cnt; i++) m_tf_button[i].IsLocked(false); //--- for(int i=0; i<cnt; i++) { m_tf_button[i].LabelText(m_timeframes[i]); m_tf_button[i].Update(true); } //--- for(int i=cnt; i<21; i++) m_tf_button[i].IsLocked(true); }
Agora vamos adicionar o seguinte ao código que chama a janela de edição com um clique em Add Signal.
//--- Click on the "Add Signal" button if(lparam==m_add_signal.Id()) { m_set_window.OpenWindow(); if(m_set_window.IsAvailable()) RebuildTimeframes(); }
Vamos para o próximo momento relacionado à configuração da interação dos botões Value e Text. Adicionamos o seguinte código na OnEvent():
//--- if(lparam==m_label_button[0].Id()) { if(m_label_button[0].IsPressed()) { m_label_button[1].IsPressed(false); m_label_button[1].Update(true); } m_text_box.Hide(); } if(lparam==m_label_button[1].Id()) { if(m_label_button[1].IsPressed()) { m_label_button[0].IsPressed(false); m_label_button[0].Update(true); } m_text_box.Show(); }
A seguinte condição é atendida aqui: se um dos botões for pressionado, o outro deverá ser pressionado. Se o Text é solto, ocultamos o campo de edição. Os cliques no botão da paleta de cores também são implementados aqui. Nós temos quatro botões, um array de quatro elementos foi declarado, portanto, o acesso a eles pode ser escrito em um loop.
//--- for(int i=0; i<4; i++) { if(lparam==m_color_button[i].Id()) { m_color_picker.ColorButtonPointer(m_color_button[i]); return; } }
E a última interação é bloquear os elementos quando as caixas de seleção não são pressionadas. Para isso, temos que adicionar o monitoramento de cliques da caixa de seleção na OnEvent() e implementar as interações.
//--- Click on the checkbox if(id==CHARTEVENT_CUSTOM+ON_CLICK_CHECKBOX) { //--- for(int i=0; i<3; i++) { if(lparam==m_set_param[i].Id()) { m_color_button[i+1].IsLocked(!m_set_param[i].IsPressed()); if(m_set_param[2].IsPressed()) m_tooltip_text.Show(); else m_tooltip_text.Hide(); } } //--- if(lparam==m_set_param[3].Id()) m_pictures_slider.IsLocked(!m_set_param[3].IsPressed()); }
Compilamos o projeto novamente e visualizamos o seu resultado.
Fig. 13 Implementações da interação dos elementos de interface do usuário da janela de edição do sinal.
Monitoramento dos sinais de negociação
O último passo nesta fase de desenvolvimento é criar uma janela do futuro monitor de sinais de negociação. Nós também devemos levar em consideração as configurações básicas que já estão implementadas na versão atual. Antes de sua criação, vamos definir algumas tarefas para que o leitor entenda para que finalidade os elementos são criados:
- Criar as linhas com rótulos de texto dos sinais selecionados na primeira etapa.
- Criar as colunas de cabeçalho com os rótulos de texto dos períodos gráficos selecionados na segunda etapa.
- Redimensionar a janela de acordo com as linhas e as colunas dos elementos criados. Uma forma de dimensionamento automático.
Para permitir a criação dos rótulos de texto de períodos gráficos e símbolos, nós criamos dois arrays das instâncias de classe CTextLabel e adicione dois métodos de implementação na classe CProgram.
CTextLabel m_timeframe_label[]; CTextLabel m_symbol_label[]; bool CreateTimeframeLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text); bool CreateSymbolLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text);
Agora, implementamos os métodos criados no arquivo MainWindow.mqh:
//+------------------------------------------------------------------+ //| Creates the text label | //+------------------------------------------------------------------+ bool CProgram::CreateTimeframeLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text) { //--- Save the window pointer text_label.MainPointer(m_step_window); //--- text_label.Font(m_base_font); text_label.FontSize(m_base_font_size); text_label.XSize(40); text_label.BackColor(m_background); //--- Create the button if(!text_label.CreateTextLabel(label_text,x_gap,y_gap)) return(false); //--- Add a pointer to element to the base CWndContainer::AddToElementsArray(0,text_label); return(true); } //+------------------------------------------------------------------+ //| Creates the text label | //+------------------------------------------------------------------+ bool CProgram::CreateSymbolLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text) { //--- Save the window pointer text_label.MainPointer(m_step_window); //--- text_label.Font(m_base_font); text_label.FontSize(m_base_font_size); text_label.XSize(100); text_label.BackColor(m_background); //--- Create the button if(!text_label.CreateTextLabel(label_text,x_gap,y_gap)) return(false); //--- Add a pointer to element to the base CWndContainer::AddToElementsArray(0,text_label); return(true); }
Antes de prosseguir com a visualização da interface da janela, nós precisamos criar duas variáveis importantes na seção private, bem como mais dois métodos:
int m_total_signals; string m_symbols[]; void ToMonitor(void); void AutoResize(const int x_size,const int y_size);
A variável m_total_signals é necessária para verificar se pelo menos um sinal de negociação foi criado. Essa verificação é realizada antes de criar a janela do monitor. O array m_symbols[] conterá uma seleção de símbolos da primeira etapa da configuração. O método ToMonitor() implementará a criação da interface do monitor, enquanto o AutoResize() ajustará a janela de tamanho de acordo com os elementos criados. Aqui está a implementação dos métodos declarados:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProgram::ToMonitor(void) { //--- Check if there is at least one signal if(m_total_signals<1) { MessageBox("No signals created!","Warning"); return; } //--- Hide Step 3 m_add_signal.Hide(); m_signal_header.Hide(); m_back_button.Hide(); m_next_button.Hide(); //--- Change window header m_step_window.LabelText("Signal Monitor"); m_step_window.Update(true); //--- Symbols int sy=ArraySize(m_symbols); ArrayResize(m_symbol_label,sy); for(int i=0; i<sy; i++) { if(!CreateSymbolLabel(m_symbol_label[i],5,m_step_window.CaptionHeight()+25+i*25,m_symbols[i])) return; m_symbol_label[i].Update(true); } //--- Timeframes int tf=ArraySize(m_timeframes); ArrayResize(m_timeframe_label,tf); //--- for(int i=0; i<tf; i++) { if(!CreateTimeframeLabel(m_timeframe_label[i],110+50*i,m_step_window.CaptionHeight()+3,m_timeframes[i])) return; m_timeframe_label[i].Update(true); } //--- Resize window AutoResize(m_timeframe_label[tf-1].XGap()+m_timeframe_label[tf-1].XSize()+5,m_symbol_label[sy-1].YGap()+m_symbol_label[sy-1].YSize()+5); }
Como pode ser visto no código acima, os dados de m_symbols são usados na seção Símbolos. Mas esses dados não são coletados ou preparados. Vamos corrigir isso. Vá para o método ToStep_2() e depois de verificar se pelo menos um símbolo foi selecionado, lembre-se dos símbolos selecionados na primeira etapa em nosso array:
//--- Count the number of selected symbols ArrayResize(m_symbols,cnt); cnt=0; //--- Remember the selected timeframe to the array for(int i=0; i<m_all_symbols; i++) { if(m_checkbox[i].IsPressed()) { m_symbols[cnt]=m_checkbox[i].LabelText(); cnt++; } }
Agora, crie o método de dimensionamento automático.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProgram::AutoResize(const int x_size,const int y_size) { m_step_window.ChangeWindowWidth(x_size); m_step_window.ChangeWindowHeight(y_size); }
Antes de verificar o projeto, definimos a variável m_total_signals para zero no construtor da CProgram. Outro ponto importante é a adição no método OnEvent(), no evento de clique no botão.
//--- Navigation if(lparam==m_back_button.Id()) { //--- Go back if(m_current_step==2) ToStep_1(); //--- Return to Step 2 else if(m_current_step==3) ToStep_2(); } //--- Go forward if(lparam==m_next_button.Id()) { //--- Go to Step 2 if(m_current_step==1) ToStep_2(); //--- Go to Step 3 else if(m_current_step==2) ToStep_3(); //--- Go to Monitor else if(m_current_step==3) ToMonitor(); }
Aqui, nós adicionamos a chamada do método criado ToMonitor() ao clicar no botão que pula para o próxima etapa. Este botão é chamado de "Create" na etapa 3. Agora, compilamos o projeto e iniciamos o aplicativo:
- Na primeira etapa, selecionamos Crosses.
- Na segunda etapa, selecionamos Senior.
- Na terceira etapa, clicamos em Add Signal.
- Depois disso, fechamos a janela de criação do sinal e clicamos em Create.
Fig. 14 Configuração básica do monitor
No próximo artigo, nós consideraremos a implementação de um algoritmo que buscará os sinais de negociação configurados nas condições criadas durante o lançamento inicial.
Conclusão
O arquivo anexado contém todos os arquivos listados, localizados nas pastas apropriadas. Para o seu bom funcionamento, você só precisa salvar a pasta MQL5 na pasta do terminal. Para abrir o diretório raiz da plataforma, no qual a pasta MQL5 está localizada, pressione a combinação de teclas Ctrl + Shift + D na plataforma MetaTrader 5 ou use o menu de contexto como é mostrado abaixo na Fig. 15.
Fig. 15. Abrindo a pasta MQL5 no diretório raiz da plataforma MetaTrader 5
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/7528
- 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