Descargar MetaTrader 5

Cree su propia Observación del Mercado usando las clases de la librería estándar

24 enero 2014, 10:43
Dmitriy Skub
0
609

Introducción

El objetivo principal fue desarrollar una herramienta fácil de usar y extensible para la representación de información textual sobre el gráfico del terminal de cliente de MetaTrader 5. Por ejemplo, la configuración actual del asesor experto o los resultados de su trabajo durante un intervalo determinado, mostrado en forma de tabla, los valores de precios o una tabla con los valores del indicador usados por el operador, o un registro de las operaciones del asesor experto. Toda esta información puede mostrarse en pantalla dinámicamente en el gráfico durante el funcionamiento del asesor experto o del indicador que use esta librería.

Como base para el desarrollo de la librería se ha usado una colección de clases de la librería estándar del terminal de cliente de MetaTrader 5. Primero veremos estas clases y luego las utilizaremos para realizar nuestra tarea.


Clases estándar para objetos gráficos

El conjunto de clases en las que estamos interesados se encuentra en las carpetas Include e Include\ChartObjects.

Son los siguientes archivos:

  • Object.mqh - contiene la clase básica CObject para la creación de otras clases relacionadas con objetos gráficos.
  • ChartObject.mqh - también contiene la clase CChartObject derivada de CObject, que extiende la funcionalidad y encapsulación de los objetos gráficos, datos generales y métodos.
  • ChartObjectsTxtControls.mqh - contiene un número de clases dirigidas a mostrar los distintos objetos gráficos con textos en el diagrama. (CChartObjectText clase básica y sus herederas: CChartObjectLabel, CChartObjectEdit, y CChartObjectButton.

Vamos a ver estas clases con mayor detalle.


CObject: la clase básica

La descripción de la clase es breve, por lo que vamos a mostrarla aquí:

class CObject
{
protected:
   CObject          *m_prev;
   CObject          *m_next;

public:
                     CObject();

   CObject          *Prev()                { return(m_prev); }
   void              Prev(CObject *node)   { m_prev=node;    }
   CObject          *Next()                { return(m_next); }
   void              Next(CObject *node)   { m_next=node;    }

   virtual bool      Save(int file_handle) { return(true);   }
   virtual bool      Load(int file_handle) { return(true);   }

   virtual int       Type() const          { return(0);      }

protected:
   virtual int       Compare(const CObject *node,int mode=0) const { return(0); }
};

//+------------------------------------------------------------------+
void CObject::CObject()
{
   m_prev=NULL;
   m_next=NULL;
}

Como puede ver, esta clase contiene solo los datos y métodos para finalidades genéricas y no está relacionada directamente con la salida en el gráfico.

Sin embargo, tiene un propiedad muy importante, ya que puede usarse para crear las listas enlazadas simples y dobles. Estas características las proporcionan los campos de datos CObject::m_prev y CObject::m_next del tipo CObject* y los métodos de su lectura/escritura. El campo CObject* se refiere al elemento de la lista previa, mientras que CObject::m_next se refiere al siguiente. Más adelante se proporcionan más detalles sobre la construcción de las listas.

Además, hay un método para comparar objetos del tipo CObject*, el método CObject::Compare, que puede usarse al clasificar los elementos de la lista. Hay dos métodos más útiles que nos permiten guardar/contabilizar campos de datos en los archivos, los métodos CObject::Save y CObject::Load. Para obtener la funcionalidad deseada, estos métodos deben sobrecargarse en las clases herederas.

CObject::Type es el método de identificación del tipo de objeto. Este método es útil para manipular una lista que contenga objetos de distintos tipos.

La clase CObject (y sus instancias) tiene las siguientes características:

  • identificación de su posición relativa a los elementos próximos en la lista.
  • identificación del tipo de objeto.
  • método para guardar y cargar los datos del objeto.
  • método para comparar los objetos especificados.

La mayoría de los métodos descritos anteriormente son virtuales y no se implementan en la clase básica. La clase básica no tiene ninguna propiedad real con significado físico. Como es habitual en la POO, la funcionalidad debe ser implementada en las clases herederas.


CChartObject: la clase básica para los objetos gráficos

CChartObject es heredera de la clase CObject .

En su nombre se puede ver que esta clase sirve para describir algunos objetos gráficos abstractos. No obstante, este objeto abstracto ya contiene algunas propiedades físicas y métodos de trabajo con tales propiedades. Estas propiedades son comunes a todos los objetos gráficos en MetaTrader 5 , por lo que es lógico ubicarlas dentro de la clase.

Vamos a verlas con mayor detalle. Vamos a usar los siguientes datos para adjuntar el objeto gráfico a la ventana del gráfico:

protegido:
  long       m_chart_id;    // identifier of the chart, which contains 
                               // specified graphic object (0 - current chart)
  int        m_window;      // number of chart sub-window (0 - main window)
  string     m_name;        // unique name of the object on the chart
  int        m_num_points;  // number of points for anchoring the object

Antes de especificar o leer las propiedades del objeto gráfico real, debe adjuntarse al objeto (instancia de clase). Esto lo hace el método CChartObject::Attach. En las clases herederas se llama inmediatamente después de la creación del objeto gráfico en el gráfico.

bool CChartObject::Attach(long chart_id,string name,int window,int points)
{
  if(ObjectFind(chart_id,name)<0)
  {
    return(false);
  }

  if(chart_id==0) chart_id=ChartID();

  m_chart_id  =chart_id;
  m_window    =window;
  m_name      =name;
  m_num_points=points;

  return(true);
}

Primero verificamos la existencia de un objeto gráfico real. Si este existe, sus propiedades se almacenan en los campos internos del objeto de la clase CChartObject. Después de esto, podemos leer y modificar las propiedades del objeto gráfico (color, ubicación, etc.).

Los métodos para guardar/leer las propiedades del objeto gráfico ya se han implementado en los métodos CChartObject::Save y CChartObject::Load. El método padre para guardar/leer debe llamarse primero en la clase heredera antes de su propio método.

La clase CChartObject (y sus instancias) tiene las siguientes propiedades nuevas en comparación con las básicas:

  • adjuntar el objeto gráfico real en el gráfico con la instancia de la clase.
  • leer y modificar las propiedades comunes de todos los objetos gráficos.
  • borrado del objeto gráfico del gráfico.
  • movimiento del objeto gráfico sobre el gráfico.


CChartObjectText: una clase para los objetos de texto y gráficos

Vamos a ver ahora el archivo ChartObjectsTxtControls.mqh. Aquí encontraremos la descripción de las clases desarrolladas para la salida de varios objetos gráficos que contienen texto en el gráfico. Vamos a ver sus características básicas.

La clase básica es CChartObjectText. Esta encapsula las propiedades y métodos asociados con la salida de texto en el gráfico.

Esta es la descripción de la clase:

class CChartObjectText : public CChartObject
{
public:
   double            Angle() const;
   bool              Angle(double angle);
   string            Font() const;
   bool              Font(string font);
   int               FontSize() const;
   bool              FontSize(int size);
   ENUM_ANCHOR_POINT  Anchor() const;
   bool              Anchor(ENUM_ANCHOR_POINT anchor);

   bool              Create(long chart_id,string name,int window,datetime time,double price);

   virtual int       Type() const { return(OBJ_TEXT); }

   virtual bool      Save(int file_handle);
   virtual bool      Load(int file_handle);
};

Comparado con CChartObject, contiene métodos para la lectura y modificación de las propiedades de objetos gráficos textuales - el ángulo de orientación del texto en el gráfico, el nombre de la fuente de texto, el tamaño de la fuente y las coordenadas del objeto gráfico. Ha aparecido un nuevo método CChartObjectText:: Create que nos permite crear un objeto gráfico real del tipo OBJ_TEXT en el gráfico.

Su implementación:

bool CChartObjectText::Create(long chart_id,string name,int window,datetime time,double price)
{
  bool result = ObjectCreate( chart_id, name, OBJ_TEXT, window, time, price );
  if(result)
  {
    result &= Attach(chart_id, name, window, 1 );
  }

  return(result);
}

En caso de crear con éxito un objeto gráfico (el método ObjectCreate devuelve verdadero) se llama al método CChartObject:: Attach, que hemos considerado anteriormente.

De esta forma, si lo comparamos con la clase padre, CChartObjectText incluye nuevas características:

  • leer y modificar las propiedades de objetos gráficos textuales.
  • crear un objeto gráfico real del tipo OBJ_TEXT en el gráfico.


CChartObjectLabel: una clase para los objetos gráficos "Text Label"

La siguiente clase en la jerarquía de clases estándar es la clase CChartObjectLabel. Nos permite crear objetos gráficos del tipo OBJ_LABEL (etiqueta de texto) en el gráfico.

Esta es la descripción de la clase:

class CChartObjectLabel : public CChartObjectText
{
public:
   int               X_Distance() const;
   bool              X_Distance(int X);
   int               Y_Distance() const;
   bool              Y_Distance(int Y);
   int               X_Size() const;
   int               Y_Size() const;
   ENUM_BASE_CORNER  Corner() const;
   bool              Corner(ENUM_BASE_CORNER corner);

   bool              Time(datetime time) { return(false);  }
   bool              Price(double price) { return(false);  }

   bool              Create(long chart_id,string name,int window,int X,int Y);

   virtual int       Type() const        { return(OBJ_LABEL); }

   virtual bool      Save(int file_handle);
   virtual bool      Load(int file_handle);
};

Aquí debemos tener en cuenta la diferencia entre el objeto gráfico del tipo OBJ_TEXT y el objeto del tipo OBJ_LABEL.

El primero se une al gráfico de precio (coordenadas precio-tiempo) o en el gráfico en la subventana. El segundo se une en las coordenadas de la ventana o subventana (píxeles) del gráfico.

Por tanto, los objetos de tipo OBJ_TEXT se mueven juntos con el gráfico al desplazarnos por la pantalla, mientras que los objetos del tipo OBJ_LABEL se quedan fijos en pantalla. Por ello, dependiendo de la tarea concreta, debemos elegir un tipo particular de objeto gráfico textual.

Comparada con la clase padre, la clase CChartObjectLabel incluye las siguientes características distintivas:

  • las coordenadas del gráfico se usan al localizar el objeto gráfico.
  • permite leer y modificar la esquina de anclaje. De hecho, está asignando el principio de las coordenadas en una de las cuatro esquinas de la ventana del gráfico.
  • crea un objeto gráfico real del tipo OBJ_LABEL en el gráfico del terminal de cliente.


CChartObjectEdit: una clase para objetos gráficos "campo de entrada"

La siguiente clase en la jerarquía es la clase CChartObjectEdit. Esta clase crea un objeto gráfico del tipo OBJ_EDIT (campo de entrada).

Este tipo de objeto se une de la misma forma que el tipo de objeto OBJ_LABEL usando las coordenadas del gráfico (píxeles), es lógico derivarlo de la clase CChartObjectLabel de la misma forma que las clases estándar:

class CChartObjectEdit : public CChartObjectLabel
{
public:
   bool              X_Size(int X);
   bool              Y_Size(int Y);
   color             BackColor() const;
   bool              BackColor(color new_color);
   bool              ReadOnly() const;
   bool              ReadOnly(bool flag);

   bool              Angle(double angle) { return(false);    }

   bool              Create(long chart_id,string name,int window,int X,int Y,int sizeX,int sizeY);

   virtual int       Type() const        { return(OBJ_EDIT); }

   virtual bool      Save(int file_handle);
   virtual bool      Load(int file_handle);
};

La diferencia entre los campos de entrada de tipo OBJ_EDIT de la etiqueta de texto es la siguiente:

  • el campo de entrada tiene las propiedades de ancho y alto (especificadas en píxeles de pantalla) que limita el tamaño del objeto gráfico. El tamaño de la etiqueta de texto se ajusta automáticamente de forma que pueda verse todo el texto.
  • hay métodos para habilitar/inhabilitar la modificación del texto - CChartObjectEdit:: ReadOnly.
  • se añade un método para cambiar el color de fondo del área ocupada por el objeto gráfico.
  • no hay posibilidad de cambiar el ángulo en el que se muestra el objeto en la pantalla. El campo de entrada solo puede mostrarse en pantalla en dirección horizontal.
  • permite crear un objeto gráfico real de tipo OBJ_EDIT en el gráfico.


CChartObjectButton: una clase para objetos gráficos "Botón"

Otra clase en la jerarquía de objetos gráficos textuales es la clase CChartObjectButton. Este objeto se llama botón y se desarrolla para crear un elemento de control en el gráfico en forma de pulsación de botón. Esta clase es heredera de la clase CChartObjectEdit y hereda su funcionalidad:

class CChartObjectButton : public CChartObjectEdit
{
public:
  bool             State() const;
  bool             State(bool state);

  virtual int       Type() const { return(OBJ_BUTTON); }

  virtual bool      Save(int file_handle);
  virtual bool      Load(int file_handle);
};

La diferencia del botón de tipo OBJ_BUTTON con respecto al campo de entrada es la siguiente:

  • Parece un botón pulsado, similar al usado en los diálogos de Windows.
  • tiene nuevos métodos para leer/modificar el estado del botón (pulsado/no pulsado) - CChartObjectButton::State.
  • permite crear un objeto gráfico real de tipo OBJ_BUTTON en el gráfico.


Estructura general de las clases estándar para objetos gráficos textuales

La estructura (jerarquía) de las clases de la librería estándar puede resumirse de la siguiente forma:

Estructura general de las clases estándar

Figura 1. Estructura general de las clases estándar

La clase CObject es una clase básica para otras clases estándar, por ejemplo, la clase CList que funciona con la lista y con otras.


Ampliar la funcionalidad de las clases de la librería estándar

Hemos discutido brevemente la jerarquía de las clases estándar que se desarrolla para generar objetos gráficos textuales sobre el gráfico. Ahora vamos a ampliar esta jerarquía con nuevas clases. En primer lugar, debemos decidir la funcionalidad requerida para la implementación. Vamos a formular los requisitos. Como estamos trabajando con la salida de información textual, es lógico proporcionar esta información en la forma estructurada siguiente:

TEXTO DE INFORMACIÓN DEL TÍTULO


Esta estructura de representación de la información textual es adecuada para la mayoría de clases simples.

Por ejemplo, el indicador de los parámetros del símbolo puede ser de esta forma:

Un ejemplo de representación estructurada de la información textual

Figura 2. Un ejemplo de representación estructurada de la información textual

Utiliza seis campos de información de la estructura propuesta anteriormente. Algunos elementos de la estructura pueden estar ausentes. Por ejemplo, en la Fig. 2, el título del campo superior no se muestra. Los demás campos contienen el título y el texto. El indicador se encuentra en el archivo PricelInfo.mq5 adjunto a este artículo.


Posicionamiento de los objetos gráficos

El segundo punto que necesitamos considerar es la forma de posicionar los objetos gráficos textuales en el gráfico. El método adoptado para especificar las coordenadas en píxeles de pantalla permite el posicionamiento del objeto en una ubicación arbitraria sobre el gráfico.

Es aconsejable en la práctica cuando necesitamos adaptar varios objetos textuales en diferentes partes del gráfico, ya que es necesario calcular todas las coordenadas de la pantalla. Además, si cambiamos el tamaño del gráfico, es necesario recalcular todas las coordenadas para asegurarnos de que la posición relativa de los objetos en pantalla no ha cambiado.

Si trazamos la ventana del gráfico en rectángulos (campos) del mismo tamaño y asignamos a cada rectángulo una coordinada vertical y horizontal, obtendremos un sistema de posicionamiento universal que es independiente de la resolución de la pantalla.

Al crear objetos textuales gráficos, el usuario puede establecer el número máximo de campos en las direcciones horizontal y vertical y las coordenadas del objeto en dichos campos como parámetros. La funcionalidad, incluida en la clase adecuada, ajustará automáticamente las coordenadas de los objetos gráficos cuando cambiemos la resolución de la pantalla. De esta forma, las coordenadas se especifican una vez y no requieren ningún ajuste posterior.


Generación automática de nombres de objeto únicos

La siguiente cuestión que debe tratarse es la generación automática del nombre del objeto de texto gráfico. El principal requisito para el método de la generación es obtener un único nombre para la ventana considerada. Esto permitirá ubicar en pantalla un número suficiente de objetos, sin preocuparnos por la posibilidad de inventar nombres duplicados.

Proponemos el siguiente método para la generación de un nombre (un string, compuesto por campos):

Número de fecha y hora
en milisegundos


Para obtener la parte del string que contiene la fecha y la hora, usamos la siguiente llamada:

TimeToString(TimeGMT(), TIME_DATE|TIME_MINUTES|TIME_SECONDS);

Para obtener el número de milisegundos usamos la siguiente llamada:

DoubleToString(GetTickCount(), 0);

Pero incluso si medimos el tiempo en milisegundos es muy posible que cuando llamemos a la función GetTickCount () dos o más veces obtengamos el mismo valor. Esto se debe a la restricción de la discontinuidad de los temporizadores internos del sistema operativo y del procesador. Por tanto, es necesario tomar medidas adicionales para detectar esta situación.

El método propuesto se implementa en las siguientes funciones:

string GetUniqName()
{
  static uint prev_count = 0;

  uint count = GetTickCount();
  while(1)
  {
    if(prev_count == UINT_MAX)
    {
      prev_count = 0;
    }
    if(count <= prev_count)
    {
      prev_count++;
      count = prev_count;
    }
    else
    {
      prev_count = count;
    }

//  Verify that there is no object already existing with such a name:
    string name = TimeToString(TimeGMT(), TIME_DATE|TIME_MINUTES|TIME_SECONDS)+" "+DoubleToString(count, 0);
    if(ObjectFind(0, name) < 0)
    {
      return(name);
    }
  }

  return(NULL);
}

Limitación de este método: la generación de no más de 4294967295 (UINT_MAX) nombres únicos por segundo. Aparentemente esto debe ser suficiente en la práctica. Solo en caso de que haya una comprobación adicional de la existencia de un objeto gráfico con el mismo nombre.


Visualización del título de la estructura informativa: clase TTitleDisplay

La clase para visualizar el título en la pantalla del terminal es la siguiente:

class TTitleDisplay : public CChartObjectLabel
{
protected:
  long    chart_id;
  int     sub_window;
  long    chart_width;       // width of the chart in pixels
  long    chart_height;      // height of the chart graph in pixels
  long    chart_width_step;  // step of the coordinates grid in the horizontal direction
  long    chart_height_step; // step of the coordinates grid in the vertical direction
  int     columns_number;    // number of columns
  int     lines_number;      // number of lines
  int     curr_column;
  int     curr_row;

protected:
  void    SetParams(long chart_id, int window, int cols, int lines);// specify the object's parameters

protected:
  string  GetUniqName();    // get a unique name
  bool    Create(long chart_id, int window, int cols, int lines, int col, int row);
  void    RecalcAndRedraw();// recount the coordinates and re-draw

        
public:
  void    TTitleDisplay();  // constructor
  void    ~TTitleDisplay(); // destructor
};

El método básico que crea un objeto textual gráfico:

bool Create(long chart_id, int window, int _cols, int _lines, int _col, int _row);

La asignación de parámetros de entrada es:

  • chart_id - identificador de la ventana (0 - ventana principal);
  • window - número de la subventana (0 - principal);
  • cols - el número máximo de objetos textuales gráficos horizontalmente (el número de columnas);
  • lines - el número máximo de objetos textuales gráficos horizontalmente (el número de filas);
  • col - coordenada horizontal del objeto gráfico (varía de cero a cols -1);
  • col - coordenada vertical del objeto gráfico (varía de cero a lines -1)

El método TTitleDisplay:: SetParams calcula los parámetros del objeto asociado con el posicionamiento en pantalla.

El código es como se muestra a continuación:

void TTitleDisplay::SetParams(long _chart_id, int _window, int _cols, int _lines)
{
  this.chart_id = _chart_id;
  this.sub_window = _window;
  this.columns_number = _cols;
  this.lines_number = _lines;

//  Specify the size of the window in pixels:
  this.chart_width = GetSystemMetrics(SM_CXFULLSCREEN);
  this.chart_height = GetSystemMetrics(SM_CYFULLSCREEN);

//  Calculate the step of the coordinates grid:
  this.chart_width_step = this.chart_width/_cols;
  this.chart_height_step = this.chart_height/_lines;
}

Aquí usamos la llamada de la función GetSystemMetrics WinAPI para obtener los ajustes de la visualización actual. La función es importada de la librería del sistema Windows user32.dll.

De forma similar se construye una clase para crear el texto informativo TFieldDisplay. Los detalles se encuentran en la librería TextDisplay.mqh ajunta a este artículo.


Clase CList: manipulación de objetos en la lista

Vamos a considerar ahora otra clase estándar que vamos a necesitar para la realización de nuestro plan. Esta clase nos permitirá agrupar objetos en listas. Se ubica en el archivo Include\Arrays\List.mqh. El archivo proporciona la descripción e implementación de una clase CList estándar que es heredera de la clase básica CObject considerada antes en este artículo. Contiene un conjunto de métodos para manipular objetos en la lista (adición a la lista, eliminación de la lista, acceso arbitrario a un elemento de la lista y vaciado de la lista).

Vamos a ver los métodos básicos:

  • Añadir a la lista:
   int Add(CObject *new_node);
   int Insert(CObject *new_node,int index);

Estos son dos métodos para añadir un nuevo elemento a la lista. El primero, CList::Add, permite añadir un nuevo elemento new_node al final de la lista. El segundo, CList::Insert, nos permite insertar un nuevo elemento new_node en un lugar arbitrario (especificado por el índice) en la lista.

  • Eliminación de la lista:
   bool  Delete(int index);

El método CList::Delete nos permite eliminar un elemento con un índice especificado de la lista. Mientras tanto, además de eliminar el elemento de la lista, la memoria ocupada por el elemento de tipo CObject se libera y es ocupada por el elemento de tipo CObject. En otras palabras, el objeto será borrado "físicamente".

  • acceso al elemento arbitrario de la lista:
   int       IndexOf(CObject* node);
   CObject*  GetNodeAtIndex(int index);
   CObject*  GetFirstNode();
   CObject*  GetPrevNode();
   CObject*  GetNextNode();
   CObject*  GetLastNode();

El método CList:: IndexOf devuelve el índice de un elemento concreto de la lista. La numeración de los índices comienza por cero: el primer elemento tiene un índice cero. Este método realiza la operación inversa y devuelve el índice por puntero del elemento. Si el elemento no está en la lista, devuelve -1.

Cuatro métodos adicionales para navegar a través de la lista CList:: GetFirstNode devuelven el elemento previo, CList:: GetNextNode devuelve el siguiente y CList:: GetLastNode devuelve el último elemento de la lista.

  • Vaciando la lista:
   void  Clear();

El método CList::Clear nos permite eliminar todos los elementos de la lista y también libera la memoria ocupada por los objetos.

Estos son métodos básicos de la clase CList, el resto se describe en la referencia de MQL5.


Clase TableDisplay: crear una tabla para visualizar texto en el gráfico

Tenemos todo lo que necesitamos para llevar a cabo nuestro plan. Esta es una clase simple que nos permite organizar objetos gráficos de texto en una tabla de cualquier tamaño:

class TableDisplay : public CList
{
protected:
  long  chart_id;
  int   sub_window;

public:
  void  SetParams(long _chart_id, int _window, ENUM_BASE_CORNER _corner = CORNER_LEFT_UPPER);
  int   AddTitleObject(int _cols, int _lines, int _col, int _row, 
                      string _title, color _color, string _fontname = "Arial", int _fontsize = 8);
  int   AddFieldObject(int _cols, int _lines, int _col, int _row, 
                          color _color, string _fontname = "Arial", int _fontsize = 8);
  bool  SetColor(int _index, color _color);
  bool  SetFont(int _index, string _fontname, int _fontsize);
  bool  SetText(int _index, string _text);

public:
  void  TableDisplay();
  void  ~TableDisplay();
};

Antes de añadir los objetos gráficos a la tabla, debemos establecer los parámetros: el identificador del gráfico, el índice de la subventana y el punto de anclaje. Esto se hace llamando al método TableDisplay:: SetParams. Después de esto, podemos añadir cualquier número de títulos y campos de texto a la tabla.

El texto completo de la librería que hemos desarrollado se encuentra en el archivo TextDisplay.mqh adjunto a este artículo.


3. Un ejemplo de creación de la observación del mercado

Vamos a considerar el ejemplo de crear una tabla para visualizar los valores de varios símbolos en forma de indicador.

Debe ser parecido a esto:

Un ejemplo de una tabla en la pantalla

Рисунок 3. Un ejemplo de una tabla en la pantalla

  • Paso 1 - Incluir la librería (en el código fuente del indicador):
#include  <TextDisplay.mqh>
  • Paso 2 - Creación de matrices con los nombres y coordenadas para los títulos:
#define  NUMBER  8
//---------------------------------------------------------------------
string  names[NUMBER]   = {"EURUSD", "GBPUSD", "AUDUSD", "NZDUSD", "USDCHF", "USDCAD", "USDJPY", "EURJPY"};
int     coord_y[NUMBER] = {2,        3,        4,        5,        6,      7,        8,       9};
Las coordenadas se ordenan empezando por cero.
  • Paso 3 - Creamos un objeto tabla del tipo TableDisplay para almacenar todos los objetos de texto visualizados:
TableDisplay  Tabla1;
  • Paso 4 - Añadimos campos para los títulos de los objetos y para la información del objeto en la tabla:
int OnInit()
{
//  Creating a table
  Table1.SetParams(0, 0);

  for(int i=0; i<NUMBER; i++)
  {
    Table1.AddFieldObject(40, 40, 3, coord_y[i], Yellow);
  }

  for(int i=0; i<NUMBER; i++)
  {
    Table1.AddTitleObject(40, 40, 1, coord_y[i], names[i]+":", White);
  }

  ChartRedraw(0);
  EventSetTimer(1);

  return(0);
}

Es mejor hacerlo en el controlador de evento OnInit. Primero, añadimos los campos de información usando el método TableDisplay:: Método AddFieldObject. Luego, le añadimos los títulos usando el método TableDisplay:: Método AddTitleObject.

La adición se implementa en ciclos separados por dos razones: primero, el número de títulos, en general, puede no coincidir con el número de campos de información y, segundo, el acceso a los campos de información actualizados es más fácil de organizar cuando están indexados en una fila (en este caso, desde cero al NÚMERO -1).

  • Paso 5 - añadir el código para actualizar la información dinámica:

En este caso la actualización de la información dinámica se organiza por el controlador de evento Timer. El controlador OnTimer para este evento debe recibir los valores del precio para los instrumentos dados y visualizar estos valores en el gráfico.

Este texto se muestra a continuación:

//---------------------------------------------------------------------
double    rates[NUMBER];
datetime  times[NUMBER];
MqlTick   tick;
//---------------------------------------------------------------------
// OnTimer event handler
//---------------------------------------------------------------------
void OnTimer()
{
  for(int i=0; i<NUMBER; i++)
  {
//  Obtain the price values:
    ResetLastError();
    if(SymbolInfoTick(names[i], tick) != true)
    {
      Table1.SetText(i,"Err "+DoubleToString(GetLastError(),0));
      Table1.SetColor(i,Yellow);
      continue;
    }

    if(tick.time>times[i])
    {
       Table1.SetText(i, DoubleToString(tick.bid, (int)(SymbolInfoInteger(names[i], SYMBOL_DIGITS))));

       if(tick.bid>rates[i])
       {
         Table1.SetColor(i, Lime);
       }
       else if(tick.bid<rates[i])
       {
         Table1.SetColor(i, Red);
       }
       else
       {
         Table1.SetColor(i, Yellow);
       }
       rates[i] = tick.bid;
       times[i] = tick.time;
    }
  }

  ChartRedraw(0);
}

Al inicio del ciclo se leen los datos de tick para los instrumentos especificados y luego se verifica la importancia de los datos - si el tiempo del tick ha cambiado con respecto al anterior, consideramos que el dato es irrelevante. Posteriormente analizamos el valor de la cotización con relación al tick previo.

Si el precio actual es mayor que el anterior, especificamos un color verde para el campo de información. Si es menor se especifica un color rojo y si es igual un color amarillo. Al final del ciclo el valor actual del precio y el tiempo del tick se guardan para el análisis en la llamada del controlador de evento OnTimer.

En general, el código para actualizar la información dinámica depende de la tarea. Básicamente, el usuario solo necesita implementar esta parte del código. Supongamos que decidimos añadir un margen diferencial a la derecha del gráfico en la Fig. 2. Vamos a ver cómo puede realizarse esto.

Todo lo que se necesita es añadir campos de datos adicionales a la tabla:

  for(int i=0; i<NUMBER; i++)
  {
    Table1.AddFieldObject(40, 40, 5, coord_y[i], Yellow);
  }

e incluir el código, que actualiza el valor del margen diferencial en el controlador de evento OnTimer:

  Table1.SetText(i+NUMBER, DoubleToString((tick.ask-tick.bid)/SymbolInfoDouble(names[i], SYMBOL_POINT), 0));

Como resultado, obtenemos la siguiente imagen:

Precios con un margen diferencial

Figura 4. Precios con un margen diferencial

Observe que los campos de índice para el margen diferencial comienzan a partir del valor NÚMERO (si i es igual a cero).

  • Paso 6 - Eliminar los objetos creados:
void OnDeinit(const int _reason)
{
  EventKillTimer();

//  Removing the elements of display:
  Table1.Clear();
}

Aquí se declaran el temporizador y la tabla. Mientras tanto, todos los objetos también se liberan de la memoria.

El texto completo de este indicador puede consultarse en el archivo PriceList.mq5 adjunto a este artículo. El adjunto contiene una versión "mejorada" del indicador que visualiza el margen diferencial. Tiene un número de parámetros externos para la especificación del color del título y la posición de una tabla dentro del gráfico.


Conclusión

El archivo adjunto MarketWatch.mq5 (y el archivo MarketWatch.mqh incluido) contiene el indicador para visualizar los parámetros básicos de los instrumentos de trading en forma de tabla resumen. Para cada símbolo se muestra la información similar a la Fig. 2.

Además, muestra el porcentaje de cambio del precio para los periodos de tiempo especificados. El conjunto de símbolos (no más de 16) y los intervalos de tiempo se especifican como string con los elementos separados por un punto y coma.  Los resultados del trabajo de este indicador se muestran en la Fig. 5:

Indicador de la revisión del mercado

Figura 5. Indicador de la observación del mercado

Hemos visto una forma de visualizar información de texto sobre el gráfico del terminal de cliente de MetaTrader 5.

Usando las clases de la librería estándar proporcionada con el terminal de cliente pudimos desarrollar de forma fácil y rápida la nueva funcionalidad para la representación de información textual en forma de tabla de dos dimensiones. El enfoque orientado a objeto de MQL5 es muy potente.


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

Archivos adjuntos |
marketwatch.mq5 (11.51 KB)
priceinfo.mq5 (8.76 KB)
pricelist.mq5 (7.29 KB)
textdisplay.mqh (15.54 KB)
marketwatch.mqh (9.48 KB)
Ejemplo simple de creación de un indicador usando la lógica difusa Ejemplo simple de creación de un indicador usando la lógica difusa

Este articulo está dedicado a la aplicación práctica del concepto de la lógica difusa al análisis de los mercados financieros. Proponemos el ejemplo del indicador que genera señales basadas en dos reglas difusas del indicador Envelopes. El indicador desarrollado utiliza varios buffers de indicador: 7 buffers para los cálculos, 5 buffers para la representación de los gráficos y 2 buffers de color.

MQL5 Wizard: crear asesores expertos sin programar MQL5 Wizard: crear asesores expertos sin programar

¿Quiere probar una estrategia de trading sin perder tiempo en programar? En el MQL5 Wizard puede seleccionar el tipo de señales de trading, añadir módulos de posiciones de arrastre y gestionar dinero, ¡y su trabajo ha terminado! Cree su propia implementación de módulos o encárguelos a través del servicio Trabajos y combine sus nuevos módulos con los que ya posee.

Construir un analizador de espectro Construir un analizador de espectro

Este artículo pretende que sus lectores se familiaricen con una posible variante del uso de los objetos gráficos en el lenguaje MQL5. Analiza un indicador que implementa un panel para la gestión de un analizador de espectro simple usando objetos gráficos. El artículo va dirigido a los lectores que están familiarizados con los conceptos básicos de MQL5.

Análisis técnico: ¿Qué analizamos? Análisis técnico: ¿Qué analizamos?

En este artículo se han intentado de analizar de forma global algunas peculiaridades de la representación de las cotizaciones accesibles para el análisis en el terminal MetaTrader. En el presente artículo no vamos a hablar de técnologías de programación, nuestro análisis tiene un carácter más general.