Modelo de continuación de movimiento - búsqueda en el gráfico y estadísticas de ejecución

Almat Kaldybay | 18 diciembre, 2018

  1. Introducción
  2. Descripción del modelo - disposiciones generales
  3. Principios de reconocimiento del modelo en el gráfico
  4. Construyendo el algoritmo y escribiendo el código
    1. Parámetros de entrada, la función OnInit() y la declaración inicial de variables
    2. Definiendo los parámetros generales
    3. Actualizando los datos en la matriz
      1. Rellenando las matrices al aparecer una nueva barra
      2. Rellenando las matrices con los datos de la barra 0
      3. Actualizando los datos sobre los fractales
    4. Buscando los extremos
      1. Buscando los extremos para la tendencia descendente
      2. Buscando los extremos para la tendencia ascendente
      3. Conviertiendo los valores de High/Low de las ondas de corrección en variables únicas
    5. Describiendo las condiciones para el reconocimiento del modelo
    6. Creando los controles
      1. Control de formación del punto de entrada en la zona de apertura de posición
      2. Control de retroceso del precio hacia la zona de apertura de posición
      3. Excluyendo la formación de posiciones duplicadas en el marco de un mismo modelo
    7. Describiendo las condiciones para el punto de entrada
    8. Describiendo las condiciones comerciales
    9. Trabajando con las operaciones comerciales
      1. Colocando las posiciones
      2. Definiendo el take-profit
      3. Trasladando las posiciones a ausencia de pérdidas
  5. Recopilando los datos estadísticos
  6. Conclusión


1. Introducción

En este artículo vamos a describir la definición programática de uno de los modelos de continuación del movimiento. La base del trabajo viene constituida por dos ondas: la principal y la de corrección. Como extremos se usarán fractales, además de los llamados fractales potenciales, es decir, los extremos que no se han formado aún como fractales. A continuación, trataremos de reunir los datos estadísticos sobre el movimiento de las ondas. Los datos se descargarán en un archivo de formato CSV.


2. Descripción del modelo - disposiciones generales

El modelo de continuación del movimiento descrito en el artículo consta de dos ondas: la onda principal y la de corrección. En la figura 1 se describe un modelo esquemático. Donde, AB es la onda principal, BC es la onda de corrección y CD es la continuación del movimiento en la dirección de la tendencia principal.


Fig. 1. Esquema del modelo de continuación del movimiento

En el gráfico, esto tendrá el asprcto siguiente:

Modelo de continuación del movimiento en el gráfico H4 de la pareja AUDJPY

Fig. 2. Modelo de continuación del movimiento en el gráfico H4 de la pareja AUDJPY


3.Principios de reconocimiento del modelo en el gráfico

Los principios de reconocimiento del modelo se describen en el recuadro 1.

Recuadro 1. Principios de reconocimiento del modelo de movimiento en cuanto a las tendencias

№ Principios de reconocimiento del modelo para la tendencia descendente №Principios de reconocimiento del modelo para la tendencia ascendente
1Consideraremos una barra-extremo a aquella cuyo High/Low esté por encima/debajo de dos los High/Low de las barras anteriores1Consideraremos una barra-extremo a aquella cuyo High/Low esté por encima/debajo de dos los High/Low de las barras anteriores
2La onda de corrección siempre deberá finalizar con la presencia del extremo superior (punto С - ver figura 1 y figura 2)2La onda de corrección siempre deberá finalizar con la presencia del extremo inferior (punto С - ver figura 1 y figura 2)
 3La duración de la onda de correción no puede ser larga y deberá estar limitada por varias barras 3La duración de la onda de correción no puede ser larga y deberá estar limitada por varias barras
 4El High del movimiento de corrección (punto С - ver figura 1 y figura 2) debe estar por debajo del High del movimiento principal (punto A - ver figura 1 y figura 2) 4El Low del movimiento de corrección (punto С - ver figura 1 y figura 2) debe estar por encima del Low del movimiento principal (punto A - ver figura 1 y figura 2)
 5El principio de oportunidad del punto de entrada significa que hay que abrir la posición solo en un momento determinado de la formación del punto de entrada 5El principio de oportunidad del punto de entrada significa que hay que abrir la posición solo en un momento determinado de la formación del punto de entrada


4. Construyendo el algoritmo y escribiendo el código

1. Parámetros de entrada, la función OnInit() y la declaración inicial de variables

En primer lugar, debemos incluir la clase CTrade para simplificar el acceso a las operaciones comerciales:

//--- incluyendo archivos
#include <Trade\Trade.mqh> 
//--- objeto para realizar operaciones comerciales
CTrade  trade;

A continuación, describimos los parámetros de entrada

//--- parámetros de entrada
input ENUM_TIMEFRAMES base_tf;  //marco temporal del periodo básico
input ENUM_TIMEFRAMES work_tf;  //marco temporal del periodo de trabajo
input double SummRisk=100;      //suma del riesgo por transacción
input double sar_step=0.1;      //set parabolic step
input double maximum_step=0.11; //set parabolic maximum step
input bool TP_mode=true;        //permiso de colocar take-profit
input int M=2;                  //relación entre beneficio y riesgo
input bool Breakeven_mode=true; //permiso de traslado de posición a ausencia de pérdidas
input double breakeven=1;       //relación entre el tamaño del beneficio y el tamaño del stop-loss

En el periodo básico el asesor determinará la dirección de la entrada, en el periodo de trabajo, determinará el punto de entrada.

El programa calculará el tamaño del lote dependiendo de la suma del riesgo en la transacción.

Asimismo, el asesor tendrá la posibilidad de establecer el take-profit según la relación indicada entre beneficio y riesgo (parámetro М), y también de trasladar la posición a ausencia de pérdidas según la relación indicada entre el tamaño del beneficio y el tamaño de stop-loss (parámetro breakeven).

Después de describir los parámetros de entrada, debemos declarar las variables para los manejadores de los indicadores y las matrices para los marcos temporales base_tf y work_tf:

//--- declarando las variables para los manejadores de los indicadores
int Fractal_base_tf,Fractal_work_tf;             //manejador del indicador iFractals
int Sar_base_tf,Sar_work_tf;                     //manejador del indicador iSar
//--- declarando las matrices para base_tf
double High_base_tf[],Low_base_tf[];             //matrices para guardar los precios de las barras High y Low
double Close_base_tf[],Open_base_tf[];           //matrices para guardar los precios de las barras Close y Open
datetime Time_base_tf[];                         //matriz para guardar la hora de apertura de las barras
double Sar_array_base_tf[];                      //matriz para guardar los precios del indicador iSar (Parabolic)
double FractalDown_base_tf[],FractalUp_base_tf[];//matriz para guardar los precios del indicador iFractals
//--- declarando las matrices para work_tf
double High_work_tf[],Low_work_tf[];
double Close_work_tf[],Open_work_tf[];
datetime Time_work_tf[];
double Sar_array_work_tf[];
double FractalDown_work_tf[],FractalUp_work_tf[];;

En el asesor se usarán dos indicadores: los fractales para determinar los extremos y Parabolic para acompañar el trailing-stop. Asimismo, planeamos usar Parabolic para determinar el punto de entrada en el marco temporal de trabajo work_tf.

A continuación, en la función OnInit(), debemos obtener los manejadores de los indicadores y rellenar las matrices con los datos iniciales.

int OnInit()
  {
//--- obteniendo el manejador del indicador iSar
   Sar_base_tf=iSAR(Symbol(),base_tf,sar_step,maximum_step);
   Sar_work_tf=iSAR(Symbol(),work_tf,sar_step,maximum_step);
//--- obteniendo el manejador del indicador iFractals
   Fractal_base_tf=iFractals(Symbol(),base_tf);
   Fractal_work_tf=iFractals(Symbol(),work_tf);
//--- transformando el orden de las matrices como en las series temporales para base_tf
   ArraySetAsSeries(High_base_tf,true);
   ArraySetAsSeries(Low_base_tf,true);
   ArraySetAsSeries(Close_base_tf,true);
   ArraySetAsSeries(Open_base_tf,true);
   ArraySetAsSeries(Time_base_tf,true);;
   ArraySetAsSeries(Sar_array_base_tf,true);
   ArraySetAsSeries(FractalDown_base_tf,true);
   ArraySetAsSeries(FractalUp_base_tf,true);
//--- rellenado inicial de la matrices para base_tf
   CopyHigh(Symbol(),base_tf,0,1000,High_base_tf);
   CopyLow(Symbol(),base_tf,0,1000,Low_base_tf);
   CopyClose(Symbol(),base_tf,0,1000,Close_base_tf);
   CopyOpen(Symbol(),base_tf,0,1000,Open_base_tf);
   CopyTime(Symbol(),base_tf,0,1000,Time_base_tf);
   CopyBuffer(Sar_base_tf,0,TimeCurrent(),1000,Sar_array_base_tf);
   CopyBuffer(Fractal_base_tf,0,TimeCurrent(),1000,FractalUp_base_tf);
   CopyBuffer(Fractal_base_tf,1,TimeCurrent(),1000,FractalDown_base_tf);
//--- transformando las matrices como en las series temporales para work_tf
   ArraySetAsSeries(High_work_tf,true);
   ArraySetAsSeries(Low_work_tf,true);
   ArraySetAsSeries(Close_work_tf,true);
   ArraySetAsSeries(Open_work_tf,true);
   ArraySetAsSeries(Time_work_tf,true);
   ArraySetAsSeries(Sar_array_work_tf,true);
   ArraySetAsSeries(FractalDown_work_tf,true);
   ArraySetAsSeries(FractalUp_work_tf,true);
//--- rellenado inicial de la matrices work_tf
   CopyHigh(Symbol(),work_tf,0,1000,High_work_tf);
   CopyLow(Symbol(),work_tf,0,1000,Low_work_tf);
   CopyClose(Symbol(),work_tf,0,1000,Close_work_tf);
   CopyOpen(Symbol(),work_tf,0,1000,Open_work_tf);
   CopyTime(Symbol(),work_tf,0,1000,Time_work_tf);
   CopyBuffer(Sar_work_tf,0,TimeCurrent(),1000,Sar_array_work_tf);
   CopyBuffer(Fractal_work_tf,0,TimeCurrent(),1000,FractalUp_work_tf);
   CopyBuffer(Fractal_work_tf,1,TimeCurrent(),1000,FractalDown_work_tf);

//---
   return(INIT_SUCCEEDED);
  }

Primero hemos obtenido el manejador de los indicadores; a continuación, el orden de las matrices, como en la serie temporal; y luego hemos rellenado la matriz con datos. Hemos calculado que para trabajar con este asesor, bastarán los datos de 1000 barras.

2. Definiendo los parámetros generales

A partir de esta etapa, pasamos al trabajo con la función OnTick().

En el apartado "Parámetros generales", normalmente registramos la información del mercado, y también declaramos las variables que se usarán para colocar las posiciones.

//+------------------------------------------------------------------+
//| 1. Parámetros generales (comienzo)                               |
//+------------------------------------------------------------------+
//--- información del mercado
//número de dígitos decimales tras la coma en el precio del instrumento
   int Digit=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
//determinando la capacidad de precio del símbolo actual
   double f=1;
   if(Digit==5) {f=100000;}
   if(Digit==4) {f=10000;}
   if(Digit==3) {f=1000;}
   if(Digit==2) {f=100;}
   if(Digit==1) {f=10;}
//---
   double spread=SymbolInfoInteger(Symbol(),SYMBOL_SPREAD)/f;//reduciendo el spread a un valor fraccional, teniendo en cuenta la capacidad del precio
   double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);//información sobre el precio Bid
   double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);//información sobre el precio Ask
   double CostOfPoint=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_VALUE);//información sobre el coste del tick
//--- variables para calcular el lote y definir la posición
   double RiskSize_points;//variable para guardar el tamaño del stop-loss de la posición actual
   double CostOfPoint_position;//variable para guardar el coste del punto de la posición actual, teniendo en cuenta el riesgo por transacción
   double Lot;//variable para guardar el tamaño del lote para la apertura de posición
   double SLPrice_sell,SLPrice_buy;//variables para guardar los niveles de precio de stop-loss
//--- variables para guardar la información sobre el número de barras
   int bars_base_tf=Bars(Symbol(),base_tf);
   int bars_work_tf=Bars(Symbol(),work_tf);
//--- variables para guardar la información sobre la posición
   string P_symbol; //símbolo de la posición
   int P_type,P_ticket,P_opentime;//tipo, ticket y hora de apertura de la posición
//+------------------------------------------------------------------+
//| 1. Parámetros generales (final)                                  |
//+------------------------------------------------------------------+ 

3. Actualizando los datos en la matriz

Las matrices han sido rellenadas en la función OnInit(), pero los datos en las matrices deberán ser siempre actuales. Rellenar las matrices cada vez que llega un tick supone una carga demasiado intensa para el sistema, lo que puede ralentizar significativamente los procesos. Por eso, será más conveniente rellenar de nuevo las matrices cuando aparezca una nueva barra.

Para ello, podemos usar la siguiente construcción:

   static datetime LastBar_base_tf=0;//variable para determinar la aparición de una nueva barra
   datetime ThisBar_base_tf=(datetime)SeriesInfoInteger(_Symbol,base_tf,SERIES_LASTBAR_DATE);//hora de la barra actual
   if(LastBar_base_tf!=ThisBar_base_tf)//si la hora no coincide, significa que ha aparecido una nueva barra
     {
         //aquí rellenamos las matrices
     }

Pero con semejante enfoque se pierden los datos de la barra cero, por eso, para los datos de la barra con el índice 0, hemos introducido matrices aparte.

Asimismo, es recomendable actualizar aparte las matrices con los datos de los fractales. Estas deberán rellenarse de nuevo cada vez que los extremos de la 0-ésima barra sean superiores o inferiores a los anteriores.

A continuación, se muestran ejemplos del rellenado de matrices.

1. Rellenando las matrices al aparecer una nueva barra

Primero rellenamos las matrices cuando aparece una nueva barra:

//+------------------------------------------------------------------+
//| 2.1 Rellenado de matrices al aparecer una nueva barra (comienzo) |
//+------------------------------------------------------------------+
//--- para base_tf
//--- ordenando las matrices como en la serie temporal
   ArraySetAsSeries(High_base_tf,true);
   ArraySetAsSeries(Low_base_tf,true);
   ArraySetAsSeries(Close_base_tf,true);
   ArraySetAsSeries(Open_base_tf,true);
   ArraySetAsSeries(Time_base_tf,true);
   ArraySetAsSeries(Sar_array_base_tf,true);
   ArraySetAsSeries(FractalDown_base_tf,true);
   ArraySetAsSeries(FractalUp_base_tf,true);
//--- rellenando matrices
   static datetime LastBar_base_tf=0;//variable para determinar la aparición de una nueva barra
   datetime ThisBar_base_tf=(datetime)SeriesInfoInteger(_Symbol,base_tf,SERIES_LASTBAR_DATE);//hora de apertura de la barra actual
   if(LastBar_base_tf!=ThisBar_base_tf)//si la hora no coincide, significa que ha aparecido una nueva barra
     {
      CopyHigh(Symbol(),base_tf,0,1000,High_base_tf);
      CopyLow(Symbol(),base_tf,0,1000,Low_base_tf);
      CopyClose(Symbol(),base_tf,0,1000,Close_base_tf);
      CopyOpen(Symbol(),base_tf,0,1000,Open_base_tf);
      CopyTime(Symbol(),base_tf,0,1000,Time_base_tf);
      CopyBuffer(Sar_base_tf,0,TimeCurrent(),1000,Sar_array_base_tf);
      CopyBuffer(Fractal_base_tf,0,TimeCurrent(),1000,FractalUp_base_tf);
      CopyBuffer(Fractal_base_tf,1,TimeCurrent(),1000,FractalDown_base_tf);
      LastBar_base_tf=ThisBar_base_tf;
     }
//--- para work_tf
//--- ordenando las matrices como en la serie temporal
   ArraySetAsSeries(High_work_tf,true);
   ArraySetAsSeries(Low_work_tf,true);
   ArraySetAsSeries(Close_work_tf,true);
   ArraySetAsSeries(Open_work_tf,true);
   ArraySetAsSeries(Time_work_tf,true);
   ArraySetAsSeries(Sar_array_work_tf,true);
   ArraySetAsSeries(FractalDown_work_tf,true);
   ArraySetAsSeries(FractalUp_work_tf,true);
//--- rellenando matrices
   static datetime LastBar_work_tf=0;//variable para determinar la aparición de una nueva barra
   datetime ThisBar_work_tf=(datetime)SeriesInfoInteger(_Symbol,work_tf,SERIES_LASTBAR_DATE);//hora de apertura de la barra actual
   if(LastBar_work_tf!=ThisBar_work_tf)//si la hora no coincide, significa que ha aparecido una nueva barra
     {
      CopyHigh(Symbol(),work_tf,0,1000,High_work_tf);
      CopyLow(Symbol(),work_tf,0,1000,Low_work_tf);
      CopyClose(Symbol(),work_tf,0,1000,Close_work_tf);
      CopyOpen(Symbol(),work_tf,0,1000,Open_work_tf);
      CopyTime(Symbol(),work_tf,0,1000,Time_work_tf);
      CopyBuffer(Sar_work_tf,0,TimeCurrent(),1000,Sar_array_work_tf);
      CopyBuffer(Fractal_work_tf,0,TimeCurrent(),1000,FractalUp_work_tf);
      CopyBuffer(Fractal_work_tf,1,TimeCurrent(),1000,FractalDown_work_tf);
      LastBar_work_tf=ThisBar_work_tf;
     }
//+------------------------------------------------------------------+
//| 2.1 Rellenado de matrices al aparecer una nueva barra(final)     |
//+------------------------------------------------------------------+

2. Rellenando las matrices con los datos de la barra 0

La información sobre las barras con el índice 1 y superior ahora será actual de forma constante, puesto que los datos sobre la barra con el índice 0 siguen siendo no actuales. Para guardar la información sobre las barras cero, hemos introducido matrices aparte:

//+------------------------------------------------------------------+
//| 2.2 Rellenando las matrices con los datos de la barra 0(inicio)  |
//+------------------------------------------------------------------+
//--- para base_tf
//--- declarando matrices
   double High_base_tf_0[],Low_base_tf_0[];
   double Close_base_tf_0[],Open_base_tf_0[];
   datetime Time_base_tf_0[];
   double Sar_array_base_tf_0[];
//--- ordenando las matrices como en la serie temporal
   ArraySetAsSeries(High_base_tf_0,true);
   ArraySetAsSeries(Low_base_tf_0,true);
   ArraySetAsSeries(Close_base_tf_0,true);
   ArraySetAsSeries(Open_base_tf_0,true);
   ArraySetAsSeries(Time_base_tf_0,true);
   ArraySetAsSeries(Sar_array_base_tf_0,true);
//--- rellenando matrices
   CopyHigh(Symbol(),base_tf,0,1,High_base_tf_0);
   CopyLow(Symbol(),base_tf,0,1,Low_base_tf_0);
   CopyClose(Symbol(),base_tf,0,1,Close_base_tf_0);
   CopyOpen(Symbol(),base_tf,0,1,Open_base_tf_0);
   CopyTime(Symbol(),base_tf,0,1,Time_base_tf_0);
   CopyBuffer(Sar_base_tf,0,TimeCurrent(),1,Sar_array_base_tf_0);
//--- para work_tf
//--- declarando matrices
   double High_work_tf_0[],Low_work_tf_0[];
   double Close_work_tf_0[],Open_work_tf_0[];
   datetime Time_work_tf_0[];
   double Sar_array_work_tf_0[];
//--- ordenando las matrices como en la serie temporal
   ArraySetAsSeries(High_work_tf_0,true);
   ArraySetAsSeries(Low_work_tf_0,true);
   ArraySetAsSeries(Close_work_tf_0,true);
   ArraySetAsSeries(Open_work_tf_0,true);
   ArraySetAsSeries(Time_work_tf_0,true);
   ArraySetAsSeries(Sar_array_work_tf_0,true);
//--- rellenando matrices
   CopyHigh(Symbol(),work_tf,0,1,High_work_tf_0);
   CopyLow(Symbol(),work_tf,0,1,Low_work_tf_0);
   CopyClose(Symbol(),work_tf,0,1,Close_work_tf_0);
   CopyOpen(Symbol(),work_tf,0,1,Open_work_tf_0);
   CopyTime(Symbol(),work_tf,0,1,Time_work_tf_0);
   CopyBuffer(Sar_work_tf,0,TimeCurrent(),1,Sar_array_work_tf_0);
//+------------------------------------------------------------------+
//| 2.2 Rellenando las matrices con los datos de la barra (final)    |
//+------------------------------------------------------------------+

3. Actualizando los datos sobre los fractales

También debemos actualizar las matrices con los datos de los fractales. Cada vez que los extremos de la 0-ésima barra sean superiores o inferiores a los anteriores, las matrices deberán ser rellanadas de nuevo:

//+------------------------------------------------------------------+
//| 2.3 Actualizando los datos sobre los fractales (inicio)          |
//+------------------------------------------------------------------+
//--- para base_tf
   if(High_base_tf_0[0]>High_base_tf[1] && High_base_tf_0[0]>High_base_tf[2])
     {
      CopyBuffer(Fractal_base_tf,0,TimeCurrent(),1000,FractalUp_base_tf);
     }
   if(Low_base_tf_0[0]<Low_base_tf[1] && Low_base_tf_0[0]<Low_base_tf[2])
     {
      CopyBuffer(Fractal_base_tf,1,TimeCurrent(),1000,FractalDown_base_tf);
     }
//--- para work_tf
   if(High_work_tf_0[0]>High_work_tf[1] && High_work_tf_0[0]>High_work_tf[2])
     {
      CopyBuffer(Fractal_work_tf,0,TimeCurrent(),1000,FractalUp_work_tf);
     }
   if(Low_work_tf_0[0]<Low_work_tf[1] && Low_work_tf_0[0]<Low_work_tf[2])
     {
      CopyBuffer(Fractal_work_tf,1,TimeCurrent(),1000,FractalDown_work_tf);
     }
//+------------------------------------------------------------------+
//| 2.3 Actualizando los datos sobre los fractales (final)           |
//+------------------------------------------------------------------+

4. Buscando los extremos

Vamos a volver al modelo de continuación del movimiento. Para ello, tenemos que echar de nuevo un vistazo a la figura 2.

El segmento АВ es la base de la onda, y el segmento ВС es la corrección de la onda. De acuerdo con los principios del modelo, la onda de corrección siempre deberá terminar con un extremo, en su esencia, un fractal. En la figura, se trata del punto С. Comenzaremos la búsqueda de extremos desde este punto. A conituación, debemos encontrar los restantes consecutivamente. Pero en el momento del punto de entrada, el fractal formado (confirmado), seguramente estará ausente. Por eso, debemos buscar la situación en la que el extremo de la barra está por encima/debajo de las dos barras anteriores: el high/low de esta barra será preciamente el punto С. Asimismo, hay que tener en cuenta que en el momento del punto de entrada, el high/low del movimiento de corrección (punto С) puede estar tanto en la barra cero, como en una barra con un índice por encima de cero.

En el recuadro 2 se muestra la secuencia para determinar los extremos.

Recuadro 2. Secuencia para determinar los extremos

№ p/pPara la tendencia descendentePara la tendencia ascendente
1Encontrar el high del movimiento de corrección (punto С)Encontrar el low del movimiento de corrección (punto С)
2A partir del high del movimiento de corrección, encontrar el siguiente extremo superior (punto А)A partir del low del movimiento de corrección, encontrar el siguiente extremo inferior (punto А)
3Entre los puntos С y А, encontrar el punto В - el low del movimiento de correcciónEntre los puntos С y А, encontrar el punto В - el high del movimiento de corrección
1. Buscando los extremos para la tendencia descendente
//+------------------------------------------------------------------+
//| 3.1 Buscando los extremos para la tendencia descendente (inicio) |
//+------------------------------------------------------------------+
//--- declarando las variables
   int High_Corr_wave_downtrend_base_tf;//para la barra high del movimiento de corrección (punto С)
   int UpperFractal_downtrend_base_tf;  //para la barra del siguiente extremo superior (punto А)
   int Low_Corr_wave_downtrend_base_tf; //para la barra low del movimiento de corrección (punto B)
//--- 
//--- Encontrar el high del movimiento de corrección (punto С)
   if(High_base_tf_0[0]>High_base_tf[1] && High_base_tf_0[0]>High_base_tf[2])
     {
      High_Corr_wave_downtrend_base_tf=0;
     }
   else
     {
      for(n=0; n<(bars_base_tf);n++)
        {
         if(High_base_tf[n]>High_base_tf[n+1] && High_base_tf[n]>High_base_tf[n+2])
            break;
        }
      High_Corr_wave_downtrend_base_tf=n;
     }
//--- 
//--- A partir del high del movimiento de corrección, encontrar el siguiente extremo superior (punto А)
   for(n=High_Corr_wave_downtrend_base_tf+1; n<(bars_base_tf);n++)
     {
      // --- if a non-empty value, terminate the loop
      if(FractalUp_base_tf[n]!=EMPTY_VALUE)
         break;
     }
   UpperFractal_downtrend_base_tf=n;
//---
//--- Entre los puntos С y А, encontrar el punto В - el low del movimiento de corrección
   int CountToFind_arrmin=UpperFractal_downtrend_base_tf-High_Corr_wave_downtrend_base_tf;
   Low_Corr_wave_downtrend_base_tf=ArrayMinimum(Low_base_tf,High_Corr_wave_downtrend_base_tf,CountToFind_arrmin);
//+------------------------------------------------------------------+
//| 3.1 Buscando los extremos para la tendencia descendente (final)  |
//+------------------------------------------------------------------+

2. Buscando los extremos para la tendencia ascendente

//+------------------------------------------------------------------+
//| 3.2 Buscando los extremos para la tendencia ascendente (inicio)  |
//+------------------------------------------------------------------+
//--- declarando las variables
   int Low_Corr_wave_uptrend_base_tf;//para la barra Low del movimiento de corrección (punto С)
   int LowerFractal_uptrend_base_tf;  //para la barra del siguiente extremo inferior (punto А)
   int High_Corr_wave_uptrend_base_tf; //para la barra High del movimiento de corrección (punto B)
//--- 
//--- Encontrar el Low del movimiento de corrección (punto С)
   if(Low_base_tf_0[0]<Low_base_tf[1] && Low_base_tf_0[0]<Low_base_tf[2])
     {
      Low_Corr_wave_uptrend_base_tf=0;
     }
   else
     {
      //buscando el low del retroceso
      for(n=0; n<(bars_base_tf);n++)
        {
         if(Low_base_tf[n]<Low_base_tf[n+1] && Low_base_tf[n]<Low_base_tf[n+2])
            break;
        }
      Low_Corr_wave_uptrend_base_tf=n;
     }
//---
//--- A partir del low del movimiento de corrección, encontrar el siguiente extremo inferior (punto А)
   for(n=Low_Corr_wave_uptrend_base_tf+1; n<(bars_base_tf);n++)
     {
      if(FractalDown_base_tf[n]!=EMPTY_VALUE)
         break;
     }
   LowerFractal_uptrend_base_tf=n;
//---
//--- Entre los puntos С y А, encontrar el punto В - el high del movimiento de corrección
int CountToFind_arrmax=LowerFractal_uptrend_base_tf-Low_Corr_wave_uptrend_base_tf;
High_Corr_wave_uptrend_base_tf=ArrayMaximum(High_base_tf,Low_Corr_wave_uptrend_base_tf,CountToFind_arrmax);
//+------------------------------------------------------------------+
//| 3.2 Buscando los extremos para la tendencia ascendente (final)   |
//+------------------------------------------------------------------+

3. Conviertiendo los valores de High/Low de las ondas de corrección en variables únicas

De esta forma, los índices de las barras (extremos) han sido localizados. Pero abrá que recurrir, no solo a los índices, sino también a los valores de precio y los valores temporales de estas barras. Para acceder a los valores de high o low de las ondas de corrección, deberemos usar dos matrices diferentes, puesto que el high o el low de la onda de corrección puede encontrarse tanto en el índice cero, como en una barra con un índice por encima de cero. Esto no resulta muy cómodo para trabajar, por eso, será más correcto convertir sus valores en variables comunes con la ayuda del operador if.

//+----------------------------------------------------------------------------------+
//| 3.3 Convertir los High/Low de las ondas correctivas en variables únicas (inicio) |
//+----------------------------------------------------------------------------------+
//--- declarando las variables
   double High_Corr_wave_downtrend_base_tf_double,Low_Corr_wave_uptrend_base_tf_double;
   datetime High_Corr_wave_downtrend_base_tf_time,Low_Corr_wave_uptrend_base_tf_time;
//--- para High_Corr_wave_downtrend_base_tf
   if(High_Corr_wave_downtrend_base_tf==0)
     {
      High_Corr_wave_downtrend_base_tf_double=High_base_tf_0[High_Corr_wave_downtrend_base_tf];
      High_Corr_wave_downtrend_base_tf_time=Time_base_tf_0[High_Corr_wave_downtrend_base_tf];
     }
   else
     {
      High_Corr_wave_downtrend_base_tf_double=High_base_tf[High_Corr_wave_downtrend_base_tf];
      High_Corr_wave_downtrend_base_tf_time=Time_base_tf[High_Corr_wave_downtrend_base_tf];
     }
//-- para Low_Corr_wave_uptrend_base_tf
   if(Low_Corr_wave_uptrend_base_tf==0)
     {
      Low_Corr_wave_uptrend_base_tf_double=Low_base_tf_0[Low_Corr_wave_uptrend_base_tf];
      Low_Corr_wave_uptrend_base_tf_time=Time_base_tf_0[Low_Corr_wave_uptrend_base_tf];
     }
   else
     {
      Low_Corr_wave_uptrend_base_tf_double=Low_base_tf[Low_Corr_wave_uptrend_base_tf];
      Low_Corr_wave_uptrend_base_tf_time=Time_base_tf[Low_Corr_wave_uptrend_base_tf];
     }
//+---------------------------------------------------------------------------------+
//| 3.3 Convertir los High/Low de las ondas correctivas en variables únicas (final) |
//+---------------------------------------------------------------------------------+

De esta forma, los valores de precio y temporales de los high/low de las ondas de corrección se registran en las variables, por lo que no habrá necesidad de acceder a diferentes matrices cada vez.

Si resumimos el trabajo de búsqueda de los extremos, resulta que hemos hallado los puntos А, В y С según el concepto de reconocimiento del modelo (ver recuadros 4 y 5).

Recuadro 4. Valores de los puntos А, В y С para la tendencia descendente

ÍndiceValor del punto AValor del punto BValor del punto C
Índice de la barraUpperFractal_downtrend_base_tfLow_Corr_wave_downtrend_base_tfHigh_Corr_wave_downtrend_base_tf
Valor temporalTime_base_tf[UpperFractal_downtrend_base_tf]Time_base_tf[Low_Corr_wave_downtrend_base_tf]High_Corr_wave_downtrend_base_tf_time
Valor del precioHigh_base_tf[UpperFractal_downtrend_base_tf]Low_base_tf[Low_Corr_wave_downtrend_base_tf]High_Corr_wave_downtrend_base_tf_double

Recuadro 5. Valores de los puntos А, В y С para la tendencia ascendente

ÍndiceValor del punto AValor del punto BValor del punto C
Índice de la barraLowerFractal_uptrend_base_tfHigh_Corr_wave_uptrend_base_tfLow_Corr_wave_uptrend_base_tf
Valor temporalTime_base_tf[LowerFractal_uptrend_base_tf]Time_base_tf[High_Corr_wave_uptrend_base_tf]Low_Corr_wave_uptrend_base_tf_time
Valor del precioLow_base_tf[LowerFractal_uptrend_base_tf]High_base_tf[High_Corr_wave_uptrend_base_tf]Low_Corr_wave_uptrend_base_tf_double


5. Describiendo las condiciones para el reconocimiento del modelo

En este apartado, describiremos solo las condiciones más necesarias (a nuestro entender) que caracterizan el modelo descrito en el presente artículo.

Recuadro 6. Conjunto mínimo de condiciones para reconocer el modelo de continuación del movimiento

№ p/pCondiciones para la tendencia descendenteCondiciones para la tendencia ascendente
1El high de la onda de corrección (punto C) está por debajo del high del extremo que le sigue (punto А)El low de la onda de corrección (punto C) está por encima del low del extremo que le sigue (punto А)
2El índice low de la onda de corrección (punto В) es mayor que el índice high de la onda de corrección (punto С)El índice high de la onda de corrección (punto В) es mayor que el índice low de la onda de corrección (punto С)
3Duración del movimiento de corrección de 2 hasta 6 barras (número de barras a partir del punto В)Duración del movimiento de corrección de 2 hasta 6 barras (número de barras a partir del punto В)

A continuación, mostramos el código de la descripción de las condiciones para reconocer el modelo. Las condiciones se recopilan en dos variables lógicas: una para la tendencia descendente y otra para la ascendente:

//+------------------------------------------------------------------+
//| 4. Describiendo condiciones de reconocimiento del modelo (inicio)|           
//+------------------------------------------------------------------+
//--- para la tendencia descendente
/*1. El high de la onda de corrección (punto C) está por debajo del high del extremo que le sigue (punto А)*/
/*2. El índice low de la onda de corrección (punto В) es mayor que el índice high de la onda de corrección (punto С)*/
/*3. Duración del movimiento de corrección de 2 hasta 6 barras (número de barras a partir del punto В)*/
   bool Model_downtrend_base_tf=(
                                 /*1.*/High_Corr_wave_downtrend_base_tf_double<High_base_tf[UpperFractal_downtrend_base_tf] && 
                                 /*2.*/Low_Corr_wave_downtrend_base_tf>High_Corr_wave_downtrend_base_tf && 
                                 /*3.*/Low_Corr_wave_downtrend_base_tf>=1 && Low_Corr_wave_downtrend_base_tf<=6
                                 );
//--- para la tendencia ascendente
/*1. El low de la onda de corrección (punto C) está por encima del low del extremo que le sigue (punto А)*/
/*2. El índice high de la onda de corrección (punto В) es mayor que el índice low de la onda de corrección (punto С)*/
/*3. Duración del movimiento de corrección de 2 hasta 6 barras (número de barras a partir del punto В)*/
   bool Model_uptrend_base_tf=(
                               /*1.*/Low_Corr_wave_uptrend_base_tf_double>Low_base_tf[LowerFractal_uptrend_base_tf] && 
                               /*2.*/High_Corr_wave_uptrend_base_tf>Low_Corr_wave_uptrend_base_tf && 
                               /*3.*/High_Corr_wave_uptrend_base_tf>=1 && High_Corr_wave_uptrend_base_tf<=6
                               );
//+------------------------------------------------------------------+
//| 4. Describiendo condiciones de reconocimiento del modelo (final) |        
//+------------------------------------------------------------------+

6. Creando los controles

El asesor, como mínimo, debe realizar tres comprobaciones.

Las dos primeras se relacionan con la entrada oportuna. La tercera comprobación se asegura de que en el marco del modelo haya abierta solo una posición, es decir, se excluye la creación de posiciones duplicadas.

Echemos un vistazo a la figura 3. En ella, se marcan con líneas punteadas las zonas de apertura de las posiciones donde puede ubicarse el punto de entrada, en algún lugar entre los puntos В y С. No es deseable entrar más tarde, cuando el precio rompa el nivel del punto B, ya que con ello aumentan los riesgos. Esta es la primera comprobación que debe realizar el programa.

Modelo de continuación del movimiento

Fig. 3. Modelo de continuación del movimiento en el gráfico H4 de la pareja AUDJPY

Asimismo, puede haber situaciones cuando el precio ha roto el nivel del punto B, y después ha vuelto de nuevo a la zona de apertura de posición. Esa situación no se puede considerar comercio. Esta es la segunda comprobación que debe realizar el programa. Y finalmente, para que no haya multitud de posiciones creadas, debemos poner limitaciones: 1 modelo - una posición abierta. Esta es la tercera comprobación que debe realizar el programa.

1. Control de formación del punto de entrada en la zona de apertura de posición

Aquí todo es muy simple: para el modelo de venta, el precio bid deberá ser superior al low del movimiento de corrección (punto В). Para el modelo de compra, el precio bid deberá ser inferior al high del movimiento de corrección (punto В).

//+------------------------------------------------------------------------+
//| 5.1 Control de formación del punto de entrada en la zona de apertura de posición (inicio) 
//+------------------------------------------------------------------------+
//--- para la tendencia descendente
bool First_downtrend_control_bool=(bid>=Low_base_tf[Low_Corr_wave_downtrend_base_tf]);
//--- para la tendencia ascendente
bool First_uptrend_control_bool=(bid<=High_base_tf[High_Corr_wave_uptrend_base_tf]);
//+------------------------------------------------------------------------+
//| 5.1 Control de formación del punto de entrada en la zona de apertura de posición (final)  
//+------------------------------------------------------------------------+

2. Control de retroceso del precio hacia la zona de apertura de posición

Para implementar este control, debemos determinar la barra con el menor valor low (para las ventas) o la barra con el mayor valor high (para las compras), comenzando desde el índice actual y hasta la barra high/low del movimiento de corrección (punto В). Para ello, se usa la función ArrayMinimum() para el modelo de venta, y ArrayMaximum() para el modelo de compra.

A continuacuón, se comparan los índices: el índice low/high del movimiento de corrección (punto В) y los índices obtenidos por las funciones ArrayMinimum() y ArrayMaximum(). Si estos coinciden, significa que no ha habido ruptura del low/high del movimiento de corrección, y podemos analizar la situación como comercial. Si los índices no coinciden, significa que el movimiento ha comenzado antes, y ya es tarde para abrir una posición.

//+------------------------------------------------------------------+
//| 5.2 Control de retroceso hacia la apertura de posición (inicio)  |     
//+------------------------------------------------------------------+
//--- para la tendencia descendente
//encontramos la barra con el precio más bajo entre la barra 0 y el low del movimiento de corrección
   int Second_downtrend_control_int=ArrayMinimum(Low_base_tf,0,Low_Corr_wave_downtrend_base_tf+1);
//si el low de la barra actual está por debajo del low del movimiento de corrección
   if(Low_base_tf_0[0]<Low_base_tf[Second_downtrend_control_int])
     {
      Second_downtrend_control_int=0; //significa que el mínimo se encuentra en la barra 0
     }
//si la barra con el precio más bajo y el low del movimiento de corrección coinciden, entonces se trata de la misma barra
//significa que el precio no ha salido de la zona de apertura de posición
   bool Second_downtrend_control_bool=(Second_downtrend_control_int==Low_Corr_wave_downtrend_base_tf);
