Вы находитесь на странице: 1из 24

Desarrollando los Asesores Expertos multimódulo

Sergey Pavlov | 30 marzo, 2018

Introducción
Hoy en día existen varios enfoques en la programación, a saber: enfoque modular, orientado a objetos y
estructurado. En este artículo se trata de la programación modular con arreglo a los robots.

La programación modular es un método del desarrollo de programas que supone la división de un programa
en unos módulos independientes.

La regla principal de la programación modular es el principio «divide y vencerás». La conveniencia del uso de
la arquitectura modular consiste en la posibilidad de actualizar (sustituir) el módulo, sin necesidad de
modificar el resto del sistema.

La programación modular se basa en tres concepciones principales.

Principio de ocultación de la información de Parnas. El módulo sirve para ocultar la información y el


algoritmo de solución de una determinada tarea. Luego, se puede sustituir el módulo por otro.
Axioma de la modularidad de Cowan. El módulo es una unidad de programación independiente que
sirve para ejecutar unas determinadas funciones del programa.
Programación de ensamblaje de Cetin. Los módulos son «ladrillos» de los que se construye un
programa.

La única alternativa a la modularidad es un programa sólido. No es de todo conveniente: si surge la necesidad


de modificar o completar algunas funciones del programa, es necesario editar el código del Asesor Experto
(EA), y en mayoría de los casos, eso puede ser hecho por el autor del código, o por otro programador
experimentado. Y si además, el programa sólido está compilado, la edición de este producto puede ser
realizada exclusivamente por el propietario de los derechos del autor, y no es seguro que Usted podrá ponerse
de acuerdo con él. Por tanto, parece más conveniente tener la posibilidad de modificar las funciones
importantes del programa, sin hacer participar al autor, sino usando su propio potencial o servicios de otros
desarrolladores.

Fig. 1. Esquema abstracto del robot comercial modular


Principio de modularidad múltiple
La programación modular es un arte de dividir las tareas en un determinado número de subtareas
implementadas en forma de unos módulos separados (archivos). En caso general, el módulo programado es un
programa separado, o una unidad funcionalmente terminada y compilada de modo autónomo que se identifica
de alguna manera y se conecta con el módulo invocado. En otras palabras, el módulo es un fragmento del
programa funcionalmente terminado e implementado en forma de un archivo compilado que sirve para el uso
en otros programas.

Al determinar el conjunto de módulos que realizan las funciones de un determinado algoritmo, hay que tomar
en cuenta lo siguiente:

cada módulo se invoca por el módulo superior, y después de terminar su trabajo, devuelve la
administración al módulo que le ha llamado;
las decisiones principales en el algoritmo se toman en el nivel superior de la jerarquía;
los módulos son independientes unos de otros en cuanto a los datos;
los módulos no dependen de la prehistoria de las llamadas a ellos.

Pues, resumiendo todo lo dicho arriba, un programa modular es aquel programa en el que se puede modificar
cualquier parte de la estructura lógica, sin alterar otras partes.

Características principales del módulo:

una entrada y una salida — en la entrada, el módulo recibe un determinado conjunto de datos
originales, los procesa y devuelve un conjunto de datos resultantes, es decir, se implementa el principio
IPO (Input-Process-Output);
acabamiento funcional — para ejecutar una función separada, el módulo ejecuta una serie completa de
operaciones reglamentarias, la cual es suficiente para completar el procesamiento iniciado;
independencia lógica — el resultado del trabajo del módulo de programa depende exclusivamente de
datos originales, y no depende del trabajo de otros módulos;
comunicaciones informativas débiles con otros módulos — el intercambio de la información entre los
módulos de ser minimizado a ser posible.

En el lenguaje MQL5, se puede escribir tres tipos de programas: Asesor Experto, indicador o script. Para el
módulo principal, lo más conveniente será el formado del EA en el que será implementada la administración
de todos los módulos y se ubicarán las funciones comerciales.Mientras que el resto de los módulos podrá ser
implementado en forma de los indicadores. Es verdad que los indicadores convienen mejor para la formación
del módulo: los datos calculados según el algoritmo especificado pueden ser guardados en los búferes de
indicador, y en caso de necesidad, se puede pasarlos al EA multimódulo. El EA, en su lugar, puede usar o
ignorar estos datos, en función de la tarea planteada. Puede que en algunos proyectos sea conveniente usar
los EAs como módulos externos, pero en este caso, habrá que idear detenidamente el mecanismo del
intercambio de datos.

Seguramente Usted ha usado las tecnologías modulares en sus EAs más de una vez: por ejemplo,
los indicadores personalizados como módulos de generación y filtración de las señales comerciales.

Según mi opinión, la solución más racional es la siguiente: todo la funcionalidad básica está concentrada en el
módulo principal y no requiere la participación de módulos externos. En su lugar, los módulos externos son
necesarios para la adaptación a diferentes condiciones de mercado y el mejoramiento de la estrategia
comercial. El conjunto de las funciones del programa no se determina por el autor del código o estrategia,
sino por el trader como usuario del robot comercial. Cabe mencionar que nadie viola los derechos legales de
cada uno.

Módulo principal — Asesor Experto


El módulo principal —donde está concentrada la administración de todo el proyecto— es el más importante en
la jerarquía del EA. Es obligatorio que incluya las funciones comerciales sin las cuales cualquier estrategia
comercial carece del sentido.

Vamos a considerar el desarrollo del EA multimódulo a base del ejemplo concreto que ha sido cogido de
CodeBase. El EA original negocia con un lote fijo en el canal del indicador iBands con la reversión de la
posición en los bordes del canal. El EA es absolutamente autosuficiente y no requiere ningún programa
externo.

No todo el EA puede ser del tipo multimódulo, sino aquél que tenga prediseñada esta posibilidad.

¿Qué es lo que necesitamos añadir al código para convertirlo en un proyecto de varios módulos?
1. Declarar los módulos externos (indicadores), los cuales el usuario podrá aplicar en el futuro. 
2. Añadir la funcionalidad necesaria para su integración.
3. Preparar la documentación para los desarrolladores de los módulos externos (incluir la función de
generación de la documentación en un archivo separado). Así, para el diseño de los módulos externos,
será necesaria la información sobre la estructura de datos que pueden ser usados por el módulo
principal de forma correcta. Por ejemplo, en el ejemplo analizado, el módulo de la Gestión de capital
tiene que transmitir el tamaño del lote en el EA, y el módulo de seguimiento de la posición: la distancia
desde el precio actual en puntos.

Como resultado de las transformaciones, obtenemos un EA modular en el que podemos integrar de hasta siete
módulos externos.

Módulo №1 — módulo de la Gestión de capital. En la salida, nos da el tamaño del lote.


Módulo №2 — módulo del seguimiento de la posición y establecimiento del SL. En la salida, nos da la
distancia hasta el Stop Loss en puntos desde el precio de la apertura de la posición.
Módulo №3 — módulo del seguimiento de la posición y establecimiento del TP. En la salida, nos da la
distancia hasta el Take Profit en puntos desde el precio de la apertura de la posición.
Módulo №4 — módulo del seguimiento de la posición y establecimiento del Trailing Stop. En la salida,
nos da la distancia hasta el Stop Loss en puntos desde el precio actual.
Módulo №5 — módulo de la generación de señales comerciales. En la salida, nos da el valor de la señal.
Módulo №6 — módulo de la filtración de señales comerciales. En la salida, nos da el valor del filtro.
Módulo №7 — módulo del seguimiento de la posición y establecimiento del nivel del punto muerto
(break even). En la salida, nos da la distancia hasta el Stop Loss desde el precio de la apertura de la
posición.
 
Fig. 2. Función OnInit() e inicialización de módulos externos
Fig. 3. Función OnTick() y lectura de datos desde los módulos externos

Fig. 4. Función OnTrade() y lectura de datos desde los módulos externos


Fig. 5. Función de la generación de señales comerciales y lectura de datos desde los módulos externos
 

//****** project (module expert): test_module_exp.mq5


//+------------------------------------------------------------------+
//| The program code is generated Modular project generator |
//| Copyright 2010-2017, Sergey Pavlov (DC2008) |
//| http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link "http://www.mql5.com/ru/users/dc2008"
#property link "1.00"
#property link "Example of a multimodule expert: project TEST Main module."
#property link "The project uses 7 external modules."
//---
#include <Trade\Trade.mqh>
//---
MqlTick last_tick;
CTrade trade;
//---
input int e_bands_period=80; // Moving average period
int e_bands_shift=0; // shift
input double e_deviation=3.0; // Number of standard deviatio
input ENUM_APPLIED_PRICE e_applied_price=PRICE_CLOSE; // Price type
input bool on_module=false; // whether or not to use plug-
//---
double lot=0.01; // Fixed lot
double min_lot=0.01; // Minimum allowable lot
bool on_trade=false; // Trade function flag
//--- Variable for storing the indicator iBands handle
int handle_Bands;
//--- module 1
bool on_lot=false;
int handle_m1;
//--- module 2
bool on_SL=false;
int handle_m2;
//--- module 3
bool on_TP=false;
int handle_m3;
//--- module 4
bool on_Trail=false;
int handle_m4;
//--- module 5
bool on_signals=false;
int handle_m5;
//--- module 6
bool on_Filter=false;
int handle_m6;
//--- module 7
bool on_Breakeven=false;
int handle_m7;
//+------------------------------------------------------------------+
//| Structure of trading signals |
//+------------------------------------------------------------------+
struct sSignal
{
bool Buy; // Buy signal
bool Sell; // Sell signal
};
//+------------------------------------------------------------------+
//| Trading signals generator |
//+------------------------------------------------------------------+
sSignal Buy_or_Sell()
{
sSignal res={false,false};
//--- MODULE 5
if(on_signals)
{ // If there is an additional module
double buffer_m5[];
ArraySetAsSeries(buffer_m5,true);
if(CopyBuffer(handle_m5,0,0,1,buffer_m5)<0) return(res);
if(buffer_m5[0]<-1) res.Sell=true;
if(buffer_m5[0]>1) res.Buy=true;
}
//--- MODULE 6
if(on_Filter)
{ // If there is an additional module
double buffer_m6[];
ArraySetAsSeries(buffer_m6,true);
if(CopyBuffer(handle_m6,0,0,1,buffer_m6)<0) return(res);
lot=buffer_m6[0];
if(buffer_m6[0]<1) res.Buy=false;
if(buffer_m6[0]>-1) res.Sell=false;
}
//---
//--- Indicator buffers
double UpperBuffer[];
double LowerBuffer[];
double MiddleBuffer[];
ArraySetAsSeries(MiddleBuffer,true); CopyBuffer(handle_Bands,0,0,1,MiddleBuffer);
ArraySetAsSeries(UpperBuffer,true); CopyBuffer(handle_Bands,1,0,1,UpperBuffer);
ArraySetAsSeries(LowerBuffer,true); CopyBuffer(handle_Bands,2,0,1,LowerBuffer);
//--- Timeseries
double L[];
double H[];
ArraySetAsSeries(L,true); CopyLow(_Symbol,_Period,0,1,L);
ArraySetAsSeries(H,true); CopyHigh(_Symbol,_Period,0,1,H);
if(H[0]>UpperBuffer[0]&& L[0]>MiddleBuffer[0]) res.Sell=true;
if(L[0]<LowerBuffer[0] && H[0]<MiddleBuffer[0]) res.Buy=true;
//---
return(res);
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Create the indicator handle
handle_Bands=iBands(_Symbol,_Period,e_bands_period,e_bands_shift,e_deviation,e_appli
if(handle_Bands==INVALID_HANDLE)
return(INIT_FAILED);
else
on_trade=true;
if(on_module)
{
//--- MODULE 1
//--- check: whether there is an external module?
handle_m1=iCustom(NULL,0,"Market\\test_module_MM");
if(handle_m1!=INVALID_HANDLE)
on_lot=true;
//--- MODULE 2
//--- check: whether there is an external module?
handle_m2=iCustom(NULL,0,"Market\\test_module_SL");
if(handle_m2!=INVALID_HANDLE)
on_SL=true;
//--- MODULE 3
//--- check: whether there is an external moduleь?
handle_m3=iCustom(NULL,0,"Market\\test_module_TP");
if(handle_m3!=INVALID_HANDLE)
on_TP=true;
//--- MODULE 4
//--- check: whether there is an external module?
handle_m4=iCustom(NULL,0,"Market\\test_module_Trail");
if(handle_m4!=INVALID_HANDLE)
on_Trail=true;
//--- MODULE 5
//--- check: whether there is an external module?
handle_m5=iCustom(NULL,0,"Market\\test_module_signals");
if(handle_m5!=INVALID_HANDLE)
on_signals=true;
//--- MODULE 6
//--- check: whether there is an external module?
handle_m6=iCustom(NULL,0,"Market\\test_module_Filter");
if(handle_m6!=INVALID_HANDLE)
on_Filter=true;
//--- MODULE 7
//--- check: whether there is an external module?
handle_m7=iCustom(NULL,0,"Market\\test_module_Breakeven");
if(handle_m7!=INVALID_HANDLE)
on_Breakeven=true;
}
//--- Minimum allowable volume for trading operationsn
min_lot=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
double Equity=AccountInfoDouble(ACCOUNT_EQUITY);
//--- MODULE 1
if(on_lot)
{ // If there is an additional module
double buffer_m1[];
ArraySetAsSeries(buffer_m1,true);
if(CopyBuffer(handle_m1,0,0,1,buffer_m1)<0) return;
lot=buffer_m1[0];
}
//--- MODULE 4
if(on_Trail)
if(PositionSelect(_Symbol))
if(PositionGetDouble(POSITION_PROFIT)>0)
{ // If there is an additional module
double buffer_m4[];
ArraySetAsSeries(buffer_m4,true);
if(CopyBuffer(handle_m4,0,0,1,buffer_m4)<0) return;
double TR=buffer_m4[0];
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
if(PositionGetDouble(POSITION_PRICE_CURRENT)-TR*_Point>PositionGetDouble
{
double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)-TR*_Point;
if(price_SL>PositionGetDouble(POSITION_SL))
trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),0)
}
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
if(PositionGetDouble(POSITION_PRICE_CURRENT)+TR*_Point<PositionGetDouble
{
double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)+TR*_Point;
if(price_SL<PositionGetDouble(POSITION_SL) || PositionGetDouble(POSIT
trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),0)
}
}
//--- MODULE 7
if(on_Breakeven)
if(PositionSelect(_Symbol))
if(PositionGetDouble(POSITION_PROFIT)>0)
{ // If there is an additional module
double buffer_m7[];
ArraySetAsSeries(buffer_m7,true);
if(CopyBuffer(handle_m7,0,0,1,buffer_m7)<0) return;
double TRB=buffer_m7[0];
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
if(PositionGetDouble(POSITION_PRICE_CURRENT)-TRB*_Point>PositionGetDoubl
{
double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)-5*_Point;
if(price_SL>PositionGetDouble(POSITION_SL))
trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),Po
}
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
if(PositionGetDouble(POSITION_PRICE_CURRENT)+TRB*_Point<PositionGetDoubl
{
double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)+5*_Point;
if(price_SL<PositionGetDouble(POSITION_SL) || PositionGetDouble(POSIT
trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),Po
}
}
//---
if(lot<min_lot) lot=min_lot;
//---
if(on_trade)
{
sSignal signal=Buy_or_Sell();
//--- The value of the required and free margin
double margin,free_margin=AccountInfoDouble(ACCOUNT_MARGIN_FREE);
//--- BUY
if(signal.Buy)
{
if(!PositionSelect(_Symbol))
{
SymbolInfoTick(_Symbol,last_tick);
if(OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,NormalizeDouble(lot,2),last_tick.
if(margin<Equity)
trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,NormalizeDouble(lot,2),last
}
else
{
if(PositionGetDouble(POSITION_PROFIT)<0) return;
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
{
trade.PositionClose(_Symbol);
SymbolInfoTick(_Symbol,last_tick);
if(OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,NormalizeDouble(lot,2),last_ti
if(margin<Equity)
trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,NormalizeDouble(lot,2),l
}
}
}
//--- SELL
if(signal.Sell)
{
if(!PositionSelect(_Symbol))
{
SymbolInfoTick(_Symbol,last_tick);
if(OrderCalcMargin(ORDER_TYPE_SELL,_Symbol,NormalizeDouble(lot,2),last_tick
if(margin<Equity)
trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,NormalizeDouble(lot,2),las
}
else
{
if(PositionGetDouble(POSITION_PROFIT)<0) return;
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
{
trade.PositionClose(_Symbol);
SymbolInfoTick(_Symbol,last_tick);
if(OrderCalcMargin(ORDER_TYPE_SELL,_Symbol,NormalizeDouble(lot,2),last_t
if(margin<Equity)
trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,NormalizeDouble(lot,2),
}
}
}
}
}
//+------------------------------------------------------------------+
//| Trade function |
//+------------------------------------------------------------------+
void OnTrade()
{
if(on_SL && on_TP) // If there is an additional module
{
//--- MODULE 2
double buffer_m2[];
ArraySetAsSeries(buffer_m2,true);
if(CopyBuffer(handle_m2,0,0,1,buffer_m2)<0) return;
double SL=buffer_m2[0];
//--- MODULE 3
double buffer_m3[];
ArraySetAsSeries(buffer_m3,true);
if(CopyBuffer(handle_m3,0,0,1,buffer_m3)<0) return;
double TP=buffer_m3[0];
//--- Position modification
if(PositionSelect(_Symbol))
if(PositionGetDouble(POSITION_SL)==0)
{
//--- BUY
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
{
double priceTP=PositionGetDouble(POSITION_PRICE_OPEN)+TP*_Point;
double priceSL=PositionGetDouble(POSITION_PRICE_OPEN)-SL*_Point;
trade.PositionModify(_Symbol,NormalizeDouble(priceSL,Digits()),Normalize
}
//--- SELL
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
{
double priceTP=PositionGetDouble(POSITION_PRICE_OPEN)-TP*_Point;
double priceSL=PositionGetDouble(POSITION_PRICE_OPEN)+SL*_Point;
trade.PositionModify(_Symbol,NormalizeDouble(priceSL,Digits()),Normalize
}
}
}
}

Si resulta que la carpeta especificada por el programa no contiene módulos necesarios (archivos), entonces el
robot comercial va a usar la funcionalidad predefinida. De esta manera, la falta de los módulos externos no
afectará la capacidad funcional del EA de forma crítica.

Módulos más importantes


¿Cómo podemos convertir un programa sólido en un EA multimódulo? Un proyecto de varios módulos se
empieza con el análisis de la tarea general y la selección de los fragmentos aislados funcionalmente, que
luego pueden ser formados como módulos compilados. Además, es necesario seleccionar las funciones más
típicas, que pueden alterar considerablemente el trabajo del EA y se basan en diferentes algoritmos. No es un
secreto que en la mayoría de los EAs se aplican los mismos procedimientos: 

módulo de la Gestión de capital (riesgos);


módulo del seguimiento de la posición (SL y TP);
módulo del Trailing Stop;
módulo de la generación de señales comerciales;
módulo de filtración de señales comerciales.

Existen muchas opciones de implementar cada uno de los módulos mencionados. En este artículo,
mostraremos las soluciones más primitivas, puesto que ahora la propia tecnología de la programación modular
es más importante para nosotros que la funcionalidad multilínea.

Tecnología del diseño de módulos auxiliares


El módulo auxiliar (externo) es un indicador que ejecuta una determinada función y posiciona los datos de
salida en los búferes de indicadores. El módulo principal utiliza estos datos cuando se anecesario. De esta
manera, el EA se adapta a las exigencias del trader que utiliza esta estrategia comercial. El mismo EA original
puede ser remodificado para cada instrumento financiero o bróker determinado. Prácticamente, el trader
obtiene un constructor que permite diseñar una cantidad ilimitada de robots comerciales.
La programación es un proceso operoso. A pesar de la presencia de un componente creativo, incluye muchas
operaciones rutinarias y homologables, las cuales es mejor automatizar. La automatización (aparte de lo
demás) aumenta el rendimiento de la programación y reduce la cantidad de errores.
Al artículo se le adjunta un generador de los módulos que permite formar de hasta ocho archivos unidos en un
proyecto multimódulo apenas en unos segundos. Eso simplifica y acelera considerablemente el proceso del
desarrollo y composición (véase el video).

Vídeo 1. Panel del control del generador de proyectos multimódulo


En el panel del control se puede indicar qué módulos ha que generar para el proyecto en desarrollo. En
nuestro ejemplo, creamos el proyecto "test". Dependiendo de la combinación de módulos seleccionada, el
generador forma el código en el que no va a haber bloques y archivos innecesarios.
Los archivos generados se colocan en la carpeta "Files" (véase Fig. 2). El nombre del módulo principal
"test_module_exp.mq5" se compone del nombre del proyecto "test" y el prefijo "_module_exp.mq5". Es
necesario moverlo ala carpeta "Experts", y el resto de los archivos de los módulos externos,  en la carpeta
"Indicators\Market".

Fig. 6. Archivos generados del proyecto "test".


Después de eso, compilamos todos los archivos y empezamos el testeo del proyecto multimódulo.

Vídeo 2. Compilación de archivos generados del proyecto "test"


  
Para crear el mismo proyecto manualmente, necesitaremos más de una hora, y es evidente que habrá que
empezar con el módulo principal. Después de definir los módulos externos que luego se podrá incluir
en el proyecto, procedemos al diseño y programación. Lo importante es controlar los valores de los datos de
salida de los módulos auxiliares para el módulo principal. Puesto que los módulos son los indicadores, y los
búferes de indicadores contienen los valores exclusivamente del tipo real, entonces hay que prever la
conversión de las variables del tipo real al tipo correspondiente al algoritmo en el módulo principal.
Es deseable diseñar los módulos externos de tal manera que haya la posibilidad de invocarlos en el módulo
principal sin parámetros de entrada, es decir, por defecto. Este mecanismo de la llamada simplifica el diseño
del sistema de la gestión de datos externos.
Vamos a considerar en detalle los módulos externos más requeridos en las estrategias comerciales.

Módulo 1: módulo de la Gestión de capital


Este módulo externo calcula el volumen del lote para la apertura de la orden del turno. A continuación, puede
ver la versión más simple del cálculo del volumen comercial (en por cientos del dinero disponible en el
depósito):

//****** project (module MM): test_module_MM_ind.mq5


//+------------------------------------------------------------------+
//| The program code is generated Modular project generator |
//| Copyright 2010-2017, Sergey Pavlov (DC2008) |
//| http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link "http://www.mql5.com/ru/users/dc2008"
#property link "1.00"
#property link "Example of a multimodule expert: project test module MM"
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots 1
//---
input double lot_perc=0.1; // Percentage of Equity value
double Buffer1[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
ArraySetAsSeries(Buffer1,true);
SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
return(INIT_SUCCEEDED);
};
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,
const int prev_calculated,
const datetime& time[],
const double& open[],
const double& high[],
const double& low[],
const double& close[],
const long& tick_volume[],
const long& volume[],
const int &spread[])
{
double Equity=AccountInfoDouble(ACCOUNT_EQUITY);
//--- calculation of the lot of equity
Buffer1[0]=NormalizeDouble(Equity*lot_perc/1000.0,2); // Lot size determination func
if(Buffer1[0]<0.01) Buffer1[0]=0.01;
return(rates_total);
};

Si llamamos a este módulo por defecto (sin especificar los valores de parámetros de entrada), el módulo
principal recibirá el tamaño del lote permitido para realizar la transacción en el volumen 0,1% de fondos
disponibles. Es el ejemplo de la llamada a este módulo desde el programa principal:

//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
double Equity=AccountInfoDouble(ACCOUNT_EQUITY);
//--- MODULE 1
if(on_lot)
{ // If there is an additional module
double buffer_m1[];
ArraySetAsSeries(buffer_m1,true);
if(CopyBuffer(handle_m1,0,0,1,buffer_m1)<0) return;
lot=buffer_m1[0];
}

...

Ejemplo 2: módulo del seguimiento de la posición (SL, TP y trailing)


Colocación del Stop Loss (SL) y Take Profit (TP) es uno de los modos del seguimiento de la posición abierta.
Puesto que en diferentes estrategias comerciales se usan diferentes combinaciones del cálculo y colocación
del Sl y TP, se impone la variante con la división en dos módulos: para SL y TP por separado. Pero si decidimos
reunir SLy TP en el mismo módulo, habrá que colocar sus valores en búferes de indicadores diferentes.
Módulo de establecimiento de SL:
//****** project (module SL): test_module_SL_ind.mq5
//+------------------------------------------------------------------+
//| The program code is generated Modular project generator |
//| Copyright 2010-2017, Sergey Pavlov (DC2008) |
//| http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link "http://www.mql5.com/ru/users/dc2008"
#property link "1.00"
#property link "Example of a multimodule expert: project test module SL"
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots 1
//---
double Buffer1[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
ArraySetAsSeries(Buffer1,true);
SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
return(INIT_SUCCEEDED);
};
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,
const int prev_calculated,
const datetime& time[],
const double& open[],
const double& high[],
const double& low[],
const double& close[],
const long& tick_volume[],
const long& volume[],
const int &spread[])
{
double SL=100; // SL in points
//--- calculation of the SL
Buffer1[0]=SL;
return(rates_total);
};

Módulo de establecimiento de TP:

//****** project (module TP): test_module_TP_ind.mq5


//+------------------------------------------------------------------+
//| The program code is generated Modular project generator |
//| Copyright 2010-2017, Sergey Pavlov (DC2008) |
//| http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link "http://www.mql5.com/ru/users/dc2008"
#property link "1.00"
#property link "Example of a multimodule expert: project test module TP"
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots 1
//---
double Buffer1[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
ArraySetAsSeries(Buffer1,true);
SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
return(INIT_SUCCEEDED);
};
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,
const int prev_calculated,
const datetime& time[],
const double& open[],
const double& high[],
const double& low[],
const double& close[],
const long& tick_volume[],
const long& volume[],
const int &spread[])
{
double TP=100; // TP in points
//--- calculation of the TP
Buffer1[0]=TP;
return(rates_total);
};

En estos códigos, se muestra una variante más evidente del el cálculo de los valores de SL y TP (en puntos).
Mejor dicho, no se calcula, sino se establece con una constante. Además, los valores se indican directamente
en el programa, en vez de usar los parámetros de entrada. Eso ha sido hecho con el fin de demostrar la
variante de la implementación de los módulos externos sin los datos de entrada. Cualquier programador
principiante puede escribir este código sin problema alguno.
Yo recomiendo ubicar la llamada a los módulos considerados en la función OnTrade. Tendremos
aproximadamente lo siguiente:

//+------------------------------------------------------------------+
//| Trade function |
//+------------------------------------------------------------------+
void OnTrade()
{
if(on_SL && on_TP) // If there is an additional module
{
//--- MODULE 2
double buffer_m2[];
ArraySetAsSeries(buffer_m2,true);
if(CopyBuffer(handle_m2,0,0,1,buffer_m2)<0) return;
double SL=buffer_m2[0];
//--- MODULE 3
double buffer_m3[];
ArraySetAsSeries(buffer_m3,true);
if(CopyBuffer(handle_m3,0,0,1,buffer_m3)<0) return;
double TP=buffer_m3[0];
//--- Position modification
if(PositionSelect(_Symbol))
if(PositionGetDouble(POSITION_SL)==0)
{
//--- BUY
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
{
double priceTP=PositionGetDouble(POSITION_PRICE_OPEN)+TP*_Point;
double priceSL=PositionGetDouble(POSITION_PRICE_OPEN)-SL*_Point;
trade.PositionModify(_Symbol,NormalizeDouble(priceSL,Digits()),Normalize
}
//--- SELL
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
{
double priceTP=PositionGetDouble(POSITION_PRICE_OPEN)-TP*_Point;
double priceSL=PositionGetDouble(POSITION_PRICE_OPEN)+SL*_Point;
trade.PositionModify(_Symbol,NormalizeDouble(priceSL,Digits()),Normalize
}
}
}
}

Aparte de los valores SL y TP estáticos establecidos inmediatamente de la apertura de la posición , a menudo


se usa el Trailing Stop o SL flotante. En la mayoría de los casos, lo colocan después de que la posición se haga
rentable. Vamos a examinar la variante de la implementación más evidente: establecemos la distancia de SL
del precio actual en puntos.

//****** project (module Trail): test_module_Trail_ind.mq5


//+------------------------------------------------------------------+
//| The program code is generated Modular project generator |
//| Copyright 2010-2017, Sergey Pavlov (DC2008) |
//| http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link "http://www.mql5.com/ru/users/dc2008"
#property link "1.00"
#property link "Example of a multimodule expert: project test module Trail"
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots 1
//---
double Buffer1[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
ArraySetAsSeries(Buffer1,true);
SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
return(INIT_SUCCEEDED);
};
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,
const int prev_calculated,
const datetime& time[],
const double& open[],
const double& high[],
const double& low[],
const double& close[],
const long& tick_volume[],
const long& volume[],
const int &spread[])
{
double TR=50; // Trail in points
//--- calculation of Trail
Buffer1[0]=TR;
return(rates_total);
};

Aquí, igual como en los códigos anteriores para SL y TP, la distancia para el cálculo del Trailing Stop se
establece usando una constante para simplificar el programa y su legibilidad.
La llamada al módulo del Trailing Stop debe realizarse en la función OnTick porque el precio actual se cambia
en cada tick, y es necesario controlar el nivel del Stop constantemente. Y el módulo principal decide si hay
que cambiarlo o no.Al obtener el valor de la distancia en puntos, el EA modifica la posición y mueve el nivel
del SL en la dirección del crecimiento del beneficio.

//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{

...

//--- MODULE 4
if(on_Trail)
if(PositionSelect(_Symbol))
if(PositionGetDouble(POSITION_PROFIT)>0)
{ // If there is an additional module
double buffer_m4[];
ArraySetAsSeries(buffer_m4,true);
if(CopyBuffer(handle_m4,0,0,1,buffer_m4)<0) return;
double TR=buffer_m4[0];
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
if(PositionGetDouble(POSITION_PRICE_CURRENT)-TR*_Point>PositionGetDouble
{
double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)-TR*_Point;
if(price_SL>PositionGetDouble(POSITION_SL))
trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),0)
}
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
if(PositionGetDouble(POSITION_PRICE_CURRENT)+TR*_Point<PositionGetDouble
{
double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)+TR*_Point;
if(price_SL<PositionGetDouble(POSITION_SL) || PositionGetDouble(POSIT
trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),0)
}
}

...

Existe otro método del seguimiento de la posición, es la colocación del SL en el punto muerto (break even).
Cuando SL dispara, la posición se cierra con el resultado nulo o con el beneficio establecido previamente. El
módulo puede tener aproximadamente el aspecto siguiente:

//****** project (module Breakeven): test_module_Breakeven_ind.mq5


//+------------------------------------------------------------------+
//| The program code is generated Modular project generator |
//| Copyright 2010-2017, Sergey Pavlov (DC2008) |
//| http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link "http://www.mql5.com/ru/users/dc2008"
#property link "1.00"
#property link "Example of a multimodule expert: project test module Breakeven"
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots 1
//---
double Buffer1[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
//+ +
int OnInit()
{
//---
ArraySetAsSeries(Buffer1,true);
SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
return(INIT_SUCCEEDED);
};
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,
const int prev_calculated,
const datetime& time[],
const double& open[],
const double& high[],
const double& low[],
const double& close[],
const long& tick_volume[],
const long& volume[],
const int &spread[])
{
double Breakeven=100; // Breakeven in points
//--- calculation of the Breakeven
Buffer1[0]=Breakeven;
return(rates_total);
};

En este módulo se establece la distancia entre el precio actual y el precio de la apertura en puntos para


colocar SL en el punto muerto. La llamada al módulo del break even también debe ubicarse en la función
OnTick.

//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{

...

//--- MODULE 7
if(on_Breakeven)
if(PositionSelect(_Symbol))
if(PositionGetDouble(POSITION_PROFIT)>0)
{ // If there is an additional module
double buffer_m7[];
ArraySetAsSeries(buffer_m7,true);
if(CopyBuffer(handle_m7,0,0,1,buffer_m7)<0) return;
double TRB=buffer_m7[0];
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
if(PositionGetDouble(POSITION_PRICE_CURRENT)-TRB*_Point>PositionGetDoubl
{
double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)-5*_Point;
if(price_SL>PositionGetDouble(POSITION_SL))
trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),Po
}
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
if(PositionGetDouble(POSITION_PRICE_CURRENT)+TRB*_Point<PositionGetDoubl
{
double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)+5*_Point;
if(price_SL<PositionGetDouble(POSITION_SL) || PositionGetDouble(POSIT
trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),Po
}
}

...

}
Ejemplo 3: módulo de la generación de señales comerciales
Puede ser que sea el módulo más complicado en cuanto a la implementación. Debe enviar las señales para
realizar las operaciones comerciales: colocación de órdenes, cierre de posiciones, etc. La complejidad de su
diseño consiste en el hecho de que casi todos los indicadores deben ser adaptados a las condiciones
comerciales. Prácticamente, no existen indicadores con los mismos parámetros de entrada que generan las
señales de trabajo para diferentes instrumentos financieros.
Es obvio que el programa principal no debe configurar los módulos de señal por sí mismo, porque se puede
crear tantos módulos que el trabajo del proyecto modular será paralizado. Por tanto, los indicadores que
generan las señales comerciales tienen que estar preparados previamente antes de ser conectados al proyecto
general. Hablaremos de eso más tarde en el apartado dedicado a la optimización de los EAs multimódulos.
Ahora, echamos un vistazo al código del módulo de señales comerciales:

//****** project (module signals): test_module_signals_ind.mq5


//+------------------------------------------------------------------+
//| The program code is generated Modular project generator |
//| Copyright 2010-2017, Sergey Pavlov (DC2008) |
//| http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link "http://www.mql5.com/ru/users/dc2008"
#property link "1.00"
#property link "Example of a multimodule expert: project test module Trading signa
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots 1
//---
double Buffer1[];
//--- Variable for storing the indicator iBands handle
int handle_Bands;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
handle_Bands=iBands(_Symbol,_Period,20,0,3.5,PRICE_CLOSE);
//---
ArraySetAsSeries(Buffer1,true);
SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
return(INIT_SUCCEEDED);
};
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,
const int prev_calculated,
const datetime& time[],
const double& open[],
const double& high[],
const double& low[],
const double& close[],
const long& tick_volume[],
const long& volume[],
const int &spread[])
{
double signal=0.0;
ArraySetAsSeries(high,true);
ArraySetAsSeries(low,true);
//--- Indicator buffers
double UpperBuffer[];
double LowerBuffer[];
ArraySetAsSeries(UpperBuffer,true); CopyBuffer(handle_Bands,1,0,1,UpperBuffer);
ArraySetAsSeries(LowerBuffer,true); CopyBuffer(handle_Bands,2,0,1,LowerBuffer);
//--- calculation of the Trading signals
if(high[0]>UpperBuffer[0]) signal=-2.0;
if(low[0]<LowerBuffer[0]) signal=2.0;
Buffer1[0]=signal;
return(rates_total);
};

