Libro de Recetas de MQL5: Obtener Propiedades de Posiciones

Anatoli Kazharski | 28 mayo, 2014

Introducción

El anterior artículo titulado "MQL5 Cookbook: Using Different Print Modes" (“Libro de Recetas MQL5: Usar Diferentes Modos de Impresión”) nos mostró la información necesaria para usar tres modos diferentes. Ahora crearemos un script que facilitará y mostrará todas las propiedades de posición al usuario.

Deberemos implementarlo de modo que permita al usuario seleccionar la opción adecuada en los parámetros externos del script de la siguiente manera: para ver las propiedades de posición solo en un símbolo (el actual), o para ir a través de todas las posiciones abiertas (si es que las hay) una por una en todos los símbolos. Esta vez veremos la información requerida en un cuadro de diálogo, lo que resulta muy conveniente, y para algunos de ustedes este método resultará más útil.


Escribir un Script

El comienzo del programa es más o menos el mismo que en el artículo anterior (vea los códigos abajo). Empezaremos con las propiedades de programa. Le sigue la línea con la directiva #define, y después asignaremos el nombre del programa a la variable SCRIPT_NAME usando la función MQLInfoString() y la constante MQL5_PROGRAM_NAME que especifica. Puede encontrar más información sobre todos los valores posibles de la función MQLInfoString() en el material de referencia de MQL5.

Continuaremos con la enumeración de los modos. Si escribe un comentario para cada identificador, el texto de tal comentario se mostrará en la lista desplegable en los parámetros externos. Implementaremos dos opciones:

Solo se podrá usar un parámetro externo (mode) para seleccionar el modo apropiado. El comentario que sigue al parámetro externo también se mostrará en la ventana de parámetros externos. Esto nos permitirá crear nombres de parámetro más significativos. Al mismo tiempo, los nombres más cortos de variables resultarían más convenientes en lo referente al código.

#property copyright   "Copyright 2012, http://tol64.blogspot.com"
#property link        "http://tol64.blogspot.com"
#property description "email: hello.tol64@gmail.com"
#property version     "1.0"
#property script_show_inputs
//---
#define SCRIPT_NAME MQL5InfoString(MQL5_PROGRAM_NAME) // Script name
//---
// ENUMERATION OF MODES
enum ENUM_SYMBOLS_MODE
  {
   CURRENT_SYMBOL =0,                     // Current symbol
   ALL_SYMBOLS    =1                      // All symbols
  };
//---
// INPUT PARAMETERS
input ENUM_SYMBOLS_MODE mode=CURRENT_SYMBOL;     // Mode

El código continúa con variables globales. Para que se pueda acceder a las variables globales desde cualquier parte del script, deben colocarse fuera de las funciones (generalmente al principio del programa).

// GLOBAL VARIABLES
long                 pos_magic=0;         // Magic number
string               pos_symbol="";       // Symbol
string               pos_comment="";      // Comment
double               pos_swap=0.0;        // Swap
double               pos_commission=0.0;  // Commission
double               pos_price=0.0;       // Current price of the position
double               pos_cprice=0.0;      // Current price of the position
double               pos_profit=0.0;      // Profit/Loss of the position
double               pos_volume=0.0;      // Position volume
double               pos_sl=0.0;          // Stop Loss of the position
double               pos_tp=0.0;          // Take Profit of the position
datetime             pos_time=NULL;       // Position opening time
long                 pos_id=0;            // Position identifier
ENUM_POSITION_TYPE   pos_type=NULL;       // Position type
//---

En la función principal del programa solo llamaremos a una función definida por el usuario, PrintPositionProperties(), que llevará a cabo todas las operaciones requeridas:

//+------------------------------------------------------------------+
//| MAIN FUNCTION                                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   PrintPositionProperties();
  }

Ahora echemos un vistazo a la estructura de la función definida por el usuario PrintPositionProperties() paso a paso. Primero formaremos la base para nuestro trabajo a partir de aquí. Es muy sencilla, y al implementarla, tiene el siguiente aspecto:

//+------------------------------------------------------------------+
//| OPENING A DIALOG BOX WITH SYMBOL DATA                            |
//+------------------------------------------------------------------+
void PrintPositionProperties()
  {
   int err=0; // Variable for handling errors
//---
// If you need to get position properties on the current symbol only
   if(mode==CURRENT_SYMBOL)
     {
 
     }
//---
// If you need to get position properties on all symbols
   if(mode==ALL_SYMBOLS)
     {
 
     }
  }

Solo tenemos dos ramas y una variable local err que es responsable de gestionar errores y se declara al principio de la función. Ahora debemos usar ejemplos de casos para cada una de las opciones. Empecemos con la primera: "If you need to get position properties on the current symbol only" ("Si necesita obtener las propiedades de posición solo en el símbolo actual").

Es muy sencillo. En primer lugar, deberemos comprobar si hay una posición en el símbolo actual. Esto se puede hacer con la función PositionSelect(), disponible en MQL5, que toma el nombre de símbolo como el único parámetro. Para pasar el símbolo actual deberemos usar la función Symbol() o la variable predefinida _Symbol que ya contiene el nombre de símbolo actual. La función PositionSelect() devolverá un resultado positivo si existe una posición en ese símbolo, o un resultado negativo si no hay una posición o se ha producido un error.

Abajo puede ver el código con comentarios detallados para la primera opción:

//---
      // If a position exists, then...
      if(PositionSelect(_Symbol))
        {
         // ...get its properties
         GetPositionProperties();
         //---
         // Open a dialog box to display all the data we obtained
         MessageBox("Symbol        : "+pos_symbol+"\n"+
                    "Comment       : "+pos_comment+"\n"+
                    "Magic Number  : "+IntegerToString(pos_magic)+"\n"+
                    "Price Open    : "+DoubleToString(pos_price,_Digits)+"\n"+
                    "Current Price : "+DoubleToString(pos_cprice,_Digits)+"\n"+
                    "Stop Loss     : "+DoubleToString(pos_sl,_Digits)+"\n"+
                    "Take Profit   : "+DoubleToString(pos_tp,_Digits)+"\n"+
                    "Type          : "+PositionTypeToString(pos_type)+"\n"+
                    "Volume        : "+DoubleToString(pos_volume,2)+"\n"+
                    "Commission    : "+DoubleToString(pos_commission,2)+"\n"+
                    "Swap          : "+DoubleToString(pos_swap,2)+"\n"+
                    "Profit        : "+DoubleToString(pos_profit,2)+"\n"+
                    "Time          : "+TimeToString(pos_time)+"\n"+
                    "Identifier    : "+IntegerToString(pos_id)+"",
                    //---
                    "Message Box",MB_ICONASTERISK);
         //---
         return;
        }
      // If there is no position or an error has occurred, report it
      else
        {
         err=GetLastError(); // Get the code of the last registered error
         //---
         if(err>0) // If there is an error
           {
            // Print the relevant message
            MessageBox("Error ("+IntegerToString(err)+") when selecting a position ("+_Symbol+") !\n\n"+
                       "It is possible that there is no position on this symbol. If this is not the case, please try again.",
                       "Error",
                       MB_ICONWARNING);
            //---
            return; // Exit the function
           }
        }
      //---

En el código de arriba podemos ver dos funciones más definidas por el usuario - GetPositionProperties() y PositionTypeToString(). Puesto que tendremos que obtener propiedades de posición en varios puntos a través del programa, es conveniente crear una función separada para reducir la cantidad de código y hacerlo así más legible. Abajo está el código de esta función. Asegúrese de revisar el material de referencia de MQL para más información sobre las funciones e identificadores de MQL5 usados en GetPositionProperties().