//---
//--- para la tendencia ascendente
//encontramos la barra con el precio más alto entre la barra 0 y el high del movimiento de corrección
   int Second_uptrend_control_int=ArrayMaximum(High_base_tf,0,High_Corr_wave_uptrend_base_tf+1);
   //si el high de la barra actual está por encima del high del movimiento de corrección
   if(High_base_tf_0[0]>High_base_tf[Second_uptrend_control_int])
     {
      Second_uptrend_control_int=0;//significa que el máximo está en la barra 0
     }
//si la barra con el precio más alto y el high del movimiento de corrección coinciden, entonces se trata de la misma barra
//significa que el precio no ha salido de la zona de apertura de posición
   bool Second_uptrend_control_bool=(Second_uptrend_control_int==High_Corr_wave_uptrend_base_tf);
//+------------------------------------------------------------------+
//| 5.2 Control de retroceso hacia la apertura de posición (final)   |          
//+------------------------------------------------------------------+

3. Excluyendo la formación de posiciones duplicadas en el marco de un mismo modelo

Este control se usa para limitar el número de posiciones abiertas. Esencia del control: un modelo, una posición abierta. El principio de funcionamiento es el siguiente: se realiza la iteración de las posiciones abiertas, a continuación, si hay una posición abierta del gráfico actual, se determina a partir del punto de entrada la barra más cercana al mismo - que es el high/low del movimiento de corrección (punto С a partir del punto de entrada), dependiendo del tipo de transacción.

Después, se determina la hora de la barra localizada — el high/low del movimiento de corrección (punto С a partir del punto de entrada), y se compara con la hora del high/low actual del movimiento de corrección (punto С actual). Si coinciden, la apertura de posición no deberá tener lugar, puesto que ya existe una posición según este modelo.

Creando un modelo de control para las ventas:

//+---------------------------------------------------------------------------+
//| 5.3.1 Para las ventas (inicio)                                            |
//+---------------------------------------------------------------------------+
//--- declarando las variables
   int Bar_sell_base_tf,High_Corr_wave_downtrend_base_tf_sell;
   bool Third_downtrend_control_bool=false;
//--- iteración de posiciones abiertas
   if(PositionsTotal()>0)
     {
      for(i=0;i<=PositionsTotal();i++)
        {
         if(PositionGetTicket(i))
           {
            //--- determinamos el símbolo, el tipo y la hora de la posición
            P_symbol=string(PositionGetString(POSITION_SYMBOL));
            P_type=int(PositionGetInteger(POSITION_TYPE));
            P_opentime=int(PositionGetInteger(POSITION_TIME));
            //--- si el símbolo de la posición coincide con el gráfico actual y el tipo de transacción es "sell"
            if(P_symbol==Symbol() && P_type==1)
              {
               //--- encontramos la barra en la que se ha abierto la posición
               Bar_sell_base_tf=iBarShift(Symbol(),base_tf,P_opentime);
               //--- a partir de la barra en la que se ha abierto la posición, realizamos la búsqueda del high del movimiento de corrección
               //si la posición se ha abierto en la barra actual
               if(Bar_sell_base_tf==0)
                 {
                  //y la barra actual es un extremo
                  if(High_base_tf_0[Bar_sell_base_tf]>High_base_tf[Bar_sell_base_tf+1] && High_base_tf_0[Bar_sell_base_tf]>High_base_tf[Bar_sell_base_tf+2])
                    {
                     High_Corr_wave_downtrend_base_tf_sell=Bar_sell_base_tf;//el high del movimiento de corrección será igual a la barra actual
                    }
                  else
                    {
                     //si la barra actual no es un extremo, iniciamos el ciclo de búsqueda del extremo
                     for(n=Bar_sell_base_tf; n<(bars_base_tf);n++)
                       {
                        if(High_base_tf[n]>High_base_tf[n+1] && High_base_tf[n]>High_base_tf[n+2])//si hemos encontrado el extremo
                           break;//interrumpimos el ciclo
                       }
                     High_Corr_wave_downtrend_base_tf_sell=n;
                    }
                  //--- describimos las condiciones para el control
                  Third_downtrend_control_bool=(
                                                /*1. Hora del high del movimiento de corrección encontrado a partir de la apertura de posición
                                                 coincide con la hora del high actual del movimiento de corrección*/Time_base_tf[High_Corr_wave_downtrend_base_tf_sell]==High_Corr_wave_downtrend_base_tf_time
                                                );
                 }
               //--- si la posición no se ha abierto en la barra actual
               if(Bar_sell_base_tf!=0 && Bar_sell_base_tf!=1000)
                 {
                  //--- iniciamos el ciclo de búsqueda de la barra extremo
                  for(n=Bar_sell_base_tf; n<(bars_base_tf);n++)
                    {
                     //--- si hemos encontrado el extremo
                     if(High_base_tf[n]>High_base_tf[n+1] && High_base_tf[n]>High_base_tf[n+2])
                        break;//interrumpimos el ciclo
                    }
                  High_Corr_wave_downtrend_base_tf_sell=n;
                 }
               Third_downtrend_control_bool=(
                                             /*1. Hora del high del movimiento de corrección encontrado a partir de la apertura de posición
                                                 coincide con la hora del high actual del movimiento de corrección*/Time_base_tf[High_Corr_wave_downtrend_base_tf_sell]==High_Corr_wave_downtrend_base_tf_time
                                             );
              }
           }
        }
     }
//+---------------------------------------------------------------------------+
//| 5.3.1 Para las ventas (final)                                             |
//+---------------------------------------------------------------------------+
Creando control para las compras:
//+---------------------------------------------------------------------------+
//| 5.3.2 Para las compras (inicio)                                           |
//+---------------------------------------------------------------------------+
//--- declarando las variables
   int Bar_buy_base_tf,Low_Corr_wave_uptrend_base_tf_buy;
   bool Third_uptrend_control_bool=false;
//--- iteración de posiciones abiertas
   if(PositionsTotal()>0)
     {
      for(i=0;i<=PositionsTotal();i++)
        {
         if(PositionGetTicket(i))
           {
            //determinamos el símbolo, el tipo y la hora de la posición
            P_symbol=string(PositionGetString(POSITION_SYMBOL));
            P_type=int(PositionGetInteger(POSITION_TYPE));
            P_opentime=int(PositionGetInteger(POSITION_TIME));
            //si el símbolo de la posición coincide con el gráfico actual y el tipo de transacción "buy"
            if(P_symbol==Symbol() && P_type==0)
              {
               //encontramos la barra en la que se ha abierto la posición
               Bar_buy_base_tf=iBarShift(Symbol(),base_tf,P_opentime);
               //a partir de la barra en la que se ha abierto la posición, realizamos la búsqueda del low del movimiento de corrección
               //si la posición se ha abierto en la barra actual
               if(Bar_buy_base_tf==0)
                 {
                 //y la barra actual es un extremo
                  if(Low_base_tf_0[Bar_buy_base_tf]<Low_base_tf[Bar_buy_base_tf+1] && Low_base_tf_0[Bar_buy_base_tf]<Low_base_tf[Bar_buy_base_tf+2])
                    {
                     Low_Corr_wave_uptrend_base_tf_buy=Bar_buy_base_tf;
                    }
                  else
                    {
                    //si la barra actual no es un extremo, iniciamos el ciclo de búsqueda del extremo
                     for(n=Bar_buy_base_tf; n<(bars_base_tf);n++)
                       {
                        if(Low_base_tf[n]<Low_base_tf[n+1] && Low_base_tf[n]<Low_base_tf[n+2])//si se ha encontrado el extremo
                           break;//interrumpimos el ciclo
                       }
                     Low_Corr_wave_uptrend_base_tf_buy=n;
                    }
                  //--- describimos las condiciones para el control  
                  Third_uptrend_control_bool=(
                                               /*1. La hora del low del movimiento de corrección encontrado a partir de la apertura de posición
                                                 coincide con la hora del low actual del movimiento de corrección*/Time_base_tf[Low_Corr_wave_uptrend_base_tf_buy]==Low_Corr_wave_uptrend_base_tf_time
                                               );
                 }
               //--- si la posición no se ha abierto en la barra actual
               if(Bar_buy_base_tf!=0 && Bar_buy_base_tf!=1000)
                 {
                  //--- iniciamos el ciclo de búsqueda de la barra extremo
                  for(n=Bar_buy_base_tf; n<(bars_base_tf);n++)
                    {
                     //--- si hemos encontrado el extremo
                     if(Low_base_tf[n]<Low_base_tf[n+1] && Low_base_tf[n]<Low_base_tf[n+2])
                        break;//interrumpimos el ciclo
                    }
                  Low_Corr_wave_uptrend_base_tf_buy=n;
                 }
                 //--- describimos las condiciones para el control  
               Third_uptrend_control_bool=(
                                            /*1. La hora del low del movimiento de corrección encontrado a partir de la apertura de posición
                                                 coincide con la hora del low actual del movimiento de corrección*/Time_base_tf[Low_Corr_wave_uptrend_base_tf_buy]==Low_Corr_wave_uptrend_base_tf_time
                                            );
              }
           }
        }
     }
//+---------------------------------------------------------------------------+
//| 5.3.2 Para las compras (final)                                            |
//+---------------------------------------------------------------------------+

7. Describiendo las condiciones para el punto de entrada

El punto de entrada deberá determinarse en el periodo de trabajo — work_tf. Esto resulta necesario para entrar en el mercado en el momento adecuado y, dentro de lo posible, reducir el riesgo en puntos. Como señal, se usan las lecturas del indicador Parabolic: si en la barra actual el valor el indicador está por encima del high de la barra actual, y en la barra anterior el valor del indicador está por debajo del low de esa misma barra, significa que podemos vender. Para la compra, será al contrario.

//+------------------------------------------------------------------+
//| 6. Describiendo las condiciones para el punto de entrada (inicio)|
//+------------------------------------------------------------------+
//--- para las ventas
   bool PointSell_work_tf_bool=(
                                /*1. El low de la barra 1 está por encima de iSar[1]*/Low_work_tf[1]>Sar_array_work_tf[1] && 
                                /*2. El high de la barra 0 está por debajo de iSar[0]*/High_work_tf_0[0]<Sar_array_work_tf_0[0]
                                );
//--- para las compras
   bool PointBuy_work_tf_bool=(
                               /*1. El high de la barra 1 está por debajo de iSar*/High_work_tf[1]<Sar_array_work_tf[1] && 
                               /*2. El low de la barra 0 está por encima de iSar[0]*/Low_work_tf_0[0]>Sar_array_work_tf_0[0]
                               );
//+------------------------------------------------------------------+
//| 6. Describiendo las condiciones para el punto de entrada (final) |
//+------------------------------------------------------------------+

8. Describiendo las condiciones comerciales

En esta etapa vamos a combinar en una variable lógica todas las condiciones y controles que hemos creado anteriormente.

//+------------------------------------------------------------------+
//| 7. Describiendo las condiciones comerciales (inicio)             |
//+------------------------------------------------------------------+
//--- para las ventas
   bool OpenSell=(
                  /*1. se ha formado el modelo*/Model_downtrend_base_tf==true && 
                  /*2. El 1 control permite abrir la posición*/First_downtrend_control_bool==true && 
                  /*3. El 2 control permite abrir la posición*/Second_downtrend_control_bool==true && 
                  /*4. El 3 control permite abrir la posición*/Third_downtrend_control_bool==false && 
                  /*5. Punto de entrada a work_tf*/PointSell_work_tf_bool==true
                  );
//--- para las compras
   bool OpenBuy=(
                 /*1. se ha formado el modelo*/Model_uptrend_base_tf==true && 
                 /*2. El 1 control permite abrir la posición*/First_uptrend_control_bool==true && 
                 /*3. El 2 control permite abrir la posición*/Second_uptrend_control_bool==true && 
                 /*4. El 3 control permite abrir la posición*/Third_uptrend_control_bool==false && 
                 /*5. Punto de entrada a work_tf*/PointBuy_work_tf_bool==true
                 );
//+------------------------------------------------------------------+
//| 7. Describiendo las condiciones comerciales (final)              |
//+------------------------------------------------------------------+

9. Trabajando con las operaciones comerciales

El trabajo con las condiciones comerciales se divide en:

1. Colocando las posiciones

//+------------------------------------------------------------------+
//| 8. Trabajando con las operaciones comerciales (inicio)           |
//+------------------------------------------------------------------+
//--- definiendo los niveles de stop-loss
   SLPrice_sell=High_Corr_wave_downtrend_base_tf_double+spread;
   SLPrice_buy=Low_Corr_wave_uptrend_base_tf_double-spread;
//+------------------------------------------------------------------+
//| 8.1 Definiendo las posiciones (inicio)                           |
//+------------------------------------------------------------------+
//--- de venta
   if(OpenSell==true)
     {
      RiskSize_points=(SLPrice_sell-bid)*f;//determinamos el tamaño del s/l en puntos y lo convertimos en valor entero
      if(RiskSize_points==0)//control de división por 0
        {
         RiskSize_points=1;
        }
      CostOfPoint_position=SummRisk/RiskSize_points;//definimos el precio de la posición en puntos, teniendo en cuenta el tamaño del s/l
      Lot=CostOfPoint_position/CostOfPoint;//calculamos el lote para abrir la posición
      //abrimos la posición
      trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,NormalizeDouble(Lot,2),bid,NormalizeDouble(SLPrice_sell,5),0,"");
     }
//--- de venta
   if(OpenBuy==true)
     {
      RiskSize_points=(bid-SLPrice_buy)*f;//definimos el tamaño del s/l en puntos y lo convertimos en valor entero
      if(RiskSize_points==0)//control de división por 0
        {
         RiskSize_points=1;
        }
      CostOfPoint_position=SummRisk/RiskSize_points;//definimos el precio de la posición en puntos, teniendo en cuenta el tamaño del s/l
      Lot=CostOfPoint_position/CostOfPoint;//calculamos el lote para abrir la posición
      //abrimos la posición
      trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,NormalizeDouble(Lot,2),ask,NormalizeDouble(SLPrice_buy,5),0,"");
     }
//+------------------------------------------------------------------+
//| 8.1 Definiendo las posiciones (final)                            |
//+------------------------------------------------------------------+

2. Definiendo el take-profit

//+------------------------------------------------------------------+
//| 8.2 Definiendo el take-profit (inicio)                           |
//+------------------------------------------------------------------+
   if(TP_mode==true)
     {
      if(PositionsTotal()>0)
        {
         for(i=0;i<=PositionsTotal();i++)
           {
            if(PositionGetTicket(i))
              {
              //obtenemos el valor de la posición
               SL_double=double (PositionGetDouble(POSITION_SL));
               OP_double=double (PositionGetDouble(POSITION_PRICE_OPEN));
               TP_double=double (PositionGetDouble(POSITION_TP));
               P_symbol=string(PositionGetString(POSITION_SYMBOL));
               P_type=int(PositionGetInteger(POSITION_TYPE));
               P_profit=double (PositionGetDouble(POSITION_PROFIT));
               P_ticket=int (PositionGetInteger(POSITION_TICKET));
               P_opentime=int(PositionGetInteger(POSITION_TIME));
               if(P_symbol==Symbol())
                 {
                  if(P_type==0 && TP_double==0)
                    {
                     double SL_size_buy=OP_double-SL_double;//definimos el tamaño del s/l en puntos
                     double TP_size_buy=SL_size_buy*M;//multiplicamos el tamaño del s/l por la relación establecida en los parámetros de entrada
                     double TP_price_buy=OP_double+TP_size_buy;//definimos el nivel de t/p
                     //modificamos la posición
                     trade.PositionModify(PositionGetInteger(POSITION_TICKET),SL_double,NormalizeDouble(TP_price_buy,5));
                    }
                  if(P_type==1 && TP_double==0)
                    {
                     double SL_size_sell=SL_double-OP_double;//definimos el tamaño del s/l en puntos
                     double TP_size_sell=SL_size_sell*M;//multiplicamos el tamaño del s/l por la relación establecida en los parámetros de entrada
                     double TP_price_sell=OP_double-TP_size_sell;//definimos el nivel de t/p
                     //modificamos la posición
                     trade.PositionModify(PositionGetInteger(POSITION_TICKET),SL_double,NormalizeDouble(TP_price_sell,5));
                    }
                 }
              }
           }
        }
     }
//+------------------------------------------------------------------+
//| 8.2 Definiendo el take-profit (final)                            |
//+------------------------------------------------------------------+

3. Trasladando las posiciones a ausencia de pérdidas

//+------------------------------------------------------------------+
//| 8.3 Trasladar la posición a ausencia de pérdidas (inicio)        |
//+------------------------------------------------------------------+
   double Size_Summ=breakeven*SummRisk;//definimos el nivel de beneficio, tras alcanzar el cual deberemos trasladar la posición a ausencia de pérdidas
   if(Breakeven_mode==true && breakeven!=0)
     {
      if(PositionsTotal()>0)
        {
         for(i=0;i<=PositionsTotal();i++)
           {
            if(PositionGetTicket(i))
              {
              //obtenemos el valor de la posición
               SL_double=double (PositionGetDouble(POSITION_SL));
               OP_double=double (PositionGetDouble(POSITION_PRICE_OPEN));
               TP_double=double (PositionGetDouble(POSITION_TP));
               P_symbol=string(PositionGetString(POSITION_SYMBOL));
               P_type=int(PositionGetInteger(POSITION_TYPE));
               P_profit=double (PositionGetDouble(POSITION_PROFIT));
               P_ticket=int (PositionGetInteger(POSITION_TICKET));
               P_opentime=int(PositionGetInteger(POSITION_TIME));
               if(P_symbol==Symbol())
                 {
                  if(P_type==0 && P_profit>=Size_Summ && SL_double<OP_double)
                    {
                     trade.PositionModify(PositionGetInteger(POSITION_TICKET),OP_double,TP_double);
                    }
                  if(P_type==1 && P_profit>=Size_Summ && SL_double>OP_double)
                    {
                     trade.PositionModify(PositionGetInteger(POSITION_TICKET),OP_double,TP_double);
                    }
                 }
              }
           }
        }
     }
//+------------------------------------------------------------------+
//| 8.3 Trasladar la posición a ausencia de pérdidas (final)         |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 8. Trabajando con las operaciones comerciales (final)            |
//+------------------------------------------------------------------+

5. Recopilando los datos estadísticos

Primero, debemos determinar el conjunto de indicadores para las estadísticas:

  1. Símbolo del instrumento;
  2. Tipo de la transacción;
  3. Hora de entrada;
  4. Precio de apertura;
  5. Nivel de stop-loss;
  6. Tamaño del stop-loss;
  7. Nivel de beneficio máximo;
  8. Tamaño del beneficio máximo;
  9. Duración de la transacción.

Debemos suponer que el punto de beneficio máximo es el high/low del primer fractal superior/inferior en el periodo básico, formado después de la barra en la que ha tenido lugar la apertura de posición.

Para comenzar, habrá que poner a prueba el funcionamiento del asesor en el simulador de estrategias. Para la simulación, hemos elegido la pareja AUDJPY en el periodo 01.01.2018—29.08.2018. Como periodo básico, hemos elegido D1; como periodo de trabajo, H6. Riesgo por transacción — $100. Traslado de la posición a ausencia de pérdidas 1/2, definición de take-profit — 1/3 (riesgo/beneficio).


Fig. 4. Parámetros de entrada del asesor

Después de la simulación, guardamos el informe en un archivo de formato CSV. En la carpeta local del terminal, creamos un nuevo archivo report.csv. Copiamos los datos del informe en el mismo. Pero no tenemos que copiarlo todo, sino solo una parte de la sección Órdenes. En este caso, además, tenemos que eliminar las líneas relacionadas con el cierre de posiciones, como se muestra en la figura 5:


Fig 5. Eliminando del informe las líneas relacionadas con el cierre de posiciones

Debemos copiar las columnas:

  1. Hora de apertura;
  2. Símbolo
  3. Tipo;
  4. Precio;
  5. S/L.

Como resultado, el archivo report.csv tendrá el aspecto siguiente:

Contenido del archivo report.csv

Fig. 6. Contenido del archivo report.csv

Ahora debemos crear un script que calculará los datos del archivo report.csv y creará un nuevo archivo file_stat.csv con información estadística adicional:

  1. Tamaño del SL;
  2. Nivel de beneficio máximo;
  3. Tamaño del beneficio máximo;
  4. Duración de la transacción en barras.

Para esta tarea, hemos usado la solución preparada del artículo Principios de programación en MQL5: Archivos en el apartado Leyendo un archivo de texto en una matriz. Hemos añadido las matrices y su rellenado para guardar los valores de las columnas en el archivo file_stat.csv.

Creamos un nuevo script bajo la función OnStart() y anotamos el código de lectura de archivos en la matriz:

//+------------------------------------------------------------------+
//| Función de lectura en la matriz (inicio)                         |
//+------------------------------------------------------------------+
bool ReadFileToArrayCSV(string FileName,SLine  &Lines[])
  {
   ResetLastError();
   int h=FileOpen(FileName,FILE_READ|FILE_ANSI|FILE_CSV,";");
   if(h==INVALID_HANDLE)
     {
      int ErrNum=GetLastError();
      printf("Error de apertura del archivo %s # %i",FileName,ErrNum);
      return(false);
     }
   int lcnt=0; // variable para calcular líneas 
   int fcnt=0; // variable para calcular campos de línea    
   while(!FileIsEnding(h))
     {
      string str=FileReadString(h);
      // nueva línea (nuevo elemento de la matriz de estructuras)
      if(lcnt>=ArraySize(Lines))
        { // la matriz de estructuras está completamente rellena
         ArrayResize(Lines,ArraySize(Lines)+1024); // aumentamos el tamaño de la matriz en 1024 elementos
        }
      ArrayResize(Lines[lcnt].field,64);// cambiamos el tamaño de la matriz en la estructura
      Lines[lcnt].field[0]=str; // asignamos el valor del primer campo
                                // comenzamos a leer el resto de campos en la línea
      fcnt=1; // por ahora está ocupado un elemento en la matriz de campos
      while(!FileIsLineEnding(h))
        { // leemos los demás campos en la línea
         str=FileReadString(h);
         if(fcnt>=ArraySize(Lines[lcnt].field))
           { // la matriz de campos está completamente rellena
            ArrayResize(Lines[lcnt].field,ArraySize(Lines[lcnt].field)+64); // aumentamos el tamaño de la matriz en 64 elementos
           }
         Lines[lcnt].field[fcnt]=str; // asignamos el valor del próximo campo
         fcnt++; // aumentamos el contador de campos
        }
      ArrayResize(Lines[lcnt].field,fcnt); // cambiamos el tamaño de la matriz de campos de acuerdo con el número real de campos
      lcnt++; // aumentamos el contador de líneas
     }
   ArrayResize(Lines,lcnt); // cambiamos la matriz de estructuras (líneas) de acuerdo con el número real de líneas
   FileClose(h);
   return(true);
  }
//+------------------------------------------------------------------+
//| función de lectura en la matriz (final)                          |
//+------------------------------------------------------------------+

A continuación, indicamos los parámetros de entrada:

#property script_show_inputs 
//--- parámetros de entrada
input ENUM_TIMEFRAMES base_tf;  //marco temporal del periodo básico
input double sar_step=0.1;      //set parabolic step
input double maximum_step=0.11; //set parabolic maximum step
//--- declarando las variables para los manejadores de los indicadores
int Fractal_base_tf;             //manejador del indicador iFractal
//--- declarando las matrices para base_tf
double High_base_tf[],Low_base_tf[];             //matrices para guardar los precios de las barras High y Low
double FractalDown_base_tf[],FractalUp_base_tf[];//matriz para guardar los precios del indicador iFractall
//--- estructura de la matriz
struct SLine
  {
   string            field[];
  };

Dentro de la función OnStart(), obtenemos el manejador del indicador iFractals, y también declaramos y rellenamos las matrices de los precios High/Low. También hay que introducir la variable bars_base_tf, que deberá usarse en el ciclo for, y asimismo introducir la variable f, que guardará el número de dígitos del precio dependiendo del número de decimales tras la coma en el precio del instrumento. Esta variable se usará para convertir las lecturas del tamaño de stop-loss y el tamaño del beneficio máximo en un valor entero.

//--- obtenemos los manejadores del indicador iFractal
   Fractal_base_tf=iFractals(Symbol(),base_tf);
//--- transformando el orden de las matrices como en las series temporales para base_tf
   ArraySetAsSeries(High_base_tf,true);
   ArraySetAsSeries(Low_base_tf,true);
   ArraySetAsSeries(FractalDown_base_tf,true);
   ArraySetAsSeries(FractalUp_base_tf,true);
//--- rellenado inicial de la matrices para base_tf
   CopyHigh(Symbol(),base_tf,0,1000,High_base_tf);
   CopyLow(Symbol(),base_tf,0,1000,Low_base_tf);
   CopyBuffer(Fractal_base_tf,0,TimeCurrent(),1000,FractalUp_base_tf);
   CopyBuffer(Fractal_base_tf,1,TimeCurrent(),1000,FractalDown_base_tf);
//--- variables para guardar la información sobre el número de barras
   int bars_base_tf=Bars(Symbol(),base_tf);
//número de dígitos decimales tras la coma en el precio del instrumento
   int Digit=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
//determinando la capacidad de precio del símbolo actual
   double f=1;
   if(Digit==5) {f=100000;}
   if(Digit==4) {f=10000;}
   if(Digit==3) {f=1000;}
   if(Digit==2) {f=100;}
   if(Digit==1) {f=10;}

A continuación, hay que añadir las matrices y variables:

//--- declarando variables y matrices
   int i,j,n; //variables para los ciclos
   datetime opentime[];//matriz para guardar la hora de establecimiento de las posiciones
   string symbol[];//matriz para guardar los símbolos
   string type[];////matriz para guardar los tipos de las transacciones
   string openprice[];//matriz para guardar los precios de apertura
   string  sl_price[];//matriz para guardar el nivel de stop loss
   int index[];//matriz para guardar el índice de la barra en la que se ha establecido la posición
   int down_fractal[];//matriz para guardar los índices de los fractales inferiores 
   int up_fractal[];//matriz para guardar los índices de los fractales superiores
   double sl_size_points[];//matriz para guardar los tamaños de stop-loss en puntos
   string maxprofit_price[];//matriz para guardar los niveles de beneficio máximo
   double maxprofit_size_points[];//matriz para guardar el tamaño del beneficio máximo
   int duration[];//matriz para guardar información sobre la duración de la onda en barras
   bool maxprofit_bool[];//matriz para definir si la posición no rompe en el s/l
   int maxprofit_int[];//matriz para definir la barra menor/mayor. Se usará con maxprofit_bool[]

Después de ello, pasamos a la lectura de los datos desde el archivo en matrices:

   SLine lines[];
   int size=0;
   if(!ReadFileToArrayCSV("report.csv",lines))
     {
      Alert("Error, mire los detalles  en la pestaña \"Expertos\"");
     }
   else
     {
      size=ArraySize(lines);
      ArrayResize(opentime,ArraySize(lines));
      ArrayResize(symbol,ArraySize(lines));
      ArrayResize(type,ArraySize(lines));
      ArrayResize(openprice,ArraySize(lines));
      ArrayResize(sl_price,ArraySize(lines));
      ArrayResize(index,ArraySize(lines));
      ArrayResize(down_fractal,ArraySize(lines));
      ArrayResize(up_fractal,ArraySize(lines));
      ArrayResize(sl_size_points,ArraySize(lines));
      ArrayResize(maxprofit_price,ArraySize(lines));
      ArrayResize(maxprofit_size_points,ArraySize(lines));
      ArrayResize(duration,ArraySize(lines));
      ArrayResize(maxprofit_bool,ArraySize(lines));
      ArrayResize(maxprofit_int,ArraySize(lines));
      for(i=0;i<size;i++)
        {
         for(j=0;j<ArraySize(lines[i].field);j=j+5)//debemos seleccionar los campos según la columna de la hora de apertura
           {
            opentime[i]=(datetime)(lines[i].field[j]);//registramos los datos en la matriz
           }
         for(j=1;j<ArraySize(lines[i].field);j=j+4)//debemos seleccionar los campos según la columna del símbolo
           {
            symbol[i]=(lines[i].field[j]);//registramos los datos en la matriz
           }
         for(j=2;j<ArraySize(lines[i].field);j=j+3)//debemos seleccionar los campos según la columna del tipo de transacción
           {
            type[i]=(lines[i].field[j]);//registramos los datos en la matriz
           }
         for(j=3;j<ArraySize(lines[i].field);j=j+2)//debemos seleccionar los campos según la columna del precio de apertura
           {
            openprice[i]=(lines[i].field[j]);//registramos los datos en la matriz
           }
         for(j=4;j<ArraySize(lines[i].field);j=j+1)//debemos seleccionar los campos según la columna de stop-loss
           {
            sl_price[i]=(lines[i].field[j]);//registramos los datos en la matriz
           }
        }
     }
//-----------------------------------------------------

