Experto comercial con interfaz gráfica: Creación del panel (Parte I)

13 septiembre 2018, 09:06
Anatoli Kazharski
0
3 404

Contenido

Introducción

A pesar del activo desarrollo del trading algorítmico, muchos tráders hasta ahora prefieren el comercio manual. Pero en nuestro caso, difícilmente podremos evitar la automatización de operaciones rutinarias.

En el artículo se muestra la creación de un experto de señal multisímbolo para el comercio manual. Como ejemplo, analizaremos las señales del indicador Stochastic del paquete estándar de la biblioteca. Este código podemos utilizarlo para crear nuestros propios expertos con interfaz gráfica: podemos conectarle cualquier otro indicador o usar los resultados de ciertos cálculos para tomar decisiones.

Para aquellos que ejecutan trabajos por encargo, este artículo puede ser útil como ejemplo de tarea técnica para mostrar a los clientes. Es posible que el ejemplo les ayude a ahorrar tiempo al preparar una tarea técnica comprensible para el programador a la hora de desarrollar un programa de interfaz gráfica.

Vamos a enumerar las cuestiones a analizar con detalle en este trabajo.

  • Creación de la interfaz gráfica.
  • Obtención de una lista de símbolos con las propiedades indicadas. 
  • Elementos de control de las operaciones comerciales. 
  • Alternancia rápida de símbolos y marcos temporales en los gráficos sin reinicializar el experto.
  • Gestión de las propiedades de los gráficos con la ayuda de la interfaz gráfica.
  • Obtención de señales del indicador a partir de multitud de símbolos con indicación de color.
  • Trabajo con posiciones abiertas. 
  • Actualizaciones de la biblioteca EasyAndFast.

El artículo se publicará en dos partes. En el presente artículo se analizará la creación de un panel, mientras que en la segunda parte se describirá la funcionalidad que lo llenará.

Elementos de la interfaz gráfica

Comenzaremos el desarrollo del experto por la construcción de la interfaz gráfica, con cuya ayuda se implementará la interacción con el usuario y la visualización de los datos. Podemos crear la interfaz gráfica usando las posibilidades de la bilioteca estándar, pero en nuestro ejemplo, se implementará sobre la base de EasyAndFast. Sus amplias posibilidades permiten concentrarse en la funcionalidad del propio programa sin distraerse en la mejora de su parte gráfica.

Primero dibujaremos el esquema general de la interfaz gráfica. En el esquema de abajo mostramos que en la ventana de la interfaz gráfica hay un grupo de dos pestañas. En las listas se muestran las funciones que debemos ubicar en ellas. Se trata de un ejemplo simplificado: el cliente y el ejecutor podrán trabajarlo con más detalle durante la discusión.

Fig. 1 - Esquema general de la interfaz gráfica con aclaraciones.

Fig. 1. Esquema general de la interfaz gráfica con aclaraciones.

Puede haber muchos elementos de control para la interfaz gráfica. Por eso, primero vamos a enumerarlos de forma jerárquica:

  • Formulario para los elementos de control
    • Barra de estado para mostrar la información adicional final
    • Grupo de pestañas:
      • Trade:
        • Campo de edición con casilla de verificación para filtrar la lista de símbolos
        • Botón para solicitar el inicio de la formación de la lista de símbolos
        • Botón para la operación comercial SELL
        • Botón para la operación comercial BUY
        • Campo de edición para establecer el volumen de la transacción
        • Botón para el cierre de todas las posiciones abiertas
        • Campo de edición para establecer el nivel de la señal de venta
        • Campo de edición para establecer el nivel de la señal de compra
        • Recuadro de símbolos para el comercio
        • Gráfico para visualizar los datos de los símbolos. Para mayor comodidad, haremos que varias propiedades del gráfico se puedan controlar con la ayuda del siguiente grupo de elementos:
          • Cuadro combinado con lista desplegable para seleccionar la alternancia del marco temporal 
          • Casilla de verificación para activar/desactivar la escala temporal 
          • Casilla de verificación para activar/desactivar la escala de precio 
          • Campo de edición para controlar la escala 
          • Botón para activar la sangría 
          • Casilla de verificación para mostrar el indicador 
      • Positions:
        • Recuadro de posiciones
    • Indicador para el proceso de reproducción de frames

En la clase principal del programa (CProgram) declaramos los métodos y ejemplares de las clases de los elementos enumerados más arriba. El código de los métodos para la creación de elementos se muestra en un archivo aparte y se incluye en el archivo con la clase del programa MQL:

//+------------------------------------------------------------------+
//| Clase para crear la aplicación                                   |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
private:
   //--- Ventana
   CWindow           m_window1;
   //--- Barra de estado
   CStatusBar        m_status_bar;
   //--- Pestañas
   CTabs             m_tabs1;
   //--- Campo de edición
   CTextEdit         m_symb_filter;
   CTextEdit         m_lot;
   CTextEdit         m_up_level;
   CTextEdit         m_down_level;
   CTextEdit         m_chart_scale;
   //--- Botones
   CButton           m_request;
   CButton           m_chart_shift;
   CButton           m_buy;
   CButton           m_sell;
   CButton           m_close_all;
   //--- Cuadros combinados
   CComboBox         m_timeframes;
   //--- Casilla de verificación
   CCheckBox         m_date_scale;
   CCheckBox         m_price_scale;
   CCheckBox         m_show_indicator;
   //--- Recuadros
   CTable            m_table_positions;
   CTable            m_table_symb;
   //--- Gráfico estándar
   CStandardChart    m_sub_chart1;
   //--- Barra de progreso
   CProgressBar      m_progress_bar;
   //---
public:
   //---Crea una interfaz gráfica
   bool              CreateGUI(void);
   //---
private:
   //--- Formulario
   bool              CreateWindow(const string text);
   //--- Barra de estado
   bool              CreateStatusBar(const int x_gap,const int y_gap);
   //--- Pestañas
   bool              CreateTabs1(const int x_gap,const int y_gap);
   //--- Campo de edición
   bool              CreateSymbFilter(const int x_gap,const int y_gap,const string text);
   bool              CreateLot(const int x_gap,const int y_gap,const string text);
   bool              CreateUpLevel(const int x_gap,const int y_gap,const string text);
   bool              CreateDownLevel(const int x_gap,const int y_gap,const string text);
   bool              CreateChartScale(const int x_gap,const int y_gap,const string text);
   //--- Botones
   bool              CreateRequest(const int x_gap,const int y_gap,const string text);
   bool              CreateChartShift(const int x_gap,const int y_gap,const string text);
   bool              CreateBuy(const int x_gap,const int y_gap,const string text);
   bool              CreateSell(const int x_gap,const int y_gap,const string text);
   bool              CreateCloseAll(const int x_gap,const int y_gap,const string text);
   //--- Cuadro combinado
   bool              CreateComboBoxTF(const int x_gap,const int y_gap,const string text);
   //--- Casilla de verificación
   bool              CreateDateScale(const int x_gap,const int y_gap,const string text);
   bool              CreatePriceScale(const int x_gap,const int y_gap,const string text);
   bool              CreateShowIndicator(const int x_gap,const int y_gap,const string text);
   //--- Recuadros
   bool              CreatePositionsTable(const int x_gap,const int y_gap);
   bool              CreateSymbolsTable(const int x_gap,const int y_gap);
   //--- Gráfico estándar
   bool              CreateSubChart1(const int x_gap,const int y_gap);
   //--- Barra de progreso
   bool              CreateProgressBar(const int x_gap,const int y_gap,const string text);
  };
//+------------------------------------------------------------------+
//| Métodos para crear los elementos de control                      |
//+------------------------------------------------------------------+
#include "CreateGUI.mqh"
//+------------------------------------------------------------------+

A continuación, vamos a analizar el montaje de la interfaz gráfica, los métodos para crear sus elementos y sus propiedades.

Montaje de la interfaz gráfica

Bien, en la interfaz gráfica de la aplicación desarrollada se usarán elementos de diez tipos diferentes.

  • Formulario para los elementos de control (CWindow)
  • Barra de estado (CStatusBar)
  • Grupo de pestañas (CTabs)
  • Campo de edición (CTextEdit)
  • Botón (CButton)
  • Cuadro combinado con lista desplegable (CComboBox)
  • Casilla de verificación (CCheckBox)
  • Recuadro (CTable)
  • Gráfico estándar (CStandardChart)
  • Barra de progreso (CProgressBar)

Vamos a necesitar más de un elemento en algunas categorías de esta lista, por eso analizaremos uno solo de cada grupo. En el mismo orden vamos a estudiar los métodos para crearlos. 

Formulario para los elementos de control

Más abajo mostramos el código del método para crear el formulario en el que se ubicarán los demás elementos. Al principio hay que añadir el formulario a la lista de elementos de la interfaz gráfica del programa. Para ello, debemos llamar el método CWndContainer::AddWindow(), transmitiendo al mismo el objeto del elemento del tipo CWindow. A continuación, antes de crear un formulario, establecemos sus propiedades. Indicamos las siguientes propiedades (en el mismo orden que en la lista de abajo):

  • Dimensiones del formulario (anchura y altura)
  • Tamaño de la fuente en el encabezado
  • Modo de desplazamiento del formulario (dentro del gráfico)
  • Modo de cambio manual del tamaño del formulario (arrastrando los bordes)
  • Botones del formulario. La visibilidad de cada botón se activa por separado. En este caso, se implican los siguientes:
    • Cierre del formulario. En el formulario principal del programa, al pulsar este botón, saldrá una ventana de diálogo en la que se preguntará si queremos cerrar el programa.
    • Maximizar y minimizar el formulario. 
    • Pistas emergentes para los elementos. Este botón tiene también dos estados. Cuando está pulsado, en los elementos de control se mostrarán pistas emergentes (con la condición de que estas hayan sido establecidas).
    • Expansión del formulario por todo el área del gráfico del terminal. Tras maximizar el formulario, es posible devolverlo a sus anteriores dimensiones con solo pulsar de nuevo este botón.
  • Para cada botón del formulario se puede establece una descripción emergente.

Después de que las propiedades hayan sido establecidas, hay que llamar el método de creación del formulario CWindow::CreateWindow() y transmitir al mismo:

  • el identificador del gráfico,
  • el número de la subventana del gráfico,
  • el texto del encabezado,
  • las coordenadas iniciales de la ubicación para el formulario.
//+------------------------------------------------------------------+
//| Crea el formulario para los elementos de control                 |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
  {
//--- Añadimos el puntero de la ventana a la matriz de ventanas
   CWndContainer::AddWindow(m_window1);
//--- Propiedades
   m_window1.XSize(750);
   m_window1.YSize(450);
   m_window1.FontSize(9);
   m_window1.IsMovable(true);
   m_window1.ResizeMode(true);
   m_window1.CloseButtonIsUsed(true);
   m_window1.CollapseButtonIsUsed(true);
   m_window1.TooltipsButtonIsUsed(true);
   m_window1.FullscreenButtonIsUsed(true);
//--- Establecemos las pistas emergentes
   m_window1.GetCloseButtonPointer().Tooltip("Close");
   m_window1.GetTooltipButtonPointer().Tooltip("Tooltips");
   m_window1.GetFullscreenButtonPointer().Tooltip("Fullscreen");
   m_window1.GetCollapseButtonPointer().Tooltip("Collapse/Expand");
//--- Creando el formulario
   if(!m_window1.CreateWindow(m_chart_id,m_subwin,caption_text,1,1))
      return(false);
//---
   return(true);
  }

Cada vez que se añada un nuevo elemento de la interfaz gráfica, sería deseable compilar el programa y ver qué resultado obtenemos: 

Fig. 2 - Formulario para los elementos de control.

Fig. 2. Formulario para los elementos de control.

Más abajo se mostrarán las capturas de pantalla de todos los resultados intermedios.

Barra de estado

El código del método para la creación de la barra comienza por la indicación del elemento principal. A partir de él, se calcularán el posicionamiento y la alineación según el tamaño de los elementos asociados al mismo. Esto ahorrará tiempo a la hora de desarrollar la aplicación: será posible desplazar un grupo completo de elementos relacionados con solo modificar las coordenadas del principal. Para anclar un elemento, su puntero se transmite al método CElement::MainPointer(). En este ejemplo, estamos anclando la barra de estado al formulario, por eso transmitimos el objeto del formulario al método.

A continuación, establecemos las propiedades de la barra de estado. En esta, crearemos tres puntos en los que se mostrará la información para el ususario. 

  • Para no indicar el tamaño con respecto al formulario, indicaremos que la anchura debe cambiar automáticamente con cada cambio del formulario.
  • La sangría del borde derecho del elemento con respecto al borde derecho del formulario será de 1 píxel.
  • Anclaremos la barra de estado a la parte inferior del formulario, para que, al cambiar la altura del formulario, este se corrija automáticamente con respecto a la parte inferior.
  • A continuación, al añadir los puntos, indicamos su anchura.

Después de que hayamos indicado las propiedades, creamos el elemento. Ahora que ya está preparado para el trabajo, podemos cambiar el texto en sus puntos mientras se ejecuta el programa. En nuestro ejemplo, definimos en el primer punto el texto «For Help, press F1»

Al final del método, tenemos que guardar necesariamente el puntero del elemento creado en la lista general de elementos de la interfaz gráfica. Para ello, llamamos el método CWndContainer::AddToElementsArray() y le transmitimos el índice del formulario y el objeto de elemento. Puesto que solo tenemos un formulario, su índice será 0.

//+------------------------------------------------------------------+
//| Crea la barra de estado                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateStatusBar(const int x_gap,const int y_gap)
  {
#define STATUS_LABELS_TOTAL 3
//--- Guardamos el puntero a la ventana
   m_status_bar.MainPointer(m_window1);
//--- Propiedades
   m_status_bar.AutoXResizeMode(true);
   m_status_bar.AutoXResizeRightOffset(1);
   m_status_bar.AnchorBottomWindowSide(true);
//--- Indicamos cuántas partes debe haber y definimos sus propiedades
   int width[STATUS_LABELS_TOTAL]={0,200,110};
   for(int i=0; i<STATUS_LABELS_TOTAL; i++)
      m_status_bar.AddItem(width[i]);
//--- Creamos el elemento de control
   if(!m_status_bar.CreateStatusBar(x_gap,y_gap))
      return(false);
//--- Definimos el texto en los puntos de la barra de estado
   m_status_bar.SetValue(0,"For Help, press F1");
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_status_bar);
   return(true);
  }

Los demás elementos de la biblioteca EasyAndFast se crean según el mismo principio. Por eso, a continuación, analizaremos solo las propiedades establecidas que se usarán en nuestro experto.

Fig. 3 - Adición de la barra de estado.

Fig. 3.  Adición de la barra de estado.

Grupo de pestañas

En el método para crear el grupo de pestañas, establecemos las siguientes propiedades para el elemento:

  • Centramos el texto en las pestañas.
  • Ubicamos las pestañas en la parte superior del área de trabajo.
  • El tamaño se ajustará automáticamente según el área del elemento principal (el formulario). En este caso, no es obligatorio indicar el tamaño del área del grupo de pestañas.
  • Indicamos las sangrías con respecto el borde derecho e inferior del elemento principal. Al cambiar el tamaño del formulario, estas sangrías se guardarán.
  • Al añadir las siguientes pestañas, al método también se transmite el nombre de la pestaña y su anchura.

Aquí tenemos el código del método:

//+------------------------------------------------------------------+
//| Crea el grupo con pestañas 1                                     |
//+------------------------------------------------------------------+
bool CProgram::CreateTabs1(const int x_gap,const int y_gap)
  {
#define TABS1_TOTAL 2
//--- Guardamos el puntero al elemento principal
   m_tabs1.MainPointer(m_window1);
//--- Propiedades
   m_tabs1.IsCenterText(true);
   m_tabs1.PositionMode(TABS_TOP);
   m_tabs1.AutoXResizeMode(true);
   m_tabs1.AutoYResizeMode(true);
   m_tabs1.AutoXResizeRightOffset(3);
   m_tabs1.AutoYResizeBottomOffset(25);
//--- Añadimos las pestañas con las propiedades indicadas
   string tabs_names[TABS1_TOTAL]={"Trade","Positions"};
   for(int i=0; i<TABS1_TOTAL; i++)
      m_tabs1.AddTab(tabs_names[i],100);
//--- Creamos el elemento de control
   if(!m_tabs1.CreateTabs(x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_tabs1);
   return(true);
  }

Fig. 4 - Adición del grupo de pestañas.

Fig. 4. Adición del grupo de pestañas.

Campo de edición

Como ejemplo, analizaremos el campo de edición en el que el usuario puede indicar las divisas y/o parejas de divisas para formar la lista de símbolos en el recuadro. El elemento principal para el mismo será un grupo de pestañas. Aquí tenemos que indicar en qué pestaña debemos representar este campo de edición. Para ello, llamaremos el método CTabs::AddToElementsArray(), transmitiremos al mismo el índice de la pestaña y el objeto del elemento unido.

A continuación, analizaremos las propiedades indicadas para este campo de edición.

  • Por defecto, en el campo de edición se introducirá el texto "USD": el programa recopilará en un recuadro los símbolos en los que haya USD. En este campo de edición se deben indicar las divisas y/o símbolos con una coma. Más abajo, mostramos el método con cuya ayuda se forma la lista de símbolos (según las líneas separadas con coma) en este campo de edición.
  • El campo de edición tendrá una casilla de verificación. Al desactivar la casilla de verificación, el texto en el campo de edición será ignorado, y en la lista de símbolos entrarán todas las parejas de divisas encontradas.
  • La anchura del campo de edición ocupará todo el ancho del elemento principal; al cambiar la anchura del área, también se corregirá.
  • A la derecha del campo de edición se encontrará el botón Request. Mientras el programa está funcionando, en el campo de edición se pueden indicar otras parejas de divisas y/o símbolos; para que la lista se forme, deberemos pulsar este botón. Puesto que se supone que la anchura del campo de edición cambia de forma automática, y el botón Request deberá hallarse siempre a su derecha, necesitaremos que siempre haya un especio libre con respecto al borde derecho del elemento. 

El elemento del tipo CTextEdit consta de varios elementos. Por eso, si es necesario cambiar sus propiedades, existe la posibilidad de obtener los punteros al mismo. Necesitaremos cambiar ciertaspropiedades del campo de edición de texto (CTextBox). Vamos a analizarlas en el mismo orden en que se realiza en la lista de código más abajo.

  • Sangría del campo de edición con respecto al borde izquierdo del elemento principal (CTextEdit). 
  • Cambio automático de la anchura con respecto al elemento principal.
  • Al activar el campo de edición (clic derecho sobre el campo de edición), el texto se seleccionará por completo de forma automática para una posible sustitución rápida.
  • Si en el campo de edición no hay texto en absoluto, se mostrará la sugerencia: «Example: EURUSD, GBP, NOK».  

Por defecto, la casilla de verificación del campo de edición se encuentra activada. Para hacer esto, debemos activarla justo después de crear el elemento.

//+--------------------------------------------------------------------------+
//| Crea la casilla de verificación con el campo de edición "Symbols filter" |
//+--------------------------------------------------------------------------+
bool CProgram::CreateSymbolsFilter(const int x_gap,const int y_gap,const string text)
  {
//--- Guardamos el puntero al elemento principal
   m_symb_filter.MainPointer(m_tabs1);
//--- Fijar para la pestaña
   m_tabs1.AddToElementsArray(0,m_symb_filter);
//--- Propiedades
   m_symb_filter.SetValue("USD"); // "EUR,USD" "EURUSD,GBPUSD" "EURUSD,GBPUSD,AUDUSD,NZDUSD,USDCHF"
   m_symb_filter.CheckBoxMode(true);
   m_symb_filter.AutoXResizeMode(true);
   m_symb_filter.AutoXResizeRightOffset(90);
   m_symb_filter.GetTextBoxPointer().XGap(100);
   m_symb_filter.GetTextBoxPointer().AutoXResizeMode(true);
   m_symb_filter.GetTextBoxPointer().AutoSelectionMode(true);
   m_symb_filter.GetTextBoxPointer().DefaultText("Example: EURUSD,GBP,NOK");
//--- Creamos el elemento de control
   if(!m_symb_filter.CreateTextEdit(text,x_gap,y_gap))
      return(false);
//--- Activamos la casilla de verificación
   m_symb_filter.IsPressed(true);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_symb_filter);
   return(true);
  }

Aparte del campo de edición de texto, en la interfaz gráfica también tendremos campos de edición numéricos. Por ejemplo, el campo Lot (volumen para la apertura de posición). Para los campos de edición de este tipo hay que indicar otras propiedades.

  • Anchura total del elemento.
  • Valor máximo a introducir.
  • Valor mínimo a introducir.
  • Salto al alternar los botones de incremento y decremento.
  • Número de dígitos decimales.
  • Para que el campo de edición sea numérico, tenemos que indicarlo con la ayuda del método CTextEdit::SpinEditMode().
  • Valor por defecto tras la carga del programa en el gráfico en el terminal.
  • Anchura del campo de edición.
  • Selección automática del texto en el campo de edición al clicar sobre él.
  • Acoplamiento del campo de edición al borde derecho del elemento.

Aquí tenemos el código de este método:

