Monitoramento de sinais de negociação multimoeda (Parte 3): Introdução de algoritmos de busca
Conteúdo
- Introdução
- Sistema de armazenamento do conjunto de símbolos
- Adição e edição de um sinal de negociação
- Algoritmos de busca de sinais de negociação
- Conclusão
Introdução
Nos artigos anteriores, nós criamos a estrutura do aplicativo para o monitoramento de sinais de negociação. Nós também implementamos a interface do aplicativo com os recursos básicos de interação. Agora é hora de preencher a parte visual do algoritmo para configurar e buscar os símbolos. Nós vamos usar o projeto do artigo anterior como base e continuará adicionando novas funcionalidades a cada passo.
Sistema de armazenamento do conjunto de símbolos
Na última vez, na primeira etapa de configuração do aplicativo, nós criamos as ferramentas para selecionar os símbolos na Observação de Mercado. Os símbolos podem ser selecionados de três maneiras diferentes:
- Manual. Tendo que marcar os símbolos necessários e clicar em Next.
- Um conjunto predefinido. Um clique em All, Major ou Crosses selecionará automaticamente um conjunto de símbolos predefinido específico.
- Conjunto salvo. Um conjunto de símbolos previamente preparado, configurado usando os dois primeiros métodos e salvo em um arquivo com um nome específico.
Fig.1 Etapa 1 de configuração do aplicativo e configurações do conjunto salvo.
As duas primeiras maneiras são bastante simples e foram criadas anteriormente. A terceira maneira precisa ser criada. Agora vamos definir com mais precisão o que vamos fazer. Nós iremos nos aprofundar na interação dos elementos no quadro vermelho na Fig.1, o que implica o seguinte:
- Os símbolos desejados são marcados com as marcas de seleção, o usuário digita o nome no campo 'Template name' e clica em Save ou pressiona a tecla de atalho S. Se salvo com sucesso, uma mensagem apropriada será exibida.
- Para acessar um modelo configurado e salvo anteriormente, o nome do modelo deve ser digitado no campo e carregado ou, de modo alternativo, pressionar a tecla de atalho L.
Abrimos o projeto, encontramos a classe base CProgram e adicionamos dois métodos em sua seção private. Os métodos serão responsáveis por carregar e salvar o modelo de símbolos.
bool SaveSymbolSet(string file_name); bool LoadSymbolSet(string file_name);
Abaixo encontramos como esses métodos são implementados.
//+------------------------------------------------------------------+ //| Save template to a file | //+------------------------------------------------------------------+ bool CProgram::SaveSymbolSet(string file_name) { if(file_name=="") { MessageBox("Select the template name to record","Signal Monitor"); return(false); } int h=FileOpen("Signal Monitor\\"+file_name+".bin",FILE_WRITE|FILE_BIN); if(h==INVALID_HANDLE) { MessageBox("Failed to create a configuration file","Signal Monitor"); return(false); } else MessageBox("The "+file_name+" configuration has been successfully saved","Signal Monitor"); //--- Save the selection of timeframes and patterns for(int i=0; i<m_all_symbols; i++) m_save.tf[i]=m_checkbox[i].IsPressed(); //--- FileWriteStruct(h,m_save); FileClose(h); //--- return(true); } //+------------------------------------------------------------------+ //| Load data to a panel | //+------------------------------------------------------------------+ bool CProgram::LoadSymbolSet(string file_name) { if(file_name=="") { MessageBox("Select the template name to load","Signal Monitor"); return(false); } int h=FileOpen("Signal Monitor\\"+file_name+".bin",FILE_READ|FILE_BIN); if(h==INVALID_HANDLE) { MessageBox("Configuration "+file_name+" not found","Signal Monitor"); return(false); } ZeroMemory(m_save); FileReadStruct(h,m_save); //--- Loading timeframes for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsPressed(m_save.tf[i]); m_checkbox[i].Update(true); } //--- FileClose(h); //--- return(true); }
No entanto, se você tentar compilar o projeto agora, isso disparará um erro conectado à variável m_save. Essa estrutura possui um parâmetro do tipo bool chamado tf. Ela lembra a escolha do usuário em um arquivo. Portanto, nós criamos essa estrutura na classe do aplicativo e adicionamos a sua instância à classe base.
//+------------------------------------------------------------------+ //| Class for creating the application | //+------------------------------------------------------------------+ struct SAVE { bool tf[100]; }; class CProgram : public CWndEvents { ... SAVE m_save;
Vamos para o método da OnEvent(), inserimos a seção relacionada ao evento de clique no botão e adicionamos o seguinte código na condição de Etapa 1:
//--- Save the template if(lparam==m_save_button.Id()) { SaveSymbolSet(m_text_edit.GetValue()); } //--- Load the template if(lparam==m_load_button.Id()) { LoadSymbolSet(m_text_edit.GetValue()); }
Além disso, nós implementamos o uso das teclas de atalho para os botões acima. No mesmo método, nós adicionamos uma verificação para o evento de pressionamento de tecla e adicionamos o código para as teclas usadas.
//--- Key press if(id==CHARTEVENT_KEYDOWN) { if(m_current_step==1) { short sym=TranslateKey((int)lparam); //--- if the entered character is successfully converted to Unicode if(sym>0) { if(ShortToString(sym)=="l" || ShortToString(sym)=="д") LoadSymbolSet(m_text_edit.GetValue()); if(ShortToString(sym)=="s" || ShortToString(sym)=="ы") SaveSymbolSet(m_text_edit.GetValue()); } } }
Compilamos o projeto. A compilação bem sucedida produzirá o seguinte resultado.
Fig.2 Salvando e carregando um modelo de usuário
Adição e edição de um sinal de negociação
Agora, vamos para a parte principal do aplicativo, responsável pela criação e edição dos sinais de negociação, bem como pelo monitoramento adicional no monitor. Abaixo está como é a criação e edição do sinal.
Fig.3 Janela de criação e edição do sinal.
No estágio atual, a janela aparece como um conjunto de elementos da GUI que controlam vários parâmetros. No entanto, essas configurações não são usadas em nenhum lugar. Começamos com a adição de dois botões na interface. Eles adicionam/salvam um sinal de negociação. Outro botão cancela a criação/edição. Abrimos o arquivo Program.mqh e adicionamos o método de implementação para esses dois botões à classe base:
bool CreateButton3(CButton &button,string text,const int x_gap,const int y_gap);
Duas instâncias de botão CButton:
CButton m_new_signal; CButton m_cancel_button;
Agora vamos para o arquivo SetWindow.mqh e implementamos esse método.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateButton3(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_set_window); //--- Set up properties before creation button.XSize(60); 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 the control if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add the element pointer to the base CWndContainer::AddToElementsArray(1,button); return(true); }
Para fazer esses dois botões aparecerem na interface da janela de negociação e adicionando um sinal, nós inserimos as seguintes linhas no final do corpo do método CreateSetWindow():
//--- Add/Cancel Buttons if(!CreateButton3(m_new_signal,"Add",m_set_window.XSize()-2*(60+10),m_set_window.YSize()-(30+10))) return(false); if(!CreateButton3(m_cancel_button,"Cancel",m_set_window.XSize()-(60+10),m_set_window.YSize()-(30+10))) return(false);
Após a compilação do projeto, dois botões aparecerão na parte inferior da janela de criação do sinal de negociação.
Fig.4 Adicionando os botões de criação e cancelamento de sinal.
Agora, nós precisamos adicionar os eventos que ocorrerão com o clique do botão. O efeito do botão Cancel é óbvio: ele não salva nenhuma ação e configuração na janela especificada e fecha a janela sem adicionar um sinal. Vamos considerar o botão Add com mais detalhes.
Primeiro, vamos determinar a sequência de ações a serem executadas quando clicarmos em Add.
- Um clique no botão salva os parâmetros selecionados usando os elementos da interface do usuário na janela de criação do sinal de negociação.
- Após salvá-lo com êxito, a janela é fechada e o primeiro registro com o nome do sinal aparece na lista de sinais na janela principal.
- Ao clicar no registro, o conjunto salvo anteriormente é aplicado aos elementos da interface do usuário da configuração do sinal e o botão Add é convertido em Save.
Para habilitar o salvamento das configurações em um arquivo, nós precisamos criar um conjunto universal de configurações que serão usadas para a exibição visual na janela de edição, bem como para a busca subsequente de sinais. Portanto, vamos criar uma estrutura e chamá-la de SIGNAL. A configuração das configurações na janela de criação e edição será gravada nessa estrutura.
struct SIGNAL { int ind_type; int ind_period; int app_price; int rule_type; double rule_value; int label_type; uchar label_value[10]; color label_color; color back_color; color border_color; bool tooltip; uchar tooltip_text[100]; bool image; int img_index; bool timeframes[21]; TFNAME tf_name[21]; };
Vamos ver cada um dos elementos na estrutura:
- ind_type — contém o tipo do indicador que foi selecionado como base de busca do sinal. Ele é exibido como Indicator Type na interface.
- ind_period — o período do indicador selecionado.
- app_price — preço usado para o cálculo do indicador. Este valor não está disponível para todos os indicadores e, portanto, ele é gravado apenas quando aplicável. Por exemplo, ele é usado para o RSI, mas não para o WPR.
- rule_type — define o tipo de regra a ser usada ao buscar um sinal de negociação. Ele aparece na interface como um menu suspenso contendo os caracteres como ==,>=,<= e outros.
- rule_value — um valor do limiar do indicador selecionado na qual a regra de busca deve ser aplicada.
- label_type — este elemento armazenará o tipo de exibição do rótulo de texto. Ele é o valor atual do indicador ou um rótulo personalizado com até 3 caracteres.
- label_value — se o segundo tipo de exibição do rótulo de texto for selecionado, esse parâmetro armazenará o texto do rótulo personalizado especificado pelo usuário.
- label_color — armazena a cor do rótulo do texto.
- back_color — armazena a cor de fundo do bloco de sinal no monitor se esta opção estiver selecionada.
- border_color — armazena a cor da borda do bloco de sinal no monitor se esta opção estiver selecionada.
- tooltip — contém uma indicação se uma dica de ferramenta é usada.
- tooltip_text — se a dica de ferramenta for usada, esse parâmetro conterá o texto.
- image — uma indicação do uso da imagem.
- img_index — salva o número da sequência da imagem, se usada.
- timeframes — matriz contendo informações sobre as configurações do período selecionado na segunda etapa.
- tf_name — salva os períodos nos quais o sinal de negociação será pesquisado.
Agora, declaramos na classe base uma matriz de estruturas para salvar as configurações dos sinais criados.
SIGNAL m_signal_set[5];
Além disso, nós criamos dois métodos na área private da CProgram para salvar um conjunto de parâmetros em um arquivo e carregá-lo do arquivo na estrutura.
bool SaveSignalSet(int index); bool LoadSignalSet(int index);
Aqui está a implementação:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::SaveSignalSet(int index) { //--- int h=FileOpen("Signal Monitor\\signal_"+string(index)+".bin",FILE_WRITE|FILE_BIN); if(h==INVALID_HANDLE) { MessageBox("Failed to create a configuration file","Signal Monitor"); return(false); } //--- Save the selection //--- Indicator type m_signal_set[index].ind_type=m_indicator_type.GetListViewPointer().SelectedItemIndex(); //--- Indicator period m_signal_set[index].ind_period=(int)m_period_edit.GetValue(); //--- Type of applied price m_signal_set[index].app_price=m_applied_price.GetListViewPointer().SelectedItemIndex(); //--- Rule type m_signal_set[index].rule_type=m_rule_type.GetListViewPointer().SelectedItemIndex(); //--- Rule value m_signal_set[index].rule_value=(double)m_rule_value.GetValue(); //--- Text label display type m_signal_set[index].label_type=m_label_button[0].IsPressed()?0:1; //--- Save the value of the text field for the second type if(m_label_button[1].IsPressed()) StringToCharArray(StringSubstr(m_text_box.GetValue(),0,3),m_signal_set[index].label_value); //--- Color of the text label m_signal_set[index].label_color=m_color_button[0].CurrentColor(); //--- Background color if(m_set_param[0].IsPressed()) m_signal_set[index].back_color=m_color_button[1].CurrentColor(); else m_signal_set[index].back_color=clrNONE; //--- Border color if(m_set_param[1].IsPressed()) m_signal_set[index].border_color=m_color_button[2].CurrentColor(); else m_signal_set[index].border_color=clrNONE; //--- Tooltip value m_signal_set[index].tooltip=m_set_param[2].IsPressed(); if(m_signal_set[index].tooltip) StringToCharArray(m_tooltip_text.GetValue(),m_signal_set[index].tooltip_text); //--- Selected image m_signal_set[index].image=m_set_param[3].IsPressed(); if(m_signal_set[index].image) m_signal_set[index].img_index=m_pictures_slider.GetRadioButtonsPointer().SelectedButtonIndex(); //--- Selected timegrames int tf=0; for(int i=0; i<21; i++) { if(!m_tf_button[i].IsLocked() && m_tf_button[i].IsPressed()) { m_signal_set[index].timeframes[i]=true; StringToCharArray(m_tf_button[i].LabelText(),m_signal_set[index].tf_name[i].tf); tf++; } else m_signal_set[index].timeframes[i]=false; } //--- if(tf<1) { MessageBox("No timeframes selected","Signal Monitor"); FileClose(h); return(false); } //--- FileWriteStruct(h,m_signal_set[index]); FileClose(h); Print("Configuration signal_"+string(index)+" has been successfully saved"); //--- return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::LoadSignalSet(int index) { int h=FileOpen("Signal Monitor\\signal_"+string(index)+".bin",FILE_READ|FILE_BIN); if(h==INVALID_HANDLE) { MessageBox("Configuration not found","Signal Monitor"); return(false); } ZeroMemory(m_signal_set[index]); FileReadStruct(h,m_signal_set[index]); //--- Loading indicator type m_indicator_type.SelectItem(m_signal_set[index].ind_type); RebuildParameters(m_signal_set[index].ind_type); m_indicator_type.GetButtonPointer().Update(true); //--- Loading indicator period m_period_edit.SetValue((string)m_signal_set[index].ind_period); m_period_edit.GetTextBoxPointer().Update(true); //--- Loading applied price if(!m_applied_price.IsLocked()) { m_applied_price.SelectItem(m_signal_set[index].app_price); m_applied_price.GetButtonPointer().Update(true); } //--- Loading signal rule m_rule_type.SelectItem(m_signal_set[index].rule_type); m_rule_type.GetButtonPointer().Update(true); m_rule_value.SetValue((string)m_signal_set[index].rule_value); m_rule_value.GetTextBoxPointer().Update(true); //--- Loading a text label if(m_signal_set[index].label_type==0) { m_label_button[0].IsPressed(true); m_label_button[0].Update(true); m_label_button[1].IsPressed(false); m_label_button[1].Update(true); m_text_box.IsLocked(true); } else { m_label_button[0].IsPressed(false); m_label_button[0].Update(true); m_label_button[1].IsPressed(true); m_label_button[1].Update(true); m_text_box.IsLocked(false); m_text_box.ClearTextBox(); m_text_box.AddText(0,CharArrayToString(m_signal_set[index].label_value)); m_text_box.Update(true); } //--- Loading the color of the text label m_color_button[0].CurrentColor(m_signal_set[index].label_color); m_color_button[0].Update(true); //--- Loading the background color if(m_signal_set[index].back_color==clrNONE) { m_set_param[0].IsPressed(false); m_set_param[0].Update(true); m_color_button[1].IsLocked(true); m_color_button[1].GetButtonPointer().Update(true); } else { m_set_param[0].IsPressed(true); m_set_param[0].Update(true); m_color_button[1].IsLocked(false); m_color_button[1].CurrentColor(m_signal_set[index].back_color); m_color_button[1].GetButtonPointer().Update(true); } //--- Loading the border color if(m_signal_set[index].border_color==clrNONE) { m_set_param[1].IsPressed(false); m_set_param[1].Update(true); m_color_button[2].IsLocked(true); m_color_button[2].GetButtonPointer().Update(true); } else { m_set_param[1].IsPressed(true); m_set_param[1].Update(true); m_color_button[2].IsLocked(false); m_color_button[2].CurrentColor(m_signal_set[index].border_color); m_color_button[2].GetButtonPointer().Update(true); } //--- Loading the tooltip value if(!m_signal_set[index].tooltip) { m_set_param[2].IsPressed(false); m_set_param[2].Update(true); m_tooltip_text.IsLocked(true); m_tooltip_text.Update(true); } else { m_set_param[2].IsPressed(true); m_set_param[2].Update(true); m_tooltip_text.IsLocked(false); m_tooltip_text.ClearTextBox(); m_tooltip_text.AddText(0,CharArrayToString(m_signal_set[index].tooltip_text)); m_tooltip_text.Update(true); } //--- Loading the image if(!m_signal_set[index].image) { m_set_param[3].IsPressed(false); m_set_param[3].Update(true); m_pictures_slider.IsLocked(true); m_pictures_slider.GetRadioButtonsPointer().Update(true); } else { m_set_param[3].IsPressed(true); m_set_param[3].Update(true); m_pictures_slider.IsLocked(false); m_pictures_slider.GetRadioButtonsPointer().SelectButton(m_signal_set[index].img_index); m_pictures_slider.GetRadioButtonsPointer().Update(true); } //--- Loading selected timeframes for(int i=0; i<21; i++) { if(!m_tf_button[i].IsLocked()) { m_tf_button[i].IsPressed(m_signal_set[index].timeframes[i]); m_tf_button[i].Update(true); } } //--- FileClose(h); return(true); }
Assim, a primeira ação com o algoritmo de salvar/carregar é concluída. Agora, nós precisamos criar o objeto que servirá como registros dos sinais criados. Ao clicar nesses objetos, nós poderemos editar os parâmetros do sinal de negociação criado anteriormente. Para implementar esses objetos, nós criamos uma matriz das instâncias de classe CButton.
CButton m_signal_editor[5];
Além disso, adicionamos um método criando os objetos.
bool CreateSignalEditor(CButton &button,string text,const int x_gap,const int y_gap);
Implementamos esse método no arquivo StepWindow.mqh, pois esses objetos pertencem à janela principal.
//+------------------------------------------------------------------+ //| Creates a button with an image | //+------------------------------------------------------------------+ #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\settings_light.bmp" bool CProgram::CreateSignalEditor(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 up 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\\settings_light.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 the control if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add the element pointer to the base CWndContainer::AddToElementsArray(0,button); return(true); }
Usando esse método, nós adicionamos cinco objetos ao corpo do CreateStepWindow(), que serão os objetos na lista de sinais.
//--- for(int i=0; i<5; i++) { if(!CreateSignalEditor(m_signal_editor[i],"Signal_"+string(i),10,40*i+90)) return(false); }
Para desativar a exibição desses elementos após o lançamento do aplicativo, esconda-o no método CreateGUI().
//+------------------------------------------------------------------+ //| Creates the graphical interface of the program | //+------------------------------------------------------------------+ bool CProgram::CreateGUI(void) { //--- Step 1-3. Symbol selection window. if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols")) return(false); //--- if(!CreateSetWindow("Signal Monitor Edit Signal")) return(false); //--- Creating form 2 for the color picker if(!CreateColorWindow("Color Picker")) return(false); //--- Finishing the creation of GUI CWndEvents::CompletedGUI(); m_back_button.Hide(); m_add_signal.Hide(); m_signal_header.Hide(); m_label_button[1].IsPressed(true); m_label_button[1].Update(true); for(int i=0; i<5; i++) m_signal_editor[i].Hide(); return(true); }
A próxima coisa a fazer antes da compilação do projeto é criar um método que excluirá todos os dados salvos anteriormente durante a configuração inicial. Para fazer isso, criamos o método ClearSaves() e chamamos ele no construtor da classe CProgram.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::ClearSaves(void) { for(int i=0; i<5; i++) FileDelete("Signal Monitor\\signal_"+string(i)+".bin"); m_total_signals=0; return(true); }
Agora, adicionamos o seguinte ao evento de clique no botão de sinal Add:
//--- Add Signal button click event if(lparam==m_add_signal.Id()) { m_set_window.OpenWindow(); m_number_signal=-1; RebuildTimeframes(); m_new_signal.LabelText("Add"); m_new_signal.Update(true); }
O mecanismo para adicionar um novo sinal de negociação está pronto após a compilação do projeto. O próximo passo é a implementação da edição de um sinal criado anteriormente.
Fig.5 Adicionando um novo sinal de negociação.
Vamos resumir tudo. Como é mostrado na figura 5, nós implementamos a possibilidade de adicionar os sinais com um clique no botão Add Signal. Além disso, um botão com o novo nome do sinal é adicionado a Signal List. Atualmente, ele é um valor predefinido sem a possibilidade de editá-lo. No entanto, nada acontece com o clique em Signal_0, então vamos corrigir isso. Vamos ativar a reabertura da janela de configurações com o carregamento na interface exatamente daquelas configurações que foram salvas anteriormente para o sinal selecionado. Outra ideia é implementar a possibilidade de editar as configurações carregadas e salvá-las.
Abrimos o corpo do OnEvent() na classe base CProgram e localizamos a parte com o evento de clique no botão. Adicionamos o seguinte código a ele:
//--- for(int i=0; i<5; i++) { if(lparam==m_signal_editor[i].Id()) { LoadSignalSet(i); m_new_signal.LabelText("Save"); m_new_signal.Update(true); m_set_window.OpenWindow(); m_number_signal=i; } }
Aqui, nós determinamos qual botão de sinal criado foi pressionado. Sabendo disso, nós carregamos os dados salvos anteriormente na interface da janela de configurações usando o método LoadSignalSet(), alteramos o nome do botão de Add para Save e abrimos a janela de configurações.
Algoritmos de busca de sinais de negociação
Agora que as ferramentas para criar e editar os sinais de negociação estão prontas, é hora de vinculá-lo à parte do aplicativo responsável pela pesquisa e exibição dos sinais. Nós já criamos uma base para o monitoramento de sinais. Ele possui uma exibição tabular com os nomes de linhas (símbolos selecionados na primeira etapa da configuração) e colunas (períodos selecionados na segunda etapa da configuração).
Fig.6 Botão criando um monitor de sinais de negociação.
A sequência de ações após a criação de pelo menos um sinal de negociação é simples. Um clique em Create aciona a formação de um monitor de sinais de negociação com base em todo o array de configurações que foram definidas anteriormente. Antes de prosseguir com a programação deste sistema, nós precisamos complementar o método ToMonitor() que é chamado depois de pressionar Create.
//--- Hide Step 3 m_add_signal.Hide(); m_signal_header.Hide(); m_back_button.Hide(); m_next_button.Hide(); for(int i=0; i<5; i++) m_signal_editor[i].Hide();
Como nós temos os objetos de botão que permitem exibir e editar os sinais de negociação criados atualmente, esses botões também devem estar ocultos ao pular para a janela do monitor, da mesma forma que todos os controles da Etapa 3 anterior.
No primeiro artigo, quando nós desenvolvemos a estrutura do aplicativo, um dos elementos do monitor era o bloco de indicação mostrado na figura 5 do primeiro artigo. Seu objetivo é exibir em tempo real a presença de um dos sinais de negociação criados anteriormente. Portanto, o primeiro passo é criar um objeto que será usado como um bloco de indicação. Isso pode ser feito implementando o método CreateSignalButton() na classe CProgram.
bool CreateSignalButton(CButton &button,const int x_gap,const int y_gap);
Além disso, adicionamos um array das instâncias de classe CButton necessárias para a criação do conjunto completo de blocos de indicação.
CButton m_signal_button[];
Agora, abrimos o StepWindow.mqh e adicionamos a implementação do método criado no final do arquivo:
//+------------------------------------------------------------------+ //| Creates an indication block | //+------------------------------------------------------------------+ bool CProgram::CreateSignalButton(CButton &button,const int x_gap,const int y_gap) { //--- color baseclr=C'220,225,235'; //--- Save the window pointer button.MainPointer(m_step_window); //--- Set up properties before creation button.TwoState(false); button.XSize(40); button.YSize(20); button.IconXGap(2); button.IconYGap(button.YSize()/2-8); button.LabelXGap(19); button.LabelYGap(2); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(baseclr); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(baseclr); button.LabelColor(clrBlack); button.LabelColorPressed(clrSlateGray); button.IconFile(""); button.IconFileLocked(""); button.IsDoubleBorder(true); //--- Create the control if(!button.CreateButton("",x_gap-button.XSize()/2,y_gap)) return(false); //--- Add the element pointer to the base CWndContainer::AddToElementsArray(0,button); return(true); }
Agora aplicamos-o no método de criação ToMonitor(). Para fazer isso, localizamos a seção Timeframes no corpo do método e adicionamos o código ao método como mostrado abaixo:
//--- 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); } //-- Signal blocks int k=0; ArrayResize(m_signal_button,sy*tf); for(int j=0; j<sy; j++) { for(int i=0; i<tf; i++) { if(!CreateSignalButton(m_signal_button[k],m_timeframe_label[i].XGap()+m_timeframe_label[i].XSize()/2,m_step_window.CaptionHeight()+25+j*25)) return; m_signal_button[k].Update(true); k++; } } //--- 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);
Compilamos o projeto e obtemos um layout pronto para a exibição futura dos sinais de negociação.
Fig.7 Um layout pronto dos sinais de negociação.
Agora lembre-se de quais elementos do bloco de indicação podem ser configurados para exibir certos sinais.
- Cor de fundo.
- A presença e cor da borda do bloco de indicação.
- A cor e o valor do rótulo do texto.
- Presença de um ícone.
- Presença de uma dica de ferramenta.
Para gerenciar essas propriedades, nós definimos os seguintes métodos na seção privada de nossa classe base CProgram:
void SetBorderColor(int index, color clr); void SetLabel(int index, string text,color clr=clrBlack); void SetIcon(int index,int number); void SetBackground(int index,color clr); void SetTooltip(int index,string text="\n");
Sua implementação:
//+------------------------------------------------------------------+ //| Set the border color | //+------------------------------------------------------------------+ void CProgram::SetBorderColor(int index, color clr) { m_signal_button[index].BorderColor(clr); m_signal_button[index].BorderColorHover(clr); m_signal_button[index].BorderColorPressed(clr); m_signal_button[index].Update(true); } //+------------------------------------------------------------------+ //| Set the label text | //+------------------------------------------------------------------+ void CProgram::SetLabel(int index, string text,color clr=clrBlack) { m_signal_button[index].LabelColor(clr); m_signal_button[index].LabelColorHover(clr); m_signal_button[index].LabelColorPressed(clr); m_signal_button[index].LabelText(text); m_signal_button[index].Update(true); } //+------------------------------------------------------------------+ //| Set the background | //+------------------------------------------------------------------+ void CProgram::SetBackground(int index,color clr) { m_signal_button[index].BackColor(clr); m_signal_button[index].BackColorHover(clr); m_signal_button[index].Update(true); } //+------------------------------------------------------------------+ //| Set the icon | //+------------------------------------------------------------------+ void CProgram::SetIcon(int index,int number) { //--- string image[]= { "Images\\EasyAndFastGUI\\Icons\\bmp16\\arrow_up.bmp", "Images\\EasyAndFastGUI\\Icons\\bmp16\\arrow_down.bmp" }; string path=(number>=0)?image[number]:""; if(number<0) m_signal_button[index].IsCenterText(true); else m_signal_button[index].IsCenterText(false); m_signal_button[index].IconFile(path); m_signal_button[index].IconFilePressed(path); m_signal_button[index].Update(true); } //+------------------------------------------------------------------+ //| Set the tooltip | //+------------------------------------------------------------------+ void CProgram::SetTooltip(int index,string text="\n") { m_signal_button[index].Tooltip(text); m_signal_button[index].ShowTooltip(true); }
Em seguida, nós precisamos criar alguns métodos auxiliares necessários para os cálculos adicionais, exibição correta e, o mais importante, para a correspondência de cada um dos blocos de indicação criados para uma linha específica (símbolo selecionado) e coluna (período de tempo). Primeiro, criamos os métodos para determinar a linha e a coluna do bloco de indicação com base no índice na tabela.
int GetRow(int index,int row_size); int GetCol(int index,int row_size); //+------------------------------------------------------------------+ //| Determining a row by the index | //+------------------------------------------------------------------+ int CProgram::GetRow(int index,int row_size) { return(int(MathFloor(index/row_size)+1)); } //+------------------------------------------------------------------+ //| Determining a column by the index | //+------------------------------------------------------------------+ int CProgram::GetCol(int index,int row_size) { return(int(MathMod(index,row_size)+1)); }
Nós também precisamos obter os dados necessários a partir da interface. Ou seja, nós precisamos converter a exibição de texto dos períodos no tipo de enumeração do período. Nós também precisamos descobrir com base no índice do bloco de indicação, a qual símbolo e período ele corresponde na tabela.
//+------------------------------------------------------------------+ //| Return timeframe by row | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES CProgram::StringToTimeframe(const string timeframe) { if(timeframe=="M1") return(PERIOD_M1); if(timeframe=="M2") return(PERIOD_M2); if(timeframe=="M3") return(PERIOD_M3); if(timeframe=="M4") return(PERIOD_M4); if(timeframe=="M5") return(PERIOD_M5); if(timeframe=="M6") return(PERIOD_M6); if(timeframe=="M10") return(PERIOD_M10); if(timeframe=="M12") return(PERIOD_M12); if(timeframe=="M15") return(PERIOD_M15); if(timeframe=="M20") return(PERIOD_M20); if(timeframe=="M30") return(PERIOD_M30); if(timeframe=="H1") return(PERIOD_H1); if(timeframe=="H2") return(PERIOD_H2); if(timeframe=="H3") return(PERIOD_H3); if(timeframe=="H4") return(PERIOD_H4); if(timeframe=="H6") return(PERIOD_H6); if(timeframe=="H8") return(PERIOD_H8); if(timeframe=="H12") return(PERIOD_H12); if(timeframe=="D1") return(PERIOD_D1); if(timeframe=="W1") return(PERIOD_W1); if(timeframe=="MN") return(PERIOD_MN1); //--- The default value return(::Period()); } //+------------------------------------------------------------------+ //| Determine the timeframe | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES CProgram::GetTimeframe(int index) { int tf=ArraySize(m_timeframes); return(StringToTimeframe((m_timeframe_label[GetCol(index,tf)-1].LabelText()))); } //+------------------------------------------------------------------+ //| Determine the symbol | //+------------------------------------------------------------------+ string CProgram::GetSymbol(int index) { int tf=ArraySize(m_timeframes); return(m_symbol_label[GetRow(index,tf)-1].LabelText()); }
O próximo método está diretamente relacionado ao algoritmo de busca de sinal: ele procura o conjunto de parâmetros dos sinais criados anteriormente no símbolo e período de tempo especificados.
bool GetSignal(string sy,ENUM_TIMEFRAMES tf,SIGNAL &signal_set);
As configurações são passadas pela estrutura SIGNAL a partir de um conjunto de parâmetros.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::GetSignal(string sy,ENUM_TIMEFRAMES tf,SIGNAL &signal_set) { //--- Getting the indicator handle int h=INVALID_HANDLE; ENUM_APPLIED_PRICE app_price; switch(signal_set.app_price) { case 0: app_price=PRICE_CLOSE; break; case 1: app_price=PRICE_OPEN; break; case 2: app_price=PRICE_HIGH; break; case 3: app_price=PRICE_LOW; break; case 4: app_price=PRICE_MEDIAN; break; case 5: app_price=PRICE_TYPICAL; break; case 6: app_price=PRICE_WEIGHTED; break; default: app_price=PRICE_CLOSE; break; } //--- switch(signal_set.ind_type) { case 0: h=iATR(sy,tf,signal_set.ind_period); break; case 1: h=iCCI(sy,tf,signal_set.ind_period,app_price); break; case 2: h=iDeMarker(sy,tf,signal_set.ind_period); break; case 3: h=iForce(sy,tf,signal_set.ind_period,MODE_SMA,VOLUME_TICK); break; case 4: h=iWPR(sy,tf,signal_set.ind_period); break; case 5: h=iRSI(sy,tf,signal_set.ind_period,app_price); break; case 6: h=iMomentum(sy,tf,signal_set.ind_period,app_price); break; default: break; } if(h==INVALID_HANDLE) { Print(sy+". Failed to get handle"); Print("Handle = ",h," error = ",GetLastError()); return(false); } //--- double arr[1]; if(CopyBuffer(h,0, 0,1,arr)!=1) { Print("sy= ",sy,"tf= ",EnumToString(tf)," Failed to get handle data ",GetLastError()); return(false); } IndicatorRelease(h); //--- Check the condition double r_value=signal_set.rule_value; double c_value=arr[0]; m_ind_value=c_value; int s=0; switch(signal_set.rule_type) { case 0: if(c_value>r_value) s=1; break; case 1: if(c_value>=r_value) s=1; break; case 2: if(c_value==r_value) s=1; break; case 3: if(c_value<r_value) s=1; break; case 4: if(c_value<=r_value) s=1; break; default: s=0; break; } //--- if(s>0) return(true); return(false); }
O método GetSignal() recebe da estrutura SIGNAL as informações sobre quais indicadores disponíveis foram selecionados para gerar os sinais de negociação, quais configurações foram selecionadas para o indicador e qual regra de busca foi ela definida. Não esqueça que a filtragem pelos períodos pode ser realizada duas vezes para cada sinal. A primeira vez que ela é realizada na segunda etapa de configuração, é possível filtrar os valores selecionados na janela de criação do sinal de negociação, conforme mostrado na figura 8 abaixo.
Fig.8 Selecionando os períodos para o sinal criado.
Para o nosso algoritmo considerar esse filtro e não procurar por sinais fora dos períodos especificados, a especificação dos períodos deve ser verificada para cada um dos sinais de negociação criados. Então, criamos o método CheckTimeframe() na classe base. O método servirá como um filtro.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CheckTimeframe(ENUM_TIMEFRAMES tf,SIGNAL &signal_set) { for(int i=0; i<21; i++) { if(StringToTimeframe(CharArrayToString(signal_set.tf_name[i].tf))==tf) return(true); } return(false); }
Agora, é hora de criar o mecanismo de busca do sinal de negociação. Para fazer isso, adicionamos um método à seção public da classe CProgram: SearchSignal().
bool SearchSignals(void);
Vamos analisar sua implementação passo a passo com mais detalhes e ver o objetivo dos métodos auxiliares criados anteriormente.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::SearchSignals(void) { //--- Search for set signals SIGNAL signal_set[]; int cnt=0; for(int i=0; i<5; i++) { if(FileIsExist("Signal Monitor\\signal_"+string(i)+".bin")) cnt++; } //--- ArrayResize(signal_set,cnt); ZeroMemory(signal_set); //--- for(int i=0; i<cnt; i++) { int h=FileOpen("Signal Monitor\\signal_"+string(i)+".bin",FILE_READ|FILE_BIN); if(h==INVALID_HANDLE) { MessageBox("Configuration not found","Signal Monitor"); return(false); } FileReadStruct(h,signal_set[i]); FileClose(h); for(int j=0; j<ArraySize(m_signal_button); j++) { //--- string sy=GetSymbol(j); ENUM_TIMEFRAMES tf=GetTimeframe(j); //--- if(!CheckTimeframe(tf,signal_set[i])) continue; //--- if(GetSignal(sy,tf,signal_set[i])) { //--- if(signal_set[i].label_type==1) SetLabel(j,CharArrayToString(signal_set[i].label_value),signal_set[i].label_color); else SetLabel(j,DoubleToString(m_ind_value,3),signal_set[i].label_color); //--- if(signal_set[i].back_color!=clrNONE) SetBackground(j,signal_set[i].back_color); //--- if(signal_set[i].border_color!=clrNONE) SetBorderColor(j,signal_set[i].border_color); else SetBorderColor(j,signal_set[i].back_color); //--- if(signal_set[i].tooltip) SetTooltip(j,CharArrayToString(signal_set[i].tooltip_text)); //--- if(signal_set[i].image) SetIcon(j,signal_set[i].img_index); else SetIcon(j,-1); } } } return(true); }
Na primeira etapa da operação, o método de busca coleta os dados sobre o número total de sinais de negociação criados e configurados. Em seguida, o método executa um loop através dos arquivos que conectam as informações sobre as configurações de sinal e lê esses dados em uma estrutura. O mecanismo determina para cada bloco de indicação o símbolo e o período apropriado aos quais o bloco corresponde na forma de tabela. Com base nesses dados, verificamos se um sinal de negociação precisa ser pesquisado no período selecionado. Se o período corresponder, buscamos um sinal. Se um sinal for encontrado, colorimos o bloco de indicação de acordo com a configuração do sinal. Agora, o método criado pode ser aplicado. O método deve ser chamado no final do corpo do método ToMonitor().
... //--- 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); //--- SearchSignals(); }
Agora, vamos tentar ativar uma pesquisa repetida após um determinado intervalo de tempo. Abrimos o arquivo SignalMonitor.mq5 e criamos uma enumeração no início do arquivo:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ enum UPDATE { MINUTE, // 1 Minute MINUTE_15, // 15 Minutes MINUTE_30, // 30 Minutes HOUR, // 1 Hour HOUR_4 // 4 Hours };
Uma nova configuração agora pode ser facilmente adicionada às entradas:
input UPDATE Update = HOUR; // Update interval
Criamos duas variáveis para os cálculos.
int cnts=0; datetime update;
Adicionamos as seguintes linhas na inicialização do expert:
//--- switch(Update) { case MINUTE: cnts=60; break; case MINUTE_15: cnts=60*15; break; case MINUTE_30: cnts=60*30; break; case HOUR: cnts=3600; break; case HOUR_4: cnts=3600*4; break; default: cnts=1; break; } update=TimeLocal()+cnts;
Assim, nós determinamos o intervalo de atualização e definimos o próximo tempo de atualização. No corpo da função OnTick(), adicionamos a verificação de tempo: se o intervalo de tempo especificado tiver passado, buscamos os sinais de negociação novamente.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(TimeLocal()>update) { program.SearchSignals(); update=TimeLocal()+cnts; } }
Compilamos o projeto e criamos um conjunto de seus próprios símbolos. Nós podemos adicionar um sinal para demonstrar a operação do monitor.
Na próxima parte desta série de artigos, nós continuaremos expandindo a funcionalidade atual para uma configuração mais flexível dos sinais de negociação, e também melhoraremos alguns dos recursos existentes.
Conclusão
O arquivo anexado abaixo contém todos os arquivos descritos adequadamente organizados em pastas. Para uma operação correta, você deve salvar a pasta MQL5 na pasta raiz da plataforma. 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 na Fig. 9 abaixo.
Fig. 9. 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/7600
- 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