Descargar MetaTrader 5

Guía práctica de MQL5: Notificaciones sonoras para los eventos de trading de MetaTrader 5

7 mayo 2014, 13:35
Anatoli Kazharski
0
620

Introducción

En este artículo vamos a tratar el tema de la inclusión de archivos de sonido en el archivo del Asesor Experto y con ello añadir notificaciones sonoras a los eventos de trading. El hecho de que se incluyan los archivos significa que los archivos de sonido van a estar ubicados dentro del Asesor Experto. De modo que al proporcionar una versión compilada del Asesor Experto (*.ex5) a otro usuario, no tendrá que proporcionarle los archivos de sonido y explicarle dónde hay que guardarlos.

 

Desarrollo

Para las pruebas, usaremos el Asesor Experto del artículo anterior "Guía práctica de MQL5: Guardar los resultados de la optimización de un Asesor Experto en base a unos criterios especificados". Para simplificar, he quitado todos los elementos que no sean relevantes para el presente tema.

Para añadir notificaciones sonoras a un evento de trading usando los recursos de MQL5, podemos utilizar las funciones Alert() y PlaySound(). Si opta por la función Alert(), esta siempre reproducirá la misma notificación sonora y abrirá una ventana con el mensaje correspondiente. Lo puede comprobar en el artículo "Guía práctica de MQL5: Utilizar diferentes modos de visualización".

Se puede definir el sonido de la alerta (alert) en los ajustes del terminal: Herramientas -> Opciones o Ctrl+O. Luego, en la pestaña Eventos, tenemos que activar la casilla "Enable" con el fin de habilitar las notificaciones sonoras para los eventos y seleccionamos el archivo de sonido correspondiente en la lista desplegable de alertas.


Fig. 1. La pestaña "Eventos" en los ajustes del terminal

No obstante, también tiene la posibilidad de establecer una notificación sonora única para cualquier evento de programa personalizado. Para ello, usamos la función PlaySound().

Antes de añadir notificaciones sonoras al Asesor Experto, vamos a crear un Asesor Experto para las pruebas. Vamos a implementar la idea de abrir un panel de sonido al cargar el Asesor Experto en el gráfico. El panel de sonido estará hecho de objetos, como un botón (OBJ_BUTTON). Cada botón tendrá asignado su propio y único sonido que se reproducirá al pulsar el botón.

He buscado por internet y encontré 25 archivos de sonido diferentes en el formato *.wav (están disponibles para su descarga al final del artículo). Hay que colocarlos en la carpeta MetaTrader 5\MQL5\Files\Sounds. Para familiarizarse con el uso de archivos de sonido, vamos a crear un nuevo Asesor Experto mediante MQL5 Wizard. Al principio, indicamos el tamaño de la matriz basado en el número de botones en el panel de sonido (habrán 26 botones en total).

//--- Array size
#define ARRAY_SIZE 26

A continuación, tenemos que indicar las carpetas y los nombres de los archivos que van a proporcionar los recursos a nuestro Asesor Experto. Se puede hacer esto mediante la directiva #resource. Después de la directiva, especificamos la ubicación del archivo entre comillas:

//--- Sound files
#resource "\\Files\\Sounds\\alert.wav"
#resource "\\Files\\Sounds\\AHOOGA.wav"
#resource "\\Files\\Sounds\\APPLAUSE.wav"
#resource "\\Files\\Sounds\\BONK.wav"
#resource "\\Files\\Sounds\\CARBRAKE.wav"
#resource "\\Files\\Sounds\\CASHREG.wav"
#resource "\\Files\\Sounds\\CLAP.wav"
#resource "\\Files\\Sounds\\CORKPOP.wav"
#resource "\\Files\\Sounds\\DOG.wav"
#resource "\\Files\\Sounds\\DRIVEBY.wav"
#resource "\\Files\\Sounds\\DRUMROLL.wav"
#resource "\\Files\\Sounds\\EXPLODE.wav"
#resource "\\Files\\Sounds\\FINALBEL.wav"
#resource "\\Files\\Sounds\\FROG.wav"
#resource "\\Files\\Sounds\\GLASS.wav"
#resource "\\Files\\Sounds\\GUNSHOT.wav"
#resource "\\Files\\Sounds\\LASER.wav"
#resource "\\Files\\Sounds\\LATNWHIS.wav"
#resource "\\Files\\Sounds\\PIG.wav"
#resource "\\Files\\Sounds\\RICOCHET.wav"
#resource "\\Files\\Sounds\\RINGIN.wav"
#resource "\\Files\\Sounds\\SIREN.wav"
#resource "\\Files\\Sounds\\TRAIN.wav"
#resource "\\Files\\Sounds\\UH_OH.wav"
#resource "\\Files\\Sounds\\VERYGOOD.wav"
#resource "\\Files\\Sounds\\WHOOSH.wav"

