Академический Документы
Профессиональный Документы
Культура Документы
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.
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.
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.
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.
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.
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.
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];
}
...
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
}
}
}
}
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:
//+------------------------------------------------------------------+
//| 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:
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.
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.
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.