En el búfer de indicadores del módulo de señales, se escriben los siguientes valores:

2.0 — si se ha formado la señal BUY;


0.0 — si no hay señales comerciales;
-2.0 — si se ha formado la señal SELL;

Es mejor usar los valores del módulo de señales obtenidos en una función especial del módulo principal, por
ejemplo así:

//+------------------------------------------------------------------+
//| Trading signals generator |
//+------------------------------------------------------------------+
sSignal Buy_or_Sell()
{
sSignal res={false,false};
//--- MODULE 5
if(on_signals)
{ // If there is an additional module
double buffer_m5[];
ArraySetAsSeries(buffer_m5,true);
if(CopyBuffer(handle_m5,0,0,1,buffer_m5)<0) return(res);
if(buffer_m5[0]<-1) res.Sell=true;
if(buffer_m5[0]>1) res.Buy=true;
}
//--- MODULE 6
if(on_Filter)
{ // If there is an additional module
double buffer_m6[];
ArraySetAsSeries(buffer_m6,true);
if(CopyBuffer(handle_m6,0,0,1,buffer_m6)<0) return(res);
lot=buffer_m6[0];
if(buffer_m6[0]<1) res.Buy=false;
if(buffer_m6[0]>-1) res.Sell=false;
}
//---
//--- Indicator buffers
double UpperBuffer[];
double LowerBuffer[];
double MiddleBuffer[];
ArraySetAsSeries(MiddleBuffer,true); CopyBuffer(handle_Bands,0,0,1,MiddleBuffer);
ArraySetAsSeries(UpperBuffer,true); CopyBuffer(handle_Bands,1,0,1,UpperBuffer);
ArraySetAsSeries(LowerBuffer,true); CopyBuffer(handle_Bands,2,0,1,LowerBuffer);
//--- Timeseries
double L[];
double H[];
ArraySetAsSeries(L,true); CopyLow(_Symbol,_Period,0,1,L);
ArraySetAsSeries(H,true); CopyHigh(_Symbol,_Period,0,1,H);
if(H[0]>UpperBuffer[0]&& L[0]>MiddleBuffer[0]) res.Sell=true;
if(L[0]<LowerBuffer[0] && H[0]<MiddleBuffer[0]) res.Buy=true;
//---
return(res);
}

Existe una enorme cantidad de estrategias comerciales, y cada una de ellas tiene sus propias señales. Por eso
, es necesario organizar el trabajo del módulo de señales comerciales de tal manera que se ajusten a la
estrategia establecida. La documentación del EA debe incluir la estrategia comercial descrita, para que los
desarrolladores de los módulos de señal actúen de acuerdo con los requerimientos técnicos del proyecto
modular.
Ejemplo 4: módulo de filtración de señales
Con el fin de aumentar la rentabilidad de robots comerciales, a menudo se usan los filtros de señales
comerciales. Puede tratarse, por ejemplo, del control de la tendencia, tiempo del trading, noticias,
indicadores de señal adicionales, etc.

//****** project (module Filter): test_module_Filter_ind.mq5


//+------------------------------------------------------------------+
//| The program code is generated Modular project generator |
//| Copyright 2010-2017, Sergey Pavlov (DC2008) |
//| http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link "http://www.mql5.com/ru/users/dc2008"
#property link "1.00"
#property link "Example of a multimodule expert: project test module Filter"
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots 1
//---
double Buffer1[];
//--- Variable for storing the indicator iBands handle
int handle_Bands;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
handle_Bands=iBands(_Symbol,_Period,35,0,4.1,PRICE_CLOSE);
//---
ArraySetAsSeries(Buffer1,true);
SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
return(INIT_SUCCEEDED);
};
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,
const int prev_calculated,
const datetime& time[],
const double& open[],
const double& high[],
const double& low[],
const double& close[],
const long& tick_volume[],
const long& volume[],
const int &spread[])
{
double filtr=0.0;
ArraySetAsSeries(high,true);
ArraySetAsSeries(low,true);
//--- Indicator buffers
double MiddleBuffer[];
ArraySetAsSeries(MiddleBuffer,true); CopyBuffer(handle_Bands,0,0,1,MiddleBuffer);
//--- calculation of the filter
if(high[0]<MiddleBuffer[0]) filtr=2.0;
if(low[0]>MiddleBuffer[0]) filtr=-2.0;
Buffer1[0]=filtr;
return(rates_total);
};

Pues bien, hemos considerado las opciones de implementación de los módulos externos y el principio de su
integración en el EA modular.

Optimización de EAs multimódulo


La optimización de los EAs multimódulo, probablemente, sea una de las cuestiones fundamentales. Es verdad,
¿cómo se puede optimizar los parámetros de entrada de los módulos externos en el Probador de Estrategias? Si
no están escritos en el módulo principal, entonces no hay ninguna manera de hacerlo (o casi ninguna). Se
puede establecer discretamente los datos de entrada de los módulos externos y luego testear el EA. Pero este
trabajo tan minucioso, y probablemente irracional, no nos conviene. ¿Entonces, qué podemos hacer?
Ésta es una de las posibles opciones: usar los indicadores con optimización automática como módulos
externos. Existen muchos artículos y ejemplos sobre la automatización automática. Haré mi aportación en el
desarrollo de este tema. Vamos a prestar las ideas del artículo «Prueba visual de la rentabilidad de los
indicadores y alertas». Como precio de la ejecución de una transacción virtual, su autor propone usar el valor
máximo de la vela para abrir la posición BUY, y el valor mínimo, para SELL. Por tanto, se seleccionan las
peores condiciones comerciales, y usando este enfoque en los precios, se realiza la optimización de los
parámetros de entrada. Se supone que con estos valores óptimos obtenidos, el resultado no será peor en el
trading real (en el mismo intervalo de datos históricos). Mientras que en el trading real, es imposible
garantizar el beneficio después de cualquier optimización.
La estrategia de nuestro EA se basa en el trading dentro del canal del indicador Bollinger, con la reversión de
las posiciones en sus bandas. Vamos a reemplazar este indicador y construir el canal a base del indicador
Envelope: desde el indicador de la media móvil formamos las bandas equidistantes de la MA a una distancia
fija. El nuevo indicador de señal va a optimizarse automáticamente antes de ser usado. Como parámetros de
entrada, se usarán los valores óptimos con los cuales ha sido mostrado el beneficio máximo. Hay dos
parámetros para la optimización: período de la MA y la distancia entre las bandas y la media móvil.
Algoritmo de la creación del indicador de señal con la función de optimización automática:

1. Determinamos los parámetros y criterios de la optimización. En nuestro caso, los parámetros serán el
período de la MA y la distancia del desplazamiento de las bandas, y el criterio, el beneficio máximo.
2. Creamos el bloque de optimización en el indicador. En el ejemplo propuesto, se realiza el repaso
completo de los datos de entrada en el intervalo establecido con un paso fijo. El período de la media
móvil es de 10 a 100, con el paso de 10. Para el desplazamiento, los valores se repasan en el rango de 1
000 a 10 000, con el paso de 1 000.

//+------------------------------------------------------------------+
//| Copyright 2018, Sergey Pavlov (DC2008) |
//| http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, Sergey Pavlov (DC2008)"
#property link "http://www.mql5.com/ru/users/dc2008"
#property link "1.00"
#property link "Example of a multimodule expert: project test module Trading signa
//---
#include <MovingAverages.mqh>
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots 1
//+------------------------------------------------------------------+
//| Estructura de los resultados de optimización |
//+------------------------------------------------------------------+
struct Opt
{
int var1; // valor óptimo del parámetro 1
int var2; // valor óptimo del parámetro 2
double profit; // beneficio
};
//---
double Buffer1[];
bool optimum=false;
Opt test={NULL,NULL,NULL};
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
ArraySetAsSeries(Buffer1,true);
SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
optimum=false;
return(INIT_SUCCEEDED);
};
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
double signal=0.0;
Buffer1[0]=signal;
//--- optimization of input parameters
if(!optimum)
{
ArraySetAsSeries(close,false);
int count=rates_total;
int total=0;
int total_profit=0;
for(int d=1000;d<10001;d+=1000)
for(int j=10;j<101;j+=10)
{
double shift=d*_Point;
bool open_buy=false;
bool open_sell=false;
double price_buy=0;
double price_sell=0;
double profit=0;
int order=0;
for(int i=j+1;i<count;i++)
{
double ma=SimpleMA(i,j,close);
double sell=ma+shift;
double buy=ma-shift;
//--- BUY
if(buy>close[i] && !open_buy)
{
price_buy=high[i]+spread[i]*_Point;
if(order==0) profit=0;
else profit+=price_sell-price_buy;
order++;
open_buy=true;
open_sell=false;
}
//--- SELL
if(sell<close[i] && !open_sell)
{
price_sell=low[i]-spread[i]*_Point;
if(order==0) profit=0;
else profit+=price_sell-price_buy;
order++;
open_sell=true;
open_buy=false;
}
//---
}
if(profit>0)
if(profit>test.profit)
{
test.var1=j;
test.var2=d;
test.profit=profit;
total_profit++;
}
//---
Comment("Optimización de los parámetros de entrada..."," repasos=",total,"
total++;
}
//---
Print(" Optimización terminada: ",test.var1," ",test.var2);
Comment("Optimización terminada");
optimum=true;
}
//---
if(optimum)
if(test.profit>0)
{
ArraySetAsSeries(close,true);
double ma=SimpleMA(0,test.var1,close);
double sell=ma+test.var2*_Period;
double buy=ma-test.var2*_Period;
//--- calculation of the Trading signals
if(buy>close[0]) signal=2.0;
if(sell<close[0]) signal=-2.0;
}
//--- Indicator buffers
Buffer1[0]=signal;
return(rates_total);
};

Claro que la optimización requerirá algo de tiempo. Durante este período, el EA no podrá realizar operaciones
comerciales. Si el robot modular trabaja 24 horas al día, el retardo debido a la optimización automática no
debe afectar considerablemente el tiempo total del trading.

Conclusión
1. Escribir un Asesor Experto multimódulo usando MQL5 es posible, y en algunas ocasiones, es
conveniente, e incluso, beneficioso.
2. En el presente artículo, se muestra una concepción primitiva del trabajo del robót con módulos
externos. No obstante, la tecnología de la programación modular permite crear unos proyectos bastante
complejos, para el diseño de los cuales se puede acudir a los desarrolladores ajenos. Al crear los
módulos, ellos son libres de no descubrir el código, protegiendo de esta manera sus derechos de autor
de los algoritmos.
3. La cuestión de la optimización de los proyectos modulares es un cabo suelto. La optimización
automática de los indicadores de señal, que se usan como módulos de señal o filtros, es un tema que
requiere su desarrollo.

Nota: El archivo adjunto al artículo permite generar los códigos fuente del proyecto modular en la
configuración necesaria.

Вам также может понравиться