Trabajando con las series temporales en la biblioteca DoEasy (Parte 56): Objeto del indicador personalizado, obtención de datos de parte de los objetos de indicador en la colección
Contenido
- Concepto
- Mejorando las clases de la biblioteca
- Objeto del indicador personalizado
- Prueba
- ¿Qué es lo próximo?
Concepto
Continuamos desarrollando la funcionalidad para trabajar con indicadores en los asesores expertos (EAs) creados a base de esta biblioteca.
Ya creamos las clases de los objetos de indicadores estándar y empezamos a colocarlos en la lista de colección. Para un «conjunto» completo nos falta sólo el objeto de indicador personalizado. Hoy, vamos a crear este objeto. El concepto de su construcción será diferente del concepto de la creación de los objetos de indicadores estándar.
Puesto que tenemos un conjunto final de indicadores estándar en el terminal, podemos crear objetos de indicadores estándar a base de los datos definidos y conocidos de antemano para cada uno de los indicadores. Pues, eso es lo que estamos haciendo, es decir, especificamos un conjunto de parámetros establecido estrictamente que corresponde al indicador a crear. Mientras que un indicador personalizado puede tener cualquier conjunto de parámetros, el que es imposible saber de antemano.
Por esta razón, el concepto de creación del objeto de indicador personalizado va a diferenciarse de la creación del objeto de indicador estándar.
Si para crear un objeto de indicador estándar basta con crear para cada uno de ellos su propio método de creación que va a contener todas sus propiedades necesarias en los parámetros de entrada, pues, para crear un indicador personalizado cuyo número y tipo de parámetros se desconocen de antemano, su método de creación tendrá que recibir una matriz de estructuras de los parámetros de entrada del indicador que ha sido rellenada de antemano. Entonces, todos los parámetros y propiedades necesarios para crear el indicador van a tomarse de esta matriz. Por eso, por desgracia, el usuario de la biblioteca tendrá que rellenar esta matriz de estructuras de los parámetros de entrada por si mismo en su programa para crear un indicador personalizado.
Puesto que tratamos de crear una biblioteca para facilitar la creación de nuestros programas, entonces, entre varias opciones de acceder a los indicadores creados en mi programa he elegido la siguiente: cada indicador creado será marcado con su propio identificador único. Usando este identificador, podremos acceder a cada uno de los indicadores creados. Gracias a que nosotros mismos los creamos, nosotros mismos les asignamos los identificadores, y sabemos perfectamente cuál de los identificadores asignados corresponde a uno u otro indicador creado en el programa.
Aparte del identificador, también habrá métodos de acceso al identificador según todos sus parámetros. Es decir, se podrá obtener el objeto de indicador indicando los mismos parámetros con los cuales este objeto de indicador ha sido creado, y luego trabajar con el objeto obtenido: obtener los datos de parte del indicador (los métodos de obtención de datos serán creados hoy), y copiarlos a las matrices para los análisis estadísticos (eso será implementado en los artículos posteriores).
Mejorando las clases de la biblioteca
Como siempre, en primer lugar, vamos a introducir nuevos mensajes de la biblioteca en el archivo \MQL5\Include\DoEasy\Data.mqh.
Añadimos los índices de mensajes nuevos:
MSG_LIB_TEXT_IND_TEXT_GROUP, // Indicator group MSG_LIB_TEXT_IND_TEXT_GROUP_TREND, // Trend indicator MSG_LIB_TEXT_IND_TEXT_GROUP_OSCILLATOR, // Oscillator MSG_LIB_TEXT_IND_TEXT_GROUP_VOLUMES, // Volumes MSG_LIB_TEXT_IND_TEXT_GROUP_ARROWS, // Arrow indicator MSG_LIB_TEXT_IND_TEXT_ID, // Indicator ID MSG_LIB_TEXT_IND_TEXT_EMPTY_VALUE, // Empty value for plotting where nothing will be drawn MSG_LIB_TEXT_IND_TEXT_SYMBOL, // Indicator symbol MSG_LIB_TEXT_IND_TEXT_NAME, // Indicator name MSG_LIB_TEXT_IND_TEXT_SHORTNAME, // Indicator short name MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS, // Indicator parameters MSG_LIB_TEXT_IND_TEXT_APPLIED_VOLUME, // Volume type for calculation MSG_LIB_TEXT_IND_TEXT_PERIOD, // Averaging period MSG_LIB_TEXT_IND_TEXT_FAST_PERIOD, // Fast MA period MSG_LIB_TEXT_IND_TEXT_SLOW_PERIOD, // Slow MA period MSG_LIB_TEXT_IND_TEXT_SIGNAL, // Difference averaging period MSG_LIB_TEXT_IND_TEXT_TENKAN_PERIOD, // Tenkan-sen period MSG_LIB_TEXT_IND_TEXT_KIJUN_PERIOD, // Kijun-sen period MSG_LIB_TEXT_IND_TEXT_SPANB_PERIOD, // Senkou Span B period MSG_LIB_TEXT_IND_TEXT_JAW_PERIOD, // Period for jaw line calculation MSG_LIB_TEXT_IND_TEXT_TEETH_PERIOD, // Period for teeth line calculation MSG_LIB_TEXT_IND_TEXT_LIPS_PERIOD, // Period for lips line calculation MSG_LIB_TEXT_IND_TEXT_JAW_SHIFT, // Horizontal shift of jaws line MSG_LIB_TEXT_IND_TEXT_TEETH_SHIFT, // Horizontal shift of teeth line MSG_LIB_TEXT_IND_TEXT_LIPS_SHIFT, // Horizontal shift of lips line MSG_LIB_TEXT_IND_TEXT_SHIFT, // Horizontal shift of the indicator MSG_LIB_TEXT_IND_TEXT_MA_METHOD, // Smoothing type MSG_LIB_TEXT_IND_TEXT_APPLIED_PRICE, // Price type or handle MSG_LIB_TEXT_IND_TEXT_STD_DEVIATION, // Number of standard deviations MSG_LIB_TEXT_IND_TEXT_DEVIATION, // Deviation of channel borders from the central line MSG_LIB_TEXT_IND_TEXT_STEP, // Price change step — acceleration factor MSG_LIB_TEXT_IND_TEXT_MAXIMUM, // Maximum step MSG_LIB_TEXT_IND_TEXT_KPERIOD, // K period (number of bars for calculation) MSG_LIB_TEXT_IND_TEXT_DPERIOD, // D period (primary smoothing period) MSG_LIB_TEXT_IND_TEXT_SLOWING, // Final smoothing MSG_LIB_TEXT_IND_TEXT_PRICE_FIELD, // Stochastic calculation method MSG_LIB_TEXT_IND_TEXT_CMO_PERIOD, // Chande Momentum period MSG_LIB_TEXT_IND_TEXT_SMOOTHING_PERIOD, // Smoothing factor period MSG_LIB_TEXT_IND_TEXT_CUSTOM_PARAM, // Input parameter //--- CIndicatorsCollection MSG_LIB_SYS_FAILED_ADD_IND_TO_LIST, // Error. Failed to add indicator object to list MSG_LIB_SYS_INVALID_IND_POINTER, // Error. Invalid pointer to indicator object is passed MSG_LIB_SYS_IND_ID_EXIST, // Error. The indicator object with the ID already exists }; //+------------------------------------------------------------------+
Añadimos los mensajes de texto correspondientes a los índices nuevamente añadidos:
{"Стрелочный индикатор","Arrow indicator"}, {"Идентификатор индикатора","Indicator ID"}, {"Пустое значение для построения, для которого нет отрисовки","Empty value for plotting, for which there is no drawing"}, {"Символ индикатора","Indicator symbol"}, {"Имя индикатора","Indicator name"}, {"Короткое имя индикатора","Indicator shortname"}, {"Параметры индикатора","Indicator parameters"}, {"Тип объема для расчета","Volume type for calculation"}, {"Период усреднения","Averaging period"}, {"Период быстрой скользящей","Fast MA period"}, {"Период медленной скользящей","Slow MA period"}, {"Период усреднения разности","Averaging period for their difference"}, {"Период Tenkan-sen","Tenkan-sen period"}, {"Период Kijun-sen","Kijun-sen period"}, {"Период Senkou Span B","Senkou Span B period"}, {"Период для расчета челюстей","Period for the calculation of jaws"}, {"Период для расчета зубов","Period for the calculation of teeth"}, {"Период для расчета губ","Period for the calculation of lips"}, {"Смещение челюстей по горизонтали","Horizontal shift of jaws"}, {"Смещение зубов по горизонтали","Horizontal shift of teeth"}, {"Смещение губ по горизонтали","Horizontal shift of lips"}, {"Смещение индикатора по горизонтали","Horizontal shift of the indicator"}, {"Тип сглаживания","Smoothing type"}, {"Тип цены или handle","Price type or handle"}, {"Количество стандартных отклонений","Number of standard deviations"}, {"Отклонение границ от средней линии","Deviation of boundaries from the midline"}, {"Шаг изменения цены - коэффициент ускорения","Price increment step - acceleration factor"}, {"Максимальный шаг","Maximum value of step"}, {"K-период (количество баров для расчетов)","K-period (number of bars for calculations)"}, {"D-период (период первичного сглаживания)","D-period (period of first smoothing)"}, {"Окончательное сглаживание","Final smoothing"}, {"Способ расчета стохастика","Stochastic calculation method"}, {"Период Chande Momentum","Chande Momentum period"}, {"Период фактора сглаживания","Smoothing factor period"}, {"Входной параметр","Input parameter"}, {"Ошибка. Не удалось добавить объект-индикатор в список","Error. Failed to add indicator object to list"}, {"Ошибка. Передан неверный указатель на объект-индикатор","Error. Invalid pointer to indicator object passed"}, {"Ошибка. Уже существует объект-индикатор с идентификатором","Error. There is already exist an indicator object with ID"}, }; //+---------------------------------------------------------------------+
Al crear un objeto de indicador estándar, establecemos de inmediato un grupo en su constructor que va a corresponder al tipo del indicador: de tendencia, de flechas, oscilador o indicador de volúmenes. Lo podemos hacer para los indicadores estándar porque sabemos de antemano el tipo del indicador a crear. Para un indicador personalizado, no podemos saber su tipo de antemano (el grupo al que pertenece) y establecerlo en el constructor de la clase del objeto. Por eso, tenemos que crear el nuevo grupo de indicadores, «cualquiera», que va a asignarse automáticamente al objeto recién creado, permitiendo al usuario especificar por sí mismo el grupo de pertenencia del indicador personalizado ya después de la creación de su objeto.
En el archivo \MQL5\Include\DoEasy\Defines.mqh, añadimos la nueva constante a la enumeración del grupo de indicadores:
//+------------------------------------------------------------------+ //| Indicator group | //+------------------------------------------------------------------+ enum ENUM_INDICATOR_GROUP { INDICATOR_GROUP_TREND, // Trend indicator INDICATOR_GROUP_OSCILLATOR, // Oscillator INDICATOR_GROUP_VOLUMES, // Volumes INDICATOR_GROUP_ARROWS, // Arrow indicator INDICATOR_GROUP_ANY, // Any indicator }; //+------------------------------------------------------------------+
Ya que hemos decidido acceder a los indicadores creados usando los identificadores únicos, necesitamos añadir una nueva propiedad a la enumeración de propiedades enteras del indicador:
//+------------------------------------------------------------------+ //| Indicator integer properties | //+------------------------------------------------------------------+ enum ENUM_INDICATOR_PROP_INTEGER { INDICATOR_PROP_STATUS = 0, // Indicator status (from enumeration ENUM_INDICATOR_STATUS) INDICATOR_PROP_TYPE, // Indicator type (from enumeration ENUM_INDICATOR) INDICATOR_PROP_TIMEFRAME, // Indicator timeframe INDICATOR_PROP_HANDLE, // Indicator handle INDICATOR_PROP_GROUP, // Indicator group INDICATOR_PROP_ID, // Indicator ID }; #define INDICATOR_PROP_INTEGER_TOTAL (6) // Total number of indicator integer properties #define INDICATOR_PROP_INTEGER_SKIP (0) // Number of indicator properties not used in sorting //+------------------------------------------------------------------+
y por tanto, vamos a aumentar el número de propiedades enteras del indicador de 5 a 6.
Para poder buscar un indicador en la lista según su identificador, introducimos un nuevo criterio de ordenación de los indicadores de acuerdo con identificadores:
//+------------------------------------------------------------------+ //| Possible indicator sorting criteria | //+------------------------------------------------------------------+ #define FIRST_INDICATOR_DBL_PROP (INDICATOR_PROP_INTEGER_TOTAL-INDICATOR_PROP_INTEGER_SKIP) #define FIRST_INDICATOR_STR_PROP (INDICATOR_PROP_INTEGER_TOTAL-INDICATOR_PROP_INTEGER_SKIP+INDICATOR_PROP_DOUBLE_TOTAL-INDICATOR_PROP_DOUBLE_SKIP) enum ENUM_SORT_INDICATOR_MODE { //--- Sort by integer properties SORT_BY_INDICATOR_INDEX_STATUS = 0, // Sort by indicator status SORT_BY_INDICATOR_TYPE, // Sort by indicator type SORT_BY_INDICATOR_TIMEFRAME, // Sort by indicator timeframe SORT_BY_INDICATOR_HANDLE, // Sort by indicator handle SORT_BY_INDICATOR_GROUP, // Sort by indicator group SORT_BY_INDICATOR_ID, // Sort by indicator ID //--- Sort by real properties SORT_BY_INDICATOR_EMPTY_VALUE = FIRST_INDICATOR_DBL_PROP,// Sort by the empty value for plotting where nothing will be drawn //--- Sort by string properties SORT_BY_INDICATOR_SYMBOL = FIRST_INDICATOR_STR_PROP, // Sort by indicator symbol SORT_BY_INDICATOR_NAME, // Sort by indicator name SORT_BY_INDICATOR_SHORTNAME, // Sort by indicator short name }; //+------------------------------------------------------------------+
Ya que han sido añadidas nuevas propiedades para el objeto de indicador, ahora hay que mejorar un poco la clase del objeto del indicador abstracto en el archivo \MQL5\Include\DoEasy\Objects\Indicators\IndicatorDE.mqh.
En la sección pública de la clase, escribiremos dos métodos para establecer y obtener la propiedad «identificador del indicador»:
//--- Set indicator’s (1) group, (2) empty value of buffers, (3) name, (4) short name, (5) indicator ID void SetGroup(const ENUM_INDICATOR_GROUP group) { this.SetProperty(INDICATOR_PROP_GROUP,group); } void SetEmptyValue(const double value) { this.SetProperty(INDICATOR_PROP_EMPTY_VALUE,value); } void SetName(const string name) { this.SetProperty(INDICATOR_PROP_NAME,name); } void SetShortName(const string shortname) { this.SetProperty(INDICATOR_PROP_SHORTNAME,shortname); } void SetID(const int id) { this.SetProperty(INDICATOR_PROP_ID,id); } //--- Return (1) status, (2) group, (3) timeframe, (4) type, (5) handle, (6) ID, //--- (7) empty value of buffers, (8) name, (9) short name, (10) indicator symbol ENUM_INDICATOR_STATUS Status(void) const { return (ENUM_INDICATOR_STATUS)this.GetProperty(INDICATOR_PROP_STATUS);} ENUM_INDICATOR_GROUP Group(void) const { return (ENUM_INDICATOR_GROUP)this.GetProperty(INDICATOR_PROP_GROUP); } ENUM_TIMEFRAMES Timeframe(void) const { return (ENUM_TIMEFRAMES)this.GetProperty(INDICATOR_PROP_TIMEFRAME); } ENUM_INDICATOR TypeIndicator(void) const { return (ENUM_INDICATOR)this.GetProperty(INDICATOR_PROP_TYPE); } int Handle(void) const { return (int)this.GetProperty(INDICATOR_PROP_HANDLE); } int ID(void) const { return (int)this.GetProperty(INDICATOR_PROP_ID); } double EmptyValue(void) const { return this.GetProperty(INDICATOR_PROP_EMPTY_VALUE); } string Name(void) const { return this.GetProperty(INDICATOR_PROP_NAME); } string ShortName(void) const { return this.GetProperty(INDICATOR_PROP_SHORTNAME); } string Symbol(void) const { return this.GetProperty(INDICATOR_PROP_SYMBOL); }
En los objetos de indicadores estándar, podíamos escribir los métodos para visualizar la descripción de cada parámetro del indicador porque sabíamos con certeza qué parámetro del indicador determinado estábamos describiendo. Para visualizar la descripción de los parámetros de un indicador personalizado, no podemos saber exactamente el propósito de cada parámetro del indicador desconocido desde el principio. Por eso, aquí tendremos que visualizar simplemente la descripción de cada siguiente parámetro desde la matriz de parámetros del indicador MqlParam.
En la sección pública de la clase del objeto de indicador abstracto, declaramos el método para visualizar la descripción de los parámetros de la estructura MqlParam, y dos métodos más: para obtener datos de parte del objeto de indicador según el índice y la hora de la barra:
//--- Return description of (1) type, (2) status, (3) group, (4) timeframe, (5) empty value of indicator, (6) parameter of m_mql_param array string GetTypeDescription(void) const { return m_ind_type_description; } string GetStatusDescription(void) const; string GetGroupDescription(void) const; string GetTimeframeDescription(void) const; string GetEmptyValueDescription(void) const; string GetMqlParamDescription(const int index) const; //--- Display the description of indicator object properties in the journal (full_prop=true - all properties, false - supported ones only) void Print(const bool full_prop=false); //--- Display (1) a short description, (2) description of indicator object parameters in the journal (implementation in the descendants) virtual void PrintShort(void) {;} virtual void PrintParameters(void) {;} //--- Return data of specified buffer from specified bar (1) by index, (2) by bar time double GetDataBuffer(const int buffer_num,const int index); double GetDataBuffer(const int buffer_num,const datetime time); }; //+------------------------------------------------------------------+
Escribimos la implementación de estos métodos fuera del cuerpo de la clase:
El método que devuelve la descripción del parámetro de la matriz de estructuras MqlParam:
//+------------------------------------------------------------------+ //| Return the description of parameter of m_mql_param array | //+------------------------------------------------------------------+ string CIndicatorDE::GetMqlParamDescription(const int index) const { return "["+(string)index+"] "+MqlParameterDescription(this.m_mql_param[index]); } //+------------------------------------------------------------------+
El índice del parámetro en la matriz se transfiere al método. Luego, se crea una cadena a base del índice de la matriz y la descripción del parámetro de acuerdo con los datos almacenados en la estructura según el índice especificado de la matriz. Más abajo escribiremos la función MqlParameterDescription() para devolver la descripción de datos de la estructura MqlParam.
Métodos para devolver datos del indicador según el índice y la hora de la barra:
//+------------------------------------------------------------------+ //| Return data of specified buffer from specified bar by index | //+------------------------------------------------------------------+ double CIndicatorDE::GetDataBuffer(const int buffer_num,const int index) { double array[1]={EMPTY_VALUE}; int copied=::CopyBuffer(this.Handle(),buffer_num,index,1,array); return(copied==1 ? array[0] : this.EmptyValue()); } //+------------------------------------------------------------------+ //| Return data of specified buffer from specified bar by time | //+------------------------------------------------------------------+ double CIndicatorDE::GetDataBuffer(const int buffer_num,const datetime time) { double array[1]={EMPTY_VALUE}; int copied=::CopyBuffer(this.Handle(),buffer_num,time,1,array); return(copied==1 ? array[0] : this.EmptyValue()); } //+------------------------------------------------------------------+
Los métodos reciben el índice o la hora de la barra cuyos datos hay que obtener del indicador. Usamos la función CopyBuffer() para solicitar una barra según el índice o la hora y devolvemos el resultado obtenido escrito en la matriz. Si no se ha podido obtener los datos por alguna razón, los métodos devuelven un valor vacío establecido para el objeto de indicador.
En el constructor de la clase, establecemos el identificador del indicador por defecto (-1):
//+------------------------------------------------------------------+ //| Closed parametric constructor | //+------------------------------------------------------------------+ CIndicatorDE::CIndicatorDE(ENUM_INDICATOR ind_type, string symbol, ENUM_TIMEFRAMES timeframe, ENUM_INDICATOR_STATUS status, ENUM_INDICATOR_GROUP group, string name, string shortname, MqlParam &mql_params[]) { //--- Set collection ID to the object this.m_type=COLLECTION_INDICATORS_ID; //--- Write description of indicator type this.m_ind_type_description=IndicatorTypeDescription(ind_type); //--- If parameter array value passed to constructor is more than zero //--- fill in the array of object parameters with data from the array passed to constructor int count=::ArrayResize(this.m_mql_param,::ArraySize(mql_params)); for(int i=0;i<count;i++) { this.m_mql_param[i].type = mql_params[i].type; this.m_mql_param[i].double_value = mql_params[i].double_value; this.m_mql_param[i].integer_value= mql_params[i].integer_value; this.m_mql_param[i].string_value = mql_params[i].string_value; } //--- Create indicator handle int handle=::IndicatorCreate(symbol,timeframe,ind_type,count,this.m_mql_param); //--- Save integer properties this.m_long_prop[INDICATOR_PROP_STATUS] = status; this.m_long_prop[INDICATOR_PROP_TYPE] = ind_type; this.m_long_prop[INDICATOR_PROP_GROUP] = group; this.m_long_prop[INDICATOR_PROP_TIMEFRAME] = timeframe; this.m_long_prop[INDICATOR_PROP_HANDLE] = handle; this.m_long_prop[INDICATOR_PROP_ID] = WRONG_VALUE; //--- Save real properties this.m_double_prop[this.IndexProp(INDICATOR_PROP_EMPTY_VALUE)]=EMPTY_VALUE; //--- Save string properties this.m_string_prop[this.IndexProp(INDICATOR_PROP_SYMBOL)] = (symbol==NULL || symbol=="" ? ::Symbol() : symbol); this.m_string_prop[this.IndexProp(INDICATOR_PROP_NAME)] = name; this.m_string_prop[this.IndexProp(INDICATOR_PROP_SHORTNAME)]= shortname; } //+------------------------------------------------------------------+
Como cada manejador del indicador es único, así como, su identificador que vamos a establecer personalmente, pues, habrá que omitir estos dos parámetros a la hora de comparar la coincidencia de los parámetros de dos indicadores. De lo contrario, cada indicador será identificado como único, y no podremos comparar de forma correcta si los parámetros de estos indicadores son idénticos.
Para que eso no ocurra, omitimos la propiedad «manejador» e «identificador» en el método de comparación de dos objetos de indicador:
//+------------------------------------------------------------------+ //| Compare CIndicatorDE objects with each other by all properties | //+------------------------------------------------------------------+ bool CIndicatorDE::IsEqual(CIndicatorDE *compared_obj) const { if(!IsEqualMqlParamArrays(compared_obj.m_mql_param)) return false; int beg=0, end=INDICATOR_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_INDICATOR_PROP_INTEGER prop=(ENUM_INDICATOR_PROP_INTEGER)i; if(prop==INDICATOR_PROP_HANDLE || prop==INDICATOR_PROP_ID) continue; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=INDICATOR_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_INDICATOR_PROP_DOUBLE prop=(ENUM_INDICATOR_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=INDICATOR_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_INDICATOR_PROP_STRING prop=(ENUM_INDICATOR_PROP_STRING)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
Añadimos la visualización de la descripción de la propiedad del indicador «identificador» al método que devuelve la descripción de una propiedad entera del indicador:
//+------------------------------------------------------------------+ //| Return description of indicator's integer property | //+------------------------------------------------------------------+ string CIndicatorDE::GetPropertyDescription(ENUM_INDICATOR_PROP_INTEGER property) { return ( property==INDICATOR_PROP_STATUS ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_STATUS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetStatusDescription() ) : property==INDICATOR_PROP_TYPE ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTypeDescription() ) : property==INDICATOR_PROP_GROUP ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_GROUP)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetGroupDescription() ) : property==INDICATOR_PROP_ID ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_ID)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==INDICATOR_PROP_TIMEFRAME ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TIMEFRAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTimeframeDescription() ) : property==INDICATOR_PROP_HANDLE ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_HANDLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : "" ); } //+------------------------------------------------------------------+
Ahora, añadimos la función para mostrar la descripción de la estructura MqlParam al archivo de funciones de servicio \MQL5\Include\DoEasy\Services\DELib.mqh :
//+------------------------------------------------------------------+ //| Return the description of parameter MqlParam array | //+------------------------------------------------------------------+ string MqlParameterDescription(const MqlParam &mql_param) { int type=mql_param.type; string res=CMessage::Text(MSG_ORD_TYPE)+" "+typename(type)+": "; //--- string parameter type if(type==TYPE_STRING) res+=mql_param.string_value; //--- datetime parameter type else if(type==TYPE_DATETIME) res+=TimeToString(mql_param.integer_value,TIME_DATE|TIME_MINUTES|TIME_SECONDS); //--- color parameter type else if(type==TYPE_COLOR) res+=ColorToString((color)mql_param.integer_value,true); //--- bool parameter type else if(type==TYPE_BOOL) res+=(string)(bool)mql_param.integer_value; //--- integer types else if(type>TYPE_BOOL && type<TYPE_FLOAT) res+=(string)mql_param.integer_value; //--- real types else res+=DoubleToString(mql_param.double_value,8); return res; } //+------------------------------------------------------------------+
En la estructura MqlParam hay varios campos. Uno de ellos contiene el tipo de datos almacenados en la estructura. De acuerdo con este tipo de datos, podemos comprender de qué campo de la estructura hay que obtener los datos, y qué función de representación string de datos hay que usar para mostrar los datos en el registro.
Obtenemos el tipo de datos y empezamos a generar la cadena compuesta de la palabra "Tipo " +" "+ descripción string del tipo de datos + ": ".
A continuación, dependiendo del tipo de datos, a la cadena ya creada se le añade la descripción del valor que se almacena en el campo de la estructura correspondiente al tipo. Para eso usamos las funciones estándar para visualizar las representaciones string del tipo de datos necesario.
Como resultado, al usar el método de la visualización de los parámetros de la estructura MqlParam de la clase del objeto del indicador abstracto y la función de servicio considerada antes en registro del terminal, la descripción de los parámetros del indicador personalizado con cuatro parámetros tendrá el siguiente aspecto:
--- Indicator parameters --- - [1] int type: 13 - [2] int type: 0 - [3] int type: 0 - [4] int type: 2
Ya que hemos añadido una nueva propiedad al objeto de indicador (su identificador), entonces, en todas las clases de todos los objetos de indicadores cuyos archivos se encuentran en la carpeta \MQL5\Include\DoEasy\Objects\Indicators\Standart\, vamos a hacer una pequeña adición a los métodos de visualizar el nombre breve del indicador —simplemente, añadimos el valor del identificador al nombre breve:
//+------------------------------------------------------------------+ //| Display a short description of indicator object in the journal | //+------------------------------------------------------------------+ void CIndAC::PrintShort(void) { string id=(this.ID()>WRONG_VALUE ? ", id #"+(string)this.ID()+"]" : "]"); ::Print(GetStatusDescription()," ",this.Name()," ",this.Symbol()," ",TimeframeDescription(this.Timeframe())," [handle ",this.Handle(),id); } //+------------------------------------------------------------------+
Aquí: creamos la descripción del identificador. Además, si el valor del identificador es más de -1, el identificador va a mostrarse; de lo contrario, si no hay identificador (su valor es -1), no va a mostrarse en la descripción (sólo corchete de cierre). A continuación, añadimos la cadena obtenida a la descripción breve del indicador.
Estas mejoras ya han sido introducidas en todas las clases de los objetos de indicadores.
Objeto del indicador personalizado
Ahora, vamos a escribir la clase del objeto de indicador personalizado. Vamos a ubicarla en la carpeta de los indicadores estándar de la biblioteca \MQL5\Include\DoEasy\Objects\Indicators\Standart\. Eso se debe a que la lista de los indicadores del terminal también contiene el indicador personalizado, y eso quiere decir que el mismo también tiene que encontrarse en la lista de los indicadores del terminal.
Vamos a analizar la clase en conjunto:
//+------------------------------------------------------------------+ //| IndCustom.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/es/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/es/users/artmedia70" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\\IndicatorDE.mqh" //+------------------------------------------------------------------+ //| Custom indicator | //+------------------------------------------------------------------+ class CIndCustom : public CIndicatorDE { private: public: //--- Constructor CIndCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,MqlParam &mql_param[]) : CIndicatorDE(IND_CUSTOM,symbol,timeframe, INDICATOR_STATUS_CUSTOM, INDICATOR_GROUP_ANY, mql_param[0].string_value, mql_param[0].string_value+"("+(symbol==NULL || symbol=="" ? ::Symbol() : symbol)+ ","+TimeframeDescription(timeframe)+")",mql_param) {} //--- Supported indicator properties (1) real, (2) integer virtual bool SupportProperty(ENUM_INDICATOR_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_INDICATOR_PROP_INTEGER property); //--- Display (1) a short description, (2) description of indicator object parameters in the journal virtual void PrintShort(void); virtual void PrintParameters(void); }; //+------------------------------------------------------------------+ //| Return 'true' if the indicator supports the passed | //| integer property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CIndCustom::SupportProperty(ENUM_INDICATOR_PROP_INTEGER property) { return true; } //+------------------------------------------------------------------+ //| Return 'true' if the indicator supports the passed | //| real property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CIndCustom::SupportProperty(ENUM_INDICATOR_PROP_DOUBLE property) { return true; } //+------------------------------------------------------------------+ //| Display a short description of indicator object in the journal | //+------------------------------------------------------------------+ void CIndCustom::PrintShort(void) { string id=(this.ID()>WRONG_VALUE ? ", id #"+(string)this.ID()+"]" : "]"); ::Print(GetStatusDescription()," ",this.Name()," ",this.Symbol()," ",TimeframeDescription(this.Timeframe())," [handle ",this.Handle(),id); } //+------------------------------------------------------------------+ //| Display parameter description of indicator object in the journal | //+------------------------------------------------------------------+ void CIndCustom::PrintParameters(void) { ::Print(" --- ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS)," --- "); int total=::ArraySize(this.m_mql_param); for(int i=1;i<total;i++) { ::Print(" - ",this.GetMqlParamDescription(i)); } } //+------------------------------------------------------------------+
Ya estamos familiarizados con todos estos métodos en las clases de los objetos de indicadores estándar. Pero a diferencia de ellos: aquí, el constructor de la clase no recibe los parámetros del indicador creado usando las variables de entrada, sino usando una matriz de estructuras de los parámetros de entrada MqlParam creada de antemano. Al constructor cerrado de la clase del objeto del indicador abstracto se le pasa el estatus «indicador personalizado» y el grupo «cualquier indicador». Como el nombre, le transmitimos el primer elemento de la matriz de parámetros, que durante la creación de un indicador personalizado, tiene por defecto el tipo TYPE_STRING y el valor del campo string_value contiene el nombre del indicador personalizado. De la misma manera, se crea el nombre breve del indicador, pero con adición de la descripción del símbolo y marco temporal. A continuación, podremos usar el nombre y el nombre breve a través de los métodos de la clase padre SetName() y SetShortName(). Lo único es que el nombre del indicador ya contiene la ruta hacia el indicador. Por eso, -por lo menos en esta fase del desarrollo de la biblioteca- no se puede alterar el nombre del indicador personalizado que ya ha sido creado.
En el método que visualiza la descripción de los parámetros del objeto de indicador en el registro, hacemos lo siguiente: primero, mostramos el encabezado, luego, imprimimos en el ciclo cada siguiente parámetro de la matriz usando los métodos considerados antes (en particular, el método de la clase padre GetMqlParamDescription()).
Todos los objetos de indicador se almacenan en la lista de colección de la clase CIndicatorsCollection en el archivo \MQL5\Include\DoEasy\Collections\IndicatorsCollection.mqh.
Al añadir el indicador a la lista de colección, necesitamos verificar adicionalmente si su identificador es único. Además, hay que añadir la visibilidad de la clase del indicador personalizado para que la clase de colección de los indicadores pueda trabajar con ella.
Incluimos el archivo de la clase del indicador personalizado a la clase de colección de los objetos de indicador:
//+------------------------------------------------------------------+ //| IndicatorsCollection.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/es/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/es/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Objects\Indicators\Standart\IndAC.mqh" #include "..\Objects\Indicators\Standart\IndAD.mqh" #include "..\Objects\Indicators\Standart\IndADX.mqh" #include "..\Objects\Indicators\Standart\IndADXW.mqh" #include "..\Objects\Indicators\Standart\IndAlligator.mqh" #include "..\Objects\Indicators\Standart\IndAMA.mqh" #include "..\Objects\Indicators\Standart\IndAO.mqh" #include "..\Objects\Indicators\Standart\IndATR.mqh" #include "..\Objects\Indicators\Standart\IndBands.mqh" #include "..\Objects\Indicators\Standart\IndBears.mqh" #include "..\Objects\Indicators\Standart\IndBulls.mqh" #include "..\Objects\Indicators\Standart\IndBWMFI.mqh" #include "..\Objects\Indicators\Standart\IndCCI.mqh" #include "..\Objects\Indicators\Standart\IndChaikin.mqh" #include "..\Objects\Indicators\Standart\IndCustom.mqh" #include "..\Objects\Indicators\Standart\IndDEMA.mqh" #include "..\Objects\Indicators\Standart\IndDeMarker.mqh" #include "..\Objects\Indicators\Standart\IndEnvelopes.mqh" #include "..\Objects\Indicators\Standart\IndForce.mqh" #include "..\Objects\Indicators\Standart\IndFractals.mqh" #include "..\Objects\Indicators\Standart\IndFRAMA.mqh" #include "..\Objects\Indicators\Standart\IndGator.mqh" #include "..\Objects\Indicators\Standart\IndIchimoku.mqh" #include "..\Objects\Indicators\Standart\IndMA.mqh" #include "..\Objects\Indicators\Standart\IndMACD.mqh" #include "..\Objects\Indicators\Standart\IndMFI.mqh" #include "..\Objects\Indicators\Standart\IndMomentum.mqh" #include "..\Objects\Indicators\Standart\IndOBV.mqh" #include "..\Objects\Indicators\Standart\IndOsMA.mqh" #include "..\Objects\Indicators\Standart\IndRSI.mqh" #include "..\Objects\Indicators\Standart\IndRVI.mqh" #include "..\Objects\Indicators\Standart\IndSAR.mqh" #include "..\Objects\Indicators\Standart\IndStDev.mqh" #include "..\Objects\Indicators\Standart\IndStoch.mqh" #include "..\Objects\Indicators\Standart\IndTEMA.mqh" #include "..\Objects\Indicators\Standart\IndTRIX.mqh" #include "..\Objects\Indicators\Standart\IndVIDYA.mqh" #include "..\Objects\Indicators\Standart\IndVolumes.mqh" #include "..\Objects\Indicators\Standart\IndWPR.mqh" //+------------------------------------------------------------------+
En la sección privada de la clase, declaramos el método de adición del objeto de indicador a la colección y el método que verifica si el objeto de indicador con identificador especificado se encuentre en la lista:
//+------------------------------------------------------------------+ //| Indicator collection | //+------------------------------------------------------------------+ class CIndicatorsCollection : public CObject { private: CListObj m_list; // List of indicator objects MqlParam m_mql_param[]; // Array of indicator parameters //--- (1) Create, (2) add to collection list a new indicator object and set an ID for it CIndicatorDE *CreateIndicator(const ENUM_INDICATOR ind_type,MqlParam &mql_param[],const string symbol_name=NULL,const ENUM_TIMEFRAMES period=PERIOD_CURRENT); int AddIndicatorToList(CIndicatorDE *indicator,const int id); //--- Return the indicator index in the list int Index(CIndicatorDE *compared_obj); //--- Check presence of indicator object with specified id in the list bool CheckID(const int id); public:
En la sección pública de la clase, declaramos el método que devuelve el puntero al objeto del indicador personalizado según su grupo y parámetros especificados en la matriz MqlParam (a diferencia de los indicadores estándar, podemos especificar los parámetros sólo a través de su transferencia en esta matriz):
CIndicatorDE *GetIndCCI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const ENUM_APPLIED_PRICE applied_price); CIndicatorDE *GetIndCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,ENUM_INDICATOR_GROUP group,MqlParam ¶m[]); CIndicatorDE *GetIndDEMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_APPLIED_PRICE applied_price);
En el bloque de declaración de los métodos de creación de indicadores, primero, añadimos la indicación del identificador de cada indicador a los parámetros de cada método; segundo, añadimos la declaración del método para crear el indicador personalizado. No vamos a mostrar la lista completa de la declaración de estos métodos. Mostraremos sólo tres de ellos (todos los métodos ya han sido mejorados y están disponibles en los archivos adjuntos al artículo):
int CreateCCI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id, const int ma_period=14, const ENUM_APPLIED_PRICE applied_price=PRICE_TYPICAL); int CreateCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id, ENUM_INDICATOR_GROUP group, MqlParam &mql_param[]); int CreateDEMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id, const int ma_period=14, const int ma_shift=0, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
En todos los métodos, después de los parámetros de entrada del símbolo y marco temporal, ha sido añadida la especificación obligatoria del identificador del indicador durante su creación.
En el método de creación del indicador personalizado, también se especifica el grupo del indicador y se transmite la matriz de parámetros del indicador creada y rellenada de antemano, a base de la cual será creado el indicador personalizado.
Al final del listado del cuerpo de la clase, declaramos el método para asignar el identificador al indicador especificado y el método que devuelve el objeto de indicador según el identificador especificado:
//--- Set ID for the specified indicator void SetID(CIndicatorDE *indicator,const int id); //--- Return indicator object by its ID CIndicatorDE *GetIndByID(const uint id); //--- Display (1) the complete and (2) short collection description in the journal void Print(void); void PrintShort(void); //--- Constructor CIndicatorsCollection(); }; //+------------------------------------------------------------------+
Ahora, vamos a considerar todos los métodos declarados.
En el método privado de la creación de un nuevo objeto de indicador, añadimos la creación del nuevo objeto de indicador personalizado:
//+------------------------------------------------------------------+ //| Create a new indicator object | //+------------------------------------------------------------------+ CIndicatorDE *CIndicatorsCollection::CreateIndicator(const ENUM_INDICATOR ind_type,MqlParam &mql_param[],const string symbol_name=NULL,const ENUM_TIMEFRAMES period=PERIOD_CURRENT) { string symbol=(symbol_name==NULL || symbol_name=="" ? ::Symbol() : symbol_name); ENUM_TIMEFRAMES timeframe=(period==PERIOD_CURRENT ? ::Period() : period); CIndicatorDE *indicator=NULL; switch(ind_type) { case IND_AC : indicator=new CIndAC(symbol,timeframe,mql_param); break; case IND_AD : indicator=new CIndAD(symbol,timeframe,mql_param); break; case IND_ADX : indicator=new CIndADX(symbol,timeframe,mql_param); break; case IND_ADXW : indicator=new CIndADXW(symbol,timeframe,mql_param); break; case IND_ALLIGATOR : indicator=new CIndAlligator(symbol,timeframe,mql_param); break; case IND_AMA : indicator=new CIndAMA(symbol,timeframe,mql_param); break; case IND_AO : indicator=new CIndAO(symbol,timeframe,mql_param); break; case IND_ATR : indicator=new CIndATR(symbol,timeframe,mql_param); break; case IND_BANDS : indicator=new CIndBands(symbol,timeframe,mql_param); break; case IND_BEARS : indicator=new CIndBears(symbol,timeframe,mql_param); break; case IND_BULLS : indicator=new CIndBulls(symbol,timeframe,mql_param); break; case IND_BWMFI : indicator=new CIndBWMFI(symbol,timeframe,mql_param); break; case IND_CCI : indicator=new CIndCCI(symbol,timeframe,mql_param); break; case IND_CHAIKIN : indicator=new CIndCHO(symbol,timeframe,mql_param); break; case IND_DEMA : indicator=new CIndDEMA(symbol,timeframe,mql_param); break; case IND_DEMARKER : indicator=new CIndDeMarker(symbol,timeframe,mql_param); break; case IND_ENVELOPES : indicator=new CIndEnvelopes(symbol,timeframe,mql_param); break; case IND_FORCE : indicator=new CIndForce(symbol,timeframe,mql_param); break; case IND_FRACTALS : indicator=new CIndFractals(symbol,timeframe,mql_param); break; case IND_FRAMA : indicator=new CIndFRAMA(symbol,timeframe,mql_param); break; case IND_GATOR : indicator=new CIndGator(symbol,timeframe,mql_param); break; case IND_ICHIMOKU : indicator=new CIndIchimoku(symbol,timeframe,mql_param); break; case IND_MA : indicator=new CIndMA(symbol,timeframe,mql_param); break; case IND_MACD : indicator=new CIndMACD(symbol,timeframe,mql_param); break; case IND_MFI : indicator=new CIndMFI(symbol,timeframe,mql_param); break; case IND_MOMENTUM : indicator=new CIndMomentum(symbol,timeframe,mql_param); break; case IND_OBV : indicator=new CIndOBV(symbol,timeframe,mql_param); break; case IND_OSMA : indicator=new CIndOsMA(symbol,timeframe,mql_param); break; case IND_RSI : indicator=new CIndRSI(symbol,timeframe,mql_param); break; case IND_RVI : indicator=new CIndRVI(symbol,timeframe,mql_param); break; case IND_SAR : indicator=new CIndSAR(symbol,timeframe,mql_param); break; case IND_STDDEV : indicator=new CIndStDev(symbol,timeframe,mql_param); break; case IND_STOCHASTIC : indicator=new CIndStoch(symbol,timeframe,mql_param); break; case IND_TEMA : indicator=new CIndTEMA(symbol,timeframe,mql_param); break; case IND_TRIX : indicator=new CIndTRIX(symbol,timeframe,mql_param); break; case IND_VIDYA : indicator=new CIndVIDYA(symbol,timeframe,mql_param); break; case IND_VOLUMES : indicator=new CIndVolumes(symbol,timeframe,mql_param); break; case IND_WPR : indicator=new CIndWPR(symbol,timeframe,mql_param); break; case IND_CUSTOM : indicator=new CIndCustom(symbol,timeframe,mql_param); break; default: break; } return indicator; } //+------------------------------------------------------------------+
Modificamos el método que añade el nuevo objeto de indicador a la lista de colección:
//+------------------------------------------------------------------+ //| Add a new indicator object to collection list | //+------------------------------------------------------------------+ int CIndicatorsCollection::AddIndicatorToList(CIndicatorDE *indicator,const int id) { //--- If invalid indicator is passed to the object - return INVALID_HANDLE if(indicator==NULL) return INVALID_HANDLE; //--- If such indicator is already in the list int index=this.Index(indicator); if(index!=WRONG_VALUE) { //--- Remove the earlier created object, get indicator object from the list and return indicator handle delete indicator; indicator=this.m_list.At(index); } //--- If indicator object is not in the list yet else { //--- If failed to add indicator object to the list - display a corresponding message, //--- remove object and return INVALID_HANDLE if(!this.m_list.Add(indicator)) { ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_ADD_IND_TO_LIST)); delete indicator; return INVALID_HANDLE; } } //--- If indicator is successfully added to the list or is already there... //--- If indicator with specified ID (not -1) is not in the list - set ID if(id>WRONG_VALUE && !this.CheckID(id)) indicator.SetID(id); //--- Return the handle of a new indicator added to the list return indicator.Handle(); } //+------------------------------------------------------------------+
Ahora, el identificador del indicador se pasa en el método también. Si todavía no hay indicador con este identificador en la lista de colección, al objeto de indicador se le asigna el identificador especificado. De lo contrario, el identificador del indicador va a ser -1 por defecto.
Ahora, todos los métodos de la creación de los objetos de indicador se han hecho más cortos.
Vamos a ver el ejemplo de la creación de los objetos de indicadores AC y Alligator:
//+------------------------------------------------------------------+ //| Create a new indicator object Accelerator Oscillator | //| and place it to the collection list | //+------------------------------------------------------------------+ int CIndicatorsCollection::CreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id) { //--- AC indicator possesses no parameters - resize the array of parameter structures ::ArrayResize(this.m_mql_param,0); //--- Create indicator object CIndicatorDE *indicator=this.CreateIndicator(IND_AC,this.m_mql_param,symbol,timeframe); //--- Return indicator handle received as a result of adding the object to collection list return this.AddIndicatorToList(indicator,id); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Create new indicator object Alligator | //| and place it to the collection list | //+------------------------------------------------------------------+ int CIndicatorsCollection::CreateAlligator(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id, const int jaw_period=13, const int jaw_shift=8, const int teeth_period=8, const int teeth_shift=5, const int lips_period=5, const int lips_shift=3, const ENUM_MA_METHOD ma_method=MODE_SMMA, const ENUM_APPLIED_PRICE applied_price=PRICE_MEDIAN) { //--- Add required indicator parameters to the array of parameter structures ::ArrayResize(this.m_mql_param,8); this.m_mql_param[0].type=TYPE_INT; this.m_mql_param[0].integer_value=jaw_period; this.m_mql_param[1].type=TYPE_INT; this.m_mql_param[1].integer_value=jaw_shift; this.m_mql_param[2].type=TYPE_INT; this.m_mql_param[2].integer_value=teeth_period; this.m_mql_param[3].type=TYPE_INT; this.m_mql_param[3].integer_value=teeth_shift; this.m_mql_param[4].type=TYPE_INT; this.m_mql_param[4].integer_value=lips_period; this.m_mql_param[5].type=TYPE_INT; this.m_mql_param[5].integer_value=lips_shift; this.m_mql_param[6].type=TYPE_INT; this.m_mql_param[6].integer_value=ma_method; this.m_mql_param[7].type=TYPE_INT; this.m_mql_param[7].integer_value=applied_price; //--- Create indicator object CIndicatorDE *indicator=this.CreateIndicator(IND_ALLIGATOR,this.m_mql_param,symbol,timeframe); //--- Return indicator handle received as a result of adding the object to collection list return this.AddIndicatorToList(indicator,id); } //+------------------------------------------------------------------+
Ahora será suficiente sólo rellenar la estructura de los parámetros de entrada, crear el indicador y llamar al método de su adición a la lista de colección. Estas modificaciones han sido hechas en todos los métodos de creación de los objetos de indicador. Por eso, no vamos a analizarlos aquí, a excepción del método para crear un indicador personalizado:
//+------------------------------------------------------------------+ //| Create a new object - custom indicator | //| and place it to the collection list | //+------------------------------------------------------------------+ int CIndicatorsCollection::CreateCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id, ENUM_INDICATOR_GROUP group, MqlParam &mql_param[]) { //--- Create indicator object CIndicatorDE *indicator=this.CreateIndicator(IND_CUSTOM,mql_param,symbol,timeframe); if(indicator==NULL) return INVALID_HANDLE; //--- Set a group for indicator object indicator.SetGroup(group); //--- Return indicator handle received as a result of adding the object to collection list return this.AddIndicatorToList(indicator,id); } //+------------------------------------------------------------------+
Aquí, es un poco diferente. Aquí, además del identificador, el método de la creación también recibe el grupo del indicador. Y todos los parámetros del indicador creado se transmiten inmediatamente en la matriz de parámetros MqlParam debido a que no podemos saber de antemano de los parámetros del indicador personalizado que se crea.
Absolutamente todos los métodos de la creación de los indicadores estándar han recibido los valores estándar predefinidos para cada parámetro de entrada. De esta manera, para crear un indicador estándar con parámetros predefinidos bastará con especificar un símbolo, marco temporal y un identificador.
Implementación del método que retorna el puntero al objeto de indicador personalizado:
//+------------------------------------------------------------------+ //| Return pointer to object- custom indicator | //+------------------------------------------------------------------+ CIndicatorDE *CIndicatorsCollection::GetIndCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,ENUM_INDICATOR_GROUP group,MqlParam ¶m[]) { CIndicatorDE *tmp=new CIndCustom(symbol,timeframe,param); if(tmp==NULL) return NULL; tmp.SetGroup(group); int index=this.Index(tmp); delete tmp; return(index>WRONG_VALUE ? this.m_list.At(index) : NULL); } //+------------------------------------------------------------------+
Aquí: creamos un objeto de indicador temporal para buscar el mismo objeto en la lista de colección, le asignamos un grupo y obtenemos el índice del objeto encontrado en la lista de colección. Luego, eliminamos el objeto temporal y devolvemos el puntero al objeto según el índice encontrado, o NULL si este objeto no ha sido encontrado en la lista.
El método que devuelve la presencia del objeto de indicador con id especificado en la lista:
//+------------------------------------------------------------------+ //| Check presence of indicator object with specified id in the list | //+------------------------------------------------------------------+ bool CIndicatorsCollection::CheckID(const int id) { CArrayObj *list=CSelect::ByIndicatorProperty(this.GetList(),INDICATOR_PROP_ID,id,EQUAL); return(list!=NULL && list.Total()!=0); } //+------------------------------------------------------------------+
Obtenemos la lista de objetos de indicador con identificador especificado y devolvemos la bandera de verificación de que la lista es válida y no está vacía (la lista debe contener un objeto).
El método que establece un identificador para el indicador especificado:
//+------------------------------------------------------------------+ //| Set ID for the specified indicator | //+------------------------------------------------------------------+ void CIndicatorsCollection::SetID(CIndicatorDE *indicator,const int id) { if(indicator==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_INVALID_IND_POINTER)); return; } if(id>WRONG_VALUE) { if(CheckID(id)) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_IND_ID_EXIST)," #",(string)id); return; } } indicator.SetID(id); } //+------------------------------------------------------------------+
El método recibe el puntero al objeto de indicador para el que es necesario establecer el identificador transmitido por el parámetro al método.
Si ha sido transmitido un puntero inválido, avisamos de ello y salimos del método.
Si el valor del identificador es más de -1, verificamos la presencia del objeto de indicador con este identificador, y si ya existe, avisamos de ello y salimos.
Si todas las verificaciones han sido hechas, establecemos el identificador para el objeto. En caso si el valor del identificador transmitido al método es menos de 0, este identificador se le asigna al objeto sin verificación alguna, y este identificador del objeto de indicador significa su ausencia.
El método que devuelve el objeto de indicador según el identificador especificado:
//+------------------------------------------------------------------+ //| Return indicator object by its ID | //+------------------------------------------------------------------+ CIndicatorDE *CIndicatorsCollection::GetIndByID(const uint id) { CArrayObj *list=CSelect::ByIndicatorProperty(this.GetList(),INDICATOR_PROP_ID,id,EQUAL); return(list==NULL || list.Total()==0 ? NULL : list.At(list.Total()-1)); } //+------------------------------------------------------------------+
Aquí: obtenemos la lista de objetos de indicador con identificador especificado y devolvemos NULL (si no hemos podido obtener la lista, o ella está vacía) o el puntero al objeto con identificador necesario. Puesto que el objeto con identificador especificado puede ser sólo uno, aquí no importa el índice a especificar en la lista obtenida: el primero o el último. Aquí, especificamos el último.
La prueba de la creación de indicadores en los asesores expertos ha demostrado un problema. Cuando cambiamos del marco temporal, se crean adicionalmente los indicadores exactamente iguales pero con otro marco temporal. Y es verdad: es que los indicadores con los mismos parámetros de entrada pero calculados en marcos temporales diferentes son dos indicadores diferentes. Para evitar este problema, será suficiente limpiar la lista de los indicadores creados al deinicializar el programa. Para eso, en el objeto principal de la biblioteca CEngine en el archivo \MQL5\Include\DoEasy\Engine.mqh declaramos el nuevo manejador OnDeinit():
//--- (1) Timer, event handler (2) NewTick, (3) Calculate, Deinit void OnTimer(SDataCalculate &data_calculate); void OnTick(SDataCalculate &data_calculate,const uint required=0); int OnCalculate(SDataCalculate &data_calculate,const uint required=0); void OnDeinit(void);
Y escribimos su implementación fuera del cuerpo de la clase:
//+------------------------------------------------------------------+ //| Deinitialize library | //+------------------------------------------------------------------+ void CEngine::OnDeinit(void) { this.m_indicators.GetList().Clear(); } //+------------------------------------------------------------------+
Este método de la clase va a invocarse desde el manejador OnDeinit() del programa. Lo que tenemos aquí es la llamada al método de la limpieza de la lista de colección para el objeto de colección de indicadores.
Prueba
Para simular la creación de diferentes indicadores y obtener los datos de parte de los objetos de indicador, usamos el asesor experto del artículo anterior.
Vamos a guardarlo en la nueva carpeta \MQL5\Experts\TestDoEasy\Part56\ con el nombre nuevo TestDoEasyPart56.mq5.
En este EA, creamos dos indicadores personalizados Moving Average, pero con parámetros diferentes (usamos los indicadores de la carpeta de los ejemplos de indicadores de la entrega estándar del terminal \MQL5\Indicators\Examples\). Además, creamos dos indicadores estándar Adaptive Moving Average también con parámetros de entrada diferentes
En el área global, establecemos las macrosustituciones para facilitar el acceso a los indicadores por sus identificadores y declaramos dos matrices de parámetros para crear dos indicadores personalizados:
//+------------------------------------------------------------------+ //| TestDoEasyPart56.mq5 | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/es/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/es/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Engine.mqh> //--- enums enum ENUM_BUTTONS { BUTT_BUY, BUTT_BUY_LIMIT, BUTT_BUY_STOP, BUTT_BUY_STOP_LIMIT, BUTT_CLOSE_BUY, BUTT_CLOSE_BUY2, BUTT_CLOSE_BUY_BY_SELL, BUTT_SELL, BUTT_SELL_LIMIT, BUTT_SELL_STOP, BUTT_SELL_STOP_LIMIT, BUTT_CLOSE_SELL, BUTT_CLOSE_SELL2, BUTT_CLOSE_SELL_BY_BUY, BUTT_DELETE_PENDING, BUTT_CLOSE_ALL, BUTT_SET_STOP_LOSS, BUTT_SET_TAKE_PROFIT, BUTT_PROFIT_WITHDRAWAL, BUTT_TRAILING_ALL }; #define TOTAL_BUTT (20) #define MA1 (1) #define MA2 (2) #define AMA1 (3) #define AMA2 (4) //--- structures struct SDataButt { string name; string text; }; //--- input variables input ushort InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 150; // StopLoss in points input uint InpTakeProfit = 150; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpDistancePReq = 50; // Distance for Pending Request's activate (points) input uint InpBarsDelayPReq = 5; // Bars delay for Pending Request's activate (current timeframe) input uint InpSlippage = 5; // Slippage in points input uint InpSpreadMultiplier = 1; // Spread multiplier for adjusting stop-orders by StopLevel input uchar InpTotalAttempts = 5; // Number of trading attempts sinput double InpWithdrawal = 10; // Withdrawal funds (in tester) sinput uint InpButtShiftX = 0; // Buttons X shift sinput uint InpButtShiftY = 10; // Buttons Y shift input uint InpTrailingStop = 50; // Trailing Stop (points) input uint InpTrailingStep = 20; // Trailing Step (points) input uint InpTrailingStart = 0; // Trailing Start (points) input uint InpStopLossModify = 20; // StopLoss for modification (points) input uint InpTakeProfitModify = 60; // TakeProfit for modification (points) sinput ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; // Mode of used symbols list sinput string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; // List of used symbols (comma - separator) sinput ENUM_TIMEFRAMES_MODE InpModeUsedTFs = TIMEFRAMES_MODE_LIST; // Mode of used timeframes list sinput string InpUsedTFs = "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; // List of used timeframes (comma - separator) sinput bool InpUseSounds = true; // Use sounds //--- global variables CEngine engine; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ushort magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint distance_pending_request; uint bars_delay_pending_request; uint slippage; bool trailing_on; bool pressed_pending_buy; bool pressed_pending_buy_limit; bool pressed_pending_buy_stop; bool pressed_pending_buy_stoplimit; bool pressed_pending_close_buy; bool pressed_pending_close_buy2; bool pressed_pending_close_buy_by_sell; bool pressed_pending_sell; bool pressed_pending_sell_limit; bool pressed_pending_sell_stop; bool pressed_pending_sell_stoplimit; bool pressed_pending_close_sell; bool pressed_pending_close_sell2; bool pressed_pending_close_sell_by_buy; bool pressed_pending_delete_all; bool pressed_pending_close_all; bool pressed_pending_sl; bool pressed_pending_tp; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; int used_symbols_mode; string array_used_symbols[]; string array_used_periods[]; bool testing; uchar group1; uchar group2; double g_point; int g_digits; //--- Arrays of custom indicator parameters MqlParam param_ma1[]; MqlParam param_ma2[]; //+------------------------------------------------------------------+
En esencia, las macrosustituciones declaradas representan una descripción de valores numéricos de los identificadores. Es que resulta más fácil llamar al identificador del indicador por su nombre que por su valor.
En el manejador OnInit() del asesor, creamos los cuatro indicadores y visualizamos los datos de todos los indicadores creados en el registro:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Calling the function displays the list of enumeration constants in the journal //--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity //EnumNumbersTest(); //--- Set EA global variables prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_"; testing=engine.IsTester(); for(int i=0;i<TOTAL_BUTT;i++) { butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i); butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i); } lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0)); magic_number=InpMagic; stoploss=InpStopLoss; takeprofit=InpTakeProfit; distance_pending=InpDistance; distance_stoplimit=InpDistanceSL; slippage=InpSlippage; trailing_stop=InpTrailingStop*Point(); trailing_step=InpTrailingStep*Point(); trailing_start=InpTrailingStart; stoploss_to_modify=InpStopLossModify; takeprofit_to_modify=InpTakeProfitModify; distance_pending_request=(InpDistancePReq<5 ? 5 : InpDistancePReq); bars_delay_pending_request=(InpBarsDelayPReq<1 ? 1 : InpBarsDelayPReq); g_point=SymbolInfoDouble(NULL,SYMBOL_POINT); g_digits=(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS); //--- Initialize random group numbers group1=0; group2=0; srand(GetTickCount()); //--- Initialize DoEasy library OnInitDoEasy(); //--- Create indicators ArrayResize(param_ma1,4); //--- Name of indicator 1 param_ma1[0].type=TYPE_STRING; param_ma1[0].string_value="Examples\\Custom Moving Average.ex5"; //--- Calculation period param_ma1[1].type=TYPE_INT; param_ma1[1].integer_value=13; //--- Horizontal shift param_ma1[2].type=TYPE_INT; param_ma1[2].integer_value=0; //--- Smoothing method param_ma1[3].type=TYPE_INT; param_ma1[3].integer_value=MODE_SMA; //--- Create indicator 1 engine.GetIndicatorsCollection().CreateCustom(NULL,PERIOD_CURRENT,MA1,INDICATOR_GROUP_TREND,param_ma1); ArrayResize(param_ma2,5); //--- Name of indicator 2 param_ma2[0].type=TYPE_STRING; param_ma2[0].string_value="Examples\\Custom Moving Average.ex5"; //--- Calculation period param_ma2[1].type=TYPE_INT; param_ma2[1].integer_value=13; //--- Horizontal shift param_ma2[2].type=TYPE_INT; param_ma2[2].integer_value=0; //--- Smoothing method param_ma2[3].type=TYPE_INT; param_ma2[3].integer_value=MODE_SMA; //--- Calculation price param_ma2[4].type=TYPE_INT; param_ma2[4].integer_value=PRICE_OPEN; //--- Create indicator 2 engine.GetIndicatorsCollection().CreateCustom(NULL,PERIOD_CURRENT,MA2,INDICATOR_GROUP_TREND,param_ma2); //--- Create indicator 3 engine.GetIndicatorsCollection().CreateAMA(NULL,PERIOD_CURRENT,AMA1); //--- Create indicator 4 engine.GetIndicatorsCollection().CreateAMA(NULL,PERIOD_CURRENT,AMA2,14); //--- Display descriptions of created indicators engine.GetIndicatorsCollection().Print(); engine.GetIndicatorsCollection().PrintShort(); //--- Check and remove remaining EA graphical objects if(IsPresentObectByPrefix(prefix)) ObjectsDeleteAll(0,prefix); //--- Create the button panel if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- Set trailing activation button status ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on); //--- Reset states of the buttons for working using pending requests for(int i=0;i<14;i++) { ButtonState(butt_data[i].name+"_PRICE",false); ButtonState(butt_data[i].name+"_TIME",false); } //--- Check playing a standard sound by macro substitution and a custom sound by description engine.PlaySoundByDescription(SND_OK); //--- Wait for 600 milliseconds engine.Pause(600); engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","Falling coin 2")); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Establecemos el precio de cálculo PRICE_OPEN para el segundo indicador personalizado MA. Si el precio de cálculo no se especifica explícitamente, por defecto, (en el primer indicador MA) se usa el precio de cálculo PRICE_CLOSE.
Cuando se crean los indicadores АМА, al primero se le establece el período de cálculo 9 (establecido por defecto), y para el segundo establecemos explícitamente el valor 14.
De esta manera, los cuatro indicadores creados tienen diferentes valores de entrada de sus parámetros.
En el manejador OnDeinit() del EA, llamamos al manejador OnDeinit() de la biblioteca:
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Remove EA graphical objects by an object name prefix ObjectsDeleteAll(0,prefix); Comment(""); //--- Deinitialize library engine.OnDeinit(); } //+------------------------------------------------------------------+
Eso limpiará la lista de colección de indicadores durante la deinicialización del EA cuando cambiamos de marcos temporales, lo cual evitará la creación de los objetos de indicador adicionales innecesarios.
En el manejador OnTick() del EA, obtenemos el acceso a cada uno de los objetos de indicador creados y visualizamos los datos de la barra actual de cada indicador mostrándolos en los comentarios en el gráfico:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Handle the NewTick event in the library engine.OnTick(rates_data); //--- If working in the tester if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(rates_data); // Working in the timer PressButtonsControl(); // Button pressing control engine.EventsHandling(); // Working with events } //--- Get custom indicator objects CIndicatorDE *ma1=engine.GetIndicatorsCollection().GetIndByID(MA1); CIndicatorDE *ma2=engine.GetIndicatorsCollection().GetIndByID(MA2); CIndicatorDE *ama1=engine.GetIndicatorsCollection().GetIndByID(AMA1); CIndicatorDE *ama2=engine.GetIndicatorsCollection().GetIndByID(AMA2); Comment ( "ma1=",DoubleToString(ma1.GetDataBuffer(0,0),6), ", ma2=",DoubleToString(ma2.GetDataBuffer(0,0),6), "\nama1=",DoubleToString(ama1.GetDataBuffer(0,0),6), ", ama2=",DoubleToString(ama2.GetDataBuffer(0,0),6) ); //--- If the trailing flag is set if(trailing_on) { TrailingPositions(); // Trailing positions TrailingOrders(); // Trailing pending orders } } //+------------------------------------------------------------------+
En el artículo anterior, creábamos temporalmente los objetos de indicador en la función de inicialización de la biblioteca OnInitDoEasy(). Eliminamos estas cadenas de la función:
//--- Create timeseries of all used symbols engine.SeriesCreateAll(array_used_periods); //--- Check created timeseries - display descriptions of all created timeseries in the journal //--- (true - only created ones, false - created and declared ones) engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions //engine.GetTimeSeriesCollection().Print(true); // Full descriptions //--- Create indicators engine.GetIndicatorsCollection().CreateAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE); engine.GetIndicatorsCollection().CreateAMA(Symbol(),Period(),10,3,32,5,PRICE_CLOSE); engine.GetIndicatorsCollection().Print(); engine.GetIndicatorsCollection().PrintShort(); //--- Create resource text files
Compilamos el EA y lo iniciamos en el gráfico, estableciendo previamente en los ajustes el uso del símbolo y marco temporal actuales.
En el registro, se mostrarán las descripciones de los parámetros de todos los indicadores creados:
--- Initializing "DoEasy" library --- Working with the current symbol only: "EURUSD" Working with the current timeframe only: H1 EURUSD symbol timeseries: - Timeseries "EURUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 6284 Library initialization time: 00:00:00.141 ============= Parameter list start: "Custom indicator" ============= Indicator status: Custom indicator Type of indicator: CUSTOM Indicator timeframe: H1 Indicator handle: 10 Indicator group: Trend indicator Indicator ID: 1 ------ Empty value for plotting where nothing will be drawn: EMPTY_VALUE ------ Indicator symbol: EURUSD Indicator name: "Examples\Custom Moving Average.ex5" Indicator short name: "Examples\Custom Moving Average.ex5(EURUSD,H1)" --- Indicator parameters --- - [1] int type: 13 - [2] int type: 0 - [3] int type: 0 ================== Parameter list end: "Custom indicator" ================== ============= Parameter list start: "Custom indicator" ============= Indicator status: Custom indicator Type of indicator: CUSTOM Indicator timeframe: H1 Indicator handle: 11 Indicator group: Trend indicator Indicator ID: 2 ------ Empty value for plotting where nothing will be drawn: EMPTY_VALUE ------ Indicator symbol: EURUSD Indicator name: "Examples\Custom Moving Average.ex5" Indicator short name: "Examples\Custom Moving Average.ex5(EURUSD,H1)" --- Indicator parameters --- - [1] int type: 13 - [2] int type: 0 - [3] int type: 0 - [4] int type: 2 ================== Parameter list end: "Custom indicator" ================== ============= Parameter list start: "Standard indicator" ============= Indicator status: Standard indicator Type of indicator: AMA Indicator timeframe: H1 Indicator handle: 12 Indicator group: Trend indicator Indicator ID: 3 ------ Empty value for plotting where nothing will be drawn: EMPTY_VALUE ------ Indicator symbol: EURUSD Indicator name: "Adaptive Moving Average" Indicator short name: "AMA(EURUSD,H1)" --- Indicator parameters --- - Averaging period: 9 - Fast MA period: 2 - Slow MA period: 30 - Horizontal shift of the indicator: 0 - Price type or handle: CLOSE ================== Parameter list end: "Standard indicator" ================== ============= Parameter list start: "Standard indicator" ============= Indicator status: Standard indicator Type of indicator: AMA Indicator timeframe: H1 Indicator handle: 13 Indicator group: Trend indicator Indicator ID: 4 ------ Empty value for plotting where nothing will be drawn: EMPTY_VALUE ------ Indicator symbol: EURUSD Indicator name: "Adaptive Moving Average" Indicator short name: "AMA(EURUSD,H1)" --- Indicator parameters --- - Averaging period: 14 - Fast MA period: 2 - Slow MA period: 30 - Horizontal shift of the indicator: 0 - Price type or handle: CLOSE ================== Parameter list end: "Standard indicator" ================== Custom indicator Examples\Custom Moving Average.ex5 EURUSD H1 [handle 10, id #1] Custom indicator Examples\Custom Moving Average.ex5 EURUSD H1 [handle 11, id #2] Standard indicator Adaptive Moving Average EURUSD H1 [handle 12, id #3] Standard indicator Adaptive Moving Average EURUSD H1 [handle 13, id #4]
En el gráfico del símbolo, se mostrarán los datos de los búferes de todos los indicadores creados:
Se puede añadir al gráfico los indicadores necesarios que corresponden según sus parámetros a los creados en el EA. Así, se puede verificar la coincidencia de los datos de indicadores en el comentario en el gráfico y en la ventana de datos: van a coincidir
¿Qué es lo próximo?
En el siguiente artículo, seguiremos desarrollando la funcionalidad para trabajar con indicadores en los asesores expertos. En los próximos artículos, se planea crear una vinculación de los datos con las barras de las clases de la serie temporal, y obtener datos de parte de los indicadores para diferentes estudios estadísticos.
Más abajo se adjuntan todos los archivos de la versión actual de la biblioteca y el archivo del asesor de prueba para MQL5. Puede descargarlo todo y ponerlo a prueba por sí mismo.
Nótese que, por el momento, la clase de colección de los indicadores se encuentra en el proceso del desarrollo. Por tanto, no se recomienda estrictamente usarla en sus programas.
Si tiene preguntas, observaciones o sugerencias, podrá concretarlas en los comentarios al artículo.
Artículos de esta serie:
Trabajando con las series temporales en la biblioteca DoEasy (Parte 35): El objeto "Barra" y la lista de serie temporal del símbolo
Trabajando con las series temporales en la biblioteca DoEasy (Parte 36): El objeto de series temporales de todos los periodos utilizados del símbolo
Trabajando con las series temporales en la biblioteca DoEasy (Parte 37): Colección de series temporales - Base de datos de series temporales según el símbolo y el periodo
Trabajando con las series temporales en la biblioteca DoEasy (Parte 38): Colección de series temporales - Actualización en tiempo real y acceso a los datos desde el programa
Trabajando con las series temporales en la biblioteca DoEasy (Parte 39): Indicadores basados en la biblioteca - Preparación de datos y eventos de la series temporales
Trabajando con las series temporales en la biblioteca DoEasy (Parte 40): Indicadores basados en la biblioteca - actualización de datos en tiempo real
Trabajando con las series temporales en la biblioteca DoEasy (Parte 41): Ejemplo de indicador de símbolo y periodo múltiples
Trabajando con las series temporales en la biblioteca DoEasy (Parte 42): La clase del objeto de búfer de indicador abstracto
Trabajando con las series temporales en la biblioteca DoEasy (Parte 43): Las clases de los objetos de búferes de indicador
Trabajando con las series temporales en la biblioteca DoEasy (Parte 44): Las clases de colección de los objetos de búferes de indicador
Trabajando con las series temporales en la biblioteca DoEasy (Parte 45): Búferes de indicador de periodo múltiple
Trabajando con las series temporales en la biblioteca DoEasy (Parte 46): Búferes de indicador de periodo y símbolos múltiples
Trabajando con las series temporales en la biblioteca DoEasy (Parte 47): Indicadores estándar de periodo y símbolo múltiples
Trabajando con las series temporales en la biblioteca DoEasy (Parte 48): Indicadores de periodo y símbolo múltiples en un búfer en una subventana
Trabajando con las series temporales en la biblioteca DoEasy (Parte 49): Indicadores estándar de periodo, símbolo y búfer múltiples
Trabajando con las series temporales en la biblioteca DoEasy (Parte 50): Indicadores estándar de periodo y símbolo múltiples con desplazamiento
Trabajando con las series temporales en la biblioteca DoEasy (Parte 51): Indicadores estándar compuestos de período y símbolo múltiples
Trabajando con las series temporales en la biblioteca DoEasy (Parte 52): Concepto multiplataforma de indicadores estándar de periodo y símbolo múltiples de búfer único
Trabajando con las series temporales en la biblioteca DoEasy (Parte 53): Clase del indicador abstracto básico
Trabajando con las series temporales en la biblioteca DoEasy (Parte 54): Clases herederas del indicador abstracto básico
Trabajando con las series temporales en la biblioteca DoEasy (Parte 55): Clase de colección de indicadores
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/8646
- 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