//+------------------------------------------------------------------+
//| GETTING SYMBOL PROPERTIES                                        |
//+------------------------------------------------------------------+
void GetPositionProperties()
  {
   pos_symbol     =PositionGetString(POSITION_SYMBOL);
   pos_comment    =PositionGetString(POSITION_COMMENT);
   pos_magic      =PositionGetInteger(POSITION_MAGIC);
   pos_price      =PositionGetDouble(POSITION_PRICE_OPEN);
   pos_cprice     =PositionGetDouble(POSITION_PRICE_CURRENT);
   pos_sl         =PositionGetDouble(POSITION_SL);
   pos_tp         =PositionGetDouble(POSITION_TP);
   pos_type       =(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
   pos_volume     =PositionGetDouble(POSITION_VOLUME);
   pos_commission =PositionGetDouble(POSITION_COMMISSION);
   pos_swap       =PositionGetDouble(POSITION_SWAP);
   pos_profit     =PositionGetDouble(POSITION_PROFIT);
   pos_time       =(datetime)PositionGetInteger(POSITION_TIME);
   pos_id         =PositionGetInteger(POSITION_IDENTIFIER);
  }

La función definida por el usuario PositionTypeToString() convierte el tipo de posición devuelta como un número íntegro a una cadena de caracteres en formato legible, tal y como se muestra en el código de abajo:

//+------------------------------------------------------------------+
//| CONVERTING POSITION TYPE TO A STRING                             |
//+------------------------------------------------------------------+
string PositionTypeToString(int position_type)
  {
   string str="";
//---
   if(position_type==0) { str="buy";  }
   if(position_type==1) { str="sell"; }
//---
   return(str);
  }

Así, el código para la primera opción que nos permite ver las propiedades de posición solo en el símbolo actual queda listo. Incluso se puede poner a prueba ahora mismo, si ha seguido todos los pasos descritos en el artículo. Abrir una posición en MetaTrader 5 usando herramientas estándar. Para ello, pulse la tecla F9 y se abrirá la ventana Order (orden), en la que podrá encontrar todas las opciones necesarias para configurar propiedades de posición antes de abrirla:

Fig. 1. La ventana Order en el terminal de cliente MetaTrader 5.

Fig. 1. La ventana Order en el terminal de cliente MetaTrader 5.

Cuando todas las propiedades se hayan configurado, seleccione Sell (vender) o Buy (comprar) y ejecute el script haciendo doble click en él o arrastrándolo al gráfico. Se abrirá una ventana de script. El valor requerido (Current symbol (símbolo actual)) del parámetro Mode (modo) ya se ha configurado por defecto. Tras hacer click en el botón OK, se abrirá un cuadro de diálogo que mostrará todas las propiedades de posición en el símbolo actual:

Fig. 2. Cuadro de diálogo con propiedades de posición en el símbolo actual.

Fig. 2. Cuadro de diálogo con propiedades de posición en el símbolo actual.

De lo contrario, si no hay posición alguna en el símbolo actual, aparecerá un cuadro de alerta:

Fig. 3. Cuadro de alerta.

Fig. 3. Cuadro de alerta.

Todo parece funcionar según lo planeado e implementado en el código.

Ahora revisemos el código de programa que se usará si usted decide ver las propiedades de posición de todos los símbolos. Abajo puede ver el código con comentarios detallados:

//---
      int digits=0; // Number of decimal places
      int mb_res=-1; // Variable with the option selected in the dialog box
      int pos_total=PositionsTotal(); // Number of open positions in the terminal
      //---
      // View properties of all positions in a loop one by one
      for(int i=0; i<pos_total; i++)
        {
         ResetLastError(); // Reset the last error
         //---
         pos_symbol=PositionGetSymbol(i); // Get the symbol name
         digits=(int)SymbolInfoInteger(pos_symbol,SYMBOL_DIGITS); // Get the number of digits in the price
         //---
         // If a position on this symbol exists, then...
         if(PositionSelect(pos_symbol))
           {
            // ...get its properties
            GetPositionProperties();
            //---
            // Open a dialog box to display all position properties obtained
            mb_res=MessageBox("Total Positions/Current: "+IntegerToString(pos_total)+"/"+IntegerToString(i+1)+"\n"+
                              "---------------------------------\n"+
                              "Symbol: "        +pos_symbol+"\n"+
                              "Comment: "       +pos_comment+"\n"+
                              "Magic Number: "  +IntegerToString(pos_magic)+"\n"+
                              "Price Open: "    +DoubleToString(pos_price,digits)+"\n"+
                              "Current Price: " +DoubleToString(pos_cprice,digits)+"\n"+
                              "Stop Loss: "     +DoubleToString(pos_sl,digits)+"\n"+
                              "Take Profit: "   +DoubleToString(pos_tp,digits)+"\n"+
                              "Type: "          +PositionTypeToString(pos_type)+"\n"+
                              "Volume: "        +DoubleToString(pos_volume,2)+"\n"+
                              "Commission: "    +DoubleToString(pos_commission,2)+"\n"+
                              "Swap: "          +DoubleToString(pos_swap,2)+"\n"+
                              "Profit: "        +DoubleToString(pos_profit,2)+"\n"+
                              "Time: "          +TimeToString(pos_time)+"\n"+
                              "Identifier: "    +IntegerToString(pos_id)+"",
                              //---
                              "Message Box",MB_CANCELTRYCONTINUE|MB_ICONASTERISK);
            //---
            if(mb_res==IDCANCEL) // If you have clicked Cancel or Close
              { Print("The program ("+SCRIPT_NAME+") has been terminated by the user!"); return; } // Exit the function
            //---
            // If you have clicked Retry   
            if(mb_res==IDTRYAGAIN) { i--; } // Reset the counter to retry
           }
         else // If there is no position or an error has occurred, report it
           {
            err=GetLastError(); // Get the code of the last registered error
            //---
            if(err>0) // If there is an error
              {
               // Print the relevant message
               MessageBox("Error ("+IntegerToString(err)+") when selecting a position ("+pos_symbol+") !\n\n"+
                          "It is possible that there is no position on this symbol. If this is not the case, please try again.",
                          "Error",
                          MB_ICONWARNING);
              }
           }
        }
      //---

Ahora solo necesitamos poner a prueba esta opción. Por ejemplo, abramos posiciones en dos símbolos (AUDUSD y EURUSD). Al ejecutar el script, seleccione el modo All symbols (todos los símbolos) en la lista desplegable en los parámetros externos y haga click en OK. Se abrirá un cuadro de diálogo tal y como se muestra a continuación:

Fig. 4. Cuadro de diálogo con propiedades de posición para la segunda opción.

Fig. 4. Cuadro de diálogo con propiedades de posición para la segunda opción.


Conclusión

Como puede ver en la figura de arriba, hay tres botones en el cuadro de diálogo. Si hace click en Retry, el contador de bucle volverá a cero, y las propiedades de posición del símbolo mostrado actualmente en el cuadro de diálogo se actualizarán. Si hace click en Continue (continuar), el programa procederá al siguiente símbolo. El botón Cancel (cancelar) sirve para terminar el programa.

También debería tener en cuenta que la primera línea por encima de la lista de propiedades de posición contiene información sobre el número total de posiciones abiertas (Total Positions (posiciones totales)) y número actual del contador de posición (Current (actual)).

Eso es todo. Puede descargarse el archivo con el código fuente adjunto abajo, que deberá compilar en el MetaEditor.