Ahora, tenemos que crear tres matrices de cadenas que contendrán las ubicaciones de los archivos de recursos, los nombres de los objetos gráficos y el texto que se muestra en los objetos gráficos. Tenga en cuenta el uso del símbolo "doble dos puntos" (::) al especificar las ubicaciones de los archivos; es una indicación especial para llamar a los recursos por el nombre.

//--- Sound file location
string sound_paths[ARRAY_SIZE]=
  {
   "::Files\\Sounds\\alert.wav",
   "::Files\\Sounds\\AHOOGA.wav",
   "::Files\\Sounds\\APPLAUSE.wav",
   "::Files\\Sounds\\BONK.wav",
   "::Files\\Sounds\\CARBRAKE.wav",
   "::Files\\Sounds\\CASHREG.wav",
   "::Files\\Sounds\\CLAP.wav",
   "::Files\\Sounds\\CORKPOP.wav",
   "::Files\\Sounds\\DOG.wav",
   "::Files\\Sounds\\DRIVEBY.wav",
   "::Files\\Sounds\\DRUMROLL.wav",
   "::Files\\Sounds\\EXPLODE.wav",
   "::Files\\Sounds\\FINALBEL.wav",
   "::Files\\Sounds\\FROG.wav",
   "::Files\\Sounds\\GLASS.wav",
   "::Files\\Sounds\\GUNSHOT.wav",
   "::Files\\Sounds\\LASER.wav",
   "::Files\\Sounds\\LATNWHIS.wav",
   "::Files\\Sounds\\PIG.wav",
   "::Files\\Sounds\\RICOCHET.wav",
   "::Files\\Sounds\\RINGIN.wav",
   "::Files\\Sounds\\SIREN.wav",
   "::Files\\Sounds\\TRAIN.wav",
   "::Files\\Sounds\\UH_OH.wav",
   "::Files\\Sounds\\VERYGOOD.wav",
   "::Files\\Sounds\\WHOOSH.wav"
  };
//--- Names of graphical objects
string sound_names[ARRAY_SIZE]=
  {
   "sound_button01","sound_button02",
   "sound_button03","sound_button04",
   "sound_button05","sound_button06",
   "sound_button07","sound_button08",
   "sound_button09","sound_button10",
   "sound_button11","sound_button12",
   "sound_button13","sound_button14",
   "sound_button15","sound_button16",
   "sound_button17","sound_button18",
   "sound_button19","sound_button20",
   "sound_button21","sound_button22",
   "sound_button23","sound_button24",
   "sound_button25","sound_button26"
  };
//--- Text displayed on graphical objects
string sound_texts[ARRAY_SIZE]=
  {
   "ALERT","AHOOGA","APPLAUSE","BONK","CARBRAKE","CASHREG",
   "CLAP","CORKPOP","DOG","DRIVEBY","DRUMROLL","EXPLODE","FINALBEL",
   "FROG","GLASS","GUNSHOT","LASER","LATNWHIS","PIG",
   "RICOCHET","RINGIN","SIREN","TRAIN","UH_OH","VERYGOOD","WHOOSH"
  };

Escribamos una función, CreateButton(), que creará el objeto gráfico "Button" (botón) en el gráfico con las propiedades especificadas:

//+------------------------------------------------------------------+
//| Creating the Button object                                       |
//+------------------------------------------------------------------+
void CreateButton(long              chart_id,         // chart id
                  int               sub_window,       // window number
                  string            name,             // object name
                  string            text,             // displayed name
                  ENUM_ANCHOR_POINT anchor,           // anchor point
                  ENUM_BASE_CORNER  corner,           // chart corner
                  string            font_name,        // font
                  int               font_size,        // font size
                  color             font_color,       // font color
                  color             background_color, // background color
                  color             border_color,     // border color
                  int               x_size,           // width
                  int               y_size,           // height
                  int               x_distance,       // X-coordinate
                  int               y_distance,       // Y-coordinate
                  long              z_order)          // Z-order
  {
//--- Creating an object
   if(ObjectCreate(chart_id,name,OBJ_BUTTON,sub_window,0,0))
     {
      ObjectSetString(chart_id,name,OBJPROP_TEXT,text);                  // setting name
      ObjectSetString(chart_id,name,OBJPROP_FONT,font_name);             // setting font
      ObjectSetInteger(chart_id,name,OBJPROP_COLOR,font_color);          // setting font color
      ObjectSetInteger(chart_id,name,OBJPROP_BGCOLOR,background_color);  // setting background color
      ObjectSetInteger(chart_id,name,OBJPROP_BORDER_COLOR,border_color); // setting border color
      ObjectSetInteger(chart_id,name,OBJPROP_ANCHOR,anchor);             // setting anchor point
      ObjectSetInteger(chart_id,name,OBJPROP_CORNER,corner);             // setting chart corner
      ObjectSetInteger(chart_id,name,OBJPROP_FONTSIZE,font_size);        // setting font size
      ObjectSetInteger(chart_id,name,OBJPROP_XSIZE,x_size);              // setting width X
      ObjectSetInteger(chart_id,name,OBJPROP_YSIZE,y_size);              // setting height Y
      ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x_distance);      // setting X-coordinate
      ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y_distance);      // setting Y-coordinate
      ObjectSetInteger(chart_id,name,OBJPROP_SELECTABLE,false);          // cannot select the object if FALSE
      ObjectSetInteger(chart_id,name,OBJPROP_STATE,false);               // button state (clicked/unclicked)
      ObjectSetInteger(chart_id,name,OBJPROP_ZORDER,z_order);            // higher/lower Z-order
      ObjectSetString(chart_id,name,OBJPROP_TOOLTIP,"\n");               // no tooltip if "\n"
     }
  }

Para que sea más alegre, se seleccionará el color de cada botón al azar. Para ello, vamos a escribir una función simple - GetRandomColor():

//+------------------------------------------------------------------+
//| Returning a random color                                         |
//+------------------------------------------------------------------+
color GetRandomColor()
  {
//--- Select a random color from 0 to 25
   switch(MathRand()%26)
     {
      case 0  : return(clrOrange);           break;
      case 1  : return(clrGold);             break;
      case 2  : return(clrChocolate);        break;
      case 3  : return(clrChartreuse);       break;
      case 4  : return(clrLime);             break;
      case 5  : return(clrSpringGreen);      break;
      case 6  : return(clrMediumBlue);       break;
      case 7  : return(clrDeepSkyBlue);      break;
      case 8  : return(clrBlue);             break;
      case 9  : return(clrSeaGreen);         break;
      case 10 : return(clrRed);              break;
      case 11 : return(clrSlateGray);        break;
      case 12 : return(clrPeru);             break;
      case 13 : return(clrBlueViolet);       break;
      case 14 : return(clrIndianRed);        break;
      case 15 : return(clrMediumOrchid);     break;
      case 16 : return(clrCrimson);          break;
      case 17 : return(clrMediumAquamarine); break;
      case 18 : return(clrDarkGray);         break;
      case 19 : return(clrSandyBrown);       break;
      case 20 : return(clrMediumSlateBlue);  break;
      case 21 : return(clrTan);              break;
      case 22 : return(clrDarkSalmon);       break;
      case 23 : return(clrBurlyWood);        break;
      case 24 : return(clrHotPink);          break;
      case 25 : return(clrLightSteelBlue);   break;
      //---
      default : return(clrGold);
     }
//---
   return(clrGold);
  }

Escribamos ahora la función que va a añadir el panel de sonido al gráfico - SetSoundPanel():

//+------------------------------------------------------------------+
//| Adding the sound panel to the chart                              |
//+------------------------------------------------------------------+
void SetSoundPanel()
  {
   int   column_count =0;       // Column counter
   int   x_dist       =10;      // Indent from the left side of the chart
   int   y_dist       =15;      // Indent from the top of the chart
   int   x_size       =100;     // Button width
   int   y_size       =20;      // Button height
   color button_color =clrNONE; // Button color
//--- Set the objects
   for(int i=0; i<ARRAY_SIZE; i++)
     {
      //--- Increase the column counter
      column_count++;
      //--- Get the button color
      button_color=GetRandomColor();
      //--- Draw a button
      CreateButton(0,0,sound_names[i],sound_texts[i],
                   ANCHOR_LEFT_UPPER,CORNER_LEFT_UPPER,"Arial",8,
                   clrWhite,button_color,button_color,x_size,y_size,x_dist,y_dist,1);
      //--- If two buttons have already been set in the same row
      if(column_count==2)
        {
         x_dist=10;        // Move the X-coordinate to the initial position
         y_dist+=20;       // Set the Y-coordinate for the next row
         column_count=0;   // Zero out the counter
        }
      else
      //--- Set the X-coordinate for the next button 
         x_dist+=x_size;
     }
//--- Refresh the chart
   ChartRedraw(0);
  }

Para quitar el gráfico del panel, usaremos las funciones proporcionadas a continuación:

//+------------------------------------------------------------------+
//| Deleting the info panel                                          |
//+------------------------------------------------------------------+
void DeleteSoundPanel()
  {
//--- Delete position properties and their values
   for(int i=0; i<ARRAY_SIZE; i++)
      DeleteObjectByName(name_sound_object[i]);
//--- Redraw the chart
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Deleting objects by name                                         |
//+------------------------------------------------------------------+
void DeleteObjectByName(string name)
  {
//--- If the object is found
   if(ObjectFind(ChartID(),name)>=0)
     {
      //--- If an error occurred when deleting, print the relevant message
      if(!ObjectDelete(ChartID(),name))
         Print("Error ("+IntegerToString(GetLastError())+") when deleting the object!");
     }
  }

Por lo tanto, cuando se carga el Asesor Experto, se coloca el panel en el gráfico mediante la función OnInit() y se elimina al quitar el Asesor Experto mediante la función OnDeinit().

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit()
  {
//--- Set the sound panel
   SetSoundPanel();
  }
//+------------------------------------------------------------------+
//| Deinitialization function of the expert advisor                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Delete the sound panel
   DeleteSoundPanel();
  }

Ahora, solo tenemos que implementar la interacción con el panel de modo que se reproduzca el sonido correspondiente al pulsar algún botón. Para hacerlo aún más alegre, vamos a cambiar los colores de los botones al pulsar algún botón del panel de sonido. Para implementarlo, nos hará falta la función ChangeColorsOnSoundPanel() con el siguiente código:

//+------------------------------------------------------------------+
//| Changing colors on the sound panel                               |
//+------------------------------------------------------------------+
void ChangeColorsOnSoundPanel()
  {
   color clr=clrNONE; // Button color
//--- Iterate over all buttons in a loop and change their color
   for(int i=0; i<ARRAY_SIZE; i++)
     {
      //--- Get the new color
      clr=GetRandomColor();
      //--- Set the border color
      ObjectSetInteger(0,sound_names[i],OBJPROP_BGCOLOR,clr);
      //--- Set the background color
      ObjectSetInteger(0,sound_names[i],OBJPROP_BORDER_COLOR,clr);
      //--- Unclicked button
      ObjectSetInteger(0,sound_names[i],OBJPROP_STATE,false);
      //--- Refresh the chart
      ChartRedraw(0);
      //--- Wait for 20 ms (lag)
      Sleep(20);
     }
  }

Y por último, habrá que añadir el siguiente código a la función OnChartEvent():

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void OnChartEvent(const int     id,     // Event identifier  
                  const long&   lparam, // Parameter of the event of type long
                  const double& dparam, // Parameter of the event of type double
                  const string& sparam) // Parameter of the event of type string
  {
//--- If there was an event of left-clicking on the object
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- If the object name contains "sound_button"
      if(StringFind(sparam,"sound_button",0)>=0)
        {
         //--- Play the sound based on the object name
         //    5019 - ERR_FILE_NOT_EXIST - The file does not exist
         if(!PlaySound(GetSoundPath(sparam)))
            Print("Error: ",GetLastError());
         //--- Change colors of all buttons
         ChangeColorsOnSoundPanel();
        }
     }
  }

La cadena resaltada del código anterior muestra que la ubicación del archivo de sonido se envía a la función PlaySound() mediante la función personalizada GetSoundPath(). A continuación, se proporciona el código de la función GetSoundPath():

//+------------------------------------------------------------------+
//| Returning sound file location by the object name                 |
//+------------------------------------------------------------------+
string GetSoundPath(string object_name)
  {
//--- Iterate over all sound panel objects in a loop
   for(int i=0; i<ARRAY_SIZE; i++)
     {
      //--- If the name of the object clicked in the chart
      //    matches one of those available on the panel, return the file location
      if(object_name==name_sound_object[i])
         return(path_sound_object[i]);
     }
//---
   return("");
  }

Ya está todo listo. Se pondrá el panel de sonido (se puede descargar el programa a partir de los archivos adjuntos al artículo) nada más se añade el Asesor Experto al gráfico:

El panel de sonido en el gráfico

Fig. 2. El panel de sonido en el gráfico

Por tanto, ya queda claro el principio de funcionamiento de los archivos de sonido. Volvemos a nuestro Asesor Experto del artículo anterior "Guía práctica de MQL5: Guardar los resultados de la optimización de un Asesor Experto en base a unos criterios especificados" y decidimos qué sonidos vamos a utilizar en nuestro Asesor Experto. Vamos a crear Resources.mqh y lo incluimos en el archivo principal del Asesor Experto.

//--- Include custom libraries
#include "Include/Errors.mqh"
#include "Include/Enums.mqh"
#include "Include/Resources.mqh"
#include "Include/TradeSignals.mqh"
#include "Include/TradeFunctions.mqh"
#include "Include/ToString.mqh"
#include "Include/Auxiliary.mqh"

Seleccionamos ahora los archivos para los eventos de trading principales.

//--- Sound files
#resource "\\Files\\Sounds\\AHOOGA.WAV"   // Error
#resource "\\Files\\Sounds\\CASHREG.WAV"  // Position opening/position volume increase/pending order triggering
#resource "\\Files\\Sounds\\WHOOSH.WAV"   // Pending order/Stop Loss/Take Profit setting/modification
#resource "\\Files\\Sounds\\VERYGOOD.WAV" // Position closing at profit
#resource "\\Files\\Sounds\\DRIVEBY.WAV"  // Position closing at loss
//--- Sound file location
string SoundError          = "::Files\\Sounds\\AHOOGA.WAV";
string SoundOpenPosition   = "::Files\\Sounds\\CASHREG.WAV";
string SoundAdjustOrder    = "::Files\\Sounds\\WHOOSH.WAV";
string SoundCloseWithProfit= "::Files\\Sounds\\VERYGOOD.WAV";
string SoundCloseWithLoss  = "::Files\\Sounds\\DRIVEBY.WAV";

Me gustaría mencionar también que además de los archivos de sonido utilizados como recursos, puede almacenar imágenes *.bmp en el Asesor Experto para la interfaz, archivos de texto e incluso indicadores. Se puede decir ahora que los Asesores Expertos para MQL5 son funcionales en toda regla; esto es muy práctico, ya que solo tiene que enviar un archivo en lugar de varios.

Continuemos. Tenemos que añadir el parámetro UseSound a los parámetros externos para tener la posibilidad de desactivar el sonido:

//--- External parameters of the Expert Advisor
input  int        NumberOfBars =2;    // Number of one-direction bars
sinput double     Lot          =0.1;  // Lot
input  double     TakeProfit   =100;  // Take Profit
input  double     StopLoss     =50;   // Stop Loss
input  double     TrailingStop =10;   // Trailing Stop
input  bool       Reverse      =true; // Position reversal
sinput bool       UseSound     =true; // Sound notifications

En Include\Enums.mqh, creamos la enumeración ENUM_SOUNDS para los sonidos.

//--- Sounds
enum ENUM_SOUNDS
  {
   SOUND_ERROR             =0,   // Error
   SOUND_OPEN_POSITION     = 1,  // Position opening/position volume increase/pending order triggering
   SOUND_ADJUST_ORDER      = 2,  // Stop Loss/Take Profit/pending order setting
   SOUND_CLOSE_WITH_PROFIT = 3,  // Position closing at profit
   SOUND_CLOSE_WITH_LOSS   = 4   // Position closing at loss
  };

Harán falta estos identificadores para la función personalizada PlaySoundByID().

