English Русский 中文 Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Evaluación de los sistemas de trading -la eficiencia de entrada, salida y transacciones en general

Evaluación de los sistemas de trading -la eficiencia de entrada, salida y transacciones en general

MetaTrader 5Probador | 19 diciembre 2013, 08:10
2 306 0
Mykola Demko
Mykola Demko

Introducción

Hay muchos criterios que permiten determinar la eficiencia de un sistema de trading; y los traders eligen el que más les gusta. En este artículo se comentan los enfoques descritos en el libro "Statistika dlya traderov" ("Estadísticas para traders") escrito por S.V. Bulashev. Lamentablemente, se hicieron pocas copias de este libro y no se volvió a publicar durante mucho tiempo; sin embrago, su versión electrónica está todavía disponible en muchos sitios Web.


Prólogo

Te recuerdo que este libro fue publicado en 2003. Era la época de MetaTrader 3 con el lenguaje MQL-II. Y la plataforma era bastante avanzada para aquellos tiempos. De este modo podemos observar los cambios de las propias condiciones del trading comparándolo con el moderno terminal de cliente MetaTrader 5. Cabe destacar que el autor del libro se ha convertido en un gurú para muchas generaciones de traders (teniendo en cuenta el rápido cambio de generaciones en este sector). Pero el tiempo no permanece inmóvil; a pesar de que los principios descritos en el libro siguen siendo aplicables, los enfoques deben adaptarse

S.V. Bulashev escribió su libro, en primer lugar, basándose en las condiciones reales del trading en aquella época. Es por lo que no podemos utilizar las estadísticas descritas por el autor sin una conversión. Para aclarar las cosas, vamos a recordar las posibilidades de trading en aquellos tiempos: el trading marginal en un mercado al contado implica que la compra de una divisa para obtener beneficios especulativos se convierte en venta después de un tiempo.

Estos son los fundamentos y vale la pena recordarlos, es exactamente la interpretación utilizada al escribir el libro "Estadísticas para traders". Cada transacción de 1 lote se debe cerrar con la transacción inversa del mismo volumen. Sin embargo, dos años después (en el 2005), el uso de dichas estadísticas necesitaba una puesta a punto. Esto se debe a la posibilidad del cierre parcial de las transacciones en MetaTrader 4. Por lo tanto, para utilizar las estadísticas descritas por Bulashev necesitamos mejorar el sistema de interpretación, en particular, se debe hacer la interpretación al cierre y no a la apertura.

5 años después, la situación cambió de manera significativa. ¿Dónde está el término tan familiar "Orden"? Ya no está. Teniendo en cuenta la cantidad de preguntas en este foro, es mejor describir con exactitud el sistema de interpretación en MetaTrader 5.

Así que hoy en día, ya no está el término clásico Orden. Una orden ahora es una petición de trading al servidor del broker, que se hace mediante el trader o mediante MTS (Sistema de trading automatizado) para abrir o cambiar la posición de una operación de trading. Ahora se trata de una posición; para entender su significado he mencionado el trading marginal. De hecho, el trading marginal se lleva a cabo con dinero prestado; y la posición existe mientras existe el dinero.

Una vez ajustas las cuentas con el prestatario mediante el cierre de la posición y como resultado fijar un beneficio/pérdida, tu posición deja de existir. Por cierto, esto explica el motivo por el cual una posición inversa no la cierra. En realidad el préstamo permanece y no hay diferencia entre pedir dinero prestado para comprar o para vender. La transacción es sólo un antecedente de una orden ejecutada.

Hablemos ahora de las características del trading. En la actualidad, en MetaTrader 5, podemos a la vez cerrar parcialmente la posición de una operación de trading o incrementar una que ya existe. Por lo tanto, el clásico sistema de interpretación, donde cada apertura de posición con un determinado volumen es seguida por el cierre con el mismo volumen, ya es cosa del pasado. ¿Pero es realmente imposible recuperar esto de la información almacenada en MetaTrader 5? Así que en primer lugar vamos a reorganizar la interpretación.


La eficiencia de entrada

No es un secreto que mucha gente quiere que su trading sea más eficiente pero, ¿cómo describir (formalizar) este término? Si asumes que la transacción es la trayectoria por la que pasa el precio, entonces es obvio que hay dos puntos extremos en esta trayectoria: el mínimo y el máximo del precio dentro del rango observado. Todo el mundo trata de entrar al mercado lo más cerca posible al mínimo (al comprar). Esto se puede considerar como el principio fundamental de cualquier transacción: comprar barato y vender caro.

La eficiencia de entrada determina lo cerca que está del mínimo tu compra. En otras palabras, la eficiencia de entrada es la relación de la distancia entre el máximo y el precio de entrada, y toda la trayectoria. ¿Por qué medimos la distancia al mínimo a través de la diferencia del máximo? Al entrar en el mínimo, necesitamos que la eficiencia sea igual a 1 (e igual a 0 al entrar en el máximo).

Es por eso que usamos la distancia restante para nuestra relación y no la distancia entre el mínimo y la propia entrada. Tenemos que señalar aquí que la situación para la venta se refleja en la comparación con la compra.

 

La eficiencia de la entrada de una posición muestra lo bueno que es el MTS detectando los potenciales beneficios relativos al precio de entrada durante determinadas operaciones de trading. Se calcula mediante las siguientes fórmulas:

para posiciones largas
enter_efficiency=(max_price_trade-enter_price)/(max_price_trade-min_price_trade);

para posiciones cortas
enter_efficiency=(enter_price-min_price_trade)/(max_price_trade-min_price_trade);

La eficiencia de entrada puede tener un valor entre 0 y 1.


La eficiencia de salida

El caso de la salida es parecido:


La eficiencia de la salida de una posición muestra lo bueno que es el MTS detectando los potenciales beneficios relativos al precio de salida durante determinadas operaciones de trading. Se calcula mediante las siguientes fórmulas:


para posiciones largas
exit_efficiency=(exit_price - min_price_trade)/(max_price_trade - min_price_trade);

para posiciones cortas
exit_efficiency=(max_price_trade - exit_price)/(max_price_trade - min_price_trade);

La eficiencia de salida puede tener un valor entre 0 y 1.


La eficiencia de una operación de trading

En conjunto, la eficiencia de una operación de trading se determina tanto mediante la entrada como la salida. También se puede calcular como la relación de la trayectoria entre la entrada y la salida y la máxima distancia durante la operación (es decir la diferencia entre el mínimo y el máximo). Por lo tanto, se puede calcular la eficiencia de una operación de trading de dos maneras -directamente mediante la información principal sobre la operación, o mediante los resultados ya calculados de entradas y salidas evaluadas previamente (moviendo el intervalo).

La eficiencia de la salida de una operación de trading muestra lo bueno que es el MTS detectando los potenciales beneficios durante determinadas operaciones de trading. Se calcula mediante las siguientes fórmulas:

para posiciones largas
trade_efficiency=(exit_price-enter_price)/(max_price_trade-min_price_trade);

para posiciones cortas
trade_efficiency=(enter_price-exit_price)/(max_price_trade-min_price_trade);

fórmula general
trade_efficiency=enter_efficiency+exit_efficiency-1;

La eficiencia de una operación de trading puede tener un valor entre -1 y 1.
La eficiencia de una operación de trading debe ser mayor que 0,2. 
El análisis visual de la eficiencia muestra la dirección para mejorar el sistema, ya que permite evaluar la calidad de las señales para la entrada y la salida de una posición por separado.


Conversión de la interpretación

En primer lugar, y con el fin de evitar cualquier confusión, tenemos que dejar claros los nombres de los objetos de la interpretación. Puesto que MetaTrader 5 y Bulashev utilizan los mismos términos - order, deal, position, tenemos que separarlos. En mi artículo, voy a utilizar el término "trade" para la interpretación de objetos de Bulashev, es decir trade es una transacción; también utiliza el término "order" para la misma, en este contexto estos términos son idénticos. El llama posición a una transacción no completada, y nosotros la llamaremos una operación de trading abierta.

Puedes ver aquí que los 3 términos caben fácilmente en la misma palabra "trade". No vamos a renombrar la interpretación en MetaTrader5, y el significado de estos tres términos sigue siendo el que fue concebido por los desarrolladores del terminal de cliente. Como resultado, hay 4 palabras que vamos a utilizar - Position, Deal, Order y Trade.

La recopilación de las estadísticas se hace mediante las transacciones y no mediante las órdenes, ya que una Orden es una instrucción que se manda al servidor para abrir/cambiar una posición y no afecta a las estadísticas directamente, pero lo hace indirectamente a través de la transacción (ya que el envío de una orden no se traduce siempre en la ejecución de la correspondiente transacción con el precio y volumen indicados).

Vamos a ver el siguiente ejemplo de interpretación de la misma posición (para mayor claridad de la descripción anterior):

interpretación en МТ-5
deal[ 0 ]  in      0.1   sell   1.22218   2010.06.14 13:33
deal[ 1 ]  in/out  0.2   buy    1.22261   2010.06.14 13:36
deal[ 2 ]  in      0.1   buy    1.22337   2010.06.14 13:39
deal[ 3 ]  out     0.2   sell   1.22310   2010.06.14 13:41
interpretación por Bulashev
trade[ 0 ]  in 0.1  sell 1.22218 2010.06.14 13:33   out 1.22261 2010.06.14 13:36
trade[ 1 ]  in 0.1  buy  1.22261 2010.06.14 13:36   out 1.22310 2010.06.14 13:41
trade[ 2 ]  in 0.1  buy  1.22337 2010.06.14 13:39   out 1.22310 2010.06.14 13:41


Ahora voy a describir de qué manera se han llevado a cabo estas operaciones. Deal[ 0 ] abre la posición, lo escribimos como el inicio de una nueva operación de trading:

trade[ 0 ]  in 0.1  sell 1.22218 2010.06.14 13:33

Después está la posición inversa; significa que hay que cerrar todas las transacciones anteriores. Esto quiere decir que se tendrá en cuenta la información sobre la transacción inversa deal[ 1 ] tanto para el cierre como para la apertura de una nueva operación de trading. Una vez se cierran todas las transacciones abiertas antes de la transacción con la dirección in/out (entrada/salida), tenemos que abrir una nueva operación de trading. Es decir que sólo usamos la información de price (precio) y time (tiempo) relativa a la transacción seleccionada para el cierre, como inversa a la apertura de una transacción, cuando se utilizan también el type (tipo) y volume (volumen). Tenemos que aclarar aquí un término que aparece en la nueva interpretación y que no ha sido utilizado antes -es la dirección de la transacción. Antes, hemos supuesto que el término "direction" (dirección) para una compra o venta tenía el mismo significado que el término "type" (tipo). De ahora en adelante tipo y dirección son dos términos diferentes.

El tipo se refiere a comprar o vender, mientras la dirección se refiere a la entrada o salida de una posición. Es por eso que una posición se abre siempre con una transacción con la dirección in, y se cierra con una transacción out. Pero la dirección no se limita sólo a la apertura y cierre de posiciones. Estos términos engloban también el incremento del volumen de una posición (si la transacción "in" no es la primera de la lista) y el cierre parcial de una posición (si las transacciones "out" no son las últimas de la lista). Ya que el cierre parcial está disponible ahora, es también lógico introducir el inverso de la posición; se produce el inverso cuando se lleva a cabo una transacción opuesta de una tamaño superior, es decir que es una transacción in/out.

Así que hemos cerrado las operaciones de trading anteriores (para invertir la posición):

trade[ 0 ]  in 0.1  sell 1.22218 2010.06.14 13:33   out 1.22261 2010.06.14 13:36

La cantidad restante es de 0.1 lotes, y se utiliza para abrir una nueva operación de trading:

trade[ 1 ]  in 0.1  buy  1.22261 2010.06.14 13:36

Después está deal[ 2 ] con la dirección in, abrimos otra operación de trading:

trade[ 2 ]  in 0.1  buy  1.22337 2010.06.14 13:39

Y por último, la transacción que cierra la posición - deal[ 3 ] cierra todas las operaciones de trading en las posiciones que aún están abiertas:

trade[ 1 ]  in 0.1  buy  1.22261 2010.06.14 13:36   out 1.22310 2010.06.14 13:41
trade[ 2 ]  in 0.1  buy  1.22337 2010.06.14 13:39   out 1.22310 2010.06.14 13:41

La interpretación descrita más arriba muestra la esencia de la interpretación utilizada por Bulashev -cada operación abierta tiene un determinado punto de entrada y un determinado punto de salida, tiene su volumen y tipo. Pero este sistema de interpretación no tiene en cuenta un matiz -el cierre parcial. Si te fijas bien, veras que el número de operaciones de trading es igual al número de transacciones in (teniendo en cuenta las transacciones in/out)). En este caso, vale la pena interpretar por transacciones in, pero habrán más transacciones out en el cierre parcial (puede darse el caso en el que el número de transacciones in sea el mismo que el de las transacciones out, pero no tendrán el mismo volumen).

Para procesar todas las transacciones out, tenemos que interpretar mediante las transacciones out. Parece que esta contradicción no tiene solución si realizamos un procesamiento separado de las transacciones, primero -todas las transacciones in, y luego todas las out (o viceversa). Sin embargo, si procesamos las transacciones de forma secuencial y aplicamos una regla de procesamiento especial a cada una, entonces ya no hay contradicciones.

He aquí un ejemplo donde el número de transacciones out es superior al número de transacciones in (con la descripción):

interpretación en МТ-5
deal[ 0 ]  in      0.3   sell      1.22133   2010.06.15 08:00
deal[ 1 ]  out     0.2   buy       1.22145   2010.06.15 08:01
deal[ 2 ]  in/out  0.4   buy       1.22145   2010.06.15 08:02
deal[ 3 ]  in/out  0.4   sell      1.22122   2010.06.15 08:03
deal[ 4 ]  out     0.1   buy       1.2206    2010.06.15 08:06
interpretación por Bulashev                                       
trade[ 0 ]  in 0.2  sell    1.22133 2010.06.15 08:00   out 1.22145 2010.06.15 08:01   
trade[ 1 ]  in 0.1  sell    1.22133 2010.06.15 08:00   out 1.22145 2010.06.15 08:02   
trade[ 2 ]  in 0.3  buy     1.22145 2010.06.15 08:02   out 1.22122 2010.06.15 08:03   
trade[ 3 ]  in 0.1  sell    1.22122 2010.06.15 08:03   out 1.2206  2010.06.15 08:06    

Tenemos un caso, en el cual se cierra una transacción después de la apertura de una posición, pero sólo tiene una parte del volumen, no todo (0.3 lotes están abiertos y 0.2 están cerrados). ¿Cómo hacer frente a esta situación? Si cada operación de trading se cierra con el mismo volumen, se puede considerar la situación como la apertura de varias operaciones de trading con una sola transacción. Por lo tanto, tienen los mismos puntos de entrada y distintos puntos de salida (está claro que el volumen de cierre es lo que determina el volumen de cada operación de trading). Por ejemplo, elegimos deal[ 0 ] para el procesamiento, abrimos la operación de trading:

trade[ 0 ]  in 0.3  sell 1.22133 2010.06.15 08:00

