Trabalhando com séries temporais na biblioteca DoEasy (Parte 35): Objeto "Barra" e lista-série temporal do símbolo
Sumário
- Ideia
- Objeto "Barra"
- Objeto "Nova barra"
- Lista de objetos-barras, pesquisa e classificação
- Teste
- O que vem agora?
Com este artigo, começamos uma nova seção para descrever a criação de uma biblioteca que facilita escrever programas para os terminais MetaTrader5 e 4. A primeira série de artigos (34 partes) foi dedicada ao desenvolvimento do conceito de objetos de biblioteca e às suas interconexões. Além disso, com base nesse conceito, foi criada uma funcionalidade para trabalhar com o estado e histórico atuais da conta.
Atualmente, a biblioteca possui a seguinte funcionalidade:
- sabe pesquisar, classificar e comparar dados de qualquer ordem ou posição,
- fornece acesso rápido e conveniente a quaisquer propriedades de ordens e posições,
- controla todos os eventos que ocorrem na conta,
- permite receber e comparar dados da conta e símbolos,
- reage a alterações nas propriedades de todos os objetos disponíveis em seus bancos de dados (coleções) e envia notificações ao programa sobre os eventos registrados por ele.
Também podemos indicar à biblioteca a quais eventos deve responder e sobre quais enviar notificações.
Além disso, é possível trabalhar com as funções de negociação dos terminais, desenvolver novos tipos de solicitações de negociação, isto é, solicitações de negociação pendentes que nos permitirão no futuro, diretamente desde nosso programa, criar sua lógica de comportamento em determinadas condições e também fornecer muitos recursos para a criação de novos tipos de ordens pendentes.
Criamos e descrevemos tudo isso e muito mais na série anterior que descreve a criação da biblioteca.
Na segunda série, consideraremos muitos aspectos do trabalho com dados de preços, gráficos de símbolos, livro de ofertas, indicadores etc. Em geral, será dedicada uma nova série de artigos ao desenvolvimento da funcionalidade da biblioteca para acessar rápido, pesquisar, comparar e classificar quaisquer matrizes de dados de preços, bem como objetos de armazenamento e fontes.
Ideia
Nos primeiros artigos da última série descrevemos em detalhes o conceito, método de armazenamento e funcionamento da estrutura de objetos da biblioteca. Hoje, repetiremos brevemente os princípios básicos da estrutura e armazenamento de objetos da biblioteca.
Qualquer conjunto de dados do mesmo tipo pode ser representado como uma lista de objetos dotados do mesmo tipo de propriedades. Quer dizer, cada objeto da lista de objetos do mesmo tipo tem o mesmo conjunto de propriedades, mas cada objeto da mesma lista classificada possui valores diferentes.
Cada lista que armazena um conjunto de objetos do mesmo tipo pode ser classificada por qualquer uma das propriedades alocadas aos objetos da lista em questão.As propriedades do objeto são de três tipos: inteiras, reais e de string.
O armazenamento desses objetos em listas classificadas permite encontrar rapidamente qualquer um dos objetos, receber dados deles e os usar em programas.Quase todos os objetos de biblioteca podem rastrear independentemente suas próprias propriedades — alterar seus valores para cada objeto. Quando a propriedade do objeto é alterada, o objeto monitorado envia uma mensagem ao programa de controle sobre o fato de seu valor limite definido para rastreamento ter sido excedido.
A base de todos os objetos da biblioteca é o objeto base da biblioteca padrão que vem com a plataforma de negociação, enquanto a matriz dinâmica de ponteiros para instâncias da classe CObject e seus descendentes da Biblioteca padrão é a lista de objetos (coleção de objetos).
Hoje, criaremos o objeto "Barra", que armazenará todas as informações inerentes à barra no gráfico do símbolo, bem como uma lista contendo todos os objetos-barras para um símbolo e período gráfico.
Cada barra do gráfico do símbolo possui um conjunto específico de parâmetros, que são descritos na estrutura Mqlrates:
- hora de início do período;
- preço de abertura;
- preço mais alto durante o período;
- menor preço durante o período;
- preço de fechamento;
- volume de tick;
- spread;
- volume bolsista.
Além dessas propriedades básicas do objeto-barra, podemos definir suas propriedades adicionais no momento da criação do objeto:
- ano ao qual pertence a barra;
- mês ao qual pertence a barra;
- dia da semana da barra;
- número de série da barra no ano;
- dia do mês;
- hora da barra;
- minuto da barra;
- índice da barra na série temporal do símbolo;
- tamanho da barra (de High a Low);
- parte superior do corpo do candle (Max(Open,Close));
- parte inferior do corpo do candle (Min(Open,Close));
- tamanho do corpo do candle (de cima para baixo);
- tamanho da sombra superior do candel (de High até a parte superior do candle);
- tamanho da sombra inferior do candle (da parte inferior do corpo do candle até Low).
Todas essas propriedades nos permitem procurar qualquer combinação de barras e candles dentro do intervalo armazenado na lista de barras. Para a lista de objetos-barras pode ser definido o intervalo de dados armazenados desejado. Por padrão, armazenaremos mil barras na lista (ou todo o histórico disponível de barras do símbolo, se for menor que mil barras ou outro intervalo especificado).
Objeto "Barra"
Criamos enumerações de todas as propriedades do objeto-barra. Para fazer isso, abrimos o arquivo Defines.mqh, armazenado no caminho \MQL5\Include\DoEasy\Defines.mqh e inserimos no final do arquivo as enumerações do tipo de barra das propriedades do objeto-barra inteiras, reais e de string, bem como os métodos de classificação da lista de objetos-barra:
//+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Info for working with serial data | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Bar type (candle body) | //+------------------------------------------------------------------+ enum ENUM_BAR_BODY_TYPE { BAR_BODY_TYPE_BULLISH, // Bullish bar BAR_BODY_TYPE_BEARISH, // Bearish bar BAR_BODY_TYPE_NULL, // Zero bar BAR_BODY_TYPE_CANDLE_ZERO_BODY, // Candle with a zero body }; //+------------------------------------------------------------------+ //| Bar integer properties | //+------------------------------------------------------------------+ enum ENUM_BAR_PROP_INTEGER { BAR_PROP_INDEX = 0, // Bar index in timeseries BAR_PROP_TYPE, // Bar type (from the ENUM_BAR_BODY_TYPE enumeration) BAR_PROP_PERIOD, // Bar period (timeframe) BAR_PROP_SPREAD, // Bar spread BAR_PROP_VOLUME_TICK, // Bar tick volume BAR_PROP_VOLUME_REAL, // Bar exchange volume BAR_PROP_TIME, // Bar period start time BAR_PROP_TIME_DAY_OF_YEAR, // Bar day serial number in a year BAR_PROP_TIME_YEAR, // A year the bar belongs to BAR_PROP_TIME_MONTH, // A month the bar belongs to BAR_PROP_TIME_DAY_OF_WEEK, // Bar week day BAR_PROP_TIME_DAY, // Bar day of month (number) BAR_PROP_TIME_HOUR, // Bar hour BAR_PROP_TIME_MINUTE, // Bar minute }; #define BAR_PROP_INTEGER_TOTAL (14) // Total number of integer bar properties #define BAR_PROP_INTEGER_SKIP (0) // Number of bar properties not used in sorting //+------------------------------------------------------------------+ //| Real bar properties | //+------------------------------------------------------------------+ enum ENUM_BAR_PROP_DOUBLE { //--- bar data BAR_PROP_OPEN = BAR_PROP_INTEGER_TOTAL, // Bar open price BAR_PROP_HIGH, // Highest price for the bar period BAR_PROP_LOW, // Lowest price for the bar period BAR_PROP_CLOSE, // Bar close price //--- candle data BAR_PROP_CANDLE_SIZE, // Candle size BAR_PROP_CANDLE_SIZE_BODY, // Candle body size BAR_PROP_CANDLE_BODY_TOP, // Candle body top BAR_PROP_CANDLE_BODY_BOTTOM, // Candle body bottom BAR_PROP_CANDLE_SIZE_SHADOW_UP, // Candle upper wick size BAR_PROP_CANDLE_SIZE_SHADOW_DOWN, // Candle lower wick size }; #define BAR_PROP_DOUBLE_TOTAL (10) // Total number of real bar properties #define BAR_PROP_DOUBLE_SKIP (0) // Number of bar properties not used in sorting //+------------------------------------------------------------------+ //| Bar string properties | //+------------------------------------------------------------------+ enum ENUM_BAR_PROP_STRING { BAR_PROP_SYMBOL = (BAR_PROP_INTEGER_TOTAL+BAR_PROP_DOUBLE_TOTAL), // Bar symbol }; #define BAR_PROP_STRING_TOTAL (1) // Total number of string bar properties //+------------------------------------------------------------------+ //| Possible bar sorting criteria | //+------------------------------------------------------------------+ #define FIRST_BAR_DBL_PROP (BAR_PROP_INTEGER_TOTAL-BAR_PROP_INTEGER_SKIP) #define FIRST_BAR_STR_PROP (BAR_PROP_INTEGER_TOTAL-BAR_PROP_INTEGER_SKIP+BAR_PROP_DOUBLE_TOTAL-BAR_PROP_DOUBLE_SKIP) enum ENUM_SORT_BAR_MODE { //--- Sort by integer properties SORT_BY_BAR_INDEX = 0, // Sort by bar index SORT_BY_BAR_TYPE, // Sort by bar type (from the ENUM_BAR_BODY_TYPE enumeration) SORT_BY_BAR_PERIOD, // Sort by bar period (timeframe) SORT_BY_BAR_SPREAD, // Sort by bar spread SORT_BY_BAR_VOLUME_TICK, // Sort by bar tick volume SORT_BY_BAR_VOLUME_REAL, // Sort by bar exchange volume SORT_BY_BAR_TIME, // Sort by bar period start time SORT_BY_BAR_TIME_DAY_OF_YEAR, // Sort by bar day number in a year SORT_BY_BAR_TIME_YEAR, // Sort by a year the bar belongs to SORT_BY_BAR_TIME_MONTH, // Sort by a month the bar belongs to SORT_BY_BAR_TIME_DAY_OF_WEEK, // Sort by a bar week day SORT_BY_BAR_TIME_DAY, // Sort by a bar day SORT_BY_BAR_TIME_HOUR, // Sort by a bar hour SORT_BY_BAR_TIME_MINUTE, // Sort by a bar minute //--- Sort by real properties SORT_BY_BAR_OPEN = FIRST_BAR_DBL_PROP, // Sort by bar open price SORT_BY_BAR_HIGH, // Sort by the highest price for the bar period SORT_BY_BAR_LOW, // Sort by the lowest price for the bar period SORT_BY_BAR_CLOSE, // Sort by a bar close price SORT_BY_BAR_CANDLE_SIZE, // Sort by a candle price SORT_BY_BAR_CANDLE_SIZE_BODY, // Sort by a candle body size SORT_BY_BAR_CANDLE_BODY_TOP, // Sort by a candle body top SORT_BY_BAR_CANDLE_BODY_BOTTOM, // Sort by a candle body bottom SORT_BY_BAR_CANDLE_SIZE_SHADOW_UP, // Sort by candle upper wick size SORT_BY_BAR_CANDLE_SIZE_SHADOW_DOWN, // Sort by candle lower wick size //--- Sort by string properties SORT_BY_BAR_SYMBOL = FIRST_BAR_STR_PROP, // Sort by a bar symbol }; //+------------------------------------------------------------------+
O tipo de barra pode ser altista, baixista e zero. Como propriedade separada do tipo de barra definimos o tipo de corpo do candle, afinal, para determinar as diferentes formações de candles é usado, em particular, o tamanho do corpo do candle, por isso um corpo de tamanho zero não deve ser equiparado a uma barra zero, uma vez que a barra zero tem apenas um único preço para todos os quatro OHLC, enquanto um candle com corpo zero pode ter sombras que, dependendo da localização e da magnitude, farão diferença ao definir as formações do candle.
Para exibir descrições das propriedades das barras e algumas outras mensagens da biblioteca, precisamos de novas mensagens de texto.
Adicionamos ao arquivo Datas.mqh, armazenado no caminho \MQL5\Include\DoEasy\Datas.mqh, os índices das novas mensagens:
MSG_LIB_SYS_ERROR_CODE_OUT_OF_RANGE, // Return code out of range of error codes MSG_LIB_SYS_FAILED_CREATE_PAUSE_OBJ, // Failed to create the \"Pause\" object MSG_LIB_SYS_FAILED_CREATE_BAR_OBJ, // Failed to create the \"Bar\" object MSG_LIB_SYS_FAILED_SYNC_DATA, // Failed to synchronize data with the server
...
MSG_LIB_TEXT_TIME_UNTIL_THE_END_DAY, // Order lifetime till the end of the current day to be used MSG_LIB_TEXT_JANUARY, // January MSG_LIB_TEXT_FEBRUARY, // February MSG_LIB_TEXT_MARCH, // March MSG_LIB_TEXT_APRIL, // April MSG_LIB_TEXT_MAY, // May MSG_LIB_TEXT_JUNE, // June MSG_LIB_TEXT_JULY, // July MSG_LIB_TEXT_AUGUST, // August MSG_LIB_TEXT_SEPTEMBER, // September MSG_LIB_TEXT_OCTOBER, // October MSG_LIB_TEXT_NOVEMBER, // November MSG_LIB_TEXT_DECEMBER, // December MSG_LIB_TEXT_SUNDAY, // Sunday
...
MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS, // Added pending request activation conditions //--- CBar MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA, // Failed to receive bar data MSG_LIB_TEXT_BAR_FAILED_GET_SERIES_DATA, // Failed to receive timeseries data MSG_LIB_TEXT_BAR_FAILED_ADD_TO_LIST, // Could not add bar object to the list MSG_LIB_TEXT_BAR, // Bar MSG_LIB_TEXT_BAR_PERIOD, // Timeframe MSG_LIB_TEXT_BAR_SPREAD, // Spread MSG_LIB_TEXT_BAR_VOLUME_TICK, // Tick volume MSG_LIB_TEXT_BAR_VOLUME_REAL, // Exchange volume MSG_LIB_TEXT_BAR_TIME, // Period start time MSG_LIB_TEXT_BAR_TIME_YEAR, // Year MSG_LIB_TEXT_BAR_TIME_MONTH, // Month MSG_LIB_TEXT_BAR_TIME_DAY_OF_YEAR, // Day serial number in a year MSG_LIB_TEXT_BAR_TIME_DAY_OF_WEEK, // Week day MSG_LIB_TEXT_BAR_TIME_DAY, // Two months MSG_LIB_TEXT_BAR_TIME_HOUR, // Hour MSG_LIB_TEXT_BAR_TIME_MINUTE, // Minute MSG_LIB_TEXT_BAR_INDEX, // Index in timeseries MSG_LIB_TEXT_BAR_HIGH, // Highest price for the period MSG_LIB_TEXT_BAR_LOW, // Lowest price for the period MSG_LIB_TEXT_BAR_CANDLE_SIZE, // Candle size MSG_LIB_TEXT_BAR_CANDLE_SIZE_BODY, // Candle body size MSG_LIB_TEXT_BAR_CANDLE_SIZE_SHADOW_UP, // Candle upper wick size MSG_LIB_TEXT_BAR_CANDLE_SIZE_SHADOW_DOWN, // Candle lower wick size MSG_LIB_TEXT_BAR_CANDLE_BODY_TOP, // Candle body top MSG_LIB_TEXT_BAR_CANDLE_BODY_BOTTOM, // Candle body bottom MSG_LIB_TEXT_BAR_TYPE_BULLISH, // Bullish bar MSG_LIB_TEXT_BAR_TYPE_BEARISH, // Bearish bar MSG_LIB_TEXT_BAR_TYPE_NULL, // Zero bar MSG_LIB_TEXT_BAR_TYPE_CANDLE_ZERO_BODY, // Candle with a zero body MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA, // First, we need to set the required amount of data using SetAmountUsedData() }; //+------------------------------------------------------------------+
e as mensagens de texto correspondentes aos índices adicionados recentemente:
{"Код возврата вне заданного диапазона кодов ошибок","Out of range of error codes return code"}, {"Не удалось создать объект \"Пауза\"","Failed to create \"Pause\" object"}, {"Не удалось создать объект \"Бар\"","Failed to create \"Bar\" object"}, {"Не удалось синхронизировать данные с сервером","Failed to sync data with server"},
...
{"Будет использоваться время действия ордера до конца текущего дня","Order validity time until the end of the current day will be used"}, {"Январь","January"}, {"Февраль","February"}, {"Март","March"}, {"Апрель","April"}, {"Май","May"}, {"Июнь","June"}, {"Июль","July"}, {"Август","August"}, {"Сентябрь","September"}, {"Октябрь","October"}, {"Ноябрь","November"}, {"Декабрь","December"}, {"Воскресение","Sunday"},
...
{"Добавлены условия активации отложенного запроса","Pending request activation conditions added"}, {"Не удалось получить данные бара","Failed to get bar data"}, {"Не удалось получить данные таймсерии","Failed to get timeseries data"}, {"Не удалось добавить объект-бар в список","Failed to add bar object to list"}, {"Бар","Bar"}, {"Таймфрейм","Timeframe"}, {"Спред","Spread"}, {"Тиковый объём","Tick volume"}, {"Биржевой объём","Real volume"}, {"Время начала периода","Period start time"}, {"Год","Year"}, {"Месяц","Month"}, {"Порядковый номер дня в году","Sequence day number in a year"}, {"День недели","Day of week"}, {"День месяца","Day of month"}, {"Час","Hour"}, {"Минута","Minute"}, {"Индекс в таймсерии","Timeseries index"}, {"Наивысшая цена за период","Highest price for the period"}, {"Наименьшая цена за период","Lowest price for the period"}, {"Размер свечи","Candle size"}, {"Размер тела свечи","Candle body size"}, {"Размер верхней тени свечи","Candle upper shadow size"}, {"Размер нижней тени свечи","Candle lower shadow size"}, {"Верх тела свечи","Top of candle body"}, {"Низ тела свечи","Bottom of candle body"}, {"Бычий бар","Bullish bar"}, {"Медвежий бар","Bearish bar"}, {"Нулевой бар","Zero bar"}, {"Свеча с нулевым телом","Candle with zero body"}, {"Сначала нужно установить требуемое количество данных при помощи SetAmountUsedData()","First you need to set required amount of data using SetAmountUsedData()"}, }; //+---------------------------------------------------------------------+
No arquivo de funções de serviço DELib.mqh, localizado em \MQL5\Include\DoEasy\Services\DELib.mqh inserimos uma função que retorna o nome do mês e uma função que retorna uma descrição do período gráfico:
//+------------------------------------------------------------------+ //| Return month names | //+------------------------------------------------------------------+ string MonthDescription(const int month) { return ( month==1 ? CMessage::Text(MSG_LIB_TEXT_JANUARY) : month==2 ? CMessage::Text(MSG_LIB_TEXT_FEBRUARY) : month==3 ? CMessage::Text(MSG_LIB_TEXT_MARCH) : month==4 ? CMessage::Text(MSG_LIB_TEXT_APRIL) : month==5 ? CMessage::Text(MSG_LIB_TEXT_MAY) : month==6 ? CMessage::Text(MSG_LIB_TEXT_JUNE) : month==7 ? CMessage::Text(MSG_LIB_TEXT_JULY) : month==8 ? CMessage::Text(MSG_LIB_TEXT_AUGUST) : month==9 ? CMessage::Text(MSG_LIB_TEXT_SEPTEMBER) : month==10 ? CMessage::Text(MSG_LIB_TEXT_OCTOBER) : month==11 ? CMessage::Text(MSG_LIB_TEXT_NOVEMBER) : month==12 ? CMessage::Text(MSG_LIB_TEXT_DECEMBER) : (string)month ); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Return timeframe description | //+------------------------------------------------------------------+ string TimeframeDescription(const ENUM_TIMEFRAMES timeframe) { return StringSubstr(EnumToString(timeframe),7); } //+------------------------------------------------------------------+
À função que retorna o nome do mês é transferido o número do mês, e de acordo com esse número é retornada a sua descrição.
À função que retorna o nome do mês é transferido o período gráfico e, em seguida, desde a representação de texto do valor da enumeração do período gráfico é extraída um substring, que começa da posição 7 até o final da linha. O resultado é retornado como texto. Assim, por exemplo, da representação de texto do período horário PERIOD_H1 é recuperado o valor H1.
Para armazenar as classes dos objetos-barras criamos uma nova pasta no diretório de objetos da biblioteca \MQL5\Include\DoEasy\Objects\Series\, e nela criamos o novo arquivo Bar.mqh da classe CBar.
Consideramos a listagem do corpo da classe e, em seguida, implementamos seus métodos:
//+------------------------------------------------------------------+ //| Bar.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" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Services\DELib.mqh" //+------------------------------------------------------------------+ //| bar class | //+------------------------------------------------------------------+ class CBar : public CObject { private: MqlDateTime m_dt_struct; // Date structure int m_digits; // Symbol's digits value string m_period_description; // Timeframe string description long m_long_prop[BAR_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[BAR_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[BAR_PROP_STRING_TOTAL]; // String properties //--- Return the index of the array the bar's (1) double and (2) string properties are located at int IndexProp(ENUM_BAR_PROP_DOUBLE property) const { return(int)property-BAR_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_BAR_PROP_STRING property) const { return(int)property-BAR_PROP_INTEGER_TOTAL-BAR_PROP_DOUBLE_TOTAL; } //--- Return the bar type (bullish/bearish/zero) ENUM_BAR_BODY_TYPE BodyType(void) const; //--- Calculate and return the size of (1) candle, (2) candle body, //--- (3) upper, (4) lower candle wick, //--- (5) candle body top and (6) bottom double CandleSize(void) const { return(this.High()-this.Low()); } double BodySize(void) const { return(this.BodyHigh()-this.BodyLow()); } double ShadowUpSize(void) const { return(this.High()-this.BodyHigh()); } double ShadowDownSize(void) const { return(this.BodyLow()-this.Low()); } double BodyHigh(void) const { return ::fmax(this.Close(),this.Open()); } double BodyLow(void) const { return ::fmin(this.Close(),this.Open()); } //--- Return the (1) year and (2) month the bar belongs to, (3) week day, //--- (4) bar serial number in a year, (5) day, (6) hour, (7) minute, int TimeYear(void) const { return this.m_dt_struct.year; } int TimeMonth(void) const { return this.m_dt_struct.mon; } int TimeDayOfWeek(void) const { return this.m_dt_struct.day_of_week; } int TimeDayOfYear(void) const { return this.m_dt_struct.day_of_year; } int TimeDay(void) const { return this.m_dt_struct.day; } int TimeHour(void) const { return this.m_dt_struct.hour; } int TimeMinute(void) const { return this.m_dt_struct.min; } public: //--- Set bar's (1) integer, (2) real and (3) string properties void SetProperty(ENUM_BAR_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_BAR_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_BAR_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value; } //--- Return (1) integer, (2) real and (3) string bar properties from the properties array long GetProperty(ENUM_BAR_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_BAR_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_BAR_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Return the flag of the bar supporting the property virtual bool SupportProperty(ENUM_BAR_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_BAR_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_BAR_PROP_STRING property) { return true; } //--- Return itself CBar *GetObject(void) { return &this;} //--- Set (1) bar symbol, timeframe and index, (2) bar object parameters void SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index); void SetProperties(const MqlRates &rates); //--- Compare CBar objects by all possible properties (for sorting the lists by a specified bar object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CBar objects by all properties (to search for equal bar objects) bool IsEqual(CBar* compared_bar) const; //--- Constructors CBar(){;} CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index); CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const MqlRates &rates); //+------------------------------------------------------------------+ //| Methods of a simplified access to the order object properties | //+------------------------------------------------------------------+ //--- Return the (1) type, (2) period, (3) spread, (4) tick, (5) exchange volume, //--- (6) bar period start time, (7) year, (8) month the bar belongs to //--- (9) week number since the year start, (10) week number since the month start //--- (11) bar's day, (12) hour, (13) minute, (14) index ENUM_BAR_BODY_TYPE TypeBody(void) const { return (ENUM_BAR_BODY_TYPE)this.GetProperty(BAR_PROP_TYPE); } ENUM_TIMEFRAMES Period(void) const { return (ENUM_TIMEFRAMES)this.GetProperty(BAR_PROP_PERIOD); } int Spread(void) const { return (int)this.GetProperty(BAR_PROP_SPREAD); } long VolumeTick(void) const { return this.GetProperty(BAR_PROP_VOLUME_TICK); } long VolumeReal(void) const { return this.GetProperty(BAR_PROP_VOLUME_REAL); } datetime Time(void) const { return (datetime)this.GetProperty(BAR_PROP_TIME); } long Year(void) const { return this.GetProperty(BAR_PROP_TIME_YEAR); } long Month(void) const { return this.GetProperty(BAR_PROP_TIME_MONTH); } long DayOfWeek(void) const { return this.GetProperty(BAR_PROP_TIME_DAY_OF_WEEK); } long DayOfYear(void) const { return this.GetProperty(BAR_PROP_TIME_DAY_OF_YEAR); } long Day(void) const { return this.GetProperty(BAR_PROP_TIME_DAY); } long Hour(void) const { return this.GetProperty(BAR_PROP_TIME_HOUR); } long Minute(void) const { return this.GetProperty(BAR_PROP_TIME_MINUTE); } long Index(void) const { return this.GetProperty(BAR_PROP_INDEX); } //--- Return bar's (1) Open, (2) High, (3) Low, (4) Close price, //--- size of the (5) candle, (6) body, (7) candle top, (8) bottom, //--- size of the (9) candle upper, (10) lower wick double Open(void) const { return this.GetProperty(BAR_PROP_OPEN); } double High(void) const { return this.GetProperty(BAR_PROP_HIGH); } double Low(void) const { return this.GetProperty(BAR_PROP_LOW); } double Close(void) const { return this.GetProperty(BAR_PROP_CLOSE); } double Size(void) const { return this.GetProperty(BAR_PROP_CANDLE_SIZE); } double SizeBody(void) const { return this.GetProperty(BAR_PROP_CANDLE_SIZE_BODY); } double TopBody(void) const { return this.GetProperty(BAR_PROP_CANDLE_BODY_TOP); } double BottomBody(void) const { return this.GetProperty(BAR_PROP_CANDLE_BODY_BOTTOM); } double SizeShadowUp(void) const { return this.GetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_UP); } double SizeShadowDown(void) const { return this.GetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_DOWN); } //--- Return bar symbol string Symbol(void) const { return this.GetProperty(BAR_PROP_SYMBOL); } //+------------------------------------------------------------------+ //| Descriptions of bar object properties | //+------------------------------------------------------------------+ //--- Get description of a bar's (1) integer, (2) real and (3) string properties string GetPropertyDescription(ENUM_BAR_PROP_INTEGER property); string GetPropertyDescription(ENUM_BAR_PROP_DOUBLE property); string GetPropertyDescription(ENUM_BAR_PROP_STRING property); //--- Return the bar type description string BodyTypeDescription(void) const; //--- Send description of bar properties to the journal (full_prop=true - all properties, false - only supported ones) void Print(const bool full_prop=false); //--- Display a short bar description in the journal virtual void PrintShort(void); //--- Return the bar object short name virtual string Header(void); //--- }; //+------------------------------------------------------------------+
Como uma revisão do material previamente visto consideraremos brevemente a composição da classe.
Na seção privada da classe estão localizados:
Três matrizes que armazenam as propriedades do objeto-barra (inteiro, real e string).
Métodos que calculam o verdadeiro índice da propriedade de um objeto na matriz correspondente.
Métodos que calculam e retornam propriedades adicionais do objeto-barra.
Na seção pública da classe estão localizados:
Métodos que gravam - nas matrizes das propriedades inteiras, reais e de string - o valor passado da propriedade do objeto.
Métodos que retornam desde a matriz o valor da propriedade inteira, real ou de string solicitada.
Métodos virtuais que retornam para cada propriedade o sinalizador que indica que o objeto mantém essa propriedade. Os métodos são destinados à implementação em objetos descendentes do objeto-barra e devem retornar false caso o objeto herdeiro não suporte a propriedade especificada. No objeto "Barra" são suportadas todas as propriedades e os métodos retornam true.
No primeiro artigo nós discutimos em detalhes toda a estrutura dos objetos da biblioteca, já aqui consideramos brevemente a implementação dos restantes métodos da classe.
A classe tem três construtores:
1. O construtor padrão sem parâmetros é usado para declarar um objeto de uma classe e, em seguida, definir os parâmetros para o objeto criado.
2. O primeiro construtor paramétrico recebe três parâmetros (símbolo, período gráfico e o índice da barra) e, com base nas mesmas, recebe da série temporal todas as propriedades do objeto-barra através da primeira forma da função CopyRates():
//+------------------------------------------------------------------+ //| Constructor 1 | //+------------------------------------------------------------------+ CBar::CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index) { MqlRates rates_array[1]; this.SetSymbolPeriod(symbol,timeframe,index); ::ResetLastError(); //--- If failed to write bar data to the array by index or set the time to the time structure //--- display an error message, create and fill the structure with zeros, and write it to the rates_array array if(::CopyRates(symbol,timeframe,index,1,rates_array)<1 || !::TimeToStruct(rates_array[0].time,this.m_dt_struct)) { int err_code=::GetLastError(); ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA),". ",CMessage::Text(MSG_LIB_SYS_ERROR)," ",CMessage::Text(err_code)," ",CMessage::Retcode(err_code)); MqlRates err={0}; rates_array[0]=err; } //--- Set the bar properties this.SetProperties(rates_array[0]); } //+------------------------------------------------------------------+
Esse construtor é usado para aquisição de dados únicos da série temporal imediatamente ao criar um objeto-barra.
3. O segundo construtor paramétrico serve para criar um objeto-barra a partir de uma matriz de estruturas MqlRates já preparada.
Quer dizer, isso implica um loop através da matriz de estruturas MqlRates e a criação de um objeto segundo o índice dessa matriz:
//+------------------------------------------------------------------+ //| Constructor 2 | //+------------------------------------------------------------------+ CBar::CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const MqlRates &rates) { this.SetSymbolPeriod(symbol,timeframe,index); ::ResetLastError(); //--- If failed to set time to the time structure, display the error message, //--- create and fill the structure with zeros, set the bar properties from this structure and exit if(!::TimeToStruct(rates.time,this.m_dt_struct)) { int err_code=::GetLastError(); ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA),". ",CMessage::Text(MSG_LIB_SYS_ERROR)," ",CMessage::Text(err_code)," ",CMessage::Retcode(err_code)); MqlRates err={0}; this.SetProperties(err); return; } //--- Set the bar properties this.SetProperties(rates); } //+------------------------------------------------------------------+
Aqui ao construtor - além do símbolo, período gráfico e índice - é transferida uma referência à estrutura MqlRates. Com base nesses dados, é criado um objeto-barra.
O método virtual Compare() se destina a comparar dois objetos de acordo com a propriedade especificada. Definido na classe de objeto base da biblioteca padrão CObject e retornará zero se os valores forem iguais e 1/-1 se um dos valores comparados for maior/menor. Para pesquisa e classificação, é usado no método Search() da Biblioteca Padrão e deve ser redefinido nas classes-herdeiras:
//+------------------------------------------------------------------+ //| Compare CBar objects by all possible properties | //+------------------------------------------------------------------+ int CBar::Compare(const CObject *node,const int mode=0) const { const CBar *bar_compared=node; //--- compare integer properties of two bars if(mode<BAR_PROP_INTEGER_TOTAL) { long value_compared=bar_compared.GetProperty((ENUM_BAR_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_BAR_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare real properties of two bars else if(mode<BAR_PROP_DOUBLE_TOTAL+BAR_PROP_INTEGER_TOTAL) { double value_compared=bar_compared.GetProperty((ENUM_BAR_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_BAR_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare string properties of two bars else if(mode<BAR_PROP_DOUBLE_TOTAL+BAR_PROP_INTEGER_TOTAL+BAR_PROP_STRING_TOTAL) { string value_compared=bar_compared.GetProperty((ENUM_BAR_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_BAR_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+
Método para definição de dois objetos-barras idênticos serve para comparar dois objetos de barra e retorna true somente se todos os campos de dois objetos comparados forem iguais:
//+------------------------------------------------------------------+ //| Compare CBar objects by all properties | //+------------------------------------------------------------------+ bool CBar::IsEqual(CBar *compared_bar) const { int beg=0, end=BAR_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_BAR_PROP_INTEGER prop=(ENUM_BAR_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_bar.GetProperty(prop)) return false; } beg=end; end+=BAR_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_BAR_PROP_DOUBLE prop=(ENUM_BAR_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_bar.GetProperty(prop)) return false; } beg=end; end+=BAR_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_BAR_PROP_STRING prop=(ENUM_BAR_PROP_STRING)i; if(this.GetProperty(prop)!=compared_bar.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
Método para definir o símbolo, o período gráfico e o índice de um objeto-barra numa série temporal:
//+------------------------------------------------------------------+ //| Set bar symbol, timeframe and index | //+------------------------------------------------------------------+ void CBar::SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index) { this.SetProperty(BAR_PROP_INDEX,index); this.SetProperty(BAR_PROP_SYMBOL,symbol); this.SetProperty(BAR_PROP_PERIOD,timeframe); this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS); this.m_period_description=TimeframeDescription(timeframe); } //+------------------------------------------------------------------+
Além de definir as três propriedades listadas, o método define o número de casas decimais para o valor do preço do símbolo como uma variável m_digits, bem como uma descrição do período gráfico na variável m_period_description , basta especificá-los uma vez ao criar objetos-barras.
Método para definir todos os parâmetros do objeto-barra simplesmente grava nas propriedades do objeto os valores da estrutura MqlRates passados para o método e também calcula os parâmetros das propriedades adicionais do objeto usando os métodos correspondentes:
//+------------------------------------------------------------------+ //| Set bar object parameters | //+------------------------------------------------------------------+ void CBar::SetProperties(const MqlRates &rates) { this.SetProperty(BAR_PROP_SPREAD,rates.spread); this.SetProperty(BAR_PROP_VOLUME_TICK,rates.tick_volume); this.SetProperty(BAR_PROP_VOLUME_REAL,rates.real_volume); this.SetProperty(BAR_PROP_TIME,rates.time); this.SetProperty(BAR_PROP_TIME_YEAR,this.TimeYear()); this.SetProperty(BAR_PROP_TIME_MONTH,this.TimeMonth()); this.SetProperty(BAR_PROP_TIME_DAY_OF_YEAR,this.TimeDayOfYear()); this.SetProperty(BAR_PROP_TIME_DAY_OF_WEEK,this.TimeDayOfWeek()); this.SetProperty(BAR_PROP_TIME_DAY,this.TimeDay()); this.SetProperty(BAR_PROP_TIME_HOUR,this.TimeHour()); this.SetProperty(BAR_PROP_TIME_MINUTE,this.TimeMinute()); //--- this.SetProperty(BAR_PROP_OPEN,rates.open); this.SetProperty(BAR_PROP_HIGH,rates.high); this.SetProperty(BAR_PROP_LOW,rates.low); this.SetProperty(BAR_PROP_CLOSE,rates.close); this.SetProperty(BAR_PROP_CANDLE_SIZE,this.CandleSize()); this.SetProperty(BAR_PROP_CANDLE_SIZE_BODY,this.BodySize()); this.SetProperty(BAR_PROP_CANDLE_BODY_TOP,this.BodyHigh()); this.SetProperty(BAR_PROP_CANDLE_BODY_BOTTOM,this.BodyLow()); this.SetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_UP,this.ShadowUpSize()); this.SetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_DOWN,this.ShadowDownSize()); //--- this.SetProperty(BAR_PROP_TYPE,this.BodyType()); } //+------------------------------------------------------------------+
Método que registra no log descrições de todas as propriedades do objeto-barra:
//+------------------------------------------------------------------+ //| Display bar properties in the journal | //+------------------------------------------------------------------+ void CBar::Print(const bool full_prop=false) { ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") ============="); int beg=0, end=BAR_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_BAR_PROP_INTEGER prop=(ENUM_BAR_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=BAR_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_BAR_PROP_DOUBLE prop=(ENUM_BAR_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=BAR_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_BAR_PROP_STRING prop=(ENUM_BAR_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.Header(),") =============\n"); } //+------------------------------------------------------------------+
Em três ciclos, matrizes de propriedades de objetos exibem descrições de cada propriedade. Se a propriedade não for suportada, não será exibida se o parâmetro de entrada do método full_prop tiver um valor false (por padrão).
Método que registra uma breve descrição de um objeto-barra:
//+------------------------------------------------------------------+ //| Display a short bar description in the journal | //+------------------------------------------------------------------+ void CBar::PrintShort(void) { int dg=(this.m_digits>0 ? this.m_digits : 1); string params= ( ::TimeToString(this.Time(),TIME_DATE|TIME_MINUTES|TIME_SECONDS)+", "+ "O: "+::DoubleToString(this.Open(),dg)+", "+ "H: "+::DoubleToString(this.High(),dg)+", "+ "L: "+::DoubleToString(this.Low(),dg)+", "+ "C: "+::DoubleToString(this.Close(),dg)+", "+ "V: "+(string)this.VolumeTick()+", "+ (this.VolumeReal()>0 ? "R: "+(string)this.VolumeReal()+", " : "")+ this.BodyTypeDescription() ); ::Print(this.Header(),": ",params); } //+------------------------------------------------------------------+
O método exibe a descrição da barra em formato
Bar "SYMBOL" H4[INDEX]: YYYY.MM.DD HH:MM:SS, O: X.XXXXX, H: X.XXXXX, L: X.XXXXX, C: X.XXXXX, V: XXXX, BAR_TYPE
Por exemplo:
Bar "EURUSD" H4[6]: 2020.02.06 20:00:00, O: 1.09749, H: 1.09828, L: 1.09706, C: 1.09827, V: 3323, Bullish bar
Método para registrar no log um nome de barra curto:
//+------------------------------------------------------------------+ //| Return the bar object short name | //+------------------------------------------------------------------+ string CBar::Header(void) { return ( CMessage::Text(MSG_LIB_TEXT_BAR)+" \""+this.GetProperty(BAR_PROP_SYMBOL)+"\" "+ TimeframeDescription((ENUM_TIMEFRAMES)this.GetProperty(BAR_PROP_PERIOD))+"["+(string)this.GetProperty(BAR_PROP_INDEX)+"]" ); } //+------------------------------------------------------------------+
Exibe o nome da barra em formato
Bar "SYMBOL" H4[INDEX]
Por exemplo:
Bar "EURUSD" H4[6]
Método que retorna a descrição da propriedade inteira do objeto-barra:
//+------------------------------------------------------------------+ //| Return the description of the bar integer property | //+------------------------------------------------------------------+ string CBar::GetPropertyDescription(ENUM_BAR_PROP_INTEGER property) { return ( property==BAR_PROP_INDEX ? CMessage::Text(MSG_LIB_TEXT_BAR_INDEX)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BAR_PROP_TYPE ? CMessage::Text(MSG_ORD_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.BodyTypeDescription() ) : property==BAR_PROP_PERIOD ? CMessage::Text(MSG_LIB_TEXT_BAR_PERIOD)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.m_period_description ) : property==BAR_PROP_SPREAD ? CMessage::Text(MSG_LIB_TEXT_BAR_SPREAD)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BAR_PROP_VOLUME_TICK ? CMessage::Text(MSG_LIB_TEXT_BAR_VOLUME_TICK)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BAR_PROP_VOLUME_REAL ? CMessage::Text(MSG_LIB_TEXT_BAR_VOLUME_REAL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BAR_PROP_TIME ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ) : property==BAR_PROP_TIME_YEAR ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME_YEAR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.Year() ) : property==BAR_PROP_TIME_MONTH ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME_MONTH)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+MonthDescription((int)this.Month()) ) : property==BAR_PROP_TIME_DAY_OF_YEAR ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME_DAY_OF_YEAR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)::IntegerToString(this.DayOfYear(),3,'0') ) : property==BAR_PROP_TIME_DAY_OF_WEEK ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME_DAY_OF_WEEK)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+DayOfWeekDescription((ENUM_DAY_OF_WEEK)this.DayOfWeek()) ) : property==BAR_PROP_TIME_DAY ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME_DAY)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)::IntegerToString(this.Day(),2,'0') ) : property==BAR_PROP_TIME_HOUR ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME_HOUR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)::IntegerToString(this.Hour(),2,'0') ) : property==BAR_PROP_TIME_MINUTE ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME_MINUTE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)::IntegerToString(this.Minute(),2,'0') ) : "" ); } //+------------------------------------------------------------------+
Ao método é transferida uma propriedade inteira e, dependendo de seu valor, é retornada uma descrição, especificada no arquivo Datas.mqh.
Métodos que retornam descrições das propriedades reais e de string do objeto-barra organizados de maneira semelhante ao método que retorna a descrição da propriedade inteira do objeto-barra:
//+------------------------------------------------------------------+ //| Return the description of the bar's real property | //+------------------------------------------------------------------+ string CBar::GetPropertyDescription(ENUM_BAR_PROP_DOUBLE property) { int dg=(this.m_digits>0 ? this.m_digits : 1); return ( property==BAR_PROP_OPEN ? CMessage::Text(MSG_ORD_PRICE_OPEN)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_HIGH ? CMessage::Text(MSG_LIB_TEXT_BAR_HIGH)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_LOW ? CMessage::Text(MSG_LIB_TEXT_BAR_LOW)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_CLOSE ? CMessage::Text(MSG_ORD_PRICE_CLOSE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_CANDLE_SIZE ? CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_SIZE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_CANDLE_SIZE_BODY ? CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_SIZE_BODY)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_CANDLE_SIZE_SHADOW_UP ? CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_SIZE_SHADOW_UP)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_CANDLE_SIZE_SHADOW_DOWN ? CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_SIZE_SHADOW_DOWN)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_CANDLE_BODY_TOP ? CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_BODY_TOP)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_CANDLE_BODY_BOTTOM ? CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_BODY_BOTTOM)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : "" ); } //+------------------------------------------------------------------+ //| Return the description of the bar string property | //+------------------------------------------------------------------+ string CBar::GetPropertyDescription(ENUM_BAR_PROP_STRING property) { return(property==BAR_PROP_SYMBOL ? CMessage::Text(MSG_LIB_PROP_SYMBOL)+": \""+this.GetProperty(property)+"\"" : ""); } //+------------------------------------------------------------------+
Método que retorna o tipo de barra:
//+------------------------------------------------------------------+ //| Return the bar type (bullish/bearish/zero) | //+------------------------------------------------------------------+ ENUM_BAR_BODY_TYPE CBar::BodyType(void) const { return ( this.Close()>this.Open() ? BAR_BODY_TYPE_BULLISH : this.Close()<this.Open() ? BAR_BODY_TYPE_BEARISH : (this.ShadowUpSize()+this.ShadowDownSize()==0 ? BAR_BODY_TYPE_NULL : BAR_BODY_TYPE_CANDLE_ZERO_BODY) ); } //+------------------------------------------------------------------+
Aqui tudo é simples: se o preço de fechamento da barra for maior que o preço de abertura, será uma barra de alta, se o preço de fechamento da barra for menor que o preço de abertura, será uma barra de baixa, de outra forma, se as duas sombras do candle forem zero, será uma barra com um corpo zero, de outra forma, será um candle de corpo zero.
Método que retorna uma descrição do tipo de barra:
//+------------------------------------------------------------------+ //| Return the bar type description | //+------------------------------------------------------------------+ string CBar::BodyTypeDescription(void) const { return ( this.BodyType()==BAR_BODY_TYPE_BULLISH ? CMessage::Text(MSG_LIB_TEXT_BAR_TYPE_BULLISH) : this.BodyType()==BAR_BODY_TYPE_BEARISH ? CMessage::Text(MSG_LIB_TEXT_BAR_TYPE_BEARISH) : this.BodyType()==BAR_BODY_TYPE_CANDLE_ZERO_BODY ? CMessage::Text(MSG_LIB_TEXT_BAR_TYPE_CANDLE_ZERO_BODY) : CMessage::Text(MSG_LIB_TEXT_BAR_TYPE_NULL) ); } //+------------------------------------------------------------------+
Dependendo do tipo de barra, o método retorna sua descrição, contida no arquivo Datas.mqh.
A classe do objeto "Barra" está pronta. Agora podemos, para cada barra da série temporal, criar um objeto-barra. Mas, isso não nos dá una vantagem significativa se compararmos com a obtenção usual de dados usando a solicitação da barra de série temporal por meio de CopyRates().
Para podermos lidar tranquilamente com os dados das séries temporais, precisamos criar uma lista de objetos-barras correspondentes à série temporal em questão. Assim, já podemos analisar os dados da lista e procurar nela todas as informações necessárias para a análise.
Isso significa que precisamos criar uma lista-série temporal na qual serão armazenados os objetos-barras.
Além disso, precisamos conhecer em que momento fixar a abertura de nova barra - para adicionar outro objeto-barra à lista e sempre ter uma ferramenta que nos sinaliza sobre a abertura de uma nova barra em qualquer símbolo e período gráfico.
Antes de criarmos uma lista para armazenar objetos-barras, escreveremos a classe New Bar, pois um objeto dessa classe será uma das propriedades da lista de barras.
Objeto "Nova barra"
Para determinar a abertura de nova barra, basta comparar a hora de abertura da barra atual com seu tempo de abertura anterior. Se esses tempos não coincidirem, estaremos perante a abertura de uma nova barra. Nesse caso, precisamos salvar o novo tempo de abertura como o anterior para posterior comparação:
NewBar = false; if(PrevTime != Time) { NewBar = true; PrevTime = Time; }
Esta opção será exibida uma vez que o evento de abertura de nova barra e todos os comandos subsequentes já serão executados na nova barra.
Mas, às vezes, a execução de determinados comandos é necessária precisamente após o evento "Nova barra" e, até que todos os comandos sejam concluídos, esse evento deve ser relevante. Para fazer isso, precisamos executar todos os comandos que devem ser concluídos no momento do surgimento de nova barra, antes de atribuir um novo valor ao tempo anterior:
NewBar = false; if(PrevTime != Time) { NewBar = true; // ... commands to be // ... executed // ... when a new bar appears PrevTime = Time; }
Assim, a primeira opção pode ser executada como uma função independente que retorna o sinalizador para abertura de uma nova barra. A segunda opção na versão apresentada deve fazer parte do manipulador OnTick(), de preferência no início, para que sejam executados todos os comandos que devem ser iniciados ao surgir uma nova barra primeiro e somente então tudo o que sempre é executado.
No caso mais simples, isso é suficiente para controlar a abertura de nova barra.
Mas isso não é suficiente para as necessidades da biblioteca, pois precisamos de uma definição separada de nova barra para cada símbolo e cada período gráfico.
Além disso, devem ser feitas duas variações:
- controle automático e armazenamento de tempo para um determinado símbolo e período gráfico (retorno do sinalizador de evento "Nova barra" separadamente para cada símbolo e período gráfico),
- controle de tempo para um determinado símbolo e período gráfico com controle manual que armazena seu novo valor
(a definição do evento "Nova barra" e o usuário determinando quando armazenar um novo tempo para o controle subsequente da próxima nova barra separadamente para cada símbolo e período gráfico).
Também é importante que não é recomendável desde os indicadores acessar as funções de atualização de séries temporais, se os dados forem solicitados no símbolo e período gráfico atuais. É provável que essa solicitação cause um erro, pois os dados históricos são atualizados no mesmo fluxo em que funciona o indicador. Portanto, é imperativo verificar solicitações desde iniciadores "e não desde o símbolo e período gráfico atuais, estamos, tentamos obter dados de séries temporais" e, se for o caso, para tal solicitação, é necessário usar outros métodos que não sejam SeriesInfoInteger() e outras funções que retornam dados seriais que são acessados ao iniciar o carregamento do histórico.
Essa abordagem existe e é bastante simples:
Nos indicadores, nos parâmetros do manipulador OnCalculate() já existem as variáveis predefinidas que precisamos:
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[])
- rates_total — quantidade de histórico disponível nas séries temporais (análogo da função Bars() sem parâmetros),
- prev_calculated — quantidade de dados já calculada na última chamada,
- time[] — matriz-séries temporais com dados sobre o tempo das barras.
Nos indicadores, podemos acompanhar as alterações nos dados históricos usando um cálculo simples:
- se (rates_total - prev_calculated) for maior que 1, isso significa que o histórico está carregado e que é necessário redesenhar o indicador,
- se (rates_total - prev_calculated) for igual a 1, isso indica a abertura de uma nova barra no símbolo-período gráfico atual.
- no estado normal, a cada novo tick, o valor da expressão (rates_total - prev_calculated) é 0.
Nos indicadores do símbolo e período atuais, para definir uma nova barra e indicar seu tempo, transferiremos o tempo da barra desde a matriz time[] para os métodos da classe e, em outros casos, obteremos o tempo dentro dos métodos da classe.
Na pasta da biblioteca \MQL5\Include\DoEasy\Objects\Series\ criamos o novo arquivo NewBarObj.mqh da classe CNewBarObj:
//+------------------------------------------------------------------+ //| NewBarObj.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" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| "New bar" object class | //+------------------------------------------------------------------+ class CNewBarObj { private: string m_symbol; // Symbol ENUM_TIMEFRAMES m_timeframe; // Timeframe datetime m_new_bar_time; // New bar time for auto time management datetime m_prev_time; // Previous time for auto time management datetime m_new_bar_time_manual; // New bar time for manual time management datetime m_prev_time_manual; // Previous time for manual time management //--- Return the current bar data datetime GetLastBarDate(const datetime time); public: //--- Set (1) symbol and (2) timeframe void SetSymbol(const string symbol) { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); } void SetPeriod(const ENUM_TIMEFRAMES timeframe) { this.m_timeframe=(timeframe==PERIOD_CURRENT ? (ENUM_TIMEFRAMES)::Period() : timeframe); } //--- Save the new bar time during the manual time management void SaveNewBarTime(const datetime time) { this.m_prev_time_manual=this.GetLastBarDate(time); } //--- Return (1) symbol and (2) timeframe string Symbol(void) const { return this.m_symbol; } ENUM_TIMEFRAMES Period(void) const { return this.m_timeframe; } //--- Return the (1) new bar time datetime TimeNewBar(void) const { return this.m_new_bar_time; } //--- Return the new bar opening flag during the time (1) auto, (2) manual management bool IsNewBar(const datetime time); bool IsNewBarManual(const datetime time); //--- Constructors CNewBarObj(void) : m_symbol(::Symbol()), m_timeframe((ENUM_TIMEFRAMES)::Period()), m_prev_time(0),m_new_bar_time(0), m_prev_time_manual(0),m_new_bar_time_manual(0) {} CNewBarObj(const string symbol,const ENUM_TIMEFRAMES timeframe); }; //+------------------------------------------------------------------+
Parece-me que aqui tudo é bastante simples:
na seção privada são declaradas as variáveis-membros da classe para armazenamento do símbolo e do período gráfico, para os quais o objeto definirá o evento "Nova barra",
variáveis para armazenamento do tempo de abertura da nova barra e do último tempo de abertura são separadas para controle automático e controle manual do tempo (acima é discutido para que é necessário.
O método GetLastBarDate() retorna o tempo de abertura da nova barra e será considerado abaixo.
Na seção pública da classe, é anunciado e implementado o método de armazenamento do tempo da nova barra como o anterior para gerenciamento manual de tempo SaveNewBarTime(), pois permite que o usuário da biblioteca armazene independentemente o tempo da nova barra após a conclusão de todas as ações necessárias na nova barra.
Os métodos restantes falam por si e não os descreveremos aqui.
Na classe são implementados dois construtores, o primeiro não possui parâmetros, em sua lista de inicialização são gravados o símbolo e o período atuais, e são redefinidos e todos os valores do tempo da nova barra e do último tempo de abertura da barra. Após criar esse objeto, devemos chamar independentemente os métodos para definir o símbolo e o período desejados para o objeto de classe criado.
O segundo construtor é paramétrico, para ele são imediatamente transferidos o símbolo e o período necessários:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CNewBarObj::CNewBarObj(const string symbol,const ENUM_TIMEFRAMES timeframe) : m_symbol(symbol),m_timeframe(timeframe) { this.m_prev_time=this.m_prev_time_manual=this.m_new_bar_time=this.m_new_bar_time_manual=0; } //+------------------------------------------------------------------+
Na sua lista de inicialização, são definidos o símbolo e o período, passados nos parâmetros do objeto de classe criado e, em seguida, todos os valores do tempo são definidos como zero no corpo da classe.
Método que retorna o sinalizador de abertura de nova barra durante o gerenciamento automático de tempo:
//+------------------------------------------------------------------+ //| Return new bar opening flag | //+------------------------------------------------------------------+ bool CNewBarObj::IsNewBar(const datetime time) { //--- Get the current bar time datetime tm=this.GetLastBarDate(time); //--- If the previous and current time are equal to zero, this is the first launch if(this.m_prev_time==0 && this.m_new_bar_time==0) { //--- set the new bar opening time, //--- set the previous bar time as the current one and return 'false' this.m_new_bar_time=this.m_prev_time=tm; return false; } //--- If the previous time is not equal to the current bar open time, this is a new bar if(this.m_prev_time!=tm) { //--- set the new bar opening time, //--- set the previous time as the current one and return 'true' this.m_new_bar_time=this.m_prev_time=tm; return true; } //--- in other cases, return 'false' return false; } //+------------------------------------------------------------------+
O método uma vez cada vez que uma nova barra é aberta no símbolo e no período gráfico especificado para o objeto, retorna true.
Método que retorna o sinalizador de abertura de nova barra durante o gerenciamento manual de tempo:
//+------------------------------------------------------------------+ //| Return the new bar opening flag during the manual management | //+------------------------------------------------------------------+ bool CNewBarObj::IsNewBarManual(const datetime time) { //--- Get the current bar time datetime tm=this.GetLastBarDate(time); //--- If the previous and current time are equal to zero, this is the first launch if(this.m_prev_time_manual==0 && this.m_new_bar_time_manual==0) { //--- set the new bar opening time, //--- set the previous bar time as the current one and return 'false' this.m_new_bar_time_manual=this.m_prev_time_manual=tm; return false; } //--- If the previous time is not equal to the current bar open time, this is a new bar if(this.m_prev_time_manual!=tm) { //--- set the new bar opening time and return 'true' //--- Save the previous time as the current one from the program using the SaveNewBarTime() method //--- Till the previous time is forcibly set as the current one from the program, //--- the method returns the new bar flag allowing the completion of all the necessary actions on the new bar. this.m_new_bar_time=tm; return true; } //--- in other cases, return 'false' return false; } //+------------------------------------------------------------------+
Diferentemente do método com gerenciamento automático de tempo, esse método não registra o valor do tempo atual da abertura de nova barra na variável que armazena o último de barra. Isso possibilita a cada novo tick após o registro do evento "Nova Barra" enviar o sinalizador de abertura de nova barra até que o usuário ache que todas as ações a serem feitas na abertura da nova barra foram realizadas e que é necessário armazenar o tempo de abertura da nova barra como o último.
O tempo da nova barra é passado para os dois métodos em seus parâmetros de entrada; depois, usando o método GetLastBarDate(), é decidido o tempo a ser utilizado:
//+------------------------------------------------------------------+ //| Return the current bar time | //+------------------------------------------------------------------+ datetime CNewBarObj::GetLastBarDate(const datetime time) { return ( ::MQLInfoInteger(MQL_PROGRAM_TYPE)==PROGRAM_INDICATOR && this.m_symbol==::Symbol() && this.m_timeframe==::Period() ? time : (datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_LASTBAR_DATE) ); } //+------------------------------------------------------------------+
Se este indicador, bem como o símbolo e o período gráfico do objeto "Nova Barra" coincidirem com o símbolo e período gráfico atuais, será retornado o tempo, passado para o método (nos parâmetros este tempo existe nos parâmetros OnCalculate() na matriz time[], e desde esta matriz será necessário passar o tempo para os métodos de definição da nova barra), caso contrário, obteremos o tempo da última barra com ajuda da SeriesInfoInteger(), nesse caso, em vez do tempo, poderemos passar qualquer valor.
Para as necessidades atuais, o objeto de classe "Nova barra" está pronto. Vamos começar a criar uma lista de objetos-barras.
A lista de objetos-barras é essencialmente uma parte dos dados históricos das séries temporais que compõem o MqlRates. Por que criar uma lista separada?
Em primeiro lugar, para classificação, pesquisa e comparações rápidas e, em segundo lugar, o objeto de barra, cujo número especificado é armazenado na lista, fornece, além dos campos da estrutura MqlRates, campos adicionais com valores que facilitam a pesquisa de várias formações de candles no futuro.
Lista de objetos-barras, pesquisa e classificação
Para a lista-séries temporais (lista de objetos-barras), usaremos a classe de matriz dinâmica de ponteiros para instâncias da classe CObject e seus descendentes da biblioteca padrão. Hoje criaremos uma única lista de objetos-barras de objetos na qual serão armazenadas as barras de apenas uma série temporal com base no símbolo e período gráfico. Nos artigos futuros, com base nessa lista, criaremos uma coleção de séries temporais por períodos gráficos para cada símbolo usado no programa do usuário. Assim, teremos muitas coleções semelhantes de listas-séries temporais, nas quais será possível procurar rapidamente as informações necessárias para análise e comparação com outras listas-coleções de séries temporais disponíveis para o usuário na biblioteca.
Cada lista de objetos-barra terá um número objetos-barra (profundidade do histórico) definido pelo usuário. Por padrão, a profundidade do histórico de todas as listas terá uma dimensão de 1 000 barras. E cada lista, antes de ser criada, deve levar em consideração a sincronização de dados com o servidor de negociação. Cada lista de cada série do tempo terá um valor indicando o número de barras do histórico disponíveis. Este valor é retornado pela função Bars() sem parâmetros. Se retornar zero, significa que o histórico ainda não está sincronizado. Faremos várias tentativas com pouca espera entre elas, à espera da sincronização de dados com o servidor.
Criamos no arquivo Defines.mqh a substituição de macros que define a profundidade do histórico usado, o número de milissegundos de uma pausa entre tentativas de sincronização do histórico com o servidor e o número de tentativas para obter o fato da sincronização:
//--- Pending request type IDs #define PENDING_REQUEST_ID_TYPE_ERR (1) // Type of a pending request created based on the server return code #define PENDING_REQUEST_ID_TYPE_REQ (2) // Type of a pending request created by request //--- Timeseries parameters #define SERIES_DEFAULT_BARS_COUNT (1000) // Required default amount of timeseries data #define PAUSE_FOR_SYNC_ATTEMPTS (16) // Amount of pause milliseconds between synchronization attempts #define ATTEMPTS_FOR_SYNC (5) // Number of attempts to receive synchronization with the server //+------------------------------------------------------------------+ //| Structures | //+------------------------------------------------------------------+
Para pesquisa rápida e classificação de listas-coleções, já criamos a funcionalidade na classe CSelect,
descrita na pasta de funções de serviço e de classes \MQL5\Include\DoEasy\Services\ no arquivo Select.mqh.
À classe adicionamos os métodos para pesquisar e classificar nas listas de barras-objetos.
Anexamos à listagem o arquivo da classe do objeto "Barra":
//+------------------------------------------------------------------+ //| Select.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> #include "..\Objects\Orders\Order.mqh" #include "..\Objects\Events\Event.mqh" #include "..\Objects\Accounts\Account.mqh" #include "..\Objects\Symbols\Symbol.mqh" #include "..\Objects\PendRequest\PendRequest.mqh" #include "..\Objects\Series\Bar.mqh" //+------------------------------------------------------------------+
Inserimos no corpo da classe a definição dos métodos de pesquisa e classificação com base nas propriedades do objeto "Barra":
//+------------------------------------------------------------------+ //| Class for sorting objects meeting the criterion | //+------------------------------------------------------------------+ class CSelect { private: //--- Method for comparing two values template<typename T> static bool CompareValues(T value1,T value2,ENUM_COMPARER_TYPE mode); public: //+------------------------------------------------------------------+ //| Methods of working with orders | //+------------------------------------------------------------------+ //--- Return the list of orders with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Return the order index with the maximum value of the order's (1) integer, (2) real and (3) string properties static int FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property); static int FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property); static int FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property); //--- Return the order index with the minimum value of the order's (1) integer, (2) real and (3) string properties static int FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property); static int FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property); static int FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property); //+------------------------------------------------------------------+ //| Methods of working with events | //+------------------------------------------------------------------+ //--- Return the list of events with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Return the event index with the maximum value of the event's (1) integer, (2) real and (3) string properties static int FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property); static int FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property); static int FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property); //--- Return the event index with the minimum value of the event's (1) integer, (2) real and (3) string properties static int FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property); static int FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property); static int FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property); //+------------------------------------------------------------------+ //| Methods of working with accounts | //+------------------------------------------------------------------+ //--- Return the list of accounts with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Return the event index with the maximum value of the event's (1) integer, (2) real and (3) string properties static int FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property); static int FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property); static int FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property); //--- Return the event index with the minimum value of the event's (1) integer, (2) real and (3) string properties static int FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property); static int FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property); static int FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property); //+------------------------------------------------------------------+ //| Methods of working with symbols | //+------------------------------------------------------------------+ //--- Return the list of symbols with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Return the symbol index with the maximum value of the order's (1) integer, (2) real and (3) string properties static int FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property); static int FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property); static int FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property); //--- Return the symbol index with the minimum value of the order's (1) integer, (2) real and (3) string properties static int FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property); static int FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property); static int FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property); //+------------------------------------------------------------------+ //| Methods of working with pending requests | //+------------------------------------------------------------------+ //--- Return the list of pending requests with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion static CArrayObj *ByPendReqProperty(CArrayObj *list_source,ENUM_PEND_REQ_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByPendReqProperty(CArrayObj *list_source,ENUM_PEND_REQ_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByPendReqProperty(CArrayObj *list_source,ENUM_PEND_REQ_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Return the pending request index with the maximum value of the order's (1) integer, (2) real and (3) string properties static int FindPendReqMax(CArrayObj *list_source,ENUM_PEND_REQ_PROP_INTEGER property); static int FindPendReqMax(CArrayObj *list_source,ENUM_PEND_REQ_PROP_DOUBLE property); static int FindPendReqMax(CArrayObj *list_source,ENUM_PEND_REQ_PROP_STRING property); //--- Return the pending request index with the minimum value of the order's (1) integer, (2) real and (3) string properties static int FindPendReqMin(CArrayObj *list_source,ENUM_PEND_REQ_PROP_INTEGER property); static int FindPendReqMin(CArrayObj *list_source,ENUM_PEND_REQ_PROP_DOUBLE property); static int FindPendReqMin(CArrayObj *list_source,ENUM_PEND_REQ_PROP_STRING property); //+------------------------------------------------------------------+ //| Methods of working with timeseries bars | //+------------------------------------------------------------------+ //--- Return the list of bars with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Return the pending request index with the maximum value of the order's (1) integer, (2) real and (3) string properties static int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property); static int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property); static int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_STRING property); //--- Return the pending request index with the minimum value of the order's (1) integer, (2) real and (3) string properties static int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property); static int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property); static int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_STRING property); //--- }; //+------------------------------------------------------------------+
E fora do corpo da classe, escrevemos uma implementação dos métodos adicionados:
//+------------------------------------------------------------------+ //| Methods of working with timeseries bar lists | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Return the list of bars with one integer | //| property meeting the specified criterion | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); int total=list_source.Total(); for(int i=0; i<total; i++) { CBar *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; long obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Return the list of bars with one real | //| property meeting the specified criterion | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); for(int i=0; i<list_source.Total(); i++) { CBar *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; double obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Return the list of bars with one string | //| property meeting the specified criterion | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); for(int i=0; i<list_source.Total(); i++) { CBar *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; string obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Return the listed bar index | //| with the maximum integer property value | //+------------------------------------------------------------------+ int CSelect::FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CBar *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBar *obj=list_source.At(i); long obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); long obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the listed bar index | //| with the maximum real property value | //+------------------------------------------------------------------+ int CSelect::FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CBar *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBar *obj=list_source.At(i); double obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); double obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the listed bar index | //| with the maximum string property value | //+------------------------------------------------------------------+ int CSelect::FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_STRING property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CBar *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBar *obj=list_source.At(i); string obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); string obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the listed bar index | //| with the minimum integer property value | //+------------------------------------------------------------------+ int CSelect::FindBarMin(CArrayObj* list_source,ENUM_BAR_PROP_INTEGER property) { int index=0; CBar *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBar *obj=list_source.At(i); long obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); long obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the listed bar index | //| with the minimum real property value | //+------------------------------------------------------------------+ int CSelect::FindBarMin(CArrayObj* list_source,ENUM_BAR_PROP_DOUBLE property) { int index=0; CBar *min_obj=NULL; int total=list_source.Total(); if(total== 0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBar *obj=list_source.At(i); double obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); double obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+ //| Return the listed bar index | //| with the minimum string property value | //+------------------------------------------------------------------+ int CSelect::FindBarMin(CArrayObj* list_source,ENUM_BAR_PROP_STRING property) { int index=0; CBar *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBar *obj=list_source.At(i); string obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); string obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+
Nós consideramos o funcionamento de métodos semelhantes no terceiro artigo da série anterior que descrevia bibliotecas, por tanto, não vamos falar sobre isso aqui, já que sempre é possível ler esse material novamente. E, claro, todas as perguntas podem ser expressas na discussão do artigo.
Criamos na pasta \MQL5\Include\DoEasy\Objects\Series\ o novo arquivo Series.mqh da classe CSeries e anexamos a ele o arquivo da classe CSelect e as novas classes do objeto "Nova barra" e do objeto "Barra":
//+------------------------------------------------------------------+ //| Series.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" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Services\Select.mqh" #include "NewBarObj.mqh" #include "Bar.mqh" //+------------------------------------------------------------------+
Agora, escreveremos no corpo da classe todas as variáveis-membro da classe necessárias e declararemos os métodos da classe:
//+------------------------------------------------------------------+ //| Series.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" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Services\Select.mqh" #include "NewBarObj.mqh" #include "Bar.mqh" //+------------------------------------------------------------------+ //| Timeseries class | //+------------------------------------------------------------------+ class CSeries : public CObject { private: ENUM_PROGRAM_TYPE m_program; // Program type ENUM_TIMEFRAMES m_timeframe; // Timeframe string m_symbol; // Symbol uint m_amount; // 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 public: //--- Return the timeseries list 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 and timeframe and (2) the number of applied timeseries data void SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe); bool SetAmountUsedData(const uint amount,const uint rates_total); //--- Return (1) symbol, (2) timeframe, (3) number of applied timeseries data, //--- (4) number of bars in the timeseries, the new bar flag with the (5) auto, (6) manual time management string Symbol(void) const { return this.m_symbol; } ENUM_TIMEFRAMES Period(void) const { return this.m_timeframe; } uint AmountUsedData(void) const { return this.m_amount; } uint Bars(void) const { return this.m_bars; } 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 CBar *GetBarByListIndex(const uint index); CBar *GetBarBySeriesIndex(const uint index); //--- 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); //--- Save the new bar time during the manual time management void SaveNewBarTime(const datetime time) { this.m_new_bar_obj.SaveNewBarTime(time); } //--- Synchronize symbol data with server data bool SyncData(const uint amount,const uint rates_total); //--- (1) Create and (2) update the timeseries list int Create(const uint amount=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); //--- Constructors CSeries(void); CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint amount=0); }; //+------------------------------------------------------------------+
Temos métodos para obter listas em todas as classes-coleções de objetos, métodos esses cujo desenvolvimento consideramos no terceiro artigo da série anterior.
Vamos examinar a lista de métodos declarados e analisar sua implementação.
O primeiro construtor de classe não possui parâmetros e serve para criar uma lista para o símbolo e o período gráfico atuais:
//+------------------------------------------------------------------+ //| Constructor 1 (current symbol and period timeseries) | //+------------------------------------------------------------------+ CSeries::CSeries(void) : m_bars(0),m_amount(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(),(ENUM_TIMEFRAMES)::Period()); } //+------------------------------------------------------------------+
Na lista de inicialização, são redefinidos os valores do número de barras de série temporal disponíveis, o número de barras armazenadas na lista e o sinalizador de sincronização de dados com o servidor.
Em seguida, é definido o tipo de programa, é limpa a lista de objetos-barras e para ele é definido um sinalizador de classificação por índice de barras, depois disso, para a lista são definidos o símbolo e o período gráfico atuais
. Após criar uma lista de objetos-barras, é necessário definir o número de barras usadas, usando o método SetAmountUsedData() ou SyncData(), que inclui o método SetAmountUsedData(). Para indicadores, é necessário passar o segundo parâmetro para o método rates_total.
O segundo construtor da classe possui três parâmetros de entrada (símbolo, período e tamanho de lista de objetos-barras) e serve para criar uma lista para o símbolo e o período especificados com a profundidade de histórico desejada:
//+------------------------------------------------------------------+ //| Constructor 2 (specified symbol and period timeseries) | //+------------------------------------------------------------------+ CSeries::CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint amount=0) : m_bars(0), m_amount(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.SetAmountUsedData(amount,0); } //+------------------------------------------------------------------+
Na lista de inicialização, são redefinidos os valores do número de barras de série temporal disponíveis, o número de barras armazenadas na lista e o sinalizador de sincronização de dados com o servidor.
Em seguida, é definido o tipo de programa, é limpa a lista de objetos-barras e para ele é definido um sinalizador de classificação por índice de barras,
depois disso, para a lista são definidos o símbolo e o período gráfico atuais.
Finalmente, para o sinalizador de sincronização é definido o resultado do método de configuração de número de barras necessárias da lista de objetos-barras SetAmountUsedData(), ao qual é transferida a profundidade do histórico, indicada pelo parâmetro de entrada amount do construtor.
Após criar uma lista de objetos de barra, é imperativo que o programa controle a sincronização com o servidor usando o método SyncData().
Método para definir símbolo e período gráfico:
//+------------------------------------------------------------------+ //| Set a symbol and timeframe | //+------------------------------------------------------------------+ void CSeries::SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe) { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); this.m_timeframe=(timeframe==PERIOD_CURRENT ? (ENUM_TIMEFRAMES)::Period() : timeframe); this.m_new_bar_obj.SetSymbol(this.m_symbol); this.m_new_bar_obj.SetPeriod(this.m_timeframe); } //+------------------------------------------------------------------+
Ao método são transferidos o símbolo e o período gráfico, é verificado se os valores estão corretos e é definido o símbolo atual e o período gráfico ou os passados para o método. Em seguida, para o objeto "Nova barra" da lista de barras são definidos o símbolo e o período gráfico recém-salvos nas variáveis.
Método que define a quantidade de dados usados da série-temporal para a lista de objetos-barras:
//+------------------------------------------------------------------+ //| Set the number of required data | //+------------------------------------------------------------------+ bool CSeries::SetAmountUsedData(const uint amount,const uint rates_total) { //--- Set the number of available timeseries bars this.m_bars= ( //--- 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 : ::Bars(this.m_symbol,this.m_timeframe) ); //--- If managed to set the number of available history, set the amount of data in the list: if(this.m_bars>0) { //--- if zero 'amount' 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 'amount' value is passed, //--- use either the 'amount' value or the number of available history bars - the least one of them this.m_amount=(amount==0 ? ::fmin(SERIES_DEFAULT_BARS_COUNT,this.m_bars) : ::fmin(amount,this.m_bars)); return true; } return false; } //+------------------------------------------------------------------+
Ao método é transferida a quantidade necessária de dados para a lista de objetos-barras e o número total de barras da série temporal atual (para indicadores).
Em seguida, é verificado o tipo de programa e é selecionado o lugar de onde obtemos a quantidade de histórico disponível na variável m_bars , a partir do valor passado para o método (para o indicador no símbolo e período gráfico atuais) ou a partir do ambiente. Em seguida, determinamos qual valor deve ser definido para a variável m_amount , com base no tamanho do histórico disponível e requerido.
Método para sincronizar dados por símbolo e período gráfico com os dados no servidor:
//+------------------------------------------------------------------+ //|Synchronize symbol and timeframe data with server data | //+------------------------------------------------------------------+ bool CSeries::SyncData(const uint amount,const uint rates_total) { //--- If managed to obtain the available number of bars in the timeseries //--- and return the size of the bar object list, return 'true' this.m_sync=this.SetAmountUsedData(amount,rates_total); if(this.m_sync) return true; //--- Data is not yet synchronized with the server //--- Create a pause object CPause *pause=new CPause(); if(pause==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_PAUSE_OBJ)); return false; } //--- Set the pause duration of 16 milliseconds (PAUSE_FOR_SYNC_ATTEMPTS) and initialize the tick counter pause.SetWaitingMSC(PAUSE_FOR_SYNC_ATTEMPTS); pause.SetTimeBegin(0); //--- Make five (ATTEMPTS_FOR_SYNC) attempts to obtain the available number of bars in the timeseries //--- and set the bar object list size int attempts=0; while(attempts<ATTEMPTS_FOR_SYNC && !::IsStopped()) { //--- If data is currently synchronized with the server if(::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_SYNCHRONIZED)) { //--- if managed to obtain the available number of bars in the timeseries //--- and set the size of the bar object list, break the loop this.m_sync=this.SetAmountUsedData(amount,rates_total); if(this.m_sync) break; } //--- Data is not yet synchronized. //--- If the pause of 16 ms is over if(pause.IsCompleted()) { //--- set the new start of the next waiting for the pause object //--- and increase the attempt counter pause.SetTimeBegin(0); attempts++; } } //--- Remove the pause object and return the m_sync value delete pause; return this.m_sync; } //+------------------------------------------------------------------+
A lógica do método está bem descrita nos comentários do código.
Métodos de criação e de atualização da lista de objetos-barras também estão comentados em detalhes na listagem e, para não ocupar muito espaço os descrevendo, vamos considerá-los na íntegra. Se tiverem alguma dúvida sobre esses métodos, sempre podem perguntar na discussão do artigo:
//+------------------------------------------------------------------+ //| Create the timeseries list | //+------------------------------------------------------------------+ int CSeries::Create(const uint amount=0) { //--- If the required history depth is not set for the list yet, //--- display the appropriate message and return zero, if(this.m_amount==0) { ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA)); return 0; } //--- otherwise, if the passed 'amount' value exceeds zero and is not equal to the one already set, //--- while being lower than the available bar number, //--- set the new value of the required history depth for the list else if(amount>0 && this.m_amount!=amount && amount<this.m_bars) { //--- If failed to set a new value, return zero if(!this.SetAmountUsedData(amount,0)) return 0; } //--- For the rates[] array we are to receive historical data to, //--- set the flag of direction like in the timeseries, //--- clear the bar object list and set the flag of sorting by bar index MqlRates rates[]; ::ArraySetAsSeries(rates,true); this.m_list_series.Clear(); this.m_list_series.Sort(SORT_BY_BAR_INDEX); ::ResetLastError(); //--- Get historical data of the MqlRates structure to the rates[] array starting from the current bar in the amount of m_amount, //--- if failed to get data, display the appropriate message and return zero int copied=::CopyRates(this.m_symbol,this.m_timeframe,0,this.m_amount,rates),err=ERR_SUCCESS; if(copied<1) { err=::GetLastError(); ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_GET_SERIES_DATA)," ",this.m_symbol," ",TimeframeDescription(this.m_timeframe),". ", CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(err),CMessage::Retcode(err)); return 0; } //--- Historical data is received in the rates[] array //--- In the rates[] array loop, for(int i=0; i<copied; i++) { //--- create a new bar object out of the current MqlRates structure by the loop index ::ResetLastError(); CBar* bar=new CBar(this.m_symbol,this.m_timeframe,i,rates[i]); if(bar==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_BAR_OBJ),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(::GetLastError())); continue; } //--- If failed to add bar object to the list, //--- display the appropriate message with the error description in the journal if(!this.m_list_series.Add(bar)) { err=::GetLastError(); ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_ADD_TO_LIST)," ",bar.Header(),". ", CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(err),CMessage::Retcode(err)); } } //--- Return the size of the created bar object list return this.m_list_series.Total(); } //+------------------------------------------------------------------+ //| 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) { 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; } //--- if the specified timeseries size exceeds one bar, remove the earliest bar if(this.m_list_series.Total()>1) 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]); } //+------------------------------------------------------------------+
Para criar uma lista-série temporal, primeiro precisamos definir o tamanho do histórico e obter o sinalizador de sincronização com o servidor usando o método SyncData(), e logo a seguir chamar o método Create(). Para atualizar os dados da lista de objetos-barras, basta chamar o método Refresh() em cada tick. O método determina a abertura de nova barra e adiciona um novo objeto-barra à lista-série temporal. Neste caso, o primeiro objeto-barra é removido da lista de objetos-barras, para que o tamanho da lista permaneça sempre como foi definido usando o método SyncData().
Precisamos obter objetos desde a lista-série temporal os objetos "Barra" para usar seus dados na execução de algumas tarefas. Se para a lista-série temporal estiver definido o sinalizador de classificação por índice (SORT_BY_BAR_INDEX), a sequência de localização de objetos-barras na lista corresponderá à localização de barras reais na série temporal. Mas, se para a lista estiver definido outro sinalizador de classificação, a sequência de localização dos objetos na lista não corresponderá à localização de barras reais na série temporal, uma vez que estarão localizadas em ordem crescente do valor da propriedade pela qual é classificada a lista. Por isso, para selecionar objetos da lista de objetos-barras, temos dois métodos: um método que retorna um objeto-barra por seu índice na série temporal e um método que retorna um objeto-barra por seu índice na lista de objetos-barras.
Consideremos estes dois métodos.
Método que retorna um objeto-barra pelo seu índice na lista de objetos-barras:
//+------------------------------------------------------------------+ //| Return the bar object by index in the list | //+------------------------------------------------------------------+ CBar *CSeries::GetBarByListIndex(const uint index) { return this.m_list_series.At(this.m_list_series.Total()-index-1); } //+------------------------------------------------------------------+
Ao método é transferido o índice do objeto-barra desejado. O índice transmitido implica direção como na série temporal: um índice zero indica o último objeto na lista. Porém, os objetos na lista são armazenados do índice zero até list.Total()-1, quer dizer, para obter a última barra da lista, é necessário solicitá-la pelo índice list.Total()-1, e já para obter a barrá mais à direita no gráfico, é preciso solicitá-la pelo índice 0 — esta é uma indexação reversa.
Por isso, no método para obter o índice correto, ele é recalculado: do tamanho da lista é retirado o número do índice-1 transferido e é retornado o objeto-barra pelo índice calculado de acordo com a direção da indexação, como na série temporal.
Método que retorna objeto-barra pelo seu índice na série temporal:
//+------------------------------------------------------------------+ //| Return the bar object by index in the timeseries | //+------------------------------------------------------------------+ CBar *CSeries::GetBarBySeriesIndex(const uint index) { CArrayObj *list=this.GetList(BAR_PROP_INDEX,index); return(list==NULL || list.Total()==0 ? NULL : list.At(0)); } //+------------------------------------------------------------------+
Ao método é transferido o índice do objeto-barra desejado. O índice transmitido implica a direção como na série temporal.
Para obter um objeto com esse índice de barra na série temporal, é necessário selecionar por propriedade BAR_PROP_INDEX. Se na lista de objetos-barras houver uma barra com o índice desejado, na lista list haverá um único objeto que vamos retornar.
Se tal objeto não existir, será retornado NULL. No entanto, se houver um erro, esses dois métodos retornam o valor NULL.
Métodos que retornam as propriedades básicas do objeto-barra desde a lista de objetos-barras por índice:
//+------------------------------------------------------------------+ //| Return bar's Open by the index | //+------------------------------------------------------------------+ double CSeries::Open(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.Open() : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return bar's High by the timeseries index or the list of bars | //+------------------------------------------------------------------+ double CSeries::High(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.High() : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return bar's Low by the timeseries index or the list of bars | //+------------------------------------------------------------------+ double CSeries::Low(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.Low() : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return bar's Close by the timeseries index or the list of bars | //+------------------------------------------------------------------+ double CSeries::Close(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.Close() : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return bar time by the timeseries index or the list of bars | //+------------------------------------------------------------------+ datetime CSeries::Time(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.Time() : 0); } //+-------------------------------------------------------------------+ //|Return bar tick volume by the timeseries index or the list of bars | //+-------------------------------------------------------------------+ long CSeries::TickVolume(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.VolumeTick() : WRONG_VALUE); } //+--------------------------------------------------------------------+ //|Return bar real volume by the timeseries index or the list of bars | //+--------------------------------------------------------------------+ long CSeries::RealVolume(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.VolumeReal() : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return bar spread by the timeseries index or the list of bars | //+------------------------------------------------------------------+ int CSeries::Spread(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.Spread() : WRONG_VALUE); } //+------------------------------------------------------------------+
Ao método são transferidos o índice da barra e o sinalizador que indica se o índice solicitado corresponde (true) à direção de indexação como na série temporal.
Com base no valor deste sinalizadorobtemos o objeto-barra por meio do método GetBarBySeriesIndex() ou com ajuda do método GetBarByListIndex(), e em seguida retornamos o valor da propriedade solicitada no método.
Os outros métodos da classe CSeries que não foram considerados aqui são usados para configurar ou retornar facilmente os valores das variáveis-membros de classe.
Para verificar o que fizemos hoje, precisamos tornar o programa externo saiba das classes criadas. Para fazer isso, basta anexar o arquivo da classe CSeries ao arquivo do objeto principal da biblioteca CEngine:
//+------------------------------------------------------------------+ //| 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 "TradingControl.mqh" #include "Objects\Series\Series.mqh" //+------------------------------------------------------------------+
Agora, no EA de teste, podemos definir uma variável com o tipo de classe CSeries para criar e usar (nesta implementação, não totalmente, mas apenas para teste) listas de séries temporais com um determinado número de objetos-barras. Vamos verificar isso agora.
Teste
Para o teste, pegamos o EA do último artigo da série anterior e o salvamos na nova pasta \MQL5\Experts\TestDoEasy\ Part35\ usando o novo nome TestDoEasyPart32.mq5.
Como vamos testar...
No EA criamos duas variáveis (para teste) do objeto da classe CSeries — uma para o período gráfico de minutos (2 barras), e outro para o atual (10 barras). No manipulador OnInit() definimos para eles todos os parâmetros e exibimos três listas:
- lista completa de barras da série temporal atual, classificada pelo tamanho de candle (de High a Low) — exibimos descrições breves dos objetos-barras;
- lista completa de barras da série temporal atual, classificada por índices de barras (de barra 0 a barra 9) — exibimos descrições breves dos objetos-barras;
- lista completa de propriedades do objeto-barra, que corresponde ao índice 1 (barra anterior) da série temporal atual — exibimos a lista completa de propriedades de barra.
Já no manipulador OnTick(), em cada tick, atualizaremos ambas as séries temporais e exibiremos no log uma entrada sobre a abertura de nova barra em cada um dos períodos gráficos destas duas listas-série temporal — no período gráfico М1 e no atual. Para as séries temporais atuais, ao abrir uma nova barra, além de enviar entradas para o log, reproduziremos o som padrão "news.wav".
Na lista de variáveis globais do EA, definimos duas variáveis com o tipo de classe CSeries — variável da lista série temporal do período gráfico atual e variável da lista série temporal do período gráfico M1:
//+------------------------------------------------------------------+ //| TestDoEasyPart35.mq5 | //| 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" //--- includes #include <DoEasy\Engine.mqh> //--- enums enum ENUM_BUTTONS { BUTT_BUY, BUTT_BUY_LIMIT, BUTT_BUY_STOP, BUTT_BUY_STOP_LIMIT, BUTT_CLOSE_BUY, BUTT_CLOSE_BUY2, BUTT_CLOSE_BUY_BY_SELL, BUTT_SELL, BUTT_SELL_LIMIT, BUTT_SELL_STOP, BUTT_SELL_STOP_LIMIT, BUTT_CLOSE_SELL, BUTT_CLOSE_SELL2, BUTT_CLOSE_SELL_BY_BUY, BUTT_DELETE_PENDING, BUTT_CLOSE_ALL, BUTT_SET_STOP_LOSS, BUTT_SET_TAKE_PROFIT, BUTT_PROFIT_WITHDRAWAL, BUTT_TRAILING_ALL }; #define TOTAL_BUTT (20) //--- structures struct SDataButt { string name; string text; }; //--- input variables input ushort InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 150; // StopLoss in points input uint InpTakeProfit = 150; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpDistancePReq = 50; // Distance for Pending Request's activate (points) input uint InpBarsDelayPReq = 5; // Bars delay for Pending Request's activate (current timeframe) input uint InpSlippage = 5; // Slippage in points input uint InpSpreadMultiplier = 1; // Spread multiplier for adjusting stop-orders by StopLevel input uchar InpTotalAttempts = 5; // Number of trading attempts sinput double InpWithdrawal = 10; // Withdrawal funds (in tester) sinput uint InpButtShiftX = 0; // Buttons X shift sinput uint InpButtShiftY = 10; // Buttons Y shift input uint InpTrailingStop = 50; // Trailing Stop (points) input uint InpTrailingStep = 20; // Trailing Step (points) input uint InpTrailingStart = 0; // Trailing Start (points) input uint InpStopLossModify = 20; // StopLoss for modification (points) input uint InpTakeProfitModify = 60; // TakeProfit for modification (points) sinput ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; // Mode of used symbols list sinput string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; // List of used symbols (comma - separator) sinput bool InpUseSounds = true; // Use sounds //--- global variables CEngine engine; CSeries series; CSeries series_m1; 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 used_symbols; string array_used_symbols[]; bool testing; uchar group1; uchar group2; double g_point; int g_digits; //+------------------------------------------------------------------+
No manipulador OnInit() do EA definimos as propriedades necessárias para ambas as variáveis de objetos-série temporal, e imediatamente exibimos todas as informações sobre a lista de objetos-barras gerada do período gráfico atual. Para o período gráfico M1, simplesmente exibimos uma mensagem sobre a criação bem-sucedida da lista:
//+------------------------------------------------------------------+ //| 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 the M1 timeseries object parameters series_m1.SetSymbolPeriod(Symbol(),PERIOD_M1); //--- If symbol and M1 timeframe data are synchronized if(series_m1.SyncData(2,0)) { //--- create the timeseries list of two bars (the current and previous ones), //--- if the timeseries is created, display the appropriate message in the journal int total=series_m1.Create(2); if(total>0) Print(TextByLanguage("Создана таймсерия М1 с размером ","Created timeseries M1 with size "),(string)total); } //--- Check filling price data on the current symbol and timeframe series.SetSymbolPeriod(Symbol(),(ENUM_TIMEFRAMES)Period()); //--- If symbol and the current timeframe data are synchronized if(series.SyncData(10,0)) { //--- create the timeseries list of ten bars (bars 0 - 9), //--- if the timeseries is created, display three lists: //--- 1. the list of bars sorted by candle size (from bars' High to Low) //--- 2. the list of bars sorted by bar indices (according to their sequence in the timeseries) //--- 3. the full list of all properties of the previous bar object (bar properties with the timeseries index of 1) int total=series.Create(10); if(total>0) { CArrayObj *list=series.GetList(); CBar *bar=NULL; //--- Display short properties of the bar list by the candle size Print("\n",TextByLanguage("Бары, сортированные по размеру свечи от High до Low:","Bars sorted by candle size from High to Low:")); list.Sort(SORT_BY_BAR_CANDLE_SIZE); for(int i=0;i<total;i++) { bar=series.GetBarByListIndex(i); if(bar==NULL) continue; bar.PrintShort(); } //--- Display short properties of the bar list by the timeseries index Print("\n",TextByLanguage("Бары, сортированные по индексу таймсерии:","Bars sorted by timeseries index:")); list.Sort(SORT_BY_BAR_INDEX); for(int i=0;i<total;i++) { bar=series.GetBarByListIndex(i); if(bar==NULL) continue; bar.PrintShort(); } //--- Display all bar 1 properties Print(""); list=CSelect::ByBarProperty(list,BAR_PROP_INDEX,1,EQUAL); if(list.Total()==1) { bar=list.At(0); bar.Print(); } } } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
No manipulador OnTick(), em cada tick, atualizaremos as listas-série temporal de objetos de classe CSeries, e no evento "Nova barra" para cada uma das duas listas exibiremos mensagens sobre esse evento. Além disso, reproduziremos um som para o evento de abertura de nova barra no período gráfico atual.:
//+------------------------------------------------------------------+ //| 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 } //--- Check the update of the current and M1 timeseries series.Refresh(); if(series.IsNewBar(0)) { Print("New bar on ",series.Symbol()," ",TimeframeDescription(series.Period())," ",TimeToString(series.Time(0))); engine.PlaySoundByDescription(SND_NEWS); } series_m1.Refresh(); if(series_m1.IsNewBar(0)) { Print("New bar on ",series_m1.Symbol()," ",TimeframeDescription(series_m1.Period())," ",TimeToString(series_m1.Time(0))); } } //+------------------------------------------------------------------+
Compilamos o EA e o executamos na gráfico de símbolos.
No log, primeiro serão exibidas listas sobre a criação da lista-série temporal para o período gráfico М1 com base em duas barras, em seguida, será exibida a lista de barras classificada por tamanho de candles, depois, a lista de barras classificada segundo a ordem dos índices de barras na série tempora, e, finalmente, todas as propriedades do objeto-barra com índice 1 na série temporal:
Account 15585535: Artyom Trishkin (MetaQuotes Software Corp.) 9999.40 USD, 1:100, Demo account MetaTrader 5 Work only with the current symbol. The number of symbols used: 1 Created timeseries M1 with size 2 Bars, sorted by size candle from High to Low: Bar "EURUSD" H1[2]: 2020.02.12 10:00:00, O: 1.09145, H: 1.09255, L: 1.09116, C: 1.09215, V: 2498, Bullish bar Bar "EURUSD" H1[3]: 2020.02.12 09:00:00, O: 1.09057, H: 1.09150, L: 1.09022, C: 1.09147, V: 1773, Bullish bar Bar "EURUSD" H1[1]: 2020.02.12 11:00:00, O: 1.09215, H: 1.09232, L: 1.09114, C: 1.09202, V: 1753, Bearish bar Bar "EURUSD" H1[9]: 2020.02.12 03:00:00, O: 1.09130, H: 1.09197, L: 1.09129, C: 1.09183, V: 1042, Bullish bar Bar "EURUSD" H1[4]: 2020.02.12 08:00:00, O: 1.09108, H: 1.09108, L: 1.09050, C: 1.09057, V: 581, Bearish bar Bar "EURUSD" H1[8]: 2020.02.12 04:00:00, O: 1.09183, H: 1.09197, L: 1.09146, C: 1.09159, V: 697, Bearish bar Bar "EURUSD" H1[5]: 2020.02.12 07:00:00, O: 1.09122, H: 1.09143, L: 1.09096, C: 1.09108, V: 591, Bearish bar Bar "EURUSD" H1[6]: 2020.02.12 06:00:00, O: 1.09152, H: 1.09159, L: 1.09121, C: 1.09122, V: 366, Bearish bar Bar "EURUSD" H1[7]: 2020.02.12 05:00:00, O: 1.09159, H: 1.09177, L: 1.09149, C: 1.09152, V: 416, Bearish bar Bar "EURUSD" H1[0]: 2020.02.12 12:00:00, O: 1.09202, H: 1.09204, L: 1.09181, C: 1.09184, V: 63, Bearish bar Bars, sorted by timeseries index: Bar "EURUSD" H1[9]: 2020.02.12 03:00:00, O: 1.09130, H: 1.09197, L: 1.09129, C: 1.09183, V: 1042, Bullish bar Bar "EURUSD" H1[8]: 2020.02.12 04:00:00, O: 1.09183, H: 1.09197, L: 1.09146, C: 1.09159, V: 697, Bearish bar Bar "EURUSD" H1[7]: 2020.02.12 05:00:00, O: 1.09159, H: 1.09177, L: 1.09149, C: 1.09152, V: 416, Bearish bar Bar "EURUSD" H1[6]: 2020.02.12 06:00:00, O: 1.09152, H: 1.09159, L: 1.09121, C: 1.09122, V: 366, Bearish bar Bar "EURUSD" H1[5]: 2020.02.12 07:00:00, O: 1.09122, H: 1.09143, L: 1.09096, C: 1.09108, V: 591, Bearish bar Bar "EURUSD" H1[4]: 2020.02.12 08:00:00, O: 1.09108, H: 1.09108, L: 1.09050, C: 1.09057, V: 581, Bearish bar Bar "EURUSD" H1[3]: 2020.02.12 09:00:00, O: 1.09057, H: 1.09150, L: 1.09022, C: 1.09147, V: 1773, Bullish bar Bar "EURUSD" H1[2]: 2020.02.12 10:00:00, O: 1.09145, H: 1.09255, L: 1.09116, C: 1.09215, V: 2498, Bullish bar Bar "EURUSD" H1[1]: 2020.02.12 11:00:00, O: 1.09215, H: 1.09232, L: 1.09114, C: 1.09202, V: 1753, Bearish bar Bar "EURUSD" H1[0]: 2020.02.12 12:00:00, O: 1.09202, H: 1.09204, L: 1.09181, C: 1.09184, V: 63, Bearish bar ============= The beginning of the event parameter list (Bar "EURUSD" H1[1]) ============= Timeseries index: 1 Type: Bearish bar Timeframe: H1 Spread: 1 Tick volume: 1753 Real volume: 0 Period start time: 2020.02.12 11:00:00 Sequence day number in the year: 042 Year: 2020 Month: February Day of week: Wednesday Day od month: 12 Hour: 11 Minute: 00 ------ Price open: 1.09215 Highest price for the period: 1.09232 Lowest price for the period: 1.09114 Price close: 1.09202 Candle size: 0.00118 Candle body size: 0.00013 Top of the candle body: 1.09215 Bottom of the candle body: 1.09202 Candle upper shadow size: 0.00017 Candle lower shadow size: 0.00088 ------ Symbol: "EURUSD" ============= End of the parameter list (Bar "EURUSD" H1[1]) =============
Agora, executamos o EA no modo visual do testador no período gráfico M5 e observamos as mensagens do log do testador sobre a abertura de novas barras:
Como vemos, a cada quinta mensagem, uma sobre a abertura de uma nova barra no M5, e entre elas, mensagens sobre a abertura de uma nova barra em M1.
O que vem agora?
No próximo artigo, criaremos uma classe-coleção para listas de barras.
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 da série anterior:
Parte 1. Conceito, gerenciamento de dados e primeiros resultados
Parte 2. Coleção do histórico de ordens e negócios
Parte 3. Coleção de ordens e posições de mercado, busca e ordenação
Parte 4. Eventos de Negociação. Conceito
Parte 5. Classes e coleções de eventos de negociação. Envio de eventos para o programa
Parte 6. Eventos da conta netting
Parte 7. Eventos de ativação da ordem stoplimit, preparação da funcionalidade para os eventos de modificação de ordens e posições
Parte 8. Eventos de modificação de ordens e posições
Parte 9. Compatibilidade com a MQL4 — preparação dos dados
Parte 10. Compatibilidade com a MQL4 — eventos de abertura de posição e ativação de ordens pendentes
Parte 11. Compatibilidade com a MQL4 — eventos de encerramento de posição
Parte 12. Implementação da classe de objeto "conta" e da coleção de objetos da conta
Parte 13. Eventos do objeto conta
Parte 14. O objeto símbolo
Parte 15. Coleção de objetos-símbolos
Parte 16. Eventos de coleção de símbolos
Parte 17. Interatividade de objetos de biblioteca
Parte 18. Interatividade do objeto-conta e quaisquer de outros objetos da biblioteca
Parte 19. Classe de mensagens de biblioteca
Parte 20. Criação e armazenamento de recursos de programas
Parte 21 Classes de negociação - objeto base de negociação multiplataforma
Parte 22. Classes de negociação - classe básica de negociação, controle de restrições
Parte 23. Classes de negociação - classe básica de negociação, controle de parâmetros válidos
Parte 24. Classes de negociação - classe básica de negociação, correção automática de parâmetros errados
Parte 25. Classes de negociação - classe básica de negociação, processamento de erros retornados pelo servidor de negociação
Parte 26. Trabalho com ordens pendentes, primeira implementação (abertura de posições)
Parte 27. Trabalho com ordens pendentes, posicionamento de ordens pendentes Parte 28. Trabalho com ordens pendentes de negociação - fechamento, exclusão, modificações
Parte 29. Trabalho com ordens de negociação pendentes, classes de objetos-ordens
Parte 30. Trabalho com ordens de negociação pendentes, gerenciamento de objetos-ordens
Parte 31. Trabalho com ordens de negociação pendentes, abertura de posições por condições
Parte 32. Trabalho com ordens de negociação pendentes, posicionando ordens pendentes por condições
Parte 33. Trabalho com ordens de negociação pendentes, fechamento (parcial, total, usando uma oposta) de posições por condições
Parte 34. Trabalho com ordens de negociação pendentes - exclusão de ordens, modificação de ordens/posições por condições
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/7594
- 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