Modelo de continuación de movimiento - búsqueda en el gráfico y estadísticas de ejecución
- Introducción
- Descripción del modelo - disposiciones generales
- Principios de reconocimiento del modelo en el gráfico
- Construyendo el algoritmo y escribiendo el código
- Parámetros de entrada, la función OnInit() y la declaración inicial de variables
- Definiendo los parámetros generales
- Actualizando los datos en la matriz
- Rellenando las matrices al aparecer una nueva barra
- Rellenando las matrices con los datos de la barra 0
- Actualizando los datos sobre los fractales
- Buscando los extremos
- Buscando los extremos para la tendencia descendente
- Buscando los extremos para la tendencia ascendente
- Conviertiendo los valores de High/Low de las ondas de corrección en variables únicas
- Describiendo las condiciones para el reconocimiento del modelo
- Creando los controles
- Control de formación del punto de entrada en la zona de apertura de posición
- Control de retroceso del precio hacia la zona de apertura de posición
- Excluyendo la formación de posiciones duplicadas en el marco de un mismo modelo
- Describiendo las condiciones para el punto de entrada
- Describiendo las condiciones comerciales
- Trabajando con las operaciones comerciales
- Recopilando los datos estadísticos
- 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:
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 |
---|---|---|---|
1 | Consideraremos una barra-extremo a aquella cuyo High/Low esté por encima/debajo de dos los High/Low de las barras anteriores | 1 | Consideraremos una barra-extremo a aquella cuyo High/Low esté por encima/debajo de dos los High/Low de las barras anteriores |
2 | La onda de corrección siempre deberá finalizar con la presencia del extremo superior (punto С - ver figura 1 y figura 2) | 2 | La onda de corrección siempre deberá finalizar con la presencia del extremo inferior (punto С - ver figura 1 y figura 2) |
3 | La duración de la onda de correción no puede ser larga y deberá estar limitada por varias barras | 3 | La duración de la onda de correción no puede ser larga y deberá estar limitada por varias barras |
4 | El 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) | 4 | El 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) |
5 | El 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 | 5 | El 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/p | Para la tendencia descendente | Para la tendencia ascendente |
---|---|---|
1 | Encontrar el high del movimiento de corrección (punto С) | Encontrar el low del movimiento de corrección (punto С) |
2 | A 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 А) |
3 | Entre los puntos С y А, encontrar el punto В - el low del movimiento de corrección | Entre los puntos С y А, encontrar el punto В - el high del movimiento de corrección |
//+------------------------------------------------------------------+ //| 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
Índice | Valor del punto A | Valor del punto B | Valor del punto C |
---|---|---|---|
Índice de la barra | UpperFractal_downtrend_base_tf | Low_Corr_wave_downtrend_base_tf | High_Corr_wave_downtrend_base_tf |
Valor temporal | Time_base_tf[UpperFractal_downtrend_base_tf] | Time_base_tf[Low_Corr_wave_downtrend_base_tf] | High_Corr_wave_downtrend_base_tf_time |
Valor del precio | High_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
Índice | Valor del punto A | Valor del punto B | Valor del punto C |
---|---|---|---|
Índice de la barra | LowerFractal_uptrend_base_tf | High_Corr_wave_uptrend_base_tf | Low_Corr_wave_uptrend_base_tf |
Valor temporal | Time_base_tf[LowerFractal_uptrend_base_tf] | Time_base_tf[High_Corr_wave_uptrend_base_tf] | Low_Corr_wave_uptrend_base_tf_time |
Valor del precio | Low_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/p | Condiciones para la tendencia descendente | Condiciones para la tendencia ascendente |
---|---|---|
1 | El 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 А) |
2 | El í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 С) |
3 | Duració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.
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:
- Definición de posiciones;
- Definición de take-profit;
- Trasladar la posición a ausencia de pérdidas.
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:
- Símbolo del instrumento;
- Tipo de la transacción;
- Hora de entrada;
- Precio de apertura;
- Nivel de stop-loss;
- Tamaño del stop-loss;
- Nivel de beneficio máximo;
- Tamaño del beneficio máximo;
- 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:
- Hora de apertura;
- Símbolo
- Tipo;
- Precio;
- S/L.
Como resultado, el archivo report.csv tendrá el aspecto siguiente:
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:
- Tamaño del SL;
- Nivel de beneficio máximo;
- Tamaño del beneficio máximo;
- 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:
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:
Fig. 8. Ejemplo de reconocimiento del modelo de continuación del movimiento
Fig. 9. Ejemplo de reconocimiento del modelo de continuación del movimiento
Fig. 10. Ejemplo de reconocimiento del modelo de continuación del movimiento
Fig. 11. Ejemplo de reconocimiento del modelo de continuación del movimiento
Programas usados en el artículo:
# | Nombre | Tipo | Descripción |
---|---|---|---|
1 | Trade.mqh | Biblioteca de clase | Clase de operaciones comerciales |
2 | MQL5_POST_final | Asesor | Asesor que determina el modelo de continuación del movimiento |
3 | Report_read | Script | Script para recopilar datos estadísticos |
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/4222
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso