El enfoque orientado a objetos en MQL
Introducción
Este artículo puede resultar muy interesante para los programadores que sean principiantes o expertos y que trabajan en el entorno MQL. Me gustaría también que lo leyeran los desarrolladores del entorno MQL, ya que las preguntas que se plantean aquí pueden convertirse en proyectos para las futuras implementaciones de MetaTrader y MQL. En cierto modo, se pueden encontrar en este artículo algunos conceptos parecidos a la plantilla de un Asesor Experto universal y el envío de señales de trading en un Asesor Experto universal
Por lo tanto,
En mi opinión como programador, una de las desventajas de MQL es la ausencia del enfoque orientado a objetos en la construcción del modelo del sistema de trading. Los desarrolladores de MQL nos ofrecen dos soluciones: usar las llamadas a funciones externas o usar el parámetro MAGIC para identificar la pertenencia de la orden.
De hecho, la identificación de órdenes no es necesaria cuando opera un sólo sistema en una sola cuenta. Pero no podemos hacer nada sin MAGIC cuando disponemos de un programa capaz de conectar varios sistemas de trading automatizado a una sola cuenta. Incluso cuando se llama a las funciones externas, necesitamos identificar las órdenes. Podemos construir una matriz OrderTicket e identificar únicamente la matriz que pertenece a un sólo sistema de trading, pero teniendo en cuenta que en algunas compañías de borkerage el ticket de la orden cambia con el swap (en realidad se cierra uno y se abre otro). Es por ello que que no podemos hacer nada sin usar MAGIC.
Así que mientras los desarrolladores están ocupados en mejorar el lenguaje MQL haciéndolo más flexible, vamos a tratar de implementar el enfoque orientado a objetos en la construcción de un modelo de trading.
Este es un sistema de trading según mi modelo orientado a objetos. No es universal, pero hasta el momento no veo otros enfoques.
Así que vamos a analizar este modelo.
A). Sistema de señales (SS)
El objeto de este módulo procesa e interpreta las cotizaciones entrantes. En general, el "objeto" del sistema de señales es un conjunto de indicadores, por ejemplo, promedios móviles. Como resultado del procesamiento de las cotizaciones y de los valores de los indicadores, el "objeto" (o semáforo) genera señales para entrar/salir o modificar órdenes, etc.
El semáforo genera la señal y la envía a otro objeto desde el módulo Entrada/Salida (ES).
Configurar el semáforo en MQL es bastante sencillo.
1. Establecer un identificador global mediante #define.
En lugar de definir números consecutivos del tipo 1, 2, 3, 4..., es mejor utilizar 5-10, de modo que podamos usar una señal para varios procesos en el Asesor Experto (ver el segundo módulo).
//+------------------------------------------------------------------+ //| Signals.mqh | //| Copyright © 2007 Сергеев Алексей | //| los@we.kherson.ua | //+------------------------------------------------------------------+ #property copyright "Copyright © 2007, Сергеев Алексей " #property link "mailto: los@we.kherson.ua" #property library #define BLACKSYS 10 #define BORCHAN 20 #define ELDER 80 #define ENVELOP 90
2. Después, en la función global de este módulo tenemos que habilitar su procesador.
int CheckSignal(bool bEntry, int SignalID) { switch (SignalID) { case BLACKSYS: return (BlackSys(bEntry)); break; case BORCHAN: return (BorChan(bEntry)); break; case ELDER: return (Elder(bEntry)); break; case ENVELOP: return (Envelop(bEntry)); break; default: return (-1); } }
3. En el último paso se describen las funciones.
Este es un ejemplo de un procesador de señales de un objeto que hereda las características del indicador Envelope.
int Envelope(bool bEntry) { int MA=21; double Deviation=0.6; int Mode=MODE_SMA;//0-sma, 1-ema, 2-smma, 3-lwma int Price=PRICE_CLOSE;//0-close, 1-open, 2-high, 3-low, 4-median, 5-typic, 6-wieight double envH0, envL0, m0; double envH1, envL1, m1; envH0=iEnvelopes(NULL, 0, MA, Mode, 0, Price, Deviation, MODE_UPPER, 0); envL0=iEnvelopes(NULL, 0, MA, Mode, 0, Price, Deviation, MODE_LOWER, 0); envH1=iEnvelopes(NULL, 0, MA, Mode, 0, Price, Deviation, MODE_UPPER, 1); envL1=iEnvelopes(NULL, 0, MA, Mode, 0, Price, Deviation, MODE_LOWER, 1); m0 = (Low[0]+High[0])/2; m1 = (Low[1]+High[1])/2; //----- condition for operation execution if (bEntry) //for opening { if (envH0<m0 && envH1<m1) return (OP_SELL); if (envL0>m0 && envL1>m1) return (OP_BUY); } else //for closing { if (envH0<m0 && envH1<m1) return (OP_BUY); if (envL0>m0 && envL1>m1) return (OP_SELL); } return (-1); //no signal }
De este modo obtenemos un módulo que contiene distintas "señales-objetos".
B). Los objetos del bloque ES hacen un mínimo de tareas:
En primer lugar, interactúan con las "señales-objetos"; obsérvelos. El ciclo de vida y la interacción son los siguientes:
Comprobar el semáforo -> si hay cualquier señal positiva, abrir/cerrar/modificar posiciones -> pasar el control a los objetos en el módulo SP (soporte de posición).
Todos los objetos del módulo ES llevan el prefijo Process..., que determina su comportamiento de manera más exacta.
Por ejemplo:
ProcessAvgLim // - the object processes signals with opening pending limit-orders and positions averaging ProcessTurn // - the object processes signals with position turning
Cada parte de un sistema de trading debe tener sus propias características (lo sabemos todos y lo usamos en nuestros modelos), tales como el beneficio, el stop-loss y la gestión del dinero, además de otros parámetros implementados en distintas opciones de trailing, etc.
Al implementar estas características he probado con varias opciones y la más satisfactoria en mi opinión es la creación de una matriz de dos dimensiones. Esta es su descripción:
double SysPar[nSignal][11]; #define _TP 0 // Profit #define _NullTP 1 // profit level, after which we set into losslessness #define _NullTP2 2 // profit level, after which we set into losslessness #define _TS 3 // distance of the trailing stop #define _NullSL 4 // level, after achieving which the expected profit is transfered into opening point #define _SL 5 // level, after achieving which the expected profit is transfered into opening point #define _dSL 6 // the initial step upon the opening level of the next order in the position support #define _dStep 7 // The step is increased in .. times upon the level of the next opening #define _dLot 8 // In how many times (as compared to the last one) we increase the lot on the next one #define _nLot 9 // In how many times (as compared to the initial one) we increase the lot on the next one string SysParName[nSignal];
donde nSignal es el identificador de las "señales-objetos".
Por ejemplo:
SysPar[ENVELOP][_TS] = 40.0; // distance of the trailing stop SysPar[ENVELOP][_NullSL] = 20.0;// level, after achieving which the expected profit is transfered into opening point SysPar[ENVELOP][_SL] = 70; // changing stop-loss
Puede incrementar el número de parámetros establecidos en la estructura de esta matriz a su conveniencia.
Así que después de ajustar los parámetros llamamos a la función de procesamiento del semáforo. En otras palabras, interactuamos con el sistema de señales. Esto se hace en mi función favorita start()
void start()
{
ProcessAvgLim(ENVELOP, ENVELOP, Green, Red);
… …
Como se puede observar en el esquema, tenemos 4 semáforos y 3 observadores en el sistema de trading. Cada semáforo se basa en su propia interpretación de las cotizaciones.
Por ejemplo, el semáforo 1 envía señales al analizar el indicador MACD. Después de recibir estas señales, el observador 1 abre las órdenes en un esquema sencillo ProcessSimple.
Los observadores 2 y 3 son más complicados. Cada uno controla las señales de dos semáforos. Por lo tanto, el enfoque de apertura de órdenes es distinto.
Así que después de establecer los parámetros del observador y conectarlos a un semáforo, tenemos que controlar y mantener las posiciones abiertas.
Los objetos del módulo Soporte de posición (SP) son "responsables" del estado de las órdenes abiertas.
C). En mi opinión, el bloque SP es el más interesante y no menos importante que los semáforos.
Se implementan aquí distintas opciones de trailing, se abren las órdenes pendientes, se implementa también el soporte y la resistencia de las posiciones, el control del beneficio y las perdidas y así sucesivamente. Este SP debe responder adecuadamente a las señales de ES sobre la salida del mercado en el caso de posiciones perdedoras con un mínimo de pérdidas.
Hay una interesante librería de trailing en este sitio Librería de funciones y Asesores Expertos para el trailing / Yury Dzyuban. Se pueden añadir todos los tipos de trailing al sistema fácilmente.
Para facilitar la percepción, todos los objetos de soporte empiezan con el prefijo Trailing...
Su esquema es el siguiente:
Se lleva a cabo el envío del control de llamadas entre el observador y el trailing en la misma función start()
void start()
{
ProcessSimple(MACD, MACD, Black, Plum); TrailingSimple(MACD, Black, Plum);
ProcessAvgLim(ENVELOPE, ENVELOPE, Green, Red); TrailingAvgLim(ENVELOPE, Green, Red);
}
Esto fue un ejemplo del enfoque orientado a objetos para la construcción de un sistema. Lo puede utilizar quien quiera.
Una vez más, me gustaría pedir a los desarrolladores de MQL que amplíen las opciones que ofrece el lenguaje. Este es un ejemplo de implementación de clases de objetos en el lenguaje C++.
struct SystemParam { double TP; // profit double NullTP; // profit level, after which we set into losslessness double NullTP2; // profit level, after which we set into losslessness a set of one-direction orders double TS; // distance of the trailing stop double NullSL; // loss level, at which we transfer the expected profit into losslessness double SL; // stop-loss double dSL; // a step upon the opening level of the next order for the position support double dStep; // In how many times we increase the step upon the opening level of the next order double dLot; // In how many times we increase the lot on the next order } class MTS { public: string m_NameTS; // system name (for making comments for the order) int m_SignalID; // identifier of trading signals (for semaphore inquiry) long int Tickets[1000]; // array of order tickets, selected upon m_SignalID (MAGIC) SystemParam SysPar; // Trading system parameters color ClrBuy; // color for indicating BUY order color ClrSell; // color for indicating SELL order // Initialization void MyMTS (); // standard function that sets initial values of the system void MyMTS (int aSignalID, int nProcessMode, int nTrailingMode); // standard function // that sets initial values of the system // Implementation int CheckSignal(); //function of checking state of market signals // Processing int m_nProcessMode; // identifier of observation mode int m_nTrailingMode; // identifier of trailing mode void Process(); // EE function - processing CheckSignal() void Trailing(); // PS function - order trailing // Special functions bool CreatTicketArray(int dir); // creating an array of tickets, selected upon m_SignalID (MAGIC) // and desired type dir: buy, sell, buylim, buystop, sellim, sellstop bool ArrangeOrderBy(int iSort); // arranging array Tickets upon the parameter (date, profit, price...) }; … MTS MyTS; // our trading system … int init() { … MyTS.m_SignalID = SIGNAL_MACD; // our system is based on MACD signals MyTS.m_NameTS = "MACD"; MyTS.SysPar.TP = 500; MyTS.SysPar.NullTP = 20; MyTS.SysPar.TS = 50; MyTS.SysPar.SL = 1000; MyTS.SetProcess (MODE_AVGLIM); MyTS.SetTrailing (MODE_AVGLIM); … } void start() { … MyTS.Process (); MyTS.Trailing (); … } … void MTS::Process() { … int Signal = CheckSignal(true, m_SignalID); //calling the global function of signal processing if (Signal == -1) return; // if no signal, do nothing //----- for buying if(Signal == OP_BUY) { } if(Signal == OP_SELL) { } … } … // global processor of semaphores int CheckSignal(bool bEntry, int SignalID) { switch (SignalID) { case ELDER: return (Elder(bEntry)); break; case ENVELOP: return (Envelop(bEntry)); break; case LAGUER: return (Laguer(bEntry)); break; case MACD: return (Macd(bEntry)); break; … } } // calling a certain semaphore int Macd(bool bEntry) { double MACDOpen=3; double MACDClose=2; double MA=26; int MODE_MA = MODE_EMA; // method of the calculation of averages int PRICE_MA = PRICE_CLOSE; // method of the calculation of averages int PERIOD = PERIOD_H1; // the period to work with //parameters of averages double MacdCur, MacdPre, SignalCur; double SignalPre, MaCur, MaPre; //---- get the value MacdCur=iMACD(NULL,0,8,17,9,PRICE_MA,MODE_MAIN,0); MacdPre=iMACD(NULL,0,8,17,9,PRICE_MA,MODE_MAIN,1); SignalCur=iMACD(NULL,0,8,17,9,PRICE_MA,MODE_SIGNAL,0); SignalPre=iMACD(NULL,0,8,17,9,PRICE_MA,MODE_SIGNAL,1); MaCur=iMA(NULL,0,MA,0,MODE_MA,PRICE_MA,0); MaPre=iMA(NULL,0,MA,0,MODE_MA,PRICE_MA,1); //----- condition for the operation execution if (bEntry) //for buying bEntry==true { if(MacdCur<0 && MacdCur>SignalCur && MacdPre<SignalPre && MathAbs(MacdCur)>(MACDOpen*Point) && MaCur>MaPre) return (OP_BUY); if(MacdCur>0 && MacdCur<SignalCur && MacdPre>SignalPre && MacdCur>(MACDOpen*Point) && MaCur<MaPre) return (OP_SELL); } else //for closing bEntry==false { if(MacdCur>0 && MacdCur<SignalCur && MacdPre>SignalPre && MacdCur>(MACDClose*Point)) return (OP_BUY); if(MacdCur>0 && MacdCur<SignalCur && MacdPre>SignalPre && MacdCur>(MACDOpen*Point) && MaCur<MaPre) return (OP_BUY); if(MacdCur<0 && MacdCur>SignalCur && MacdPre<SignalPre && MathAbs(MacdCur)>(MACDClose*Point)) return (OP_SELL); if(MacdCur<0 && MacdCur>SignalCur && MacdPre<SignalPre && MathAbs(MacdCur)>(MACDOpen*Point) && MaCur>MaPre) return (OP_SELL); } return (-1); //no signal }
La lógica del sistema en el lenguaje MQL no sería muy distinta. Todas las funciones se vuelven globales. Y para distinguir las órdenes de un sistema de trading de las órdenes de otro sistema tendremos que añadir el parámetro SignalID (es decir, MAGIC) en todas las funciones que trabajan con órdenes.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/1499
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso