English Русский 中文 Español Deutsch 日本語
Sistema de notificações de voz de eventos e sinais de negociação

Sistema de notificações de voz de eventos e sinais de negociação

MetaTrader 5Exemplos | 24 novembro 2020, 09:26
1 635 0
Alexander Fedosov
Alexander Fedosov

Sumário

Introdução

O terminal de negociação MetaTrader 5 possui um sistema para configurar notificações sonoras, que inclui 11 eventos para os quais podemos por um aviso de áudio. No entanto, ao usar o terminal, existem muitas outras situações em que seria útil receber informações sonoras, como o aparecimento de um sinal de um sistema de negociação ou informações de que uma posição colocada é aberta, fechada ou alterada por um Expert Advisor ativo. Hoje em dia, os assistentes de voz ocupam um papel proeminente na vida humana, seja um navegador, um mecanismo de busca por voz ou um tradutor. Por isso, essa ideia pode ser usada ao negociar por meio do terminal MetaTrader 5. Neste artigo, tentarei desenvolver um sistema simples e compreensível de notificações de voz para diferentes eventos, condições de mercado ou sinais de sistemas de negociação.


Desenvolvendo o sistema de notificações de voz

Antes de embarcarmos na criação do sistema, gostaria de ressaltar o fato de que os eventos que escolhi para a implementação de notificações de voz são apenas um conjunto padrão para demonstração. Por esse motivo, para aqueles para quem não for suficiente, o sistema permitirá adicionar eventos e notificações de voz próprios. Mesmo para pessoas com conhecimento modesto de MQL5, será simples de entender como estender o sistema de acordo com suas necessidades.

O sistema é implementado como uma classe CSoundsLib no arquivo de inclusão. Portanto, na pasta MQL5/Include vamos criar a pasta SoundsLib e nesta, o arquivo SoundsLib.mqh. Antes de criar a classe, apresentaremos duas enumerações que serão necessárias posteriormente ao trabalhar com notificações de voz. A primeira delas é LANGUAGE, usada para selecionar o idioma de notificação. No sistema, serão dois idiomas, russo e inglês.

//+------------------------------------------------------------------+
//| Enumeration for switching the notification language              |
//+------------------------------------------------------------------+
enum LANGUAGE
{
   RUSSIAN,       // Russian
   ENGLISH        // English
};

A segunda enumeração conterá o conjunto de eventos que escolhi como demonstração e, mais adiante no artigo, será mostrado como integrá-los em sistemas prontos de diferentes tipos, desde indicadores e EAs a aplicativos como ferramentas de negociação rápida. Assim, a enumeração será chamada de MESSAGE:

//+------------------------------------------------------------------+
//| List of voice alerts                                             |
//+------------------------------------------------------------------+
enum MESSAGE
{
   STATUS_ON,                          // Status of enabled voice alerts
   SIGNAL_BUY,                         // A Buy signal
   SIGNAL_SELL,                        // A Sell signal
   BUY_ORDER_SET,                      // A Buy order has been placed
   SELL_ORDER_SET,                     // A Sell order has been placed
   BUYLIMIT_ORDER_SET,                 // A Limit Buy order has been placed
   BUYSTOP_ORDER_SET,                  // A Stop Buy order has been placed
   SELLLIMIT_ORDER_SET,                // A Limit Sell order has been placed
   SELLSTOP_ORDER_SET,                 // A Stop Sell order has been placed
   BUYLIMIT_ORDER_DELETE,              // A Limit Buy order has been deleted
   BUYSTOP_ORDER_DELETE,               // A Stop Buy order has been deleted
   SELLLIMIT_ORDER_DELETE,             // A Limit Sell order has been deleted
   SELLSTOP_ORDER_DELETE,              // A Stop Sell order has been deleted
   BUY_ORDER_CLOSE_PROFIT,             // A Buy order has closed with a profit
   BUY_ORDER_CLOSE_LOSS,               // A Buy order has closed with a loss
   SELL_ORDER_CLOSE_PROFIT,            // A Sell order has closed with a profit
   SELL_ORDER_CLOSE_LOSS,              // A Sell order has closed with a loss
   BUY_ORDER_CLOSE_TP,                 // A Buy order has been closed by Take Profit
   BUY_ORDER_CLOSE_SL,                 // A Buy order has been closed by Stop Loss
   SELL_ORDER_CLOSE_TP,                // A Sell order has been closed by Take Profit
   SELL_ORDER_CLOSE_SL,                // A Sell order has been closed by Stop Loss
   MARKET_CLOSE,                       // Market is closed
   AUTO_TRADING_ON,                    // Automated trading is allowed
   AUTO_TRADING_OFF,                   // Automated trading is prohibited
};

O conjunto básico contém 24 notificações. A maioria deles é associado ao trabalho e estado de posições abertas ou ordens pendentes. E também estão presentes aqueles que irão dar som ao ambiente de negociação. Já os três últimos são comuns. A notificação do status do sistema de notificação de voz habilitado, bem como as mensagens sobre a presença de um sinal de compra ou venda são úteis ao trabalhar com sistemas manuais ou semiautomáticos de EAs ou com diferentes sinais vindos de indicadores, tanto simples quanto como parte de uma estratégia de negociação.

Agora criamos a classe CSoundsLib, escrevemos nela os métodos necessários para trabalharmos.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CSoundsLib
{
private:
   LANGUAGE          m_language;
   bool              m_activity_status;
public:
                     CSoundsLib(void); 
                    ~CSoundsLib(void);
   //--- Set the notification language
   void              Language(LANGUAGE lang);
   //--- Set/get the status of the voice alerts system
   void              IsActive(bool flag);
   bool              IsActive(void);
   //--- Play the specified notification
   bool              Message(MESSAGE msg);
};

Na seção privada estão m_language e m_activity_status. Elas são duas variáveis necessárias para os métodos Language() e IsActive() criados abaixo. Estes são usados para definir o idioma das notificações por voz e receber/definir a atividade do próprio sistema. Sua implementação é mostrada na listagem abaixo:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CSoundsLib::Language(LANGUAGE lang)
{
   m_language=lang;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CSoundsLib::IsActive(void)
{
   return(m_activity_status);
}

Outro método é Message() que reproduz a própria notificação, definida a partir da enumeração MESSAGE. A implementação deste método também é fácil de entender:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool  CSoundsLib::Message(MESSAGE msg)
{
   if(!m_activity_status)
      return(false);
   string name=(m_language==RUSSIAN ? EnumToString(msg)+"_RU" : EnumToString(msg)+"_EN");
   if(PlaySound("\\Files\\SoundsLib\\"+name+".wav"))
      return(true);
   else
   {
      if(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian")
         Print("Файл не найден");
      else
         Print("File not found");
      return(false);
   }
}

Gostaria de ressaltar vários pontos importantes, pois entendê-los afetará diretamente o quão exato resulte expandir este sistema usando suas próprias notificações de voz. O primeiro é o local correto para a instalação dos arquivos de som, ou seja, por padrão eles devem estar na pasta MQL5/Files/SoundsLib. Além disso, devemos criar a pasta SoundsLib. O segundo é os nomes e o formato dos arquivos de som que serão colocados na pasta criada. Vejamos que essas linhas de código adicionam o sufixo _RU ou _EN à sua enumeração MESSAGE no final. Por isso, o nome do arquivo em questão, por exemplo, para a notificação de voz sobre o sinal de compra SIGNAL_BUY, será associado a dois arquivos de som: SIGNAL_BUY _RU e SIGNAL_BUY_EN para sonorização em russo e inglês. Além disso, devemos nos lembrar de que a função do sistema PlaySound() só pode reproduzir um arquivo no formato *.WAV e, portanto, os nomes completos dos arquivos em nosso exemplo com extensões na pasta SoundsLib vai ficar assim:

Fig.1 Nome completo e extensão do arquivo de som.

Portanto, os 24 eventos definidos na enumeração MESSAGE corresponderão a 48 arquivos de som. 2 para cada evento em dois idiomas. A seguir, mostrarei minha própria maneira de criar notificações de voz, mas todos são livres para usar a que mais gosta. Para o artigo, usei o serviço gratuito de conversão de mensagens de texto em arquivos de som.

Fig. 2 Serviço de conversão de texto em arquivo de som.

Essa ferramenta possui funcionalidade suficiente para realizar a tarefa requerida. Pode-se escolher o idioma do mecanismo de voz e os tipos com o formato de que precisamos. No entanto, não há formato WAV para o mecanismo de voz em inglês, portanto, por meio de um conversor online ou de um software para trabalhar com arquivos de som, você pode converter facilmente o formato mp3 para wav. Com este serviço, iremos receber todos os arquivos necessários para o nosso sistema e colocá-los numa pasta emMQL5\Files\SoundsLib com o formato e nomes corretos de acordo com a enumeração MESSAGE e sufixos de idioma. Aqui está a lista que obtive:

Fig. 3 Lista completa de arquivos de som para notificações de voz.

Para uma compreensão mais clara, a seguir darei um guia passo a passo sobre como criar notificações de voz próprias.

Passo 1. Adicionando seu evento de voz ao sistema.

Para fazer isso, vamos para o arquivo SoundsLib.mqh e encontramos a enumeração MESSAGE. Devemos adicionar seu evento com um nome significativo. Um exemplo de nomes é mostrado na Fig. 3 e acima no código.

Passo 2. Criando o arquivo de som da futura notificação de voz.

Vamos para o serviço (ou o que você quiser) e personalizamos os parâmetros desejados (mostrados na Fig. 2) e salvamos o arquivo em formato WAV na pasta MQL5\Files\SoundsLib com o nome "O nome do seu evento na enumeração MESSAGE"+_ RU(_EN) dependendo de se pertence ao idioma russo ou inglês. Se você fez tudo corretamente, o novo arquivo de som será associado ao novo evento adicionado por você no passo 1, e estará pronto para ser usado.


Aplicação prática em indicadores

Agora vamos ver como isso funciona através de vários exemplos. Vamos criar um indicador composto com base nos dois sinais indicadores descritos na tabela abaixo:

Parâmetros Descrição
Indicador utilizado ADXCloud
Indicador utilizado ColorZerolagRVI
Timeframe Qualquer um
Condições de compra ADXCloud é verde, ColorZerolagRVI muda de vermelho para verde.
Condições de venda ADXCloud é vermelho, ColorZerolagRVI muda de verde para vermelho.

Na Fig. 4 são mostrados exemplos de entrada com base nos dois sinais desses indicadores. Embora sejam bastante simples, faremos com eles um indicador de sinal composto que mostrará no gráfico usando setas os pontos de entrada no mercado. 

Fig.4 Condições de entrada com base nos sinais desses indicadores.

//+------------------------------------------------------------------+
//|                                                      Example.mq5 |
//|                                                         Alex2356 |
//|                           https://www.mql5.com/en/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Alex2356"
#property link      "https://www.mql5.com/en/users/alex2356"
#property version   "1.00"
#property indicator_chart_window
//--- two buffers are used for calculating and drawing the indicator
#property indicator_buffers 2
//--- used graphic constructions
#property indicator_plots   2
#property indicator_label1  "Buy Signal"
#property indicator_type1   DRAW_ARROW
//---
#property indicator_label2  "Sell Signal"
#property indicator_type2   DRAW_ARROW
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
input group "ADX Cloud Parameters"
input int                  ADXPeriod         =  8;
input double               Alpha1            =  0.25;
input double               Alpha2            =  0.25;
input group "RVI Color Parameters"
input uint                 Smoothing         =  15;
//----
input double               Weight1           =  0.05;
input int                  RVI_period1       =  8;
//----
input double               Weight2           = 0.10;
input int                  RVI_period2       =   21;
//----
input double               Weight3           = 0.16;
input int                  RVI_period3       =   34;
//----
input double               Weight4           = 0.26;
input int                  RVI_period4       =   55;
//----
input double               Weight5           = 0.43;
input int                  RVI_period5       =   89;
//---
double BuySignal[],SellSignal[],ADXCloud[],FastRVI[],SlowRVI[];
int ADX_Handle,RVI_Hadnle,min_rates_total;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
//---
   SetIndexBuffer(0,BuySignal,INDICATOR_DATA);
   SetIndexBuffer(1,SellSignal,INDICATOR_DATA);
//---
   PlotIndexSetInteger(0,PLOT_ARROW,233);
   PlotIndexSetInteger(1,PLOT_ARROW,234);
//---
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,clrDodgerBlue);
   PlotIndexSetInteger(1,PLOT_LINE_COLOR,clrCrimson);
//---
   ArraySetAsSeries(SellSignal,true);
   ArraySetAsSeries(BuySignal,true);
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,EMPTY_VALUE);
   PlotIndexSetInteger(0,PLOT_ARROW_SHIFT,20);
   PlotIndexSetInteger(1,PLOT_ARROW_SHIFT,-20);
//---
   ADX_Handle=iCustom(Symbol(),PERIOD_CURRENT,"adxcloud",ADXPeriod,Alpha1,Alpha2);
   if(ADX_Handle==INVALID_HANDLE)
   {
      Print(" Failed to get the indicator handle");
      return(INIT_FAILED);
   }
//---
   RVI_Hadnle=iCustom(Symbol(),PERIOD_CURRENT,"colorzerolagrvi",
                      Smoothing,
                      Weight1,RVI_period1,
                      Weight2,RVI_period2,
                      Weight3,RVI_period3,
                      Weight4,RVI_period4,
                      Weight5,RVI_period5
                     );
   if(RVI_Hadnle==INVALID_HANDLE)
   {
      Print(" Failed to get the indicator handle");
      return(INIT_FAILED);
   }
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
//--- c
   if(BarsCalculated(ADX_Handle)<rates_total || BarsCalculated(RVI_Hadnle)<rates_total || rates_total<min_rates_total)
      return(0);
//--- 
   int limit,to_copy,i;
//--- 
   ArraySetAsSeries(ADXCloud,true);
   ArraySetAsSeries(FastRVI,true);
   ArraySetAsSeries(SlowRVI,true);
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
//--- 
   if(prev_calculated>rates_total || prev_calculated<=0) 
      limit=rates_total-2;
   else
      limit=rates_total-prev_calculated; 
   to_copy=limit+2;
//---
   if(CopyBuffer(ADX_Handle,0,0,to_copy,ADXCloud)<=0)
      return(0);
//---
   if(CopyBuffer(RVI_Hadnle,0,0,to_copy,FastRVI)<=0)
      return(0);
   if(CopyBuffer(RVI_Hadnle,1,0,to_copy,SlowRVI)<=0)
      return(0);
//--- 
   for(i=limit-1; i>=0 && !IsStopped(); i--)
   {
      if(ADXCloud[i+1]>0 && FastRVI[i+1]>SlowRVI[i+1] && FastRVI[i+2]<SlowRVI[i+2])
      {
         BuySignal[i]=low[i];
         SellSignal[i]=EMPTY_VALUE;
      }
      else if(ADXCloud[i+1]<0 && FastRVI[i+1]<SlowRVI[i+1] && FastRVI[i+2]>SlowRVI[i+2])
      {
         SellSignal[i]=high[i];
         BuySignal[i]=EMPTY_VALUE;
      }
      else
      {
         BuySignal[i]=EMPTY_VALUE;
         SellSignal[i]=EMPTY_VALUE;
      }
   }
//--- return value of prev_calculated for the next call
   return(rates_total);
}
//+------------------------------------------------------------------+

Como resultado teremos a implementação mostrada na Fig. 5, e agora temos que implementar um sistema de notificação de voz.

Fig.5 Indicador de seta baseado em dois indicadores.

Primeiro, vamos anexar o arquivo SoundsLib.mqh ao indicador:

#include <SoundsLib/SoundsLib.mqh>

Criamos uma instância da classe do sistema de notificação por voz:

CSoundsLib Notify;

Na função de inicialização OnInit() definimos o idioma das notificações de voz, vou colocar em inglês. Embora, se você usar o inglês para as notificações, podemos dispensar a instalação do idioma, uma vez que é definido por padrão, mas, para entender melhor isto, iremos instalá-lo.

Notify.Language(ENGLISH);

Como o indicador de seta mostra apenas pontos de entrada no mercado ou sinais de compra/venda, a partir da lista de MESSAGE usaremos estas duas notificações de voz: 

   SIGNAL_BUY,                         // A Buy signal
   SIGNAL_SELL,                        // A Sell signal

No entanto, ao introduzir notificações de voz no indicador criado, é necessário que elas não venham com base em todos os sinais do histórico, mas apenas para a barra atual, portanto, modificamos o ciclo de busca de sinal da seguinte forma:

//--- 
   for(i=limit-1; i>=0 && !IsStopped(); i--)
   {
      if(ADXCloud[i+1]>0 && FastRVI[i+1]>SlowRVI[i+1] && FastRVI[i+2]<SlowRVI[i+2])
      {
         BuySignal[i]=low[i];
         SellSignal[i]=EMPTY_VALUE;
         if(i==0)
            Notify.Message(SIGNAL_BUY);
      }
      else if(ADXCloud[i+1]<0 && FastRVI[i+1]<SlowRVI[i+1] && FastRVI[i+2]>SlowRVI[i+2])
      {
         SellSignal[i]=high[i];
         BuySignal[i]=EMPTY_VALUE;
         if(i==0)
            Notify.Message(SIGNAL_SELL);
      }
      else
      {
         BuySignal[i]=EMPTY_VALUE;
         SellSignal[i]=EMPTY_VALUE;
      }
   }

Aqui nós verificamos se há um sinal na barra zero e, se estiver presente, notificamos o usuário do terminal sobre isso


Aplicação prática em EAs

Para o indicador, a integração de notificações por voz é limitada a apenas dois tipos, embora nada impeça você de adicionar suas próprias, por exemplo, para osciladores, uma notificação sobre a entrada na zona de sobrecompra, quebra de canal para bandas de Bollinger, etc. O Expert Advisor pode usar muitas mais notificações diferentes a partir do conjunto combinável. Por isso, criaremos um robô de negociação de teste que avisará não só que encontrou um sinal para entrar no mercado, mas também comentará sobre suas ações futuras, por exemplo, acerca de com que tipo de posição entrará no mercado. Primeiro, vamos definir a estratégia de entrada do futuro Expert Advisor. 

Parâmetros Descrição
Indicador utilizado ColorStDev
Indicador utilizado Três nível Tirone
Timeframe Qualquer um
Condições de compra O histograma ColorStdDev é vermelho (tendência forte), enquanto o preço atual deve estar acima do nível superior de Tirone.
Condições de venda O histograma ColorStdDev é vermelho (tendência forte), enquanto o preço atual deve estar abaixo do nível inferior de Tirone.
Condições de saída   Take-Profit/Stop-Loss

Visualmente, os pontos de entrada no mercado serão parecidos com a Fig. 6 dada como exemplo.

Fig. 6 Exemplos de entrada no mercado usando esta estratégia.

Agora vamos implementar essa estratégia no MetaTrader 5, e usaremos notificações de voz para alguns eventos. 

//+------------------------------------------------------------------+
//|                                                  VoiceNotify.mq5 |
//|                                                         Alex2356 |
//|                           https://www.mql5.com/en/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Alex2356"
#property link      "https://www.mql5.com/en/users/alex2356"
#property version   "1.00"
#include <SoundsLib/SoundsLib.mqh>
#include <DoEasy25/Engine.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
input uint                 InpStopLoss          =  150;              // Stop Loss, in pips
input uint                 InpTakeProfit        =  250;              // Take Profit, in pips
input double               InpLot               =  0.1;              // Take Profit, in pips
input ulong                InpDeviation         =  10;               // Deviation
input int                  InpMagic             =  2356;             // Magic number
input LANGUAGE             NotifyLanguage       =  ENGLISH;          // Notification Language
//--- ColorStDev indicator parameters
input int                  StDevPeriod          =  12;               // Smoothing period StDev
input ENUM_MA_METHOD       MA_Method            =  MODE_EMA;         // Histogram smoothing method
input ENUM_APPLIED_PRICE   applied_price        =  PRICE_CLOSE;      // Applied price
input int                  MaxTrendLevel        =  90;               // Maximum trend level
input int                  MiddLeTrendLevel     =  50;               // Middle trend level
input int                  FlatLevel            =  20;               // Flat level
//--- Tirone Levels indicator parameters
input int                  TironePeriod         =  13;               // Tirone Period
//---
CEngine trade;
CSoundsLib notify;
int Handle1,Handle2;
double stdev[],tirone_b[],tirone_s[];
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()


{
//---
   if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
      notify.Message(AUTO_TRADING_OFF);
//---
   OnInitTrading();
//--- Get the handle of the ColorStDev indicator
   Handle1=iCustom(Symbol(),PERIOD_CURRENT,"ArticleVoiceNotify\\colorstddev",
                   StDevPeriod,
                   MA_Method,
                   applied_price,
                   MaxTrendLevel,
                   MiddLeTrendLevel,
                   FlatLevel
                  );
   if(Handle1==INVALID_HANDLE)
   {
      Print("Failed to get colorstddev handle");
      Print("Handle = ",Handle1,"  error = ",GetLastError());
      return(INIT_FAILED);
   }
//--- Getting the handle of the Tirone Levels indicator
   Handle2=iCustom(Symbol(),PERIOD_CURRENT,"ArticleVoiceNotify\\tirone_levels_x3",TironePeriod,0);
   if(Handle2==INVALID_HANDLE)
   {
      Print("Failed to get Tirone Levels handle");
      Print("Handle = ",Handle2,"  error = ",GetLastError());
      return(INIT_FAILED);
   }
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
//--- If there are no market positions
   if(ExistPositions(Symbol(),-1,InpMagic)<1)
   {
      //--- Getting data for calculations
      if(!GetIndValue())
         return;
      //--- Open an order if there is a buy signal
      if(BuySignal())
      {
         notify.Message(SIGNAL_BUY);
         if(trade.OpenBuy(InpLot,Symbol(),InpMagic,InpStopLoss,InpTakeProfit))
         {
            Sleep(1400);
            notify.Message(BUY_ORDER_SET);
         }
      }
      //--- Opening an order if there is a sell signal
      if(SellSignal())
      {
         notify.Message(SIGNAL_SELL);
         if(trade.OpenSell(InpLot,Symbol(),InpMagic,InpStopLoss,InpTakeProfit))
         {
            Sleep(1400);
            notify.Message(SELL_ORDER_SET);
         }
      }
   }
}
//+------------------------------------------------------------------+
//| Buy conditions                                                   |
//+------------------------------------------------------------------+
bool BuySignal()
{
   return(tirone_b[1]>iClose(Symbol(),PERIOD_CURRENT,1) && stdev[0]>FlatLevel)?true:false;
}
//+------------------------------------------------------------------+
//| Sell conditions                                                  |
//+------------------------------------------------------------------+
bool SellSignal()
{
   return(tirone_b[1]<iClose(Symbol(),PERIOD_CURRENT,1) && stdev[0]>FlatLevel)?true:false;
}
//+------------------------------------------------------------------+
//| Getting the current values of indicators                         |
//+------------------------------------------------------------------+
bool GetIndValue()
{
   return(CopyBuffer(Handle1,0,0,2,stdev)<=0    ||
          CopyBuffer(Handle2,0,0,2,tirone_b)<=0 ||
          CopyBuffer(Handle2,2,0,2,tirone_s)<=0
         )?false:true;
}
//+----------------------------------------------------------------------------+
//|  Returns the number of open orders                                         |
//+----------------------------------------------------------------------------+
//|  Parameters:                                                               |
//|    op - operation                  (-1   - any position)                   |
//|    mn - MagicNumber                (-1   - any magic number)               |
//+----------------------------------------------------------------------------+
int ExistPositions(string sy,int op=-1,int mn=-1)
{
   int pos=0;
   uint total=PositionsTotal();
//---
   for(uint i=0; i<total; i++)
   {
      if(SelectByIndex(i))
         if(PositionGetString(POSITION_SYMBOL)==sy)
            if(op<0 || PositionGetInteger(POSITION_TYPE)==op)
               if(mn<0 || PositionGetInteger(POSITION_MAGIC)==mn)
                  pos++;
   }
   return(pos);
}
//+------------------------------------------------------------------+
//| Select a position on the index                                   |
//+------------------------------------------------------------------+
bool SelectByIndex(const int index)
{
   ENUM_ACCOUNT_MARGIN_MODE margin_mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
//---
   if(margin_mode==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
   {
      ulong ticket=PositionGetTicket(index);
      if(ticket==0)
         return(false);
   }
   else
   {
      string name=PositionGetSymbol(index);
      if(name=="")
         return(false);
   }
//---
   return(true);
}
//+------------------------------------------------------------------+
//| Trading Environment Initialization                               |
//+------------------------------------------------------------------+
void 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
   trade.SetUsedSymbols(array_used_symbols);
//--- Pass all existing collections to the trading class
   trade.TradingOnInit();
   trade.TradingSetMagic(InpMagic);
   trade.TradingSetLogLevel(LOG_LEVEL_ERROR_MSG);
//--- Set synchronous passing of orders for all used symbols
   trade.TradingSetAsyncMode(false);
//--- Set correct order expiration and filling types to all trading objects
   trade.TradingSetCorrectTypeExpiration();
   trade.TradingSetCorrectTypeFilling();
}
//+------------------------------------------------------------------+

Vamos considerar esta lista com mais detalhes a nível de uso de notificações. Na inicialização do robô de negociação, foi utilizada uma verificação de se era permitida a negociação usando sistemas automáticos no terminal. Se esta opção for desativada, o usuário será avisado pela notificação de voz correspondente. Em seguida, na função OnTick() quando é encontrado um determinado sinal de negociação, o Expert Advisor avisa que foi encontrado um sinal de compra ou venda, respectivamente. Ocorre a tentativa de colocar uma posição de acordo com o sinal e, se tudo correr bem, outra notificação de voz avisará que ela foi posicionada no mercado.

Essas notificações são muito mais eficazes a nível de entrega de informações ao usuário do terminal, porque nem sempre é possível ver atempadamente a notificação de texto necessária de um EA na guia Experts ou em outro local. Quanto aos alertas, os sons que vem com a plataforma podem ser diferentes para diferentes indicadores e EAs. Quanto às notificações por voz, tudo é claro e simples de compreender, pois o evento necessário é entregue por fala.


Aplicação prática em ferramentas de negociação rápida

Nos meus dois artigos anteriores, desenvolvi uma ferramenta para traders que negociam manualmente, ou seja, que procuram sinais para entrar no mercado, definem ordens, acompanham e fecham. Como exemplo, no âmbito deste artigo, gostaria de complementar esse aplicativo com um sistema de notificação de voz. E, assim, mostrar que não será difícil adicioná-lo a uma ferramenta deste tipo. Vou tomar como base o projeto anexado no final deste artigo. Primeiro, vamos definir uma lista de ações e eventos para as quais serão incorporadas notificações de voz.

  • Abertura bem-sucedida de uma posição de compra ou venda.
  • Colocação bem-sucedida de ordens pendentes e sua remoção.
  • Verificação de se é possível fazer ordens através de um EA.

Antes de começar a integrar notificações de voz, vamos anexar a biblioteca a este projeto. Para fazer isso, abrimos o arquivo Program.mqh e no começo vamos fazer isso.

//+------------------------------------------------------------------+
//|                                                      Program.mqh |
//|                                                         Alex2356 |
//|                    https://www.mql5.com/en/users/alex2356/       |
//+------------------------------------------------------------------+
#include <EasyAndFastGUI\WndEvents.mqh>
#include <DoEasy25\Engine.mqh>
#include "Defines.mqh"
#include <SoundsLib/SoundsLib.mqh>

Agora vamos para a seção privada da classe CFastTrading e criamos uma variável-instância da classe CSoundsLib.

   //---
   CSoundsLib        m_notify;

Também precisamos definir dois novos parâmetros na própria caixa de ferramentas para habilitar/desabilitar notificações de voz e selecionar idioma. Vamos para SimpleTrading.mq5 e na seção Parâmetros de Entrada do EA iremos escrever novos:

//+------------------------------------------------------------------+
//| 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
//---
input bool                 UseVoiceNotify    =  true;                // Use Voice Notify
input LANGUAGE             NotifyLanguage    =  ENGLISH;             // Notification Language

E para passá-los para uma instância da classe CSoundsLib m_notify na seção pública da classe base CFastTrading, vamos criar dois métodos e implementá-los:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::SetNotifyLanguage(LANGUAGE lang)
{
   m_notify.Language(lang);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::UseVoiceNotify(bool state)
{
   m_notify.IsActive(state);
}
//+------------------------------------------------------------------+

Agora vamos aplicá-los na função OnInit() do arquivo SimpleTrading.mq5 e passamos os parâmetros de entrada para os métodos que acabamos de criar.

//+------------------------------------------------------------------+
//| 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);
   program.UseVoiceNotify(UseVoiceNotify);
   program.SetNotifyLanguage(NotifyLanguage);
//--- Set up the trading panel
   if(!program.CreateGUI())
   {
      Print(__FUNCTION__," > Failed to create graphical interface!");
      return(INIT_FAILED);
   }
   program.OnInitEvent();
//---
   return(INIT_SUCCEEDED);
}

Assim, configuramos os principais parâmetros de entrada do sistema de notificação de voz. Agora encontramos métodos para definir posições de compra e venda a mercado. Na classe base CFastTrading, eles são SetBuyOrder() e SetSellOrder(). Vamos para o corpo do método para colocar uma posição de compra e nos locais onde ocorre uma verificação de abertura bem-sucedida de posição, escreveremos a notificação de voz correspondente BUY_ORDER_SET:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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))
         {
            m_notify.Message(BUY_ORDER_SET);
            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))
         {
            m_notify.Message(BUY_ORDER_SET);
            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))
         {
            m_notify.Message(BUY_ORDER_SET);
            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))
         {
            m_notify.Message(BUY_ORDER_SET);
            return(true);
         }
      }
   }
   return(false);
}

Faremos a alteração análoga com o método de abertura de posições de venda, mas apenas com uma chamada de notificação de voz SELL_ORDER_SET:

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))
         {
            m_notify.Message(SELL_ORDER_SET);
            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))
         {
            m_notify.Message(SELL_ORDER_SET);
            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))
         {
            m_notify.Message(SELL_ORDER_SET);
            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))
         {
            m_notify.Message(SELL_ORDER_SET);
            return(true);
         }
      }
   }
   return(false);
}

Agora vamos prosseguir para as ordens pendentes. Existem quatro tipos na caixa de ferramentas, já para posicioná-las há um método para cada uma:

   bool              SetBuyStopOrder(int id,long lparam);
   bool              SetSellStopOrder(int id,long lparam);
   bool              SetBuyLimitOrder(int id,long lparam);
   bool              SetSellLimitOrder(int id,long lparam);

Para cada uma, precisamos definir a notificação de voz correspondente. Vamos fazer isso usando o exemplo de uma ordem BuyStop, porque devemos fazer ações semelhantes com o resto. Como se pode ver na lista abaixo, é usada a notificação BUYSTOP_ORDER_SET.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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))
         {
            m_notify.Message(BUYSTOP_ORDER_SET);
            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))
         {
            m_notify.Message(BUYSTOP_ORDER_SET);
            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))
         {
            m_notify.Message(BUYSTOP_ORDER_SET);
            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))
         {
            m_notify.Message(BUYSTOP_ORDER_SET);
            return(true);
         }
      }
   }
   return(false);
}

Se tivermos concluído a instalação de notificações de voz para fazer ordens pendentes, agora resta implementar notificações para excluir ordens feitas anteriormente. Para isso, vamos encontrar o método RemoveOrder(), que determina qual das ordens pendentes na tabela é selecionada, depois disso torna-se possível trabalhar com ela - editar ou excluir. Mas neste método, a ordem pendente é excluída clicando no botão excluir. 

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::RemoveOrder(long lparam)
{
//--- Check the element ID
   if(lparam==m_small_button[3].Id())
   {
      //--- Get index and symbol
      if(m_table_orders.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_orders.SelectedItem();
      ulong ticket=(ulong)m_table_orders.GetValue(0,row);
      //---
      if(OrderSelect(ticket))
      {
         string position_symbol=OrderGetString(ORDER_SYMBOL);                          // symbol
         ulong  magic=OrderGetInteger(ORDER_MAGIC);                                    // order MagicNumber
         ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);            // order type
         if(type==ORDER_TYPE_BUY_STOP)
            m_notify.Message(BUYSTOP_ORDER_DELETE);
         else if(type==ORDER_TYPE_SELL_STOP)
            m_notify.Message(SELLSTOP_ORDER_DELETE);
         else if(type==ORDER_TYPE_BUY_LIMIT)
            m_notify.Message(BUYLIMIT_ORDER_DELETE);
         else if(type==ORDER_TYPE_SELL_LIMIT)
            m_notify.Message(SELLLIMIT_ORDER_DELETE);
         //--- declare the request and the result
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- zeroing the request and result values
         ZeroMemory(request);
         ZeroMemory(result);
         //--- set the operation parameters
         request.action=TRADE_ACTION_REMOVE;             // trading operation type
         request.order = ticket;                         // order ticket
         //--- sending a request
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
//---
   return(false);
}

Vamos considerar a alteração do corpo do método com mais detalhes. Após determinar o ticket da ordem selecionada, recebemos os dados necessários para fazer uma solicitação de exclusão, preenchendo a estrutura MqlTradeRequest e chamando o métodoOrderSend(). Para entender que tipo de ordem pendente foi selecionada na tabela, usamos o valor da variável type. Com base em seu valor, atribuímos tipo apropriado de notificação de voz ao o método Message().

E a última coisa que resta de nossa tarefa principal é avisar por voz se o modo de negociação automática está desativado no terminal MetaTrader 5. Visto que o conjunto de ferramentas é, na verdade, um EA e, embora as ordens sejam colocadas por ele apenas com base em ações do usuário, ou seja, manualmente, o terminal e a corretora os reconhecem como negociação automática. Para adicionar a verificação de permissão, precisamos, na classe base do aplicativo, irmos para o manipulador de eventos OnEvent() na seção ON_END_CREATE_GUI e na parte inferior registrarmos uma verificação com atribuição de notificação de voz:

// --- GUI 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);
      //---
      if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
         m_notify.Message(AUTO_TRADING_OFF);
   }

Em conclusão, usando como exemplo um conjunto de ferramentas de negociação rápida, o vídeo gravado a seguir mostra o funcionamento das notificações por voz ao trabalhar com posições a mercado e ordens pendentes.


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 7 abaixo.


Fig. 7 Busca da pasta MQL5 na raiz do terminal MetaTrader 5

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/8111

Arquivos anexados |
MQL5.zip (8868.77 KB)
Usando criptografia com aplicativos externos Usando criptografia com aplicativos externos
Consideraremos problemas de criptografia/descriptografia de objetos no MetaTrader e em programas de terceiros, a fim de descobrir as condições sob as quais são obtidos os mesmos resultados quando os dados iniciais são os mesmos.
Teoria das probabilidades e estatística matemática com exemplos (Parte I): fundamentos e teoria elementar Teoria das probabilidades e estatística matemática com exemplos (Parte I): fundamentos e teoria elementar
Fazer trading é sempre sobre como tomar decisões diante da incerteza. Isso significa que os resultados das decisões tomadas não são muito óbvios no momento em que são tomadas. Por isso, são importantes as abordagens teóricas para a construção de modelos matemáticos que possibilitem descrever tais situações de maneira significativa.
Trabalhando com séries temporais na biblioteca DoEasy (Parte 47): indicadores padrão multiperíodos multissímbolos Trabalhando com séries temporais na biblioteca DoEasy (Parte 47): indicadores padrão multiperíodos multissímbolos
Neste artigo começaremos a desenvolver métodos para trabalhar com indicadores padrão, o que nos permitirá criar indicadores multissímbolos e multiperíodos padrão. Também adicionaremos o evento "Barras ausentes" às classes das séries temporais e descarregaremos o código do programa principal movendo as funções de preparação da biblioteca para a classe CEngine.
Trabalhando com séries temporais na biblioteca DoEasy (Parte 46): buffers de indicador multiperíodos multissímbolos Trabalhando com séries temporais na biblioteca DoEasy (Parte 46): buffers de indicador multiperíodos multissímbolos
No artigo acaberemos de modificar as classes-objetos de buffers de indicador para trabalhar no modo multissímbolo. Dessa maneira, teremos tudo pronto para criar indicadores multissímbolos multiperíodos em nossos programas. Adicionaremos a funcionalidade que falta aos objetos dos buffers calculados, o que nos permitirá criar indicadores multissímbolos e multiperíodos padrão.