Después seleccionamos deal[ 1 ], cerramos la operación de trading abierta, y durante el cierre descubrimos que le volumen de cierre no es suficiente. Hacemos una copia de la operación de trading previamente abierta e indicamos el volumen que falta en su parámetro "volume". Después cerramos la operación de trading inicial con el volumen de la transacción (es decir, cambiamos el volumen de la operación de trading inicial indicado a la apertura por el volumen de cierre):

trade[ 0 ]  in 0.2  sell 1.22133 2010.06.15 08:00   out 1.22145 2010.06.15 08:01   
trade[ 1 ]  in 0.1  sell 1.22133 2010.06.15 08:00

Tal conversión puede parecer incomoda para un trader, ya que este último puede querer abrir otra operación de trading, en lugar de esta. Pero en cualquier caso, una conversión correcta no puede perjudicar la evaluación de los sistemas. Lo único que pueda verse afectado es el grado de confianza de los traders en las operaciones de trading sin pérdidas en MetaTrader 4; este nuevo cálculo revelará todos los delirios.

El sistema de interpretación estadística descrito en el libro de Bulashev no tiene emociones y permite evaluar las decisiones honestamente desde la posición de entrada, salida y la evaluación conjunta de ambas. Y la posibilidad de convertir la interpretación (de un sistema a otro sin pérdida de datos) demuestra que es erróneo afirmar que no se puede convertir un MTS diseñado para MetaTrader 4 al sistema de interpretación de MetaTrader 5. La única pérdida durante la conversión de la interpretación puede ser la que corresponde al volumen de las diferentes órdenes (MetaTrader 4). Pero de hecho, si no hay más órdenes (en el viejo significado del término) a contabilizar, entonces se trata sólo de una estimación subjetiva del trader.


Código para la conversión de la interpretación

Vamos a echar un vistazo al propio código. Para preparar la conversión necesitamos la característica de herencia de la POO (Programación orientada a objetos). Por eso recomiendo a los que aún no están familiarizados con ella, que abran la Guía de usuario MQL5 y que conozcan la teoría. En primer lugar, vamos a describir una estructura de interpretación de una transacción (podemos acelerar el código obteniendo estos valores directamente mediante las funciones estándar de MQL5, pero es menos legible y te puede confundir).

//+------------------------------------------------------------------+
//| estructura de transacción                                        |
//+------------------------------------------------------------------+
struct S_Stat_Deals
  {
public:
   ulong             DTicket;         // ticket de la transacción   
   ENUM_DEAL_TYPE     deals_type;      // tipo de transacción
   ENUM_DEAL_ENTRY    deals_entry;     // dirección de la transacción 
   double            deals_volume;    // volumen de la transacción   
   double            deals_price;     // precio de apertura de la transacción   
   datetime          deals_date;      // momento de apertura de la transacción 

                     S_Stat_Deals(){};
                    ~S_Stat_Deals(){};
  };

Esta estructura contiene los principales detalles de la transacción, los detalles derivados no están incluidos ya que se pueden calcular si hace falta. Puesto que los desarrolladores ya han implementado varios métodos de las estadísticas de Bulashev en el simulador de estrategias, sólo nos falta complementarlos con métodos personalizados. Así que vamos a implementar dichos métodos y la eficiencia las transacciones en general, y la eficiencia de la apertura y el cierre.

Y para obtener estos valores, tenemos que implementar la interpretación de los datos principales, como el precio de apertura/cierre, el momento de apertura/cierre y el precio mínimo/máximo, en una operación de trading. Si disponemos de estos datos primordiales, podemos obtener muchos datos derivados de los mismos. También quiero llamar tu atención sobre la siguiente estructura de operaciones de trading, es la estructura principal y en ella se basan todas las conversiones de la interpretación.

//+------------------------------------------------------------------+
//| estructure de la operación de trading                            |
//+------------------------------------------------------------------+
struct S_Stat_Trades
  {
public:
   ulong             OTicket;         // ticket de apertura de transacción
   ulong             CTicket;         // ticket de cierre de transacción     
   ENUM_DEAL_TYPE     trade_type;     // tipo de la operación de trading
   double            trade_volume;    // volumen de la operación de trading
   double            max_price_trade; // precio máximo de la operación de trading
   double            min_price_trade; // precio mínimo de la operación de trading
   double            enter_price;     // precio de apertura de la operación de trading
   datetime          enter_date;      // momento de apertura de la operación de trading
   double            exit_price;      // precio de cierre de la operación de trading/s22>
   datetime          exit_date;       // momento de cierre de la operación de trading

   double            enter_efficiency;// eficiencia de entrada
   double            exit_efficiency; // eficiencia de salida
   double            trade_efficiency;// eficiencia de la operación de trading

                     S_Stat_Trades(){};
                    ~S_Stat_Trades(){};
  };


Ahora que hemos creado dos estructuras principales, podemos definir una nueva clase C_Pos, que convierte la interpretación. En primer lugar, vamos a declarar los punteros de las estructuras de interpretación de transacciones y operaciones de trading. Ya que se pueden obtener los datos necesarios a partir de las funciones heredadas, la declaras como public (pública); y como puede haber muchas transacciones y operaciones de trading, usa un array como puntero de estructura en lugar de una variable. De esta forma, los datos van a estar estructurados y disponibles desde cualquier lugar.

Después necesitamos dividir el historial en posiciones separadas y llevar a cabo la conversión tanto en la posición como en un ciclo completo de trading. Para ello, declara las variables para la interpretación de los atributos de la posición (id de la posición, símbolos de la posición, número de transacciones, número de operaciones de trading).

//+-----------------------------------------------------------------------+
//| clase para la conversión de transacciones en operaciones de trading   |
//+-----------------------------------------------------------------------+
class C_Pos
  {
public:
   S_Stat_Deals      m_deals_stats[];  // estructura de las transacciones
   S_Stat_Trades     m_trades_stats[]; // estructura las operaciones de trading
   long              pos_id;          // id de posición
   string            symbol;          // símbolo de posición
   int               count_deals;     // número de transacciones
   int               count_trades;    // número de operaciones de trading
   int               trades_ends;     // número de operaciones de trading cerradas
   int               DIGITS;          // precisión del volumen mínimo mediante los símbolos de la posición 
                     C_Pos()
     {
      count_deals=0;
      count_trades=0;
      trades_ends=0;
     };
                    ~C_Pos(){};
   void              OnHistory();         // creación del historial de posiciones
   void              OnHistoryTransform();// conversión del historial de posiciones al nuevo sistema de interpretación
   void              efficiency();        // cálculo de la eficiencia mediante el método de Bulashev

private:
   void              open_pos(int c);
   void              copy_pos(int x);
   void              close_pos(int i,int c);
   double            nd(double v){return(NormalizeDouble(v,DIGITS));};// normalización al volumen mínimo
   void              DigitMinLots(); // precisión del volumen mínimo
   double            iHighest(string          symbol_name,// nombre del símbolo
                              ENUM_TIMEFRAMES  timeframe,  // período
                              datetime         start_time, // fecha de inicio
                              datetime         stop_time   // fecha final
                              );
   double            iLowest(string          symbol_name,// nombre del símbolo
                             ENUM_TIMEFRAMES  timeframe,  // período
                             datetime         start_time, // fecha inicial
                             datetime         stop_time   // fecha final
                             );
  };


La clase tiene tres métodos públicos que procesan las posiciones.

OnHistory() crea el historial de la posición:
//+------------------------------------------------------------------+
//| Rellenar las estructuras de las transacciones del historial      |
//+------------------------------------------------------------------+
void C_Pos::OnHistory()
  {
   ArrayResize(m_deals_stats,count_deals);
   for(int i=0;i<count_deals;i++)
     {
      m_deals_stats[i].DTicket=HistoryDealGetTicket(i);
      m_deals_stats[i].deals_type=(ENUM_DEAL_TYPE)HistoryDealGetInteger(m_deals_stats[i].DTicket,DEAL_TYPE);   // type of deal
      m_deals_stats[i].deals_entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(m_deals_stats[i].DTicket,DEAL_ENTRY);// direction of deal
      m_deals_stats[i].deals_volume=HistoryDealGetDouble(m_deals_stats[i].DTicket,DEAL_VOLUME);              // volumen de la transacción
      m_deals_stats[i].deals_price=HistoryDealGetDouble(m_deals_stats[i].DTicket,DEAL_PRICE);                // precio de apertura
      m_deals_stats[i].deals_date=(datetime)HistoryDealGetInteger(m_deals_stats[i].DTicket,DEAL_TIME);        // momento de apertura
     }
  };

El método crea una estructura para cada transacción y la rellena con los datos de dicha transacción. Es exactamente a lo que me refería al afirmar que podemos prescindir de él, pero es mejor utilizarlo (los que persiguen la reducción del tiempo en microsegundos pueden sustituir la llamada a estas estructuras por la línea que queda a la derecha del signo igual "=").

OnHistoryTransform() convierte el historial de la posición al nuevo sistema de interpretación:

  • He explicado antes de que manera se deben convertir los datos, para ello, vamos a ver un ejemplo. Para la conversión necesitamos el valor exacto con el cual deberíamos calcular el volumen de una transacción (volumen mínimo); lo utiliza DigitMinLots() para hacer transacciones; no obstante, si un programador está seguro de que este código no se va a ejecutar bajo otras condiciones, se puede especificar este parámetro en el constructor y omitir la función.
  • A continuación, ponemos a cero los contadores count_trades y trades_ends. Después de eso, reasignamos la memoria para la estructura de interpretación de las operaciones de trading. Puesto que no sabemos el número exacto de operaciones, deberíamos reasignar la memoria según el número de transacciones en la posición. Si más adelante hay más operaciones de trading, tendremos que reasignar la memoria varias veces; pero al mismo tiempo la mayoría de las operaciones de trading tendrán bastante memoria, y la asignación de la memoria para todo el array ahorra mucho tiempo a nuestra máquina.

Recomiendo utilizar este método siempre que sea necesario; asignar la memoria cada vez que aparece un objeto de interpretación. Si no hay una información precisa sobre la cantidad de memoria requerida, tendremos que asignarle un valor aproximado. En cualquier caso es más eficiente que reasignar todo el array en cada etapa.

Después está el bucle en el cual se filtran todas las transacciones mediante tres filtros: si la transacción es in, in/out, out. Se implementa una operación específica para cada opción. Los filtros son secuenciales y anidados. En otras palabras, sólo en el caso de que un filtro devuelva false (falso), comprobaremos el siguiente filtro. Esta construcción permite ahorrar muchos recursos, gracias a la eliminación de las operaciones innecesarias. Para hacer el código más legible, se realizan muchas operaciones en las funciones declaradas en la clase como private (privadas). Por cierto, estas funciones eran del tipo public durante el desarrollo, pero al darme cuenta más adelante de que no hacen falta en las otras partes del código, las he vuelto a declarar del tipo private. Es así de fácil manejar el alcance de los datos en POO.

Por lo tanto, se lleva a cabo la creación de una nueva operación de trading en el filtro in (la función open_pos()), este es el motivo por el cual incrementamos el tamaño del array de punteros de uno y copiamos la estructura de la transacción en los campos correspondientes de la estructura de la operación de trading. Además, como la estructura de la operación de trading tiene dos veces más de campos para el precio y el tiempo, sólo se rellenan los campos de apertura cuando se abre una operación de trading, por lo que se considera incompleta; lo puedes entender con la diferencia ente count_trades y trades_ends. La cuestión está en los valores nulos de los contadores al inicio. En cuanto haya un una operación de trading, se incrementa el contador count_trades, y cuando se cierra la operación, se incrementa el contador trades_ends. De esta forma, podemos averiguar cuantas operaciones de trading están abiertas en cualquier momento, gracias a la diferencia entre count_trades and trades_ends.

La función open_pos() es bastante sencilla, sólo abre las operaciones de trading y activa el contador correspondiente; otras funciones de esta clase no son tan sencillas. Así que si una transacción no es del tipo in, entonces puede ser del tipo in/out o out. Primero, a partir de dos variantes, comprobamos la que se ejecuta más fácilmente (no es un problema fundamental, pero lo he hecho para comprobar la ejecución de fácil a difícil).

La función que procesa el filtro in/out suma las posiciones abiertas con todas las operaciones de trading abiertas (ya mencioné cómo averiguar cuáles son las operaciones de trading sin cerrar, mediante la diferencia entre count_trades y trades_ends). De este modo, calculamos el volumen total que se cierra mediante una determinada transacción (y se vuelve a abrir el resto del volumen pero con el tipo de la transacción actual). Hay que tener en cuenta que la transacción tiene la dirección in/out, Lo que significa que su volumen excede el volumen total de la posición abierta previamente. Es lo que explica la lógica de calcular la diferencia entre la posición y la transacción in/out, para conocer el volumen de la nueva operación de trading que se va a abrir.

Si la transacción tiene la dirección out, todo se vuelve más complicado. En primer lugar, la última transacción en una posición siempre tiene la dirección out, por lo que hacemos una excepción aquí -si es la última transacción, cerrar todo lo que tenemos. De lo contrario (si la transacción no es la última), hay dos escenarios posibles. Puesto que la transacción no es in/out, sino out, entonces hay dos escenarios posibles: en el primer escenario el volumen es exactamente igual al de la apertura, es decir, el volumen de la transacción de entrada es igual al volumen de la transacción de salida; en el segundo escenario estos volúmenes no son iguales.

El primer escenario se procesa mediante el cierre. El segundo escenario es más complicado, dos nuevos escenarios son posibles: cuando el volumen es superior o inferior al volumen de apertura. Cuando el volumen es superior, se cierra la siguiente operación de trading hasta que el volumen de cierre sea igual o inferior al volumen de apertura. Si el volumen no es suficiente para cerrar la siguiente operación de trading del todo (volumen insuficiente), se trata de un cierre parcial. Aquí tenemos que cerrar la operación de trading con el nuevo volumen (el que queda después de las operaciones anteriores), pero antes, hacemos una copia de la operación con el volumen que falta. Y por supuesto, no te olvides de los contadores.

En el trading, puede haber una situación en la que hay una cola con las últimas operaciones parcialmente cerradas después de abrir una operación. Para evitar la confusión, se desplazan todas de una posición hacia delante, para mantener el orden cronológico del cierre.

//+-------------------------------------------------------------------------------+
//| conversión de transacciones en operaciones de trading (clases motoras)        |
//+-------------------------------------------------------------------------------+
void C_Pos::OnHistoryTransform()
  {
   DigitMinLots();// rellenar el valor de DIGITS
   count_trades=0;trades_ends=0;
   ArrayResize(m_trades_stats,count_trades,count_deals);
   for(int c=0;c<count_deals;c++)
     {
      if(m_deals_stats[c].deals_entry==DEAL_ENTRY_IN)
        {
         open_pos(c);
        }
      else// else in
        {
         double POS=0;
         for(int i=trades_ends;i<count_trades;i++)POS+=m_trades_stats[i].trade_volume;

         if(m_deals_stats[c].deals_entry==DEAL_ENTRY_INOUT)
           {
            for(int i=trades_ends;i<count_trades;i++)close_pos(i,c);
            trades_ends=count_trades;
            open_pos(c);
            m_trades_stats[count_trades-1].trade_volume=m_deals_stats[c].deals_volume-POS;
           }
         else// else in/out
           {
            if(m_deals_stats[c].deals_entry==DEAL_ENTRY_OUT)
              {
               if(c==count_deals-1)// si es la última transacción
                 {
                  for(int i=trades_ends;i<count_trades;i++)close_pos(i,c);
                  trades_ends=count_trades-1;
                 }
               else// si no es la última transacción
                 {
                  double out_vol=nd(m_deals_stats[c].deals_volume);
                  while(nd(out_vol)>0)
                    {
                     if(nd(out_vol)>=nd(m_trades_stats[trades_ends].trade_volume))
                       {
                        close_pos(trades_ends,c);
                        out_vol-=nd(m_trades_stats[trades_ends].trade_volume);
                        trades_ends++;
                       }
                     else// Si el remanente de la posición cerrada es inferior al operación de trading siguiente
                       {
                        // adelantar todas la posiciones de uno
                        count_trades++;
                        ArrayResize(m_trades_stats,count_trades);
                        for(int x=count_trades-1;x>trades_ends;x--)copy_pos(x);
                        // abrir una copia con un volumen igual a la diferencia entre la posición actual y el remanente
                        m_trades_stats[trades_ends+1].trade_volume=nd(m_trades_stats[trades_ends].trade_volume-out_vol);
                        // cerrar la operación de trading actual con un nuevo volumen, que es igual al remanente
                        close_pos(trades_ends,c);
                        m_trades_stats[trades_ends].trade_volume=nd(out_vol);
                        out_vol=0;
                        trades_ends++;
                       }
                    }// while(out_vol>0)
                 }// si no es la última transacción
              }// if out
           }// else in/out
        }// else in
     }
  };


Cálculo de la eficiencia

Una vez convertido el sistema de interpretación, podemos evaluar la eficiencia de las transacciones mediante la metodología de Bulashev. Las funciones necesarias para esta evaluación están en el método efficiency(), también se rellena la estructura de trading con los datos ahí calculados. Se mide la eficiencia de entrada y salida de 0 a 1, y para toda la operación de trading se mide de -1 a 1.

//+------------------------------------------------------------------+
//| cálculo de la eficiencia                                         |
//+------------------------------------------------------------------+
void C_Pos::efficiency()
  {
   for(int i=0;i<count_trades;i++)
     {
      m_trades_stats[i].max_price_trade=iHighest(symbol,PERIOD_M1,m_trades_stats[i].enter_date,m_trades_stats[i].exit_date); // precio máximo de la operación de trading
      m_trades_stats[i].min_price_trade=iLowest(symbol,PERIOD_M1,m_trades_stats[i].enter_date,m_trades_stats[i].exit_date);  // precio mínimo de la operación de trading
      double minimax=0;
      minimax=m_trades_stats[i].max_price_trade-m_trades_stats[i].min_price_trade;// diferencia entre el máximo y mínimo
      if(minimax!=0)minimax=1.0/minimax;
      if(m_trades_stats[i].trade_type==DEAL_TYPE_BUY)
        {
         //Eficiencia de entrada de posición
         m_trades_stats[i].enter_efficiency=(m_trades_stats[i].max_price_trade-m_trades_stats[i].enter_price)*minimax;
         //Eficiencia de salida de una posición
         m_trades_stats[i].exit_efficiency=(m_trades_stats[i].exit_price-m_trades_stats[i].min_price_trade)*minimax;
         //Eficiencia de la operación de trading
         m_trades_stats[i].trade_efficiency=(m_trades_stats[i].exit_price-m_trades_stats[i].enter_price)*minimax;
        }
      else
        {
         if(m_trades_stats[i].trade_type==DEAL_TYPE_SELL)
           {
            //Eficiencia de entrada de posición
            m_trades_stats[i].enter_efficiency=(m_trades_stats[i].enter_price-m_trades_stats[i].min_price_trade)*minimax;
            //Eficiencia de salida de una posición
            m_trades_stats[i].exit_efficiency=(m_trades_stats[i].max_price_trade-m_trades_stats[i].exit_price)*minimax;
            //Eficiencia de la operación de trading
            m_trades_stats[i].trade_efficiency=(m_trades_stats[i].enter_price-m_trades_stats[i].exit_price)*minimax;
           }
        }
     }
  }


El método utiliza dos métodos privados iHighest() y iLowest(), son parecidos y la única diferencia entre ellos consiste en los datos requeridos y la función de búsqueda fmin o fmax.

//+------------------------------------------------------------------+
//| búsqueda del máximo con el período start_time --> stop_time      |
//+------------------------------------------------------------------+
double C_Pos::iHighest(string           symbol_name,// nombre del símbolo
                       ENUM_TIMEFRAMES  timeframe,  // período
                       datetime         start_time, // fecha de inicio
                       datetime         stop_time   // fecha final
                       )
  {
   double  buf[];
   datetime  start_t=(start_time/60)*60;// normalización del momento de apertura
   datetime  stop_t=(stop_time/60+1)*60;// normalización del momento de cierre 
   int period=CopyHigh(symbol_name,timeframe,start_t,stop_t,buf);
   double res=buf[0];
   for(int i=1;i<period;i++)
      res=fmax(res,buf[i]);
   return(res);
  }

El método busca el máximo en el intervalo entre las dos fechas indicadas. Los parámetros start_time y stop_time se envían a la función para las fechas. Puesto que las fechas se envían a la función y la petición de una operación de trading puede llegar en medio de una barra de un minuto, la normalización de la fecha, al valor más próximo de la barra, se lleva a cabo en la función. Se hace lo mismo con la función iLowest(). Con el elaborado método efficiency() tenemos toda lo necesario para trabajar con una posición; pero aún no hay ningún procesamiento de la propia posición. Vamos a adelantar esto definiendo una nueva clase, y todos los métodos anteriores estarán disponibles en ella; en otras palabra, la declaramos como una derivada de C_Pos.


Clase derivada (clases motoras)

class C_PosStat:public C_Pos

Para explicar la información estadística, crea una estructura que se asignará a la nueva clase.

//+------------------------------------------------------------------+
//| estructura de eficiencia                                         |
//+------------------------------------------------------------------+
struct S_efficiency

  {
   double            enter_efficiency; // eficiencia de entrada
   double            exit_efficiency;  // eficiencia de salida
   double            trade_efficiency; // eficiencia de la operación de trading
                     S_efficiency()
     {
      enter_efficiency=0;
      exit_efficiency=0;
      trade_efficiency=0;
     };
                    ~S_efficiency(){};
  };


Y el cuerpo de la propia clase.

//+------------------------------------------------------------------+
//| clase de estadísticas de la operación de trading en conjunto     |
//+------------------------------------------------------------------+
class C_PosStat:public C_Pos
  {
public:
   int               PosTotal;         // número de posiciones en el historial
   C_Pos             pos[];            // array de punteros a las posiciones
   int               All_count_trades; // número total de operaciones de trading en el historial
   S_efficiency      trade[];          // array de punteros a la estructura de eficiencia de entrada, salida y operaciones de trading
   S_efficiency      avg;              // array de punteros a la estructura del valor promedio de la eficiencia de entrada, salida y operaciones de trading
   S_efficiency      stdev;            // puntero a la estructura de la desviación estándar desde 
                                       // valor promedio de la eficiencia de entrada, salida y operaciones de trading

                     C_PosStat(){PosTotal=0;};
                    ~C_PosStat(){};
   void              OnPosStat();                         // clases motoras
   void              OnTradesStat();                      // recopilación de información sobre las operaciones de trading en el array común
   
   // funciones para escribir datos en un archivo
   void              WriteFileDeals(string folder="deals");
   void              WriteFileTrades(string folder="trades");
   void              WriteFileTrades_all(string folder="trades_all");
   void              WriteFileDealsHTML(string folder="deals");
   void              WriteFileDealsHTML2(string folder="deals");
   void              WriteFileTradesHTML(string folder="trades");
   void              WriteFileTradesHTML2(string folder="trades");
   string            enum_translit(ENUM_DEAL_ENTRY x,bool latin=true);// conversión de la enumeración en una cadena
   string            enum_translit(ENUM_DEAL_TYPE x,bool latin=true); 
                                                              // conversión de la enumeración en una cadena (sobrecargada)
private:   

   S_efficiency      AVG(int count);                                        // media aritmética
   S_efficiency      STDEV(const S_efficiency &mo,int count);               // desviación estándar
   S_efficiency      add(const S_efficiency &a,const S_efficiency &b);      //añadir
   S_efficiency      take(const S_efficiency &a,const S_efficiency &b);     //restar
   S_efficiency      multiply(const S_efficiency &a,const S_efficiency &b); //multiplicar
   S_efficiency      divided(const S_efficiency &a,double b);               //dividir
   S_efficiency      square_root(const S_efficiency &a);                    //raíz cuadrada
   string            Head_style(string title);
  };  


Sugiero analizar esta clase al revés, desde el final hasta el principio. Todo finaliza con la escritura de una tabla de las transacciones y las operaciones de trading en los archivos. Una serie de funciones están escritas para este propósito (puedes saber para qué sirve cada una por su nombre). Las funciones crean un informe csv relativo a las transacciones y las operaciones de trading además de dos tipos de informes html (el contenido es el mismo, la única diferencia es visual).

      void              WriteFileDeals();      // escritura del informe csv sobre las transacciones
      void              WriteFileTrades();     // escritura del informe csv sobre las operaciones de trading
      void              WriteFileTrades_all(); // escritura de un informe resumido sobre las funciones de aptitud
      void              WriteFileDealsHTML2(); // escritura del informe html sobre las transacciones, 1 opción 
      void              WriteFileTradesHTML2();// escritura del informe html sobre las operaciones de trading, 2 opciones

La función enum_translit() está diseñada para convertir los valores de tipo numérico al tipo cadena para escribirlos en el archivo de registro. La sección private contiene varias funciones de la estructura S_efficiency. Todas las funciones conforman las deficiencias del lenguaje, y en particular las operaciones aritméticas con estructuras. Debido a las distintas opiniones sobre la implementación de estos métodos, se pueden llevar a cabo de distintas maneras. He utilizados los campos de estructuras para conseguir métodos de operaciones aritméticas. Hay quien puede decir que es mejor procesar cada campo de estructura mediante un sólo método. En resumen, yo diría que hay tantas opiniones como programadores. Espero que en un futuro tengamos la posibilidad de llevar a cabo estas operaciones mediante métodos integrados.

El método AVG() calcula el valor de la media aritmética del array, pero no muestra la imagen de la distribución global, por eso se proporciona otro método que calcula la desviación estándar STDEV(). La función OnTradesStat() obtiene los valores de la eficiencia (previamente calculados en OnPosStat()) y los procesa con métodos estadísticos. Y por último, la función principal de la clase - OnPosStat().

Hay que abordar esta función en detalle. Se compone de dos partes, por lo que se puede dividir fácilmente. La primera parte busca todas la posiciones, procesa su id y lo guarda en el array temporal id_pos. Paso a paso: selecciona el historial disponible entero, calcula el número de transacciones, ejecuta el bucle de procesamiento de transacciones. El bucle: si la transacción es de tipo balance, omitirla (no hace falta interpretar la transacción del inicio), de lo contrario guardar el id de la posición en la variable y llevar a cabo la búsqueda. Si ya existe el mismo id en la base de datos (el array id_pos), pasar a la siguiente transacción, de lo contrario, escribe el id en la base de datos. De esta manera, después de procesar todas las transacciones, todos los id' existentes de las posiciones y el número de posiciones se almacenarán en el array.

   long  id_pos[];// array adicional para crear el historial de las posiciones
   if(HistorySelect(0,TimeCurrent()))
     {
      int HTD=HistoryDealsTotal();
      ArrayResize(id_pos,PosTotal,HTD);
      for(int i=0;i<HTD;i++)
        {
         ulong DTicket=(ulong)HistoryDealGetTicket(i);
         if((ENUM_DEAL_TYPE)HistoryDealGetInteger(DTicket,DEAL_TYPE)==DEAL_TYPE_BALANCE)
            continue;// si es una transacción de tipo balance, omitir
         long id=HistoryDealGetInteger(DTicket,DEAL_POSITION_ID);
         bool present=false; // estado inicial, no hay tal posición           
         for(int j=0;j<PosTotal;j++)
           { if(id==id_pos[j]){ present=true; break; } }// si existe tal posición, detener

         if(!present)// escribir id al aparecer una nueva posición
           {
            PosTotal++;
            ArrayResize(id_pos,PosTotal);
            id_pos[PosTotal-1]=id;
           }
        }
     }
   ArrayResize(pos,PosTotal);

En la siguiente parte, implementamos todos los métodos descritos anteriormente en la clase base C_Pos. Se trata del bucle que recorre las posiciones y ejecuta los correspondientes métodos de procesamiento de posiciones. El método está descrito en el siguiente código.

   for(int p=0;p<PosTotal;p++)
     {
      if(HistorySelectByPosition(id_pos[p]))// seleccionar posición
        {
         pos[p].pos_id=id_pos[p]; // asignar id de la posición al campo correspondiente de clase C_Pos
         pos[p].count_deals=HistoryDealsTotal();// asignar el número de transacción en la posición al campo de clase C_Pos
         pos[p].symbol=HistoryDealGetString(HistoryDealGetTicket(0),DEAL_SYMBOL);// las mismas operaciones con el símbolo
         pos[p].OnHistory();          // empezar a rellenar la estructura con el historial de la posición
         pos[p].OnHistoryTransform(); // conversión de la interpretación, rellenar la estructura
         pos[p].efficiency();         // cálculo de la eficiencia de los datos obtenidos
         All_count_trades+=pos[p].count_trades;// guardar el número de operaciones de trading para mostrar el total
        }
     }


Llamadas a los métodos de la clase

Hemos considerado toda la clase. Es el momento de ver un ejemplo de llamada. Para mantener las posibilidades de construcción, no he declarado la llamada de forma explícita en una función. Además, puedes mejorar la clase para tus necesidades e implementar nuevos métodos de procesamiento estadístico de datos. Este es un ejemplo de llamada de un método de la clase a partir de un script:

//+------------------------------------------------------------------+
//| Función de inicio del script                                     |
//+------------------------------------------------------------------+
#include <Bulaschev_Statistic.mqh> void OnStart()
  {
   C_PosStat  start;
   start.OnPosStat();
   start.OnTradesStat();
   start.WriteFileDeals();
   start.WriteFileTrades();
   start.WriteFileTrades_all();
   start.WriteFileDealsHTML2();
   start.WriteFileTradesHTML2();
   Print("cko tr ef=" ,start.stdev.trade_efficiency);
   Print("mo  tr ef=" ,start.avg.trade_efficiency);
   Print("cko out ef=",start.stdev.exit_efficiency);
   Print("mo  out ef=",start.avg.exit_efficiency);
   Print("cko in ef=" ,start.stdev.enter_efficiency);
   Print("mo  in ef=" ,start.avg.enter_efficiency);
  }

El script crea 5 archivos de informes según la cantidad de las funciones que escriben datos en el archivo del repertorio Files\OnHistory. Las siguientes funciones principales se encuentran en OnPosStat() y OnTradesStat(), se utilizan para llamar a todos los métodos necesarios. El script termina mostrando el valor obtenido de la eficiencia del trading en su conjunto. Se puede utilizar cada uno de estos valores para una optimización genética.

Puesto que no hace falta escribir cada informe en un archivo durante la optimización, la llamada a la clase en un Expert Advisor se ve un poco diferente. En primer lugar, a diferencia de un script, un Expert Advisor se puede ejecutar en el simulador (lo hemos preparado para este propósito). Trabajar con el simulador de estrategias tiene sus peculiaridades. Durante la optimización, podemos utilizar la función OnTester() por lo que su ejecución se lleva a cabo antes de la ejecución de la función OnDeinit(). Por lo tanto, se puede llamar a los métodos de conversión por separado. Para facilitar la modificación de la función de aptitud a partir de los parámetros de un Expert Advisor, he declarado una enumeración global, pero no como parte de la clase. La enumeración está en la misma hoja que los métodos de la clase C_PosStat.

//+------------------------------------------------------------------+
//| enumeración de las funciones de aptitud                          |
//+------------------------------------------------------------------+
enum Enum_Efficiency
  {
   avg_enter_eff,
   stdev_enter_eff,
   avg_exit_eff,
   stdev_exit_eff,
   avg_trade_eff,
   stdev_trade_eff
  };

 Esto es lo que hay que añadir al encabezado del Expert Advisor.

#include <Bulaschev_Statistic.mqh>
input Enum_Efficiency result=0;// Función de aptitud


Ahora, sólo podemos describir cómo se pasan los parámetros mediante el operador switch.

//+------------------------------------------------------------------+
//| Función de optimización del experto                              |
//+------------------------------------------------------------------+
double OnTester()
  {
   start.OnPosStat();
   start.OnTradesStat();
   double res;
   switch(result)
     {
      case 0: res=start.avg.enter_efficiency;    break;
      case 1: res=-start.stdev.enter_efficiency; break;
      case 2: res=start.avg.exit_efficiency;     break;
      case 3: res=-start.stdev.exit_efficiency;  break;
      case 4: res=start.avg.trade_efficiency;    break;
      case 5: res=-start.stdev.trade_efficiency; break;
      default : res=0; break;
     }  
   return(res);
  }

Quiero recordarte que la función OnTester() se utiliza para maximizar la función personalizada. Si necesitas encontrar el mínimo de la función personalizada, lo mejor sería multiplicar la propia función por -1. Igual que en el ejemplo de la desviación estándar, todos sabemos que más pequeño es el valor de stdev, más pequeña es la diferencia entre las eficiencias de las operaciones de trading, por lo tanto, las operaciones tienen mayor estabilidad. Por esta razón hay que minimizar stdev. Ahora que ya hemos visto las llamadas a los métodos de la clase, vamos a ver la escritura de informes en un archivo.

Había mencionado antes los métodos de la clase que crean el informe. Vamos a ver ahora dónde y cuándo hay que llamarlos. Los informes sólo se deben crear cuando el Expert Advisor está en marcha para una sola ejecución. De lo contrario, el Expert Advisor creará los informes en el modo de optimización; es decir, en lugar de crear un archivo creará muchos archivos (si se pasan nombres de archivo distintos cada vez) o uno, pero el último con el mismo nombre para todas las ejecuciones, lo que no significa nada, ya que gasta un recurso para una información que se va a eliminar.

En cualquier caso, no debes crear archivos de informes durante la optimización. Si obtienes muchos archivos con distintos nombres, probablemente no abrirías la mayoría de ellos. La segunda opción es una pérdida de recursos para obtener una información que se va a eliminar enseguida.

Por eso, la mejor opción es crear un filtro (iniciar el informe únicamente en el modo Optimization[disabled]). De esta manera, no se llena el disco duro con informes que no se van a ver nunca. Por otra parte, se incrementa la velocidad de optimización (no es ningún secreto que las operaciones con los archivos son las más lentas); además, se mantiene la posibilidad de obtener rápidamente un informe con los parámetros necesarios. En realidad, no importa dónde se implementa el filtro, se puede hacer en la función OnTester() o en OnDeninit(). Lo importante es que la llamada a los métodos de la clase, que crean el informe, se debe hacer después de los métodos principales que llevan a cabo la conversión. He implementado el filtro en OnDeinit() para no sobrecargar el código:

//+------------------------------------------------------------------+
//| Función de desinicialización del experto                         |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(!(bool)MQL5InfoInteger(MQL5_OPTIMIZATION))
     {
      start.WriteFileDeals();      // escritura del informe csv sobre las transacciones
      start.WriteFileTrades();     // escritura del informe csv sobre las operaciones de trading
      start.WriteFileTrades_all(); // escritura del informe csv resumido sobre las funciones de aptitud
      start.WriteFileDealsHTML2(); // escritura del informe html sobre las transacciones
      start.WriteFileTradesHTML2();// escritura del informe html sobre las operaciones de trading
     }
  }
//+------------------------------------------------------------------+

El orden de llamada de los métodos no es importante. Todo lo necesario para crear los informes está en los métodos OnPosStat y OnTradesStat. Además, no importa si llamas a todos los métodos de escritura de informes o sólo a algunos; cada uno funciona de manera independiente; es una interpretación de la información que ya está almacenada en la clase.

Comprobación en el Simulador de estrategias

A continuación está el resultado de una sola ejecución en el simulador de estrategias:

Estadística de los promedios móviles de las operaciones de trading
# Ticket Tipo volumen Apertura Cierre Precio Eficiencia
apertura cierre precio tiempo precio tiempo máx. mín. entrada salida transacción
pos[0] id 2 EURUSD
0 2 3 buy 0.1 1.37203 2010.03.15 13:00:00 1.37169 2010.03.15 14:00:00 1.37236 1.37063 0.19075 0.61272 -0.19653
pos[1] id 4 EURUSD
1 4 5 sell 0.1 1.35188 2010.03.23 8:00:00 1.35243 2010.03.23 10:00:00 1.35292 1.35025 0.61049 0.18352 -0.20599
pos[2] id 6 EURUSD
2 6 7 sell 0.1 1.35050 2010.03.23 12:00:00 1.35343 2010.03.23 16:00:00 1.35600 1.34755 0.34911 0.30414 -0.34675
pos[3] id 8 EURUSD
3 8 9 sell 0.1 1.35167 2010.03.23 18:00:00 1.33343 2010.03.26 5:00:00 1.35240 1.32671 0.97158 0.73842 0.71000
pos[4] id 10 EURUSD
4 10 11 sell 0.1 1.34436 2010.03.30 16:00:00 1.33616 2010.04.08 23:00:00 1.35904 1.32821 0.52384 0.74213 0.26597
pos[5] id 12 EURUSD
5 12 13 buy 0.1 1.35881 2010.04.13 8:00:00 1.35936 2010.04.15 10:00:00 1.36780 1.35463 0.68261 0.35915 0.04176
pos[6] id 14 EURUSD
6 14 15 sell 0.1 1.34735 2010.04.20 4:00:00 1.34807 2010.04.20 10:00:00 1.34890 1.34492 0.61055 0.20854 -0.18090
pos[7] id 16 EURUSD
7 16 17 sell 0.1 1.34432 2010.04.20 18:00:00 1.33619 2010.04.23 17:00:00 1.34491 1.32016 0,97616 0.35232 0.32848
pos[8] id 18 EURUSD
8 18 19 sell 0.1 1.33472 2010.04.27 10:00:00 1.32174 2010.04.29 5:00:00 1.33677 1.31141 0.91916 0.59267 0.51183
pos[9] id 20 EURUSD
9 20 21 sell 0.1 1.32237 2010.05.03 4:00:00 1.27336 2010.05.07 20:00:00 1.32525 1.25270 0.96030 0.71523 0.67553

Informe de eficiencia
Función de actitud Valor promedio Desviación estándar
Entrada 0.68 0.26
Salida 0.48 0.21
Transacciones 0.16 0.37


Este es el gráfico del saldo:


En el gráfico, puedes ver claramente que la función personalizada de optimización no trata de elegir los parámetros para una mayor cantidad de transacciones, sino las transacciones con mayor duración, de esta manera tienen casi el mismo beneficio, es decir, la dispersión no es alta.

Puesto que el código de los Promedios móviles no dispone de las características para incrementar el volumen de una posición o cerrarla parcialmente, no parece que el resultado de la conversión sea próximo al que se ha descrito anteriormente. Más abajo, puedes encontrar otros resultados de la ejecución del script con la cuenta abierta especialmente para probar los códigos:

pos[286] id 1019514 EURUSD
944 1092288 1092289 buy 0.1 1.26733 2010.07.08 21:14:49 1.26719 2010.07.08 21:14:57 1.26752 1.26703 0.38776 0.32653 -0.28571
pos[287] id 1019544 EURUSD
945 1092317 1092322 sell 0.2 1.26761 2010.07.08 21:21:14 1.26767 2010.07.08 21:22:29 1.26781 1.26749 0.37500 0.43750 -0.18750
946 1092317 1092330 sell 0.2 1.26761 2010.07.08 21:21:14 1.26792 2010.07.08 21:24:05 1.26782 1.26749 0.36364 -0.30303 -0.93939
947 1092319 1092330 sell 0.3 1.26761 2010.07.08 21:21:37 1.26792 2010.07.08 21:24:05 1.26782 1.26749 0.36364 -0.30303 -0.93939
pos[288] id 1019623 EURUSD
948 1092394 1092406 buy 0.1 1.26832 2010.07.08 21:36:43 1.26843 2010.07.08 21:37:38 1.26882 1.26813 0.72464 0.43478 0.15942
pos[289] id 1019641 EURUSD
949 1092413 1092417 buy 0.1 1.26847 2010.07.08 21:38:19 1.26852 2010.07.08 21:38:51 1.26910 1.26829 0.77778 0.28395 0.06173
950 1092417 1092433 sell 0.1 1.26852 2010.07.08 21:38:51 1.26922 2010.07.08 21:39:58 1.26916 1.26829 0.26437 -0.06897 -0.80460
pos[290] id 1150923 EURUSD
951 1226007 1226046 buy 0.2 1.31653 2010.08.05 16:06:20 1.31682 2010.08.05 16:10:53 1.31706 1.31611 0.55789 0.74737 0.30526
952 1226024 1226046 buy 0.3 1.31632 2010.08.05 16:08:31 1.31682 2010.08.05 16:10:53 1.31706 1.31611 0.77895 0.74737 0.52632
953 1226046 1226066 sell 0.1 1.31682 2010.08.05 16:10:53 1.31756 2010.08.05 16:12:49 1.31750 1.31647 0.33981 -0.05825 -0.71845
954 1226046 1226078 sell 0.2 1.31682 2010.08.05 16:10:53 1.31744 2010.08.05 16:15:16 1.31750 1.31647 0.33981 0.05825 -0.60194
pos[291] id 1155527 EURUSD
955 1230640 1232744 sell 0.1 1.31671 2010.08.06 13:52:11 1.32923 2010.08.06 17:39:50 1.33327 1.31648 0.01370 0.24062 -0.74568
956 1231369 1232744 sell 0.1 1.32584 2010.08.06 14:54:53 1.32923 2010.08.06 17:39:50 1.33327 1.32518 0.08158 0.49938 -0.41904
957 1231455 1232744 sell 0.1 1.32732 2010.08.06 14:58:13 1.32923 2010.08.06 17:39:50 1.33327 1.32539 0.24492 0.51269 -0.24239
958 1231476 1232744 sell 0.1 1.32685 2010.08.06 14:59:47 1.32923 2010.08.06 17:39:50 1.33327 1.32539 0.18528 0.51269 -0.30203
959 1231484 1232744 sell 0.2 1.32686 2010.08.06 15:00:20 1.32923 2010.08.06 17:39:50 1.33327 1.32539 0.18655 0.51269 -0.30076
960 1231926 1232744 sell 0.4 1.33009 2010.08.06 15:57:32 1.32923 2010.08.06 17:39:50 1.33327 1.32806 0.38964 0.77543 0.16507
961 1232591 1232748 sell 0.4 1.33123 2010.08.06 17:11:29 1.32850 2010.08.06 17:40:40 1.33129 1.32806 0.98142 0.86378 0.84520
962 1232591 1232754 sell 0.4 1.33123 2010.08.06 17:11:29 1.32829 2010.08.06 17:42:14 1.33129 1.32796 0.98198 0.90090 0.88288
963 1232591 1232757 sell 0.2 1.33123 2010.08.06 17:11:29 1.32839 2010.08.06 17:43:15 1.33129 1.32796 0.98198 0.87087 0.85285
pos[292] id 1167490 EURUSD
964 1242941 1243332 sell 0.1 1.31001 2010.08.10 15:54:51 1.30867 2010.08.10 17:17:51 1.31037 1.30742 0.87797 0.57627 0.45424
965 1242944 1243333 sell 0.1 1.30988 2010.08.10 15:55:03 1.30867 2010.08.10 17:17:55 1.31037 1.30742 0.83390 0.57627 0.41017
pos[293] id 1291817 EURUSD
966 1367532 1367788 sell 0.4 1.28904 2010.09.06 0:24:01 1.28768 2010.09.06 2:53:21 1.28965 1.28710 0.76078 0.77255 0.53333


Así es como se ve la información convertida; para permitir a los lectores considerar todo deliberadamente (y la cognición viene a través de la comparación), guardo el historial original en un archivo separado; muchos traders echan en falta este historial, es el que se utiliza para visualizar la sección [Results] (resultados) de MetaTrader 4.

Conclusión

En conclusión, me gustaría recomendar a los desarrolladores que añadan la posibilidad de optimizar el Expert Advisor, pero no sólo mediante el parámetro personalizado, sino haciéndolo en combinación con los parámetros estándar tal y como se hace con las otras funciones de optimización. Como resumen de este artículo, puedo decir que contiene sólo los conceptos básicos, el primer potencial; y espero que los lectores sean capaces de mejorar la clase según sus propias necesidades. Buena suerte!

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

Archivos adjuntos |
posstat.mq5 (3.43 KB)
Cómo solicitar un EA y obtener el resultado deseado Cómo solicitar un EA y obtener el resultado deseado
¿Cómo escribir correctamente las especificaciones de los requisitos? ¿Qué debería o no debería esperar de un programador de una orden de un EA o de un indicador? ¿Cómo hacer para mantener un diálogo, cual es el momento de prestar especial atención? Este artículo da respuestas a éstas, y muchas otras cuestiones, que a menudo no parecen obvias a mucha gente.
Distintas maneras para averiguar la tendencia en MQL5 Distintas maneras para averiguar la tendencia en MQL5
Cualquier trader daría lo que fuera por la posibilidad de determinar con precisión la tendencia en un momento dado. Es quizá el Santo Grial que busca todo el mundo. En este artículo abordaremos distintas maneras de detección de tendencias. Para ser más preciso -cómo programar distintas métodos clásicas para la detección de tendencias mediante MQL5.
Cómo Pedir un Robot de Comercio en MQL5 y MQL4 Cómo Pedir un Robot de Comercio en MQL5 y MQL4
El servicio "Freelance" es la mayor bolsa para el encargo de robots comerciales e indicadores técnicos. Cientos de desarrolladores profesionales están preparados para escribir una aplicación comercial para el terminal MetaTrader 4/5.
Creación de indicadores multicolor en MQL5 Creación de indicadores multicolor en MQL5
En este artículo, abordaremos la manera de crear indicadores multicolor o convertir los que ya existen en multicolor. MQL5 permite una representación práctica de los datos. Ya no hace falta ver a una docena de gráficos con indicadores y hacer al análisis del nivel RSI o Estocástico, es mejor colorear las velas de otros colores en función de los valores de los indicadores.