//+------------------------------------------------------------------+
//| Playing sounds                                                   |
//+------------------------------------------------------------------+
void PlaySoundByID(ENUM_SOUNDS id)
  {
//--- If it is the real-time mode and sounds are enabled
   if(IsRealtime() && UseSound)
     {
      //--- Play the sound based on the identifier passed
      switch(id)
        {
         case SOUND_ERROR              : PlaySound(SoundError);            break;
         case SOUND_OPEN_POSITION      : PlaySound(SoundOpenPosition);     break;
         case SOUND_ADJUST_ORDER       : PlaySound(SoundAdjustOrder);      break;
         case SOUND_CLOSE_WITH_PROFIT  : PlaySound(SoundCloseWithProfit);  break;
         case SOUND_CLOSE_WITH_LOSS    : PlaySound(SoundCloseWithLoss);    break;
        }
     }
  }

Durante las operaciones de trading llevadas a cabo por el Asesor Experto, se pueden reproducir los efectos sonoros llamando a PlaySoundByID() desde las funciones de trading correspondientes. Veamos cómo se implementa esto en la función OpenPosition():

//+------------------------------------------------------------------+
//| Opening a position                                               |
//+------------------------------------------------------------------+
void OpenPosition(double lot,
                  ENUM_ORDER_TYPE order_type,
                  double price,
                  double sl,
                  double tp,
                  string comment)
  {
//--- Set the magic number in the trading structure
   trade.SetExpertMagicNumber(0);
//--- Set the slippage in points
   trade.SetDeviationInPoints(CorrectValueBySymbolDigits(10));
//--- The Instant Execution and Market Execution modes
//    *** Starting with build 803, Stop Loss and Take Profit                             ***
//    *** can be set upon opening a position in the SYMBOL_TRADE_EXECUTION_MARKET mode ***
   if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_INSTANT ||
      symb.execution_mode==SYMBOL_TRADE_EXECUTION_MARKET)
     {
      //--- If the position failed to open
      if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment))
        {
         //--- Play the error sound and print the relevant message
         PlaySoundByID(SOUND_ERROR);
         Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
        }
      //--- Otherwise play the position opening sound
      else
         PlaySoundByID(SOUND_OPEN_POSITION);
     }
  }

En el caso de que se cierre una posición, mediante Stop Loss, Take Profit, de forma manual o de cualquier otra manera, será necesario hacer un seguimiento de este evento en la función OnTrade(). Para implementar esto, vamos a escribir una función más, SoundNotification(), que se encargará de las comprobaciones necesarias: si el historial de transacciones muestra una nueva transacción con el identificador DEAL_ENTRY_OUT o DEAL_ENTRY_INOUT (cierre completo o parcial de la posición o una inversión) para el símbolo actual, el programa comprobará si esta transacción se ha cerrado con beneficio o pérdidas y reproducirá el sonido correspondiente.

//+------------------------------------------------------------------+
//| Sound notification                                               |
//+------------------------------------------------------------------+
void SoundNotification()
  {
//--- If it is the real-time mode and sounds are enabled
   if(IsRealtime() && UseSound)
     {
      ulong        ticket      =0; // Deal ticket
      int          total       =0; // Total deals
      static ulong last_ticket =0; // Last ticket prior to this check
      //--- Get the complete history
      if(!HistorySelect(0,TimeCurrent()+1000))
         return;
      //--- Get the number of deals in the obtained list
      total=HistoryDealsTotal();
      //--- In the obtained list, iterate over all deals from the last one to the first one
      for(int i=total-1; i>=0; i--)
        {
         //--- If the deal ticket by its position in the list has been obtained
         if((ticket=HistoryDealGetTicket(i))>0)
           {
            //--- get the symbol of the deal
            GetHistoryDealProperties(ticket,D_SYMBOL);
            //--- If the symbol of the deal and the current symbol are the same
            if(deal.symbol==_Symbol)
              {
               //--- get the direction of the deal
               GetHistoryDealProperties(ticket,D_ENTRY);
               //--- If it is position closing, volume decrease or reversal
               if(deal.entry==DEAL_ENTRY_OUT || deal.entry==DEAL_ENTRY_INOUT)
                 {
                  //--- If the ticket of the current deal from the list (the last deal for the symbol) is equal to the previous ticket
                  //    or this is the initialization of the ticket of the last deal
                  if(ticket==last_ticket || last_ticket==0)
                    {
                     //--- Save the ticket and exit
                     last_ticket=ticket;
                     return;
                    }
                  //--- Get the result of the deal
                  GetHistoryDealProperties(ticket,D_PROFIT);
                  //--- In case of profit
                  if(deal.profit>=0)
                    {
                     //--- Profit sound
                     PlaySoundByID(SOUND_CLOSE_WITH_PROFIT);
                     //--- Save the ticket number
                     last_ticket=ticket;
                     return;
                    }
                  //--- In case of loss
                  if(deal.profit<0)
                    {
                     //--- Loss sound
                     PlaySoundByID(SOUND_CLOSE_WITH_LOSS);
                     //--- Save the ticket number
                     last_ticket=ticket;
                     return;
                    }
                 }
              }
           }
        }
     }
  }

Hay que colocar la función SoundNotification() en las funciones OnInit() y OnTrade():

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Initialize the new bar
   CheckNewBar();
//--- Initialize tickets of the last deals for the symbol
   SoundNotification();
//--- Initialization completed successfully
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Monitoring trade events                                          |
//+------------------------------------------------------------------+
void OnTrade()
  {
//--- Sound notification
   SoundNotification();
  }

También se ha añadido la notificación sonora al final de la función ModifyTrailingStop() al modificar el nivel de Trailing Stop.

 

Conclusión

Esto es todo. Se pueden descargar todos los archivos para realizar las pruebas a partir de los archivos adjuntos al artículo. Hablando de sonidos en el terminal, me gustaría llamar su atención sobre una solución interesante que está disponible en la biblioteca de códigos fuente con el nombre de CMIDI (por Integer); le permite reproducir archivos MIDI en MetaTrader 5. ¡Buena suerte!

Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/748

Archivos adjuntos |
sounds.zip (321.8 KB)
soundpanel.mq5 (14.07 KB)
Guía práctica de MQL5: Guardar los resultados de la optimización de un Asesor Experto en base a unos criterios especificados Guía práctica de MQL5: Guardar los resultados de la optimización de un Asesor Experto en base a unos criterios especificados

Continuamos con la serie de artículos sobre la programación en MQL5. Esta vez, veremos cómo obtener los resultados de cada pasada de optimización durante el proceso de optimización de los parámetros del Asesor Experto. Se hará la implementación de modo que si se cumplen las condiciones especificadas en los parámetros externos, se escriben los valores correspondientes a la pasada de optimización en un archivo. Además de los valores de las pruebas, guardaremos también los parámetros que han llevado a estos resultados.

Ampliación de la librería estándar de MQL5 y la reutilización del código Ampliación de la librería estándar de MQL5 y la reutilización del código

La librería estándar de MQL5 le facilita la vida como desarrollador. No obstante, no abarca todas las necesidades de todos los desarrolladores del mundo, con lo cual querrá tener a su disposición más material personalizado para dar un paso más y ampliarla. En este artículo, se describe la integración del indicador técnico Zig-Zag de MetaQuotes en la librería estándar. Para conseguir nuestro objetivo, nos hemos basado en la filosofía de diseño de MetaQuotes.

Guía práctica de MQL5: Supervisar múltiples períodos de tiempo en una sola ventana Guía práctica de MQL5: Supervisar múltiples períodos de tiempo en una sola ventana

MetaTrader 5 ofrece 21 períodos de tiempo para el análisis. Puede aprovechar los objetos gráficos especiales que puede colocar en el gráfico existente y establecer el símbolo, el período de tiempo y otras propiedades. En este artículo se va a proporcionar una información detallada acerca de estos objetos gráficos: crearemos un indicador con controles (botones) que nos permitirán establecer múltiples objetos gráficos en una subventana al mismo tiempo. Además, se encajarán los objetos gráficos con precisión en la subventana y se ajustarán automáticamente al modificar el tamaño del gráfico principal o el de la ventana del terminal.

Guía práctica de MQL5: Controles de la subventana del indicador: Botones Guía práctica de MQL5: Controles de la subventana del indicador: Botones

En este artículo, vamos a ver un ejemplo del desarrollo de una interfaz de usuario con controles mediante botones. Para transmitir la idea de interactividad al usuario, los botones cambiarán de color cuando se pasa el cursor por encima de ellos. Cuando el cursor está encima de un botón, el color de este último será ligeramente oscurecido, haciéndose notablemente más oscuro cuando se pulsa el botón. Además, vamos a añadir tooltips (información sobre herramientas) a cada botón, creando así una interfaz intuitiva.