Instrumental para el comercio manual rápido: Trabajando con órdenes abiertas y órdenes pendientes

4 noviembre 2020, 14:25
Alexander Fedosov
0
644

Contenido

Introducción

En el artículo anterior, creamos una funcionalidad básica diseñada para simplificar y acelerar el trabajo de los tráders que se dedican al comercio manual. En primer lugar, nos centramos en facilitar la definición de ciertas solicitudes, es decir, en conseguir un instrumental más afinado para el ingreso en el mercado, con su funcionalidad correspondiente. No obstante, ya sabemos desde hace mucho que cualquier estrategia comercial, sea esta manual o automática, debe contener tres etapas básicas para trabajar con los mercados. Nos referimos a las reglas de ingreso en el mercado, el monitoreo de las posiciones abiertas y las condiciones de cierre de estas. En cuanto a la funcionalidad actual, solo hemos desarrollado su primera etapa, así que resultaría lógico añadir más recursos para trabajar con las posiciones que ya están abiertas o están pendientes, y también ampliar las condiciones para el cierre de transacciones. En este caso, todos los cálculos deben ofrecerse al instrumental, mientras que la propia decisión se dejan al tráder. 


Formulando la tarea

Vamos a empezar determinando el rango de tareas requeridas para implementar la nueva funcionalidad. Para ello, destacaremos las principales etapas que marcarán el posterior desarrollo de la aplicación actual:

  • Rediseño de la estructura de la aplicación. Originalmente, la aplicación se creó como una ventana principal con un grupo de botones para tareas. Dos de ellos abrían ventanas modales con herramientas para colocar posiciones de mercado y órdenes pendientes. Los tres botones restantes ofrecían una funcionalidad sencilla para cerrar posiciones según el mercado y eliminar todas las órdenes pendientes. Ahora, necesitamos añadir una interfaz para gestionar las posiciones actuales y editar las órdenes pendientes.
  • Ampliación de los modos de trabajo con órdenes. Anteriormente, podíamos cerrar o bien todas las órdenes rentables, o bien todas las órdenes con pérdidas. Ahora, necesitamos una funcionalidad más flexible, dividida según el tipo de posición, y que además proporcione la posibilidad de establecer varias condiciones tanto para cerrar todas las órdenes, como solo las seleccionadas. Respecto a las órdenes pendientes, solo teníamos la posibilidad de cerrar en masa las órdenes creadas anteriormente con el instrumental. Y esto no es suficiente: deberíamos poder eliminar una orden pendiente aparte o modificar esta. Asimismo, sería positivo tener listas separadas para las órdenes de mercado y las órdenes pendientes.

Vamos a analizar con mayor detalle cada una de las etapas por separado. 


Rediseñando la estructura de la aplicación

Para comprender todo con mayor claridad, debemos recordar lo que tenemos en estos momentos. La fig.1 muestra los bloques principales, que realizan tareas en dos categorías: apertura/creación y cierre/eliminación. La tercera categoría, como hemos escrito más arriba, se encarga del acompañamiento y la edición manuales.


Fig.1 Bloques principales del instrumental

Así, introduciremos tres pestañas para las categorías. En la primera de ellas, dejaremos la funcionalidad representada en la fig.1, mientras que en las otras dos, se localizará el trabajo con las posiciones de mercado y las órdenes pendientes.


Fig.2 Nueva estructura de la aplicación

En la pestaña Trading tendremos todo lo que antes se encontraba en la ventana principal del instrumental. En la pestaña Control de Posiciones Abiertas se encontrará un recuadro con las posiciones ya existentes, abiertas con esta aplicación. Asimismo, tendremos la posibilidad de trabajar con ellas. El control de órdenes pendientes también contendrá solo aquellas posiciones creadas por el instrumental, así como los elementos de la interfaz para cerrar y editar las órdenes colocadas. Vamos a echar un vistazo más detallado.


Fig.3 Pestaña Control de Posiciones de Mercado

Bien, en la fig.3 se encuentra la Pestaña Control de Posiciones de Mercado Esta incluye los siguientes elementos:

  • Recuadro de posiciones de mercado. Representa la información de todas las posiciones abiertas por esta aplicación. Es semejante al recuadro que se encuentra en la pestaña Comercio en el terminal MetaTrader 5. 
  • Tres campos de edición. Se corresponden y relacionan con las columnas de izquierda a derecha: Volumen, Stop Loss, Take Profit.
  • Dos botones. Al pulsar en una línea del recuadro en los campos de edición, se representarán los parámetros de la posición que se puedan editar. Así, al pulsar en Cambiar, podremos modificar el Stop Loss o el Take Profit de una posición seleccionada en el recuadro, o eliminar estos. El botón Cerrar cierra una posición al precio actual. Debemos notar que, al realizar el cierre, se comprueba el campo de edición Volumen. Es decir, podemos seleccionar un valor de lote menor que el lote de la posición actual y cerrar la posición parcialmente. 

Ahora, en la fig.4, vamos a analizar la pestaña Control de Órdenes Pendientes.


Fig.4 Control de Órdenes Pendientes.

Aquí, todo es prácticamente igual:

  • Recuadro de órdenes pendientes. Contiene la lista de órdenes pendientes abiertas con este instrumental.
  • Tres campos de edición. Aquí, al elegir una orden pendiente en el recuadro, podremos cambiar su precio de ejecución actual, y también modificar o eliminar un Stop-Loss o un Take-Profit.
  • Dos botones. El botón Cambiar, a diferencia del recuadro de posiciones de mercado, accede a los tres campos de edición para editar la orden pendiente seleccionada. El botón Cerrar elimina la orden.

Vamos a volver a la pestaña Comercio, que también tendrá cambios importantes. En primer lugar, completaremos el instrumental existente para cerrar posiciones tanto rentables como con pérdidas. Ahora podremos cerrar solo posiciones largas o cortas. Asimismo, presentaremos los conmutadores de los modos de cierre.


Fig.5 Ampliando la funcionalidad de cierre de órdenes de mercado.

Como podemos ver en la fig.5, tenemos 4 nuevos botones: Cerrar BUY rentables, Cerrar SELL rentables, Cerrar BUY con pérdidas, Cerrar SELL con pérdidas. Hay conmutadores adicionales a la derecha de los botones; merece la pena analizar estos con más detalle. Los conmutadores resultan similares, por lo que las siguientes descripciones se aplicarán a todos ellos.

  • All. Valor por defecto. Aplicado a los botones, no establece ninguna restricción, y cierra además todos los elementos seleccionados.
  • >Point. Cierra todas las posiciones del tipo seleccionado con un beneficio/pérdidas mayores que el número de puntos especificado.
  • >Currency. Cierra todas las posiciones del tipo seleccionado con un beneficio/pérdidas mayores que la cantidad especificada en la divisa de depósito.
  • Sum>Points. Cierra todas las posiciones del tipo seleccionado con un beneficio/pérdidas totales mayores que la cantidad especificada en la divisa de depósito.
  • Sum>Currency. Cierra todas las posiciones del tipo seleccionado con un beneficio/pérdidas total mayor que la cantidad especificada en la divisa de depósito.
Observación importante: Al seleccionar los últimos dos puntos, los valores se calculan solo para las posiciones abiertas por esta aplicación con el número mágico establecido. Asimismo, la comprobación de los valores se produce en todas las posiciones que cumplan con dos criterios: el tipo de posición y el número mágico.

Para mayor claridad, ofrecemos un ejemplo con la configuración de la Fig. 5: Cerrar Todas las Posiciones con Pérdidas y la opción sum>currency. En este caso, el instrumental encontrará todas las posiciones abiertas por él, sumará su beneficio y, si es superior a 10 unidades en la divisa del depósito, las cerrará todas.


Implementando los complementos del instrumental

Como base, usaremos el anterior proyecto al final del artículo Instrumental para el comercio manual rápido: Funcionalidad básica. En primer lugar, necesitaremos construir de nuevo la estructura de la ventana principal como se muestra en la figura 2. Para ello, añadiremos el elemento de interfaz Pestaña creando en la clase básica CProgram el método CreateTabs() e implementándolo en el archivo MainWindow.mqh.

//+------------------------------------------------------------------+
//| Create a group with tabs                                         |
//+------------------------------------------------------------------+
bool CFastTrading::CreateTabs(const int x_gap,const int y_gap)
{
//--- Store the pointer to the main control
   m_tab.MainPointer(m_main_window);
//--- Properties
   m_tab.Font(m_base_font);
   m_tab.FontSize(m_base_font_size);
   m_tab.LabelColor(clrWhite);
   m_tab.LabelColorHover(clrWhite);
   m_tab.IsCenterText(true);
   m_tab.AutoXResizeMode(true);
   m_tab.AutoYResizeMode(true);
   m_tab.AutoXResizeRightOffset(5);
   m_tab.AutoYResizeBottomOffset(5);
   m_tab.TabsYSize(27);
   m_tab.GetButtonsGroupPointer().Font(m_base_font);
   m_tab.GetButtonsGroupPointer().FontSize(m_base_font_size);
//--- Add tabs with the specified properties
   string tabs_names[3];
   tabs_names[0]=TRADING;
   tabs_names[1]=CAPTION_M_CONTROL_NAME;
   tabs_names[2]=CAPTION_P_CONTROL_NAME;
   for(int i=0; i<3; i++)
      m_tab.AddTab(tabs_names[i],180);
//--- Create a control element
   if(!m_tab.CreateTabs(x_gap,y_gap))
      return(false);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,m_tab);
   return(true);
}

En el cuerpo del método creado se han introducido tres nuevas macrosustituciones que sirven como encabezados de las pestañas, por eso debemos añadirlas en los dos idiomas al archivo Defines.mqh:

#define TRADING                        (m_language==RUSSIAN ? "Трейдинг" : "Trading")
#define CAPTION_M_CONTROL_NAME         (m_language==RUSSIAN ? "Контроль рыночных позиций" : "Market Control")
#define CAPTION_P_CONTROL_NAME         (m_language==RUSSIAN ? "Контроль отложенных ордеров" : "Pending Control")

Antes de aplicar el método que acabamos de crear, necesitaremos reconstruir los métodos que crean botones y vincular estos métodos a la primera pestaña. Para ello, vamos a buscar el método común CreateButton() que crea ambos y a editarlo de la forma siguiente:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateButton(CButton &button,string text,color baseclr,int x_gap,int y_gap)
{
//--- Store the window pointer
   button.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(0,button);
//--- Set properties before creation
   button.XSize(180);
   button.YSize(40);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(baseclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(baseclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Create a control element
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Add a pointer to the element to the database
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}

A continuación, aplicamos los cambios actuales en el método para crear la ventana principal.

//+------------------------------------------------------------------+
//| Create a form for orders                                         |
//+------------------------------------------------------------------+
bool CFastTrading::CreateMainWindow(void)
{
//--- Add a window pointer to the window array
   CWndContainer::AddWindow(m_main_window);
//--- Properties
   m_main_window.XSize(600);
   m_main_window.YSize(375);
//--- Coordinates
   int x=5;
   int y=20;
   m_main_window.CaptionHeight(22);
   m_main_window.IsMovable(true);
   m_main_window.CaptionColor(m_caption_color);
   m_main_window.CaptionColorLocked(m_caption_color);
   m_main_window.CaptionColorHover(m_caption_color);
   m_main_window.BackColor(m_background_color);
   m_main_window.FontSize(m_base_font_size);
   m_main_window.Font(m_base_font);
//--- Create the form
   if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y))
      return(false);
//--- Tabs
   if(!CreateTabs(5,22+27))
      return(false);
//---
   if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',20,10))
      return(false);
   if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',210,10))
      return(false);
   if(!CreateButton(m_order_button[2],MARKET_ORDERS_PROFIT_CLOSE+"(C)",C'87,128,255',20,60))
      return(false);
   if(!CreateButton(m_order_button[3],MARKET_ORDERS_LOSS_CLOSE+"(D)",C'87,128,255',20,110))
      return(false);
   if(!CreateButton(m_order_button[4],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',210,60))
      return(false);
//---
   return(true);
}

Como vemos aquí, primero hemos añadido las pestañas, luego hemos actualizado las llamadas a los métodos que crean los botones, y después hemos modificado el tamaño de la ventana principal. Tras de compilar el proyecto, obtendremos como resultado la transferencia de todos los botones a la primera de las tres pestañas creadas:


Fig.6 Creando las pestañas y transfiriendo los botones a la primera de ellas

De acuerdo con la figura 5, creamos los botones y campos de edición adicionales para implementar la funcionalidad requerida. Para los botones grandes, utilizaremos el método CreateButton() actualizado; para crear los campos de edición y los conmutadores, en cambio, necesitaremos introducir los métodos adicionales: CreateModeButton(), el conmutador de modo, y CreateModeEdit(), los campos de edición de los valores. Aquí tenemos su implementación completa:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateModeButton(CButton &button,string text,int x_gap,int y_gap)
{
//--- Store the window pointer
   button.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(0,button);
   color baseclr=clrDarkViolet;
//--- Set properties before creation
   button.XSize(80);
   button.YSize(20);
   button.Font(m_base_font);
   button.FontSize(9);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(baseclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(baseclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Create a control element
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Add a pointer to the element to the database
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateModeEdit(CTextEdit &text_edit,int x_gap,int y_gap)
{
//--- Store the window pointer
   text_edit.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(0,text_edit);
//--- Properties
   text_edit.XSize(80);
   text_edit.YSize(20);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.SetDigits(2);
   text_edit.MaxValue(99999);
   text_edit.StepValue(0.01);
   text_edit.MinValue(0.01);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0.01));
   text_edit.IsLocked(true);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}

Más abajo, podemos ver el método para crear la ventana principal utilizando los métodos mecionados arriba y la adición de los botones de la fig.5:

//+------------------------------------------------------------------+
//| Create a form for orders                                         |
//+------------------------------------------------------------------+
bool CFastTrading::CreateMainWindow(void)
{
//--- Add a window pointer to the window array
   CWndContainer::AddWindow(m_main_window);
//--- Properties
   m_main_window.XSize(600);
   m_main_window.YSize(375);
//--- Coordinates
   int x=5;
   int y=20;
   m_main_window.CaptionHeight(22);
   m_main_window.IsMovable(true);
   m_main_window.CaptionColor(m_caption_color);
   m_main_window.CaptionColorLocked(m_caption_color);
   m_main_window.CaptionColorHover(m_caption_color);
   m_main_window.BackColor(m_background_color);
   m_main_window.FontSize(m_base_font_size);
   m_main_window.Font(m_base_font);
//--- Create the form
   if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y))
      return(false);
//--- Tabs
   if(!CreateTabs(5,22+27))
      return(false);
//---
   if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',10,10))
      return(false);
   if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',300,10))
      return(false);
   if(!CreateButton(m_order_button[2],CLOSE_ALL_PROFIT+"(C)",C'87,128,255',10,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[3],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',300,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[4],CLOSE_BUY_PROFIT,C'87,128,255',10,10+45*2))
      return(false);
   if(!CreateButton(m_order_button[5],CLOSE_SELL_PROFIT,C'87,128,255',10,10+45*3))
      return(false);
   if(!CreateButton(m_order_button[6],CLOSE_ALL_LOSS+"(D)",C'87,128,255',10,10+45*4))
      return(false);
   if(!CreateButton(m_order_button[7],CLOSE_BUY_LOSS,C'87,128,255',10,10+45*5))
      return(false);
   if(!CreateButton(m_order_button[8],CLOSE_SELL_LOSS,C'87,128,255',10,10+45*6))
      return(false);
//---
   for(int i=0; i<6; i++)
   {
      if(!CreateModeButton(m_mode_button[i],"all",205-10,10+45*(i+1)))
         return(false);
      if(!CreateModeEdit(m_mode_edit[i],204-10,30+45*(i+1)))
         return(false);
   }
//---
   return(true);
}

Aquí, se introducen también nuevas macrosustituciones, por eso añadiremos sus valores en ambos idiomas a Defines.mqh:

#define CLOSE_BUY_PROFIT               (m_language==RUSSIAN ? "Закрыть BUY прибыльные" : "Close BUY Profit")
#define CLOSE_SELL_PROFIT              (m_language==RUSSIAN ? "Закрыть SELL прибыльные" : "Close SELL Profit")
#define CLOSE_ALL_PROFIT               (m_language==RUSSIAN ? "Закрыть ВСЕ прибыльные" : "Close ALL Profit")
#define CLOSE_BUY_LOSS                 (m_language==RUSSIAN ? "Закрыть BUY убыточные" : "Close BUY Losing")
#define CLOSE_SELL_LOSS                (m_language==RUSSIAN ? "Закрыть SELL убыточные" : "Close SELL Losing")
#define CLOSE_ALL_LOSS                 (m_language==RUSSIAN ? "Закрыть ВСЕ убыточные" : "Close ALL Losing")

Compilamos el proyecto y obtenemos el siguiente resultado intermedio:


Fig.7 Añadiendo los botones y los conmutadores de modos

No obstante, esto es tan solo una implementación visual, una fachada. El siguiente paso será asignar su propia tarea lógica a cada uno de los elementos añadidos. En primer lugar, deberemos configurar el mecanismo de conmutación, ya que todos los elementos posteriores se referirán a sus valores y estados. Para los objetos de botón, crearemos el nuevo método ModeButtonSwitch(). Su misión consistirá en cambiar los modos de pulsación de los botones.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::ModeButtonSwitch(CButton &button,long lparam,string &states[])
{
   if(lparam==button.Id())
   {
      int size=ArraySize(states);
      for(int i=0; i<size; i++)
      {
         if(button.LabelText()==states[i])
         {
            if(i==size-1)
            {
               SetButtonParam(button,states[0]);
               break;
            }
            else
            {
               SetButtonParam(button,states[i+1]);
               break;
            }
         }
      }
   }
}

El segundo nuevo método, ModeEditSwitch(), es necesario para que los ajustes de los campos de edición se correspondan con el modo del botón seleccionado. Por ejemplo, los puntos son números enteros, y cuando usamos la divisa del depósito, los valores deberán tener 2 dígitos.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::ModeEditSwitch(long lparam,string &states[])
{
   for(int i=0; i<6; i++)
   {
      if(lparam==m_mode_button[i].Id())
      {
         if(m_mode_button[i].LabelText()==states[1])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(0);
            m_mode_edit[i].StepValue(1);
            m_mode_edit[i].MaxValue(9999);
            m_mode_edit[i].MinValue(1);
            m_mode_edit[i].SetValue(string(20));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=1;
         }
         else if(m_mode_button[i].LabelText()==states[2])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(2);
            m_mode_edit[i].StepValue(0.01);
            m_mode_edit[i].MaxValue(99999);
            m_mode_edit[i].MinValue(0.01);
            m_mode_edit[i].SetValue(string(0.1));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=2;
         }
         else if(m_mode_button[i].LabelText()==states[3])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(0);
            m_mode_edit[i].StepValue(1);
            m_mode_edit[i].MaxValue(9999);
            m_mode_edit[i].MinValue(1);
            m_mode_edit[i].SetValue(string(20));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=3;
         }
         else if(m_mode_button[i].LabelText()==states[4])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(2);
            m_mode_edit[i].StepValue(0.01);
            m_mode_edit[i].MaxValue(99999);
            m_mode_edit[i].MinValue(0.01);
            m_mode_edit[i].SetValue(string(0.1));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=4;
         }
         else
         {
            m_mode_edit[i].IsLocked(true);
            m_current_mode[i]=0;
         }
      }
   }
}

En la implementación actual, hemos introducido la matriz estática m_current_mode con el tamaño correspondiente al número de conmutadores de los modos, es decir, 6. En esta se registran los modos seleccionados por el usuario para cada botón que cierra la posición. Para que todo lo que acabamos de añadir funcione correctamente, vamos a pasar a nuestro manejador de eventos OnEvent() y añadir el siguiente código a la sección de eventos de pulsación de botón:

      //---
      string states[5]= {"all",">points",">currency","sum>points","sum>currency"};
      for(int i=0; i<6; i++)
         ModeButtonSwitch(m_mode_button[i],lparam,states);
      //---
      ModeEditSwitch(lparam,states);

Compilamos el proyecto. Ahora, en la fig. 8, podemos ver que al alternar el modo cambian también las propiedades de los campos de edición.


Fig.8 Alternando el modo de cierre de posiciones de mercado

El siguiente paso será implementar la lógica de acción según las descripciones de los botones, que también deberán estar vinculados a los modos añadidos anteriormente. Ya tenemos dos acciones: "Cerrar Todas las Posiciones Rentables" y "Cerrar Todas las Posiciones con Pérdidas". Ahora, deberíamos complementarlas teniendo en cuenta los nuevos modos de cierre. A estas acciones les corresponden los métodos CloseAllMarketProfit() y CloseAllMarketLoss().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseAllMarketProfit(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[2].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_C))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[0]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            int profit_pp;
            if(type==POSITION_TYPE_BUY)
               profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
            else
               profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
               {
                  switch(m_current_mode[0])
                  {
                  case  3:
                     sum_pp+=profit_pp;
                     break;
                  case  4:
                     sum_cur+=profit_cur+swap;
                     break;
                  }
               }
         }
      }
      //--- iterate over open positions
      for(int i=total-1; i>=0; i--)
      {
         //--- order parameters
         ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         int profit_pp;
         //---
         if(type==POSITION_TYPE_BUY)
            profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
         else
            profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
         //--- if MagicNumber matches
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
            {
               if(   (m_current_mode[0]==0 && profit_cur+swap>0) ||                                   // Close all profitable positions
                     (m_current_mode[0]==1 && profit_pp>int(m_mode_edit[0].GetValue())) ||            // Close all positions having profit more than N points
                     (m_current_mode[0]==2 && profit_cur+swap>double(m_mode_edit[0].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                     (m_current_mode[0]==3 && sum_pp>int(m_mode_edit[0].GetValue())) ||               // Close all positions with the total profit more than N points
                     (m_current_mode[0]==4 && sum_cur>double(m_mode_edit[0].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                 )
               {
                  //--- zeroing the request and result values
                  ZeroMemory(request);
                  ZeroMemory(result);
                  //--- set the operation parameters
                  request.action   =TRADE_ACTION_DEAL;        // trading operation type
                  request.position =position_ticket;          // position ticket
                  request.symbol   =position_symbol;          // symbol
                  request.volume   =volume;                   // position volume
                  request.deviation=5;                        // allowable price deviation
                  request.magic    =m_magic_number;           // position MagicNumber
                  //--- Set order price and type depending on the position type
                  if(type==POSITION_TYPE_BUY)
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                  }
                  else
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                  }
                  //--- sending a request
                  bool res=true;
                  for(int j=0; j<5; j++)
                  {
                     res=OrderSend(request,result);
                     if(res && result.retcode==TRADE_RETCODE_DONE)
                        break;
                     else
                        PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                  }
               }
            }
      }
   }
}

Se trata de una modificación del método Cierre de Todas las Posiciones de Mercado. Como recordaremos, antes hemos introducido la matriz estática m_current_mode, que controla la selección del modo para cada una de las acciones en el botón. Por eso, el cálculo se efectúa para los modos 4 y 5, en los que todas las posiciones se cierran según el beneficio total en puntos o en divisa del depósito. A continuación, elegimos las posiciones que pertenecen a nuestro instrumental y, dependiendo del modo de cierre seleccionado, elegimos las condiciones según las cuales deberán cerrarse todas las posiciones de mercado creadas por el instrumental.

De forma análoga, cambiamos también el segundo método que cierra todas las posiciones con pérdidas:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseAllMarketLoss(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[6].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_D))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      ZeroMemory(request);
      ZeroMemory(result);
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate losses
      if(m_current_mode[3]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double loss_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            int loss_pp;
            //---
            if(type==POSITION_TYPE_BUY)
               loss_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
            else
               loss_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
               {
                  switch(m_current_mode[3])
                  {
                  case  3:
                     sum_pp+=loss_pp;
                     break;
                  case  4:
                     sum_cur+=loss_cur+swap;
                     break;
                  }
               }
         }
      }
      //--- iterate over open positions
      for(int i=total-1; i>=0; i--)
      {
         //--- order parameters
         ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         double loss_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         int loss_pp;
         if(type==POSITION_TYPE_BUY)
            loss_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
         else
            loss_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
         //--- if MagicNumber matches
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
            {
               if(   (m_current_mode[3]==0 && loss_cur+swap<0) ||
                     (m_current_mode[3]==1 && loss_pp<-int(m_mode_edit[3].GetValue())) ||
                     (m_current_mode[3]==2 && loss_cur+swap<-double(m_mode_edit[3].GetValue())) ||
                     (m_current_mode[3]==3 && sum_pp<-int(m_mode_edit[3].GetValue())) ||
                     (m_current_mode[3]==4 && sum_cur<-double(m_mode_edit[3].GetValue()))
                 )
               {
                  //--- zeroing the request and result values
                  ZeroMemory(request);
                  ZeroMemory(result);
                  //--- set the operation parameters
                  request.action=TRADE_ACTION_DEAL;         // trading operation type
                  request.position=position_ticket;         // position ticket
                  request.symbol=position_symbol;           // symbol
                  request.volume=volume;                    // position volume
                  request.deviation=5;                      // allowable price deviation
                  request.magic=m_magic_number;             // position MagicNumber
                  //--- Set order price and type depending on the position type
                  if(type==POSITION_TYPE_BUY)
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                  }
                  else
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                  }
                  //--- sending a request
                  bool res=true;
                  for(int j=0; j<5; j++)
                  {
                     res=OrderSend(request,result);
                     if(res && result.retcode==TRADE_RETCODE_DONE)
                        break;
                     else
                        PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                  }
               }
            }
      }
   }
}

Además, se realiza el cálculo para los métodos de comprobación del valor de las pérdidas en todas las posiciones abiertas en puntos o divisa de depósito, y, a continuación, dependiendo del modo de cierre, se comprueban las condiciones según las cuales se cierran todas las posiciones creadas por la aplicación.

A continuación, vamos a pasar a las nuevas acciones que se realizan con las posiciones abiertas, es decir, el Cierre de posiciones Buy/Sell, o bien rentables, o bien con pérdidas. En esencia, son casos especiales de los dos métodos descritos anteriormente. En los actuales todo resultará igual, excepto que se añadirá el filtrado por tipo. Para comenzar, vamos a crear varios métodos cuya función será ejecutar las acciones especificadas:

  • CloseBuyMarketProfit() — cierra todas las posiciones Buy rentables.
  • CloseSellMarketProfit() — cierra todas las posiciones Sell rentables.
  • CloseBuyMarketLoss() — cierra todas las posiciones Buy con pérdidas.  
  • CloseSellMarketLoss() — cierra todas las posiciones Sell con pérdidas.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseBuyMarketProfit(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[4].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_U))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[1]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_BUY)
                  {
                     int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                     switch(m_current_mode[1])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- iterate over open positions
      for(int i=total-1; i>=0; i--)
      {
         //--- order parameters
         ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_BUY)
               {
                  int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                  if(   (m_current_mode[1]==0 && profit_cur+swap>0) ||                                   // Close all profitable positions
                        (m_current_mode[1]==1 && profit_pp>int(m_mode_edit[1].GetValue())) ||            // Close all positions having profit more than N points
                        (m_current_mode[1]==2 && profit_cur+swap>double(m_mode_edit[1].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                        (m_current_mode[1]==3 && sum_pp>int(m_mode_edit[1].GetValue())) ||               // Close all positions with the total profit more than N points
                        (m_current_mode[1]==4 && sum_cur>double(m_mode_edit[1].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                    )
                  {
                     //--- zeroing the request and result values
                     ZeroMemory(request);
                     ZeroMemory(result);
                     //--- set the operation parameters
                     request.action   =TRADE_ACTION_DEAL;        // trading operation type
                     request.position =position_ticket;          // position ticket
                     request.symbol   =position_symbol;          // symbol
                     request.volume   =volume;                   // position volume
                     request.deviation=5;                        // allowable price deviation
                     request.magic    =m_magic_number;           // position MagicNumber
                     //--- Set order price and type depending on the position type
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                     //--- sending a request
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                     }
                  }
               }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseBuyMarketLoss(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[7].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_H))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[4]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_BUY)
                  {
                     int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                     switch(m_current_mode[1])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- iterate over open positions
      for(int i=total-1; i>=0; i--)
      {
         //--- order parameters
         ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_BUY)
               {
                  int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                  if(   (m_current_mode[4]==0 && profit_cur+swap<0) ||                                   // Close all profitable positions
                        (m_current_mode[4]==1 && profit_pp<-int(m_mode_edit[4].GetValue())) ||            // Close all positions having profit more than N points
                        (m_current_mode[4]==2 && profit_cur+swap<-double(m_mode_edit[4].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                        (m_current_mode[4]==3 && sum_pp<-int(m_mode_edit[4].GetValue())) ||               // Close all positions with the total profit more than N points
                        (m_current_mode[4]==4 && sum_cur<-double(m_mode_edit[4].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                    )
                  {
                     //--- zeroing the request and result values
                     ZeroMemory(request);
                     ZeroMemory(result);
                     //--- set the operation parameters
                     request.action   =TRADE_ACTION_DEAL;        // trading operation type
                     request.position =position_ticket;          // position ticket
                     request.symbol   =position_symbol;          // symbol
                     request.volume   =volume;                   // position volume
                     request.deviation=5;                        // allowable price deviation
                     request.magic    =m_magic_number;           // position MagicNumber
                     //--- Set order price and type depending on the position type
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                     //--- sending a request
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                     }
                  }
               }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseSellMarketProfit(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[5].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_J))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[2]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_SELL)
                  {
                     int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                     switch(m_current_mode[2])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- iterate over open positions
      for(int i=total-1; i>=0; i--)
      {
         //--- order parameters
         ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_SELL)
               {
                  int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                  if(   (m_current_mode[2]==0 && profit_cur+swap>0) ||                                   // Close all profitable positions
                        (m_current_mode[2]==1 && profit_pp>int(m_mode_edit[2].GetValue())) ||            // Close all positions having profit more than N points
                        (m_current_mode[2]==2 && profit_cur+swap>double(m_mode_edit[2].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                        (m_current_mode[2]==3 && sum_pp>int(m_mode_edit[2].GetValue())) ||               // Close all positions with the total profit more than N points
                        (m_current_mode[2]==4 && sum_cur>double(m_mode_edit[2].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                    )
                  {
                     //--- zeroing the request and result values
                     ZeroMemory(request);
                     ZeroMemory(result);
                     //--- set the operation parameters
                     request.action   =TRADE_ACTION_DEAL;        // trading operation type
                     request.position =position_ticket;          // position ticket
                     request.symbol   =position_symbol;          // symbol
                     request.volume   =volume;                   // position volume
                     request.deviation=5;                        // allowable price deviation
                     request.magic    =m_magic_number;           // position MagicNumber
                     //--- Set order price and type depending on the position type
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                     //--- sending a request
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                     }
                  }
               }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseSellMarketLoss(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[8].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_L))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[5]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_SELL)
                  {
                     int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                     switch(m_current_mode[1])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- iterate over open positions
      for(int i=total-1; i>=0; i--)
      {
         //--- order parameters
         ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         double profit_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_SELL)
               {
                  int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                  if(   (m_current_mode[5]==0 && profit_cur+swap<0) ||                                   // Close all profitable positions
                        (m_current_mode[5]==1 && profit_pp<-int(m_mode_edit[5].GetValue())) ||            // Close all positions having profit more than N points
                        (m_current_mode[5]==2 && profit_cur+swap<-double(m_mode_edit[5].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                        (m_current_mode[5]==3 && sum_pp<-int(m_mode_edit[5].GetValue())) ||               // Close all positions with the total profit more than N points
                        (m_current_mode[5]==4 && sum_cur<-double(m_mode_edit[5].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                    )
                  {
                     //--- zeroing the request and result values
                     ZeroMemory(request);
                     ZeroMemory(result);
                     //--- set the operation parameters
                     request.action   =TRADE_ACTION_DEAL;        // trading operation type
                     request.position =position_ticket;          // position ticket
                     request.symbol   =position_symbol;          // symbol
                     request.volume   =volume;                   // position volume
                     request.deviation=5;                        // allowable price deviation
                     request.magic    =m_magic_number;           // position MagicNumber
                     //--- Set order price and type depending on the position type
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                     //--- sending a request
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                     }
                  }
               }
      }
   }
}

Después de crearlos e implementarlos, los llamaremos en el cuerpo del manejador de eventos OnEvent(), fuera de cualquier sección.

//---
   CloseBuyMarketProfit(id,lparam);
   CloseSellMarketProfit(id,lparam);
   CloseBuyMarketLoss(id,lparam);
   CloseSellMarketLoss(id,lparam);

Para cada una de las acciones creadas, aparte de la pulsación del botón, hemos creado eventos de pulsación de atajos de teclado. El lector podrá reasignarlos en el código. Para mayor claridad, mostraremos sus valores junto a los nombres de las acciones en los botones.

//---
   if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',10,10))
      return(false);
   if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',300,10))
      return(false);
   if(!CreateButton(m_order_button[2],CLOSE_ALL_PROFIT+"(C)",C'87,128,255',10,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[3],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',300,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[4],CLOSE_BUY_PROFIT+"(U)",C'87,128,255',10,10+45*2))
      return(false);
   if(!CreateButton(m_order_button[5],CLOSE_SELL_PROFIT+"(J)",C'87,128,255',10,10+45*3))
      return(false);
   if(!CreateButton(m_order_button[6],CLOSE_ALL_LOSS+"(D)",C'87,128,255',10,10+45*4))
      return(false);
   if(!CreateButton(m_order_button[7],CLOSE_BUY_LOSS+"(H)",C'87,128,255',10,10+45*5))
      return(false);
   if(!CreateButton(m_order_button[8],CLOSE_SELL_LOSS+"(L)",C'87,128,255',10,10+45*6))
      return(false);

Como resultado, obtendremos los siguientes valores:


Fig.9 Asignando los atajos de teclado a las acciones añadidas

Ya hemos implementado la funcionalidad de la pestaña Comercio. Ahora, vamos a pasar a la siguiente etapa: la creación de un recuadro con las posiciones abiertas por el instrumental y la adición de la posibilidad de gestionar las posiciones en la pestaña "Control de Posiciones de Mercado". La fig.3 al inicio del artículo, contiene un esquema visual para crear los elementos de la interfaz, que consta de tres campos de edición, dos botones y un recuadro. Comenzaremos por la pestaña. En primer lugar, crearemos tres campos de edición para editar el lote, el Stop Loss y el Take Profit de las posiciones abiertas. Hablamos de los métodos CreateLotControl(), CreateStopLossControl(), CreateTakeProfitControl().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateLotControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab)
{
//--- Store the pointer to the main control
   text_edit.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,text_edit);
//--- Properties
   text_edit.XSize(80);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.MaxValue(9999);
   text_edit.StepValue(_Point);
   text_edit.SetDigits(_Digits);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateTakeProfitControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab)
{
//--- Store the pointer to the main control
   text_edit.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,text_edit);
//--- Properties
   text_edit.XSize(80);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.MaxValue(9999);
   text_edit.StepValue(_Point);
   text_edit.SetDigits(_Digits);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateStopLossControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab)
{
//--- Store the pointer to the main control
   text_edit.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,text_edit);
//--- Properties
   text_edit.XSize(80);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.MaxValue(9999);
   text_edit.StepValue(_Point);
   text_edit.SetDigits(_Digits);
   text_edit.MinValue(0);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}

Y llamamos a los nuevos métodos en el cuerpo de CreateMainWindow().

//--- Input field for editing open positions
   if(!CreateLotControl(m_lot_edit[6],375,3,1))
      return(false);
   if(!CreateStopLossControl(m_sl_edit[6],375+80,3,1))
      return(false);
   if(!CreateTakeProfitControl(m_tp_edit[6],375+83*2,3,1))
      return(false);

Para los botones Cambiar y Cerrar también crearemos dos nuevos métodos encargados de implementarlos: CreateModifyButton(), CreateCloseButton().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateModifyButton(CButton &button,string text,int x_gap,int y_gap,int tab)
{
//--- Store the window pointer
   button.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,button);
   color baseclr=clrDarkOrange;
   color pressclr=clrOrange;
//--- Set properties before creation
   button.XSize(80);
   button.YSize(24);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(pressclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(pressclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Create a control element
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Add a pointer to the element to the database
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateCloseButton(CButton &button,string text,int x_gap,int y_gap,int tab)
{
//--- Store the window pointer
   button.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,button);
   color baseclr=clrCrimson;
   color pressclr=clrFireBrick;
//--- Set properties before creation
   button.XSize(80);
   button.YSize(24);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(pressclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(pressclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Create a control element
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Add a pointer to the element to the database
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}

De esta forma, los añadiremos al método para crear la ventana principal:

//--- Position editing/closing buttons
   if(!CreateModifyButton(m_small_button[0],MODIFY,622,3,1))
      return(false);
   if(!CreateCloseButton(m_small_button[1],CLOSE,622+80,3,1))
      return(false);

Aquí, tenemos dos macrosustituciones para la localización de la interfaz, por eso, pasamos a Defines.mqh y añadimos sus valores:

#define MODIFY                         (m_language==RUSSIAN ? "Изменить" : "Modify")
#define CLOSE                          (m_language==RUSSIAN ? "Закрыть" : "Close")

Ahora, vamos a pasar al propio recuadro. Para ello, crearemos el método CreatePositionsTable(), lo implementaremos y también lo añadiremos al método de la ventana principal de la aplicación.

//+------------------------------------------------------------------+
//| Create a table of positions                                      |
//+------------------------------------------------------------------+
bool CFastTrading::CreatePositionsTable(CTable &table,const int x_gap,const int y_gap)
{
#define COLUMNS2_TOTAL 9
//--- Store the pointer to the main control
   table.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(1,table);
//--- Array of column widths
   int width[COLUMNS2_TOTAL];
   ::ArrayInitialize(width,80);
   width[0]=100;
   width[1]=110;
   width[2]=100;
   width[3]=60;
   width[6]=90;
//--- Array of text alignment in columns
   ENUM_ALIGN_MODE align[COLUMNS2_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
//--- Array of text offset along the X axis in the columns
   int text_x_offset[COLUMNS2_TOTAL];
   ::ArrayInitialize(text_x_offset,7);
//--- Array of column image offsets along the X axis
   int image_x_offset[COLUMNS2_TOTAL];
   ::ArrayInitialize(image_x_offset,3);
//--- Array of column image offsets along the Y axis
   int image_y_offset[COLUMNS2_TOTAL];
   ::ArrayInitialize(image_y_offset,2);
//--- Properties
   table.Font(m_base_font);
   table.FontSize(m_base_font_size);
   table.XSize(782);
   table.CellYSize(24);
   table.TableSize(COLUMNS2_TOTAL,1);
   table.TextAlign(align);
   table.ColumnsWidth(width);
   table.TextXOffset(text_x_offset);
   table.ImageXOffset(image_x_offset);
   table.ImageYOffset(image_y_offset);
   table.ShowHeaders(true);
   table.HeadersColor(C'87,128,255');
   table.HeadersColorHover(clrCornflowerBlue);
   table.HeadersTextColor(clrWhite);
   table.IsSortMode(false);
   table.LightsHover(true);
   table.SelectableRow(true);
   table.IsZebraFormatRows(clrWhiteSmoke);
   table.DataType(0,TYPE_LONG);
   table.AutoYResizeMode(true);
   table.AutoYResizeBottomOffset(5);
//--- Create a control element
   if(!table.CreateTable(x_gap,y_gap))
      return(false);
//--- Set the header titles
   table.SetHeaderText(0,TICKET);
   table.SetHeaderText(1,SYMBOL);
   table.SetHeaderText(2,TYPE_POS);
   table.SetHeaderText(3,PRICE);
   table.SetHeaderText(4,VOLUME);
   table.SetHeaderText(5,SL);
   table.SetHeaderText(6,TP);
   table.SetHeaderText(7,SWAP);
   table.SetHeaderText(8,PROFIT);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,table);
   return(true);
}

El recuadro tendrá 9 columnas. Asimismo, ajustaremos en él la anchura de las columnas, dado que los nombres de estas tienen distinta longitud. Y establecemos los encabezados de las columnas aplicando la localización, es decir, creamos para cada columna su propia macrosustitución en el archivo Defines.mqh.

#define SYMBOL                         (m_language==RUSSIAN ? "Символ" : "Symbol")
#define VOLUME                         (m_language==RUSSIAN ? "Объем" : "Volume")
#define TYPE_POS                       (m_language==RUSSIAN ? "Тип позиции" : "Position Type")
#define SWAP                           (m_language==RUSSIAN ? "Своп" : "Swap")
#define PROFIT                         (m_language==RUSSIAN ? "Прибыль" : "Profit")

No obstante, si intentamos compilar el proyecto ahora, veremos que el recuadro, los botones y los campos se salen del borde derecho de la ventana. Por eso, necesitaremos añadir un mecanismo que ajuste la anchura de la ventana principal según su contenido. Para ello, crearemos el método WindowRezise().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::WindowResize(int x_size,int y_size)
{
   m_main_window.GetCloseButtonPointer().Hide();
   m_main_window.ChangeWindowWidth(x_size);
   m_main_window.ChangeWindowHeight(y_size);
   m_main_window.GetCloseButtonPointer().Show();
}

Y creamos en el manejador de eventos un nuevo evento de clic sobre la pestaña. En él, escribiremos la anchura de la ventana principal para cada pestaña.

//--- Tab switching event
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_TAB)
   {
      if(m_tab.SelectedTab()==0)
         WindowResize(600,m_main_window.YSize());
      if(m_tab.SelectedTab()==1)
         WindowResize(782+10,m_main_window.YSize());
      if(m_tab.SelectedTab()==2)
         WindowResize(682+10,m_main_window.YSize());
   }

Ahora, todo se mostrará correctamente, según la fig.3. El siguiente paso consistirá en obtener la información sobre las posiciones abiertas por la aplicación y mostrarla en el recuadro creado. Para ello, deberemos designar tres etapas de preparación de datos y su visualización en el recuadro:

  • Inicialización. Aquí definiremos el número de posiciones que pertenecerán a nuestro instrumental y reconstruimos el recuadro para estos datos. 
  • Adición de datos. Añadiremos al recuadro los parámetros de cada posición abierta, según la descripción en los encabezados de las columnas.
  • Actualización del recuadro con la información rellenada.

Para el primer paso de la inicialización, crearemos la función InitializePositionsTable() y crearemos una muestra de todas las posiciones abiertas según el símbolo actual y el número mágico. De esta manera, obtendremos el número de posiciones que cumplen con nuestras condiciones. Después, añadiremos al recuadro el mismo número de filas.

//+------------------------------------------------------------------+
//| Initializing the table of positions                              |
//+------------------------------------------------------------------+
void CFastTrading::InitializePositionsTable(void)
{
//--- Get symbols of open positions
   int total=PositionsTotal(); // the number of open positions
   int cnt=0;
//--- Delete all rows
   m_table_positions.DeleteAllRows();
//--- Set the number of rows equal to the number of positions
   for(int i=0; i<total; i++)
   {
      //--- order parameters
      ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
      //--- if MagicNumber matches
      if(magic==m_magic_number)
         if(position_symbol==Symbol())
         {
            m_table_positions.AddRow(cnt);
            cnt++;
         }
   }
//--- If there are positions
   if(cnt>0)
   {
      //--- Set the values in the table
      SetValuesToPositionsTable();
      //--- Update the table
      UpdatePositionsTable();
   }
}

A continuación, se comprobará si se ha encontrado aunque sea una posición abierta por nuestro instrumental. En caso contrario, estableceremos la información sobre las posiciones abiertas en las líneas recién creadas con la ayuda del método SetValuePositionTable().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::SetValuesToPositionsTable(void)
{
//---
   int cnt=0;
   for(int i=PositionsTotal()-1; i>=0; i--)
   {
      //--- order parameters
      ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
      int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
      double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
      ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
      double stoploss=PositionGetDouble(POSITION_SL);
      double takeprofit=PositionGetDouble(POSITION_TP);
      double swap=PositionGetDouble(POSITION_SWAP);
      double profit=PositionGetDouble(POSITION_PROFIT);
      double openprice=PositionGetDouble(POSITION_PRICE_OPEN);
      string pos=(type==POSITION_TYPE_BUY)?"BUY":"SELL";
      profit+=swap;
      //--- if MagicNumber matches
      if(magic==m_magic_number)
         if(position_symbol==Symbol())
         {
            m_table_positions.SetValue(0,i,string(position_ticket));
            m_table_positions.SetValue(1,i,string(position_symbol));
            m_table_positions.SetValue(2,i,pos);
            m_table_positions.SetValue(3,i,string(openprice));
            m_table_positions.SetValue(4,i,string(volume));
            m_table_positions.SetValue(5,i,string(stoploss));
            m_table_positions.SetValue(6,i,string(takeprofit));
            m_table_positions.SetValue(7,i,string(swap));
            m_table_positions.SetValue(8,i,DoubleToString(profit,2));
            //---
            m_table_positions.TextColor(2,i,(pos=="BUY")? clrForestGreen : clrCrimson);
            m_table_positions.TextColor(8,i,(profit!=0)? (profit>0)? clrForestGreen : clrCrimson : clrSilver);
            cnt++;
         }
   }
}

Y después de rellenar los datos, actualizaremos el recuadro con la ayuda de UpdatePositionsTable():

//+------------------------------------------------------------------+
//| Update the table of positions                                    |
//+------------------------------------------------------------------+
void CFastTrading::UpdatePositionsTable(void)
{
//--- Update the table
   m_table_positions.Update(true);
   m_table_positions.GetScrollVPointer().Update(true);
}

Para que los cambios introducidos en el proyecto surtan efecto, deberemos ajustarlos correctamente. Para ello, pasaremos al archivo SimpleTrading.mq5, y añadiremos en la función OnInit()la llamada del método de inicialización de la clase de la aplicación:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//---
   tick_counter=GetTickCount();
//--- Initialize class variables
   program.FontName("Trebuchet MS");
   program.FontSize(Inp_BaseFont);
   program.BackgroundColor(Background);
   program.CaptionColor(Caption);
   program.SetLanguage(Language);
   program.SetMagicNumber(MagicNumber);
//--- Set a trading panel
   if(!program.CreateGUI())
   {
      Print(__FUNCTION__," > Failed to create graphical interface!");
      return(INIT_FAILED);
   }
   program.OnInitEvent();
//---
   return(INIT_SUCCEEDED);
}

Esto debe hacerse estrictamente después de crear la aplicación CreateGUI(). Ahora, pasamos al cuerpo de OnInitEvent() y llamamos ahí a la inicialización del recuadro. 

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
void CFastTrading::OnInitEvent(void)
{
   InitializePositionsTable();
}

Ahora, en el recuadro se muestra correctamente toda la información sobre las posiciones abiertas. No obstante, dicha información solo es relevante cuando se inicia la aplicación, y deberá actualizarse constantemente. Esto debe hacerse en el manejador de eventos comerciales OnTrade(), así como en la función OnTick(). En los eventos comerciales, monitorearemos el número de posiciones abiertas actuales y sus parámetros, mientras que en el tick actualizaremos la información sobre el beneficio actual de cada orden.

Para ello, creamos el método OnTradeEvent() en la sección pública de la clase base y llamamos a la inicialización del recuadro en su cuerpo. 

//+------------------------------------------------------------------+
//| Trade event                                                      |
//+------------------------------------------------------------------+
void CFastTrading::OnTradeEvent(void)
{
//--- If a new trade
      InitializePositionsTable();
}

El propio método nuevo lo llamamos en el manejador del evento comercial:

//+------------------------------------------------------------------+
//| Trade function                                                   |
//+------------------------------------------------------------------+
void OnTrade(void)
{
   program.OnTradeEvent();
}

Con las acciones realizadas, hemos ajustado la actualidad de la representación de las posiciones abiertas. Para actualizar el beneficio, creamos en la sección pública de la clase CFastTrading el método UpdatePositionProfit():

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::UpdatePositionProfit(void)
{
//---
   int cnt=0;
   for(int i=PositionsTotal()-1; i>=0; i--)
   {
      //--- order parameters
      ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
      double swap=PositionGetDouble(POSITION_SWAP);
      double profit=PositionGetDouble(POSITION_PROFIT);
      profit+=swap;
      //--- if MagicNumber matches
      if(magic==m_magic_number)
         if(position_symbol==Symbol())
         {
            m_table_positions.SetValue(8,i,DoubleToString(profit,2));
            //---
            m_table_positions.TextColor(8,i,(profit!=0)? (profit>0)? clrForestGreen : clrCrimson : clrSilver);
            cnt++;
         }
   }
//---
   if(cnt>0)
      UpdatePositionsTable();
}

Y lo llamamos en OnTick():

void OnTick()
{
   program.UpdatePositionProfit();
}

Con esto, podemos considerar completa la implementación del recuadro. Ahora, vamos a crear la posibilidad de editar y cerrar las posiciones abiertas disponibles en la lista actual. Para ello, debemos lograr que al clicar en una fila del recuadro, los campos de edición muestren el lote, el Take Profit y el Stop Loss de la posición seleccionada. 

//--- Event of clicking on a table row
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
   {
      //--- Check the element ID
      if(lparam==m_table_positions.Id())
      {
         //---
         int row=m_table_positions.SelectedItem();
         m_lot_edit[6].SetValue(string(m_table_positions.GetValue(4,row)));
         m_sl_edit[6].SetValue(string(m_table_positions.GetValue(5,row)));
         m_tp_edit[6].SetValue(string(m_table_positions.GetValue(6,row)));
         m_lot_edit[6].GetTextBoxPointer().Update(true);
         m_sl_edit[6].GetTextBoxPointer().Update(true);
         m_tp_edit[6].GetTextBoxPointer().Update(true);
      }
   }

Compilamos el proyecto y vemos (como se muestra en la fig.10) que los valores de la posición de mercado seleccionada se representan en los campos de edición.


Fig.10 Seleccionado una posición abierta para su posterior edición.

A continuación, creamos dos métodos: ModifyPosition() y ClosePosition(), que, al pulsar en los botones Cambiar y Cerrar, aplicarán las acciones correspondientes a la posición abierta seleccionada. 

//+------------------------------------------------------------------+
//| Modifying a selected open position                               |
//+------------------------------------------------------------------+
bool CFastTrading::ModifyPosition(long lparam)
{
//--- Check the element ID
   if(lparam==m_small_button[0].Id())
   {
//--- Get index and symbol
      if(m_table_positions.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_positions.SelectedItem();
      ulong ticket=(ulong)m_table_positions.GetValue(0,row);
//---
      if(PositionSelectByTicket(ticket))
      {
         //--- declare the request and the result
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- calculation and rounding of the Stop Loss and Take Profit values
         double sl=NormalizeDouble((double)m_sl_edit[6].GetValue(),_Digits);
         double tp=NormalizeDouble((double)m_tp_edit[6].GetValue(),_Digits);
         //--- zeroing the request and result values
         ZeroMemory(request);
         ZeroMemory(result);
         //--- set the operation parameters
         request.action  =TRADE_ACTION_SLTP; // trading operation type
         request.position=ticket;            // position ticket
         request.symbol=Symbol();            // symbol
         request.sl      =sl;                // position Stop Loss
         request.tp      =tp;                // position Take Profit
         request.magic=m_magic_number;       // position MagicNumber
         //--- sending a request
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
//---
   return(false);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::ClosePosition(long lparam)
{
//--- Check the element ID
   if(lparam==m_small_button[1].Id())
   {
      //--- Get index and symbol
      if(m_table_positions.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_positions.SelectedItem();
      ulong ticket=(ulong)m_table_positions.GetValue(0,row);
//---
      if(PositionSelectByTicket(ticket))
      {
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         //--- declare the request and the result
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- zeroing the request and result values
         ZeroMemory(request);
         ZeroMemory(result);
         //--- Set order price and type depending on the position type
         if(type==POSITION_TYPE_BUY)
         {
            request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
            request.type =ORDER_TYPE_SELL;
         }
         else if(type==POSITION_TYPE_SELL)
         {
            request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
            request.type =ORDER_TYPE_BUY;
         }
         //--- check volume
         double position_volume=PositionGetDouble(POSITION_VOLUME);
         double closing_volume=(double)m_lot_edit[6].GetValue();
         if(closing_volume>position_volume)
            closing_volume=position_volume;
         //--- setting request
         request.action   =TRADE_ACTION_DEAL;
         request.position =ticket;
         request.symbol   =Symbol();
         request.volume   =NormalizeLot(Symbol(),closing_volume);
         request.magic    =m_magic_number;
         request.deviation=5;
         //--- sending a request
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
//---
   return(false);
}

Con esto, podemos dar por finalizada la implementación de tareas en la pestaña Control de Posiciones de Mercado. Vamos a pasar al desarrollo de la pestaña Control de Órdenes Pendientes. Para ello, al final del cuerpo del método de la ventana principal CreateMainWindow(), añadimos un código que agregará la misma funcionalidad que en la pestaña anterior, concretamente: un recuadro de registro de órdenes pendientes, así como los botones y los campos de edición para su edición.

//--- Create a table of pending orders
   if(!CreateOrdersTable(m_table_orders,0,22+5))
      return(false);
//--- Input fields for editing pending orders
   if(!CreateLotControl(m_pr_edit[4],360,3,2))
      return(false);
   if(!CreateStopLossControl(m_sl_edit[7],360+80,3,2))
      return(false);
   if(!CreateTakeProfitControl(m_tp_edit[7],360+80*2,3,2))
      return(false);
//--- Pending order modifying/deleting orders
   if(!CreateModifyButton(m_small_button[2],MODIFY,361+80*3,3,2))
      return(false);
   if(!CreateCloseButton(m_small_button[3],REMOVE,361+80*3,3+24,2))
      return(false);

En esta adición, tenemos un nuevo método que añade un recuadro para registrar las órdenes pendientes, y también una nueva macrosustitución para el botón que elimina la orden seleccionada. Veamos con mayor detalle la creación del recuadro:

//+------------------------------------------------------------------+
//| Create a table of pending orders                                 |
//+------------------------------------------------------------------+
//---
bool CFastTrading::CreateOrdersTable(CTable &table,const int x_gap,const int y_gap)
{
#define COLUMNS1_TOTAL 7
//--- Store the pointer to the main control
   table.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(2,table);
//--- Array of column widths
   int width[COLUMNS1_TOTAL];
   ::ArrayInitialize(width,80);
   width[0]=100;
   width[2]=100;
//--- Array of text offset along the X axis in the columns
   int text_x_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(text_x_offset,7);
//--- Array of column image offsets along the X axis
   int image_x_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(image_x_offset,3);
//--- Array of column image offsets along the Y axis
   int image_y_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(image_y_offset,2);
//--- Array of text alignment in columns
   ENUM_ALIGN_MODE align[COLUMNS1_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
   align[6]=ALIGN_LEFT;
//--- Properties
   table.Font(m_base_font);
   table.FontSize(m_base_font_size);
   table.XSize(602);
   table.CellYSize(24);
   table.TableSize(COLUMNS1_TOTAL,1);
   table.TextAlign(align);
   table.ColumnsWidth(width);
   table.TextXOffset(text_x_offset);
   table.ImageXOffset(image_x_offset);
   table.ImageYOffset(image_x_offset);
   table.ShowHeaders(true);
   table.HeadersColor(C'87,128,255');
   table.HeadersColorHover(clrCornflowerBlue);
   table.HeadersTextColor(clrWhite);
   table.IsSortMode(false);
   table.LightsHover(true);
   table.SelectableRow(true);
   table.IsZebraFormatRows(clrWhiteSmoke);
   table.AutoYResizeMode(true);
   table.AutoYResizeBottomOffset(5);
   table.DataType(0,TYPE_LONG);
   table.DataType(1,TYPE_STRING);
   table.DataType(2,TYPE_STRING);
   table.DataType(3,TYPE_DOUBLE);
   table.DataType(4,TYPE_DOUBLE);
   table.DataType(5,TYPE_DOUBLE);
//--- Create a control element
   if(!table.CreateTable(x_gap,y_gap))
      return(false);
//--- Set the header titles
   table.SetHeaderText(0,TICKET);
   table.SetHeaderText(1,SYMBOL);
   table.SetHeaderText(2,TYPE_POS);
   table.SetHeaderText(3,VOLUME);
   table.SetHeaderText(4,PRICE);
   table.SetHeaderText(5,SL);
   table.SetHeaderText(6,TP);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,table);
   return(true);
}

Aquí, cambia el número de columnas y su orden. Al editar las posiciones de mercado abiertas, podemos modificar el lote, así como los valores de Take Profit y Stop Loss. En cuanto a las órdenes pendientes, aquí podemos modificar el precio de apertura en lugar del lote. Compilamos el proyecto y verificamos el resultado en la pestaña Control de Órdenes Pendientes:


Fig.11 Interfaz para trabajar con órdenes pendientes.

En esencia, el trabajo posterior es muy semejante a la creación de la pestaña anterior. En primer lugar, encontramos todas las órdenes que pertenecen a nuestro instrumental con la ayuda de InitializeOrdersTable():

//+------------------------------------------------------------------+
//| Initializing the table of positions                              |
//+------------------------------------------------------------------+
void CFastTrading::InitializeOrdersTable(void)
{
//---
   int total=OrdersTotal();
   int cnt=0;
//--- Delete all rows
   m_table_orders.DeleteAllRows();
//--- Set the number of rows equal to the number of positions
   for(int i=0; i<total; i++)
   {
      //--- order parameters
      ulong  order_ticket=OrderGetTicket(i);                                   // order ticket
      string order_symbol=OrderGetString(ORDER_SYMBOL);                        // symbol
      ulong  magic=OrderGetInteger(ORDER_MAGIC);                               // position MagicNumber
      //--- if MagicNumber matches
      if(magic==m_magic_number)
         if(order_symbol==Symbol())
         {
            m_table_orders.AddRow(cnt);
            cnt++;
         }
   }
//--- If there are positions
   if(cnt>0)
   {
      //--- Set the values in the table
      SetValuesToOrderTable();
      //--- Update the table
      UpdateOrdersTable();
   }
}

Si no hemos encontrado órdenes pendientes, introducimos la información sobre ellas en el recuadro a través del método SetValuesToOrderTable():

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::SetValuesToOrderTable(void)
{
//---
   int cnt=0;
   ulong    ticket;
   for(int i=0; i<OrdersTotal(); i++)
   {
      //--- order parameters
      if((ticket=OrderGetTicket(i))>0)
      {
         string position_symbol=OrderGetString(ORDER_SYMBOL);                          // symbol
         ulong  magic=OrderGetInteger(ORDER_MAGIC);                                    // order MagicNumber
         double volume=OrderGetDouble(ORDER_VOLUME_INITIAL);                           // order volume
         ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);            // order type
         double price=OrderGetDouble(ORDER_PRICE_OPEN);
         double stoploss=OrderGetDouble(ORDER_SL);
         double takeprofit=OrderGetDouble(ORDER_TP);
         string pos="";
         if(type==ORDER_TYPE_BUY_LIMIT)
            pos="Buy Limit";
         else if(type==ORDER_TYPE_SELL_LIMIT)
            pos="Sell Limit";
         else if(type==ORDER_TYPE_BUY_STOP)
            pos="Buy Stop";
         else if(type==ORDER_TYPE_SELL_STOP)
            pos="Sell Stop";
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
            {
               m_table_orders.SetValue(0,i,string(ticket));
               m_table_orders.SetValue(1,i,string(position_symbol));
               m_table_orders.SetValue(2,i,pos);
               m_table_orders.SetValue(3,i,string(volume));
               m_table_orders.SetValue(4,i,string(price));
               m_table_orders.SetValue(5,i,string(stoploss));
               m_table_orders.SetValue(6,i,string(takeprofit));
               cnt++;
            }
      }
   }
}

Y actualizamos los datos establecidos utilizando el método UpdateOrdersTable():

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::UpdateOrdersTable(void)
{
//--- Update the table
   m_table_orders.Update(true);
   m_table_orders.GetScrollVPointer().Update(true);
}

Para incluir todo en la aplicación, ejecutamos las mismas acciones de la pestaña anterior. Concretamente, inicializamos el recuadro con órdenes pendientes:

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
void CFastTrading::OnInitEvent(void)
{
   InitializeOrdersTable();
   InitializePositionsTable();
}

Y repetimos lo mismo en el manejador de eventos comerciales:

//+------------------------------------------------------------------+
//| Trade event                                                      |
//+------------------------------------------------------------------+
void CFastTrading::OnTradeEvent(void)
{
//--- 
   InitializePositionsTable();
   InitializeOrdersTable();
}

Para que, al clicar sobre una fila del recuadro, los parámetros de la orden pendiente se transfieran a los campos de edición, vamos a añadir un código en la sección correspondiente del manejador de eventos:

//--- Event of clicking on a table row
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
   {
      //--- Check the element ID
      if(lparam==m_table_positions.Id())
      {
         //---
         int row=m_table_positions.SelectedItem();
         m_lot_edit[6].SetValue(string(m_table_positions.GetValue(4,row)));
         m_sl_edit[6].SetValue(string(m_table_positions.GetValue(5,row)));
         m_tp_edit[6].SetValue(string(m_table_positions.GetValue(6,row)));
         m_lot_edit[6].GetTextBoxPointer().Update(true);
         m_sl_edit[6].GetTextBoxPointer().Update(true);
         m_tp_edit[6].GetTextBoxPointer().Update(true);
      }
      //--- Check the element ID
      if(lparam==m_table_orders.Id())
      {
         //---
         int row=m_table_orders.SelectedItem();
         m_pr_edit[4].SetValue(string(m_table_orders.GetValue(4,row)));
         m_sl_edit[7].SetValue(string(m_table_orders.GetValue(5,row)));
         m_tp_edit[7].SetValue(string(m_table_orders.GetValue(6,row)));
         m_pr_edit[4].GetTextBoxPointer().Update(true);
         m_sl_edit[7].GetTextBoxPointer().Update(true);
         m_tp_edit[7].GetTextBoxPointer().Update(true);
      }
   }

Lo último que nos queda por hacer es asignar las acciones necesarias a los botones Cambiar y Eliminar. Para ello, crearemos los métodos ModifyOrder() y RemoveOrder()

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::ModifyOrder(long lparam)
{
//--- Check the element ID
   if(lparam==m_small_button[2].Id())
   {
      //--- Get index and symbol
      if(m_table_orders.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_orders.SelectedItem();
      ulong ticket=(ulong)m_table_orders.GetValue(0,row);
      //---
      if(OrderSelect(ticket))
      {
         string position_symbol=OrderGetString(ORDER_SYMBOL);                          // symbol
         ulong  magic=OrderGetInteger(ORDER_MAGIC);                                    // order MagicNumber
         ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);            // order type
         //--- calculation and rounding of the Stop Loss and Take Profit values
         double sl=NormalizeDouble((double)m_sl_edit[7].GetValue(),_Digits);
         double tp=NormalizeDouble((double)m_tp_edit[7].GetValue(),_Digits);
         double price=NormalizeDouble((double)m_pr_edit[4].GetValue(),_Digits);
         //--- declare the request and the result
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- zeroing the request and result values
         ZeroMemory(request);
         ZeroMemory(result);
         //--- set the operation parameters
         request.action=TRADE_ACTION_MODIFY; // trading operation type
         request.order = ticket;             // order ticket
         request.symbol=Symbol();            // symbol
         request.sl      =sl;                // position Stop Loss
         request.tp      =tp;                // position Take Profit
         request.price=price;                // new price
         request.magic=m_magic_number;       // position MagicNumber
         //--- sending a request
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
//---
   return(false);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::RemoveOrder(long lparam)
{
//--- Check the element ID
   if(lparam==m_small_button[3].Id())
   {
      //--- Get index and symbol
      if(m_table_orders.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_orders.SelectedItem();
      ulong ticket=(ulong)m_table_orders.GetValue(0,row);
      //---
      if(OrderSelect(ticket))
      {
         string position_symbol=OrderGetString(ORDER_SYMBOL);                          // symbol
         ulong  magic=OrderGetInteger(ORDER_MAGIC);                                    // order MagicNumber
         ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);            // order type
         //--- declare the request and the result
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- zeroing the request and result values
         ZeroMemory(request);
         ZeroMemory(result);
         //--- set the operation parameters
         request.action=TRADE_ACTION_REMOVE;             // trading operation type
         request.order = ticket;                         // order ticket
         //--- sending a request
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
//---
   return(false);
}

Y luego los llamaremos en el manejador de eventos fuera de cualquier sección:

      //---
      if(ModifyOrder(lparam))
         return;
      if(RemoveOrder(lparam))
         return;

Querríamos añadir otra característica conveniente para trabajar con órdenes pendientes. Esta ofrece la posibilidad de establecer el precio de apertura de una orden pendiente previamente seleccionada, con solo clicar en el gráfico. Para mayor claridad, esta acción se muestra en la figura 12:


Fig.12 Estableciendo el precio de apertura de una orden pendiente.

La secuencia a seguir sería: 

  • Clicando con el ratón en un campo de edición, activamos su edición.
  • Después, transladamos el cursor del ratón a cualquier lugar del gráfico y nos orientamos según el eje de ordenadas. Para mayor comodidad, podemos pulsar la ruleta del ratón y seleccionar el precio necesario a través de la cruceta.
  • Tras detener el cursor del ratón en el precio necesario, clicamos en el gráfico, y el precio se trasladará al campo de edición.

Con este método, podremos establecer rápida y cómodamente el precio de una orden pendiente, especialmente si debemos hacerlo a ojo. Si necesitamos establecer el precio de una forma más precisa, podremos introducirlo en el campo de edición, utilizando para ello el teclado. La implementación es muy sencilla. Creamos en el manejador de la clase básica una sección con el evento de clic sobre el gráfico y añadimos el siguiente código:

//--- The event of clicking on the chart
   if(id==CHARTEVENT_CLICK)
   {
      for(int i=0; i<4; i++)
      {
         if(m_pr_edit[i].GetTextBoxPointer().TextEditState())
         {
            m_last_index=i;
            break;
         }
         else
         {
            if(m_last_index>=0)
            {
               //---
               datetime dt    =0;
               int      window=0;
               //--- convert X and Y coordinates to date/time
               if(ChartXYToTimePrice(0,(int)lparam,(int)dparam,window,dt,m_xy_price))
               {
                  m_pr_edit[m_last_index].SetValue(DoubleToString(m_xy_price));
                  m_pr_edit[m_last_index].GetTextBoxPointer().Update(true);
                  m_last_index=-1;
               }
            }
         }
      }
   }

Aquí, determinaremos cuál de los campos se está editando; luego lo guardaremos, obtendremos el valor haciendo clic en el gráfico e insertaremos este valor en el campo de edición. Podrá ver las principales posibilidades e innovaciones en el vídeo a continuación.




Conclusión

Al final del artículo se adjunta un fichero con todos los archivos enumerados, clasificados por carpetas. Por eso, para que funcione correctamente, basta con colocar la carpeta MQL5 en la carpeta raíz del terminal. Para encontrar la carpeta raíz del terminal en la que se encuentra la carpeta MQL5, debemos pulsar en MetaTarder 5 la combinación de   Ctrl+Shift+D o utilizar el menú contextual como se muestra en la fig.13, más abajo.


Fig.13 Abriendo la carpeta MQL5 en la carpeta raíz del terminal MetaTrader 5.

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

Archivos adjuntos |
MQL5.zip (5952.86 KB)
Teoría de probabilidad y estadística matemática con ejemplos (Parte I): Fundamentos y teoría elemental Teoría de probabilidad y estadística matemática con ejemplos (Parte I): Fundamentos y teoría elemental

El trading siempre ha estado relacionado con la toma de decisiones en condiciones de incertidumbre. Esto significa que los resultados de las decisiones tomadas no son totalmente obvios en el momento en que se toman. Por este motivo, resultan importantes los enfoques teóricos sobre la construcción de los modelos matemáticos que permiten describir estas situaciones ofreciendo información relevante e ilustrativa.

Trabajando con las series temporales en la biblioteca DoEasy (Parte 46): Búferes de indicador de periodo y símbolo múltiples Trabajando con las series temporales en la biblioteca DoEasy (Parte 46): Búferes de indicador de periodo y símbolo múltiples

En el presente artículo, mejoraremos las clases de los objetos de los búferes de indicador para trabajar en el modo multisímbolo. De esta forma, tendremos todo listo para crear en nuestros programas indicadores de periodo y símbolo múltiples. También añadiremos la funcionalidad que falta en los búferes de cálculo, lo cual nos permitirá crear indicadores estándar de periodo y símbolo múltiples.

Trabajando con las series temporales en la biblioteca DoEasy (Parte 47): Indicadores estándar de periodo y símbolo múltiples Trabajando con las series temporales en la biblioteca DoEasy (Parte 47): Indicadores estándar de periodo y símbolo múltiples

En el presente artículo, comenzaremos a desarrollar los métodos de trabajo con los indicadores estándar, lo cual nos permitirá crear indicadores estándar de periodo y símbolo múltiples basados en las clases de la bibliotecas. Asimismo, añadiremos a las clases de las series temporales el evento "Barras Omitidas" y aligeraremos el código del programa principal, trasladando las funciones de preparación de la biblioteca de dicho programa a la clase CEngine.

Sistema de notificaciones de voz para evetos comerciales y señales Sistema de notificaciones de voz para evetos comerciales y señales

En nuestros tiempos, los asistentes de voz juegan hace mucho un papel considerable en la vida del hombre, ya sea como navegador, buscador de voz o traductor. Por eso, en el presente artículo trataremos de desarrollar un sistema sencillo y comprensible de notificaciones de voz para los diferentes eventos comerciales, los estados del mercado o las señales de los sistemas comerciales.