English Русский 中文 Deutsch 日本語 Português
Estrategia de trading '80-20'

Estrategia de trading '80-20'

MetaTrader 5Sistemas comerciales | 13 diciembre 2016, 08:37
4 000 0
Alexander Puzanov
Alexander Puzanov

Introducción

'80-20' es el nombre de una de las Estrategias Comerciales descritas en el libro «Street Smarts: High Probability Short-Term Trading Strategies», escrito por Linda Raschke y Laurence Connors. Igual como dos estrategias analizadas en mi artículo anterior, los autores la atribuyeron a la fase del testeo de los límites del intervalo por el precio. Ella también está enfocada en la obtención del beneficio desde la ruptura falsa o retroceso de los límites. Sin embargo, esta vez para detectar la señal, se analiza el movimiento del precio en un período significativamente más corto del historial, solamente el día anterior. El tiempo de vida de la señal obtenida también es relativamente pequeño, el sistema está diseñado para el trading intradía.

El primero de los objetivos de este artículo consiste en describir la creación del módulo de señal en el lenguaje MQL5 que se encarga de la implementación de la estrategia comercial '80-20'. Luego conectaremos este módulo al Asesor Experto (EA) básico creado en el artículo anterior de esta serie, modificándolo un poco. Además de eso, usaremos este módulo sin cambiarlo durante el desarrollo del indicador para el trading manual.

Recordaré que el código que se está desarrollando en esta serie de artículos está orientado principalmente a la categoría de los programadores que puede ser definida como «principiantes ligeramente avanzados». Por tanto, aparte de su tarea principal, este código está ideado para ayudar a pasar del enfoque procesal al enfoque orientado a objetos. Aquí no vamos a crear las clase, pero vamos a usar en totalidad sus análogos más simples, es decir, las estructuras.

Otro objetivo de este artículo es crear las herramientas que permitan comprobar la eficacia de esta estrategia hoy en día. Pues, durante su creación Raschke y Connors se basaban en el comportamiento del mercado del final del siglo pasado. Algunas pruebas en datos históricos actuales del EA creado serán presentadas al final del artículo.


Sistema de trading '80-20'

Como fundamentación teórica, los autores se refieren al libro The Taylor Trading Technique de George Taylor, así como a los trabajos sobre el análisis de ordenador de los mercados de futuros de Steve Moore, y la experiencia práctica de Derek Gipson. La hipótesis, como base de la Estrategia Comercial, puede ser resumida de la siguiente manera: si los precios de apertura y de cierre del día de ayer están distribuidos en las áreas opuestas del intervalo diario, entonces, hoy con mayor probabilidad, se puede esperar la reversa a la dirección de la apertura del día de ayer. Además, es importante que, por un lado, los precios de apertura y de cierre de ayer estuvieran bastante cerca de los límites del intervalo, y, por otro lado, la reversa empezara precisamente hoy, y no antes del cierre de la barra diaria de ayer. Completado con sus propios retoques de los autores, el conjunto de las reglas de la Estrategia Comercial '80-20' para la entrada en la operación de compra puede ser formulado de la siguiente manera:

1. Asegúrese de que ayer el mercado se abrió en los 20% superiores del intervalo diario, y se cerró en los 20% inferiores del intervalo.

2. Espere a que el precio mínimo de hoy rompa el mínimo de ayer por lo menos en 5 ticks.

3. Coloque la orden pendiente de compra en el límite inferior del intervalo de ayer.

4. Inmediatamente después de la activación de la orden pendiente, establezca su StopLoss inicial en el mínimo de hoy.

5. Utilice Trailing Stop para proteger el beneficio obtenido.

Las reglas para entrar en venta son similares, pero la barra de ayer debe ser alcista, hay que colocar la orden de compra en el límite superior de esta barra, y colocar StopLoss en el nivel del máximo de hoy.

Otro detalle importante aparece en el libro durante la discusión sobre las ilustraciones de la Estrategia Comercial en los gráficos desde el historial: los autores llaman la atención en el tamaño de la barra diaria que se cierra. Según Raschke, debe ser bastante grande, más grande que el tamaño medio de las barras diarias. La verdad es que no especifica el número de días del historial que hay que tomar en consideración a la hora de calcular el rango diario medio.

No olvidemos que esta Estrategia Comercial está destinada exclusivamente para el trading intradía, los ejemplos mostrados en el libro usan los gráficos con el período de tiempo de 15 minutos.

Más abajo, se describe el bloque de señal y el indicador que lo utiliza y hace trazado según esta estrategia. A continuación, Usted puede ver unas capturas de pantalla con el resultado de trabajo del indicador. Se ven muy bien los patrones que corresponden a las reglas del sistema, y los niveles de trading vinculados a estos patrones.

Período de tiempo M5:


Como resultado del análisis de este patrón debe ser la colocación de la orden pendiente de compra. Los niveles de trading correspondientes se ven mejor en el período de tiempo M1:


El patrón semejante con la dirección opuesta del trading en el período de tiempo M5:

Patrón de la estrategia 80-20

Sus niveles de trading (M1):


 

Módulo de señal

Para mostrar el ejemplo de cómo añadir las opciones adicionales a la Estrategia Comercial, añadiremos el cálculo del nivel Take Profit. Este nivel no está presente en la versión original, para el cierre de la posición se utiliza solamente el trailing del nivel Stop Loss. Hagamos que Take Profit dependa del valor mínimo de la ruptura establecido por el usuario (TS_8020_Extremum_Break) — vamos a multiplicarlo por el coeficiente personalizado TS_8020_Take_Profit_Ratio.

De la función principal del módulo de señal fe_Get_Entry_Signal vamos a necesitar lo siguiente: el estatus actual de la señal, los niveles calculados de la entrada y salida (Stop Loss y Take Profit), así como los límites del intervalo del día anterior. Obtendremos todos los niveles por las referencias traspasadas a las variables de la función, y el estatus de la señal devuelto va a usar la lista de opciones del artículo anterior:

enum ENUM_ENTRY_SIGNAL {  // Lista de señales de entrada
  ENTRY_BUY,              // señal de compra
  ENTRY_SELL,             // señal de venta
  ENTRY_NONE,             // no hay señal
  ENTRY_UNKNOWN           // estatus no definido
};

ENUM_ENTRY_SIGNAL fe_Get_Entry_Signal( // Análisis del patrón de dos velas D1
  datetime  t_Time,          // hora actual
  double&    d_Entry_Level,  // nivel de entrada (referencia a la variable)
  double&    d_SL,           // nivel StopLoss (referencia a la variable)
  double&    d_TP,           // nivel TakeProfit (referencia a la variable)
  double&    d_Range_High,   // máximo del intervalo de la 1-a barra del patrón (referencia a la variable)
  double&    d_Range_Low     // mínimo del intervalo de la 1-a barra del patrón (referencia a la variable)
) {}

Para identificar la señal, es necesario analizar dos últimas barras del timeframe diario. Empezamos con la primera de ellas: si ella no corresponde a los criterios de la Estrategia Comercial, no tendrá sentido comprobar la segunda barra. Hay dos criterios:

1. La magnitud de la barra (diferencia entre los precios High y Low) debe ser superior a la media para los últimos XX días (se establece con la configuración personalizada TS_8020_D1_Average_Period)

2. Los niveles de apertura y de cierre de la barra deben estar relacionados con los 20% opuestos del intervalo de la barra

Si estas condiciones se cumplen, entonces hay que recordar los precios High y Low para el uso futuro. Puesto que los parámetros de la primera barra del patrón no van a cambiar durante todo el día, no vamos a comprobarlos con cada llamada de la función, los guardaremos en las variables estáticas:

// ajustes personalizados
input uint  TS_8020_D1_Average_Period = 20;  // 80-20: Número de días para calcular el intervalo diario medio
input uint  TS_8020_Extremum_Break = 50;     // 80-20: Ruptura mínima del extremo de ayer (en puntos)


static ENUM_ENTRY_SIGNAL se_Possible_Signal = ENTRY_UNKNOWN; // dirección de la señal en la 1-a barra del patrón
static double
  // variables para almacenar los niveles calculados entre los ticks
  sd_Entry_Level = 0,
  sd_SL = 0, sd_TP = 0,
  sd_Range_High = 0, sd_Range_Low = 0
;


// prueba de 1-a barra del patrón en D1:
if(se_Possible_Signal == ENTRY_UNKNOWN) { // todavía no ha sido realizada hoy
  st_Last_D1_Bar = t_Curr_D1_Bar; // este día la 1-a barra no va a cambiar más
  
  // intervalo diario medio
  double d_Average_Bar_Range = fd_Average_Bar_Range(TS_8020_D1_Average_Period, PERIOD_D1, t_Time);
  
  if(ma_Rates[0].high — ma_Rates[0].low <= d_Average_Bar_Range) {
    // la 1-a barra no es suficientemente grande
    se_Possible_Signal = ENTRY_NONE; // significa que hoy no habrá la señal
    return(se_Possible_Signal);
  }
  
  double d_20_Percents = 0.2 * (ma_Rates[0].high — ma_Rates[0].low); // 20% del intervalo de ayer
  if((
      // barra bajista:
      ma_Rates[0].open > ma_Rates[0].high — d_20_Percents // la barra se ha abierto en los 20% superiores
      &&
      ma_Rates[0].close < ma_Rates[0].low + d_20_Percents // y se ha cerrado en los 20% inferiores
    ) || (
      // alcista:
      ma_Rates[0].close > ma_Rates[0].high — d_20_Percents // la barra se ha cerrado en los 20% superiores
      &&
      ma_Rates[0].open < ma_Rates[0].low + d_20_Percents // y se ha abierto en los 20% inferiores
  )) {
    // la 1-a barra corresponde a las condiciones
    // definición de la dirección del trading para hoy por la 1-a barra del patrón:
    se_Possible_Signal = ma_Rates[0].open > ma_Rates[0].close ? ENTRY_BUY : ENTRY_SELL;
    // nivel de la entrada en el mercado:
    sd_Entry_Level = d_Entry_Level = se_Possible_Signal == ENTRY_BUY ? ma_Rates[0].low : ma_Rates[0].high;
    // límites del intervalo de la 1-a barra del patrón:
    sd_Range_High = d_Range_High = ma_Rates[0].high;
    sd_Range_Low = d_Range_Low = ma_Rates[0].low;
  } else {
    // los niveles de la apertura/cierre de la 1-a barra no corresponde a las condiciones
    se_Possible_Signal = ENTRY_NONE; // significa que hoy no habrá la señal
    return(se_Possible_Signal);
  }
}

Listado de la función de la definición del intervalo medio de la barra para el número establecido de las barras del timeframe especificado, empezando desde la hora indicada a la función:

double fd_Average_Bar_Range(    // Cálculo del tamaño medio de la barra
  int i_Bars_Limit,             // cuantas barras a tomar en cuenta
  ENUM_TIMEFRAMES e_TF = PERIOD_CURRENT,  // timeframe de las barras
  datetime t_Time = WRONG_VALUE  // a partir de que momento empezar el cálculo
) {
  double d_Average_Range = 0; // variable para sumar los valores
  if(i_Bars_Limit < 1) return(d_Average_Range);
  
  MqlRates ma_Rates[]; // array para la información sobre las barras
  
  // obtención de la información sobre las barras desde la parte establecida del historial:
  if(t_Time == WRONG_VALUE) t_Time = TimeCurrent();
  int i_Price_Bars = CopyRates(_Symbol, e_TF, t_Time, i_Bars_Limit, ma_Rates);
  
  if(i_Price_Bars == WRONG_VALUE) { // procesamiento de la función CopyRates
    if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyRates: error #%u", __FUNCTION__, _LastError);
    return(d_Average_Range);
  }
  
  if(i_Price_Bars < i_Bars_Limit) { // la función CopyRates ha extraído los datos parcialmente
    if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyRates: copiado %u barras de %u", __FUNCTION__, i_Price_Bars, i_Bars_Limit);
  }
  
  // suma de intervalos:
  int i_Bar = i_Price_Bars;
  while(i_Bar-- > 0)
    d_Average_Range += ma_Rates[i_Bar].high — ma_Rates[i_Bar].low;
  
  // valor medio:
  return(d_Average_Range / double(i_Price_Bars));
}

Para la segunda barra (actual) del patrón, el criterio es único: la ruptura del límite del intervalo de ayer no debe ser menos de la ruptura establecida en los ajustes (TS_8020_Extremum_Break). En cuanto se alcance este nivel, aparecerá la señal para colocar la orden pendiente:

// prueba de 2-a barra (actual) del patrón en D1:
if(se_Possible_Signal == ENTRY_BUY) {
  sd_SL = d_SL = ma_Rates[1].low; // StopLoss — en el máximo del precio de hoy
  if(TS_8020_Take_Profit_Ratio > 0) sd_TP = d_TP = d_Entry_Level + _Point * TS_8020_Extremum_Break * TS_8020_Take_Profit_Ratio; // TakeProfit
  return(
    // ¿está bien expresada la ruptura para abajo?
    ma_Rates[1].close < ma_Rates[0].low — _Point * TS_8020_Extremum_Break ?
    ENTRY_BUY : ENTRY_NONE
  );
}

if(se_Possible_Signal == ENTRY_SELL) {
  sd_SL = d_SL = ma_Rates[1].high; // StopLoss — en el mínimo del precio de hoy
  if(TS_8020_Take_Profit_Ratio > 0) sd_TP = d_TP = d_Entry_Level — _Point * TS_8020_Extremum_Break * TS_8020_Take_Profit_Ratio; // TakeProfit
  return(
    // ¿está bien expresada la ruptura para arriba?
    ma_Rates[1].close > ma_Rates[0].high + _Point * TS_8020_Extremum_Break ?
    ENTRY_SELL : ENTRY_NONE
  );
}

Vamos a guardar dos funciones descritas arriba (fe_Get_Entry_Signal y fd_Average_Bar_Range) y los ajustes personalizadas referentes a la obtención de la señal en el archivo de la librería mqh. Encontrará el código completo en el anexo a este artículo Vamos a nombrar el archivo Signal_80-20.mqh y colocarlo en la carpeta correspondiente (MQL5\Include\Expert\Signal) del directorio del terminal. 


Indicador para el trading manual

El indicador, igual como el EA, va a utilizar el módulo de señal descrito anteriormente. El indicador debe informar al trader sobre la recepción de la señal para la colocación de la orden pendiente y comunicar los niveles de cálculo: nivel de colocación de la orden, los niveles Take Profit y Stop Loss. Los métodos de notificación el usuario puede eligirlos personalmente, eso puede ser una ventana emergente estándar, mensaje de correo electrónico o notificación en el dispositivo móvil. Se puede elegir todo, o cualquier combinación conveniente de opciones mencionadas.

Otra finalidad del indicador es el trazado en el historial del trading a base de la Estrategia Comercial '80-20'. Va a resaltar las barras diarias correspondientes a los criterios del sistema y trazar los niveles de cálculo. Basándose en las líneas de los niveles, será posible evaluar cómo iba evolucionando la situación a lo largo de tiempo Para la mejor demostración hagamos lo siguiente: cuando el precio toca la línea de señal, ella se termina, se empieza la línea de la orden pendiente; y cuando la orden pendiente se activa, se termina su línea y se empiezan las líneas Take Profit y Stop Loss. Estas líneas se interrumpirán cuando el precio toque una de ellas (la orden se cerrará). Con este trazado será más fácil evaluar la eficacia de las reglas del sistema de trading y comprender qué es lo que se puede mejorar.

Vamos a empezar con la declaración de los búferes y los parámetros de su visualización. En primer lugar, nos hace falta declarar dos búferes con el relleno del área vertical (DRAW_FILLING). Uno de ellos va a resaltar el intervalo completo de la barra diaria del día anterior, el otro, sólo el área interior, para destacarla de los 20% superiores e inferiores del intervalo involucrados en la Estrategia Comercial. Luego, declararemos dos búferes para la línea de señal y la línea de la orden pendiente multicolores (DRAW_COLOR_LINE). Sus colores van a depender de la dirección del trading. Otras dos líneas (Take Proft y Stop Loss) no van a cambiar sus colores (DRAW_LINE)— van a utilizar los mismos colores estándar que les han sido asignados en el terminal. A excepción de la línea simple, todos los tipos de visualización requieren dos búferes, por eso el código va a tener el siguiente aspecto:

#property indicator_chart_window
#property indicator_buffers  10
#property indicator_plots    6

#property indicator_label1  "1 barra del patrón"
#property indicator_type1   DRAW_FILLING
#property indicator_color1  clrDeepPink, clrDodgerBlue
#property indicator_width1  1

#property indicator_label2  "1 barra del patrón"
#property indicator_type2   DRAW_FILLING
#property indicator_color2  clrDeepPink, clrDodgerBlue
#property indicator_width2  1

#property indicator_label3  "Nivel de señal"
#property indicator_type3   DRAW_COLOR_LINE
#property indicator_style3  STYLE_SOLID
#property indicator_color3  clrDeepPink, clrDodgerBlue
#property indicator_width3  2

#property indicator_label4  "Nivel de entrada"
#property indicator_type4   DRAW_COLOR_LINE
#property indicator_style4  STYLE_DASHDOT
#property indicator_color4  clrDeepPink, clrDodgerBlue
#property indicator_width4  2

#property indicator_label5  "Stop Loss"
#property indicator_type5   DRAW_LINE
#property indicator_style5  STYLE_DASHDOTDOT
#property indicator_color5  clrCrimson
#property indicator_width5  1

#property indicator_label6  "Take Profit"
#property indicator_type6   DRAW_LINE
#property indicator_style6  STYLE_DASHDOTDOT
#property indicator_color6  clrLime
#property indicator_width6  1

Vamos a dar al usuario la posibilidad de desactivar el relleno de la primera barra diaria del patrón, elegir las opciones de notificación sobre la señal y limitar la profundidad del trazado del historial. Aquí mismo incluiremos todos los ajustes del sistema de trading desde el módulo de señal. Para eso tendremos que enumerar previamente las variables involucradas en el módulo, aunque si algunas de ellas van a usarse sólo en el EA y no serán necesarias para el indicador:

#include <Expert\Signal\Signal_80-20.mqh> // módulo de señal de la estrategia '80-20'

input bool    Show_Outer = true;      // 1-a barra del patrón: ¿Mostrar el intervalo completo?
input bool    Show_Inner = true;      // 1-a barra del patrón: ¿Mostrar el área interna?
input bool    Alert_Popup = true;     // Alerta: ¿Mostrar la ventana emergente?
input bool    Alert_Email = false;    // Alerta: ¿Envar el email?
input string  Alert_Email_Subj = "";  // Alerta: Asunto del mensaje del email
input bool    Alert_Push = true;      // Alerta: ¿Enviar la notificación push?

input uint  Bars_Limit = 2000;  // Profundidad del trazado del historial (en las barras del timeframe actual)



ENUM_LOG_LEVEL  Log_Level = LOG_LEVEL_NONE;  // Modo de registro
double
  buff_1st_Bar_Outer[], buff_1st_Bar_Outer_Zero[], // búferes para el dibujo del intervalo completo de la 1-a barra del patrón
  buff_1st_Bar_Inner[], buff_1st_Bar_Inner_Zero[], // búferes para el dibujo de los 60% internos de la 1-a barra del patrón
  buff_Signal[], buff_Signal_Color[], // búferes de la línea de señal
  buff_Entry[], buff_Entry_Color[], // búferes de la línea de la orden pendiente
  buff_SL[], buff_TP[], // búfere de las líneas StopLoss y TakeProfit
  gd_Extremum_Break = 0 // TS_8020_Extremum_Break en precios del símbolo
;
int
  gi_D1_Average_Period = 1, // valor correcto para TS_8020_D1_Average_Period
  gi_Min_Bars = WRONG_VALUE // número mínimo obligatorio de las barras para el recálculo
;



int OnInit() {
  // comprobación del parámetro introducido TS_8020_D1_Average_Period:
  gi_D1_Average_Period = int(fmin(1, TS_8020_D1_Average_Period));
  // conversión de los puntos en los precios del símbolo:
  gd_Extremum_Break = TS_8020_Extremum_Break * _Point;
  // número mínimo obligatorio de barras para el recálculo = número de barras del timeframe actual en el día
  gi_Min_Bars = int(86400 / PeriodSeconds());
  
  // finalidad de los búferes del indicador:
  
  // rectángulo del intervalo completo de la 1-a barra
  SetIndexBuffer(0, buff_1st_Bar_Outer, INDICATOR_DATA);
    PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0);
  SetIndexBuffer(1, buff_1st_Bar_Outer_Zero, INDICATOR_DATA);
  
  // rectángulo del área interna de la 1-a barra
  SetIndexBuffer(2, buff_1st_Bar_Inner, INDICATOR_DATA);
    PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0);
  SetIndexBuffer(3, buff_1st_Bar_Inner_Zero, INDICATOR_DATA);
  
  // línea de señal
  SetIndexBuffer(4, buff_Signal, INDICATOR_DATA);
    PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, 0);
  SetIndexBuffer(5, buff_Signal_Color, INDICATOR_COLOR_INDEX);
  
  // línea de colocación de la orden pendiente
  SetIndexBuffer(6, buff_Entry, INDICATOR_DATA);
    PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, 0);
  SetIndexBuffer(7, buff_Entry_Color, INDICATOR_COLOR_INDEX);
  
  // línea SL
  SetIndexBuffer(8, buff_SL, INDICATOR_DATA);
    PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, 0);
  
  // línea TP
  SetIndexBuffer(9, buff_TP, INDICATOR_DATA);
    PlotIndexSetDouble(5, PLOT_EMPTY_VALUE, 0);
  
  IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
  IndicatorSetString(INDICATOR_SHORTNAME, "ТС 80-20");
  
  return(INIT_SUCCEEDED);
}

Colocamos el código del programa principal en la función estándar OnCalculate. Es decir, organizamos el ciclo que va a repasar (del pasado al futuro) las barras del timeframe actual y comprobar la presencia de la señal en ellas usando la función desde el módulo de señal. Declaramos e inicializamos previamente las variables usando los valores iniciales. La barra más antigua del ciclo para el primer cálculo se determina tomando en cuenta la profundidad del historial establecida por el usuario (Bars_Limit). Para las llamadas siguientes vamos a recalcular no sólo la última barra, sino todas las barras del día actual, pues el patrón de dos barras en realidad pertenece al gráfico D1, independientemente del timeframe actual.

Además de eso, habrá que tomar medidas para protegernos contra así llamadas «fantasmas»: si no vamos a limpiar obligatoriamente los búferes de los indicadores durante la reinicialización, entonces al conmutar los timeframes o al cambiar el símbolo, en la pantalla van a quedarse las áreas coloreadas que no son actuales para el gráfico actual. Hay que vincular esta acción (limpieza de los búferes) a la primera llamada a la función OnCalculate después de la inicialización del indicador. Pero será imposible determinar que es la primera llamada usando sólo la variable estándar prev_calculated, ya que esta variable contiene el cero no sólo durante la primera llamada a la función, sino durante «el cambio de la suma de control». Vamos a gastar un poco de tiempo para resolver este problema— crearemos una estructura independiente de que la variable prev_calculated se ponga a cero. Esta estructura va a almacenar y procesar los datos útiles que se utilizan con frecuencia en los indicadores.

- bandera de la primera ejecución de la función OnCalculate;

- contador de las barras contadas que no se pone a cero al cambiar la suma de control;

- bandera del cambio de la suma de control;

- bandera del inicio de nueva barra;

- hora del inicio de la barra actual.

La estructura que reúne todos estos datos será declarada a nivel global y podrá recopilar o facilitar la información en/de cualquier función estándar o personalizada. Un nombre adecuado para esa esencia de programa sería «Duende amigo» (Brownie). Podemos colocarla el final del código del indicador. Ahí mismo, vamos a declarar otro objeto-estructura global con el nombre go_Brownie:

struct BROWNIE {                // Duende amigo: estructura para almacenar y procesar los datos a nivel global
  datetime  t_Last_Bar_Time;    // hora de la última barra procesada
  int        i_Prew_Calculated; // número de barras contadas
  bool      b_First_Run;        // bandera de la primera ejecución
  bool      b_History_Updated;  // bandera de actualización del historial
  bool      b_Is_New_Bar;       // bandera de apertura de nueva barra
  
  BROWNIE() { // constructor
    // valores por defecto:
    t_Last_Bar_Time = 0;
    i_Prew_Calculated = WRONG_VALUE;
    b_First_Run = b_Is_New_Bar = true;
    b_History_Updated = false;
  }
  
  void f_Reset(bool b_Reset_First_Run = true) { // puesta a cero de las variables
    // valores por defecto:
    t_Last_Bar_Time = 0;
    i_Prew_Calculated = WRONG_VALUE;
    if(b_Reset_First_Run) b_First_Run = true; // puesta a cero, si hay permiso
    b_Is_New_Bar = true;
    b_History_Updated = false;
  }
  
  void f_Update(int i_New_Prew_Calculated = WRONG_VALUE) { // actualización de variables
    // bandera de la primera llamada a la función estándar OnCalculate
    if(b_First_Run && i_Prew_Calculated > 0) b_First_Run = false;
    
    // ¿nueva barra?
    datetime t_This_Bar_Time = TimeCurrent() - TimeCurrent() % PeriodSeconds();
    b_Is_New_Bar = t_Last_Bar_Time == t_This_Bar_Time;
    
    // ¿actualizar la hora de la barra actual?
    if(b_Is_New_Bar) t_Last_Bar_Time = t_This_Bar_Time;
    
    if(i_New_Prew_Calculated > -1) {
      // ¿hay algunos cambios en el historial?
      b_History_Updated = i_New_Prew_Calculated == 0 && i_Prew_Calculated > WRONG_VALUE;
      
      // prew_calculated usar, si es la 1-a llamada a OnCalculate
      if(i_Prew_Calculated == WRONG_VALUE) i_Prew_Calculated = i_New_Prew_Calculated;
      // o si no ha habido actualizaciones del historial
      else if(i_New_Prew_Calculated > 0) i_Prew_Calculated = i_New_Prew_Calculated;
    }
  }
};
BROWNIE go_Brownie;

Vamos a prever también la notificación del «Duende amigo» sobre la deinicialización del indicador:

void OnDeinit(const int reason) {
  go_Brownie.f_Reset(); // avisar al Duende
}

En caso de necesidad, es posible ampliar la colección de la información almacenada por el «Duende amigo» si las funciones personalizadas o las clases van a necesitar, por ejemplo, los precios, volúmenes o el tamaño del spread de la barra actual (Open, High, Low, Close, tick_volume, volume, spread). Es más cómodo coger los datos hechos desde la función OnCalculate y enviarlos a través del «Duende amigo» en vez de usar las funciones del copiado de las series temporales (CopyOpen, CopyHigh etc. o CopyRates). Eso va a ahorrar los recursos de la CPU y eliminará la necesidad de organizar el procesamiento de errores de estas funciones del lenguaje.

Volvamos a la función principal del indicador. La declaración de las variables y preparación de los arrays con el uso de la estructura go_Brownie tendrá el siguiente aspecto:

go_Brownie.f_Update(prev_calculated); // alimentar al Duende con la información

int
  i_Period_Bar = 0, // contador adicional
  i_Current_TF_Bar = rates_total - int(Bars_Limit) // índice de la barra del inicio del ciclo del timeframe actual
;
static datetime st_Last_D1_Bar = 0; // hora de la última barra D1 del par procesado (2-a barra del patrón)
static int si_1st_Bar_of_Day = 0; // índice de la 1-a barra del día actual

if(go_Brownie.b_First_Run) { // si es la 1-a ejecución
  // limpiar el búfer durante la reinicialización:
  ArrayInitialize(buff_1st_Bar_Inner, 0); ArrayInitialize(buff_1st_Bar_Inner_Zero, 0);
  ArrayInitialize(buff_1st_Bar_Outer, 0); ArrayInitialize(buff_1st_Bar_Outer_Zero, 0);
  ArrayInitialize(buff_Entry, 0); ArrayInitialize(buff_Entry_Color, 0);
  ArrayInitialize(buff_Signal, 0); ArrayInitialize(buff_Signal_Color, 0);
  ArrayInitialize(buff_TP, 0);
  ArrayInitialize(buff_SL, 0);
  st_Last_D1_Bar = 0;
  si_1st_Bar_of_Day = 0;
} else { // no es la primera ejecución
  datetime t_Time = TimeCurrent();
  // profundidad mínima del recálculo - desde ayer:
  i_Current_TF_Bar = rates_total - Bars(_Symbol, PERIOD_CURRENT, t_Time - t_Time % 86400, t_Time) - 1;
}
ENUM_ENTRY_SIGNAL e_Signal = ENTRY_UNKNOWN; // сигнал
double
  d_SL = WRONG_VALUE, // уровень SL
  d_TP = WRONG_VALUE, // nivel TP
  d_Entry_Level = WRONG_VALUE, // nivel de la entrada
  d_Range_High = WRONG_VALUE, d_Range_Low = WRONG_VALUE // límites del intervalo de la 1-a barra del patrón
;
tipo datetime  t_Curr_D1_Bar = 0, // hora de la barra actual D1 (2-a barra del patrón)
  t_D1_Bar_To_Fill = 0 // hora de la barra D1 que hay que colorear (1-a barra del patrón)
;

// controlamos que el índice de la barra inicial del recálculo se encuentre dentro de los límites aceptables:
i_Current_TF_Bar = int(fmax(0, fmin(i_Current_TF_Bar, rates_total - gi_Min_Bars)));

while(++i_Current_TF_Bar < rates_total && !IsStopped()) { // repaso de las barras del timeframe actual
  // aquí habrá el ciclo principal del programa
}

Durante el repaso de las barras del timeframe actual, vamos a comprobar la presencia de la señal:

e_Signal = fe_Get_Entry_Signal(Time[i_Current_TF_Bar], d_Entry_Level, d_SL, d_TP, d_Range_High, d_Range_Low);
if(e_Signal > 1) continue; // el día al que pertenece esta barra no tiene señal

Si, por un lado, la señal existe y, por otro, es la primera barra del día nuevo, es necesario organizar el relleno del intervalo de la barra diaria anterior. El valor de la variable t_D1_Bar_To_Fill tipo datetime será la bandera; si tiene asignado el valor WRONG_VALUE, el relleno no es necesario para esta barra. La línea de señal debe comenzar en la misma primera barra, pero para mejor comprensión del trazado vamos a prolongarla hasta la última barra del día anterior. Puesto que los cálculos del nivel de señal, el color de las líneas y de las áreas coloreadas son diferentes para la barra alcista y bajista, hagamos dos bloques parecidos entre ellos:

t_Curr_D1_Bar = Time[i_Current_TF_Bar] — Time[i_Current_TF_Bar] % 86400; // inicio del día al que pertenece esta barra
if(st_Last_D1_Bar < t_Curr_D1_Bar) { // es la barra del día nuevo
  t_D1_Bar_To_Fill = Time[i_Current_TF_Bar — 1] — Time[i_Current_TF_Bar — 1] % 86400;
  si_1st_Bar_of_Day = i_Current_TF_Bar;
}
else t_D1_Bar_To_Fill = WRONG_VALUE; // barra del día antiguo, el nuevo relleno no es necesario
st_Last_D1_Bar = t_Curr_D1_Bar; // recordamos

if(t_D1_Bar_To_Fill != WRONG_VALUE) { // новый бар D1
  // Relleno de la barra D1 del día anterior:
  i_Period_Bar = i_Current_TF_Bar;
  if(d_Entry_Level < d_Range_High) { // barra bajista D1
    if(Show_Outer) while(--i_Period_Bar > 0) { // intervalo completo
      if(Time[i_Period_Bar] < t_D1_Bar_To_Fill) break;
      buff_1st_Bar_Outer_Zero[i_Period_Bar] = d_Range_Low;
      buff_1st_Bar_Outer[i_Period_Bar] = d_Range_High;
    }
    if(Show_Inner) { // área interna
      i_Period_Bar = i_Current_TF_Bar;
      while(--i_Period_Bar > 0) {
        if(Time[i_Period_Bar] < t_D1_Bar_To_Fill) break;
        buff_1st_Bar_Inner_Zero[i_Period_Bar] = d_Range_Low + 0.2 * (d_Range_High — d_Range_Low);
        buff_1st_Bar_Inner[i_Period_Bar] = d_Range_High — 0.2 * (d_Range_High — d_Range_Low);
      }
    }
    // inicio de la línea de señal — desde la última barra del día anterior
    buff_Signal[i_Current_TF_Bar] = buff_Signal[i_Current_TF_Bar — 1] = d_Range_Low — gd_Extremum_Break;
    buff_Signal_Color[i_Current_TF_Bar] = buff_Signal_Color[i_Current_TF_Bar — 1] = 0;
  } else { // barra alcista D1
    if(Show_Outer) while(--i_Period_Bar > 0) { // intervalo completo
      if(Time[i_Period_Bar] < t_D1_Bar_To_Fill) break;
      buff_1st_Bar_Outer_Zero[i_Period_Bar] = d_Range_High;
      buff_1st_Bar_Outer[i_Period_Bar] = d_Range_Low;
    }
    if(Show_Inner) { // área interna
      i_Period_Bar = i_Current_TF_Bar;
      while(--i_Period_Bar > 0) {
        if(Time[i_Period_Bar] < t_D1_Bar_To_Fill) break;
        buff_1st_Bar_Inner_Zero[i_Period_Bar] = d_Range_High — 0.2 * (d_Range_High — d_Range_Low);
        buff_1st_Bar_Inner[i_Period_Bar] = d_Range_Low + 0.2 * (d_Range_High — d_Range_Low);
      }
    }
    // inicio de la línea de señal — desde la última barra del día anterior
    buff_Signal[i_Current_TF_Bar] = buff_Signal[i_Current_TF_Bar — 1] = d_Range_High + gd_Extremum_Break;
    buff_Signal_Color[i_Current_TF_Bar] = buff_Signal_Color[i_Current_TF_Bar — 1] = 1;
  }
} else continue;

Vamos a organizar el dibujo del resto de las líneas del trazado aquí mismo (dentro del ciclo del repaso de las barras del timeframe actual). Recordare que la línea de señal debe terminarse sobre la barra donde ella ha sido tocada por el precio. La línea de la orden pendiente debe empezar sobre esta misma barra. Ella debe terminar sobre la barra de contacto con el precio, y en la misma barra, deben empezar las líneas de Take Profit y Stop Loss. En la barra de contacto de una de ellas con el precio, el trazado de este patrón será terminado:

// Línea de señal hasta la barra que la atraviesa:
i_Period_Bar = i_Current_TF_Bar;
if(d_Entry_Level < d_Range_High) { // barra bajista D1
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_Signal[i_Period_Bar] = d_Range_Low — gd_Extremum_Break;
    buff_Signal_Color[i_Period_Bar] = 0;
    if(d_Range_Low — gd_Extremum_Break >= Low[i_Period_Bar]) break;
  }
} else { // barra alcista D1
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_Signal[i_Period_Bar] = d_Range_High + gd_Extremum_Break;
    buff_Signal_Color[i_Period_Bar] = 1;
    if(d_Range_High + gd_Extremum_Break <= High[i_Period_Bar]) break;
  }
}

// Línea de entrada hasta la barra que la atraviesa:
if(d_Entry_Level < d_Range_High) { // barra bajista D1
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_Entry[i_Period_Bar] = d_Range_Low;
    buff_Entry_Color[i_Period_Bar] = 0;
    if(d_Range_Low <= High[i_Period_Bar]) {
      if(buff_Entry[i_Period_Bar — 1] == 0.) {
        // inicio y fin en la misma barra, extendemos a 1 barra al pasado
        buff_Entry[i_Period_Bar — 1] = d_Range_Low;
        buff_Entry_Color[i_Period_Bar — 1] = 0;
      }
      break;
    }
  }
} else { // barra alcista D1
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_Entry[i_Period_Bar] = d_Range_High;
    buff_Entry_Color[i_Period_Bar] = 1;
    if(d_Range_High >= Low[i_Period_Bar]) {
      if(buff_Entry[i_Period_Bar — 1] == 0.) {
        // inicio y fin en la misma barra, extendemos a 1 barra al pasado
        buff_Entry[i_Period_Bar — 1] = d_Range_High;
        buff_Entry_Color[i_Period_Bar — 1] = 1;
      }
      break;
    }
  }
}

// Líneas TP y SL hasta la barra que ha cruzado una de ellas:
if(d_Entry_Level < d_Range_High) { // barra bajista D1
  // SL es igual al mínimo desde el inicio del día:
  d_SL = Low[ArrayMinimum(Low, si_1st_Bar_of_Day, i_Period_Bar — si_1st_Bar_of_Day)];
  
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_SL[i_Period_Bar] = d_SL;
    buff_TP[i_Period_Bar] = d_TP;
    if(d_TP <= High[i_Period_Bar] || d_SL >= Low[i_Period_Bar]) {
      if(buff_SL[i_Period_Bar — 1] == 0.) {
        // inicio y fin en la misma barra, extendemos a 1 barra al pasado
        buff_SL[i_Period_Bar — 1] = d_SL;
        buff_TP[i_Period_Bar — 1] = d_TP;
      }
      break;
    }
  }
} else { // barra alcista D1
  // SL es igual al máximo desde el inicio del día:
  d_SL = High[ArrayMaximum(High, si_1st_Bar_of_Day, i_Period_Bar — si_1st_Bar_of_Day)];
  
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_SL[i_Period_Bar] = d_SL;
    buff_TP[i_Period_Bar] = d_TP;
    if(d_SL <= High[i_Period_Bar] || d_TP >= Low[i_Period_Bar]) {
      if(buff_SL[i_Period_Bar — 1] == 0.) {
        // inicio y fin en la misma barra, extendemos a 1 barra al pasado
        buff_SL[i_Period_Bar — 1] = d_SL;
        buff_TP[i_Period_Bar — 1] = d_TP;
      }
      break;
    }
  }
}

Colocaremos el código de la llamada a la función de la notificación sobre la señal f_Do_Alert fuera del ciclo. En realidad, sus posibilidades son un poco más amplias que las que se utilizan en este indicador: la función puede trabajar con los archivos de audio, es decir, se puede añadir en los ajuste personalizados la activación de esta opción y la selección de los archivos separados para las señales de compra y venta. El listado de esta función:

