
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
Contenido
- Concepto
- Mejorando los objetos de series temporales creados anteriormente
- La clase de colección de los objetos de series temporales de los símbolos y periodos
- Simulación
- ¿Qué es lo próximo?
Concepto
- Al principio de esta serie, creamos el objeto de barra, que contiene los datos de una barra con el símbolo y el periodo del gráfico establecidos.
- También cramos la colección de barras, un objeto de serie temporal con el símbolo y periodo del gráfico establecidos.
- Asimismo, combinamos todos los objetos de un símbolo en un objeto de serie temporal de un mismo símbolo.
Hoy, vamos a crear un objeto de colección con las series temporales de los símbolos usados en el programa. Cada una de ellas contendrá los datos de los marcos temporales establecidos de un símbolo. Como resultado, obtendremos un objeto que contendrá todos los datos sobre el número de barras establecido para cada serie temporal de cada símbolo.
La colección de series temporales guardará todos los datos históricos necesarios de cada uno de los símbolos utilizados en el programa y de todos los marcos temporales que también se establecerán en los ajustes del programa.
Además de esto, la colección permitirá establecer los datos necesarios para cada marco temporal de cada símbolo de forma individual.
Debido a que la descripción de la funcionalidad creada para la colección de series temporales es muy voluminosa, implementaremos las actualizaciones en tiempo real de los datos de la colección y la obtención de todos los datos posibles de la colección en el próximo artículo.
Mejorando los objetos de series temporales creados anteriormente
La mayoría de los objetos de la biblioteca son herederos del objeto básico de todos los objetos de la biblioteca, que a su vez es heredado de la clase básica para construir la Biblioteca estándar MQL5.
A medida que han aumentado las demandas de la biblioteca, ha crecido también la clase CBaseObj del objeto básico de la biblioteca, y ahora, si queremos heredar nuevos objetos de él, estos obtendrán métodos adicionales, que no siempre resultan necesarios.
Para resolver este problema, dividiremos la clase del objeto básico en dos:
- la primera (CBaseObj) contendrá el conjunto mínimo imprescindible de propiedades y métodos para cada objeto de la biblioteca,
- la segunda será ampliada (CBaseObjExt), descendiente de CBaseObj, y contendrá las propiedades y métodos para actuar de forma interactiva con el usuario y la funcionalidad de eventos de los objetos herederos.
De esta forma, los objetos que necesiten las propiedades y métodos básicos, los heredaremos de CBaseObj, mientras que los objetos que necesiten la funcionalidad de eventos, los heredaremos de CBaseObjExt.
¿Cómo lo haremos? Para empezar, solo tenemos que renombrar la clase del objeto básico CBaseObj que se encuentra en el archivo \MQL5\Include\DoEasy\Objects\BaseObj.mqh como la clase con el nombre CBaseObjExt y compilar el archivo del objeto principal de la biblioteca CEngine que se ubica en la dirección \MQL5\Include\DoEasy\Engine.mqh, lo cual provocará una larga lista de errores de compilación (hemos renombrado la clase del objeto básico de la biblioteca).
Simplemente iteraremos por la lista con todos los errores que indican la ausencia de la clase CBaseObj, y sustituiremos en los listados de las clases todas las entradas de las líneas "CBaseObj" por "CBaseObjExt". La nueva compilación con las denominaciones de la clase del objeto básico ya corregidas deberá realizarse con éxito.
Ahora, en el listado de la clase del objeto básico, añadimos una nueva clase que llamaremos CBaseObj, heredamos esta del objeto básico de la biblioteca MQL5, y después trasladamos desde la clase CBaseObjExt todas las variables y métodos que deben estar en la nueva clase del objeto básico, mientras que la clase CBaseObjExt la heredamos de CBaseObj.
Parece complicado, pero si echamos un vistazo al listado de las clases, todo resultará comprensible (no tiene sentido mostrar el listado completo, el lector podrá estudiarlo en los archivos adjuntos al artículo):
//+------------------------------------------------------------------+ //| Base object class for all library objects | //+------------------------------------------------------------------+ class CBaseObj : public CObject { protected: ENUM_LOG_LEVEL m_log_level; // Logging level ENUM_PROGRAM_TYPE m_program; // Program type bool m_first_start; // First launch flag bool m_use_sound; // Flag of playing the sound set for an object bool m_available; // Flag of using a descendant object in the program int m_global_error; // Global error code long m_chart_id_main; // Control program chart ID long m_chart_id; // Chart ID string m_name; // Object name string m_folder_name; // Name of the folder storing CBaseObj descendant objects string m_sound_name; // Object sound file name int m_type; // Object type (corresponds to the collection IDs) public: //--- (1) Set, (2) return the error logging level void SetLogLevel(const ENUM_LOG_LEVEL level) { this.m_log_level=level; } ENUM_LOG_LEVEL GetLogLevel(void) const { return this.m_log_level; } //--- (1) Set and (2) return the chart ID of the control program void SetMainChartID(const long id) { this.m_chart_id_main=id; } long GetMainChartID(void) const { return this.m_chart_id_main; } //--- (1) Set and (2) return chart ID void SetChartID(const long id) { this.m_chart_id=id; } long GetChartID(void) const { return this.m_chart_id; } //--- (1) Set the sub-folder name, (2) return the folder name for storing descendant object files void SetSubFolderName(const string name) { this.m_folder_name=DIRECTORY+name; } string GetFolderName(void) const { return this.m_folder_name; } //--- (1) Set and (2) return the name of the descendant object sound file void SetSoundName(const string name) { this.m_sound_name=name; } string GetSoundName(void) const { return this.m_sound_name; } //--- (1) Set and (2) return the flag of playing descendant object sounds void SetUseSound(const bool flag) { this.m_use_sound=flag; } bool IsUseSound(void) const { return this.m_use_sound; } //--- (1) Set and (2) return the flag of using the descendant object in the program void SetAvailable(const bool flag) { this.m_available=flag; } bool IsAvailable(void) const { return this.m_available; } //--- Return the global error code int GetError(void) const { return this.m_global_error; } //--- Return the object name string GetName(void) const { return this.m_name; } //--- Return an object type virtual int Type(void) const { return this.m_type; } //--- Constructor CBaseObj() : m_program((ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE)), m_global_error(ERR_SUCCESS), m_log_level(LOG_LEVEL_ERROR_MSG), m_chart_id_main(::ChartID()), m_chart_id(::ChartID()), m_folder_name(DIRECTORY), m_sound_name(""), m_name(__FUNCTION__), m_type(0), m_use_sound(false), m_available(true), m_first_start(true) {} }; //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Extended base object class for all library objects | //+------------------------------------------------------------------+ #define CONTROLS_TOTAL (10) class CBaseObjExt : public CBaseObj { private: int m_long_prop_total; int m_double_prop_total; //--- Fill in the object property array template<typename T> bool FillPropertySettings(const int index,T &array[][CONTROLS_TOTAL],T &array_prev[][CONTROLS_TOTAL],int &event_id); protected: CArrayObj m_list_events_base; // Object base event list CArrayObj m_list_events; // Object event list MqlTick m_tick; // Tick structure for receiving quote data double m_hash_sum; // Object data hash sum double m_hash_sum_prev; // Object data hash sum during the previous check int m_digits_currency; // Number of decimal places in an account currency bool m_is_event; // Object event flag int m_event_code; // Object event code int m_event_id; // Event ID (equal to the object property value) //--- Data for storing, controlling and returning tracked properties: //--- [Property index][0] Controlled property increase value //--- [Property index][1] Controlled property decrease value //--- [Property index][2] Controlled property value level //--- [Property index][3] Property value //--- [Property index][4] Property value change //--- [Property index][5] Flag of a property change exceeding the increase value //--- [Property index][6] Flag of a property change exceeding the decrease value //--- [Property index][7] Flag of a property increase exceeding the control level //--- [Property index][8] Flag of a property decrease being less than the control level //--- [Property index][9] Flag of a property value being equal to the control level long m_long_prop_event[][CONTROLS_TOTAL]; // The array for storing object's integer properties values and controlled property change values double m_double_prop_event[][CONTROLS_TOTAL]; // The array for storing object's real properties values and controlled property change values long m_long_prop_event_prev[][CONTROLS_TOTAL]; // The array for storing object's controlled integer properties values during the previous check double m_double_prop_event_prev[][CONTROLS_TOTAL]; // The array for storing object's controlled real properties values during the previous check //--- Return (1) time in milliseconds, (2) milliseconds from the MqlTick time value long TickTime(void) const { return #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ; } ushort MSCfromTime(const long time_msc) const { return #ifdef __MQL5__ ushort(this.TickTime()%1000) #else 0 #endif ; } //--- return the flag of the event code presence in the event object bool IsPresentEventFlag(const int change_code) const { return (this.m_event_code & change_code)==change_code; } //--- Return the number of decimal places of the account currency int DigitsCurrency(void) const { return this.m_digits_currency; } //--- Returns the number of decimal places in the 'double' value int GetDigits(const double value) const; //--- Set the size of the array of controlled (1) integer and (2) real object properties bool SetControlDataArraySizeLong(const int size); bool SetControlDataArraySizeDouble(const int size); //--- Check the array size of object properties bool CheckControlDataArraySize(bool check_long=true); //--- Check the list of object property changes and create an event void CheckEvents(void); //--- (1) Pack a 'ushort' number to a passed 'long' number long UshortToLong(const ushort ushort_value,const uchar to_byte,long &long_value); protected: //--- (1) convert a 'ushort' value to a specified 'long' number byte long UshortToByte(const ushort value,const uchar to_byte) const; public: //--- Set the value of the pbject property controlled (1) increase, (2) decrease, (3) control level template<typename T> void SetControlledValueINC(const int property,const T value); template<typename T> void SetControlledValueDEC(const int property,const T value); template<typename T> void SetControlledValueLEVEL(const int property,const T value); //--- Return the set value of the controlled (1) integer and (2) real object properties increase long GetControlledLongValueINC(const int property) const { return this.m_long_prop_event[property][0]; } double GetControlledDoubleValueINC(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][0]; } //--- Return the set value of the controlled (1) integer and (2) real object properties decrease long GetControlledLongValueDEC(const int property) const { return this.m_long_prop_event[property][1]; } double GetControlledDoubleValueDEC(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][1]; } //--- Return the specified control level of object's (1) integer and (2) real properties long GetControlledLongValueLEVEL(const int property) const { return this.m_long_prop_event[property][2]; } double GetControlledDoubleValueLEVEL(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][2]; } //--- Return the current value of the object (1) integer and (2) real property long GetPropLongValue(const int property) const { return this.m_long_prop_event[property][3]; } double GetPropDoubleValue(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][3]; } //--- Return the change value of the controlled (1) integer and (2) real object property long GetPropLongChangedValue(const int property) const { return this.m_long_prop_event[property][4]; } double GetPropDoubleChangedValue(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][4]; } //--- Return the flag of an (1) integer and (2) real property value change exceeding the increase value long GetPropLongFlagINC(const int property) const { return this.m_long_prop_event[property][5]; } double GetPropDoubleFlagINC(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][5]; } //--- Return the flag of an (1) integer and (2) real property value change exceeding the decrease value long GetPropLongFlagDEC(const int property) const { return this.m_long_prop_event[property][6]; } double GetPropDoubleFlagDEC(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][6]; } //--- Return the flag of an (1) integer and (2) real property value increase exceeding the control level long GetPropLongFlagMORE(const int property) const { return this.m_long_prop_event[property][7]; } double GetPropDoubleFlagMORE(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][7]; } //--- Return the flag of an (1) integer and (2) real property value decrease being less than the control level long GetPropLongFlagLESS(const int property) const { return this.m_long_prop_event[property][8]; } double GetPropDoubleFlagLESS(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][8]; } //--- Return the flag of an (1) integer and (2) real property being equal to the control level long GetPropLongFlagEQUAL(const int property) const { return this.m_long_prop_event[property][9]; } double GetPropDoubleFlagEQUAL(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][9]; } //--- Reset the variables of (1) tracked and (2) controlled object data (can be reset in the descendants) void ResetChangesParams(void); virtual void ResetControlsParams(void); //--- Add the (1) object event and (2) the object event reason to the list bool EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam); bool EventBaseAdd(const int event_id,const ENUM_BASE_EVENT_REASON reason,const double value); //--- Set/return the occurred event flag to the object data void SetEvent(const bool flag) { this.m_is_event=flag; } bool IsEvent(void) const { return this.m_is_event; } //--- Return (1) the list of events, (2) the object event code and (3) the global error code CArrayObj *GetListEvents(void) { return &this.m_list_events; } int GetEventCode(void) const { return this.m_event_code; } //--- Return (1) an event object and (2) a base event by its number in the list CEventBaseObj *GetEvent(const int shift=WRONG_VALUE,const bool check_out=true); CBaseEvent *GetEventBase(const int index); //--- Return the number of (1) object events int GetEventsTotal(void) const { return this.m_list_events.Total(); } //--- Update the object data to search for changes (Calling from the descendants: CBaseObj::Refresh()) virtual void Refresh(void); //--- Return an object event description string EventDescription(const int property, const ENUM_BASE_EVENT_REASON reason, const int source, const string value, const string property_descr, const int digits); //--- Data location in the magic number int value //----------------------------------------------------------- // bit 32|31 24|23 16|15 8|7 0| //----------------------------------------------------------- // byte | 3 | 2 | 1 | 0 | //----------------------------------------------------------- // data | uchar | uchar | ushort | //----------------------------------------------------------- // descr |pend req id| id2 | id1 | magic | //----------------------------------------------------------- //--- Set the ID of the (1) first group, (2) second group, (3) pending request to the magic number value void SetGroupID1(const uchar group,uint &magic) { magic &=0xFFF0FFFF; magic |= uint(this.ConvToXX(group,0)<<16); } void SetGroupID2(const uchar group,uint &magic) { magic &=0xFF0FFFFF; magic |= uint(this.ConvToXX(group,1)<<16); } void SetPendReqID(const uchar id,uint &magic) { magic &=0x00FFFFFF; magic |= (uint)id<<24; } //--- Convert the value of 0 - 15 into the necessary uchar number bits (0 - lower, 1 - upper ones) uchar ConvToXX(const uchar number,const uchar index) const { return((number>15 ? 15 : number)<<(4*(index>1 ? 1 : index))); } //--- Return (1) the specified magic number, the ID of (2) the first group, (3) second group, (4) pending request from the magic number value ushort GetMagicID(const uint magic) const { return ushort(magic & 0xFFFF); } uchar GetGroupID1(const uint magic) const { return uchar(magic>>16) & 0x0F; } uchar GetGroupID2(const uint magic) const { return uchar((magic>>16) & 0xF0)>>4; } uchar GetPendReqID(const uint magic) const { return uchar(magic>>24) & 0xFF; } //--- Constructor CBaseObjExt(); }; //+------------------------------------------------------------------+
En la clase del nuevo objeto básico de todos los objetos de la biblioteca, ahora se declaran tres nuevas variables de miembro de clase:
bool m_use_sound; // Flag of playing the sound set for an object bool m_available; // Flag of using a descendant object in the program string m_sound_name; // Object sound file name
y se han añadido los métodos correspondientes para establecer y retornar los valores de estas variables:
//--- (1) Set and (2) return the name of the descendant object sound file void SetSoundName(const string name) { this.m_sound_name=name; } string GetSoundName(void) const { return this.m_sound_name; } //--- (1) Set and (2) return the flag of playing descendant object sounds void SetUseSound(const bool flag) { this.m_use_sound=flag; } bool IsUseSound(void) const { return this.m_use_sound; } //--- (1) Set and (2) return the flag of using the descendant object in the program void SetAvailable(const bool flag) { this.m_available=flag; } bool IsAvailable(void) const { return this.m_available; }
El nombre del archivo de sonido para el objeto heredero permitirá establecer (SetSoundName()) u obtener (GetSoundName()) el nombre del archivo de sonido del objeto, que podrá ser reproducido al darse la condición de la propiedad de control de este objeto.
Además de poder asignar un nombre al objeto, podremos activar/desactivar (SetUseSound()) el permiso de reproducción de este archivo y obtener la bandera de permiso establecido para la reproducción del archivo (IsUseSound()).
Qué indica la "bandera de uso del objeto heredero en el programa", y para qué necesitamos establecerla y obtenerla...
Por ejemplo, tenemos los objetos de las series temporales del símbolo para los periodos М5, М30, Н1 y D1. Pero, en algún momento, no queremos procesar la serie temporal М5. Así, estableciendo/quitando esta bandera, podremos gestionar la necesidad de controlar la biblioteca de eventos, por ejemplo, de una nueva barra para la serie temporal М5.
La presencia de esta bandera en el objeto básico de todos los objetos de la biblioteca nos permitirá gestionar de forma flexible la necesidad de procesar los estados de las propiedades de estos objetos. En otras palabras: si debemos controlar y usar un objeto en el programa, estableceremos la bandera; si no tenemos esa necesidad, quitaremos la bandera.
Naturalmente, también han sido modificados los constructores de las clases: hemos eliminado las variables trasladadas a la nueva clase, al tiempo que inicializamos todas las variables en la nueva clase. El lector podrá familiarizarse con los cambios estudiando los archivos adjuntos al artículo.
Qué clases se han heredado ahora del objeto básico ampliado CBaseObjExt:
- CAccountsCollection en el archivo \MQL5\Include\DoEasy\Collections\AccountsCollection.mqh
- CEventsCollection en el archivo \MQL5\Include\DoEasy\Collections\EventsCollection.mqh
(se han sustituido las líneas "CBaseObj::EventAdd" por "CBaseObjExt::EventAdd")
- CSymbolsCollection en el archivo \MQL5\Include\DoEasy\Collections\SymbolsCollection.mqh
- CAccount en el archivo \MQL5\Include\DoEasy\Objects\Accounts\Account.mqh
(se han sustituido las líneas "CBaseObj::Refresh()" por "CBaseObjExt::Refresh()") - COrder en el archivo \MQL5\Include\DoEasy\Objects\Orders\Order.mqh
- CPendRequest en el archivo \MQL5\Include\DoEasy\Objects\PendRequest\PendRequest.mqh
(se han sustituido las líneas "return CBaseObj::GetMagicID" por "return CBaseObjExt::GetMagicID",
CBaseObj::GetGroupID1" por "return CBaseObjExt::GetGroupID1" y CBaseObj::GetGroupID2" por "return CBaseObjExt::GetGroupID2") - CSymbol en el archivo \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh
(se han sustituido las líneas "CBaseObj::Refresh()" por "CBaseObjExt::Refresh()") - CTradeObj en el archivo \MQL5\Include\DoEasy\Objects\Trade\TradeObj.mqh
(se ha eliminado la variable bool m_use_sound y los métodos SetUseSound() y IsUseSound(), ahora se encuentran en la clase básica) - CTrading en el archivo \MQL5\Include\DoEasy\Trading.mqh
(se ha eliminado la variable bool m_use_sound y el método IsUseSounds(), ahora se encuentran en la clase básica)
Antes de mejorar las clases de los objetos de series temporales ya creadas, vamos a añadir los datos necesarios al archivo Datas.mqh, una nueva macrosustitución que indica el separador en la línea de la lista de los símbolos y marcos temporales utilizados en los parámetros de entrada del programa, la enumeración de los modos de trabajo con los marcos temporales, y los índices de los nuevos mensajes, además de los textos de los mensajes que se corresponden con los índices declarados:
//+------------------------------------------------------------------+ //| Datas.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" //+------------------------------------------------------------------+ //| Macrosustituciones | //+------------------------------------------------------------------+ #define INPUT_SEPARATOR (",") // Separator in the inputs string #define TOTAL_LANG (2) // Number of used languages //+------------------------------------------------------------------+ //| Enumerations | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Modes of working with symbols | //+------------------------------------------------------------------+ enum ENUM_SYMBOLS_MODE { SYMBOLS_MODE_CURRENT, // Work with the current symbol only SYMBOLS_MODE_DEFINES, // Work with the specified symbol list SYMBOLS_MODE_MARKET_WATCH, // Work with the Market Watch window symbols SYMBOLS_MODE_ALL // Work with the full symbol list }; //+------------------------------------------------------------------+ //| Mode of working with timeframes | //+------------------------------------------------------------------+ enum ENUM_TIMEFRAMES_MODE { TIMEFRAMES_MODE_CURRENT, // Work with the current timeframe only TIMEFRAMES_MODE_LIST, // Work with the specified timeframe list TIMEFRAMES_MODE_ALL // Work with the full timeframe list }; //+------------------------------------------------------------------+ MSG_LIB_SYS_ERROR_EMPTY_SYMBOLS_STRING, // Error. Predefined symbols string empty, to be used MSG_LIB_SYS_FAILED_PREPARING_SYMBOLS_ARRAY, // Failed to prepare array of used symbols. Error MSG_LIB_SYS_ERROR_EMPTY_PERIODS_STRING, // Error. The string of predefined periods is empty and is to be used MSG_LIB_SYS_FAILED_PREPARING_PERIODS_ARRAY, // Failed to prepare array of used periods. Error MSG_LIB_SYS_INVALID_ORDER_TYPE, // Invalid order type:
...
//--- CTimeSeries MSG_LIB_TEXT_TS_TEXT_FIRS_SET_SYMBOL, // First, set a symbol using SetSymbol() MSG_LIB_TEXT_TS_TEXT_UNKNOWN_TIMEFRAME, // Unknown timeframe MSG_LIB_TEXT_TS_FAILED_GET_SERIES_OBJ, // Failed to receive the timeseries object MSG_LIB_TEXT_TS_REQUIRED_HISTORY_DEPTH, // Requested history depth MSG_LIB_TEXT_TS_ACTUAL_DEPTH, // Actual history depth MSG_LIB_TEXT_TS_AMOUNT_HISTORY_DATA, // Created historical data MSG_LIB_TEXT_TS_HISTORY_BARS, // Number of history bars on the server MSG_LIB_TEXT_TS_TEXT_SYMBOL_TIMESERIES, // Symbol timeseries MSG_LIB_TEXT_TS_TEXT_TIMESERIES, // Timeseries MSG_LIB_TEXT_TS_TEXT_REQUIRED, // Requested MSG_LIB_TEXT_TS_TEXT_ACTUAL, // Actual MSG_LIB_TEXT_TS_TEXT_CREATED, // Created MSG_LIB_TEXT_TS_TEXT_HISTORY_BARS, // On the server MSG_LIB_TEXT_TS_TEXT_SYMBOL_FIRSTDATE, // The very first date by a period symbol MSG_LIB_TEXT_TS_TEXT_SYMBOL_LASTBAR_DATE, // Time of opening the last bar by period symbol MSG_LIB_TEXT_TS_TEXT_SYMBOL_SERVER_FIRSTDATE, // The very first date in history by a server symbol MSG_LIB_TEXT_TS_TEXT_SYMBOL_TERMINAL_FIRSTDATE, // The very first date in history by a symbol in the client terminal }; //+------------------------------------------------------------------+
...
{"Ошибка. Строка предопределённых символов пустая, будет использоваться ","Error. String of predefined symbols is empty, the Symbol will be used: "}, {"Не удалось подготовить массив используемых символов. Ошибка ","Failed to create an array of used symbols. Error "}, {"Ошибка. Строка предопределённых периодов пустая, будет использоваться ","Error. String of predefined periods is empty, the Period will be used: "}, {"Не удалось подготовить массив используемых периодов. Ошибка ","Failed to create an array of used periods. Error "}, {"Неправильный тип ордера: ","Invalid order type: "},...
{"Сначала нужно установить символ при помощи SetSymbol()","First you need to set the Symbol using SetSymbol()"}, {"Неизвестный таймфрейм","Unknown timeframe"}, {"Не удалось получить объект-таймсерию ","Failed to get timeseries object "}, {"Запрошенная глубина истории: ","Required history depth: "}, {"Фактическая глубина истории: ","Actual history depth: "}, {"Создано исторических данных: ","Total historical data created: "}, {"Баров истории на сервере: ","Server history Bars number: "}, {"Таймсерия символа","Symbol time series"}, {"Таймсерия","Timeseries"}, {"Запрошено","Required"}, {"Фактически","Actual"}, {"Создано","Created"}, {"На сервере","On server"}, {"Самая первая дата по символу-периоду","The very first date for the symbol-period"}, {"Время открытия последнего бара по символу-периоду","Open time of the last bar of the symbol-period"}, {"Самая первая дата в истории по символу на сервере","The very first date in the history of the symbol on the server"}, {"Самая первая дата в истории по символу в клиентском терминале","The very first date in the history of the symbol in the client terminal"}, }; //+---------------------------------------------------------------------+
Ya hemos escrito todos los datos necesarios para introducir las mejoras escritas anteriormente (de las clases de las series temporales) y para crear la colección de todas las series temporales.
Vamos a mejorar la clase de objeto temporal del símbolo para un marco temporal de CSeries.
Añadimos a la sección de la clase cuatro nuevas variables y un método para establecer los valores de las fechas de la serie temporal:
//+------------------------------------------------------------------+ //| Timeseries class | //+------------------------------------------------------------------+ class CSeries : public CBaseObj { private: ENUM_TIMEFRAMES m_timeframe; // Timeframe string m_symbol; // Symbol string m_period_description; // Timeframe string description datetime m_firstdate; // The very first date by a period symbol at the moment datetime m_lastbar_date; // Time of opening the last bar by period symbol uint m_amount; // Amount of applied timeseries data uint m_required; // Required amount of applied timeseries data uint m_bars; // Number of bars in history by symbol and timeframe bool m_sync; // Synchronized data flag CArrayObj m_list_series; // Timeseries list CNewBarObj m_new_bar_obj; // "New bar" object //--- Set the very first date by a period symbol at the moment and the new time of opening the last bar by a period symbol void SetServerDate(void) { this.m_firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_FIRSTDATE); this.m_lastbar_date=(datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_LASTBAR_DATE); } public:
Vamos a anotar en la variable m_period_description la descripción del periodo del gráfico de la serie temporal justo al construir el objeto de la clase en el constructor y en los métodos que establecen el marco temporal para el objeto de serie temporal. Lo hemos hecho así para no tener que recurrir cada vez a la función TimeframeDescription() desde el archivo de funciones de servicio DELib.mqh de la biblioteca: la función busca la subcadena en la descripción de línea del marco temporal de la enumeración ENUM_TIMEFRAMES, lo cual no posibilita una alta velocidad de ejecución. Por eso, al construir los objetos de la biblioteca, resulta mejor ejecutar directamente las funciones más laboriosas, en el caso de que estos datos no vayan a cambiar, o bien vayan a hacerlo rara vez cuando el programa los solicite.
La variable m_firstdate guardará la primera fecha del símbolo-periodo en este momento, obtenida mediante la función SeriesInfoInteger() con el identificador de propiedad SERIES_FIRSTDATE.
La variable m_lastbar_date guardará la hora de apertura de la última barra del símbolo-periodo, obtenida mediante la función SeriesInfoInteger() con el identificador de propiedad SERIES_LASTBAR_DATE.
Ambas variables se establecerán llamando al método SetServerDate(), solo en el momento en que se cree el objeto de clase o cambien los datos en la nueva barra, así como al asignar un nuevo símbolo o marco temporal al objeto de serie temporal.
La variable m_required guardará el número necesario (el último solicitado) de datos utilizados para la serie temporal. Al momento de solicitar el número necesario de barras de la serie temporal, podría resultar que la cantidad de datos solicitados para crear la serie temporal simplemente no se encuentre en el servidor. En este caso, la serie temporal se creará usando un número de datos igual a la historia disponible en el servidor. Y en esta variable siempre se guardará la última cantidad de datos solicitado, independientemente de cuántos datos hayamos conseguido obtener y crear en realidad. Por cierto, partiendo de esto, utilizamos el concepto "datos solicitados". En la clase ha cambiado el nombre de los métodos en los que existía "Amount" (cantidad): ahora, ha sido sustituido por "Required" (solicitado, requerido).
Asimismo, en la sección pública de la clase se han añadido nuevos métodos:
public: //--- Return (1) oneself and (2) the timeseries list CSeries *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &m_list_series;} //--- Return the list of bars by selected (1) double, (2) integer and (3) string property fitting a compared condition CArrayObj *GetList(ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByBarProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByBarProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByBarProperty(this.GetList(),property,value,mode); } //--- Set (1) symbol, (2) timeframe, (3) symbol and timeframe, (4) amount of applied timeseries data void SetSymbol(const string symbol); void SetTimeframe(const ENUM_TIMEFRAMES timeframe); void SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe); bool SetRequiredUsedData(const uint required,const uint rates_total); //--- Return (1) symbol, (2) timeframe, number of (3) used and (4) requested timeseries data, //--- (5) number of bars in the timeseries, (6) the very first date, (7) time of opening the last bar by a symbol period, //--- new bar flag with (8) automatic and (9) manual time management string Symbol(void) const { return this.m_symbol; } ENUM_TIMEFRAMES Timeframe(void) const { return this.m_timeframe; } ulong AvailableUsedData(void) const { return this.m_amount; } ulong RequiredUsedData(void) const { return this.m_required; } ulong Bars(void) const { return this.m_bars; } datetime FirstDate(void) const { return this.m_firstdate; } datetime LastBarDate(void) const { return this.m_lastbar_date; } bool IsNewBar(const datetime time) { return this.m_new_bar_obj.IsNewBar(time); } bool IsNewBarManual(const datetime time) { return this.m_new_bar_obj.IsNewBarManual(time); } //--- Return the bar object by index (1) in the list and (2) in the timeseries, as well as (3) the real list size CBar *GetBarByListIndex(const uint index); CBar *GetBarBySeriesIndex(const uint index); int DataTotal(void) const { return this.m_list_series.Total(); } //--- Return (1) Open, (2) High, (3) Low, (4) Close, (5) time, (6) tick volume, (7) real volume, (8) bar spread by index double Open(const uint index,const bool from_series=true); double High(const uint index,const bool from_series=true); double Low(const uint index,const bool from_series=true); double Close(const uint index,const bool from_series=true); datetime Time(const uint index,const bool from_series=true); long TickVolume(const uint index,const bool from_series=true); long RealVolume(const uint index,const bool from_series=true); int Spread(const uint index,const bool from_series=true); //--- (1) Set and (2) return the sound of a sound file of the "New bar" timeseries event void SetNewBarSoundName(const string name) { this.m_new_bar_obj.SetSoundName(name); } string NewBarSoundName(void) const { return this.m_new_bar_obj.GetSoundName(); } //--- Save the new bar time during the manual time management void SaveNewBarTime(const datetime time) { this.m_new_bar_obj.SaveNewBarTime(time); } //--- Synchronize symbol and timeframe data with server data bool SyncData(const uint required,const uint rates_total); //--- (1) Create and (2) update the timeseries list int Create(const uint required=0); void Refresh(const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0); //--- Return the timeseries name string Header(void); //--- Display (1) the timeseries description and (2) the brief timeseries description in the journal void Print(void); void PrintShort(void); //--- Constructors CSeries(void); CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0); }; //+------------------------------------------------------------------+
El método GetObject() retorna al programa de control el puntero al objeto de serie temporal completo. Este nos permite obtener al completo un objeto de serie temporal y trabajar con él en nuestro programa.
El método RequiredUsedData() retorna al programa que realiza la llamada el valor de la variable m_required, que hemos analizado anteriormente.
Los métodos FirstDate() y LastBarDate() retornan respectivamente los valores de las variables m_firstdate y m_lastbar_date, que hemos analizado con anterioridad.
El método SetNewBarSoundName() establece el nombre del archivo de sonido para el objeto CNewBarObj "Nueva barra", incluido en el objeto de serie temporal.
El método NewBarSoundName() retorna el nombre del archivo de sonido designado para el objeto CNewBarObj "Nueva barra", incluido en el objeto de serie temporal.
Los métodos permiten asignar un sonido a cualquier objeto de serie temporal que se reproduzca al detectarse el evento "Nueva barra".
El método Header() crea y retorna la denominación breve de un objeto de serie temporal:
//+------------------------------------------------------------------+ //| Return the timeseries name | //+------------------------------------------------------------------+ string CSeries::Header(void) { return CMessage::Text(MSG_LIB_TEXT_TS_TEXT_TIMESERIES)+" \""+this.m_symbol+"\" "+this.m_period_description; } //+------------------------------------------------------------------+
Desde el método, se retorna la descripción de línea de una serie temporal en forma de
Timeseries "SYMBOL" TIMEFRAME_DESCRIPTION
Por ejemplo:
Timeseries "AUDUSD" M15
El método Print() muestra en el diario la descripción completa de una serie temporal:
//+------------------------------------------------------------------+ //| Display the timeseries description in the journal | //+------------------------------------------------------------------+ void CSeries::Print(void) { string txt= ( CMessage::Text(MSG_LIB_TEXT_TS_REQUIRED_HISTORY_DEPTH)+(string)this.RequiredUsedData()+", "+ CMessage::Text(MSG_LIB_TEXT_TS_ACTUAL_DEPTH)+(string)this.AvailableUsedData()+", "+ CMessage::Text(MSG_LIB_TEXT_TS_AMOUNT_HISTORY_DATA)+(string)this.DataTotal()+", "+ CMessage::Text(MSG_LIB_TEXT_TS_HISTORY_BARS)+(string)this.Bars() ); ::Print(this.Header(),": ",txt); } //+------------------------------------------------------------------+
Imprime en el diario los datos de una serie temporal en forma de
HEADER: HISTORY_DEPTH: XXXX, ACTUAL_DEPTH: XXXX, AMOUNT_HISTORY_DATA: XXXX, HISTORY_BARS: XXXX
Por ejemplo:
Timeseries "AUDUSD" W1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 1400 Timeseries "AUDUSD" MN1: Requested history depth: 1000, Actual history depth: 322, Historical data created: 322, History bars on the server: 322
El método PrintShort() muestra en el diario la descripción breve de una serie temporal:
//+------------------------------------------------------------------+ //| Display a short timeseries description in the journal | //+------------------------------------------------------------------+ void CSeries::PrintShort(void) { string txt= ( CMessage::Text(MSG_LIB_TEXT_TS_TEXT_REQUIRED)+": "+(string)this.RequiredUsedData()+", "+ CMessage::Text(MSG_LIB_TEXT_TS_TEXT_ACTUAL)+": "+(string)this.AvailableUsedData()+", "+ CMessage::Text(MSG_LIB_TEXT_TS_TEXT_CREATED)+": "+(string)this.DataTotal()+", "+ CMessage::Text(MSG_LIB_TEXT_TS_TEXT_HISTORY_BARS)+": "+(string)this.Bars() ); ::Print(this.Header(),": ",txt); } //+------------------------------------------------------------------+
Imprime en el diario los datos de una serie temporal en forma de
HEADER: REQUIRED: XXXX, ACTUAL: XXXX, CREATED: XXXX, HISTORY_BARS: XXXX
Por ejemplo:
Timeseries "USDJPY" W1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2562 Timeseries "USDJPY" MN1: Requested: 1000, Actual: 589, Created: 589, On the server: 589
En ambos constructores de la clase, añadimos el guardado de la descripción del marco temporal y el establecimiento de las fechas de la serie temporal:
//+------------------------------------------------------------------+ //| Constructor 1 (current symbol and period timeseries) | //+------------------------------------------------------------------+ CSeries::CSeries(void) : m_bars(0),m_amount(0),m_required(0),m_sync(false) { this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE); this.m_list_series.Clear(); this.m_list_series.Sort(SORT_BY_BAR_INDEX); this.SetSymbolPeriod(NULL,(ENUM_TIMEFRAMES)::Period()); this.m_period_description=TimeframeDescription(this.m_timeframe); this.SetServerDate(); } //+------------------------------------------------------------------+ //| Constructor 2 (specified symbol and period timeseries) | //+------------------------------------------------------------------+ CSeries::CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0) : m_bars(0), m_amount(0),m_required(0),m_sync(false) { this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE); this.m_list_series.Clear(); this.m_list_series.Sort(SORT_BY_BAR_INDEX); this.SetSymbolPeriod(symbol,timeframe); this.m_sync=this.SetRequiredUsedData(required,0); this.m_period_description=TimeframeDescription(this.m_timeframe); this.SetServerDate(); } //+------------------------------------------------------------------+
En el método para establecer el símbolo, añadimos la comprobación del mismo símbolo y el establecimiento de las fechas de la serie temporal:
//+------------------------------------------------------------------+ //| Set a symbol | //+------------------------------------------------------------------+ void CSeries::SetSymbol(const string symbol) { if(this.m_symbol==symbol) return; this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); this.m_new_bar_obj.SetSymbol(this.m_symbol); this.SetServerDate(); } //+------------------------------------------------------------------+
Aquí, si se ha transmitido al método un símbolo ya utilizado en el objeto, no necesitamos establecer nada: salimos del método.
En el método para establecer el marco temporal, añadimos la comprobación del mismo marco temporal y el establecimiento de las fechas de la serie temporal:
//+------------------------------------------------------------------+ //| Set a timeframe | //+------------------------------------------------------------------+ void CSeries::SetTimeframe(const ENUM_TIMEFRAMES timeframe) { if(this.m_timeframe==timeframe) return; this.m_timeframe=(timeframe==PERIOD_CURRENT ? (ENUM_TIMEFRAMES)::Period() : timeframe); this.m_new_bar_obj.SetPeriod(this.m_timeframe); this.m_period_description=TimeframeDescription(this.m_timeframe); this.SetServerDate(); } //+------------------------------------------------------------------+
Aquí, si se ha transmitido al método un marco temporal ya utilizado en el objeto, no necesitamos establecer nada: salimos del método.
Cambiamos el método para establecer el símbolo y el marco temporal:
//+------------------------------------------------------------------+ //| Set a symbol and timeframe | //+------------------------------------------------------------------+ void CSeries::SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe) { if(this.m_symbol==symbol && this.m_timeframe==timeframe) return; this.SetSymbol(symbol); this.SetTimeframe(timeframe); } //+------------------------------------------------------------------+
Aquí, si se han transmitido al método el mismo símbolo y marco temporal, no tenemos que cambiar nada, salimos del método.
A continuación, llamamos los métodos para establecer el símbolo y el marco temporal.
En el método para establecer la profundidad de la historia de la serie temporal, guardamos el valor solicitado de profundidad de la historia:
//+------------------------------------------------------------------+ //| Set the number of required data | //+------------------------------------------------------------------+ bool CSeries::SetRequiredUsedData(const uint required,const uint rates_total) { this.m_required=(required==0 ? SERIES_DEFAULT_BARS_COUNT : required); //--- Set the number of available timeseries bars this.m_bars=(uint) ( //--- If this is an indicator and the work is performed on the current symbol and timeframe, //--- add the rates_total value passed to the method, //--- otherwise, get the number from the environment this.m_program==PROGRAM_INDICATOR && this.m_symbol==::Symbol() && this.m_timeframe==::Period() ? rates_total : ::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_BARS_COUNT) ); //--- If managed to set the number of available history, set the amount of data in the list: if(this.m_bars>0) { //--- if zero 'required' value is passed, //--- use either the default value (1000 bars) or the number of available history bars - the least one of them //--- if non-zero 'required' value is passed, //--- use either the 'required' value or the number of available history bars - the least one of them this.m_amount=(required==0 ? ::fmin(SERIES_DEFAULT_BARS_COUNT,this.m_bars) : ::fmin(required,this.m_bars)); return true; } return false; } //+------------------------------------------------------------------+
Si se ha transmitido un valor required igual a cero, solicitaremos la historia en la cantidad por defecto (1000 barras), establecida con la macrosustitución SERIES_DEFAULT_BARS_COUNT en el archivo Define.mqh, de lo contrario, el valor transmitido será required.
Añadimos al método de actualización de la serie temporal la comprobación del uso de esta serie temporal en el programa
si la serie temporal no se usa, no deberemos actualizar nada:
//+------------------------------------------------------------------+ //| Update timeseries list and data | //+------------------------------------------------------------------+ void CSeries::Refresh(const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { //--- If the timeseries is not used, exit if(!this.m_available) return; MqlRates rates[1]; //--- Set the flag of sorting the list of bars by index this.m_list_series.Sort(SORT_BY_BAR_INDEX); //--- If a new bar is present on a symbol and period, if(this.IsNewBarManual(time)) { //--- create a new bar object and add it to the end of the list CBar *new_bar=new CBar(this.m_symbol,this.m_timeframe,0); if(new_bar==NULL) return; if(!this.m_list_series.Add(new_bar)) { delete new_bar; return; } //--- Write the very first date by a period symbol at the moment and the new time of opening the last bar by a period symbol this.SetServerDate(); //--- if the timeseries exceeds the requested number of bars, remove the earliest bar if(this.m_list_series.Total()>(int)this.m_required) this.m_list_series.Delete(0); //--- save the new bar time as the previous one for the subsequent new bar check this.SaveNewBarTime(time); } //--- Get the index of the last bar in the list and the object bar by the index int index=this.m_list_series.Total()-1; CBar *bar=this.m_list_series.At(index); //--- if the work is performed in an indicator and the timeseries belongs to the current symbol and timeframe, //--- copy price parameters (passed to the method from the outside) to the bar price structure int copied=1; if(this.m_program==PROGRAM_INDICATOR && this.m_symbol==::Symbol() && this.m_timeframe==::Period()) { rates[0].time=time; rates[0].open=open; rates[0].high=high; rates[0].low=low; rates[0].close=close; rates[0].tick_volume=tick_volume; rates[0].real_volume=volume; rates[0].spread=spread; } //--- otherwise, get data to the bar price structure from the environment else copied=::CopyRates(this.m_symbol,this.m_timeframe,0,1,rates); //--- If the prices are obtained, set the new properties from the price structure for the bar object if(copied==1) bar.SetProperties(rates[0]); } //+------------------------------------------------------------------+
Y de manera adicional: si en la serie temporal ha aparecido una nueva barra, actualizaremos las fechas de la serie temporal.
Estos son los cambios principales en esta clase. No vamos a analizar aquí los cambios poco considerables en los nombres de los métodos: el lector podrá familiarizarse con el listado completo de la clase en los archivos adjuntos al final del artículo.
Por el momento, hemos terminado con la clase CSeries.
Vamos a mejorar la clase CTimeSeries, que contine los objetos CSeries para todos los posibles periodos del gráfico de un mismo símbolo.
Para obtener el índice de un marco temporal en la lista que guarda las series temporales de los periodos correspondientes del gráfico y el marco temporal según el índice de la lista, tenemos en el listado de la clase los métodos correspondientes: IndexTimeframe() y TimeframeByIndex(). Los métodos son bastantes específicos, dado que se basan en que el índice del menor marco temporal permitido (PERIOD_M1) está contenido en el índice cero de la lista. No obstante, en la enumeración ENUM_TIMEFRAMES, el índice del periodo М1 será ya igual a la unidad, puesto que en el índice cero se encuentra la constante PERIOD_CURRENT. Es decir, todos los índices han sido desplazados en 1 con respecto al valor cero.
Tras reflexionar un poco, hemos decidido que podríamos necesitar algunas funciones que retornen el índice de la constante del periodo del gráfico dentro de la enumeración ENUM_TIMEFRAMES, y al contrario: que retornen según el periodo del gráfico su constante desde la enumeración. Por eso, hemos creado funciones que ejecuten estas tareas: en el archivo de funciones de servicio, IndexEnumTimeframe() y TimeframeByEnumIndex(), así como en el listado de la clase CTimeSeries, eliminamos la implementación de los métodos IndexTimeframe() y TimeframeByIndex(), añadiendo al cuerpo de la clase la implementación: la llamada de las funciones IndexEnumTimeframe() y TimeframeByEnumIndex() con desplazamiento de una unidad.
Bien, en el archivo de funciones de servicio DELib.mqh, escribimos tres funciones:
//+------------------------------------------------------------------+ //| Return the timeframe index in the ENUM_TIMEFRAMES enumeration | //+------------------------------------------------------------------+ char IndexEnumTimeframe(ENUM_TIMEFRAMES timeframe) { int statement=(timeframe==PERIOD_CURRENT ? Period() : timeframe); switch(statement) { case PERIOD_M1 : return 1; case PERIOD_M2 : return 2; case PERIOD_M3 : return 3; case PERIOD_M4 : return 4; case PERIOD_M5 : return 5; case PERIOD_M6 : return 6; case PERIOD_M10 : return 7; case PERIOD_M12 : return 8; case PERIOD_M15 : return 9; case PERIOD_M20 : return 10; case PERIOD_M30 : return 11; case PERIOD_H1 : return 12; case PERIOD_H2 : return 13; case PERIOD_H3 : return 14; case PERIOD_H4 : return 15; case PERIOD_H6 : return 16; case PERIOD_H8 : return 17; case PERIOD_H12 : return 18; case PERIOD_D1 : return 19; case PERIOD_W1 : return 20; case PERIOD_MN1 : return 21; default : Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_UNKNOWN_TIMEFRAME)); return WRONG_VALUE; } } //+------------------------------------------------------------------+ //| Return the timeframe by the ENUM_TIMEFRAMES enumeration index | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES TimeframeByEnumIndex(const uchar index) { if(index==0) return(ENUM_TIMEFRAMES)Period(); switch(index) { case 1 : return PERIOD_M1; case 2 : return PERIOD_M2; case 3 : return PERIOD_M3; case 4 : return PERIOD_M4; case 5 : return PERIOD_M5; case 6 : return PERIOD_M6; case 7 : return PERIOD_M10; case 8 : return PERIOD_M12; case 9 : return PERIOD_M15; case 10 : return PERIOD_M20; case 11 : return PERIOD_M30; case 12 : return PERIOD_H1; case 13 : return PERIOD_H2; case 14 : return PERIOD_H3; case 15 : return PERIOD_H4; case 16 : return PERIOD_H6; case 17 : return PERIOD_H8; case 18 : return PERIOD_H12; case 19 : return PERIOD_D1; case 20 : return PERIOD_W1; case 21 : return PERIOD_MN1; default : Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_DATAS),"... ",CMessage::Text(MSG_SYM_STATUS_INDEX),": ",(string)index); return WRONG_VALUE; } } //+------------------------------------------------------------------+ //| Return the timeframe by its description | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES TimeframeByDescription(const string timeframe) { return ( timeframe=="M1" ? PERIOD_M1 : timeframe=="M2" ? PERIOD_M2 : timeframe=="M3" ? PERIOD_M3 : timeframe=="M4" ? PERIOD_M4 : timeframe=="M5" ? PERIOD_M5 : timeframe=="M6" ? PERIOD_M6 : timeframe=="M10" ? PERIOD_M10 : timeframe=="M12" ? PERIOD_M12 : timeframe=="M15" ? PERIOD_M15 : timeframe=="M20" ? PERIOD_M20 : timeframe=="M30" ? PERIOD_M30 : timeframe=="H1" ? PERIOD_H1 : timeframe=="H2" ? PERIOD_H2 : timeframe=="H3" ? PERIOD_H3 : timeframe=="H4" ? PERIOD_H4 : timeframe=="H6" ? PERIOD_H6 : timeframe=="H8" ? PERIOD_H8 : timeframe=="H12" ? PERIOD_H12 : timeframe=="D1" ? PERIOD_D1 : timeframe=="W1" ? PERIOD_W1 : timeframe=="MN1" ? PERIOD_MN1 : PERIOD_CURRENT ); } //+------------------------------------------------------------------+
En el archivo de la clase CTimeSeries, eliminamos del listado de la clase la implementación de los métodos IndexTimeframe() y TimeframeByIndex():
//+------------------------------------------------------------------+
//| Return the timeframe index in the list |
//+------------------------------------------------------------------+
char CTimeSeries::IndexTimeframe(ENUM_TIMEFRAMES timeframe) const
{
int statement=(timeframe==PERIOD_CURRENT ? ::Period() : timeframe);
switch(statement)
{
case PERIOD_M1 : return 0;
case PERIOD_M2 : return 1;
case PERIOD_M3 : return 2;
case PERIOD_M4 : return 3;
case PERIOD_M5 : return 4;
case PERIOD_M6 : return 5;
case PERIOD_M10 : return 6;
case PERIOD_M12 : return 7;
case PERIOD_M15 : return 8;
case PERIOD_M20 : return 9;
case PERIOD_M30 : return 10;
case PERIOD_H1 : return 11;
case PERIOD_H2 : return 12;
case PERIOD_H3 : return 13;
case PERIOD_H4 : return 14;
case PERIOD_H6 : return 15;
case PERIOD_H8 : return 16;
case PERIOD_H12 : return 17;
case PERIOD_D1 : return 18;
case PERIOD_W1 : return 19;
case PERIOD_MN1 : return 20;
default : ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_UNKNOWN_TIMEFRAME)); return WRONG_VALUE;
}
}
//+------------------------------------------------------------------+
//| Return a timeframe by index |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CTimeSeries::TimeframeByIndex(const uchar index) const
{
switch(index)
{
case 0 : return PERIOD_M1;
case 1 : return PERIOD_M2;
case 2 : return PERIOD_M3;
case 3 : return PERIOD_M4;
case 4 : return PERIOD_M5;
case 5 : return PERIOD_M6;
case 6 : return PERIOD_M10;
case 7 : return PERIOD_M12;
case 8 : return PERIOD_M15;
case 9 : return PERIOD_M20;
case 10 : return PERIOD_M30;
case 11 : return PERIOD_H1;
case 12 : return PERIOD_H2;
case 13 : return PERIOD_H3;
case 14 : return PERIOD_H4;
case 15 : return PERIOD_H6;
case 16 : return PERIOD_H8;
case 17 : return PERIOD_H12;
case 18 : return PERIOD_D1;
case 19 : return PERIOD_W1;
case 20 : return PERIOD_MN1;
default : ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_DATAS),"... ",CMessage::Text(MSG_SYM_STATUS_INDEX),": ",(string)index); return WRONG_VALUE;
}
}
//+------------------------------------------------------------------+
En lugar de estos métodos, ahora llamaremos las funciones del archivo de funciones de servicio DELib.mqh:
//--- Return (1) the timeframe index in the list and (2) the timeframe by the list index char IndexTimeframe(const ENUM_TIMEFRAMES timeframe) const { return IndexEnumTimeframe(timeframe)-1; } ENUM_TIMEFRAMES TimeframeByIndex(const uchar index) const { return TimeframeByEnumIndex(uchar(index+1)); }
Dado que lista de series temporales de la clase contiene todos los posibles periodos de los gráficos, en el índice cero de la lista se contiene la serie temporal del periodo del gráfico М1. En el índice cero de la enumeración ENUM_TIMEFRAMES, en cambio, se contiene PERIOD_CURRENT, mientras que М1 está en el primero, por eso, para obtener el ínidice correcto en la lista, necesitamos desplazar el valor del índice en 1, lo que precisamente hacemos aquí.
Hemos añadido a la sección privada de la clase dos variables de miembro de clase para establecer la primera fecha en la historia en el servidor y el terminal, y un método para asignar los valores de estas fechas a estas variables:
//+------------------------------------------------------------------+ //| Symbol timeseries class | //+------------------------------------------------------------------+ class CTimeSeries : public CBaseObj { private: string m_symbol; // Timeseries symbol CArrayObj m_list_series; // List of timeseries by timeframes datetime m_server_firstdate; // The very first date in history by a server symbol datetime m_terminal_firstdate; // The very first date in history by a symbol in the client terminal //--- Return (1) the timeframe index in the list and (2) the timeframe by the list index char IndexTimeframe(const ENUM_TIMEFRAMES timeframe) const { return IndexEnumTimeframe(timeframe)-1; } ENUM_TIMEFRAMES TimeframeByIndex(const uchar index) const { return TimeframeByEnumIndex(uchar(index+1)); } //--- Set the very first date in history by symbol on the server and in the client terminal void SetTerminalServerDate(void) { this.m_server_firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,::Period(),SERIES_SERVER_FIRSTDATE); this.m_terminal_firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,::Period(),SERIES_TERMINAL_FIRSTDATE); } public:
En la variable m_server_firstdate se guardará la primera fecha en la historia según el símbolo en el servidor. Esta se obtiene mediante la función SeriesInfoInteger() con el identificador de propiedad SERIES_SERVER_FIRSTDATE.
En la variable m_terminal_firstdate se guardará la primera fecha en la historia según el símbolo en el terminal. Esta se obtiene mediante la función SeriesInfoInteger() con el identificador de propiedad SERIES_TERMINAL_FIRSTDATE.
En la sección pública de la clase, añadimos seis nuevos métodos y un constructor paramétrico:
public: //--- Return (1) oneself, (2) the full list of timeseries, (3) specified timeseries object and (4) timeseries object by index CTimeSeries *GetObject(void) { return &this; } CArrayObj *GetListSeries(void) { return &this.m_list_series; } CSeries *GetSeries(const ENUM_TIMEFRAMES timeframe) { return this.m_list_series.At(this.IndexTimeframe(timeframe)); } CSeries *GetSeriesByIndex(const uchar index) { return this.m_list_series.At(index); } //--- Set/return timeseries symbol void SetSymbol(const string symbol) { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); } string Symbol(void) const { return this.m_symbol; } //--- Set the history depth (1) of a specified timeseries and (2) of all applied symbol timeseries bool SetRequiredUsedData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0); bool SetRequiredAllUsedData(const uint required=0,const int rates_total=0); //--- Return the flag of data synchronization with the server data of the (1) specified timeseries, (2) all timeseries bool SyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0); bool SyncAllData(const uint required=0,const int rates_total=0); //--- Return the very first date in history by symbol (1) on the server and (2) in the client terminal datetime ServerFirstDate(void) const { return this.m_server_firstdate; } datetime TerminalFirstDate(void) const { return this.m_terminal_firstdate; } //--- Create (1) the specified timeseries list and (2) all timeseries lists bool Create(const ENUM_TIMEFRAMES timeframe,const uint required=0); bool CreateAll(const uint required=0); //--- Update (1) the specified timeseries list and (2) all timeseries lists void Refresh(const ENUM_TIMEFRAMES timeframe, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0); void RefreshAll(const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0); //--- Compare CTimeSeries objects (by symbol) virtual int Compare(const CObject *node,const int mode=0) const; //--- Display (1) description and (2) short symbol timeseries description in the journal void Print(const bool created=true); void PrintShort(const bool created=true); //--- Constructors CTimeSeries(void){;} CTimeSeries(const string symbol); }; //+------------------------------------------------------------------+
El método GetObject() retorna el puntero al objeto de la clase. Este nos permite obtener el objeto de clase de la serie temporal del símbolo y trabajar con él en nuestro programa.
Los métodos ServerFirstDate() y TerminalFirstDate() retornan el valor de las variables m_server_firstdate y m_terminal_firstdate respectivamente, que hemos analizado con anterioridad.
El método virtual Compare() permite comparar dos objetos de series temporales según la denomiación del símbolo de la serie temporal:
//+------------------------------------------------------------------+ //| Compare CTimeSeries objects | //+------------------------------------------------------------------+ int CTimeSeries::Compare(const CObject *node,const int mode=0) const { const CTimeSeries *compared_obj=node; return(this.Symbol()>compared_obj.Symbol() ? 1 : this.Symbol()<compared_obj.Symbol() ? -1 : 0); } //+------------------------------------------------------------------+
El método retorna cero en el caso de que los símbolos de los dos objetos de series temporales comparados sean iguales. De lo contrario, se retornará +/- 1. El método ha sido declarado en la clase CObject de la biblioteca estándar, y debe ser redefinido en sus herederos.
El método Print() muestra en el diario las descripciones completas de todas las series temporales del símbolo:
//+------------------------------------------------------------------+ //| Display descriptions of all symbol timeseries in the journal | //+------------------------------------------------------------------+ void CTimeSeries::Print(const bool created=true) { ::Print(CMessage::Text(MSG_LIB_TEXT_TS_TEXT_SYMBOL_TIMESERIES)," ",this.m_symbol,": "); for(int i=0;i<this.m_list_series.Total();i++) { CSeries *series=this.m_list_series.At(i); if(series==NULL || (created && series.DataTotal()==0)) continue; series.Print(); } } //+------------------------------------------------------------------+
En el diario, se muestra una lista de todos las series temporales (created=true) creadas o todos las series temporales(created=false) creadas y declaradas del símbolo, en un formato determinado, por ejemplo:
created=true:
GBPUSD symbol timeseries: Timeseries "GBPUSD" M1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 6296 Timeseries "GBPUSD" M5: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 3921 Timeseries "GBPUSD" M15: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 3227 Timeseries "GBPUSD" M30: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 3053 Timeseries "GBPUSD" H1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 6187 Timeseries "GBPUSD" H4: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 5298 Timeseries "GBPUSD" D1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 5288 Timeseries "GBPUSD" W1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 1398 Timeseries "GBPUSD" MN1: Requested history depth: 1000, Actual history depth: 321, Historical data created: 321, History bars on the server: 321
created=false:
GBPUSD symbol timeseries: Timeseries "GBPUSD" M1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 6296 Timeseries "GBPUSD" M2: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 5483 Timeseries "GBPUSD" M3: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 4616 Timeseries "GBPUSD" M4: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 4182 Timeseries "GBPUSD" M5: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 3921 Timeseries "GBPUSD" M6: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 3748 Timeseries "GBPUSD" M10: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 3401 Timeseries "GBPUSD" M12: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 3314 Timeseries "GBPUSD" M15: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 3227 Timeseries "GBPUSD" M20: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 3140 Timeseries "GBPUSD" M30: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 3053 Timeseries "GBPUSD" H1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 6187 Timeseries "GBPUSD" H2: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 5047 Timeseries "GBPUSD" H3: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 5031 Timeseries "GBPUSD" H4: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 5298 Timeseries "GBPUSD" H6: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 6324 Timeseries "GBPUSD" H8: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 6301 Timeseries "GBPUSD" H12: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 0, History bars on the server: 5762 Timeseries "GBPUSD" D1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 5288 Timeseries "GBPUSD" W1: Requested history depth: 1000, Actual history depth: 1000, Historical data created: 1000, History bars on the server: 1398 Timeseries "GBPUSD" MN1: Requested history depth: 1000, Actual history depth: 321, Historical data created: 321, History bars on the server: 321
El método PrintShort() muestra en el diario las descripciones breves de todas las series temporales del símbolo:
//+-------------------------------------------------------------------+ //| Display short descriptions of all symbol timeseries in the journal| //+-------------------------------------------------------------------+ void CTimeSeries::PrintShort(const bool created=true) { ::Print(CMessage::Text(MSG_LIB_TEXT_TS_TEXT_SYMBOL_TIMESERIES)," ",this.m_symbol,": "); for(int i=0;i<this.m_list_series.Total();i++) { CSeries *series=this.m_list_series.At(i); if(series==NULL || (created && series.DataTotal()==0)) continue; series.PrintShort(); } } //+------------------------------------------------------------------+
En el diario, se muestra una lista de todos las series temporales (created=true) creadas o todos las series temporales(created=false) creadas y declaradas del símbolo, en un formato determinado, por ejemplo:
created=true:
USDJPY symbol timeseries: Timeseries "USDJPY" M1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2880 Timeseries "USDJPY" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3921 Timeseries "USDJPY" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3227 Timeseries "USDJPY" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3053 Timeseries "USDJPY" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5095 Timeseries "USDJPY" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5023 Timeseries "USDJPY" D1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5305 Timeseries "USDJPY" W1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2562 Timeseries "USDJPY" MN1: Requested: 1000, Actual: 589, Created: 589, On the server: 589
created=false:
USDJPY symbol timeseries: Timeseries "USDJPY" M1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2880 Timeseries "USDJPY" M2: Requested: 1000, Actual: 1000, Created: 0, On the server: 3608 Timeseries "USDJPY" M3: Requested: 1000, Actual: 1000, Created: 0, On the server: 4616 Timeseries "USDJPY" M4: Requested: 1000, Actual: 1000, Created: 0, On the server: 4182 Timeseries "USDJPY" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3921 Timeseries "USDJPY" M6: Requested: 1000, Actual: 1000, Created: 0, On the server: 3748 Timeseries "USDJPY" M10: Requested: 1000, Actual: 1000, Created: 0, On the server: 3401 Timeseries "USDJPY" M12: Requested: 1000, Actual: 1000, Created: 0, On the server: 3314 Timeseries "USDJPY" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3227 Timeseries "USDJPY" M20: Requested: 1000, Actual: 1000, Created: 0, On the server: 3140 Timeseries "USDJPY" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3053 Timeseries "USDJPY" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5095 Timeseries "USDJPY" H2: Requested: 1000, Actual: 1000, Created: 0, On the server: 5047 Timeseries "USDJPY" H3: Requested: 1000, Actual: 1000, Created: 0, On the server: 5031 Timeseries "USDJPY" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5023 Timeseries "USDJPY" H6: Requested: 1000, Actual: 1000, Created: 0, On the server: 6390 Timeseries "USDJPY" H8: Requested: 1000, Actual: 1000, Created: 0, On the server: 6352 Timeseries "USDJPY" H12: Requested: 1000, Actual: 1000, Created: 0, On the server: 5796 Timeseries "USDJPY" D1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5305 Timeseries "USDJPY" W1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2562 Timeseries "USDJPY" MN1: Requested: 1000, Actual: 589, Created: 589, On the server: 589
Hemos añadido en el constructor de la clase, el establecimiento de las fechas de la serie temporal:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTimeSeries::CTimeSeries(const string symbol) : m_symbol(symbol) { this.m_list_series.Clear(); this.m_list_series.Sort(); for(int i=0;i<21;i++) { ENUM_TIMEFRAMES timeframe=this.TimeframeByIndex((uchar)i); CSeries *series_obj=new CSeries(this.m_symbol,timeframe); this.m_list_series.Add(series_obj); } this.SetTerminalServerDate(); } //+------------------------------------------------------------------+
En el método de adición de la serie temporal indicada, hemos añadido el establecimiento de las fechas de la serie temporalal detectarse el evento "Nueva barra" de la serie temporal actualizada:
//+------------------------------------------------------------------+ //| Update a specified timeseries list | //+------------------------------------------------------------------+ void CTimeSeries::Refresh(const ENUM_TIMEFRAMES timeframe, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe)); if(series_obj==NULL || series_obj.DataTotal()==0) return; series_obj.Refresh(time,open,high,low,close,tick_volume,volume,spread); if(series_obj.IsNewBar(time)) this.SetTerminalServerDate(); } //+------------------------------------------------------------------+
En el método de actualización de series temporales, también hemos añadido la actualización de las fechas de la serie temporal:
//+------------------------------------------------------------------+ //| Update all timeseries lists | //+------------------------------------------------------------------+ void CTimeSeries::RefreshAll(const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { bool upd=false; for(int i=0;i<21;i++) { CSeries *series_obj=this.m_list_series.At(i); if(series_obj==NULL || series_obj.DataTotal()==0) continue; series_obj.Refresh(time,open,high,low,close,tick_volume,volume,spread); if(series_obj.IsNewBar(time)) upd &=true; } if(upd) this.SetTerminalServerDate(); } //+------------------------------------------------------------------+
No obstante, aquí deberemos actualizar las fechas solo una vez al encontrar el evento "Nueva barra" en cualquiera de las series temporales actualizadas presentes en la lista (ya que hay 21 series temporales, y no tiene sentido establecer 21 veces la misma fecha). Por eso, registramos en la bandera que indica la necesidad de actualizar las fechas el valor true al encontrar el evento "Nueva barra" en el ciclo por todas las series temporales, y, al finalizar el ciclo, si la bandera ha sido establecida, actualizamos las fechas.
Ya hemos finalizado las mejoras de la clase CTimeSeries. No vamos a describir aquí las correcciones poco significativas: el lector podrá verlo todo en los archivos adjuntos al final del artículo.
En estos momentos, tenemos tres clases que contienen toda la información necesaria para crear las colecciones de series temporales:
- La "barra de un periodo de un símbolo" CBar, incluye los datos de una barra del símbolo establecido del periodo establecido;
- La "serie temporal de un periodo de un símbolo" CSeries, incluye la lista de colección de barras (1) de un periodo de un símbolo;
- Las "series temporales de todos los periodos de un símbolo" CTimeSeries, incluyen la lista de series temporales (2) para cada periodo de un símbolo;
Ahora, vamos a crear una colección de series temporales que supone una lista de colección de series temporales (3) para cada símbolo usado en el programa.
La clase de colección de los objetos de series temporales de los símbolos y periodos
La colección de series temporales es una matriz dinámica de punteros a los ejemplares de la clase CObject y sus herederos (punteros a los objetos de clase CTimeSeries).
Creamos en la carpeta de la biblioteca \MQL5\Include\DoEasy\Collections\ el nuevo archivo TimeSeriesCollection.mqh de la clase CTimeSeriesCollection.
El objeto básico de la clase será el objeto básico para construir la biblioteca estándar CObject.
A continuación, veremos con mayor detalle el listado de la clase, y también analizaremos poco a poco su construcción:
//+------------------------------------------------------------------+ //| TimeSeriesCollection.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" //+------------------------------------------------------------------+ //| Archivos de inclusión | //+------------------------------------------------------------------+ #include "..\Objects\Series\TimeSeries.mqh" #include "..\Objects\Symbols\Symbol.mqh" //+------------------------------------------------------------------+ //| Symbol timeseries collection | //+------------------------------------------------------------------+ class CTimeSeriesCollection : public CObject { private: CArrayObj m_list; // List of applied symbol timeseries //--- Return the timeseries index by symbol name int IndexTimeSeries(const string symbol); public: //--- Return (1) oneself and (2) the timeseries list CTimeSeriesCollection *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &this.m_list; } //--- Create the symbol timeseries list collection bool CreateCollection(const CArrayObj *list_symbols); //--- Set the flag of using (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols //--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols void SetAvailable(const string symbol,const ENUM_TIMEFRAMES timeframe,const bool flag=true); void SetAvailable(const ENUM_TIMEFRAMES timeframe,const bool flag=true); void SetAvailable(const string symbol,const bool flag=true); void SetAvailable(const bool flag=true); //--- Get the flag of using (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols //--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols bool IsAvailable(const string symbol,const ENUM_TIMEFRAMES timeframe); bool IsAvailable(const ENUM_TIMEFRAMES timeframe); bool IsAvailable(const string symbol); bool IsAvailable(void); //--- Set the history depth of (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols //--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols bool SetRequiredUsedData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0); bool SetRequiredUsedData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0); bool SetRequiredUsedData(const string symbol,const uint required=0,const int rates_total=0); bool SetRequiredUsedData(const uint required=0,const int rates_total=0); //--- Return the flag of data synchronization with the server data of the (1) specified timeseries of the specified symbol, //--- (2) the specified timeseries of all symbols, (3) all timeseries of the specified symbol and (4) all timeseries of all symbols bool SyncData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0); bool SyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0); bool SyncData(const string symbol,const uint required=0,const int rates_total=0); bool SyncData(const uint required=0,const int rates_total=0); //--- Create (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols, //--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols bool CreateSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0); bool CreateSeries(const ENUM_TIMEFRAMES timeframe,const uint required=0); bool CreateSeries(const string symbol,const uint required=0); bool CreateSeries(const uint required=0); //--- Update (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols, //--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols void Refresh(const string symbol,const ENUM_TIMEFRAMES timeframe, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0); void Refresh(const ENUM_TIMEFRAMES timeframe, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0); void Refresh(const string symbol, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0); void Refresh(const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0); //--- Display (1) the complete and (2) short collection description in the journal void Print(const bool created=true); void PrintShort(const bool created=true); //--- Constructor CTimeSeriesCollection(); }; //+------------------------------------------------------------------+
En esencia, en estos momentos, la clase supone una lista de objetos de series temporales y métodos de creación, de establecimiento de parámetros y de actualización de las series temporales necesarias según el símbolo y el periodo:
La matriz de punteros a los objetos de la clase CObject m_list contendrá los punteros a los objetos de clase CTimeSeries. Con la ayuda de esta lista, podremos obtener los datos de las series temporales necesarias y trabajar con ellos.
El método IndexTimeSeries(), que retorna el índice de una serie temporal según el nombre del símbolo, permite obtener acceso al objeto CTimeSeries necesario según el nombre del símbolo:
//+------------------------------------------------------------------+ //| Return the timeseries index by symbol name | //+------------------------------------------------------------------+ int CTimeSeriesCollection::IndexTimeSeries(const string symbol) { CTimeSeries *tmp=new CTimeSeries(symbol); if(tmp==NULL) return WRONG_VALUE; this.m_list.Sort(); int index=this.m_list.Search(tmp); delete tmp; return index; } //+------------------------------------------------------------------+
Creamos un nuevo objeto temporal de serie temporal con el valor del símbolo transmitido al método,
asignamos a la lista m_list la bandera de lista clasificada y,
con la ayuda del método Searsh(), obtenemos el índice de ese objeto en la lista.
Eliminamos necesariamente el objeto temporal de serie temporal y
retornamos el índice obtenido.
Si en la lista no se encuentra el objeto con el índice establecido, el método retornará -1, de lo contrario, se retornará el valor del índice del objeto encontrado.
El método GetObject() retorna al programa de control el puntero al objeto de colección de series temporales, y permite obtener el objeto de colección completo, para trabajar con él en nuestro programa.
El método GetList() retorna el puntero a la lista de colección de series temporales CTimeSeries, y permite obtener la lista con todas las series temporales de los símbolos, para trabajar con ella en nuestro programa.
El método CreateCollection() crea una lista vacía de objetos de series temporales:
//+------------------------------------------------------------------+ //| Create the symbol timeseries collection list | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::CreateCollection(const CArrayObj *list_symbols) { //--- If an empty list of symbol objects is passed, exit if(list_symbols==NULL) return false; //--- Get the number of symbol objects in the passed list int total=list_symbols.Total(); //--- Clear the timeseries collection list this.m_list.Clear(); //--- In a loop by all symbol objects for(int i=0;i<total;i++) { //--- get the next symbol object CSymbol *symbol_obj=list_symbols.At(i); //--- if failed to get a symbol object, move on to the next one in the list if(symbol_obj==NULL) continue; //--- Create a new timeseries object with the current symbol name CTimeSeries *timeseries=new CTimeSeries(symbol_obj.Name()); //--- If failed to create the timeseries object, move on to the next symbol in the list if(timeseries==NULL) continue; //--- Set the sorted list flag for the timeseries collection list this.m_list.Sort(); //--- If the object with the same symbol name is already present in the timeseries collection list, remove the timeseries object if(this.m_list.Search(timeseries)>WRONG_VALUE) delete timeseries; //--- if failed to add the timeseries object to the collection list, remove the timeseries object else if(!this.m_list.Add(timeseries)) delete timeseries; } //--- Return the flag indicating that the created collection list has a size greater than zero return this.m_list.Total()>0; } //+-----------------------------------------------------------------------+
Cada línea del método ha sido comentada en su listado.
Al método se transmite la lista de todos los símbolos utilizados en el programa (creada anteriormente), y luego, en un ciclo por dicha lista, se crean los objetos de series temporales CTimeSeries, para las cuales se indica de inmediato el nombre del símbolo durante su creación. De esta forma, obtendremos un lista de colección de series temporales según la cantidad de símbolos utilizados en el programa. Todos los demás datos de los objetos de series temporales creados permanecerán vacíos: habrá que establecerlos por separado.
Hemos hecho esto así para que en la biblioteca siempre haya una lista de colección de series vacías en una cantidad igual a la cantidad de datos establecidos para el funcionamiento de los símbolos. En cuanto a los marcos temporales, y, propiamente, los objetos de series temporales con los que deberemos trabajar en el programa, se establecen ya en el siguiente paso, o a medida que resulten necesarios.
Pero es mejor crearlos en el manejador OnInit() del programa, o bien justo después de que comience a funcionar, dado que la creación de un gran número de series temporales necesita tiempo, sobre todo en un inicio "en frío" del programa.
Los cuatro métodos SetAvailable() sobrecargados sirven para establecer la bandera que indica la necesidad de trabajar en el programa con las series temporales establecidas.
Método para establecer la bandera de uso de la serie temporal indicada del símbolo establecido:
//+-----------------------------------------------------------------------+ //|Set the flag of using the specified timeseries of the specified symbol | //+-----------------------------------------------------------------------+ void CTimeSeriesCollection::SetAvailable(const string symbol,const ENUM_TIMEFRAMES timeframe,const bool flag=true) { int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return; CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return; CSeries *series=timeseries.GetSeries(timeframe); if(series==NULL) return; series.SetAvailable(flag); } //+------------------------------------------------------------------+
Transmitimos al método el símbolo, el marco temporal y la propia bandera que se debe establecer para la serie temporal que se corresponde con símbolo de marco temporal.
Primero obtenemos el índice de las series temporales CTimeSeries en la lista m_list según el símbolo con la ayuda del método IndexTimeSeries(), que hemos analizado anteriormente, y obtenemos según este índice la serie temporal de la lista. A partir del objeto de serie temporal, obtenemos la serie temporal CSeries necesaria del periodo que hemos indicado para el gráfico mediante el método GetSeries(), que analizamos en el anterior artículo, y establecemos para ella la bandera transmitida al método mediante el método SetAvailable() de la clase CBaseObj.
Método para establecer la bandera de uso de la serie temporal indicada para todos los símbolos:
//+------------------------------------------------------------------+ //|Set the flag of using the specified timeseries of all symbols | //+------------------------------------------------------------------+ void CTimeSeriesCollection::SetAvailable(const ENUM_TIMEFRAMES timeframe,const bool flag=true) { int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; CSeries *series=timeseries.GetSeries(timeframe); if(series==NULL) continue; series.SetAvailable(flag); } } //+------------------------------------------------------------------+
Transmitimos al método el marco temporal y la bandera que debemos establecer para la serie temporal indicada de todos los símbolos.
En un ciclo por la lista de todas las series temporales de los símbolos, obtenemos la siguiente serie temporal CTimeSeries según el índice del ciclo, luego, a partir del objeto de serie temporal, obtenemos la serie temporal CSeries indicada del periodo que hemos establecido para el gráfico mediante el método GetSeries(), que analizamos en el anterior artículo, y establecemos para ella la bandera transmitida al método mediante el método SetAvailable() de la clase CBaseObj.
Método para establecer la bandera de uso de todas las series temporales para el símbolo indicado:
//+------------------------------------------------------------------+ //|Set the flag of using all timeseries of the specified symbol | //+------------------------------------------------------------------+ void CTimeSeriesCollection::SetAvailable(const string symbol,const bool flag=true) { int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return; CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return; CArrayObj *list=timeseries.GetListSeries(); if(list==NULL) return; int total=list.Total(); for(int i=0;i<total;i++) { CSeries *series=list.At(i); if(series==NULL) continue; series.SetAvailable(flag); } } //+------------------------------------------------------------------+
Transmitimos al método el símbolo y la bandera que se debe establecer para todas las series temporales del símbolo indicado.
Primero obtenemos el índice de las series temporales CTimeSeries en la lista m_list según el símbolo con la ayuda del método IndexTimeSeries(), que hemos analizado anteriormente, y obtenemos según este índice la serie temporal CTimeSeries de la lista. Del objeto de serie temporal obtenido, obtenemos con la ayuda de GetListSeries() la lista completa de todas las series temporales CSeries. En un ciclo por la lista obtenida, obtenemos la siguiente serie temporal CSeries y establecemos para ella la bandera transmitida al método mediante el método SetAvailable() de la clase CBaseObj.
Método para establecer la bandera de uso de todas las series temporales de todos los símbolos:
//+------------------------------------------------------------------+ //| Set the flag of using all timeseries of all symbols | //+------------------------------------------------------------------+ void CTimeSeriesCollection::SetAvailable(const bool flag=true) { int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; CArrayObj *list=timeseries.GetListSeries(); if(list==NULL) continue; int total_series=list.Total(); for(int j=0;j<total_series;j++) { CSeries *series=list.At(j); if(series==NULL) continue; series.SetAvailable(flag); } } } //+--------------------------------------------------------------------+
Transmitimos al método la bandera que debemos establecer para todas las series temporales de todos los símbolos.
En un ciclo por la lista de las series temporales, obtenemos el siguiente objeto de serie temporal CTimeSeries según el índice del ciclo; después, del objeto obtenido mediante el método GetListSeries(), obtenemos la lista con todas las series temporales CSeries. A continuación, en un ciclo por la lista de series temporales CSeries, obtenemos la siguiente serie temporal según el índice del ciclo y establecemos para ella la bandera transmitida al método mediante el método SetAvailable() de la clase CBaseObj.
Los cuatro métodos encargados retornar la bandera de uso de todas las series temporales, o solo de las indicadas, son:
//+-------------------------------------------------------------------------+ //|Return the flag of using the specified timeseries of the specified symbol| //+-------------------------------------------------------------------------+ bool CTimeSeriesCollection::IsAvailable(const string symbol,const ENUM_TIMEFRAMES timeframe) { int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return false; CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return false; CSeries *series=timeseries.GetSeries(timeframe); if(series==NULL) return false; return series.IsAvailable(); } //+------------------------------------------------------------------+ //| Return the flag of using the specified timeseries of all symbols | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::IsAvailable(const ENUM_TIMEFRAMES timeframe) { bool res=true; int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; CSeries *series=timeseries.GetSeries(timeframe); if(series==NULL) continue; res &=series.IsAvailable(); } return res; } //+------------------------------------------------------------------+ //| Return the flag of using all timeseries of the specified symbol | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::IsAvailable(const string symbol) { bool res=true; int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return false; CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return false; CArrayObj *list=timeseries.GetListSeries(); if(list==NULL) return false; int total=list.Total(); for(int i=0;i<total;i++) { CSeries *series=list.At(i); if(series==NULL) continue; res &=series.IsAvailable(); } return res; } //+------------------------------------------------------------------+ //| Return the flag of using all timeseries of all symbols | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::IsAvailable(void) { bool res=true; int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; CArrayObj *list=timeseries.GetListSeries(); if(list==NULL) continue; int total_series=list.Total(); for(int j=0;j<total_series;j++) { CSeries *series=list.At(j); if(series==NULL) continue; res &=series.IsAvailable(); } } return res; } //+--------------------------------------------------------------------+
Los métodos funcionan de forma casi igual a los métodos para asignar la bandera a las series temporales, que acabamos de ver. La única diferencia es el uso de la variable local res, que tiene el estado inicial true. Para obtener la bandera "colectiva" de todas las series temporales en los métodos que retornan la bandera general de uso de multitud de series temporales. En un ciclo por las series temporales CSeries, registramos en la variable el estado de la bandera de cada serie temporal comprobada, y si, aunque sea una sola de ellas tiene el estado false, en la variable se registrará la bandera false. El valor de esta variable se retorna desde el método al finalizar todos los ciclos por todas las series temporales.
Los cuatro métodos para establecer la profundidad necesaria de la historia, ya sea para las series temporales indicadas, ya sea para todas las series temporales, es el siguiente:
//+--------------------------------------------------------------------------+ //|Set the history depth for the specified timeseries of the specified symbol| //+--------------------------------------------------------------------------+ bool CTimeSeriesCollection::SetRequiredUsedData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0) { int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return false; CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return false; CSeries *series=timeseries.GetSeries(timeframe); if(series==NULL) return false; return series.SetRequiredUsedData(required,rates_total); } //+------------------------------------------------------------------+ //| Set the history depth of the specified timeseries of all symbols | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::SetRequiredUsedData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0) { bool res=true; int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; CSeries *series=timeseries.GetSeries(timeframe); if(series==NULL) continue; res &=series.SetRequiredUsedData(required,rates_total); } return res; } //+------------------------------------------------------------------+ //| Set the history depth for all timeseries of the specified symbol | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::SetRequiredUsedData(const string symbol,const uint required=0,const int rates_total=0) { bool res=true; int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return false; CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return false; CArrayObj *list=timeseries.GetListSeries(); if(list==NULL) return false; int total=list.Total(); for(int i=0;i<total;i++) { CSeries *series=list.At(i); if(series==NULL) continue; res &=series.SetRequiredUsedData(required,rates_total); } return res; } //+------------------------------------------------------------------+ //| Set the history depth for all timeseries of all symbols | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::SetRequiredUsedData(const uint required=0,const int rates_total=0) { bool res=true; int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; CArrayObj *list=timeseries.GetListSeries(); if(list==NULL) continue; int total_series=list.Total(); for(int j=0;j<total_series;j++) { CSeries *series=list.At(j); if(series==NULL) continue; res &=series.SetRequiredUsedData(required,rates_total); } } return res; } //+------------------------------------------------------------------+
Los métodos funcionan exactamente igual que los métodos que retornan las banderas de uso de las series temporales. Retornan el resultado de la asignación a los objetos de series temporales CSeries de la cantidad de datos solicitada mediante los métodos SetRequiredUsedData(), que retornan un valor booleano. Por eso, aquí también se usa la bandera general al establecer la profundidad de la historia para multitud de series temporales CSeries.
Los cuatro métodos encargados retornar la bandera de sincronización, o bien de la serie temporal indicada, o bien de todas, son:
//+------------------------------------------------------------------+ //| Return the flag of data synchronization with the server data | //| for a specified timeseries of a specified symbol | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::SyncData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0) { int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return false; CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return false; CSeries *series=timeseries.GetSeries(timeframe); if(series==NULL) return false; return series.SyncData(required,rates_total); } //+------------------------------------------------------------------+ //| Return the flag of data synchronization with the server data | //| for a specified timeseries of all symbols | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::SyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0) { bool res=true; int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; CSeries *series=timeseries.GetSeries(timeframe); if(series==NULL) continue; res &=series.SyncData(required,rates_total); } return res; } //+------------------------------------------------------------------+ //| Return the flag of data synchronization with the server data | //| for all timeseries of a specified symbol | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::SyncData(const string symbol,const uint required=0,const int rates_total=0) { bool res=true; int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return false; CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return false; CArrayObj *list=timeseries.GetListSeries(); if(list==NULL) return false; int total=list.Total(); for(int i=0;i<total;i++) { CSeries *series=list.At(i); if(series==NULL) continue; res &=series.SyncData(required,rates_total); } return res; } //+------------------------------------------------------------------+ //| Return the flag of data synchronization with the server data | //| for all timeseries of all symbols | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::SyncData(const uint required=0,const int rates_total=0) { bool res=true; int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; CArrayObj *list=timeseries.GetListSeries(); if(list==NULL) continue; int total_series=list.Total(); for(int j=0;j<total_series;j++) { CSeries *series=list.At(j); if(series==NULL) continue; res &=series.SyncData(required,rates_total); } } return res; } //+------------------------------------------------------------------+
El funcionamiento de los métodos es idéntico al que hemos visto más arriba. Se retorna el resultado de la comprobación de la sincronización de las series temporales con el método SyncData() de la clase CSeries.
Hay cuatro métodos para crear las series temporales indicadas, o bien todas ellas.
Método para crear la serie temporal indicada del símbolo indicado:
//+------------------------------------------------------------------+ //| Create the specified timeseries of the specified symbol | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::CreateSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0) { int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return false; CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return false; return timeseries.Create(timeframe,required); } //+------------------------------------------------------------------+
Transmitimos al método el símbolo para el que debemos crear la serie temporal cuyo periodo también se transmite al método.
Obtenemos el índice de la serie temporal en la lista según la denomiación del símbolo mediante el método IndexTimeSeries(); según el índice obtenido, obtenemos la serie temporal CTimeSeries de la lista y retornamos el resultado de la creación de la serie temporal indicada mediante el método Create() de la clase CTimeSeries.
Método para crear la serie temporal indicada de todos los símbolos:
//+------------------------------------------------------------------+ //| Create the specified timeseries of all symbols | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::CreateSeries(const ENUM_TIMEFRAMES timeframe,const uint required=0) { bool res=true; int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; res &=timeseries.Create(timeframe,required); } return res; } //+------------------------------------------------------------------+
Transmitimos al método el marco temporal cuya serie temporal debemos crear para todos los símbolos.
En un ciclo por todos los objetos de series temporales de todos los símbolos, obtenemos el siguiente objeto de serie temporal CTimeSeries según el índice del ciclo, añadiendo luego a la variable res el resultado de la creación de la serie temporal indicada con el método Create() de la clase CTimeSeries. Al terminar el ciclo, retornamos el resultado de la creación de la serie temporal indicada para todos los símbolos. Con que solo una serie temporal no haya sido creada, el resultado será false.
Método para crear todas las series temporales del símbolo indicado:
//+------------------------------------------------------------------+ //| Create all timeseries of the specified symbol | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::CreateSeries(const string symbol,const uint required=0) { int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return false; CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return false; return timeseries.CreateAll(required); } //+------------------------------------------------------------------+
Transmitimos al método el símbolo para el que debemos crear todas las series temporales.
Obtenemos el índice de la serie temporal en la lista según la denomiación del símbolo mediante el método IndexTimeSeries(); según el índice obtenido, obtenemos la serie temporal CTimeSeries de la lista y retornamos el resultado de la creación de todas las series temporales mediante el método CreateAll() de la clase CTimeSeries.
Método para crear todas las series temporales de todos los símbolos:
//+------------------------------------------------------------------+ //| Create all timeseries of all symbols | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::CreateSeries(const uint required=0) { bool res=true; int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; res &=timeseries.CreateAll(required); } return res; } //+------------------------------------------------------------------+
Transmitimos al método solo la cantidad de la profundidad creada de la historia (como en todos los métodos analizados anteriormente). Por defecto, se transmite cero, lo que indica una profundidad de la historia igual a 1000 barras, establecida con la macrosustitución SERIES_DEFAULT_BARS_COUNT en el archivo Defines.mqh.
En un ciclo por la lista de series temporales, obtenemos el siguiente objeto de serie temporal CTimeSeries según el índice del ciclo, añadiendo luego a la variable res el resultado de todas las series temporales para el símbolo del objeto CTimeSeries actual mediante el método CreateAll(), que retorna la bandera de creación de todas las series temporales del símbolo del objeto CTimeSeries.
Al terminar el ciclo, retornamos el resultado de la creación de todas las series temporales para todos los símbolos. Con que solo una serie temporal no haya sido creada, el resultado será false.
Los cuatro métodos para actualizar las series temporales indicadas del símbolo indicado, o bien todas ellas, son:
//+------------------------------------------------------------------+ //| Update the specified timeseries of the specified symbol | //+------------------------------------------------------------------+ void CTimeSeriesCollection::Refresh(const string symbol,const ENUM_TIMEFRAMES timeframe, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return; CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return; timeseries.Refresh(timeframe,time,open,high,low,close,tick_volume,volume,spread); } //+------------------------------------------------------------------+ //| Update the specified timeseries of all symbols | //+------------------------------------------------------------------+ void CTimeSeriesCollection::Refresh(const ENUM_TIMEFRAMES timeframe, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; timeseries.Refresh(timeframe,time,open,high,low,close,tick_volume,volume,spread); } } //+------------------------------------------------------------------+ //| Update all timeseries of the specified symbol | //+------------------------------------------------------------------+ void CTimeSeriesCollection::Refresh(const string symbol, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return; CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return; timeseries.RefreshAll(time,open,high,low,close,tick_volume,volume,spread); } //+------------------------------------------------------------------+ //| Update all timeseries of all symbols | //+------------------------------------------------------------------+ void CTimeSeriesCollection::Refresh(const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; timeseries.RefreshAll(time,open,high,low,close,tick_volume,volume,spread); } } //+------------------------------------------------------------------+
Aquí, en todos los métodos, obtenemos de una forma u otra (hemos analizado todas las maneras en los métodos anteriores) el objeto de serie temporal CTimeSeries indicado y llamamos los métodos de actualización de una serie temporal indicada Refresh(), o de todas las series temporales, RefreshAll(), de la clase CTimeSeries.
Transmitimos a todos los métodos los datos actuales de las matrices de series temporales. Esto es necesario para trabajar en los indicadores en el periodo actual del símbolo actual. En el resto de los casos, los valores transmitidos no son importantes, por eso, se les asigna por defecto 0.
Método que retorna al diario la descripción completa de la colección:
//+------------------------------------------------------------------+ //| Display complete collection description to the journal | //+------------------------------------------------------------------+ void CTimeSeriesCollection::Print(const bool created=true) { int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; timeseries.Print(created); } } //+------------------------------------------------------------------+
Al método se le transmite la bandera que indica la necesidad de mostrar en el diario solo las series temporales creadas.
En un ciclo por la lista de objetos de series temporales, obtenemos el siguiente objeto de la serie temporal CTimeSeries y llamamos su método homónimo. Ya hemos visto los resultados de su trabajo anteriormente. Finalmente, se imprimirán en el diario los datos de todas las series temporales disponibles de todos los símbolos de la colección.
Método que muestra en el diario la descripción breve de la colección:
//+------------------------------------------------------------------+ //| Display the short collection description in the journal | //+------------------------------------------------------------------+ void CTimeSeriesCollection::PrintShort(const bool created=true) { int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; timeseries.PrintShort(created); } } //+------------------------------------------------------------------+
Al método se le transmite la bandera que indica la necesidad de mostrar en el diario solo las series temporales creadas.
En un ciclo por la lista de objetos de series temporales, obtenemos el siguiente objeto de la serie temporal CTimeSeries y llamamos su método homónimo. Ya hemos visto los resultados de su trabajo anteriormente. Finalmente, se imprimirán en el diario los datos de todas las series temporales disponibles de todos los símbolos de la colección, de forma breve.
La clase de colección de series temporales en su versión actual está preparada. En los archivos adjuntos al final del artículo, podemos ver y analizar el listado completo de la clase.
Ahora, debemos organizar el acceso desde el exterior a la colección de series temporales creada, y también implementar un establecimiento cómodo de los parámetros para la series creadas.
Cualquier programa obtiene acceso a los métodos de la biblioteca desde el objeto principal de la biblioteca CEngine.
Vamos a añadir al archivo de la clase CEngine el acceso al trabajo con la colección de series temporales.
Abrimos el archivo Engine.mqh del directorio de la biblioteca \MQL5\Include\DoEasy\ e introducimos en él los cambios necesarios.
Dado que en la versión anterior incluimos en la clase CEngine el archivo de clase CTimeSeries solo para comprobar su funcionamiento, eliminaremos este de la lista de archivos de inclusión
//+------------------------------------------------------------------+ //| Archivos de inclusión | //+------------------------------------------------------------------+ #include "Services\TimerCounter.mqh" #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Collections\AccountsCollection.mqh" #include "Collections\SymbolsCollection.mqh" #include "Collections\ResourceCollection.mqh" #include "TradingControl.mqh" #include "Objects\Series\TimeSeries.mqh" //+------------------------------------------------------------------+
e incluiremos el archivo de la clase de colección de series temporales:
//+------------------------------------------------------------------+ //| Engine.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" //+------------------------------------------------------------------+ //| Archivos de inclusión | //+------------------------------------------------------------------+ #include "Services\TimerCounter.mqh" #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Collections\AccountsCollection.mqh" #include "Collections\SymbolsCollection.mqh" #include "Collections\ResourceCollection.mqh" #include "Collections\TimeSeriesCollection.mqh" #include "TradingControl.mqh" //+------------------------------------------------------------------+
En la sección privada de la clase, declaramos la variable con el tipo de la clase de la colección de series temporales:
//+------------------------------------------------------------------+ //| Library basis class | //+------------------------------------------------------------------+ class CEngine { private: CHistoryCollection m_history; // Collection of historical orders and deals CMarketCollection m_market; // Collection of market orders and deals CEventsCollection m_events; // Event collection CAccountsCollection m_accounts; // Account collection CSymbolsCollection m_symbols; // Symbol collection CTimeSeriesCollection m_series; // Timeseries collection CResourceCollection m_resource; // Resource list CTradingControl m_trading; // Trading management object CArrayObj m_list_counters; // List of timer counters
En la sección pública de la clase, cambiamos la implementación del método para establecer la lista de símbolos utilizados:
//--- Set the list of (1) used symbols bool SetUsedSymbols(const string &array_symbols[]) { return this.m_symbols.SetUsedSymbols(array_symbols);}
En esta versión, dicho método llama al método homónimo para establecer la lista de símbolos para la clase de colección de símbolos. Y precisamente en este lugar, justo después del establecimiento de la lista de símbolos en la colección de símbolos, será cómodo para nosotros crear también la colección de series temporales basadas en la lista creada de colección de símbolos.
Solo dejaremos aquí la declaración del método:
//--- Set the list of used symbols in the symbol collection and create the collection of symbol timeseries bool SetUsedSymbols(const string &array_symbols[]);
y la implementaremos fuera del cuerpo de la clase:
//+------------------------------------------------------------------+ //| Set the list of used symbols in the symbol collection | //| and create the symbol timeseries collection | //+------------------------------------------------------------------+ bool CEngine::SetUsedSymbols(const string &array_symbols[]) { bool res=this.m_symbols.SetUsedSymbols(array_symbols); CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL) return false; res&=this.m_series.CreateCollection(list); return res; } //+------------------------------------------------------------------+
Aquí, declaramos la variableres y la inicializamos con el resultado del funcionamiento del método para establecer la lista de símbolos en la colección de símbolos.
A continuación, obtenemos la lista de símbolos utilizados de la clase de colección de símbolos; si la lista no ha sido establecida, retornaremos false,
de lo contrario, añadiremos a la variable res el resultado de la creación de la colección de series temporales, usando como base la lista de la colección de símbolos.
Retornamos el resultado total desde el método.
Añadimos a la sección pública de la clase los métodos para trabajar con la colección de series temporales:
//--- Return the list of pending requests CArrayObj *GetListPendingRequests(void) { return this.m_trading.GetListRequests(); } //--- Return (1) the timeseries collection and (2) the list of timeseries from the timeseries collection CTimeSeriesCollection *GetTimeSeriesCollection(void) { return &this.m_series; } CArrayObj *GetListTimeSeries(void) { return this.m_series.GetList(); } //--- Set the flag of using (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols //--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols void SeriesSetAvailable(const string symbol,const ENUM_TIMEFRAMES timeframe,const bool flag=true) { this.m_series.SetAvailable(symbol,timeframe,flag);} void SeriesSetAvailable(const ENUM_TIMEFRAMES timeframe,const bool flag=true) { this.m_series.SetAvailable(timeframe,flag); } void SeriesSetAvailable(const string symbol,const bool flag=true) { this.m_series.SetAvailable(symbol,flag); } void SeriesSetAvailable(const bool flag=true) { this.m_series.SetAvailable(flag); } //--- Set the history depth of (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols //--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols bool SeriesSetRequiredUsedData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0) { return this.m_series.SetRequiredUsedData(symbol,timeframe,required,rates_total);} bool SeriesSetRequiredUsedData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0) { return this.m_series.SetRequiredUsedData(timeframe,required,rates_total); } bool SeriesSetRequiredUsedData(const string symbol,const uint required=0,const int rates_total=0) { return this.m_series.SetRequiredUsedData(symbol,required,rates_total); } bool SeriesSetRequiredUsedData(const uint required=0,const int rates_total=0) { return this.m_series.SetRequiredUsedData(required,rates_total); } //--- Return the flag of data synchronization with the server data of the (1) specified timeseries of the specified symbol, //--- (2) the specified timeseries of all symbols, (3) all timeseries of the specified symbol and (4) all timeseries of all symbols bool SeriesSyncData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0) { return this.m_series.SyncData(symbol,timeframe,required,rates_total); } bool SeriesSyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0) { return this.m_series.SyncData(timeframe,required,rates_total); } bool SeriesSyncData(const string symbol,const uint required=0,const int rates_total=0) { return this.m_series.SyncData(symbol,required,rates_total); } bool SeriesSyncData(const uint required=0,const int rates_total=0) { return this.m_series.SyncData(required,rates_total); } //--- Create (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols, //--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols bool SeriesCreate(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0) { return this.m_series.CreateSeries(symbol,timeframe,required); } bool SeriesCreate(const ENUM_TIMEFRAMES timeframe,const uint required=0) { return this.m_series.CreateSeries(timeframe,required); } bool SeriesCreate(const string symbol,const uint required=0) { return this.m_series.CreateSeries(symbol,required); } bool SeriesCreate(const uint required=0) { return this.m_series.CreateSeries(required); } //--- Update (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols, //--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols void SeriesRefresh(const string symbol,const ENUM_TIMEFRAMES timeframe, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { this.m_series.Refresh(symbol,timeframe,time,open,high,low,close,tick_volume,volume,spread); } void SeriesRefresh(const ENUM_TIMEFRAMES timeframe, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { this.m_series.Refresh(timeframe,time,open,high,low,close,tick_volume,volume,spread); } void SeriesRefresh(const string symbol, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { this.m_series.Refresh(symbol,time,open,high,low,close,tick_volume,volume,spread); } void SeriesRefresh(const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { this.m_series.Refresh(time,open,high,low,close,tick_volume,volume,spread); }
El método GetTimeSeriesCollection() retorna al programa de control el puntero al objeto de colección de series temporales. Permite obtener en el programa el puntero a la colección y trabajar con ella de forma plenamente operativa.
El método GetListTimeSeries() retorna al programa de control el puntero a la lista de series temporales de la colección. Permite obtener en el programa el puntero a la lista de objetos de series temporales CTimeSeries y trabajar con ella de forma plenamente operativa.
Los métodos sobrecargados SeriesSetAvailable() dan acceso a los métodos SetAvailable() de la clase de colección de series temporales CTimeSeriesCollection(), que ya hemos analizado anteriormente.
Los métodos sobrecargados SeriesSetRequiredUsedData() dan acceso a los métodos SetRequiredUsedData() de la clase de colección de series temporales CTimeSeriesCollection(), que ya hemos analizado anteriormente.
Los métodos sobrecargados SeriesSyncData() dan acceso a los métodos SyncData() de la clase de colección de series temporales CTimeSeriesCollection(), que ya hemos analizado anteriormente.
Los métodos sobrecargados SeriesCreate() dan acceso a los métodos CreateSeries() de la clase de colección de series temporales CTimeSeriesCollection(), que ya hemos analizado anteriormente.
Los métodos sobrecargados SeriesRefresh() dan acceso a los métodos Refresh() de la clase de colección de series temporales CTimeSeriesCollection(), que ya hemos analizado anteriormente.
Y estas son todas las tareas necesarias por hoy en cuanto a la mejora de las clases para la simulación de la nueva clase de colección de series temporales.
Simulación
Para realizar la simulación de la creación y el rellenado de la colección de series temporales, tomaremos el asesor del artículo anterior
y lo guardaremos en la nueva carpeta \MQL5\Experts\TestDoEasy\Part37\ con el nuevo nombre TestDoEasyPart37.mq5.
Para seleccionar el modo en que el programa trabajará con los símbolos, tenemos la enumeración Datas.mqh
//+------------------------------------------------------------------+ //| Modes of working with symbols | //+------------------------------------------------------------------+ enum ENUM_SYMBOLS_MODE { SYMBOLS_MODE_CURRENT, // Work with the current symbol only SYMBOLS_MODE_DEFINES, // Work with the specified symbol list SYMBOLS_MODE_MARKET_WATCH, // Work with the Market Watch window symbols SYMBOLS_MODE_ALL // Work with the full symbol list }; //+------------------------------------------------------------------+
Y en los parámetros de entrada del asesor, tenemos una variable que permite seleccionar los símbolos para trabajar con ellos:
sinput ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; // Mode of used symbols list
Usando como base los valores de esta variable, se crea una matriz de símbolos que se transmite a la biblioteca al inicializarse esta en la función del asesor OnInitDoEasy(), y ya después en la biblioteca se crea la lista de símbolos de trabajo.
Las mismas operaciones debemos realizar para seleccionar y crear la lista de marcos temporales de trabajo del asesor.
Vamos a crear una nueva enumeración en el archivo Datas.mqh para seleccionar los modos de trabajo con los periodos de los gráficos de los símbolos:
//+------------------------------------------------------------------+ //| Datas.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" //+------------------------------------------------------------------+ //| Macrosustituciones | //+------------------------------------------------------------------+ #define INPUT_SEPARATOR (",") // Separator in the inputs string #define TOTAL_LANG (2) // Number of used languages //+------------------------------------------------------------------+ //| Enumerations | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Modes of working with symbols | //+------------------------------------------------------------------+ enum ENUM_SYMBOLS_MODE { SYMBOLS_MODE_CURRENT, // Work with the current symbol only SYMBOLS_MODE_DEFINES, // Work with the specified symbol list SYMBOLS_MODE_MARKET_WATCH, // Work with the Market Watch window symbols SYMBOLS_MODE_ALL // Work with the full symbol list }; //+------------------------------------------------------------------+ //| Mode of working with timeframes | //+------------------------------------------------------------------+ enum ENUM_TIMEFRAMES_MODE { TIMEFRAMES_MODE_CURRENT, // Work with the current timeframe only TIMEFRAMES_MODE_LIST, // Work with the specified timeframe list TIMEFRAMES_MODE_ALL // Work with the full timeframe list }; //+------------------------------------------------------------------+
En el archivo de funciones de servicio, tenemos una función para preparar la matriz de símbolos utilizados para la biblioteca:
//+------------------------------------------------------------------+ //| Prepare the symbol array for a symbol collection | //+------------------------------------------------------------------+ bool CreateUsedSymbolsArray(const ENUM_SYMBOLS_MODE mode_used_symbols,string defined_used_symbols,string &used_symbols_array[]) { //--- When working with the current symbol if(mode_used_symbols==SYMBOLS_MODE_CURRENT) { //--- Write the name of the current symbol to the only array cell ArrayResize(used_symbols_array,1); used_symbols_array[0]=Symbol(); return true; } //--- If working with a predefined symbol set (from the defined_used_symbols string) else if(mode_used_symbols==SYMBOLS_MODE_DEFINES) { //--- Set a comma as a separator string separator=","; //--- Replace erroneous separators with correct ones if(StringFind(defined_used_symbols,";")>WRONG_VALUE) StringReplace(defined_used_symbols,";",separator); if(StringFind(defined_used_symbols,":")>WRONG_VALUE) StringReplace(defined_used_symbols,":",separator); if(StringFind(defined_used_symbols,"|")>WRONG_VALUE) StringReplace(defined_used_symbols,"|",separator); if(StringFind(defined_used_symbols,"/")>WRONG_VALUE) StringReplace(defined_used_symbols,"/",separator); if(StringFind(defined_used_symbols,"\\")>WRONG_VALUE) StringReplace(defined_used_symbols,"\\",separator); if(StringFind(defined_used_symbols,"'")>WRONG_VALUE) StringReplace(defined_used_symbols,"'",separator); if(StringFind(defined_used_symbols,"-")>WRONG_VALUE) StringReplace(defined_used_symbols,"-",separator); if(StringFind(defined_used_symbols,"`")>WRONG_VALUE) StringReplace(defined_used_symbols,"`",separator); //--- Delete as long as there are spaces while(StringFind(defined_used_symbols," ")>WRONG_VALUE && !IsStopped()) StringReplace(defined_used_symbols," ",""); //--- As soon as there are double separators (after removing spaces between them), replace them with a separator while(StringFind(defined_used_symbols,separator+separator)>WRONG_VALUE && !IsStopped()) StringReplace(defined_used_symbols,separator+separator,separator); //--- If a single separator remains before the first symbol in the string, replace it with a space if(StringFind(defined_used_symbols,separator)==0) StringSetCharacter(defined_used_symbols,0,32); //--- If a single separator remains after the last symbol in the string, replace it with a space if(StringFind(defined_used_symbols,separator)==StringLen(defined_used_symbols)-1) StringSetCharacter(defined_used_symbols,StringLen(defined_used_symbols)-1,32); //--- Remove all redundant things to the left and right #ifdef __MQL5__ StringTrimLeft(defined_used_symbols); StringTrimRight(defined_used_symbols); //--- __MQL4__ #else defined_used_symbols=StringTrimLeft(defined_used_symbols); defined_used_symbols=StringTrimRight(defined_used_symbols); #endif //--- Prepare the array ArrayResize(used_symbols_array,0); ResetLastError(); //--- divide the string by separators (comma) and add all found substrings to the array int n=StringSplit(defined_used_symbols,StringGetCharacter(separator,0),used_symbols_array); //--- if nothing is found, display the appropriate message (working with the current symbol is selected automatically) if(n<1) { string err= (n==0 ? DFUN_ERR_LINE+CMessage::Text(MSG_LIB_SYS_ERROR_EMPTY_STRING)+Symbol() : DFUN_ERR_LINE+CMessage::Text(MSG_LIB_SYS_FAILED_PREPARING_SYMBOLS_ARRAY)+(string)GetLastError() ); Print(err); return false; } } //--- If working with the Market Watch window or the full list else { //--- Add the (mode_used_symbols) working mode to the only array cell ArrayResize(used_symbols_array,1); used_symbols_array[0]=EnumToString(mode_used_symbols); } return true; } //+------------------------------------------------------------------+
Tenemos que hacer una función semejante para preparar la matriz de marcos temporales utilizados. Y aquí entendemos que vamos a tener el mismo bloque de código en dos funciones análogas (marcado a color en el listado presentado).
Sacamos este bloque de código a una función aparte:
//+------------------------------------------------------------------+ //| Prepare the passed string of parameters | //+------------------------------------------------------------------+ int StringParamsPrepare(string defined_used,string separator,string &array[]) { //--- Replace erroneous separators with correct ones if(separator!=";" && StringFind(defined_used,";")>WRONG_VALUE) StringReplace(defined_used,";",separator); if(separator!=":" && StringFind(defined_used,":")>WRONG_VALUE) StringReplace(defined_used,":",separator); if(separator!="|" && StringFind(defined_used,"|")>WRONG_VALUE) StringReplace(defined_used,"|",separator); if(separator!="/" && StringFind(defined_used,"/")>WRONG_VALUE) StringReplace(defined_used,"/",separator); if(separator!="\\"&& StringFind(defined_used,"\\")>WRONG_VALUE) StringReplace(defined_used,"\\",separator); if(separator!="'" && StringFind(defined_used,"'")>WRONG_VALUE) StringReplace(defined_used,"'",separator); if(separator!="-" && StringFind(defined_used,"-")>WRONG_VALUE) StringReplace(defined_used,"-",separator); if(separator!="`" && StringFind(defined_used,"`")>WRONG_VALUE) StringReplace(defined_used,"`",separator); //--- Delete as long as there are spaces while(StringFind(defined_used," ")>WRONG_VALUE && !IsStopped()) StringReplace(defined_used," ",""); //--- As soon as there are double separators (after removing spaces between them), replace them with a separator while(StringFind(defined_used,separator+separator)>WRONG_VALUE && !IsStopped()) StringReplace(defined_used,separator+separator,separator); //--- If a single separator remains before the first symbol in the string, replace it with a space if(StringFind(defined_used,separator)==0) StringSetCharacter(defined_used,0,32); //--- If a single separator remains after the last symbol in the string, replace it with a space if(StringFind(defined_used,separator)==StringLen(defined_used)-1) StringSetCharacter(defined_used,StringLen(defined_used)-1,32); //--- Remove all redundant things to the left and right #ifdef __MQL5__ StringTrimLeft(defined_used); StringTrimRight(defined_used); //--- __MQL4__ #else defined_used=StringTrimLeft(defined_used); defined_used=StringTrimRight(defined_used); #endif //--- Prepare the array ArrayResize(array,0); ResetLastError(); //--- divide the string by separators (comma), write all detected substrings into the array and return the number of obtained substrings return StringSplit(defined_used,StringGetCharacter(separator,0),array); } //+------------------------------------------------------------------+
Y entonces, la función para crear la matriz de símbolos utilizados adoptará el aspecto que sigue:
//+------------------------------------------------------------------+ //| Prepare the symbol array for a symbol collection | //+------------------------------------------------------------------+ bool CreateUsedSymbolsArray(const ENUM_SYMBOLS_MODE mode_used_symbols,string defined_used_symbols,string &used_symbols_array[]) { //--- When working with the current symbol if(mode_used_symbols==SYMBOLS_MODE_CURRENT) { //--- Write the name of the current symbol to the only array cell ArrayResize(used_symbols_array,1); used_symbols_array[0]=Symbol(); return true; } //--- If working with a predefined symbol set (from the defined_used_symbols string) else if(mode_used_symbols==SYMBOLS_MODE_DEFINES) { //--- Set comma as a separator (defined in the Datas.mqh file, page 11) string separator=INPUT_SEPARATOR; int n=StringParamsPrepare(defined_used_symbols,separator,used_symbols_array); //--- if nothing is found, display the appropriate message (working with the current symbol is selected automatically) if(n<1) { int err_code=GetLastError(); string err= (n==0 ? DFUN_ERR_LINE+CMessage::Text(MSG_LIB_SYS_ERROR_EMPTY_SYMBOLS_STRING)+Symbol() : DFUN_ERR_LINE+CMessage::Text(MSG_LIB_SYS_FAILED_PREPARING_SYMBOLS_ARRAY)+(string)err_code+": "+CMessage::Text(err_code) ); Print(err); return false; } } //--- If working with the Market Watch window or the full list else { //--- Add the (mode_used_symbols) working mode to the only array cell ArrayResize(used_symbols_array,1); used_symbols_array[0]=EnumToString(mode_used_symbols); } //--- All is successful return true; } //+------------------------------------------------------------------+
El bloque de código sacado a una función aparte ahora ha sido reemplazado por la llamada de la función de preparación de una línea de parámetros.
Ahora, creamos exactamente de la misma forma la función para preparar la matriz de marcos temporales utilizados en el programa:
//+------------------------------------------------------------------+ //| Prepare the array of timeframes for the timeseries collection | //+------------------------------------------------------------------+ bool CreateUsedTimeframesArray(const ENUM_TIMEFRAMES_MODE mode_used_periods,string defined_used_periods,string &used_periods_array[]) { //--- If working with the current chart period, set the current timeframe flag if(mode_used_periods==TIMEFRAMES_MODE_CURRENT) { ArrayResize(used_periods_array,1,21); used_periods_array[0]=TimeframeDescription((ENUM_TIMEFRAMES)Period()); return true; } //--- If working with a predefined set of chart periods (from the defined_used_periods string) else if(mode_used_periods==TIMEFRAMES_MODE_LIST) { //--- Set comma as a separator (defined in the Datas.mqh file, page 11) string separator=INPUT_SEPARATOR; //--- Fill in the array of parameters from the string with predefined timeframes int n=StringParamsPrepare(defined_used_periods,separator,used_periods_array); //--- if nothing is found, display the appropriate message (working with the current period is selected automatically) if(n<1) { int err_code=GetLastError(); string err= (n==0 ? DFUN_ERR_LINE+CMessage::Text(MSG_LIB_SYS_ERROR_EMPTY_PERIODS_STRING)+TimeframeDescription((ENUM_TIMEFRAMES)Period()) : DFUN_ERR_LINE+CMessage::Text(MSG_LIB_SYS_FAILED_PREPARING_PERIODS_ARRAY)+(string)err_code+": "+CMessage::Text(err_code) ); Print(err); //--- Set the current period to the array ArrayResize(used_periods_array,1,21); used_periods_array[0]=TimeframeDescription((ENUM_TIMEFRAMES)Period()); return false; } } //--- If working with the full list of timeframes, fill in the array with strings describing all timeframes else { ArrayResize(used_periods_array,21,21); for(int i=0;i<21;i++) used_periods_array[i]=TimeframeDescription(TimeframeByEnumIndex(uchar(i+1))); } //--- All is successful return true; } //+------------------------------------------------------------------+
Aquí, llamamos exactamente de la misma manera la función para preparar la línea de parámetros: estos códigos son idénticos en las dos funciones CreateUsedSymbolsArray() y CreateUsedTimeframesArray().
En el mismo lugar, en el archivo DELib.mqh, tenemos una función que muestra la hora con milisegundos:
//+------------------------------------------------------------------+ //| Retorna la hora con milisegundos | //+------------------------------------------------------------------+ string TimeMSCtoString(const long time_msc) { return TimeToString(time_msc/1000,TIME_DATE|TIME_MINUTES|TIME_SECONDS)+"."+IntegerToString(time_msc%1000,3,'0'); } //+------------------------------------------------------------------+
La función siempre muestra la fecha y la hora en el formato YYYY.MM.DD HH:MM:SS.MSC
Creamos la posibilidad de seleccionar el formato para mostrar la hora:
//+------------------------------------------------------------------+ //| Retorna la hora con milisegundos | //+------------------------------------------------------------------+ string TimeMSCtoString(const long time_msc,int flags=TIME_DATE|TIME_MINUTES|TIME_SECONDS) { return TimeToString(time_msc/1000,flags)+"."+IntegerToString(time_msc%1000,3,'0'); } //+------------------------------------------------------------------+
Ahora, será posible establecer el formato de muestra de hora + milisegundos.
Añadimos al bloque de parámetros de entrada del archivo del asesor la selección de los marcos temporales utilizados:
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
InpModeUsedTFs permite seleccionar el modo de uso de los marcos temporales
- Trabajar solo con el marco temporal actual
- Trabajar con la lista de marcos temporales establecida
- Trabajar con la lista de marcos temporales completa
Al seleccionar el segundo punto, en el programa se usará la lista de marcos temporales descritos en la línea establecida por el parámetro de entrada de la variable InpUsedTFs.
Ahora, no vamos a eliminar del bloque de variables globales del asesor la variable necesaria que declara el objeto de clase CTimeSeries, ya que el acceso a las series temporales se realiza recurriendo al objeto de la biblioteca engine.
//--- global variables CEngine engine; CTimeSeries timeseries; SDataButt butt_data[TOTAL_BUTT];
En este mismo bloque de variables globales, añadimos una nueva matriz en la que se registrarán las denominaciones de los marcos temporales utilizados por el programa:
//--- 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; //+------------------------------------------------------------------+
Eliminamos del manejador OnInit() del asesor el código para crear las dos series temporales que han quedado de las anteriores simulaciones:
//+------------------------------------------------------------------+ //| 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(); //--- Check and remove remaining EA graphical objects if(IsPresentObects(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); Sleep(600); engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","Falling coin 2")); //--- Set a symbol for created timeseries timeseries.SetSymbol(Symbol()); //#define TIMESERIES_ALL //--- Create two timeseries #ifndef TIMESERIES_ALL timeseries.SyncData(PERIOD_CURRENT,10); timeseries.Create(PERIOD_CURRENT); timeseries.SyncData(PERIOD_M15,2); timeseries.Create(PERIOD_M15); //--- Create all timeseries #else timeseries.SyncAllData(); timeseries.CreateAll(); #endif //--- Check created timeseries CArrayObj *list=timeseries.GetList(); Print(TextByLanguage("Данные созданных таймсерий:","Data of created timeseries:")); for(int i=0;i<list.Total();i++) { CSeries *series_obj=timeseries.GetSeriesByIndex((uchar)i); if(series_obj==NULL || series_obj.AmountUsedData()==0 || series_obj.DataTotal()==0) continue; Print( DFUN,i,": ",series_obj.Symbol()," ",TimeframeDescription(series_obj.Timeframe()), ": AmountUsedData=",series_obj.AmountUsedData(),", DataTotal=",series_obj.DataTotal(),", Bars=",series_obj.Bars() ); } Print(""); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Y también eliminamos del manejador OnTick() del aseasor el código de actualización de las series temporales creadas (la actualización de las series temporales la implementaremos en el próximo artículo):
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- If working in the tester if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(); // Working in the timer PressButtonsControl(); // Button pressing control EventsHandling(); // Working with events } //--- If the trailing flag is set if(trailing_on) { TrailingPositions(); // Trailing positions TrailingOrders(); // Trailing of pending orders } //--- Update created timeseries CArrayObj *list=timeseries.GetList(); for(int i=0;i<list.Total();i++) { CSeries *series_obj=timeseries.GetSeriesByIndex((uchar)i); if(series_obj==NULL || series_obj.DataTotal()==0) continue; series_obj.Refresh(); if(series_obj.IsNewBar(0)) { Print(TextByLanguage("Новый бар на ","New bar on "),series_obj.Symbol()," ",TimeframeDescription(series_obj.Timeframe())," ",TimeToString(series_obj.Time(0))); if(series_obj.Timeframe()==Period()) engine.PlaySoundByDescription(SND_NEWS); } } } //+------------------------------------------------------------------+
A continuación, añadimos a la función de inicialización de la biblioteca OnInitDoEasy() la creación y representación de la lista de marcos temporales utilizados en el asesor, así como la muestra de la fecha y la hora de inicialización de la biblioteca en el diario.
Veamos el listado completo. Para comprender mejor qué hemos hecho exactamente, hemos destacado a color los cambios y hemos comentado las líneas:
//+------------------------------------------------------------------+ //| Initializing DoEasy library | //+------------------------------------------------------------------+ void OnInitDoEasy() { //--- Check if working with the full list is selected used_symbols_mode=InpModeUsedSymbols; if((ENUM_SYMBOLS_MODE)used_symbols_mode==SYMBOLS_MODE_ALL) { int total=SymbolsTotal(false); string ru_n="\nКоличество символов на сервере "+(string)total+".\nМаксимальное количество: "+(string)SYMBOLS_COMMON_TOTAL+" символов."; string en_n="\nNumber of symbols on server "+(string)total+".\nMaximum number: "+(string)SYMBOLS_COMMON_TOTAL+" symbols."; string caption=TextByLanguage("Внимание!","Attention!"); string ru="Выбран режим работы с полным списком.\nВ этом режиме первичная подготовка списков коллекций символов и таймсерий может занять длительное время."+ru_n+"\nПродолжить?\n\"Нет\" - работа с текущим символом \""+Symbol()+"\""; string en="Full list mode selected.\nIn this mode, the initial preparation of lists of symbol collections and timeseries can take a long time."+en_n+"\nContinue?\n\"No\" - working with the current symbol \""+Symbol()+"\""; string message=TextByLanguage(ru,en); int flags=(MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2); int mb_res=MessageBox(message,caption,flags); switch(mb_res) { case IDNO : used_symbols_mode=SYMBOLS_MODE_CURRENT; break; default: break; } } //--- Set the counter start point to measure the approximate library initialization time ulong begin=GetTickCount(); Print(TextByLanguage("--- Инициализация библиотеки \"DoEasy\" ---","--- Initializing the \"DoEasy\" library ---")); //--- Fill in the array of used symbols CreateUsedSymbolsArray((ENUM_SYMBOLS_MODE)used_symbols_mode,InpUsedSymbols,array_used_symbols); //--- Set the type of the used symbol list in the symbol collection and fill in the list of symbol timeseries engine.SetUsedSymbols(array_used_symbols); //--- Displaying the selected mode of working with the symbol object collection in the journal string num= ( used_symbols_mode==SYMBOLS_MODE_CURRENT ? ": \""+Symbol()+"\"" : TextByLanguage(". Количество используемых символов: ",". The number of symbols used: ")+(string)engine.GetSymbolsCollectionTotal() ); Print(engine.ModeSymbolsListDescription(),num); //--- Implement displaying the list of used symbols only for MQL5 - MQL4 has no ArrayPrint() function #ifdef __MQL5__ if(InpModeUsedSymbols!=SYMBOLS_MODE_CURRENT) { string array_symbols[]; CArrayObj* list_symbols=engine.GetListAllUsedSymbols(); for(int i=0;i<list_symbols.Total();i++) { CSymbol *symbol=list_symbols.At(i); if(symbol==NULL) continue; ArrayResize(array_symbols,ArraySize(array_symbols)+1,1000); array_symbols[ArraySize(array_symbols)-1]=symbol.Name(); } ArrayPrint(array_symbols); } #endif //--- Set used timeframes CreateUsedTimeframesArray(InpModeUsedTFs,InpUsedTFs,array_used_periods); //--- Display the selected mode of working with the timeseries object collection string mode= ( InpModeUsedTFs==TIMEFRAMES_MODE_CURRENT ? TextByLanguage("Работа только с текущим таймфреймом: ","Work only with the current Period: ")+TimeframeDescription((ENUM_TIMEFRAMES)Period()) : InpModeUsedTFs==TIMEFRAMES_MODE_LIST ? TextByLanguage("Работа с заданным списком таймфреймов:","Work with a predefined list of Periods:") : TextByLanguage("Работа с полным списком таймфреймов:","Work with the full list of all Periods:") ); Print(mode); //--- Implement displaying the list of used timeframes only for MQL5 - MQL4 has no ArrayPrint() function #ifdef __MQL5__ if(InpModeUsedTFs!=TIMEFRAMES_MODE_CURRENT) ArrayPrint(array_used_periods); #endif //--- Create timeseries of all used symbols CArrayObj *list_timeseries=engine.GetListTimeSeries(); int total=list_timeseries.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=list_timeseries.At(i); if(list_timeseries==NULL) continue; int total_periods=ArraySize(array_used_periods); for(int j=0;j<total_periods;j++) { ENUM_TIMEFRAMES timeframe=TimeframeByDescription(array_used_periods[j]); engine.SeriesSyncData(timeseries.Symbol(),timeframe); engine.SeriesCreate(timeseries.Symbol(),timeframe); } } //--- Check created timeseries - display descriptions of all created timeseries in the journal //--- (true - only created ones, false - created and declared ones) engine.GetTimeSeriesCollection().PrintShort(true); // Short descriptions //engine.GetTimeSeriesCollection().Print(false); // Full descriptions //--- Create resource text files engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_01",TextByLanguage("Звук упавшей монетки 1","Falling coin 1"),sound_array_coin_01); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_02",TextByLanguage("Звук упавших монеток","Falling coins"),sound_array_coin_02); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_03",TextByLanguage("Звук монеток","Coins"),sound_array_coin_03); engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_04",TextByLanguage("Звук упавшей монетки 2","Falling coin 2"),sound_array_coin_04); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_01",TextByLanguage("Звук щелчка по кнопке 1","Button click 1"),sound_array_click_01); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_02",TextByLanguage("Звук щелчка по кнопке 2","Button click 2"),sound_array_click_02); engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_03",TextByLanguage("Звук щелчка по кнопке 3","Button click 3"),sound_array_click_03); engine.CreateFile(FILE_TYPE_WAV,"sound_array_cash_machine_01",TextByLanguage("Звук кассового аппарата","Cash machine"),sound_array_cash_machine_01); engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_green",TextByLanguage("Изображение \"Зелёный светодиод\"","Image \"Green Spot lamp\""),img_array_spot_green); engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_red",TextByLanguage("Изображение \"Красный светодиод\"","Image \"Red Spot lamp\""),img_array_spot_red); //--- Pass all existing collections to the trading class engine.TradingOnInit(); //--- Set the default magic number for all used symbols engine.TradingSetMagic(engine.SetCompositeMagicNumber(magic_number)); //--- Set synchronous passing of orders for all used symbols engine.TradingSetAsyncMode(false); //--- Set the number of trading attempts in case of an error engine.TradingSetTotalTry(InpTotalAttempts); //--- Set correct order expiration and filling types to all trading objects engine.TradingSetCorrectTypeExpiration(); engine.TradingSetCorrectTypeFilling(); //--- Set standard sounds for trading objects of all used symbols engine.SetSoundsStandart(); //--- Set the general flag of using sounds engine.SetUseSounds(InpUseSounds); //--- Set the spread multiplier for symbol trading objects in the symbol collection engine.SetSpreadMultiplier(InpSpreadMultiplier); //--- Set controlled values for symbols //--- Get the list of all collection symbols CArrayObj *list=engine.GetListAllUsedSymbols(); if(list!=NULL && list.Total()!=0) { //--- In a loop by the list, set the necessary values for tracked symbol properties //--- By default, the LONG_MAX value is set to all properties, which means "Do not track this property" //--- It can be enabled or disabled (by setting the value less than LONG_MAX or vice versa - set the LONG_MAX value) at any time and anywhere in the program /* for(int i=0;i<list.Total();i++) { CSymbol* symbol=list.At(i); if(symbol==NULL) continue; //--- Set control of the symbol price increase by 100 points symbol.SetControlBidInc(100000*symbol.Point()); //--- Set control of the symbol price decrease by 100 points symbol.SetControlBidDec(100000*symbol.Point()); //--- Set control of the symbol spread increase by 40 points symbol.SetControlSpreadInc(400); //--- Set control of the symbol spread decrease by 40 points symbol.SetControlSpreadDec(400); //--- Set control of the current spread by the value of 40 points symbol.SetControlSpreadLevel(400); } */ } //--- Set controlled values for the current account CAccount* account=engine.GetAccountCurrent(); if(account!=NULL) { //--- Set control of the profit increase to 10 account.SetControlledValueINC(ACCOUNT_PROP_PROFIT,10.0); //--- Set control of the funds increase to 15 account.SetControlledValueINC(ACCOUNT_PROP_EQUITY,15.0); //--- Set profit control level to 20 account.SetControlledValueLEVEL(ACCOUNT_PROP_PROFIT,20.0); } //--- Get the end of the library initialization time counting and display it in the journal ulong end=GetTickCount(); Print(TextByLanguage("Время инициализации библиотеки: ","Library initialization time: "),TimeMSCtoString(end-begin,TIME_MINUTES|TIME_SECONDS)); } //+------------------------------------------------------------------+
Estas son todas las mejoras del asesor de prueba.
Lo compilamos y lo iniciamos, estableciendo en los parámetros el uso del símbolo y el marco temporal actual.
En el diario podremos ver los mensajes:
--- Initializing "DoEasy" library --- Working with the current symbol only: "EURUSD" Working with the current timeframe only: H4 EURUSD symbol timeseries: Timeseries "EURUSD" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5330 Library initialization time: 00:00:00.141
Establecemos en los ajustes el uso del símbolo actual y la lista de marcos temporales establecida (en la lista se indican los principales marcos temporales).
En el diario podremos ver los mensajes:
--- Initializing "DoEasy" library --- Working with the current symbol only: "EURUSD" Working with the specified timeframe list: "M1" "M5" "M15" "M30" "H1" "H4" "D1" "W1" "MN1" EURUSD symbol timeseries: Timeseries "EURUSD" M1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3286 Timeseries "EURUSD" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3566 Timeseries "EURUSD" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3109 Timeseries "EURUSD" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2894 Timeseries "EURUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5505 Timeseries "EURUSD" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5330 Timeseries "EURUSD" D1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5087 Timeseries "EURUSD" W1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2564 Timeseries "EURUSD" MN1: Requested: 1000, Actual: 590, Created: 590, On the server: 590 Library initialization time: 00:00:00.032
Establecemos en los ajustes el uso del símbolo actual y la lista completa de marcos temporales.
En el diario podremos ver los mensajes:
--- Initializing "DoEasy" library --- Working with the current symbol only: "EURUSD" Working with the full list of timeframes: "M1" "M2" "M3" "M4" "M5" "M6" "M10" "M12" "M15" "M20" "M30" "H1" "H2" "H3" "H4" "H6" "H8" "H12" "D1" "W1" "MN1" EURUSD symbol timeseries: Timeseries "EURUSD" M1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3390 Timeseries "EURUSD" M2: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5626 Timeseries "EURUSD" M3: Requested: 1000, Actual: 1000, Created: 1000, On the server: 4713 Timeseries "EURUSD" M4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 4254 Timeseries "EURUSD" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3587 Timeseries "EURUSD" M6: Requested: 1000, Actual: 1000, Created: 1000, On the server: 4805 Timeseries "EURUSD" M10: Requested: 1000, Actual: 1000, Created: 1000, On the server: 4035 Timeseries "EURUSD" M12: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3842 Timeseries "EURUSD" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3116 Timeseries "EURUSD" M20: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3457 Timeseries "EURUSD" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2898 Timeseries "EURUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5507 Timeseries "EURUSD" H2: Requested: 1000, Actual: 1000, Created: 1000, On the server: 6303 Timeseries "EURUSD" H3: Requested: 1000, Actual: 1000, Created: 1000, On the server: 6263 Timeseries "EURUSD" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5331 Timeseries "EURUSD" H6: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5208 Timeseries "EURUSD" H8: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5463 Timeseries "EURUSD" H12: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5205 Timeseries "EURUSD" D1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5087 Timeseries "EURUSD" W1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2564 Timeseries "EURUSD" MN1: Requested: 1000, Actual: 590, Created: 590, On the server: 590 Library initialization time: 00:00:00.094
Establecemos en los ajustes el uso de la lista de símbolos establecida e indicamos en la lista los tres símbolos EURUSD,AUDUSD,EURAUD, así como el uso de la lista de marcos temporales establecida (en la lista se indican los principales marcos temporales).
En el diario podremos ver los mensajes:
--- Initializing "DoEasy" library --- Working with predefined symbol list. The number of used symbols: 3 "AUDUSD" "EURUSD" "EURAUD" Working with the specified timeframe list: "M1" "M5" "M15" "M30" "H1" "H4" "D1" "W1" "MN1" AUDUSD symbol timeseries: Timeseries "AUDUSD" M1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3394 Timeseries "AUDUSD" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 4024 Timeseries "AUDUSD" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3262 Timeseries "AUDUSD" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3071 Timeseries "AUDUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5104 Timeseries "AUDUSD" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5026 Timeseries "AUDUSD" D1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5289 Timeseries "AUDUSD" W1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 1401 Timeseries "AUDUSD" MN1: Requested: 1000, Actual: 323, Created: 323, On the server: 323 EURAUD symbol timeseries: Timeseries "EURAUD" M1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3393 Timeseries "EURAUD" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 4025 Timeseries "EURAUD" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3262 Timeseries "EURAUD" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3071 Timeseries "EURAUD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5104 Timeseries "EURAUD" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5026 Timeseries "EURAUD" D1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 4071 Timeseries "EURAUD" W1: Requested: 1000, Actual: 820, Created: 820, On the server: 820 Timeseries "EURAUD" MN1: Requested: 1000, Actual: 189, Created: 189, On the server: 189 EURUSD symbol timeseries: Timeseries "EURUSD" M1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3394 Timeseries "EURUSD" M5: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3588 Timeseries "EURUSD" M15: Requested: 1000, Actual: 1000, Created: 1000, On the server: 3116 Timeseries "EURUSD" M30: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2898 Timeseries "EURUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5507 Timeseries "EURUSD" H4: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5331 Timeseries "EURUSD" D1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5087 Timeseries "EURUSD" W1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 2564 Timeseries "EURUSD" MN1: Requested: 1000, Actual: 590, Created: 590, On the server: 590 Library initialization time: 00:00:00.266
Bien, podemos ver que, dependiendo de los símbolos y los marcos temporales establecidos en los ajustes del asesor, podemos crear las series temporales necesarias. La fecha y la hora de creación de las series temporales depende del inicio del asesor (en frío o en caliente), y de si se han utilizado anteriormente los símbolos seleccionados y sus marcos temporales.
¿Qué es lo próximo?
En el próximo artículo, crearemos la funcionalidad para actualizar en tiempo real las series temporales creadas, enviar mensajes al programa sobre los eventos de "Nueva barra" para todas las series temporales utilizadas, y también para obtener los datos necesarios de las series temporales disponibles.
Más abajo se adjuntan todos los archivos de la versión actual de la biblioteca y los archivos del asesor de prueba. 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
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/7663





- 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