Trabajando con los precios en la biblioteca DoEasy (Parte 59): Objeto para almacenar los datos de un tick
Contenido
- Concepto
- Preparando los datos
- Clase del objeto de datos del tick
- Probando el objeto de datos del tick
- ¿Qué es lo próximo?
Concepto
En este artículo, empezaremos a desarrollar la funcionalidad de la biblioteca para trabajar con los datos de ticks.
El concepto del almacenamiento y el uso de los datos de ticks será muy parecido al concepto del almacenamiento de los datos de series temporales, donde la unidad mínima de los datos es una barra. Los objetos de barras se almacenan en las listas que pertenecen a los marcos temporales correspondientes, que a su vez se guardan en las listas pertenecientes a los símbolos.
En el concepto del almacenamiento de los datos de ticks, la unidad mínima del volumen será representada por los valores de la estructura del precio en un tick. Estos valores se describen a través de la estructura para el guardado de las últimas cotizaciones del símbolo MqlTick. El objeto para almacenar estos valores va a contener unas propiedades adicionales: spread (diferencia entre los precios Ask y Bid) y símbolo cuyos datos de un tick se describen por el objeto.
Para cada uno de los símbolos van a crearse sus propias listas de objetos con los datos de ticks (para una estructurización más conveniente de las listas). Naturalmente, cada una de las listas de objetos de los datos de ticks va a actualizarse automáticamente. La lista va a recibir los datos nuevos de los ticks que llegan, y el tamaño de las listas va a mantenerse según el volumen definido por el usuario.
El concepto del almacenamiento de los datos en la biblioteca implica la búsqueda y ordenación según cualquiera de las propiedades de los objetos almacenados en las listas. Eso permite obtener los datos requeridos para su posterior uso o ejecución de estudios analíticos. Y esto significa que los datos de ticks también tendrán esta posibilidad. Eso permite al usuario analizar rápidamente los flujos de ticks de los datos para su seguimiento, por ejemplo, si se trata de algunos cambios en el carácter de su llegada, o para la búsqueda de patrones establecidos, etc.
Preparando los datos
Igual como para cualquier objeto de la biblioteca, tenemos que añadir los mensajes de texto para visualizar la descripción de sus parámetros y crear enumeraciones de las propiedades del objeto, según las cuales podremos buscarlos en las listas u ordenar objetos según estas propiedades.
Añadimos los índices de nuevos mensajes al archivo \MQL5\Include\DoEasy\Data.mqh:
//--- CSeriesDataInd MSG_LIB_TEXT_METHOD_NOT_FOR_INDICATORS, // The method is not intended to handle indicator programs MSG_LIB_TEXT_IND_DATA_FAILED_GET_SERIES_DATA, // Failed to get indicator data timeseries MSG_LIB_TEXT_IND_DATA_FAILED_GET_CURRENT_DATA, // Failed to get current data of indicator buffer MSG_LIB_SYS_FAILED_CREATE_IND_DATA_OBJ, // Failed to create indicator data object MSG_LIB_TEXT_IND_DATA_FAILED_ADD_TO_LIST, // Failed to add indicator data object to list //--- CTick MSG_TICK_TEXT_TICK, // Tick MSG_TICK_TIME_MSC, // Time of the last update of prices in milliseconds MSG_TICK_TIME, // Time of the last update of prices MSG_TICK_VOLUME, // Volume for the current Last price MSG_TICK_FLAGS, // Flags MSG_TICK_VOLUME_REAL, // Volume for the current Last price with greater accuracy MSG_TICK_SPREAD, // Spread MSG_LIB_TEXT_TICK_CHANGED_DATA, // Changed data on tick: MSG_LIB_TEXT_TICK_FLAG_BID, // Bid price change MSG_LIB_TEXT_TICK_FLAG_ASK, // Ask price change MSG_LIB_TEXT_TICK_FLAG_LAST, // Last deal price change MSG_LIB_TEXT_TICK_FLAG_VOLUME, // Volume change }; //+------------------------------------------------------------------+
así como, añadimos los textos de los mensajes correspondientes a los índices nuevamente añadidos:
//--- CSeriesDataInd {"Метод не предназначен для работы с программами-индикаторами","The method is not intended for working with indicator programs"}, {"Не удалось получить таймсерию индикаторных данных","Failed to get indicator data timeseries"}, {"Не удалось получить текущие данные буфера индикатора","Failed to get the current data of the indicator buffer"}, {"Не удалось создать объект индикаторных данных","Failed to create indicator data object"}, {"Не удалось добавить объект индикаторных данных в список","Failed to add indicator data object to the list"}, //--- CTick {"Тик","Tick"}, {"Время последнего обновления цен в миллисекундах","Last price update time in milliseconds"}, {"Время последнего обновления цен","Last price update time"}, {"Объем для текущей цены Last","Volume for the current Last price"}, {"Флаги","Flags"}, {"Объем для текущей цены Last c повышенной точностью","Volume for the current \"Last\" price with increased accuracy"}, {"Спред","Spread"}, {"Изменённые данные на тике:","Changed data on a tick:"}, {"Изменение цены Bid","Bid price change"}, {"Изменение цены Ask","Ask price change"}, {"Изменение цены последней сделки","Last price change"}, {"Изменение объема","Volume change"}, }; //+---------------------------------------------------------------------+
Añadimos al archivo \MQL5\Include\DoEasy\Defines.mqh las enumeraciones para especificar las propiedades de tipo entero, real y string del objeto de los datos de ticks:
//+------------------------------------------------------------------+ //| Data for working with tick data | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Integer tick properties | //+------------------------------------------------------------------+ enum ENUM_TICK_PROP_INTEGER { TICK_PROP_TIME_MSC = 0, // Time of the last price update in milliseconds TICK_PROP_TIME, // Time of the last update TICK_PROP_VOLUME, // Volume for the current Last price TICK_PROP_FLAGS, // Tick flags }; #define TICK_PROP_INTEGER_TOTAL (4) // Total number of tick integer properties #define TICK_PROP_INTEGER_SKIP (0) // Number of tick properties not used in sorting //+------------------------------------------------------------------+ //| Real tick properties | //+------------------------------------------------------------------+ enum ENUM_TICK_PROP_DOUBLE { TICK_PROP_BID = TICK_PROP_INTEGER_TOTAL, // Tick Bid price TICK_PROP_ASK, // Tick Ask price TICK_PROP_LAST, // Current price of the last trade (Last) TICK_PROP_VOLUME_REAL, // Volume for the current Last price with greater accuracy TICK_PROP_SPREAD, // Tick spread (Ask - Bid) }; #define TICK_PROP_DOUBLE_TOTAL (5) // Total number of real tick properties #define TICK_PROP_DOUBLE_SKIP (0) // Number of tick properties not used in sorting //+------------------------------------------------------------------+ //| String tick properties | //+------------------------------------------------------------------+ enum ENUM_TICK_PROP_STRING { TICK_PROP_SYMBOL = (TICK_PROP_INTEGER_TOTAL+TICK_PROP_DOUBLE_TOTAL), // Tick symbol }; #define TICK_PROP_STRING_TOTAL (1) // Total number of string tick properties //+------------------------------------------------------------------+
Indicamos para cada una de las enumeraciones el número total de las propiedades utilizadas y no utilizadas en la ordenación.
Para tener la posibilidad de buscar y ordenar según las propiedades especificadas, vamos a añadir una enumeración más, en la que se indicarán unos posibles criterios de la ordenación de los datos de ticks:
//+------------------------------------------------------------------+ //| Possible tick sorting criteria | //+------------------------------------------------------------------+ #define FIRST_TICK_DBL_PROP (TICK_PROP_INTEGER_TOTAL-TICK_PROP_INTEGER_SKIP) #define FIRST_TICK_STR_PROP (TICK_PROP_INTEGER_TOTAL-TICK_PROP_INTEGER_SKIP+TICK_PROP_DOUBLE_TOTAL-TICK_PROP_DOUBLE_SKIP) enum ENUM_SORT_TICK_MODE { //--- Sort by integer properties SORT_BY_TICK_TIME_MSC = 0, // Sort by the time of the last price update in milliseconds SORT_BY_TICK_TIM, // Sort by the time of the last price update SORT_BY_TICK_VOLUME, // Sort by volume for the current Last price SORT_BY_TICK_FLAGS, // Sort by tick flags //--- Sort by real properties SORT_BY_TICK_BID = FIRST_TICK_DBL_PROP, // Sort by tick Bid price SORT_BY_TICK_ASK, // Sort by tick Ask price SORT_BY_TICK_LAST, // Sort by current price of the last trade (Last) SORT_BY_TICK_VOLUME_REAL, // Sort by volume for the current Last price with greater accuracy SORT_BY_TICK_SPREAD, // Sort by tick spread //--- Sort by string properties SORT_BY_TICK_SYMBOL = FIRST_TICK_STR_PROP, // Sort by tick symbol }; //+------------------------------------------------------------------+
Antes, ya creamos el objeto «Nuevo tick» en el artículo 38 que permitía monitorear la llegada de un tick nuevo para el símbolo especificado.
El objeto se ubica en la carpeta \MQL5\Include\DoEasy\Objects\Ticks\ del directorio de la biblioteca, en el archivo NewTickObj.mqh.
Vamos a mejorar la clase del objeto de tal manera que haya posibilidad de transmitir al método de definición del símbolo el valor NULL (o una cadena vacía) para indicar el símbolo actual para el objeto:
//--- Set a symbol void SetSymbol(const string symbol) { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); }
En la lista de inicialización del constructor de la clase, vamos a establecer el valor false para la variable m_new_tick que almacena la bandera del tick nuevo:
//+------------------------------------------------------------------+ //| Parametric constructor CNewTickObj | //+------------------------------------------------------------------+ CNewTickObj::CNewTickObj(const string symbol) : m_symbol(symbol),m_new_tick(false) { //--- Reset the structures of the new and previous ticks ::ZeroMemory(this.m_tick); ::ZeroMemory(this.m_tick_prev); //--- If managed to get the current prices to the tick structure, //--- copy data of the obtained tick to the previous tick data and reset the first launch flag if(::SymbolInfoTick(this.m_symbol,this.m_tick)) { this.m_tick_prev=this.m_tick; this.m_first_start=false; } } //+------------------------------------------------------------------+
Antes, esta variable no se inicializaba con el valor inicial en ninguna parte, lo cual no es correcto.
Clase del objeto de datos del tick
En la carpeta donde se ubica la clase del objeto «Nuevo tick» \MQL5\Include\DoEasy\Objects\Ticks\ vamos a crear el nuevo archivo de la clase del objeto de los datos de tick DataTick.mqh.
El objeto no va a representar nada nuevo para las clases de la biblioteca: todo será del tipo estándar. Vamos a analizar el cuerpo de la clase:
//+------------------------------------------------------------------+ //| DataTick.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" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\BaseObj.mqh" #include "..\..\Services\DELib.mqh" //+------------------------------------------------------------------+ //| "Tick" class | //+------------------------------------------------------------------+ class CDataTick : public CBaseObj { private: MqlTick m_tick; // Structure for obtaining current prices int m_digits; // Symbol's digits value long m_long_prop[TICK_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[TICK_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[TICK_PROP_STRING_TOTAL]; // String properties //--- Return the index of the array the tick’s (1) double and (2) string properties are actually located at int IndexProp(ENUM_TICK_PROP_DOUBLE property) const { return(int)property-TICK_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_TICK_PROP_STRING property) const { return(int)property-TICK_PROP_INTEGER_TOTAL-TICK_PROP_DOUBLE_TOTAL;} public: //--- Set tick’s (1) integer, (2) real and (3) string property void SetProperty(ENUM_TICK_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_TICK_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_TICK_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Return tick’s (1) integer, (2) real and (3) string property from the properties array long GetProperty(ENUM_TICK_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_TICK_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_TICK_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Return the flag of the tick supporting this property virtual bool SupportProperty(ENUM_TICK_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_TICK_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_TICK_PROP_STRING property) { return true; } //--- Return itself CDataTick *GetObject(void) { return &this;} //--- Compare CDataTick objects with each other by the specified property (for sorting the lists by a specified object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CDataTick objects with each other by all properties (to search equal objects) bool IsEqual(CDataTick* compared_obj) const; //--- Constructors CDataTick(){;} CDataTick(const string symbol,const MqlTick &tick); //+------------------------------------------------------------------+ //| Descriptions of object tick data properties | //+------------------------------------------------------------------+ //--- Return description of tick's (1) integer, (2) real and (3) string property string GetPropertyDescription(ENUM_TICK_PROP_INTEGER property); string GetPropertyDescription(ENUM_TICK_PROP_DOUBLE property); string GetPropertyDescription(ENUM_TICK_PROP_STRING property); //--- Display the description of tick properties in the journal (full_prop=true - all properties, false - supported ones only) void Print(const bool full_prop=false); //--- Display a short description of the tick in the journal virtual void PrintShort(void); //--- Return the (1) short name and (2) description of tick data object flags virtual string Header(void); string FlagsDescription(void); //+------------------------------------------------------------------+ //| Methods of simplified access to tick data object properties | //+------------------------------------------------------------------+ //--- Return tick’s (1) Time, (2) time in milliseconds, (3) volume, (4) flags datetime Time(void) const { return (datetime)this.GetProperty(TICK_PROP_TIME); } long TimeMSC(void) const { return this.GetProperty(TICK_PROP_TIME_MSC); } long Volume(void) const { return this.GetProperty(TICK_PROP_VOLUME); } uint Flags(void) const { return (uint)this.GetProperty(TICK_PROP_FLAGS); } //--- Return tick’s (1) Bid, (2) Ask, (3) Last price, (4) volume with greater accuracy, (5) spread of the tick //--- size of the (9) candle upper, (10) lower wick double Bid(void) const { return this.GetProperty(TICK_PROP_BID); } double Ask(void) const { return this.GetProperty(TICK_PROP_ASK); } double Last(void) const { return this.GetProperty(TICK_PROP_LAST); } double VolumeReal(void) const { return this.GetProperty(TICK_PROP_VOLUME_REAL); } double Spread(void) const { return this.GetProperty(TICK_PROP_SPREAD); } //--- Return tick symbol string Symbol(void) const { return this.GetProperty(TICK_PROP_SYMBOL); } //--- Return bar (1) time, (2) index on the specified timeframe the tick time falls into datetime TimeBar(const ENUM_TIMEFRAMES timeframe)const { return ::iTime(this.Symbol(),timeframe,this.Index(timeframe)); } int Index(const ENUM_TIMEFRAMES timeframe) const { return ::iBarShift(this.Symbol(),(timeframe==PERIOD_CURRENT ? ::Period() : timeframe),this.Time()); } //--- Return the flag of (1) Bid, (2) Ask, (3) Last price, (4) volume change; (5) buy and (6) sell trades bool IsChangeBid() const { return((this.Flags() & TICK_FLAG_BID)==TICK_FLAG_BID); } bool IsChangeAsk() const { return((this.Flags() & TICK_FLAG_ASK)==TICK_FLAG_ASK); } bool IsChangeLast() const { return((this.Flags() & TICK_FLAG_LAST)==TICK_FLAG_LAST); } bool IsChangeVolume() const { return((this.Flags() & TICK_FLAG_VOLUME)==TICK_FLAG_VOLUME); } bool IsChangeBuy() const { return((this.Flags() & TICK_FLAG_BUY)==TICK_FLAG_BUY); } bool IsChangeSell() const { return((this.Flags( )& TICK_FLAG_SELL)==TICK_FLAG_SELL); } //--- }; //+------------------------------------------------------------------+
En el primer artículo, ya hablamos en detalle de la estructura y de la creación de los objetos de la biblioteca. Ahora, sólo vamos a repasar rápidamente las variables y los métodos principales, y luego, analizaremos su implementación en la clase.
En la sección privada de la clase, se encuentran las variables auxiliares, las matrices para almacenar los valores de las propiedades de tipo entero, real y string del objeto, así como los métodos que devuelven los índices reales de las propiedades de acuerdo de los cuales ellas se ubican físicamente en las matrices.
En la sección pública de la clase, se encuentran los métodos para establecer y devolver valores de las propiedades indicadas en las matrices correspondientes de las propiedades del objeto, los métodos que devuelven las banderas del soporte de una u otra propiedad por el objeto, los métodos para buscar, comparar y ordenar objetos, así como los constructores de la clase.
Ahí mismo, se encuentran los métodos para visualizar las descripciones de las propiedades del objeto y los métodos para un acceso simplificado a las propiedades del objeto.
Ahora, vamos a analizar la implementación de los métodos de la clase.
Al constructor paramétrico de la clase se le transmite el símbolo y la estructura rellenada con datos del tick actual:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CDataTick::CDataTick(const string symbol,const MqlTick &tick) { //--- Save symbol’s Digits this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS); //--- Save integer tick properties this.SetProperty(TICK_PROP_TIME,tick.time); this.SetProperty(TICK_PROP_TIME_MSC,tick.time_msc); this.SetProperty(TICK_PROP_VOLUME,tick.volume); this.SetProperty(TICK_PROP_FLAGS,tick.flags); //--- Save real tick properties this.SetProperty(TICK_PROP_BID,tick.bid); this.SetProperty(TICK_PROP_ASK,tick.ask); this.SetProperty(TICK_PROP_LAST,tick.last); this.SetProperty(TICK_PROP_VOLUME_REAL,tick.volume_real); //--- Save additional tick properties this.SetProperty(TICK_PROP_SPREAD,tick.ask-tick.bid); this.SetProperty(TICK_PROP_SYMBOL,(symbol==NULL || symbol=="" ? ::Symbol() : symbol)); } //+------------------------------------------------------------------+
Dentro del constructor de la clase, simplemente rellenamos las propiedades del objeto con valores correspondientes desde la estructura del tick y el símbolo del que se reciben los datos del tick.
Método de comparación de dos parámetros de objetos según la propiedad indicada:
//+--------------------------------------------------------------------+ //| Compare CDataTick objects with each other by the specified property| //+--------------------------------------------------------------------+ int CDataTick::Compare(const CObject *node,const int mode=0) const { const CDataTick *obj_compared=node; //--- compare integer properties of two objects if(mode<TICK_PROP_INTEGER_TOTAL) { long value_compared=obj_compared.GetProperty((ENUM_TICK_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_TICK_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare real properties of two objects else if(mode<TICK_PROP_DOUBLE_TOTAL+TICK_PROP_INTEGER_TOTAL) { double value_compared=obj_compared.GetProperty((ENUM_TICK_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_TICK_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare string properties of two objects else if(mode<TICK_PROP_DOUBLE_TOTAL+TICK_PROP_INTEGER_TOTAL+TICK_PROP_STRING_TOTAL) { string value_compared=obj_compared.GetProperty((ENUM_TICK_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_TICK_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+
Al método se le transmite el puntero al objeto con el que es necesario comparar el objeto actual, y el tipo de la propiedad según la cual van a compararse estos dos objetos.
Dependiendo del modo transmitido de la comparación de objetos, comparamos estas propiedades de dos objetos y devolvemos 1/-1/0 si el valor de la propiedad del objeto actual es mayor/menor o igual al valor de la propiedad del objeto comparado, respectivamente.
El método que compara si dos objetos son iguales:
//+------------------------------------------------------------------+ //| Compare CDataTick objects with each other by all properties | //+------------------------------------------------------------------+ bool CDataTick::IsEqual(CDataTick *compared_obj) const { int beg=0, end=TICK_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_TICK_PROP_INTEGER prop=(ENUM_TICK_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=TICK_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_TICK_PROP_DOUBLE prop=(ENUM_TICK_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=TICK_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_TICK_PROP_STRING prop=(ENUM_TICK_PROP_STRING)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
Aquí: al método se le transmite el puntero al objeto para la comparación. Luego, en cada grupo de propiedades, comparamos en tres ciclos cada siguiente propiedad de dos objetos. Si aunque sea una sola de las propiedades no es igual a la misma del objeto comparado, devolvemos false. Una vez finalizados los tres ciclos, devolvemos true, lo que significa que todas las propiedades de ambos objetos son iguales, entonces, los objetos son idénticos.
Método para mostrar en el diario todas las propiedades del objeto:
//+------------------------------------------------------------------+ //| Display tick properties in the journal | //+------------------------------------------------------------------+ void CDataTick::Print(const bool full_prop=false) { ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") ============="); int beg=0, end=TICK_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_TICK_PROP_INTEGER prop=(ENUM_TICK_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=TICK_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_TICK_PROP_DOUBLE prop=(ENUM_TICK_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=TICK_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_TICK_PROP_STRING prop=(ENUM_TICK_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.Header(),") =============\n"); } //+------------------------------------------------------------------+
Aquí: recorriendo en tres ciclos cada uno de los grupos de las propiedades del objeto, mostramos la descripción de cada propiedad siguiente a través del método correspondiente GetPropertyDescription(). Si en los parámetros del método, se transmite el valor false en la variable full_prop, se visualizan sólo las propiedades soportadas por el objeto. Para una propiedad que no se soporta, en el registro se muestra que esta propiedad no se soporta. Aunque en este objeto, se soportan todas las propiedades, pero se puede modificar eso en los objetos herederos de la clase.
Los métodos que retornan la descripción de la propiedad especificada de tipo entero, real y string del objeto:
//+------------------------------------------------------------------+ //| Return description of tick's integer property | //+------------------------------------------------------------------+ string CDataTick::GetPropertyDescription(ENUM_TICK_PROP_INTEGER property) { return ( property==TICK_PROP_TIME_MSC ? CMessage::Text(MSG_TICK_TIME_MSC)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+TimeMSCtoString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ) : property==TICK_PROP_TIME ? CMessage::Text(MSG_TICK_TIME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ) : property==TICK_PROP_VOLUME ? CMessage::Text(MSG_TICK_VOLUME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==TICK_PROP_FLAGS ? CMessage::Text(MSG_TICK_FLAGS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property)+"\n"+CMessage::Text(MSG_LIB_TEXT_TICK_CHANGED_DATA)+this.FlagsDescription() ) : "" ); } //+------------------------------------------------------------------+ //| Return description of tick's real property | //+------------------------------------------------------------------+ string CDataTick::GetPropertyDescription(ENUM_TICK_PROP_DOUBLE property) { int dg=(this.m_digits>0 ? this.m_digits : 1); return ( property==TICK_PROP_BID ? CMessage::Text(MSG_LIB_PROP_BID)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==TICK_PROP_ASK ? CMessage::Text(MSG_LIB_PROP_ASK)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==TICK_PROP_LAST ? CMessage::Text(MSG_LIB_PROP_LAST)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==TICK_PROP_VOLUME_REAL ? CMessage::Text(MSG_TICK_VOLUME_REAL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : property==TICK_PROP_SPREAD ? CMessage::Text(MSG_TICK_SPREAD)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : "" ); } //+------------------------------------------------------------------+ //| Return description of tick's string property | //+------------------------------------------------------------------+ string CDataTick::GetPropertyDescription(ENUM_TICK_PROP_STRING property) { return(property==TICK_PROP_SYMBOL ? CMessage::Text(MSG_LIB_PROP_SYMBOL)+": \""+this.GetProperty(property)+"\"" : ""); } //+------------------------------------------------------------------+
Aquí: dependiendo de la propiedad transmitida al método, retornamos su descripción de tipo string.
Método que devuelve la cadena con la descripción de todas las banderas del tick:
//+------------------------------------------------------------------+ //| Display the description of flags | //+------------------------------------------------------------------+ string CDataTick::FlagsDescription(void) { string flags= ( (this.IsChangeAsk() ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_ASK) : "")+ (this.IsChangeBid() ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_BID) : "")+ (this.IsChangeLast() ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_LAST) : "")+ (this.IsChangeVolume() ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_VOLUME) : "")+ (this.IsChangeBuy() ? "\n - "+CMessage::Text(MSG_DEAL_TO_BUY) : "")+ (this.IsChangeSell() ? "\n - "+CMessage::Text(MSG_DEAL_TO_SELL) : "") ); return flags; } //+------------------------------------------------------------------+
En las propiedades de cada tick hay una variable que almacena un conjunto de banderas. Estas banderas describen los eventos que provocan este tick.
Información sobre MqlTick en el manual de referencia
Para saber qué datos han sido alterados con la llegada del tick actual, analizamos sus banderas:
- TICK_FLAG_BID — el tick ha cambiado el precio bid
- TICK_FLAG_ASK — el tick ha cambiado el precio ask
- TICK_FLAG_LAST — el tick ha cambiado el precio de la última transacción
- TICK_FLAG_VOLUME — el tick ha cambiado el volumen
- TICK_FLAG_BUY — el tick ha aparecido como resultado de una transacción de compra
- TICK_FLAG_SELL — el tick ha aparecido como resultado de una transacción de venta
Tenemos seis métodos (según el número de banderas) que devuelven la bandera de la presencia de cada una de las banderas en la variable:
//--- Return the flag of (1) Bid, (2) Ask, (3) Last price, (4) volume change; (5) buy and (6) sell trades bool IsChangeBid() const { return((this.Flags() & TICK_FLAG_BID)==TICK_FLAG_BID); } bool IsChangeAsk() const { return((this.Flags() & TICK_FLAG_ASK)==TICK_FLAG_ASK); } bool IsChangeLast() const { return((this.Flags() & TICK_FLAG_LAST)==TICK_FLAG_LAST); } bool IsChangeVolume() const { return((this.Flags() & TICK_FLAG_VOLUME)==TICK_FLAG_VOLUME); } bool IsChangeBuy() const { return((this.Flags() & TICK_FLAG_BUY)==TICK_FLAG_BUY); } bool IsChangeSell() const { return((this.Flags() & TICK_FLAG_SELL)==TICK_FLAG_SELL); }
En el método descrito, primero, se verifica la bandera de la presencia de la bandera en la estructura de la variable. Dependiendo de que si este cambio ha tenido lugar o no, en la cadena resultante se escribe el avance de línea + la descripción de la bandera, o bien, una cadena vacía. Después de verificar todas las banderas, como resultado, tenemos una cadena completada que contiene las descripciones de las banderas que se encuentran en la variable. Si tenemos varias banderas, la descripción de cada una de ellas va a comenzar a partir de una línea nueva en el registro.
Método que muestra en el registro la descripción breve del objeto de los datos de tick:
//+------------------------------------------------------------------+ //| Display a short description of the tick in the journal | //+------------------------------------------------------------------+ void CDataTick::PrintShort(void) { ::Print(this.Header()); } //+------------------------------------------------------------------+
El método simplemente muestra en el registro su nombre breve que se devuelve por el siguiente método:
//+------------------------------------------------------------------+ //| Return a short name of tick data object | //+------------------------------------------------------------------+ string CDataTick::Header(void) { return ( CMessage::Text(MSG_TICK_TEXT_TICK)+" \""+this.Symbol()+"\" "+TimeMSCtoString(TimeMSC()) ); } //+------------------------------------------------------------------+
Con esto, damos por finalizada la creación del objeto de los datos de ticks.
Probando el objeto de datos del tick
Para la simulación, vamos a tomar el asesor del artículo anterior
y guardarlo en la nueva carpeta \MQL5\Experts\TestDoEasy\Part59\ con el nombre nuevo TestDoEasyPart59.mq5
.
En este asesor, ya no necesitamos los indicadores y sus datos con sus series temporales. Aquí, simplemente vamos a crear el objeto de los datos de ticks, mostrar su descripción completa en el registro y su descripción breve en el comentario en el gráfico. Con la llegada de cada tick nuevo, su descripción en el gráfico va a cambiar, mientras que en el registro se visualizará el primer tick que llegue tras la ejecución de asesor.
Dado que todavía no hay ninguna vinculación del asesor con este objeto nuevo de la biblioteca, simplemente vamos a incluir el archivo de su clase en el asesor:
//+------------------------------------------------------------------+ //| TestDoEasyPart59.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> #include <DoEasy\Objects\Ticks\DataTick.mqh>
En el área de variables globales del asesor, en vez de las matrices de los parámetros de indicadores personalizados,
//--- Arrays of custom indicator parameters MqlParam param_ma1[]; MqlParam param_ma2[]; //+------------------------------------------------------------------+
declaramos el objeto de la clase «Nuevo tick». Lo necesitamos para simular el trabajo dentro de las futuras clases de colección de los datos de ticks de la biblioteca:
//--- 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; //--- "New tick" object CNewTickObj check_tick; //+------------------------------------------------------------------+
En el manejador OnInit() eliminamos el bloque de la creación de indicadores:
//--- 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,1,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,1,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();
Al final de OnInit() establecemos el símbolo actual para el objeto «Nuevo tick» como símbolo de trabajo:
//--- Set the current symbol for "New tick" object check_tick.SetSymbol(Symbol()); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Añadimos al manejador OnTick() del asesor el bloque del código para determinar un tick nuevo (como simulación del trabajo dentro de la futura clase de colección de los ticks) y para crear un nuevo objeto de los datos de ticks con la visualización de su descripción en el gráfico y en el registro:
//+------------------------------------------------------------------+ //| 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 } //--- Create a temporary list for storing “Tick data” objects, //--- a variable for obtaining tick data and //--- a variable for calculating incoming ticks static int tick_count=0; CArrayObj list; MqlTick tick_struct; //--- Check a new tick on the current symbol if(check_tick.IsNewTick()) { //--- If failed to get the price - exit if(!SymbolInfoTick(Symbol(),tick_struct)) return; //--- Create a new tick data object CDataTick *tick_obj=new CDataTick(Symbol(),tick_struct); if(tick_obj==NULL) return; //--- Increase tick counter (simply to display on the screen, no other purpose is provided) tick_count++; //--- Limit the number of ticks in the counting as one hundred thousand (again, no purpose is provided) if(tick_count>100000) tick_count=1; //--- In the comment on the chart display the tick number and its short description Comment("--- №",IntegerToString(tick_count,5,'0'),": ",tick_obj.Header()); //--- If this is the first tick (which follows the first launch of EA) display its full description in the journal if(tick_count==1) tick_obj.Print(); //--- Remove if failed to put the created tick data object in the list if(!list.Add(tick_obj)) delete tick_obj; } //--- If the trailing flag is set if(trailing_on) { TrailingPositions(); // Trailing positions TrailingOrders(); // Trailing pending orders } } //+------------------------------------------------------------------+
La lógica se describe detalladamente en los comentarios del listado. Creo que todo está bien claro y es fácil.
Compilamos el EA y lo iniciamos en el gráfico, estableciendo previamente en los ajustes el uso del símbolo y marco temporal actuales. Después de ejecutarlo y recibir un tick nuevo, la descripción del objeto de los datos de ticks (tick recibido) se visualizará en el registro:
Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10426.13 USD, 1:100, Hedge, MetaTrader 5 demo --- 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: 5153 Library initialization time: 00:00:00.000 ============= Beginning of parameter list (Tick "EURUSD" 2020.12.16 13:22:32.822) ============= Last price update time in milliseconds: 2020.12.16 13:22:32.822 Last price update time: 2020.12.16 13:22:32 Volume for the current Last price: 0 Flags: 6 Changed data on the tick: - Ask price change - Bid price change ------ Bid price: 1.21927 Ask price: 1.21929 Last price: 0.00000 Volume for the current Last price with greater accuracy: 0.00 Spread: 0.00002 ------ Symbol: "EURUSD" ============= End of parameter list (Tick "EURUSD" 2020.12.16 13:22:32.822) =============
Con la llegada de cada tick nuevo, el comentario con su descripción breve se mostrará en el gráfico:
¿Qué es lo próximo?
En el siguiente artículo, comenzaremos a crear la colección de los datos de ticks para un símbolo.
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.
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
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
Trabajando con las series temporales en la biblioteca DoEasy (Parte 57): Objeto de datos del búfer de indicador
Trabajando con las series temporales en la biblioteca DoEasy (Parte 58): Series temporales de los datos de búferes de indicadores
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/8818
- 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