![Trabalhando com séries temporais na biblioteca DoEasy (Parte 37): coleção de séries temporais - banco de dados de séries temporais para símbolos e períodos](https://c.mql5.com/i/articles/overlay.png)
Trabalhando com séries temporais na biblioteca DoEasy (Parte 37): coleção de séries temporais - banco de dados de séries temporais para símbolos e períodos
Sumário
- Ideia
- Modificando os objetos das séries temporais criadas anteriormente
- Classe-coleção de objetos-séries temporais com base em símbolos e períodos
- Teste
- O que vem agora?
Ideia
- Desde o início desta série, criamos um objeto-barra contendo dados de uma barra de um determinado símbolo e período gráfico.
- Criamos uma coleção de barras: objeto-série temporal de um determinado símbolo e período gráfico.
- Juntamos todos os objetos-séries temporais num objeto-série temporal de um símbolo.
Hoje criaremos um objeto-coleção de séries temporais (dos símbolos usados no programa) que conterá dados dos períodos gráficos especificados para um símbolo. Como resultado, obteremos um objeto que terá todos os dados para um determinado número de barras para cada série temporal de cada símbolo.
A coleção de séries temporais armazenará todos os dados históricos necessários para cada um dos símbolos usados no programa e para todos os períodos gráficos que também serão definidos nas configurações do programa.
Além disso, a coleção permitirá definir os dados necessários individualmente para cada período gráfico de cada símbolo.
Como o escopo da descrição da funcionalidade criada da coleção de séries temporais irá se tornar grande, atualizaremos em tempo real os dados da coleção e obteremos todos os dados possíveis da coleção no próximo artigo.
Modificando os objetos das séries temporais criadas anteriormente
A maioria dos objetos de biblioteca é herdeira do objeto base de todos os objetos da biblioteca, que, por sua vez, é herdado da classe base para criar o Biblioteca padrão MQL5.
Com as crescentes necessidades da biblioteca, a classe CBaseObj do objeto base da biblioteca cresceu e, agora, ao herdar novos objetos dela, eles receberão, em alguns casos, métodos completamente desnecessários.
Para resolver esse problema, dividiremos a classe do objeto base em dois:
- o primeiro (CBaseObj) conterá o conjunto mínimo necessário de propriedades e métodos para cada objeto da biblioteca,
- o segundo (CBaseObjExt), o descendente CBaseObj, conterá propriedades e métodos para experiência interativa do usuário e para funcionalidade de evento de objetos-herdeiros.
Assim, de CBaseObj herdaremos aqueles objetos que precisam de propriedades e métodos básicos , já de CBaseObjExt herdaremos os objetos que precisam da funcionalidade de evento.
Como proceder...? Primeiro, renomeamos a classe do objeto base CBaseObj, localizada no arquivo \MQL5\Include\DoEasy\Objects\BaseObj.mqh na classe CBaseObjExt e compilamos o arquivo do primeiro objeto da biblioteca CEngine, localizado em \MQL5\Include\DoEasy\Engine.mqh. Isso causará uma grande lista de erros de compilação, uma vez que estaremos renomeando a classe do objeto da biblioteca base.
Basta percorrer a lista de erros que indicam a ausência da classe CBaseObj e substituir nas listagens das classes todas as ocorrências das strings "CBaseObjExt". A recompilação com os nomes de classe corrigidos do objeto base deverá ser bem-sucedida.
Agora na listagem da classe do objeto base adicionamos uma nova classe que chamaremos de CBaseObj, vamos herdá-la do objeto base da biblioteca MQL5, e, em seguida, transferiremos a partir da classe CBaseObjExt as variáveis e métodos que deverão estar na nova classe do objeto base, já quanto à classe CBaseObjExt, vamos herdá-la de CBaseObj.
Parece complicado, mas se olharmos para a listagem de classes, tudo fica esclarecido (não faz sentido realizar uma listagem completa, uma vez que tudo pode ser visto nos arquivos anexados ao artigo):
//+------------------------------------------------------------------+ //| 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(); }; //+------------------------------------------------------------------+
Na classe do novo objeto base contendo todos os objetos de biblioteca, agora são declaradas três novas variáveis-membro da classe:
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
e adicionados os métodos correspondentes para definir e retornar os valores dessas variáveis:
//--- (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; }
O nome do arquivo de som para o objeto-herdeiro permite definir (SetSoundName()) ou obter (GetSoundName()) o nome do arquivo de som do objeto, que pode ser reproduzido se houver alguma condição que controle as propriedades desse objeto.
Além do fato de o nome poder ser atribuído ao objeto, será possível ativar/desativar (SetUseSound()) a permissão para reproduzir este arquivo e obter o sinalizador de permissão para reproduzir o arquivo (IsUseSound()).
O que é um "sinalizador de uso do objeto-herdeiro no programa" e para que é necessário defini-lo e obtê-lo...
Por exemplo, temos objetos de séries temporais do símbolo para os períodos М5, М30, Н1 e D1. Mas, em algum momento, não queremos processar a série temporal M5. Assim, ao definir/remover este sinalizador, podemos gerenciar a necessidade de controle da biblioteca de eventos de, por exemplo, uma nova barra para a série temporal M5.
A presença desse sinalizador no objeto base que contém todos os objetos da biblioteca nos permitirá controlar de forma flexível a necessidade de processar o estado das propriedades de tais objetos. Em outras palavras, se precisarmos controlar e usar o objeto no programa, definimos o sinalizador, enquanto se esse recurso desaparecer, removemos o sinalizador.
Naturalmente, os construtores de classe também foram alterados, pois as variáveis transferidas para a nova classe foram excluídas e na nova classe todas as variáveis foram inicializadas. Adicionalmente, as alterações podem ser vistas nos arquivos anexados ao artigo.
Classes que agora são herdadas do objeto base estendido CBaseObjExt:
- CAccountsCollection no arquivo \MQL5\Include\DoEasy\Collections\AccountsCollection.mqh
- CEventsCollection no arquivo \MQL5\Include\DoEasy\Collections\EventsCollection.mqh
(substituídas as strings "CBaseObj::EventAdd" em "CBaseObjExt::EventAdd")
- CSymbolsCollection no arquivo \MQL5\Include\DoEasy\Collections\SymbolsCollection.mqh
- CAccount no arquivo \MQL5\Include\DoEasy\Objects\Accounts\Account.mqh
(substituídas as strings "CBaseObj::Refresh()" em "CBaseObjExt::Refresh()") - COrder no arquivo \MQL5\Include\DoEasy\Objects\Orders\Order.mqh
- CPendRequest no arquivo \MQL5\Include\DoEasy\Objects\PendRequest\PendRequest.mqh
(substituídas as strings "return CBaseObj::GetMagicID" em "return CBaseObjExt::GetMagicID",
CBaseObj::GetGroupID1" em "return CBaseObjExt::GetGroupID1" e CBaseObj::GetGroupID2" em "return CBaseObjExt::GetGroupID2") - CSymbol no arquivo \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh
(substituídas as strings "CBaseObj::Refresh()" em "CBaseObjExt::Refresh()") - CTradeObj no arquivo \MQL5\Include\DoEasy\Objects\Trade\TradeObj.mqh
(removidos a variável bool m_use_sound e os métodos SetUseSound() e IsUseSound() — agora estão na classe base) - CTrading no arquivo \MQL5\Include\DoEasy\Trading.mqh
(removidos a classe base bool m_use_sound e o método IsUseSounds() — agora estão na classe base)
Antes de modificarmos as classes já criadas de objetos-séries temporais, adicionamos dados necessários ao arquivo Datas.mqh — uma nova substituição de macros indicando o separador na string da lista de símbolos e períodos gráficos usados nos parâmetros de entrada do programa, enumeração de modos de trabalho com períodos gráficos, bem como índices de novas mensagens e textos de mensagens, correspondente aos índices declarados:
//+------------------------------------------------------------------+ //| Datas.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" //+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ #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"}, }; //+---------------------------------------------------------------------+
Escrevemos todos os dados necessários para modificar as classes de séries temporais escritas anteriormente e criar uma coleção de todas as séries temporais.
Modificamos a classe de objeto-série temporal do símbolo para um período gráfico CSeries.
À seção privada da classe adicionamos quatro novas variáveis e um método para definir os valores de data da série 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:
Na variável m_period_description registramos a descrição do período gráfico da série temporal ao criar o objeto da classe no construtor e nos métodos, que definem o período gráfico para o objeto-série temporal. Isso é feito, em vez de sempre acessar a função TimeframeDescription() no arquivo DELib.mqh das funções de serviço da biblioteca: a função procura uma subsequência na descrição da string do período da enumeração ENUM_TIMEFRAMES, o que diminui a velocidade de execução. Por isso, é melhor executar imediatamente funções dispendiosas ao criar objetos de biblioteca, caso esses dados não sejam alterados ou sejam alterados em casos raros, mediante solicitação do programa.
A variável m_firstdate armazenará a primeira data pelo símbolo-período atual, obtido através da função SeriesInfoInteger() com o ID da propriedade SERIES_FIRSTDATE.
A variável m_lastbar_date armazenará o tempo de abertura da última barra pelo símbolo-período, obtido através da função SeriesInfoInteger() com o ID da propriedade SERIES_LASTBAR_DATE.
Ambas as variáveis serão definidas chamando o método SetServerDate() apenas no momento de criação do objeto da classe ou alteração de dados numa nova barra, bem como ao definir um novo símbolo ou período gráfico para um objeto de série temporal.
A variável m_required armazenará a quantidade necessária (última solicitação) de dados de série temporal usados. Ao solicitar o número necessário de barras de série temporal, pode acontecer que a quantidade solicitada de dados para a criação da série temporal simplesmente não esteja no servidor. Neste caso, o número de dados da série temporal é produzida em proporção igual ao histórico disponível no servidor. E nessa variável, sempre será armazenada a última quantidade solicitada de dados, independentemente da quantidade de dados realmente obtida e criada. Além disso, como usamos o conceito "dados solicitados", o nome dos métodos, em que aparecia "Amount" (quantidade), foi mudado para "Required" (solicitado).
Na seção pública da classe também foram adicionados novos 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); }; //+------------------------------------------------------------------+
O método GetObject() retorna ao programa de controle um ponteiro para todo o objeto-série temporal. Permite obter todo o objeto-série temporal e trabalhar com ele em seu programa.
O método RequiredUsedData() retorna para o programa de chamada o valor da variável m_required, discutido acima.
Os métodos FirstDate() e LastBarDate() retornam valores das respectivas variáveis m_firstdate e m_lastbar_date, discutidas acima.
O método SetNewBarSoundName() define o no me do arquivo de som para o objeto CNewBarObj "Nova Barra", que faz parte do objeto-série temporal.
O método NewBarSoundName() retorna o nome do arquivo de som, atribuído ao objeto CNewBarObj "Nova Barra", que faz parte do objeto-série temporal.
Os métodos permitem atribuir som ao objeto-série temporal, que será tocado quando acontecer o evento "Nova Barra".
O método Header() cria e retorna o nome abreviado do objeto-série 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 o método é retornada uma descrição de string da série temporal na forma
Timeseries "SYMBOL" TIMEFRAME_DESCRIPTION
Por exemplo:
Timeseries "AUDUSD" M15
O método Print() exibe no log uma descrição completa da série 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); } //+------------------------------------------------------------------+
Exibe no log dados da série temporal na forma
HEADER: HISTORY_DEPTH: XXXX, ACTUAL_DEPTH: XXXX, AMOUNT_HISTORY_DATA: XXXX, HISTORY_BARS: XXXX
Por exemplo:
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
O método PrintShort() exibe no log uma breve descrição da série 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); } //+------------------------------------------------------------------+
Exibe no log dados da série temporal na forma
HEADER: REQUIRED: XXXX, ACTUAL: XXXX, CREATED: XXXX, HISTORY_BARS: XXXX
Por exemplo:
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
Nos dois construtores de classe, adicionamos o armazenamento da descrição do período gráfico e a definição de datas de série 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(); } //+------------------------------------------------------------------+
No método para definir o símbolo, adicionamos a verificação de símbolo e a definição de data de série 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(); } //+------------------------------------------------------------------+
Neste caso, se ao método for transferido um objeto já usado no símbolo, não será necessário definir nada, portanto, saímos do método.
No método para definir o período gráfico, adicionamos a verificação de período gráfico e a definição de datas de série 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(); } //+------------------------------------------------------------------+
Neste caso, se ao método for transferido um período gráfico já usado no objeto, não será necessário definir nada, portanto, saímos do método.
Alteramos o método para definir o símbolo e o período gráfico:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Neste caso, se ao método forem transferidos os mesmos símbolo e período gráfico, não será necessário alterar nada, portanto, saímos do método.
Em seguida, chamamos os métodos para definir o símbolo e o período gráfico.
No método para definir a profundidade necessária de histórico de série temporal, armazenamos o valor solicitado de profundidade de histórico:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Se for transferido o valor nulo required, solicitaremos o histórico na quantidade por padrão (1000 barras), quantidade essa definida pela substituição de macros SERIES_DEFAULT_BARS_COUNT no arquivo Define.mqh, caso contrário, o valor passado desde required.
No método de atualização de série temporal, adicionamos uma verificação que confirma para o uso dessa série temporal no programa
se a série temporal não for usada, não será necessário atualizar 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]); } //+------------------------------------------------------------------+
Adicionalmente, se na série temporal aparece uma nova barra, atualizaremos as datas de série temporal.
Estas são as principais mudanças nesta classe. Não consideramos pequenas alterações nos nomes dos métodos, já que nos arquivos anexados no final do artigo pode ser encontrada uma listagem completa da classe.
A classe CSeries está concluída.
Modificamos a classe CTimeSeries, que contém os objetos CSeries para todos os possíveis períodos gráficos do símbolo em questão.
Para obter o índice do período gráfico na lista, que armazena a série temporal correspondente e o período gráfico para o índice da lista, na listagem da classe temos os métodos IndexTimeframe() e TimeframeByIndex(). Os métodos são bastante específicos, pois são baseados no fato de que o índice do período gráfico mais pequeno possível (PERIOD_M1) está contido no índice zero da lista. Mas, na enumeração ENUM_TIMEFRAMES, o índice do período M1 já é igual a um, pois o índice zero contém a constante PERIOD_CURRENT. Ou seja, todos os índices são deslocados 1 em relação a zero.
Depois de pensar um pouco, achei que poderiam ser úteis funções que retornassem o índice da constante do período gráfico que faz parte da enumeração ENUM_TIMEFRAMES e vice-versa, isto é, que segundo o período gráfico retornassem sua constante desde a enumeração. Por isso, criaremos funções, que realizam tais tarefas, no arquivo das funções de serviço IndexEnumTimeframe() e TimeframeByEnumIndex(), enquanto na listagem da classe CTimeSeries removeremos a implementação dos métodos IndexTimeframe() e TimeframeByIndex(), após escrever no corpo da classe a implementação, isto é, a chamada das funções IndexEnumTimeframe() e TimeframeByEnumIndex() com deslocamento de 1.
Assim, no arquivo de funções de serviço DELib.mqh, escreveremos três funções:
//+------------------------------------------------------------------+ //| 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 ); } //+------------------------------------------------------------------+
No arquivo de classe CTimeSeries, da listagem da classe removemos a implementação dos métodos IndexTimeframe() e 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;
}
}
//+------------------------------------------------------------------+
Em vez desses métodos agora chamaremos as funções desde o arquivo de função de serviço 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)); }
A lista de séries temporais da classe contém todos os possíveis períodos gráficos, o índice zero da lista tem a série temporal do período gráfico М1 e na enumeração ENUM_TIMEFRAMES o índice zero encerra PERIOD_CURRENT, por esses motivos, para obter o índice correto, na lista precisamos deslocar 1 o valor do índice, que é o que estamos fazendo aqui.
À seção privada da classe foram adicionadas duas variáveis-membros da classe para definição da primeira data no histórico no servidor e no terminal, bem como um método que define os valores dessas datas para essas variáveis:
//+------------------------------------------------------------------+ //| 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:
Na variável m_server_firstdate será armazenada a primeira data do histórico de acordo com o símbolo no servidor, data essa obtida através da função SeriesInfoInteger() com o ID da propriedade SERIES_SERVER_FIRSTDATE.
Na variável m_terminal_firstdate será armazenada a primeira data da histórico segundo o símbolo no terminal, data essa obtida através da função SeriesInfoInteger() com o ID da propriedade SERIES_TERMINAL_FIRSTDATE.
Na seção pública da classe foram adicionados seis novos métodos e um construtor 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); }; //+------------------------------------------------------------------+
O método GetObject() retorna o ponteiro para o objeto da classe. Permite obter o objeto da classe das séries temporais do símbolo e trabalhar com ele em seu programa.
Os métodos ServerFirstDate() e TerminalFirstDate() retornam os valores das variáveis m_server_firstdate e m_terminal_firstdate respectivamente, consideradas acima.
O método virtual Compare() permite comparar dois objetos-séries temporais pelo nome do símbolo da série 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); } //+------------------------------------------------------------------+
O método retorna zero se os símbolos de dois objetos-séries temporais comparados forem iguais. Caso contrário, retorna +/- 1. O método declarado na classe CObject da biblioteca padrão deve ser redefinido nos seus herdeiros.
O método Print() exibe no log descrições completas de todas as séries temporais do 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(); } } //+------------------------------------------------------------------+
No log é exibida uma lista contendo todas as séries temporais criadas (created=true) ou criadas e declaradas (created=false) do símbolo no formato, por exemplo
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
O método PrintShort() exibe no log descrições abreviadas de todas as séries temporais do 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(); } } //+------------------------------------------------------------------+
No log é exibida uma lista contendo todas as séries temporais criadas (created=true) ou criadas e declaradas (created=false) do símbolo no formato, por exemplo
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
No construtor da classe foi adicionada a definição de data da série 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(); } //+------------------------------------------------------------------+
No método de atualização da série temporal especificada, foi adicionada a definição de datas de série temporal ao detectar o evento "Nova Barra" da série temporal atualizada:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
The method of updating all timeseries also features updating timeseries dates:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Porém, neste caso, devemos atualizar as datas apenas uma vez quando acontecer o evento "Nova Barra" em qualquer uma das séries temporais atualizadas presentes na lista (afinal, existem 21 séries temporais e não há necessidade de definir as mesmas datas 21 vezes). Por isso, registramos no sinalizador a necessidade de atualizar o valor da data true uando acontecer o evento "Nova Barra" num ciclo percorrendo todas as séries temporais, já no final do ciclo e no sinalizador definido, atualizamos as datas.
Assim concluímos a modificação da classe CTimeSeries. Não descreveremos pequenas correções, pois tudo pode ser visualizado nos arquivos anexados no final do artigo.
No momento, temos três classes que contêm todas as informações necessárias para criar uma coleção de séries temporais:
- A "barra de um período de um símbolo" CBar inclui dados de uma barra de um símbolo definido de um período definido;
- As "séries temporais de um período de um símbolo" CSeries inclui uma lista-coleção de barras (1) de um período de um símbolo;
- As "séries temporais de todos os períodos de um símbolo" CTimeSeries inclui uma lista de séries temporais (2) para cada período de um símbolo
Agora criamos uma coleção de séries temporais, que é uma lista-coleção de séries temporais (3) de cada símbolo usado no programa.
Classe-coleção de objetos-séries temporais com base em símbolos e períodos
A coleção de séries temporais será uma matriz dinâmica de ponteiros para as instâncias da classe CObject e seus herdeiros (ponteiros para objetos de classe CTimeSeries).
Na pasta da biblioteca \MQL5\Include\DoEasy\Collections\ criamos o novo arquivo TimeSeriesCollection.mqh da classe CTimeSeriesCollection.
O objeto base da classe será um objeto base para criar uma biblioteca CObject padrão.
Vamos dar uma olhada na listagem da classe e analisar gradualmente sua estrutura:
//+------------------------------------------------------------------+ //| TimeSeriesCollection.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #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(); }; //+------------------------------------------------------------------+
Na verdade, no momento, a classe é uma lista de objetos-séries temporais e métodos para criar/definir parâmetros e atualizar as séries temporais necessárias de acordo com o símbolo e o período:
O matriz de ponteiros para objetos da classe CObject m_list conterá ponteiros para objetos da classe CTimeSeries. A partir desta lista, receberemos os dados das séries temporais necessárias e trabalharemos com eles.
O método IndexTimeSeries(), que retorna o índice da série temporal de acordo com o nome do símbolo, permite acessar ao objeto necessário CTimeSeries com base no nome do 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; } //+------------------------------------------------------------------+
Crie um novo objeto-série temporal temporário com o valor do símbolo passado para o método,
para a lista m_list definimos o sinalizador de lista classificada e
com ajuda do método Searsh() obtemos o índice de tal objeto na lista.
Devemos excluir o objeto-série temporal e
retornamos o índice recebido.
Se esse objeto com o símbolo especificado não estiver na lista, o método retornará -1, caso contrário, o valor do índice do objeto encontrado.
O método GetObject() retorna um ponteiro para um objeto-coleção de séries temporais para o programa de controle. Permite obter todo o objeto-coleção inteiro e trabalhar com ele em seu programa.
O método GetList() retorna um ponteiro para a lista-coleção de séries temporais CTimeSeries. Permite obter uma lista contendo todos os períodos gráficos de todos os símbolos e trabalhar com ela em seu programa.
O método CreateCollection() cria uma coleção vazia de objetos-séries temporais:
//+------------------------------------------------------------------+ //| 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 linha do método é comentada em sua listagem.
Ao método é transferida a lista criada anteriormente contendo todos os símbolos usados no programa, e num ciclo percorrendo esta lista são criados os objetos-séries temporais CTimeSeries com especificação do nome do símbolo. Assim, obtemos uma lista-coleção de séries temporais de acordo com o número de símbolos usados no programa. Todos os outros dados dos objetos-séries temporais criadas permanecem vazios, uma vez que eles precisam ser definidos separadamente.
Isso é feito para que a biblioteca sempre tenha uma lista-coleção de séries temporais vazias numa quantidade igual ao número de símbolos especificado para trabalhar. Mas, os períodos gráficos e, consequentemente, seus objetos-séries temporais, com os quais o programa precisará trabalhar, são definidos na próxima etapa ou conforme necessário.
No entanto, é melhor criá-los no manipulador OnInit() do programa ou imediatamente após sua inicialização, pois a criação de muitas séries temporais consome muito tempo.
Os quatro métodos sobrecarregados SetAvailable() servem para definir o sinalizador que indica a necessidade de trabalhar no programa com as séries temporais especificadas.
Método para definir o sinalizador que indica o uso de uma série temporal para o símbolo especificado:
//+-----------------------------------------------------------------------+ //|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); } //+------------------------------------------------------------------+
Ao método são transferidos o símbolo, o período gráfico e o sinalizador que deve ser definido para a série temporal que corresponde ao símbolo-período gráfico.
Primeiro, obtemos o índice de séries temporais CTimeSeries na lista m_list de acordo com o símbolo com ajuda do método IndexTimeSeries(), examinado acima, obtemos segundo este índice a série temporal a partir da lista. Desde o objeto-série temporal recebido, obtemos as séries temporais necessárias Cseries do período gráfico especificado usando o método GetSeries() que analisamos no último artigo, e definimos o sinalizador passado para o método por meio do método SetAvailable() da classe CBaseObj.
Método para definir o sinalizador que indica o uso de uma série temporal para todos os 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); } } //+------------------------------------------------------------------+
Ao método são transferidos o período gráfico e o sinalizador que deve ser definido para a série temporal especificada de todos os símbolos.
Num ciclo percorrendo a lista de todas as séries temporais dos símbolos obtemos a série temporal CTimeSeries em questão, desde o objeto-série temporal recebido obtemos as séries temporais necessárias Cseries do período gráfico especificado usando o método GetSeries() que analisamos no último artigo, e definimos o sinalizador passado para o método por meio do método SetAvailable() da classe CBaseObj.
Método para definir o sinalizador que indica o uso de uma série temporal para o símbolo especificado:
//+------------------------------------------------------------------+ //|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); } } //+------------------------------------------------------------------+
Ao método são transferidos o símbolo e o sinalizador que deve ser definido para todas as séries temporais do símbolo especificado.
Primeiro, obtemos o índice de séries temporais CTimeSeries na lista m_list de acordo com o símbolo com ajuda do método IndexTimeSeries(), examinado acima, obtemos segundo este índice a série temporal CTimeSeries a partir da lista. Desde o objeto-série temporal recebido usando o método GetListSeries(), obtemos uma lista completa de todas as séries temporais CSeries. Num ciclo percorrendo a lista recebida obtemos a próxima série temporal CSeries e definimos para ela o sinalizador transferido ao método por meio do método SetAvailable() da classe CBaseObj.
Método para definir o sinalizador que indica o uso de todas as séries temporais para todos os 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); } } } //+--------------------------------------------------------------------+
Ao método é transferido o sinalizador que deve ser definido para as séries temporais de todos os símbolos.
Num ciclo percorrendo a lista de séries temporais obtemos o próximo objeto-série temporal CTimeSeries de acordo com o índice de ciclo, desde o objeto recebido pelo método GetListSeries obtemos a lista de todas suas séries temporais Cseries, num ciclo percorrendo a lista de séries temporais CSeries obtemos a próxima série temporal de acordo com o índice de ciclo e definimos para ela o sinalizador transferido ao método por meio do método SetAvailable() da classe CBaseObj.
Quatro métodos que retornam o sinalizador para usar as séries temporais especificadas ou todas:
//+-------------------------------------------------------------------------+ //|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; } //+--------------------------------------------------------------------+
Os métodos funcionam quase de forma idêntica aos métodos descritos para definir o sinalizador de séries temporais. A diferença é o uso da variável local res que tem status inicial true, para receber um sinalizador "coletivo" indicando o uso de um conjunto de séries temporais, nos métodos que o retornam. No ciclo percorrendo as séries temporais CSeries na variável será registrado o status do sinalizador de cada série temporal verificada, e se pelo menos uma das séries temporais tiver um valor false, na variável será registrado o sinalizador false. O valor dessa variável é retornado desde o método ao finalizarem todos os ciclos em todas as séries temporais.
Quatro métodos para definir a profundidade do histórico necessária para as séries temporais especificadas ou imediatamente para todas elas:
//+--------------------------------------------------------------------------+ //|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; } //+------------------------------------------------------------------+
Os métodos funcionam de forma idêntica aos métodos que retornam os sinalizadores de uso de séries temporais. Retornam o resultado ao definir para os objetos-séries temporais Cseries a quantidade de dados solicitada com ajuda dos métodos SetRequiredUsedData(), que retornam um valor booleano. Por isso, aqui também é usado um sinalizador comum ao definir a profundidade do histórico para muitas séries temporais CSeries.
Quatro métodos que retornam o sinalizador de sincronização, a série temporal especificada ou todas:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
O trabalho dos métodos é idêntico ao considerado acima. É retornado resultado da verificação da sincronização das séries temporais pelo método SyncData() da classe CSeries.
Quatro métodos para criar as séries temporais especificadas ou todas.
Método para criar uma série temporal para o símbolo especificado:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Ao método é transferido o símbolo para o qual deve ser criada uma série temporal, cujo período também é passado para o método.
Obtemos o índice de série temporal na lista de acordo com o nome do símbolo através do método IndexTimeSeries(), segundo o índice obtido obtemos a série temporal CTimeSeries a partir da lista e retornamos o resultado da criação da série temporal especificada por meio do método Create() da classe CTimeSeries.
Método para criar uma série temporal para todos os 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; } //+------------------------------------------------------------------+
Ao método é transferido o período gráfico, cuja série temporal deve ser criada para todos os símbolos.
Num ciclo percorrendo todos os objetos das séries temporais de todos os símbolos obtemos o seguinte objeto-série temporal CTimeSeries de acordo com o índice do ciclo, à variável res adicionamos o resultado da criação da série temporal especificada através do método Create() da classe CTimeSeries. No final do ciclo, retornamos o resultado da criação da série temporal especificada para todos os símbolos. Se pelo menos uma série temporal não tiver sido criada, o resultado será false.
Método para criar todas as séries temporais para o símbolo especificado:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Ao método é transferido o símbolo, para o qual devem ser criadas todas as séries temporais.
Obtemos o índice de série temporal na lista de acordo com o nome do símbolo através do método IndexTimeSeries(), segundo o índice obtido obtemos a série temporal CTimeSeries a partir da lista e retornamos o resultado da criação de todas as séries temporais especificadas por meio do método CreateAll() da classe CTimeSeries.
Método para criar todas as séries temporais para todos os 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; } //+------------------------------------------------------------------+
Ao método é transferida apenas a profundidade do histórico a ser criada (como em todos os métodos acima). Por padrão, é transferido zero, o que significa uma profundidade de histórico de 1000 barras, que é especificada pela substituição de macro SERIES_DEFAULT_BARS_COUNT no arquivo Defines.mqh.
Num ciclo percorrendo a lista das séries temporais obtemos o seguinte objeto-série temporal CTimeSeries de acordo com o ciclo, e à variável res adicionamos o resultado da criação de todas as séries temporais para o símbolo do objeto atual CTimeSeries através do método CreateAll(), que retorna o sinalizador para criar todas as séries temporais do símbolo do objeto CTimeSeries.
No final do ciclo, retornamos o resultado da criação de todas as séries temporais para todos os símbolos. Se pelo menos uma série temporal não tiver sido criada, o resultado será false.
Quatro métodos para atualizar as séries temporais especificadas do símbolo especificado ou todas as séries temporais:
//+------------------------------------------------------------------+ //| 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); } } //+------------------------------------------------------------------+
Aqui, em todos os métodos, de uma maneira ou de outra (todos os métodos foram considerados nos métodos anteriores), obtemos o objeto-série temporal necessário CTimeSeries e chamamos os métodos de atualização da série temporal especificada Refresh() ou de todas as séries temporais RefreshAll() da classe CTimeSeries.
Para todos os métodos são transferidos os dados atuais a partir das matrizes de séries temporais. Isso é necessário para trabalhar com indicadores no período atual do símbolo atual. Em outros casos, os valores transferidos não são importantes, portanto, por padrão, eles são definidos como 0.
O método que registra no log uma descrição completa da coleção:
//+------------------------------------------------------------------+ //| 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); } } //+------------------------------------------------------------------+
Ao método é transferido o sinalizador que indica a necessidade de exibir no log apenas séries temporais criadas.
Num ciclo percorrendo a lista de objetos-séries temporais obtemos o seguinte objeto-série temporal CTimeSeries e chamamos o método de mesmo nome, cujo resultado foi considerado acima. Como resultado, serão registrados no log os dados de todas as séries temporais disponíveis de todos os símbolos da coleção.
O método que registra no log uma breve descrição da coleção:
//+------------------------------------------------------------------+ //| 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); } } //+------------------------------------------------------------------+
Ao método é transferido o sinalizador que indica a necessidade de exibir no log apenas séries temporais criadas.
Num ciclo percorrendo a lista de objetos-séries temporais obtemos o seguinte objeto-série temporal CTimeSeries e chamamos o método de mesmo nome, cujo resultado foi considerado acima. Como resultado, serão de forma abreviada registrados no log os dados de todas as séries temporais disponíveis de todos os símbolos da coleção.
A versão atual da classe-coleção de séries temporais está pronta. A listagem completa da classe pode ser vista e estudada nos arquivos anexados no final do artigo.
Agora, precisamos realizar o acesso externo à coleção de séries temporais criada e a definição conveniente dos parâmetros das séries temporais criadas.
Qualquer programa acessa métodos de biblioteca do objeto principal da biblioteca CEngine.
No arquivo de classe CEngine, escrevemos o acesso ao trabalho com a coleção de séries temporais.
Abrimos o arquivo Engine.mqh desde o catálogo da biblioteca \MQL5\Include\DoEasy\ e fazemos as alterações necessárias.
Como na versão anterior, à classe CEngine foi anexado o arquivo de classe CTimeSeries para verificar sua operação, então o removemos da lista de arquivos anexados
//+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #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 anexamos o arquivo de classe-coleção de séries temporais:
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #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" //+------------------------------------------------------------------+
Na seção privada da classe declaramos uma variável com o tipo de classe de coleção de séries temporais:
//+------------------------------------------------------------------+ //| 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
Na seção pública da classe alteramos a implementação do método para definir a lista de símbolos usados:
//--- Set the list of (1) used symbols bool SetUsedSymbols(const string &array_symbols[]) { return this.m_symbols.SetUsedSymbols(array_symbols);}
Esta versão do método chama o método (que tem o mesmo nome) a fim de definir a lista de símbolos para a classe-coleção de símbolos. E é exatamente neste mesmo local que, logo após definir a lista de símbolos na coleção de símbolos, será conveniente criar uma coleção de séries temporais com base na lista criada da coleção de símbolos.
Aqui deixamos apenas a declaração do 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[]);
e fora do corpo da classe, escrevemos sua implementação:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Neste caso, declaramos a variável res, e a inicializamos por meio do resultado da operação do método que serve para definir a lista de símbolos na coleção de símbolos.
Em seguida, obtemos a lista de todos os símbolos usados a partir da classe da coleção de símbolos, se a lista não for criada, retornaremos false,
caso contrário, à variável res adicionamos o resultado da criação da lista de coleção de séries temporais com base na lista de coleção de símbolos.
O resultado final é retornado desde o método.
À seção pública da classe adicionamos os métodos para trabalhar com a coleção de séries temporais:
//--- 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); }
O método GetTimeSeriesCollection() retorna ao programa de chamada um ponteiro para o objeto-coleção de séries temporais. Permite obter no programa um ponteiro para a coleção e trabalhar totalmente com ela.
O método GetListTimeSeries() retorna ao programa de chamada um ponteiro para a lista de séries temporais da coleção. Permite obter no programa um ponteiro para uma lista de objetos-séries temporais CTimeSeries e trabalhar totalmente com ele.
Os método sobrecarregados SeriesSetAvailable() concedem acesso aos métodos SetAvailable() da classe de coleção de séries temporais CTimeSeriesCollection(), que examinamos acima.
Os método sobrecarregados SeriesSetRequiredUsedData() concedem acesso aos métodos SetRequiredUsedData() da classe de coleção de séries temporais CTimeSeriesCollection(), que examinamos acima.
Os método sobrecarregados SeriesSyncData() concedem acesso aos métodos SyncData() da classe de coleção de séries temporais CTimeSeriesCollection(), que examinamos acima.
Os método sobrecarregados SeriesCreate() concedem acesso aos métodos CreateSeries() da classe de coleção de séries temporais CTimeSeriesCollection(), que examinamos acima.
Os método sobrecarregados SeriesRefresh() concedem acesso aos métodos Refresh() da classe de coleção de séries temporais CTimeSeriesCollection(), que examinamos acima.
No momento, estas são todas as tarefas necessárias para modificar todas as classes para testar uma nova classe-coleção de séries temporais.
Teste
Para testar a criação e o preenchimento da coleção de séries temporais, tomamos o EA do artigo anterior
e o armazenamos na nova pasta \MQL5\Experts\TestDoEasy\Part37\ usando o novo nome TestDoEasyPart37.mq5.
Para selecionar o modo de operação do programa com símbolos, temos uma enumeração no arquivo 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 }; //+------------------------------------------------------------------+
E nos parâmetros de entrada do EA, temos uma variável que permite selecionar os símbolos para trabalhar com eles:
sinput ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; // Mode of used symbols list
Com base no valor dessa variável, é criada uma matriz de símbolos, que é transferida à biblioteca ao ser inicializada na função do EA OnInitDoEasy() e, em seguida, já na biblioteca é criada a lista de símbolos de trabalho.
Precisamos realizar as mesmas operações para selecionar e criar uma lista de períodos gráficos de trabalho do EA.
Criamos uma nova enumeração no arquivo Datas.mqh para selecionar modos de trabalho com os períodos gráficos dos símbolos:
//+------------------------------------------------------------------+ //| Datas.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" //+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ #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 }; //+------------------------------------------------------------------+
No arquivo de funções de serviço, temos uma função para preparar uma matriz de símbolos usados para a 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; } //+------------------------------------------------------------------+
Precisamos criar uma função semelhante para preparar uma matriz de períodos gráficos a serem usados. E então fica claro que em duas funções semelhantes teremos o mesmo bloco de código (marcado em cores na listagem).
Inserimos este bloco de código numa função separada:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
E então a função para criar uma matriz de símbolos usados terá o seguinte formato:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
O bloco de código transferido para uma função separada agora é substituído por uma chamada para a função para preparar a string de parâmetros.
E agora, da mesma maneira, criaremos uma função para preparar uma matriz de períodos gráficos usados no 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; } //+------------------------------------------------------------------+
Aqui exatamente da mesma maneira é chamada a função para preparar uma string de parâmetros, uma vez que estes códigos nas funções CreateUsedSymbolsArray() e CreateUsedTimeframesArray() são idênticas.
No mesmo local, isto é, no arquivo DELib.mqh temos uma função que exibe o tempo em milissegundos:
//+------------------------------------------------------------------+ //| Return time with milliseconds | //+------------------------------------------------------------------+ string TimeMSCtoString(const long time_msc) { return TimeToString(time_msc/1000,TIME_DATE|TIME_MINUTES|TIME_SECONDS)+"."+IntegerToString(time_msc%1000,3,'0'); } //+------------------------------------------------------------------+
A função sempre exibe a hora no formato AAAA.MM.DD HH:MM:SS.MSC
Escolhemos o formato de exibição do tempo:
//+------------------------------------------------------------------+ //| Return time with milliseconds | //+------------------------------------------------------------------+ 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'); } //+------------------------------------------------------------------+
Agora será possível definir o formato de saída do tempo + milissegundos.
Ao bloco de dados de parâmetros de entrada contido no arquivo do EA adicionamos a seleção de períodos gráficos usados:
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 selecionar o modo de uso de período gráfico
- Trabalhando apenas com o período gráfico atual
- Trabalhando com a lista de períodos gráficos definida
- Trabalhado com a lista completa de períodos gráficos
Ao selecionar o segundo item do programa, será usada uma lista de períodos gráficos descritos na string, definida pelo parâmetro de entrada da variável InpUsedTFs.
Do bloco de variáveis globais do EA removemos uma variável desnecessária, que declara o objeto da classe CTimeSeries, pois agora o acesso às séries temporais é realizado através do uso do objeto de biblioteca engine.
//--- global variables CEngine engine; CTimeSeries timeseries; SDataButt butt_data[TOTAL_BUTT];
E a este mesmo bloco de variáveis globais adicionamos uma nova matriz que armazenará os nomes dos períodos gráficos usados pelo 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; //+------------------------------------------------------------------+
Do manipulador OnInit() do EA excluímos o código de criação das duas séries temporais restantes dos testes anteriores:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Já do manipulador OnTick() do EA removemos o código de atualização das séries temporais criadas (faremos a atualização de séries temporais no próximo artigo):
//+------------------------------------------------------------------+ //| 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); } } } //+------------------------------------------------------------------+
À função de inicialização da biblioteca OnInitDoEasy() adicionamos a criação/exibição da lista de períodos gráficos usados no EA e a exibição do tempo de inicialização da biblioteca no log.
Vou fazer uma listagem completa cujas alterações estão marcadas em cores e cujas strings estão comentadas para entender o que estamos fazendo:
//+------------------------------------------------------------------+ //| 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 são todas as modificações do EA de teste.
Vamos compilá-lo e iniciá-lo, após definir nos parâmetros o uso do símbolo atual e do período gráfico atual.
No log serão exibidas as mensagens:
--- 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
Nas configurações, definimos o uso do símbolo atual e a lista de períodos gráficos especificada (na lista são indicados os principais períodos gráficos).
No log serão exibidas as mensagens:
--- 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
Nas configurações, definimos o uso do símbolo atual e da lista completa de períodos gráficos.
No log serão exibidas as mensagens:
--- 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
Nas configurações, definimos o uso da lista especificada de símbolos, e na lista especificamos os três símbolos EURUSD, AUDUSD, EURAUD, nem como a lista definida de períodos gráficos (na lista são indicados os principais períodos gráficos).
No log serão exibidas as mensagens:
--- 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
Portanto, observamos que, dependendo dos símbolos e períodos gráficos especificados nas configurações do EA, são criadas as séries temporais necessárias. O tempo de criação de séries temporais depende da inicialização do EA e do uso prévio de símbolos e períodos gráficos.
O que vem agora?
No próximo artigo, criaremos funcionalidades para atualizar em tempo real as séries temporais criadas, enviar para o programa mensagens sobre eventos "Nova Barra" para todas as séries temporais usadas e para obter os dados necessários das séries temporais existentes.
Abaixo estão anexados todos os arquivos da versão atual da biblioteca e os arquivos do EA de teste. Você pode baixá-los e testar tudo sozinho.
Se você tiver perguntas, comentários e sugestões, poderá expressá-los nos comentários do artigo.
Artigos desta série:
Trabalhando com séries temporais na biblioteca DoEasy (Parte 35): Objeto "Barra" e lista-série temporal do símbolo
Trabalhando com séries temporais na biblioteca DoEasy (Parte 36): objeto das séries temporais de todos os períodos usados do símbolo
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/7663
![Otimização Walk Forward Contínua (parte 5): Panorama do Projeto Otimizador Automático e Criação da Interface Gráfica](https://c.mql5.com/2/38/MQL5-avatar-continuous_optimization__1.png)
![Implementado OLAP na negociação (Parte 4): análise quantitativa e visual dos relatórios do testador](https://c.mql5.com/2/38/OLAP_in_trading.png)
![Trabalhando com séries temporais na biblioteca DoEasy (Parte 38): coleção de séries temporais - atualização em tempo real e acesso aos dados do programa](https://c.mql5.com/2/38/MQL5-avatar-doeasy-library__3.png)
![Trabalhando com séries temporais na biblioteca DoEasy (Parte 36): objeto das séries temporais de todos os períodos usados do símbolo](https://c.mql5.com/2/38/MQL5-avatar-doeasy-library__1.png)
![MQL5 - Linguagem para estratégias de negociação inseridas no terminal do cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso