Conjunto de ferramentas para negociação manual rápida: funcionalidade básica
Sumário
- Introdução
- Ideia por trás do conjunto de ferramentas
- Implementando uma funcionalidade básica
- Fim do artigo
Introdução
Atualmente mais e mais traders estão mudando para sistemas de negociação automáticos que ou requerem configuração inicial ou estão totalmente automatizados. No entanto, ainda existe uma parte considerável de traders que negociam manualmente à moda antiga, deixando dentro dos seus sistemas de negociação um lugar para a tomada de decisões humanas em cada situação de negociação específica. Às vezes, essas situações se desenvolvem muito rapidamente, sendo que ao negociar manualmente (por exemplo, no scalping) a precisão e a oportunidade de entrar no mercado são importantes. Para tais situações, são necessárias ferramentas que garantam o mais rápido possível a implementação da ação planejada para entrar ou sair do mercado. Por isso, decidi implementar uma funcionalidade básica própria para atender a essas necessidades.
Ideia por trás do conjunto de ferramentas
Primeiro, é preciso determinar um conjunto de ações básicas para negociação manual e desenvolver uma ferramenta que permita fazer isso o mais rápido e convenientemente possível. Tal negociação manual deve ocorrer de acordo com um determinado sistema de negociação, o que implica entrar no mercado por uma das seguintes formas: ordens a mercado ou ordens pendentes. Por esse motivo, as categorias principais do conjunto de ferramentas trabalharão precisamente com esses dois tipos de ordens. Além disso, será necessário escolher as tarefas que o trader realizará durante a negociação e reduzir o tempo e o número de ações necessárias para fazê-las.
Fig. 1 Janela principal do conjunto de ferramentas
A figura 1 apresenta duas categorias, nomeadamente ordem a mercado e ordem pendente. Também escolhi três tarefas básicas que às vezes precisam ser resolvidas rapidamente e, ao mesmo tempo, não podem ser realizadas numa única ação. Como é sabido, muitos aplicativos, incluindo o terminal MetaTrader 5, têm seu próprio conjunto básico de teclas de atalho para chamar rapidamente determinado comando ou ação. Isso também será levado em consideração neste aplicativo. Entre colchetes é indicada uma tecla de atalho que ao ser pressionada desencadeia a ação especificada ou, no caso de trabalhar com ordens, abre uma janela para trabalhar com eles. Adicionalmente, ficará disponível um recurso para trabalhar com o mouse. Isto quer dizer que, por exemplo, poderemos fechar todas as ordens lucrativas tanto através de a tecla de atalho C (do inglês close) como através do botão "Fechar todas as lucrativas".
Ao pressionar a tecla de atalho M (do inglês market) ou no botão "Ordem a mercado", a janela "Configurações: Ordem a mercado" é aberta. Ela já fornece um conjunto de ferramenta de entrada de dados para colocar ordens de compra ou venda a mercado.
Fig. 2 Janela para configuração e criação de ordens a mercado
Além da configuração básica, semelhante à do terminal, será possível selecionar um lote não só usando um valor numérico, mas também com base no percentual do saldo da conta de negociação. Ademais, poderemos definir Take Profit e Stop Loss não apenas em formato de preço, mas também em pontos. As ações Comprar e Vender, conforme acima, podem ser realizadas de duas maneiras: clicando no botão correspondente ou pressionando a tecla de atalho indicada entre colchetes.
Agora, ao clicar em P (do inglês pending), chegaremos à janela de configurações de ordens pendentes. Aqui são quatro.
Fig. 3 Janela para configuração e criação de ordens pendentes
Assim como na janela de ordens a mercado, nas ordens pendentes é possível selecionar um lote numérico ou percentual do saldo e selecionar Stop Loss e Take Profit em formato de preço ou pip.
Implementando um conjunto de ferramentas
Primeiro, vamos criar a estrutura inicial do projeto. Na pasta Experts, criamos uma pasta Simple Trading e vários arquivos, como mostrado na Fig. 4 logo abaixo.
Fig. 4 Estrutura de arquivo do projeto
Vejamos o propósito dos arquivos criados:
- SimpleTrading.mq5 — arquivo de Expert Advisor no qual será construída a interface gráfica e ficarão disponíveis as configurações iniciais do aplicativo.
- Program.mqh — Expert Advisor anexado ao arquivo que conterá a classe CFastTrading, seus campos e métodos, e também sua implementação parcial.
- MainWindow.mqh — arquivo anexado ao Program.mqh que conterá a implementação dos métodos dos elementos da GUI.
- Defines.mqh — também é anexado ao Program.mqh e conterá um conjunto de substituições de macro nos elementos de interface para a implementação das versões em russo e inglês.
Primeiro, vamos para Program.mqh, anexamos as bibliotecas necessárias para a implementação das funções de interface e trading, e criamos uma classe CFast Trading.
//+------------------------------------------------------------------+ //| Program.mqh | //| Alex2356 | //| https://www.mql5.com/en/users/alex2356/seller | //+------------------------------------------------------------------+ #include <EasyAndFastGUI\WndEvents.mqh> #include <DoEasy25\Engine.mqh> #include "Defines.mqh" //+------------------------------------------------------------------+ //| Enumeration for switching the interface language | //+------------------------------------------------------------------+ enum LANG { RUSSIAN, // Russian ENGLISH // English }; //+------------------------------------------------------------------+ //| Class for creating an application | //+------------------------------------------------------------------+ class CFastTrading : public CWndEvents { public: CFastTrading(void); ~CFastTrading(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); }; //+------------------------------------------------------------------+ //| Adding GUI elements | //+------------------------------------------------------------------+ #include "MainWindow.mqh" //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CFastTrading::CFastTrading(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CFastTrading::~CFastTrading(void) { } //+------------------------------------------------------------------+ //| Initialization | //+------------------------------------------------------------------+ void CFastTrading::OnInitEvent(void) { } //+------------------------------------------------------------------+ //| Deinitialization | //+------------------------------------------------------------------+ void CFastTrading::OnDeinitEvent(const int reason) { //--- Remove the interface CWndEvents::Destroy(); } //+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CFastTrading::OnTimerEvent(void) { CWndEvents::OnTimerEvent(); //--- } //+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { } //+------------------------------------------------------------------+
Agora vamos para o arquivo do EA SimpleTrading.mq5, anexamos a ele Program.mqh e criamos uma instância da classe recém-criada. Também definimos os parâmetros de entrada, entre os quais estarão:
- Base FontSize — tamanho da fonte base do aplicativo.
- Caption Color — cor de cabeçalho da janela principal do aplicativo.
- Back color — cor de fundo.
- Interface language — idioma da interface.
- Magic Number — número único de ordens criadas pelo Expert Advisor em questão.
//+------------------------------------------------------------------+ //| SimpleTrading.mq5 | //| Alex2356 | //| https://www.mql5.com/en/users/alex2356/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, 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; // Base FontSize input color Caption = C'0,130,225'; // Caption Color input color Background = clrWhite; // Back color input LANG Language = ENGLISH; // Interface language input ulong MagicNumber = 1111; // Magic Number //--- CFastTrading program; ulong tick_counter; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- tick_counter=GetTickCount(); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { program.OnDeinitEvent(reason); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ //| 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"); } } //+------------------------------------------------------------------+
Para que os parâmetros de entrada do EA estejam disponíveis para esta classe, é necessário criar nela variáveis, às quais serão atribuídos os valores de suas configurações de EA e os métodos pelos quais isso acontecerá. Criamos variáveis na seção privada da classe.
private: //--- color m_caption_color; color m_background_color; //--- int m_base_font_size; int m_m_edit_index; int m_p_edit_index; //--- ulong m_magic_number; //--- string m_base_font; //--- LANG m_language;
Geramos os métodos na seção pública:
//--- Caption color void CaptionColor(const color clr); //--- Background color void BackgroundColor(const color clr); //--- Font size void FontSize(const int font_size); //--- Font name void FontName(const string font_name); //--- Setting the interface language void SetLanguage(const LANG lang); //--- Setting the magic number void SetMagicNumber(ulong magic_number);
E vamos implementá-los:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::CaptionColor(const color clr) { m_caption_color=clr; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::BackgroundColor(const color clr) { m_background_color=clr; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::FontSize(const int font_size) { m_base_font_size=font_size; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::FontName(const string font_name) { m_base_font=font_name; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::SetLanguage(const LANG lang) { m_language=lang; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::SetMagicNumber(const ulong magic_number) { m_magic_number=magic_number; }
Agora, na inicialização do Expert Advisor, iremos aplicá-las:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- tick_counter=GetTickCount(); //--- Initialize class variables program.FontSize(Inp_BaseFont); program.BackgroundColor(Background); program.CaptionColor(Caption); program.SetLanguage(Language); program.SetMagicNumber(MagicNumber); //--- return(INIT_SUCCEEDED); }
Vamos adicionar um métodoCreateGUI() que criará toda a interface, mas por enquanto ela estará vazia. À medida que os elementos da IU são criados, ela será preenchida.
//--- Create the graphical interface of the program bool CreateGUI(void);
Ela já pode ser adicionada à inicialização do EA:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- tick_counter=GetTickCount(); //--- Initialize class variables program.FontName("Trebuchet MS"); program.FontSize(Inp_BaseFont); program.BackgroundColor(Background); program.CaptionColor(Caption); program.SetLanguage(Language); program.SetMagicNumber(MagicNumber); //--- Set up the trading panel if(!program.CreateGUI()) { Print(__FUNCTION__," > Failed to create graphical interface!"); return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); }
Agora vamos criar a janela principal do aplicativo. Para fazer isso, adicionamos o métodoCreateMainWindow() na seção protegida de nossa classe.
protected: //--- forms bool CreateMainWindow(void);
Por outra parte, vamos colocar sua implementação no arquivo MainWindow.mqh edepois disso vamos chamá-la no CreateGUI(). Como para implementar este método já usei a substituição de macro CAPTION_NAME, ela deve ser criada num arquivo Defines.mqh criado especialmente para isso.
//+------------------------------------------------------------------+ //| Creates a form for orders | //+------------------------------------------------------------------+ bool CFastTrading::CreateMainWindow(void) { //--- Add a window pointer to the window array CWndContainer::AddWindow(m_main_window); //--- Properties m_main_window.XSize(400); m_main_window.YSize(182); //--- Coordinates int x=5; int y=20; m_main_window.CaptionHeight(22); m_main_window.IsMovable(true); m_main_window.CaptionColor(m_caption_color); m_main_window.CaptionColorLocked(m_caption_color); m_main_window.CaptionColorHover(m_caption_color); m_main_window.BackColor(m_background_color); m_main_window.FontSize(m_base_font_size); m_main_window.Font(m_base_font); //--- Create the form if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y)) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| Creates the graphical interface of the program | //+------------------------------------------------------------------+ bool CFastTrading::CreateGUI(void) { //--- Create the main application window if(!CreateMainWindow()) return(false); //--- Complete GUI creation CWndEvents::CompletedGUI(); return(true); }
Agora vamos criar uma nova substituição de macro.
//+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ #include "Program.mqh" #define CAPTION_NAME (m_language==RUSSIAN ? "Быстрый трейдинг" : "Fast Trading")
Após compilar o projeto, obtemos a janela principal do aplicativo. Agora vamos adicionar botões (Fig. 1) que realizarão as ações descritas acima. Para implementá-los, criaremos um método universal na seção protegida de nossa classe CreateButton().
//--- Buttons bool CreateButton(CWindow &window,CButton &button,string text,color baseclr,int x_gap,int y_gap,int w_number);
Vamos implementá-lo em MainWindow.mqh e aplicá-lo no corpo do método de criação da janela principal.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateButton(CWindow &window,CButton &button,string text,color baseclr,int x_gap,int y_gap,int w_number) { //--- Store the window pointer button.MainPointer(window); //--- Set properties before creation button.XSize(170); button.YSize(40); button.Font(m_base_font); 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(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); //--- Create a control element if(!button.CreateButton(text,x_gap,window.CaptionHeight()+y_gap)) return(false); //--- Add a pointer to the element to the database CWndContainer::AddToElementsArray(w_number,button); return(true); } //+------------------------------------------------------------------+ //| Creates a form for orders | //+------------------------------------------------------------------+ bool CFastTrading::CreateMainWindow(void) { //--- Add a window pointer to the window array CWndContainer::AddWindow(m_main_window); //--- Properties m_main_window.XSize(400); m_main_window.YSize(182); //--- Coordinates int x=5; int y=20; m_main_window.CaptionHeight(22); m_main_window.IsMovable(true); m_main_window.CaptionColor(m_caption_color); m_main_window.CaptionColorLocked(m_caption_color); m_main_window.CaptionColorHover(m_caption_color); m_main_window.BackColor(m_background_color); m_main_window.FontSize(m_base_font_size); m_main_window.Font(m_base_font); m_main_window.TooltipsButtonIsUsed(true); //--- Create the form if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y)) return(false); //--- if(!CreateButton(m_main_window,m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',20,10,0)) return(false); if(!CreateButton(m_main_window,m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',210,10,0)) return(false); if(!CreateButton(m_main_window,m_order_button[2],MARKET_ORDERS_PROFIT_CLOSE+"(C)",C'87,128,255',20,60,0)) return(false); if(!CreateButton(m_main_window,m_order_button[3],MARKET_ORDERS_LOSS_CLOSE+"(D)",C'87,128,255',20,110,0)) return(false); if(!CreateButton(m_main_window,m_order_button[4],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',210,60,0)) return(false); return(true); }
Mas como aqui também são usadas novas substituições de macro, vamos para Defines.mqh e vamos escrevê-las em duas línguas.
//+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ #include "Program.mqh" #define CAPTION_NAME (m_language==RUSSIAN ? "Быстрый трейдинг" : "Fast Trading System") #define MARKET_ORDER_NAME (m_language==RUSSIAN ? "Рыночный ордер" : "Marker Order") #define PENDING_ORDER_NAME (m_language==RUSSIAN ? "Отложенный ордер" : "Pending Order") #define MARKET_ORDERS_PROFIT_CLOSE (m_language==RUSSIAN ? "Закрыть все прибыльные" : "Close all profitable") #define MARKET_ORDERS_LOSS_CLOSE (m_language==RUSSIAN ? "Закрыть все убыточные" : "Close all losing") #define PEND_ORDERS_ALL_CLOSE (m_language==RUSSIAN ? "Закрыть все отложенные" : "Close all pending")
Como resultado, ao escolher o inglês, temos a seguinte variação.
Fig. 5 Janela principal do aplicativo.
A versão em russo é mostrada na Fig. 1 acima. Conforme afirmado no ideia por trás, o aplicativo deverá ter mais duas janelas para trabalhar ordens a mercado e pendentes. Portanto, iremos criá-las e associá-las aos botões Market Order e Pending Order conforme acordado acima: clicando sobre eles, bem como utilizando as teclas de atalho M e P, respectivamente. Por isso, adicionamos os dois métodos CreateMarketOrdersWindow() e CreatePendingOrdersWindow() à seção protegida da classe e os implementamos no arquivo MainWindow.mqh.
bool CreateMarketOrdersWindow(void); bool CreatePendingOrdersWindow(void); //+------------------------------------------------------------------+ //| Market order creation and editing window | //+------------------------------------------------------------------+ bool CFastTrading::CreateMarketOrdersWindow(void) { //--- Add a window pointer to the window array CWndContainer::AddWindow(m_orders_windows[0]); //--- Properties m_orders_windows[0].XSize(450); m_orders_windows[0].YSize(242+58); //--- Coordinates int x=m_order_button[0].XGap(); int y=m_order_button[0].YGap()+60; //--- color clrmain=C'87,128,255'; //--- m_orders_windows[0].CaptionHeight(22); m_orders_windows[0].IsMovable(true); m_orders_windows[0].CaptionColor(clrmain); m_orders_windows[0].CaptionColorLocked(clrmain); m_orders_windows[0].CaptionColorHover(clrmain); m_orders_windows[0].BackColor(m_background_color); m_orders_windows[0].BorderColor(clrmain); m_orders_windows[0].FontSize(m_base_font_size); m_orders_windows[0].Font(m_base_font); m_orders_windows[0].WindowType(W_DIALOG); //--- Create the form if(!m_orders_windows[0].CreateWindow(m_chart_id,m_subwin,CAPTION_M_ORD_NAME,x,y)) return(false); return(true); } //+------------------------------------------------------------------+ //| Pending order creation and editing window | //+------------------------------------------------------------------+ bool CFastTrading::CreatePendingOrdersWindow(void) { //--- Add a window pointer to the window array CWndContainer::AddWindow(m_orders_windows[1]); //--- Properties m_orders_windows[1].XSize(600); m_orders_windows[1].YSize(580); //--- Coordinates int x=m_order_button[0].XGap(); int y=m_order_button[0].YGap()+60; //--- color clrmain=C'31,209,111'; //--- m_orders_windows[1].CaptionHeight(22); m_orders_windows[1].IsMovable(true); m_orders_windows[1].CaptionColor(clrmain); m_orders_windows[1].CaptionColorLocked(clrmain); m_orders_windows[1].CaptionColorHover(clrmain); m_orders_windows[1].BackColor(m_background_color); m_orders_windows[1].BorderColor(clrmain); m_orders_windows[1].FontSize(m_base_font_size); m_orders_windows[1].Font(m_base_font); m_orders_windows[1].WindowType(W_DIALOG); //--- Create the form if(!m_orders_windows[1].CreateWindow(m_chart_id,m_subwin,CAPTION_P_ORD_NAME,x,y)) return(false); return(true); }
Como ambas as janelas usam novas substituições de macro, nós as adicionamos ao arquivo correspondente:
#define CAPTION_M_ORD_NAME (m_language==RUSSIAN ? "Настройка: Рыночный Ордер" : "Setting: Market Order") #define CAPTION_P_ORD_NAME (m_language==RUSSIAN ? "Настройка: Отложенный Ордер" : "Setting: Pending Order")
Agora vamos chamar as janelas recém-criadas no método base CreateGUI() que gera a interface de aplicativo:
//+------------------------------------------------------------------+ //| Creates the graphical interface of the program | //+------------------------------------------------------------------+ bool CFastTrading::CreateGUI(void) { //--- Create the main application window if(!CreateMainWindow()) return(false); if(!CreateMarketOrdersWindow()) return(false); if(!CreatePendingOrdersWindow()) return(false); //--- Complete GUI creation CWndEvents::CompletedGUI(); return(true); }
Como as duas janelas criadas são janelas de diálogo, elas não serão visíveis ao iniciar o aplicativo. Além disso, é necessário criar um mecanismo para exibi-las. Mas já decidimos que elas serão uma abertura clicando nos botões Ordem a mercado e Pendente, bem como nas teclas de atalho. Para fazer isso, encontramos o corpo do método na classe base OnEvent() e anotamos as condições necessárias para nossa tarefa.
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Pressing the button event if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { //--- if(lparam==m_order_button[0].Id()) m_orders_windows[0].OpenWindow(); //--- if(lparam==m_order_button[1].Id()) m_orders_windows[1].OpenWindow(); } //--- Key press event if(id==CHARTEVENT_KEYDOWN) { //--- Opening a market order window if(lparam==KEY_M) { if(m_orders_windows[0].IsVisible()) { m_orders_windows[0].CloseDialogBox(); } else { if(m_orders_windows[1].IsVisible()) { m_orders_windows[1].CloseDialogBox(); } //--- m_orders_windows[0].OpenWindow(); } } //--- Opening a pending order window if(lparam==KEY_P) { if(m_orders_windows[1].IsVisible()) { m_orders_windows[1].CloseDialogBox(); } else { if(m_orders_windows[0].IsVisible()) { m_orders_windows[0].CloseDialogBox(); } //--- m_orders_windows[1].OpenWindow(); } } } }
Agora podemos compilar o projeto. Vamos tentar abrir caixas de diálogo usando teclas de atalho. O resultado deve ser semelhante ao da Fig. 6 abaixo:
Fig. 6 Abrindo e alternando janelas usando teclas de atalho.
Aqui também é possível notar que não é necessário fechar uma caixa de diálogo para abrir outra. Ocorre a transferência de um para a outra, o que economiza tempo e permite, se necessário, passar de ordens a mercado para ordens pendentes, bastando pressionar um botão. Como outro pequeno toque de conveniência, decidi adicionar caixas de diálogo de fechamento usando a tecla Esc. Ao testar a abertura algumas vezes, houve um desejo de fechá-los por meio deste botão, por isso, vamos adicionar o seguinte código à seção do Evento de pressionamento de botão no teclado:
//--- Exiting the order placing window if(lparam==KEY_ESC) { if(m_orders_windows[0].IsVisible()) { m_orders_windows[0].CloseDialogBox(); } else if(m_orders_windows[1].IsVisible()) { m_orders_windows[1].CloseDialogBox(); } }
Vamos dar uma olhada mais de perto nas janelas criadas e começar com a janela de Ordens a mercado. Como lembramos da Figura 2, é necessário criar dois blocos para gerenciar as ordens de compra e venda. Primeiro, vamos criar dois elementos de interface, isto é, frames com o novo método CreateFrame().
bool CreateFrame(CWindow &window,CFrame &frame,const int x_gap,const int y_gap,string caption,int w_number);
Sua implementação será a seguinte:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateFrame(CWindow &window,CFrame &frame,const int x_gap,const int y_gap,string caption,int w_number) { //--- Store the pointer to the main control frame.MainPointer(window); //--- color clrmain=clrNONE; if(caption==BUY_ORDER) clrmain=C'88,212,210'; else if(caption==SELL_ORDER) clrmain=C'236,85,79'; //--- frame.YSize(110); frame.LabelColor(clrmain); frame.BorderColor(clrmain); frame.BackColor(m_background_color); frame.GetTextLabelPointer().BackColor(m_background_color); frame.Font(m_base_font); frame.FontSize(m_base_font_size); frame.AutoXResizeMode(true); frame.AutoXResizeRightOffset(10); //--- Create a control element if(!frame.CreateFrame(caption,x_gap,window.CaptionHeight()+y_gap)) return(false); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(w_number,frame); return(true); }
Ao implementar a criação de um quadro, temos duas novas substituições de macro para os cabeçalhos dos quadros, portanto, vamos adicioná-los ao Defines.mqh:
#define BUY_ORDER (m_language==RUSSIAN ? "Buy-ордер" : "Buy-order") #define SELL_ORDER (m_language==RUSSIAN ? "Sell-ордер" : "Sell-order")
Para aplicar este método, é preciso ir até o final do corpo CreateMarketOrdersWindow() para criação de uma janela de ordens a mercado e escrever o seguinte:
... //--- Create the form if(!m_orders_windows[0].CreateWindow(m_chart_id,m_subwin,CAPTION_M_ORD_NAME,x,y)) return(false); //--- BUY BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1)) return(false); //--- SELL BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1)) return(false); return(true); }
Agora vamos ver o resultado:
Fig. 7 Blocos de ordens a mercado.
Cada um dos blocos na Fig. 7 conterá as 4 categorias principais de elementos de interface:
- Cabeçalhos de texto: lote, take-profit, stop-loss.
- Botões de alternância: para o lote, é lote/% do depósito, para take-profit e stop-loss, esse é o preço ou modo de pontos.
- Campos de entrada: lote, take-profit, stop-loss.
- Botão de ação: vender ou comprar.
Seguimos com a implementação em fases de cada categoria. Para os cabeçalhos vamos criar um método genérico CreateLabel().
//+------------------------------------------------------------------+ //| Creates the text label | //+------------------------------------------------------------------+ bool CFastTrading::CreateLabel(CWindow &window,CTextLabel &text_label,const int x_gap,const int y_gap,string label_text,int w_number) { //--- Store the window pointer text_label.MainPointer(window); //--- text_label.Font(m_base_font); text_label.FontSize(m_base_font_size); text_label.XSize(80); text_label.BackColor(m_background_color); text_label.IsCenterText(true); //--- Creating a label if(!text_label.CreateTextLabel(label_text,x_gap,window.CaptionHeight()+y_gap)) return(false); //--- Add a pointer to the element to the database CWndContainer::AddToElementsArray(w_number,text_label); return(true); }
Acima, complementamos o método de criação da janela de ordem a mercado, agora iremos complementá-lo levando em consideração a implementação atual.
//--- BUY BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[0],m_m_text_labels[0],20,30,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[1],20+80+20,30,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[2],20+(80+20)*2,30,SL,1)) return(false); //--- SELL BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[0],m_m_text_labels[3],20,170,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[4],20+80+20,170,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[5],20+(80+20)*2,170,SL,1)) return(false); return(true); }
Existem mais três novas substituições de macro para Lote, Take Profit e Stop Loss. Por isso, adicionamos seus nomes em dois idiomas:
#define LOT (m_language==RUSSIAN ? "Лот" : "Lot") #define TP (m_language==RUSSIAN ? "Тейк профит" : "Take Profit") #define SL (m_language==RUSSIAN ? "Стоп лосс" : "Stop Loss")
A próxima categoria são os botões de alternância. O método CreateSwitchButton() será especialmente criado para eles.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateSwitchButton(CWindow &window,CButton &button,string text,int x_gap,int y_gap,int w_number) { //--- Store the window pointer button.MainPointer(window); color baseclr=clrSlateBlue; color pressclr=clrIndigo; //--- Set properties before creation button.XSize(80); button.YSize(24); button.Font(m_base_font); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(pressclr); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(pressclr); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); button.TwoState(true); //--- Create a control element if(!button.CreateButton(text,x_gap,window.CaptionHeight()+y_gap)) return(false); //--- Add a pointer to the element to the database CWndContainer::AddToElementsArray(w_number,button); return(true); }
Também vamos aplicá-lo para ambos os blocos no mesmo método CreateMarketWindow():
... //--- BUY BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[0],m_m_text_labels[0],20,30,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[1],20+80+20,30,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[2],20+(80+20)*2,30,SL,1)) return(false); //--- Toggle buttons for(int i=0; i<3; i++) if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*i,60,1)) return(false); //--- SELL BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[0],m_m_text_labels[3],20,170,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[4],20+80+20,170,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[5],20+(80+20)*2,170,SL,1)) return(false); //--- Toggle buttons for(int i=3; i<6; i++) if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*(i-3),170+30,1)) return(false); return(true); }
Teremos de ver mais profundamente a seguinte categoria de campos de entrada, pois exigirá um entendimento adicional de que esses elementos de interface devem ter certas restrições de uso. Por exemplo, é necessário ter em mente que os valores para o campo de entrada Lote devem ser limitados pela especificação do símbolo atual, bem como pelas especificações da conta de negociação. Quer dizer, ao editar este campo, é necessário levar em consideração parâmetros como o lote mínimo e máximo possível, bem como o incremento mínimo para alterá-lo. Com base nisso, criamos CreateLotEdit() e configuramos seu campo de entrada assim:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateLotEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number) { //--- Store the pointer to the main control text_edit.MainPointer(window); //--- Properties text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX)); text_edit.StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP)); text_edit.MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN)); text_edit.SpinEditMode(true); text_edit.SetDigits(2); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- Create a control element if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap)) return(false); text_edit.SetValue(string(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN))); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(w_number,text_edit); return(true); }
Além disso, criamos campos de entrada para Stop Loss e Take Profit. Adicionamos tudo o que foi criado acima ao mesmo método de criação de janela de ordens a mercado.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateTakeProfitEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number) { //--- Store the pointer to the main control text_edit.MainPointer(window); //--- Properties text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.MaxValue(9999); text_edit.StepValue(1); text_edit.MinValue(0); text_edit.SpinEditMode(true); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- Create a control element if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap)) return(false); text_edit.SetValue(string(150)); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(w_number,text_edit); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateStopLossEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number) { //--- Store the pointer to the main control text_edit.MainPointer(window); //--- Properties text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.MaxValue(9999); text_edit.StepValue(1); text_edit.MinValue(0); text_edit.SpinEditMode(true); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- Create a control element if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap)) return(false); text_edit.SetValue(string(150)); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(w_number,text_edit); return(true); }
E a última categoria de elementos de interface dessa janela serão os dois botões Comprar e Vender. E os respectivos métodos CreateBuyButton() e CreateSellButton() para adicioná-los.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateBuyButton(CWindow &window,CButton &button,string text,int x_gap,int y_gap,int w_number) { //--- Store the window pointer button.MainPointer(window); color baseclr=C'88,212,210'; //--- Set properties before creation button.XSize(120); button.YSize(40); button.Font(m_base_font); 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(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); //--- Create a control element if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add a pointer to the element to the database CWndContainer::AddToElementsArray(w_number,button); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateSellButton(CWindow &window,CButton &button,string text,int x_gap,int y_gap,int w_number) { //--- Store the window pointer button.MainPointer(window); color baseclr=C'236,85,79'; //--- Set properties before creation button.XSize(120); button.YSize(40); button.Font(m_base_font); 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(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); //--- Create a control element if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add a pointer to the element to the database CWndContainer::AddToElementsArray(w_number,button); return(true); }
Com a adição de dois botões concluímos a implementação do método CreateMarketWindow():
//+------------------------------------------------------------------+ //| Market order creation and editing window | //+------------------------------------------------------------------+ bool CFastTrading::CreateMarketOrdersWindow(void) { //--- Add a window pointer to the window array CWndContainer::AddWindow(m_orders_windows[0]); //--- Properties m_orders_windows[0].XSize(450); m_orders_windows[0].YSize(242+58); //--- Coordinates int x=m_order_button[0].XGap(); int y=m_order_button[0].YGap()+60; //--- color clrmain=C'87,128,255'; //--- m_orders_windows[0].CaptionHeight(22); m_orders_windows[0].IsMovable(true); m_orders_windows[0].CaptionColor(clrmain); m_orders_windows[0].CaptionColorLocked(clrmain); m_orders_windows[0].CaptionColorHover(clrmain); m_orders_windows[0].BackColor(m_background_color); m_orders_windows[0].BorderColor(clrmain); m_orders_windows[0].FontSize(m_base_font_size); m_orders_windows[0].Font(m_base_font); m_orders_windows[0].WindowType(W_DIALOG); //--- Create the form if(!m_orders_windows[0].CreateWindow(m_chart_id,m_subwin,CAPTION_M_ORD_NAME,x,y)) return(false); //--- BUY BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[0],m_m_text_labels[0],20,30,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[1],20+80+20,30,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[2],20+(80+20)*2,30,SL,1)) return(false); //--- Toggle buttons for(int i=0; i<3; i++) if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*i,60,1)) return(false); //--- Edits if(!CreateLotEdit(m_orders_windows[0],m_lot_edit[0],20,60+34,1)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[0],m_tp_edit[0],20+(80+20),60+34,1)) return(false); if(!CreateStopLossEdit(m_orders_windows[0],m_sl_edit[0],20+(80+20)*2,60+34,1)) return(false); //--- The Buy button if(!CreateBuyButton(m_orders_windows[0],m_buy_execute,BUY+"(B)",m_orders_windows[0].XSize()-(120+20),103,1)) return(false); //--- SELL BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[0],m_m_text_labels[3],20,170,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[4],20+80+20,170,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[5],20+(80+20)*2,170,SL,1)) return(false); //--- Toggle buttons for(int i=3; i<6; i++) if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*(i-3),170+30,1)) return(false); //--- Edits if(!CreateLotEdit(m_orders_windows[0],m_lot_edit[1],20,170+30+35,1)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[0],m_tp_edit[1],20+80+20,170+30+35,1)) return(false); if(!CreateStopLossEdit(m_orders_windows[0],m_sl_edit[1],20+(80+20)*2,170+30+35,1)) return(false); //--- The Sell button if(!CreateSellButton(m_orders_windows[0],m_sell_execute,SELL+"(S)",m_orders_windows[0].XSize()-(120+20),242,1)) return(false); return(true); }
Além disso, não nos esquecemos de adicionar duas novas substituições de macro:
#define BUY (m_language==RUSSIAN ? "Купить" : "Buy") #define SELL (m_language==RUSSIAN ? "Продать" : "Sell")
Compilamos o projeto neste estágio e obtemos o seguinte resultado:
Fig. 8 Interface completa da janela para criação de ordens a mercado.
Mas, por enquanto, isto é apenas um modelo. Já para o seu desenvolvimento, agora devem ser resolvidas as seguintes tarefas:
- Criar botões de alternância.
- Alterar as propriedades dos campos de entrada dependendo de seu estado.
- Implementar a colocar ordens a mercado de acordo com os modos dos alternadores e os dados dos campos de entrada.
Antes de começar a implementar o mecanismo de alternância de botões para alterar nomes, é necessário definir seus valores padrão, uma vez que na Fig. 8 são visíveis alguns traços. Para definir o nome, apresentamos o método SetButtonParam(), especialmente porque será útil para alterá-lo no futuro.
//+------------------------------------------------------------------+ //| Setting the button text | //+------------------------------------------------------------------+ void CFastTrading::SetButtonParam(CButton &button,string text) { button.LabelText(text); button.Update(true); }
Vamos para o manipulador de eventos e entramos na seção Evento após a conclusão da criação da interface e usando nosso método SetButtonParam() atribuímos nomes a todos os botões de alternância.
//--- UI creation completion if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI) { //--- SetButtonParam(m_switch_button[0],LOT); SetButtonParam(m_switch_button[1],POINTS); SetButtonParam(m_switch_button[2],POINTS); SetButtonParam(m_switch_button[3],LOT); SetButtonParam(m_switch_button[4],POINTS); SetButtonParam(m_switch_button[5],POINTS); }
Aqui temos mais uma substituição de macro para o nome dos botões que devemos adicionar ao Defines.mqh:
#define POINTS (m_language==RUSSIAN ? "Пункты" : "Points")
Os preparativos para a criação do mecanismo de alternância estão prontos. Criamos uma função ButtonSwitch() que rastreará o estado do botão (pressionado/solto) do comutador e, com base nisso, atribuímos um nome a ele.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::ButtonSwitch(CButton &button,long lparam,string state1,string state2) { if(lparam==button.Id()) { if(!button.IsPressed()) SetButtonParam(button,state1); else SetButtonParam(button,state2); } }
Como a alternância do nome ocorre pelo evento de pressionamento de botão, vamos chamar o método criado em apenas uma seção de manipulador de eventos.
//--- Pressing the button event if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { //--- if(lparam==m_order_button[0].Id()) m_orders_windows[0].OpenWindow(); //--- if(lparam==m_order_button[1].Id()) m_orders_windows[1].OpenWindow(); //--- ButtonSwitch(m_switch_button[0],lparam,LOT,PERC_DEPO); ButtonSwitch(m_switch_button[1],lparam,POINTS,PRICE); ButtonSwitch(m_switch_button[2],lparam,POINTS,PRICE); ButtonSwitch(m_switch_button[3],lparam,LOT,PERC_DEPO); ButtonSwitch(m_switch_button[4],lparam,POINTS,PRICE); ButtonSwitch(m_switch_button[5],lparam,POINTS,PRICE); }
Também descreveremos novas substituições de macro em ambos os idiomas:
#define PERC_DEPO (m_language==RUSSIAN ? "% Депозит" : "% Deposit") #define PRICE (m_language==RUSSIAN ? "Цена" : "Price")
Compilamos o projeto novamente e obtemos a mudança de nome clicando no botão comutador:
Fig. 9 Alteração dos nomes dos botões comutadores.
Seguimos em frente. A próxima tarefa é alterar as propriedades dos campos de entrada dependendo do estado do botão comutador correspondente. Para fazer isso, apresentaremos três novos métodos para cada campo de entrada: LotMarketSwitch(),TakeMarketSwitch(),StopMarketSwitch(). Para o primeiro método, haverá uma alternância do valor do lote para porcentagem do saldo da conta.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::LotMarketSwitch(long lparam) { for(int i=0; i<2; i++) { if(lparam==m_switch_button[i*3].Id()) { if(m_switch_button[i*3].IsPressed()) { m_lot_edit[i].SetDigits(0); m_lot_edit[i].StepValue(1); m_lot_edit[i].MaxValue(100); m_lot_edit[i].MinValue(1); m_lot_edit[i].SetValue(string(2)); m_lot_edit[i].GetTextBoxPointer().Update(true); } else { m_lot_edit[i].SetDigits(2); m_lot_edit[i].StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP)); m_lot_edit[i].MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN)); m_lot_edit[i].MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX)); m_lot_edit[i].SetValue(string(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN))); m_lot_edit[i].GetTextBoxPointer().Update(true); } } } }
Para os outros dois métodos, será do valor de definição de nível em pontos para valor de preço.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::TakeMarketSwitch(long lparam) { for(int i=0; i<2; i++) { if(lparam==m_switch_button[3*i+1].Id()) { if(m_switch_button[3*i+1].IsPressed()) { MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) { m_tp_edit[i].SetDigits(_Digits); m_tp_edit[i].StepValue(_Point); m_tp_edit[i].SetValue(string(tick.ask)); m_tp_edit[i].GetTextBoxPointer().Update(true); } } else { m_tp_edit[i].SetDigits(0); m_tp_edit[i].StepValue(1); m_tp_edit[i].SetValue(string(150)); m_tp_edit[i].GetTextBoxPointer().Update(true); } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::StopMarketSwitch(long lparam) { for(int i=0; i<2; i++) { if(lparam==m_switch_button[3*i+2].Id()) { if(m_switch_button[3*i+2].IsPressed()) { MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) { m_sl_edit[i].SetDigits(_Digits); m_sl_edit[i].StepValue(_Point); m_sl_edit[i].SetValue(string(tick.bid)); m_sl_edit[i].GetTextBoxPointer().Update(true); } } else { m_sl_edit[i].SetDigits(0); m_sl_edit[i].StepValue(1); m_sl_edit[i].SetValue(string(150)); m_sl_edit[i].GetTextBoxPointer().Update(true); } } } }
Agora, vamos chamar todos os três métodos no manipulador de eventos na seção Evento ao clicar no botão:
// --- Switch Lot/Percent of balance LotMarketSwitch(lparam); //--- Switch Take Profit Points/Price TakeMarketSwitch(lparam); //--- Switch Stop Loss Points/Price StopMarketSwitch(lparam);
E verificamos o que resulta no final:
Fig.10 Alternância de modos de campos de entrada na janela de ordens a mercado.
A última tarefa é colocar ordens a mercado de acordo com as configurações e valores dos campos de entrada usando um botão ou tecla de atalho. Para fazer isso, criamos três novos métodos:
- OnInitTrading() — define e configura as condições do ambiente de negociação.
- SetBuyOrder() — abre uma ordem de compra a mercado.
- SetSellOrder() — abre uma ordem de venda a mercado.
Vamos criá-los na seção privada de nossa classe base CFastTrading e implementá-lo nela.
//+------------------------------------------------------------------+ //| Trading Environment Initialization | //+------------------------------------------------------------------+ void CFastTrading::OnInitTrading() { string array_used_symbols[]; //--- Fill in the array of used symbols CreateUsedSymbolsArray(SYMBOLS_MODE_CURRENT,"",array_used_symbols); //--- Set the type of the used symbol list in the symbol collection and fill in the list of symbol timeseries m_trade.SetUsedSymbols(array_used_symbols); //--- Pass all existing collections to the trading class m_trade.TradingOnInit(); m_trade.TradingSetMagic(m_magic_number); m_trade.TradingSetLogLevel(LOG_LEVEL_ERROR_MSG); //--- Set synchronous passing of orders for all used symbols m_trade.TradingSetAsyncMode(false); //--- Set correct order expiration and filling types to all trading objects m_trade.TradingSetCorrectTypeExpiration(); m_trade.TradingSetCorrectTypeFilling(); }
Chamamos esse método no construtor da classe:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CFastTrading::CFastTrading(void) { OnInitTrading(); }
Antes de implementar funções de negociação deve-se ter em mente que sua execução será realizada com base em dois eventos diferentes.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetBuyOrder(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buy_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_B)) { //--- double lot; if(m_switch_button[0].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[0].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[0].GetValue())); if(m_switch_button[1].IsPressed() && m_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[0].GetValue()); double sl=double(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(!m_switch_button[1].IsPressed() && !m_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[0].GetValue()); int sl=int(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(m_switch_button[1].IsPressed() && !m_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[0].GetValue()); int sl=int(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(!m_switch_button[1].IsPressed() && m_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[0].GetValue()); double sl=double(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) return(true); } } return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetSellOrder(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_sell_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_S)) { //--- double lot; if(m_switch_button[3].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_SELL,SymbolInfoDouble(Symbol(),SYMBOL_BID),StringToDouble(m_lot_edit[1].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[1].GetValue())); //--- if(m_switch_button[4].IsPressed() && m_switch_button[5].IsPressed()) { double tp=double(m_tp_edit[1].GetValue()); double sl=double(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(!m_switch_button[4].IsPressed() && !m_switch_button[5].IsPressed()) { int tp=int(m_tp_edit[1].GetValue()); int sl=int(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(!m_switch_button[4].IsPressed() && m_switch_button[5].IsPressed()) { int tp=int(m_tp_edit[1].GetValue()); double sl=double(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(m_switch_button[4].IsPressed() && !m_switch_button[5].IsPressed()) { double tp=double(m_tp_edit[1].GetValue()); int sl=int(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) return(true); } } return(false); }
Embora ambos os métodos sejam chamados da mesma maneira como acontece com outros no manipulador de eventos, eles estarão fora de qualquer uma das seções de identificação de eventos pelo motivo indicado acima. A implementação atual das funções acima usa o método LotPercent() para calcular o tamanho do lote no modo 'como uma porcentagem do saldo da conta'.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CFastTrading::LotPercent(string symbol,ENUM_ORDER_TYPE trade_type,double price,double percent) { double margin=0.0; //--- checks if(symbol=="" || price<=0.0 || percent<1 || percent>100) return(0.0); //--- calculate margin requirements for 1 lot if(!OrderCalcMargin(trade_type,symbol,1.0,price,margin) || margin<0.0) return(0.0); //--- if(margin==0.0) return(0.0); //--- calculate maximum volume double volume=NormalizeDouble(AccountInfoDouble(ACCOUNT_BALANCE)*percent/100.0/margin,2); //--- normalize and check limits double stepvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP); if(stepvol>0.0) volume=stepvol*MathFloor(volume/stepvol); //--- double minvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN); if(volume<minvol) volume=0.0; //--- double maxvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX); if(volume>maxvol) volume=maxvol; //--- return volume return(volume); }
Assim fica concluído o trabalho com ordens a mercado. Agora vamos para a janela de criação de ordens pendentes e, usando o mesmo procedimento realizado com a janela das ordens a mercado, vamos criar uma interface passo a passo. Não faz sentido cobrir toda a sequência de ações, já que são muitos momentos repetitivos, mas vamos nos deter em novas situações.
//+------------------------------------------------------------------+ //| Pending order creation and editing window | //+------------------------------------------------------------------+ bool CFastTrading::CreatePendingOrdersWindow(void) { //--- Add a window pointer to the window array CWndContainer::AddWindow(m_orders_windows[1]); //--- Properties m_orders_windows[1].XSize(600); m_orders_windows[1].YSize(580); //--- Coordinates int x=m_order_button[0].XGap(); int y=m_order_button[0].YGap()+60; //--- color clrmain=C'31,209,111'; //--- m_orders_windows[1].CaptionHeight(22); m_orders_windows[1].IsMovable(true); m_orders_windows[1].CaptionColor(clrmain); m_orders_windows[1].CaptionColorLocked(clrmain); m_orders_windows[1].CaptionColorHover(clrmain); m_orders_windows[1].BackColor(m_background_color); m_orders_windows[1].BorderColor(clrmain); m_orders_windows[1].FontSize(m_base_font_size); m_orders_windows[1].Font(m_base_font); m_orders_windows[1].WindowType(W_DIALOG); //--- Create the form if(!m_orders_windows[1].CreateWindow(m_chart_id,m_subwin,CAPTION_P_ORD_NAME,x,y)) return(false); //---BUY-STOP BLOCK if(!CreateFrame(m_orders_windows[1],m_frame[2],10,20,BUYSTOP_ORDER,2)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[1],m_p_text_labels[0],20,60,PRICE,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[1],20+80+20,30,LOT,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[2],20+(80+20)*2,30,TP,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[3],20+(80+20)*3,30,SL,2)) return(false); //--- Switches for(int i=0; i<3; i++) if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*i,60,2)) return(false); //--- Edits if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[0],20,60+35,2)) return(false); if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[2],20+(80+20),60+35,2)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[2],20+(80+20)*2,60+35,2)) return(false); if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[2],20+(80+20)*3,60+35,2)) return(false); //--- Buy Stop placing button if(!CreateBuyButton(m_orders_windows[1],m_buystop_execute,"Buy Stop ( 1 )",m_orders_windows[1].XSize()-(120+20),103,2)) return(false); //---SELL-STOP BLOCK if(!CreateFrame(m_orders_windows[1],m_frame[3],10,160,SELLSTOP_ORDER,2)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[1],m_p_text_labels[4],20,170+30,PRICE,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[5],20+80+20,170,LOT,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[6],20+(80+20)*2,170,TP,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[7],20+(80+20)*3,170,SL,2)) return(false); //--- Switches for(int i=3; i<6; i++) if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*(i-3),170+30,2)) return(false); //--- Edits if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[1],20,170+30+35,2)) return(false); if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[3],20+(80+20),170+30+35,2)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[3],20+(80+20)*2,170+30+35,2)) return(false); if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[3],20+(80+20)*3,170+30+35,2)) return(false); //--- Sell Stop placing button if(!CreateSellButton(m_orders_windows[1],m_sellstop_execute,"Sell Stop ( 2 )",m_orders_windows[1].XSize()-(120+20),242,2)) return(false); //---BUY-LIMIT BLOCK if(!CreateFrame(m_orders_windows[1],m_frame[4],10,300,BUYLIMIT_ORDER,2)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[1],m_p_text_labels[8],20,330,PRICE,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[9],20+80+20,310,LOT,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[10],20+(80+20)*2,310,TP,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[11],20+(80+20)*3,310,SL,2)) return(false); //--- Switches for(int i=6; i<9; i++) if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*(i-6),330,2)) return(false); //--- Edits if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[2],20,365,2)) return(false); if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[4],20+(80+20),365,2)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[4],20+(80+20)*2,365,2)) return(false); if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[4],20+(80+20)*3,365,2)) return(false); //--- Buy Limit placing button if(!CreateBuyButton(m_orders_windows[1],m_buylimit_execute,"Buy Limit ( 3 )",m_orders_windows[1].XSize()-(120+20),382,2)) return(false); //---SELL-LIMIT BLOCK if(!CreateFrame(m_orders_windows[1],m_frame[5],10,440,SELLLIMIT_ORDER,2)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[1],m_p_text_labels[12],20,470,PRICE,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[13],20+80+20,450,LOT,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[14],20+(80+20)*2,450,TP,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[15],20+(80+20)*3,450,SL,2)) return(false); //--- Switches for(int i=9; i<12; i++) if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*(i-9),470,2)) return(false); //--- Edits if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[3],20,505,2)) return(false); if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[5],20+(80+20),505,2)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[5],20+(80+20)*2,505,2)) return(false); if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[5],20+(80+20)*3,505,2)) return(false); //--- Sell Limit placing button if(!CreateSellButton(m_orders_windows[1],m_selllimit_execute,"Sell Limit ( 4 )",m_orders_windows[1].XSize()-(120+20),522,2)) return(false); //--- return(true); }
Como podemos ver na lista acima, ela usa os mesmos métodos de criação de elementos de interface da janela para trabalhar com ordens a mercado. No entanto, aqui aparece um novo método. Ele é CreatePriceEdit(),. Pelo nome podemos adivinhar facilmente que se trata de um campo de entrada para definir o preço de uma ordem pendente.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreatePriceEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number) { //--- Store the pointer to the main control text_edit.MainPointer(window); //--- Properties text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.StepValue(_Point); text_edit.SpinEditMode(true); text_edit.SetDigits(_Digits); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- Create a control element if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap)) return(false); //--- MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) text_edit.SetValue(string(tick.bid)); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(w_number,text_edit); return(true); }
Após sua implementação, o modelo inicial estará pronto, mas a interface ainda não ficará funcional. Ela precisa ser configurada. Aqui vamos seguir a mesma ordem de antes: configuramos os botões de alternância, associamos seu estado às propriedades dos campos de entrada e criamos funções para colocar ordens pendentes.
Vamos para o manipulador de eventos OnEvent(), seção Completando a interface, e definimos os nomes dos botões de alternância:
//--- UI creation completion if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI) { //--- SetButtonParam(m_switch_button[0],LOT); SetButtonParam(m_switch_button[1],POINTS); SetButtonParam(m_switch_button[2],POINTS); SetButtonParam(m_switch_button[3],LOT); SetButtonParam(m_switch_button[4],POINTS); SetButtonParam(m_switch_button[5],POINTS); //--- SetButtonParam(m_p_switch_button[0],LOT); SetButtonParam(m_p_switch_button[1],POINTS); SetButtonParam(m_p_switch_button[2],POINTS); SetButtonParam(m_p_switch_button[3],LOT); SetButtonParam(m_p_switch_button[4],POINTS); SetButtonParam(m_p_switch_button[5],POINTS); SetButtonParam(m_p_switch_button[6],LOT); SetButtonParam(m_p_switch_button[7],POINTS); SetButtonParam(m_p_switch_button[8],POINTS); SetButtonParam(m_p_switch_button[9],LOT); SetButtonParam(m_p_switch_button[10],POINTS); SetButtonParam(m_p_switch_button[11],POINTS); }
Vamos para a seção Evento de pressionamento de botão e implementamos um mecanismo para alternar o estado dos botões com mudança de nome:
//--- ButtonSwitch(m_p_switch_button[0],lparam,LOT,PERC_DEPO); ButtonSwitch(m_p_switch_button[1],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[2],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[3],lparam,LOT,PERC_DEPO); ButtonSwitch(m_p_switch_button[4],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[5],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[6],lparam,LOT,PERC_DEPO); ButtonSwitch(m_p_switch_button[7],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[8],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[9],lparam,LOT,PERC_DEPO); ButtonSwitch(m_p_switch_button[10],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[11],lparam,POINTS,PRICE);
Para vincular os estados dos botões de alternância aos campos de entrada e suas propriedades, criaremos os três novos métodos LotPendingSwitch(), TakePendingSwitch(), StopPendingSwitch().
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::LotPendingSwitch(long lparam) { for(int i=0; i<4; i++) { if(lparam==m_p_switch_button[3*i].Id()) { if(m_p_switch_button[3*i].IsPressed()) { m_lot_edit[i+2].SetDigits(0); m_lot_edit[i+2].StepValue(1); m_lot_edit[i+2].MaxValue(100); m_lot_edit[i+2].MinValue(1); m_lot_edit[i+2].SetValue(string(2)); m_lot_edit[i+2].GetTextBoxPointer().Update(true); } else { m_lot_edit[i+2].SetDigits(2); m_lot_edit[i+2].StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP)); m_lot_edit[i+2].MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN)); m_lot_edit[i+2].MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX)); m_lot_edit[i+2].SetValue(string(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN))); m_lot_edit[i+2].GetTextBoxPointer().Update(true); } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::TakePendingSwitch(long lparam) { for(int i=0; i<4; i++) { if(lparam==m_p_switch_button[3*i+1].Id()) { if(m_p_switch_button[3*i+1].IsPressed()) { MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) { m_tp_edit[i+2].SetDigits(_Digits); m_tp_edit[i+2].StepValue(_Point); m_tp_edit[i+2].SetValue(string(tick.ask)); m_tp_edit[i+2].GetTextBoxPointer().Update(true); } } else { m_tp_edit[i+2].SetDigits(0); m_tp_edit[i+2].StepValue(1); m_tp_edit[i+2].SetValue(string(150)); m_tp_edit[i+2].GetTextBoxPointer().Update(true); } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::StopPendingSwitch(long lparam) { for(int i=0; i<4; i++) { if(lparam==m_p_switch_button[3*i+2].Id()) { if(m_p_switch_button[3*i+2].IsPressed()) { MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) { m_sl_edit[i+2].SetDigits(_Digits); m_sl_edit[i+2].StepValue(_Point); m_sl_edit[i+2].SetValue(string(tick.bid)); m_sl_edit[i+2].GetTextBoxPointer().Update(true); } } else { m_sl_edit[i+2].SetDigits(0); m_sl_edit[i+2].StepValue(1); m_sl_edit[i+2].SetValue(string(150)); m_sl_edit[i+2].GetTextBoxPointer().Update(true); } } } }
Agora vamos chamá-los no manipulador de eventos da seção clicando no botão:
// --- Switch Lot/Percent of balance LotPendingSwitch(lparam); //--- Switch Take Profit Points/Price TakePendingSwitch(lparam); //--- Switch Stop Loss Points/Price StopPendingSwitch(lparam);
Compilamos o projeto e vemos o que acabamos de criar:
Fig. 11 Modos de alternância de campos de entrada para ordens pendentes.
Bem, os alternadores de modo do campo de entrada estão prontas e a alteração da propriedade também funciona. Assim, vamos continuar criando as funções necessárias para colocar ordens pendentes. Aqui também agimos por analogia com as ordens a mercado. Criamos quatro métodos para cada tipo.
bool SetBuyStopOrder(int id,long lparam); bool SetSellStopOrder(int id,long lparam); bool SetBuyLimitOrder(int id,long lparam); bool SetSellLimitOrder(int id,long lparam);
E vamos implementá-los. Definimos uma tecla de atalho para cada uma das ordens pendentes, além do evento de pressionamento no botão criado.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetBuyStopOrder(int id,long lparam) { if(!m_orders_windows[1].IsVisible()) return(false); if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buystop_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_1)) { //--- double lot; if(m_p_switch_button[0].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[2].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[2].GetValue())); //--- double pr=double(m_pr_edit[0].GetValue()); //--- if(m_p_switch_button[1].IsPressed() && m_p_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[2].GetValue()); double sl=double(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[1].IsPressed() && !m_p_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[2].GetValue()); int sl=int(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(m_p_switch_button[1].IsPressed() && !m_p_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[2].GetValue()); int sl=int(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[1].IsPressed() && m_p_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[2].GetValue()); double sl=double(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } } return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetSellStopOrder(int id,long lparam) { if(!m_orders_windows[1].IsVisible()) return(false); if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_sellstop_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_2)) { //--- double lot; if(m_p_switch_button[3].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_SELL,SymbolInfoDouble(Symbol(),SYMBOL_BID),StringToDouble(m_lot_edit[3].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[3].GetValue())); //--- double pr=double(m_pr_edit[1].GetValue()); //--- if(m_p_switch_button[4].IsPressed() && m_p_switch_button[5].IsPressed()) { double tp=double(m_tp_edit[3].GetValue()); double sl=double(m_sl_edit[3].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[4].IsPressed() && !m_p_switch_button[5].IsPressed()) { int tp=int(m_tp_edit[3].GetValue()); int sl=int(m_sl_edit[3].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(m_p_switch_button[4].IsPressed() && !m_p_switch_button[5].IsPressed()) { double tp=double(m_tp_edit[3].GetValue()); int sl=int(m_sl_edit[3].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[4].IsPressed() && m_p_switch_button[5].IsPressed()) { int tp=int(m_tp_edit[3].GetValue()); double sl=double(m_sl_edit[3].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } } return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetBuyLimitOrder(int id,long lparam) { if(!m_orders_windows[1].IsVisible()) return(false); if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buylimit_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_3)) { //--- double lot; if(m_p_switch_button[6].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[4].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[4].GetValue())); //--- double pr=double(m_pr_edit[2].GetValue()); //--- if(m_p_switch_button[7].IsPressed() && m_p_switch_button[8].IsPressed()) { double tp=double(m_tp_edit[4].GetValue()); double sl=double(m_sl_edit[4].GetValue()); if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[7].IsPressed() && !m_p_switch_button[8].IsPressed()) { int tp=int(m_tp_edit[4].GetValue()); int sl=int(m_sl_edit[4].GetValue()); if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(m_p_switch_button[7].IsPressed() && !m_p_switch_button[8].IsPressed()) { double tp=double(m_tp_edit[4].GetValue()); int sl=int(m_sl_edit[4].GetValue()); if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[7].IsPressed() && m_p_switch_button[8].IsPressed()) { int tp=int(m_tp_edit[4].GetValue()); double sl=double(m_sl_edit[4].GetValue()); if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } } return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetSellLimitOrder(int id,long lparam) { if(!m_orders_windows[1].IsVisible()) return(false); if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_selllimit_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_4)) { //--- double lot; if(m_p_switch_button[9].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_SELL,SymbolInfoDouble(Symbol(),SYMBOL_BID),StringToDouble(m_lot_edit[5].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[5].GetValue())); //--- double pr=double(m_pr_edit[3].GetValue()); //--- if(m_p_switch_button[10].IsPressed() && m_p_switch_button[11].IsPressed()) { double tp=double(m_tp_edit[5].GetValue()); double sl=double(m_sl_edit[5].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[10].IsPressed() && !m_p_switch_button[11].IsPressed()) { int tp=int(m_tp_edit[5].GetValue()); int sl=int(m_sl_edit[5].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(m_p_switch_button[10].IsPressed() && !m_p_switch_button[11].IsPressed()) { double tp=double(m_tp_edit[5].GetValue()); int sl=int(m_sl_edit[5].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[10].IsPressed() && m_p_switch_button[11].IsPressed()) { int tp=int(m_tp_edit[5].GetValue()); double sl=double(m_sl_edit[5].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } } return(false); }
Agora vamos para o manipulador de eventos e o complementamos no início usando a chamada dos métodos recém-criados:
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- SetBuyOrder(id,lparam); SetSellOrder(id,lparam); //--- SetBuyStopOrder(id,lparam); SetSellStopOrder(id,lparam); SetBuyLimitOrder(id,lparam); SetSellLimitOrder(id,lparam);
Assim fica concluída uma funcionalidade básica para trabalhar com todo tipo de ordens. Mas, como o propósito deste aplicativo é facilitar o trading manual, decidi adicionar uma função pequena para acelerar o posicionamento de ordens a mercado e pendentes. Seu princípio é o seguinte: ao definir os valores dos campos de entrada nos diferentes modos usando as setas para cima e para baixo à esquerda do campo, é alterado o número de acordo com o incremento especificado. Tomemos como exemplo quando o preço de uma ordem pendente é definido pressionando para cima e para baixo, nesse caso a alteração em EURUSD será de 1,08500 para 1,85001. Isso é lento e não é muito conveniente. Por isso, ao selecionar um campo de entrada para editar um valor com as teclas de atalho Para cima/Para baixo, adicionamos a alteração do valor num incremento multiplicado por 10. Para fazer isso, criamos o método ArrowSwitch().
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::ArrowSwitch(long lparam) { //--- Prices for(int i=0; i<4; i++) { if(m_pr_edit[i].GetTextBoxPointer().TextEditState()) { if(lparam==KEY_UP) { //--- Get the new value double value=StringToDouble(m_pr_edit[i].GetValue())+m_pr_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_pr_edit[i].SetValue(DoubleToString(value),false); } else if(lparam==KEY_DOWN) { //--- Get the new value double value=StringToDouble(m_pr_edit[i].GetValue())-m_pr_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_pr_edit[i].SetValue(DoubleToString(value),false); } } } //--- Lot for(int i=0; i<6; i++) { if(m_lot_edit[i].GetTextBoxPointer().TextEditState()) { if(lparam==KEY_UP) { //--- Get the new value double value=StringToDouble(m_lot_edit[i].GetValue())+m_lot_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_lot_edit[i].SetValue(DoubleToString(value),false); } else if(lparam==KEY_DOWN) { //--- Get the new value double value=StringToDouble(m_lot_edit[i].GetValue())-m_lot_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_lot_edit[i].SetValue(DoubleToString(value),false); } } } //--- Take Profit, Stop Loss for(int i=0; i<6; i++) { //--- if(m_tp_edit[i].GetTextBoxPointer().TextEditState()) { if(lparam==KEY_UP) { //--- Get the new value double value=StringToDouble(m_tp_edit[i].GetValue())+m_tp_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_tp_edit[i].SetValue(DoubleToString(value),false); } else if(lparam==KEY_DOWN) { //--- Get the new value double value=StringToDouble(m_tp_edit[i].GetValue())-m_tp_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_tp_edit[i].SetValue(DoubleToString(value),false); } } //--- if(m_sl_edit[i].GetTextBoxPointer().TextEditState()) { if(lparam==KEY_UP) { //--- Get the new value double value=StringToDouble(m_sl_edit[i].GetValue())+m_sl_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_sl_edit[i].SetValue(DoubleToString(value),false); } else if(lparam==KEY_DOWN) { //--- Get the new value double value=StringToDouble(m_sl_edit[i].GetValue())-m_sl_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_sl_edit[i].SetValue(DoubleToString(value),false); } } } }
E vamos chamá-lo no manipulador de eventos na seção Pressionamento de tecla.
//--- Keypress if(id==CHARTEVENT_KEYDOWN) { //--- ArrowSwitch(lparam); ....
Na Fig. 12, você pode observar a diferença entre a velocidade de alteração do valor editado para o necessário.
Fig. 12 Mudança rápida de valores de campos de entrada usando teclas de atalho.
Assim já fica implementado o conjunto de ferramentas para a colocação de ordens a mercado e pendentes, resta ir para a janela principal do aplicativo e complementá-lo com ações para as ordens existentes, como mostrado na primeira figura no início do artigo. Serão três ações: fechar todas as ordens lucrativas a mercado, fechar todas as ordens não lucrativas e excluir todas as ordens pendentes atuais. Isso só se aplica a ordens com um número mágico predefinido nas configurações e que, além disso, foram feitas por nosso aplicativo. Também deve ser lembrado que ao colocar ordens com o número mágico atual e, depois, alterar este último nas configurações do EA, as ordens existentes segundo o número anterior serão ignoradas como se não estivessem relacionadas ao aplicativo atual.
Embarcamos na implementação das ações declaradas. Criamos três novos métodos:
void CloseAllMarketProfit(int id,long lparam); void CloseAllMarketLoss(int id,long lparam); void DeleteAllPending(int id,long lparam);
E implementamos cada um deles.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::CloseAllMarketProfit(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[2].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_C)) { //--- declare the request and the result MqlTradeRequest request; MqlTradeResult result; int total=PositionsTotal(); // the number of open positions //--- iterate over open positions for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // the number of decimal places ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber double volume=PositionGetDouble(POSITION_VOLUME); // position volume ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double profit=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- if MagicNumber matches if(magic==m_magic_number) if(position_symbol==Symbol()) if(profit+swap>0) { //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action =TRADE_ACTION_DEAL; // trading operation type request.position =position_ticket; // position ticket request.symbol =position_symbol; // symbol request.volume =volume; // position volume request.deviation=5; // allowable price deviation request.magic =m_magic_number; // position MagicNumber //--- Set order price and type depending on the position type if(type==POSITION_TYPE_BUY) { request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID); request.type =ORDER_TYPE_SELL; } else { request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK); request.type =ORDER_TYPE_BUY; } //--- sending a request if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::CloseAllMarketLoss(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[3].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_D)) { //--- declare the request and the result MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); int total=PositionsTotal(); // the number of open positions //--- iterate over open positions for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // the number of decimal places ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber double volume=PositionGetDouble(POSITION_VOLUME); // position volume ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double profit=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- if MagicNumber matches if(magic==m_magic_number) if(position_symbol==Symbol()) if(profit+swap<0) { //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action =TRADE_ACTION_DEAL; // trading operation type request.position =position_ticket; // position ticket request.symbol =position_symbol; // symbol request.volume =volume; // position volume request.deviation=5; // allowable price deviation request.magic =m_magic_number; // position MagicNumber //--- Set order price and type depending on the position type if(type==POSITION_TYPE_BUY) { request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID); request.type =ORDER_TYPE_SELL; } else { request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK); request.type =ORDER_TYPE_BUY; } //--- sending a request if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::DeleteAllPending(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[4].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_R)) { //--- declare and initialize the trade request and result of trade request MqlTradeRequest request; MqlTradeResult result; //--- iterate over all placed pending orders for(int i=OrdersTotal()-1; i>=0; i--) { ulong order_ticket=OrderGetTicket(i); // order ticket ulong magic=OrderGetInteger(ORDER_MAGIC); // order MagicNumber //--- if MagicNumber matches if(magic==m_magic_number) { //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action= TRADE_ACTION_REMOVE; // trading operation type request.order = order_ticket; // order ticket //--- sending a request if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } }
Repare que os métodos criados também estão submetidos à regra para realizar uma ação com base em dois eventos diferentes: botão na janela principal do aplicativo e tecla de atalho. Todas as três funções serão chamadas no manipulador de eventos, fora de qualquer seção.
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- SetBuyOrder(id,lparam); SetSellOrder(id,lparam); //--- SetBuyStopOrder(id,lparam); SetSellStopOrder(id,lparam); SetBuyLimitOrder(id,lparam); SetSellLimitOrder(id,lparam); //--- CloseAllMarketProfit(id,lparam); CloseAllMarketLoss(id,lparam); //--- DeleteAllPending(id,lparam);
Assim fica concluído o desenvolvimento da funcionalidade básica para trabalho manual rápido. O aplicativo criado pode ser visto no vídeo abaixo.
Fim do artigo
No final do artigo é anexado um arquivo com todos os arquivos listados, classificados por pastas. Por isso, para um correto funcionamento, basta colocar a pasta MQL5 na raiz do terminal. E para encontrar a raiz do terminal onde está localizada a pasta MQL5, no MetaTrader 5 é necessário pressionar a combinação de teclas Ctrl+Shift+D ou usar o menu contextual, conforme mostrado na Figura 13 abaixo.
Fig. 13 Busca da pasta MQL5 na raiz do terminal MetaTrader 5
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/7892
- 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