void f_Do_Alert(                  // Función del envío de señales y avisos
  string  s_Message,              // texto para la alerta
  bool    b_Alert = true,         // ¿mostrar la ventana emergente?
  bool    b_Sound = false,        // ¿reproducir archivo sonoro?
  bool    b_Email = false,        // ¿enviar mensaje de correo?
  bool    b_Notification = false, // ¿enviar notificación Push?
  string  s_Email_Subject = "",   // asunto para el mensaje de email
  string  s_Sound = "alert.wav"   // archivo de sonido
) {
  static string ss_Prev_Message = "había silencio"; // texto de la alerta anterior
  static datetime st_Prev_Time; // hora de la barra de la alerta anterior
  datetime t_This_Bar_Time = TimeCurrent() — PeriodSeconds() % PeriodSeconds(); // hora de la barra actual
  
  if(ss_Prev_Message != s_Message || st_Prev_Time != t_This_Bar_Time) {
    // otra alerta y/o la 1-a en esta barra
    
    // guardar:
    ss_Prev_Message = s_Message;
    st_Prev_Time = t_This_Bar_Time;
    
    // formar la línea del mensaje:
    s_Message = StringFormat("%s | %s | %s | %s",
      TimeToString(TimeLocal(), TIME_SECONDS), // hora local
      _Symbol, // símbolo
      StringSubstr(EnumToString(ENUM_TIMEFRAMES(_Period)), 7), // timeframe
      s_Message // сообщение
    );
    
    // saltar la señal del aviso:
    if(b_Alert) Alert(s_Message);
    if(b_Email) SendMail(s_Email_Subject + " " + _Symbol, s_Message);
    if(b_Notification) SendNotification(s_Message);
    if(b_Sound) PlaySound(s_Sound);
  }
}

Este es el código de comprobación de la necesidad de llamar a esta función y formar el texto del mensaje para ella, ubicado en el cuerpo del programa, antes de la finalización del trabajo del manejador de eventos OnCalculate:

// alerta
i_Period_Bar = rates_total — 1; // barra actual

if(Alert_Popup + Alert_Email + Alert_Push == 0) return(rates_total); // todo desactivado
if(buff_Signal[i_Period_Bar] == 0) return(rates_total); // ya o aún no hay nada que pillar
if(
  buff_Signal[i_Period_Bar] > High[i_Period_Bar]
  ||
  buff_Signal[i_Period_Bar] < Low[i_Period_Bar]
) return(rates_total); // no hay toque con la línea de señal

// texto del mensaje:
string s_Message = StringFormat("TS 80-20: es necesario %s @ %s, TP: %s, SL: %s",
  buff_Signal_Color[i_Period_Bar] > 0 ? "BuyStop" : "SellStop",
  DoubleToString(d_Entry_Level, _Digits),
  DoubleToString(d_TP, _Digits),
  DoubleToString(d_SL, _Digits)
);
// aviso:
f_Do_Alert(s_Message, Alert_Popup, false, Alert_Email, Alert_Push, Alert_Email_Subj);

return(rates_total); // fin del trabajo de OnCalculate

El código fuente completo del indicador está en los archivos adjuntos con el nombre TS_80-20.mq5. En cuanto a su uso, se puede decir que el trazado del trading según este sistema se ve mejor en los gráficos de minutos.

Es importante tener en cuenta a la hora de usar este trazado que el indicador utiliza los datos de las barras, en vez de las secuencias de los ticks dentro de las barras. Eso significa que si el precio ha cruzado varias líneas del trazado (por ejemplo, las líneas Take Profit y Stop Loss) en una barra, no siempre se puede determinar cuál de ellas ha sido la primera. Otro momento desfavorable está relacionado con el hecho de que las barras del inicio y del final de las líneas no pueden coincidir, de lo contrario las líneas desde el búfer tipo DRAW_LINE y DRAW_COLOR_LINE simplemente no estarían visibles para el usuario. Estas particularidades hacen que el trazado no sea preciso a 100%, pero es bastante ilustrativo.


Asesor Experto para probar la Estrategia Comercial '80-20'

El EA básico para las pruebas de las estrategias del libro Street Smarts: High Probability Short-Term Trading Strategies ha sido descrito detalladamente en el primer artículo. Vamos a introducir dos modificaciones importantes en él. La primera está relacionada con el hecho de que el módulo de señal va a utilizarse también en el indicador, por tanto sería lógico que haga los cálculos de los niveles de trading. Ya lo hemos hecho antes: la función fe_Get_Entry_Signal, aparte del estatus de la señal, devuelve los niveles de colocación de la orden, Stop Loss y Take Profit. Por eso quitaremos la parte correspondiente del código de la versión anterior del EA, añadiremos las variables para recibir los niveles desde la función y editaremos la llamada a esta función. No voy a mostrar aquí el listado del bloque nuevo y anterior del código, Usted podrá verlo en el archivo adjunto (líneas de 128 a 141).

La segunda adición importante en el código del EA básico está relacionada con el hecho de que esta Estrategia Comercial trata con la tendencia a corto plazo, a diferencia de dos estrategias anteriores. Ella supone que el retroceso ocurrirá una vez al día, y es poco probable que se repita. Eso significa que el robot tiene que hacer sólo una entrada e ignorar la señal existente todo el tiempo restante hasta el día siguiente. La manera más simple para implementar eso consiste en utilizar una bandera especial, la variable global o estática tipo bool en la memoria del programa. Pero si el trabajo del EA será interrumpido por alguna razón (el terminal se cierra, el EA se elimina del gráfico, etc.), se perderá también el valor de la bandera. Por tanto, después del reinicio, el EA debe tener la posibilidad de comprobar si la señal de hoy ha sido utilizado. Para eso se puede analizar el historial de las operaciones de hoy, o se puede almacenar la fecha de la última entrada en las variables globales del terminal, en vez del programa. Nosotros vamos a utilizar la segunda opción, es mucho más sencilla para la implementación.

Vamos a dar al usuario la posibilidad de controlar la opción «una entrada al día», así como establecer el identificador para cada versión del robot en ejecución: va a ser necesario para el uso de las variables globales del nivel del terminal:

input bool  One_Trade = false;    // Una posición al día
input uint  Magic_Number = 2016;  // Identificador del EA (Magic Number)

Para la implementación de la opción «una entrada al día», vamos a añadir la declaración de las variables necesarias en el bloque de determinación de variables globales del programa. Las analizaremos en la función OnInit:

string
  gs_Prefix // identificador de nombres de las variables (super)globales
;
bool
  gb_Position_Today = false,
  gb_Pending_Today = false
;

int OnInit() {

...

  // Creación del prefijo de nombres de las variables (super)globales:
  gs_Prefix = StringFormat("SSB %s %u %s", _Symbol, Magic_Number, MQLInfoInteger(MQL_TESTER) ? "t " : "");
  
  // ¿Ha trabajado el robot hoy con las órdenes de mercado o pendientes?
  gb_Position_Today = int(GlobalVariableGet(gs_Prefix + "Last_Position_Date")) == TimeCurrent() — TimeCurrent() % 86400;
  gb_Pending_Today = int(GlobalVariableGet(gs_Prefix + "Last_Pending_Date")) == TimeCurrent() — TimeCurrent() % 86400;

...
}

Aquí el robot lee el valor de las variables globales y compara la hora registrada en ellas con la hora del inicio del día: así determina si ha sido trabajada ya la señal de hoy o no. Vamos a organizar la escritura de la hora en estas variables en dos lugares: añadimos el bloque correspondiente en el código de colocación de la orden pendiente (las adiciones están marcadas):

if(i_Try != -10) { // fallo al colocar la orden pendiente
  if(Log_Level > LOG_LEVEL_NONE) Print("Error de colocación de la orden pendiente");
  // la distancia desde el precio actual no es suficiente :(
  if(Log_Level > LOG_LEVEL_ERR)
    PrintFormat(" Imposible colocar la orden pendiente en el nivel %s. Bid: %s Ask: %s StopLevel: %s",
      DoubleToString(d_Entry_Level, _Digits),
      DoubleToString(go_Tick.bid, _Digits),
      DoubleToString(go_Tick.ask, _Digits),
      DoubleToString(gd_Stop_Level, _Digits)
    );
} else { // éxito
  // actualizar la bandera:
  GlobalVariableSet( // en las variables globales del terminal
    gs_Prefix + "Last_Pending_Date",
    TimeCurrent() — TimeCurrent() % 86400
  );
  gb_Pending_Today = true; // en las variables globales del programa
}

 Colocamos el segundo bloque después del código que determina la posición recién abierta:

if(PositionSelect(_Symbol)) { // hay posición abierta
        if(PositionGetDouble(POSITION_SL) == 0.) { // nueva posición
                
                if(!gb_Position_Today) { // es la 1-a posición hoy
                        // actualizar la bandera:
                        GlobalVariableSet( // en las variables globales del terminal
                                gs_Prefix + "Last_Position_Date",
                                TimeCurrent() — TimeCurrent() % 86400
                        );
                        gb_Position_Today = true; // en las variables globales del programa
                }
...

No hay otras modificaciones importantes en el código de la versión anterior del EA. En el anexo encontrará el código fuente de la nueva versión en su forma final.

 

Prueba de la estrategia a base de datos históricos

Los autores del sistema de trading, como la justificación de su viabilidad, muestran los patrones en los gráficos del fin del siglo pasado, pero nosotros necesitamos comprobar su relevancia en las condiciones del mercado contemporáneo. Para la prueba, he escogido el par más popular en el mercado de Forex, EURUSD, así como el más volátil, USDJPY, y uno de los metales, XAUUSD. He aumentado 10 veces los márgenes especificados por Raschke y Connors, ya que en aquel entonces se utilizaban las cotizaciones de cuatro dígitos, y yo probaba el EA con cinco dígitos. Como no había ninguna orientación de parte de los autores respecto a los parámetros del trailing, he escogido los que me parecieron más adecuados para el timeframe D1 y volatilidad del símbolo. Lo mismo se refiere al algoritmo del cálculo de Take Profit adicionado a las reglas originales, el coeficiente para su cálculo ha sido elegido aleatoriamente, sin una optimización profunda.

Gráfico del cambio del balance durante la prueba en el historial de cinco años de EURUSD con reglas originales (sin Take Profit):

EURUSD D1 5 años

Con las mismas configuraciones y adición de Take Profit:

EURUSD D1 5 años

Gráfico del cambio del balance durante la prueba de las reglas originales en el historial de cinco años de USDJPY:

USDJPY D1 5 años

El mismo símbolo y timeframe con las mismas configuraciones, pero con adición de Take Profit:

USDJPY D1 5 años

Las reglas originales en las cotizaciones diarias del oro para los últimos 4 años muestran el siguiente gráfico del cambio del balance:

XAUUSD D1 4 años

La información completa sobre cada prueba de las configuraciones del robot se puede encontrar en el archivo adjunto al artículo, contiene los informes completos de cada prueba. 


Conclusión

Las reglas programadas en el módulo de señal corresponden a la descripción del sistema de trading 80-20 en el libro Street Smarts: High Probability Short-Term Trading Strategies, escrito por Linda Raschke y Laurence Connor. Tiene lugar una pequeña extensión de las reglas de los autores. Estas herramientas (robot e indicador) deben ayudar a los interesados a sacar sus propias conclusiones sobre la relevancia de la Estrategia Comercial en las condiciones del mercado de hoy. En mi humilde opinión, requiere una importante modernización. En este artículo he intentado comentar detalladamente la creación del código del módulo de señal y el robot e indicador que lo utilizan, espero que eso sirva de ayuda para quienes decidan hacer dicha modernización. Aparte de la actualización de las reglas, se puede intentar seleccionar los símbolos que convengan mejor para este sistema, los mejores parámetros de la detección de la señal y el mejor seguimiento de posiciones. 


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

Archivos adjuntos |
Reports.zip (597.76 KB)
MQL5.zip (124.99 KB)
Zigzag universal Zigzag universal
El Zigzag es uno de los indicadores más populares entre los usuario de MetaTrader 5. En este artículo se han analizado las posibilidades de creación de diferentes versiones del Zigzag. Como resultado, obtenemos un indicador universal con amplias posibilidades para la ampliación de la funcionalidad, el cual es muy cómodo utilizar en el desarrollo de los Asesores Expertos y otros indicadores.
Sistema comercial 'Turtle Soup' y su modificación 'Turtle Soup Plus One' Sistema comercial 'Turtle Soup' y su modificación 'Turtle Soup Plus One'
En este artículo han sido formalizadas y programadas las reglas de las estrategias comerciales llamadas «Turtle Soup» y «Turtle Soup Plus One» del libro titulado «Street Smarts: High Probability Short-Term Trading Strategies», escrito por Linda Raschke y Laurence Connors. Las estrategias descritas en este libro recibieron bastante amplia acogida, pero es importante comprender que sus autores las ideaban basándose en el comportamiento del mercado de hace 15-20 años.
LifeHack para tráders: Informe comparativo de varias simulaciones LifeHack para tráders: Informe comparativo de varias simulaciones
En el artículo se analiza el inicio simultáneo de la simulación del asesor en cuatro símbolos diferentes. La comparación final de los cuatro informes de la simulación se realizará en un recuadro, como sucede al elegir los productos en una tienda online. Como bonus adicional, se muestran los gráficos de distribución creados de forma automática para cada símbolo.
Interfaces gráficas X: Campo de edición del texto, slider de imágenes y controles simples (build 5) Interfaces gráficas X: Campo de edición del texto, slider de imágenes y controles simples (build 5)
En este artículo vamos a analizar los controles nuevos, tales como: «Campo de edición del texto», «Slider de imágenes», así como los controles simples adicionales, «Etiqueta de texto» e «Imagen». La librería sigue desarrollándose, y además de la aparición de controles nuevos, se van mejorando los que ya han sido creados anteriormente.