//+------------------------------------------------------------------+
//| Crea el campo de edición "Lot"                                   |
//+------------------------------------------------------------------+
bool CProgram::CreateLot(const int x_gap,const int y_gap,const string text)
  {
//--- Guardamos el puntero al elemento principal
   m_lot.MainPointer(m_tabs1);
//--- Fijar para la pestaña
   m_tabs1.AddToElementsArray(0,m_lot);
//--- Propiedades
   m_lot.XSize(80);
   m_lot.MaxValue(1000);
   m_lot.MinValue(0.01);
   m_lot.StepValue(0.01);
   m_lot.SetDigits(2);
   m_lot.SpinEditMode(true);
   m_lot.SetValue((string)0.1);
   m_lot.GetTextBoxPointer().XSize(50);
   m_lot.GetTextBoxPointer().AutoSelectionMode(true);
   m_lot.GetTextBoxPointer().AnchorRightWindowSide(true);
//--- Creamos el elemento de control
   if(!m_lot.CreateTextEdit(text,x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_lot);
   return(true);
  }

Fig. 5 - Adición de los campos de edición.

Fig. 5.  Adición de los campos de edición.

La imagen tiene un aspecto bastante lógico, pero al añadir los demás elementos, todo resultará en su sitio.

Botón

Añadimos a la interfaz gráfica de nuestro experto varios botones. Analizaremos aquel de ellos en el se han implicado más propiedades: el botón para la apertura de posiciones SELL.

  • Anchura del botón.
  • El texto del botón se encontrará en el centro, tanto en vertical, como en horizontal.
  • Color del fondo del botón.
  • Color de fondo al poner el cursor.
  • Color de fondo al clicar con el botón izquierdo.
  • Color del texto del botón.
  • Color del texto al poner el cursor.
  • Color del texto al clicar con el botón izquierdo.
  • Color del marco del botón.
  • Color del marco al poner el cursor.
  • Color del marco al clicar con el botón izquierdo.

Las mismas propiedades se cambian también en el botón BUY, con la única diferencia de los matices establecidos para el fondo.

//+------------------------------------------------------------------+
//| Crea el botón 'Sell'                                             |
//+------------------------------------------------------------------+
bool CProgram::CreateSell(const int x_gap,const int y_gap,const string text)
  {
//--- Guardar el puntero al elemento principal
   m_sell.MainPointer(m_tabs1);
//--- Fijar para la pestaña
   m_tabs1.AddToElementsArray(0,m_sell);
//--- Propiedades
   m_sell.XSize(80);
   m_sell.IsCenterText(true);
   m_sell.BackColor(C'255,51,51');
   m_sell.BackColorHover(C'255,100,100');
   m_sell.BackColorPressed(C'195,0,0');
   m_sell.LabelColor(clrWhite);
   m_sell.LabelColorHover(clrWhite);
   m_sell.LabelColorPressed(clrWhite);
   m_sell.BorderColor(clrBlack);
   m_sell.BorderColorHover(clrBlack);
   m_sell.BorderColorPressed(clrBlack);
//--- Creamos el elemento de control
   if(!m_sell.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Añadimos el puntero al elemento en la base
   CWndContainer::AddToElementsArray(0,m_sell);
   return(true);
  }

Fig. 6 - Adición de botones.

Fig. 6. Adición de botones.

Cuadro combinado con lista desplegable

Para cambiar el marco temporal, haremos un cuadro combinado con lista desplegable. Enumeramos las propiedades para su ajustes.

  • Anchura total del elemento. 
  • Cantidad de puntos en la lista (en nuestro caso, 21, de acuerdo con el número de marcos temporales en el terminal).
  • El elemento se anclará a la parte derecha del área de pestañas.
  • Anchura del botón del cuadro combinado.
  • El botón está anclado a la parte derecha del elemento.

A continuación, a cada punto en la lista se le asignan valores, y después, se le asignan ciertas propiedades a la lista con la ayuda del puntero.

  • Destacar los puntos con la ayuda del cursor.
  • Punto destacado. En este caso, se tratará del punto con el índice 18 (marco temporal D1).

Aquí tenemos el código del método para crear este cuadro combinado:

//+------------------------------------------------------------------+
//| Creamos el cuadro combinado para elegir marcos temporales        |
//+------------------------------------------------------------------+
bool CProgram::CreateComboBoxTF(const int x_gap,const int y_gap,const string text)
  {
//--- Número total de puntos en la lista
#define ITEMS_TOTAL2 21
//--- Transmitir el objeto del panel
   m_timeframes.MainPointer(m_tabs1);
//--- Fijar para la pestaña
   m_tabs1.AddToElementsArray(0,m_timeframes);
//--- Propiedades
   m_timeframes.XSize(115);
   m_timeframes.ItemsTotal(ITEMS_TOTAL2);
   m_timeframes.AnchorRightWindowSide(true);
   m_timeframes.GetButtonPointer().XSize(50);
   m_timeframes.GetButtonPointer().AnchorRightWindowSide(true);
//--- Guardamos los valores de los puntos en la lista del cuadro combinado
   string items_text[ITEMS_TOTAL2]={"M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30","H1","H2","H3","H4","H6","H8","H12","D1","W1","MN"};
   for(int i=0; i<ITEMS_TOTAL2; i++)
      m_timeframes.SetValue(i,items_text[i]);
//--- Obtenemos el puntero de la lista
   CListView *lv=m_timeframes.GetListViewPointer();
//--- Establecemos las propiedades de la lista
   lv.LightsHover(true);
   lv.SelectItem(18);
//--- Creamos el elemento de control
   if(!m_timeframes.CreateComboBox(text,x_gap,y_gap))
      return(false);
//--- Añadimos el puntero al elemento en la base
   CWndContainer::AddToElementsArray(0,m_timeframes);
   return(true);
  }

Fig. 7 - Añadir cuadro combinado.

Fig. 7. Añadir cuadro combinado.

Casilla de verificación

La casilla de verificación es el elemento más sencillo. Para este, basta con indicar dos propiedades.

  • La anchura.
  • La ubicación en la parte derecha del elemento principal.

Después de crear el elemento, podemos activar la casilla de verificación de forma programática.

//+------------------------------------------------------------------+
//| Crea la casilla de verificación "Date scale"                     |
//+------------------------------------------------------------------+
bool CProgram::CreateDateScale(const int x_gap,const int y_gap,const string text)
  {
//--- Guardamos el puntero a la ventana
   m_date_scale.MainPointer(m_tabs1);
//--- Fijar para la pestaña
   m_tabs1.AddToElementsArray(0,m_date_scale);
//--- Propiedades
   m_date_scale.XSize(70);
   m_date_scale.AnchorRightWindowSide(true);
//--- Creamos el elemento de control
   if(!m_date_scale.CreateCheckBox(text,x_gap,y_gap))
      return(false);
//--- Activar la casilla de verificación
   m_date_scale.IsPressed(true);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_date_scale);
   return(true);
  }

Fig. 8 - Añadir casillas de verificación.

Fig. 8. Añadir casillas de verificación.

Recuadro

En la interfaz gráfica tendremos dos recuadros. Analizaremos aquel que hace visible la lista formada de símbolos y señales para la apertura de posiciones. Se ubicará en la primera pestaña. Primero declaramos e inicializamos las matrices para establecer las propiedades del recuadro. Ajustamos las siguientes propiedades.

  • Anchura del elemento.
  • Dimensión del recuadro (número de columnas y líneas).
  • Anchura de las columnas (los valores se transmiten en la matriz).
  • Alineamiento del texto (los valores se transmiten en la matriz).
  • Sangría para el texto con respecto a los bordes de las celdas.
  • Mostrar los encabezados.
  • Posibilidad de destacar líneas.
  • Posibilidad de cambiar el tamaño de las columnas manualmente, abarcando el límite del encabezado.
  • Mostrar con el formato en el estilo «Zebra».
  • Cambio automático del tamaño en vertical con respecto al elemento principal.
  • Sangría con respecto al borde inferior del elemento principal.

El texto para los encabezados se puede indicar después de crear el recuadro:

//+------------------------------------------------------------------+
//| Crea el recuadro de símbolos                                     |
//+------------------------------------------------------------------+
bool CProgram::CreateSymbolsTable(const int x_gap,const int y_gap)
  {
#define COLUMNS1_TOTAL 2
#define ROWS1_TOTAL    1
//--- Guardamos el puntero al elemento principal
   m_table_symb.MainPointer(m_tabs1);
//--- Fijar para la pestaña
   m_tabs1.AddToElementsArray(0,m_table_symb);
//--- Matriz de anchura de las columnas
   int width[COLUMNS1_TOTAL]={95,58};
//--- Matriz de alineamiento del texto en columnas
   ENUM_ALIGN_MODE align[COLUMNS1_TOTAL]={ALIGN_LEFT,ALIGN_RIGHT};
//--- Matriz de sangría del texto en las columnas según el eje X
   int text_x_offset[COLUMNS1_TOTAL]={5,5};
//--- Propiedades
   m_table_symb.XSize(168);
   m_table_symb.TableSize(COLUMNS1_TOTAL,ROWS1_TOTAL);
   m_table_symb.ColumnsWidth(width);
   m_table_symb.TextAlign(align);
   m_table_symb.TextXOffset(text_x_offset);
   m_table_symb.ShowHeaders(true);
   m_table_symb.SelectableRow(true);
   m_table_symb.ColumnResizeMode(true);
   m_table_symb.IsZebraFormatRows(clrWhiteSmoke);
   m_table_symb.AutoYResizeMode(true);
   m_table_symb.AutoYResizeBottomOffset(2);
//--- Creamos el elemento de control
   if(!m_table_symb.CreateTable(x_gap,y_gap))
      return(false);
//--- Establecemos los nombres de los encabezados
   m_table_symb.SetHeaderText(0,"Symbol");
   m_table_symb.SetHeaderText(1,"Values");
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_table_symb);
   return(true);
  }

El segundo recuadro representa algunas propiedades de las posiciones abiertas. Sus diez columnas representarán los siguientes datos.

  • Símbolo de la posición.
  • Número de posiciones.
  • Volumen total de todas las posiciones abiertas.
  • Volumen de las posiciones BUY.
  • Volumen de las posiciones SELL.
  • Resultado total actual de todas las posiciones abiertas.
  • Resultado actual de todas las posiciones abiertas BUY.
  • Resultado actual de todas las posiciones abiertas SELL.
  • Carga del depósito de cada símbolo por separado.
  • Precio medio.

En el segundo recuadro hay que configurar de forma adicional las siguientes propiedades.

  • Sangría para las imágenes con respecto a los bordes derecho y superior de las celdas.
  • Posibilidad de clasificar los valores.
  • Cambio automático del tamaño en horizontal con respecto al elemento principal.
  • Sangría con respecto al límite derecho del elemento principal.

Las imágenes en las celdas de la primera columna simbolizarán los botones: al pulsar estos, podremos cerrar una posición o todas las posiciones - si se trata de una cuenta con cobertura - en el símbolo indicado. 

//+------------------------------------------------------------------+
//| Crea un recuadro de posiciones                                   |
//+------------------------------------------------------------------+
bool CProgram::CreatePositionsTable(const int x_gap,const int y_gap)
  {
...
//--- Propiedades
   m_table_positions.TableSize(COLUMNS2_TOTAL,ROWS2_TOTAL);
   m_table_positions.ColumnsWidth(width);
   m_table_positions.TextAlign(align);
   m_table_positions.TextXOffset(text_x_offset);
   m_table_positions.ImageXOffset(image_x_offset);
   m_table_positions.ImageYOffset(image_y_offset);
   m_table_positions.ShowHeaders(true);
   m_table_positions.IsSortMode(true);
   m_table_positions.SelectableRow(true);
   m_table_positions.ColumnResizeMode(true);
   m_table_positions.IsZebraFormatRows(clrWhiteSmoke);
   m_table_positions.AutoXResizeMode(true);
   m_table_positions.AutoYResizeMode(true);
   m_table_positions.AutoXResizeRightOffset(2);
   m_table_positions.AutoYResizeBottomOffset(2);
...
   return(true);
  }

Fig. 9 - Añadir un recuadro en la primera pestaña.

Fig. 9. Añadir un recuadro en la primera pestaña.

Fig. 10 - Añadir un recuadro en la segunda pestaña.

Fig. 10.  Añadir un recuadro en la segunda pestaña.

Hablaremos sobre cómo trabajar con recuadros en el archivo principal del programa (CProgram) en uno de los siguientes apartados del artículo.

Gráfico estándar

Para visualizar los datos de los símbolos se ha pensado un elemento del tipo CStandardChart. Por defecto, se mostrará el gráfico EURUSD en el marco temporal diario. Tiene las siguientes propiedades.

  • Desplazamiento horizontal.
  • Ajuste automático de la anchura.
  • Ajuste automático de la altura.
  • Sangría con respecto al límite derecho del elemento principal.
  • Sangría con respecto al borde inferior del elemento principal.

En caso necesario, podemos crear una matriz de gráficos construidos en la fila horizontal. Para ello, use el método CStandardChart::AddSubChart(), transmitiendo en el mismo el símbolo y el marco temporal como argumentos. Pero en este caso, necesitaremos solo un gráfico; los símbolos y marcos temporales los alternaremos con la ayuda de otros elementos de control.

//+------------------------------------------------------------------+
//| Crea el gráfico estándar 1                                       |
//+------------------------------------------------------------------+
bool CProgram::CreateSubChart1(const int x_gap,const int y_gap)
  {
//--- Guardamos el puntero a la ventana
   m_sub_chart1.MainPointer(m_tabs1);
//--- Fijar para la 1-era pestaña
   m_tabs1.AddToElementsArray(0,m_sub_chart1);
//--- Propiedades
   m_sub_chart1.XScrollMode(true);
   m_sub_chart1.AutoXResizeMode(true);
   m_sub_chart1.AutoYResizeMode(true);
   m_sub_chart1.AutoXResizeRightOffset(125);
   m_sub_chart1.AutoYResizeBottomOffset(2);
//--- Añadimos los gráficos
   m_sub_chart1.AddSubChart("EURUSD",PERIOD_D1);
//--- Creamos el elemento de control
   if(!m_sub_chart1.CreateStandardChart(x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_sub_chart1);
   return(true);
  }

Fig. 11 - Adición del gráfico.

Fig. 11.  Adición del gráfico.

Barra de progreso

Para que el usuario entienda qué está haciendo ahora el programa, añadiremos a la interfaz gráfica el indicador de progreso. Aquí tenemos las propiedades de nuestro ejemplo (en el mismo orden que en el código).

  • Altura total del elemento.
  • Altura del indicador (barra de progreso).
  • Sangría del indicador con respecto al eje X.
  • Sangría del indicador con respecto al eje Y.
  • Sangría de la etiqueta principal con respecto al eje X.
  • Sangría de la etiqueta principal con respecto al eje Y.
  • Sangría de la etiqueta de porcentaje con respecto al eje X.
  • Sangría de la etiqueta de porcentaje con respecto al eje Y.
  • Atributo del elemento desplegado (para el ocultamiento automático). 
  • Fuente. 
  • Color del marco del indicador.
  • Color del fondo del indicador.
  • Color de la barra de progreso del indicador.
  • Ajuste automático de la anchura.
  • Sangría con respecto al límite derecho del elemento principal.

Más abajo, mostramos ejemplos del uso del indicador de progreso.

//+------------------------------------------------------------------+
//| Crea una barra de progreso                                       |
//+------------------------------------------------------------------+
bool CProgram::CreateProgressBar(const int x_gap,const int y_gap,const string text)
  {
//--- Guardamos el puntero al elemento principal
   m_progress_bar.MainPointer(m_status_bar);
//--- Propiedades
   m_progress_bar.YSize(17);
   m_progress_bar.BarYSize(14);
   m_progress_bar.BarXGap(0);
   m_progress_bar.BarYGap(1);
   m_progress_bar.LabelXGap(5);
   m_progress_bar.LabelYGap(2);
   m_progress_bar.PercentXGap(5);
   m_progress_bar.PercentYGap(2);
   m_progress_bar.IsDropdown(true);
   m_progress_bar.Font("Consolas");
   m_progress_bar.BorderColor(clrSilver);
   m_progress_bar.IndicatorBackColor(clrWhiteSmoke);
   m_progress_bar.IndicatorColor(clrLightGreen);
   m_progress_bar.AutoXResizeMode(true);
   m_progress_bar.AutoXResizeRightOffset(2);
//--- Creación del elemento
   if(!m_progress_bar.CreateProgressBar(text,x_gap,y_gap))
      return(false);
//--- Añadimos el puntero al elemento en la base
   CWndContainer::AddToElementsArray(0,m_progress_bar);
   return(true);
  }

Hemos analizado todos los elementos de control que se usarán en la interfaz gráfica de nuestro experto. En este momento, se trata simplemente de un envoltorio gráfico. Pero, más tarde, escribiremos los métodos necesarios para que todo funcione de acuerdo con la idea incial.

Actualizaciones de la biblioteca EasyAndFast

En la biblioteca EasyAndFasten la clase CTable, se ha desarrollado por completo el método público CTable::SortData(). Ahora, como segundo argumento podemos indicar la dirección de clasificación del recuadro (parámetro no obligatorio). Antes, una nueva llamada del método CTable::SortData() comenzaba la clasificación en la dirección opuesta a la actual. Asimismo, se han añadido los métodos para obtener la dirección de clasificación actual y el índice de la columna clasificada. Ahora, si el recuadro ha sido clasificado por el usuario manualmente (con un clic en el encabezado), y a continuación los datos del recuadro no han sido actualizados en el mismo orden, entonces, tras aclarar la dirección de clasificación actual, podemos restablecerlo. 

//+------------------------------------------------------------------+
//| Clase para crear el recuadro dibujado                            |
//+------------------------------------------------------------------+
class CTable : public CElement
  {
public:
...
   //--- Clasificar los datos de la columna indicada
   void              SortData(const uint column_index=0,const int direction=WRONG_VALUE);
   //--- (1) Dirección de clasificación actual, (2) índice de la matriz clasificada
   int               IsSortDirection(void)             const { return(m_last_sort_direction);    }
   int               IsSortedColumnIndex(void)         const { return(m_is_sorted_column_index); }
...
  };
//+------------------------------------------------------------------+
//| Clasificar los datos de la columna indicada                      |
//+------------------------------------------------------------------+
void CTable::SortData(const uint column_index=0,const int direction=WRONG_VALUE)
  {
//--- Salir, si salimos de los límites del recuadro
   if(column_index>=m_columns_total)
      return;
//--- Índice a partir del cual hay que comenzar la clasificación
   uint first_index=0;
//--- Último índice
   uint last_index=m_rows_total-1;
//--- Sin control de dirección por parte del usuario
   if(direction==WRONG_VALUE)
     {
      //--- La primera vez se clasificará en orden creciente, y a continuación, todas las veces en dirección opuesta
      if(m_is_sorted_column_index==WRONG_VALUE || column_index!=m_is_sorted_column_index || m_last_sort_direction==SORT_DESCEND)
         m_last_sort_direction=SORT_ASCEND;
      else
         m_last_sort_direction=SORT_DESCEND;
     }
   else
     {
      m_last_sort_direction=(ENUM_SORT_MODE)direction;
     }
//--- Recordamos el índice de la última columna de datos clasificada
   m_is_sorted_column_index=(int)column_index;
//--- Clasificando
   QuickSort(first_index,last_index,column_index,m_last_sort_direction);
  }

Se ha hecho otra pequeña adición a la clase CKeys en el método CKeys::KeySymbol(). Anteriormente, el teclado numérico (un bloque de teclas aparte en la zona derecha del teclado) no se procesaba. Ahora podemos introducir cifras desde esta parte del teclado, y también símbolos especiales.

//+------------------------------------------------------------------+
//| Retorna el símbolo de la tecla pulsada                           |
//+------------------------------------------------------------------+
string CKeys::KeySymbol(const long key_code)
  {
   string key_symbol="";
//--- Si hay que introducir un espacio (la tecla "Space")
   if(key_code==KEY_SPACE)
     {
      key_symbol=" ";
     }
//--- Si hay que introducir (1) un símbolo alfabético o (2) un símbolo del teclado numérico o (3) un símbolo especial
   else if((key_code>=KEY_A && key_code<=KEY_Z) ||
           (key_code>=KEY_0 && key_code<=KEY_9) ||
           (key_code>=KEY_NUMLOCK_0 && key_code<=KEY_NUMLOCK_SLASH) ||
           (key_code>=KEY_SEMICOLON && key_code<=KEY_SINGLE_QUOTE))
     {
      key_symbol=::ShortToString(::TranslateKey((int)key_code));
     }
//--- Retornar símbolo
   return(key_symbol);
  }

Las nuevas versiones de las clases CTable y CKeys podemos descargarlas al final del artículo.

Conclusión

Usted ya ha leído la primera parte del artículo. En esta hemos analizado cómo podemos crear sin grandes esfuerzos interfaces gráficas de cualquier nivel de complejidad para nuestros programas. Usted puede seguir desarrollado este programa y usarlo para sus propios propósitos. En la segunda parte del artículo mostraremos cómo trabajar con la interfaz gráfica y, lo más importante, cómo llenarla con una funcionalidad. 

Más abajo podrá descargar a su computadora los archivos para realizar las pruebas y analizar con más detalle el código adjunto al artículo.

Nombre del archivo Comentarios
MQL5\Experts\TradePanel\TradePanel.mq5 Experto comercial para el comercio manual con la interfaz gráfica
MQL5\Experts\TradePanel\Program.mqh Archivo con la clase del programa
MQL5\Experts\TradePanel\CreateGUI.mqh Archivo con la implementación de los métodos para crear una interfaz gráfica a partir de la clase de programa en el archivo Program.mqh
MQL5\Include\EasyAndFastGUI\Controls\Table.mqh Clase actualizada CTable
MQL5\Include\EasyAndFastGUI\Keys.mqh Clase actualizada CKeys


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

Archivos adjuntos |
MQL5.zip (48 KB)
Neuroredes profundas (Parte VII). Conjunto de neuroredes: stacking Neuroredes profundas (Parte VII). Conjunto de neuroredes: stacking

Continuamos construyendo conjuntos. Ahora vamos a añadir al conjunto bagging creado anteriormente un combinador entrenable: una red neuronal profunda. Una red neuronal combina las mejores 7 salidas del conjunto después de la poda. La segunda recibe en la entrada las 500 salidas del conjunto, las poda y las combina. Construiremos las redes neuronales con la ayuda del paquete keras/TensorFlow de Python. Veremos brevemente las posibilidades del paquete. Y finalmente, realizaremos la simulación y compararemos la calidad de la clasificación de los conjuntos bagging y stacking.

Cómo crear una Tarea Técnica al encargar un robot Cómo crear una Tarea Técnica al encargar un robot

¿Ha desarrollado usted una estrategia comercial y negocia con ella? Si las normas de su sistema se pueden componer bien en un algoritmo programático, entonces será mejor que un robot comercie por usted. Un robot no duerme, no come y no es vulnerable a las debilidades humanas. En este artículo le mostraremos cómo crear una Tarea Técnica al encargar un robot comercial en Freelance.

Monitoreo de la cuenta comercial: una herramienta imprescindible para el tráder Monitoreo de la cuenta comercial: una herramienta imprescindible para el tráder

El monitoreo de la cuenta comercial es un informe detallado de todas las transacciones realizadas.

14 000 robots comerciales en MetaTrader Market 14 000 robots comerciales en MetaTrader Market

En la mayor tienda de aplicaciones comerciales ya tiene a su disposición 13 970 productos. Entre ellos, encontrará 4 800 robots, 6 500 indicadores, 2 400 utilidades y otras soluciones. En este caso, además, la mitad de las aplicaciones (6 000) se pueden alquilar. Una cuarta parte del total de los productos (3 800) es de acceso gratuito.