Las matrices openrpice[] y sl_price[] tienen un tipo de datos de cadena, y para usarlos en los cálculos, deberemos transformarlos en el tipo double con la ayuda de la función StringToDouble(). En este caso, además, los dígitos tras la coma se perderán. Para que no suceda esto, vamos a sustituir las comas por puntos con la ayuda de la función StringReplace():

   for(i=0;i<size;i++)
     {
      StringReplace(openprice[i],",",".");
      StringReplace(sl_price[i],",",".");
     }

A continuación, debemos definir los índices de las barras en las que se han establecido las posiciones:

//--- determinando los índices de las barras en las que se han abierto las posiciones
   for(i=0;i<size;i++)
     {
      index[i]=iBarShift(Symbol(),PERIOD_D1,opentime[i]);//registramos los datos en la matriz
     }

Después, encontramos los fractales inferiores y superiores más próximos a las posiciones colocadas:

//--- buscando el fractal descendente para la venta
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell")
        {
         for(n=index[i];n>0;n--)
           {
            if(FractalDown_base_tf[n]!=EMPTY_VALUE)
               break;
           }
         down_fractal[i]=n;
        }
     }
//--- buscando el fractal ascendente para la compra
   for(i=0;i<size;i++)
     {
      if(type[i]=="buy")
        {
         for(n=index[i];n>0;n--)
           {
            if(FractalUp_base_tf[n]!=EMPTY_VALUE)
               break;
           }
         up_fractal[i]=n;
        }
     }

A continuación, determinamos el tamaño del stop-loss en puntos y convertimos el número de puntos en un valor entero:

//--- tamaño del stop-loss en puntos
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell")
        {
         sl_size_points[i]=(StringToDouble(sl_price[i])-StringToDouble(openprice[i]))*f;
        }
      if(type[i]=="buy")
        {
         sl_size_points[i]=(StringToDouble(openprice[i])-StringToDouble(sl_price[i]))*f;
        }
     }

Basándonos en los fractales encontrados anteriormente, podemos determinar el nivel del beneficio máximo. Pero antes, debemos comprobar si la posición no se cerrará antes por stop-loss. Código de comprobación:

//--- comprobando que la posición no se haya cerrado por s/l antes de alcanzar el nivel de beneficio máximo
//--- para las transacciones sell
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell")
        {
         for(n=index[i];n>down_fractal[i];n--)
           {
            if(High_base_tf[n]>=StringToDouble(sl_price[i]))
               break;
           }
         maxprofit_int[i]=n;
         maxprofit_bool[i]=(n==down_fractal[i]);
        }
     }
//--- para las transacciones buy
   for(i=0;i<size;i++)
     {
      if(type[i]=="buy")
        {
         for(n=index[i];n>up_fractal[i];n--)
           {
            if(Low_base_tf[n]<=StringToDouble(sl_price[i]))
               break;
           }
         maxprofit_int[i]=n;
         maxprofit_bool[i]=(n==up_fractal[i]);
        }
     }

Ahora podemos escribir el código para determinar el nivel de beneficio máximo:

//--- nivel de beneficio máximo
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell" && maxprofit_bool[i]==true)
        {
         maxprofit_price[i]=(string)Low_base_tf[down_fractal[i]];
        }
      if(type[i]=="sell" && maxprofit_bool[i]==false)
        {
         maxprofit_price[i]="";
        }
      if(type[i]=="buy" && maxprofit_bool[i]==true)
        {
         maxprofit_price[i]=(string)High_base_tf[up_fractal[i]];
        }
      if(type[i]=="buy" && maxprofit_bool[i]==false)
        {
         maxprofit_price[i]="";
        }
     }

A continuación, podemos determinar el tamaño del beneficio máximo. El tamaño del beneficio será negativo respecto al tamaño del stop-loss, si se activa el control:

   for(i=0;i<size;i++)
     {
      if(type[i]=="sell" && maxprofit_bool[i]==true)
        {
         maxprofit_size_points[i]=(StringToDouble(openprice[i])-Low_base_tf[down_fractal[i]])*f;
        }
      if(type[i]=="sell" && maxprofit_bool[i]==false)
        {
         maxprofit_size_points[i]=sl_size_points[i]*-1;
        }
      if(type[i]=="buy" && maxprofit_bool[i]==true)
        {
         maxprofit_size_points[i]=(High_base_tf[up_fractal[i]]-StringToDouble(openprice[i]))*f;
        }
      if(type[i]=="buy" && maxprofit_bool[i]==false)
        {
         maxprofit_size_points[i]=sl_size_points[i]*-1;
        }
     }

Finalmente, determinamos la duración medida como el número de barras entre la barra en la que está definida la posición y la barra del beneficio máximo. Si se activa el control de cierre de posición por s/l, la duración se determinará como la diferencia entre la barra en la que está definida la posición y la barra en la que se activa el s/l:

//--- cálculo de la duración de la transacción en barras
   for(i=0;i<size;i++)
     {
      if(type[i]=="sell" && maxprofit_bool[i]==true)
        {
         duration[i]=index[i]-(int)down_fractal[i];
        }
      if(type[i]=="sell" && maxprofit_bool[i]==false)
        {
         duration[i]=index[i]-maxprofit_int[i];
        }
      if(type[i]=="buy" && maxprofit_bool[i]==true)
        {
         duration[i]=index[i]-(int)up_fractal[i];
        }
      if(type[i]=="buy" && maxprofit_bool[i]==false)
        {
         duration[i]=index[i]-maxprofit_int[i];
        }
     }

Después de ello, y para que las lecturas se representen con normalidad en el informe, sustituiremos de nuevo los puntos por comas:

   for(i=0;i<size;i++)
     {
      StringReplace(openprice[i],".",",");
      StringReplace(sl_price[i],".",",");
      StringReplace(maxprofit_price[i],".",",");
     }

Al final, solo queda registrar los datos obtenidos en el archivo file_stat.csv:

//--- registramos los datos en el nuevo archivo de estadísticas
   int h=FileOpen("file_stat.csv",FILE_READ|FILE_WRITE|FILE_ANSI|FILE_CSV,";");
//--- comprobando la apertura
   if(h==INVALID_HANDLE)
     {
      Alert("Error al abrir el archivo1");
      return;
     }
   else
     {
      FileWrite(h,
                /*1 símbolo*/"Símbolo",
                /*2 tipo de transacción*/"Tipo de transacción",
                /*3 hora de entrada*/"Hora de entrada",
                /*4 precio de apertura*/"Precio de apertura",
                /*5 nivel de s/l*/"SL",
                /*6 tamaño de s/l*/"Tamaño de SL",
                /*7 nivel de beneficio máximo*/"Nivel de beneficio máximo",
                /*8 tamaño del beneficio máximo*/"Tamaño del beneficio máximo",
                /*9 duración*/"Duración");
      //--- desplazamiento hacia el final
      FileSeek(h,0,SEEK_END);
      for(i=0;i<size;i++)
        {
         FileWrite(h,
                   /*1 símbolo*/symbol[i],
                   /*2 tipo de transacción*/type[i],
                   /*3 hora de entrada*/TimeToString(opentime[i]),
                   /*4 precio de apertura*/openprice[i],
                   /*5 nivel de s/l*/sl_price[i],
                   /*6 tamaño del s/l*/NormalizeDouble(sl_size_points[i],2),
                   /*7 nivel de beneficio máximo*/maxprofit_price[i],
                   /*8 tamaño del beneficio máximo*/NormalizeDouble(maxprofit_size_points[i],2),
                   /*9 duración*/duration[i]);
        }
     }
   FileClose(h);
   Alert("El archivo file_stat.csv ha sido creado");

Realizamos la siguiente comprobación: colocamos el script en el gráfico, sin olvidar, en este caso, establecer el periodo del marco temporal básico en los parámetros de entrada (en nuestro caso, D1), después de lo cual, aparecerá en la carpeta local del terminal el archivo file_stat.csv con el siguiente conjunto de indicadores:

Contenido del archivo file_stat.csv 


Fig.7. Contenido del archivo file_stat.csv

6. Conclusión

El artículo describe un método para definir de forma programática uno de los modelos de continuación del movimiento. La idea clave de este método es la búsqueda del extremo high/low del movimiento de corrección sin utilizar indicador alguno. Partiendo ya del extremo localizado, se realiza la búsqueda de los siguientes puntos del modelo.

En el artículo también se analiza un método de recopilación de datos estadísticos, usando para ello los resultados de la simulación en el simulador de estrategias mediante el registro de resultados en una matriz y el posterior procesamiento de los mismos. Podríamos haber creado un método más eficaz de recopilización de datos estadísticos, sin embargo, el presente método nos pareció el más sencillo y comprensible.

Hay que tener en cuenta que en el artículo se describen los requisitos mínimos de definición del modelo según el parecer del autor, y lo más importante, el conjunto mínimo de controles realizado por el asesor. Para el comercio real, el conjunto de controles, como es lógico, debe ser ampliado.

Al final del artículo, adjuntamos imágenes con ejemplos de reconocimiento del modelo de continuación del movimiento:

Ejemplo de reconocimiento del modelo

Fig. 8. Ejemplo de reconocimiento del modelo de continuación del movimiento

Ejemplo de reconocimiento del modelo

Fig. 9. Ejemplo de reconocimiento del modelo de continuación del movimiento

Ejemplo de reconocimiento del modelo de continuación de tendencia

Fig. 10. Ejemplo de reconocimiento del modelo de continuación del movimiento

Ejemplo de reconocimiento del modelo de continuación de tendencia

Fig. 11. Ejemplo de reconocimiento del modelo de continuación del movimiento

Programas usados en el artículo:

#NombreTipoDescripción
1Trade.mqhBiblioteca de claseClase de operaciones comerciales
2MQL5_POST_finalAsesorAsesor que determina el modelo de continuación del movimiento
3Report_readScriptScript para recopilar datos estadísticos