DoEasyライブラリの時系列(第35部): バーオブジェクトと銘柄の時系列リスト

9 6月 2020, 16:08
Artyom Trishkin
0
327

内容

本稿は、MetaTrader 5および4ターミナル用のプログラムを簡単に開発するためのライブラリの説明に関する新しいセクションの始まりとなります。初めのセクション(34稿)ではライブラリオブジェクトとその相互接続の概念に専念しました。この概念は、口座の現在のステータスと履歴を操作するための機能を開発するために使用されました。

現在、ライブラリには次の機能があります。

  • 任意の注文またはポジションのデータの検索、並べ替え、比較をおこなう
  • 注文およびポジションのプロパティへの迅速で便利なアクセスを提供する
  • 口座イベントを追跡する
  • 口座および銘柄データを取得および比較する
  • データベース(コレクション)内のすべての既存オブジェクトのプロパティ変更に応答し、発生したイベントの通知をプログラムに送信する

また、ライブラリが応答する必要のあるイベントをライブラリに通知したり、監視対象イベントに関する通知を送信したりすることもできます。

さらに、ターミナル取引機能と連携する機能を実装しました。また、新しいタイプの取引リクエストも作成しました。保留中取引リクエストにより、さまざまな取引条件でプログラムの動作ロジックを作成したり、新しいタイプの未決注文を作成するための機能一式を提供したりできます。
以前のセクションでは、これらすべておよびその他の多くのものが作成され、説明されました。

この2番目のセクションでは、価格データ、銘柄チャート、市場深度、指標などを操作する多くの側面を考慮しています。一般的に、今回の連載では、価格データ配列およびそれらのストレージオブジェクトとソースへの迅速なアクセス、検索、比較、並び替えのためのライブラリ機能の開発に当てられます。

概念

ライブラリオブジェクトの配置、格納、およびそれらの操作の概念については、前回の連載の最初の記事で詳しく説明しました。本稿では、ライブラリオブジェクトの配置と格納の基本原則を簡単に繰り返します。

同じ型のデータのセットは、このデータ型に固有の同一のプロパティを持つオブジェクトのリストとして表すことができます。同じ型のデータのオブジェクトのリストからの各オブジェクトは同様のプロパティセットを備えていますが、同じ並び替え済みリストの各オブジェクトは異なる値を持っています。

同じ型のオブジェクトのセットを格納する各リストは、リストオブジェクトが持つ任意のプロパティで並び替えできます。

オブジェクトプロパティには、整数、実数、文字列の3つの主要な型があります。

このようなオブジェクトを並べ替え済みリストで並べ替えることにより、オブジェクトをすばやく見つけ、そこからデータを取得して、カスタムプログラムで使用することができます。

ほとんどすべてのライブラリオブジェクトは、独自のプロパティを個別に追跡できます。つまり、関心のある各オブジェクトに設定できる特定の量だけ値を変更します。オブジェクトのプロパティが指定された値によって変更されると、追跡されたオブジェクトは、追跡用に設定された閾値に達したことを通知するメッセージを制御プログラムに送信します。

すべてのライブラリオブジェクトの基本は、取引プラットフォームで提供される基本標準ライブラリオブジェクトですが、オブジェクトリスト(オブジェクトコレクション)は、CObjectクラスインスタンスへのポインタの動的配列とその標準ライブラリの子孫です。

本稿ではバーオブジェクトを作成し、銘柄チャートの単一のバーに固有のデータ全体と、単一の銘柄と時間枠のすべてのバーオブジェクトのリストを格納します。

銘柄チャートの各バーには、MqlRates構造体で説明されている特定のパラメータセットがあります。

  • 期間開始時
  • 始値
  • 期間の最高値
  • 期間の最安値
  • 終値
  • ティックボリューム
  • スプレッド
  • ボリューム

バーオブジェクトの主要プロパティとは別に、オブジェクトの作成時には追加のプロパティを設定できます。

  • バーが属する年
  • バーが属する月
  • バーが属する日
  • 年でのバーのシリアル番号
  • 日(数)
  • バーの時間
  • バーの分
  • 銘柄時系列でのバーインデックス
  • バーサイズ(High~Low)
  • ローソク足実体の上限(Max(Open,Close))
  • ローソク足実体の下限(Min(Open,Close)).
  • ローソク足実体サイズ(上限~下限)
  • ローソク足上髭(High~ローソク足実体の上限)
  • ローソク足下髭(ローソク足実体の下限~Low)

これらすべてのプロパティにより、範囲内のバーとローソク足(バーリストに格納)の任意の組み合わせを探すことができます。保存するデータの目的の範囲は、バーオブジェクトのリストに設定できます。デフォルトでは、リストに1000本のバー(または、利用可能な履歴全体が1000本またはその他の指定された範囲未満の場合は利用可能な履歴全体)を保存します。

バーオブジェクト

すべてのバーオブジェクトプロパティの列挙を作成してみましょう。これには、\MQL5\Include\DoEasy\Defines.mqhファイルを開いて、バーオブジェクトのバータイプ整数実数文字列プロパティの列挙とバーオブジェクトリストを並び替えるメソッドをファイル末尾に追加します。

//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 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
  };
//+------------------------------------------------------------------+

バータイプは強気、弱気、ゼロのいずれかです。ローソク足の実体サイズはさまざまなローソク足の形成を定義するために使用されるパラメータの1つであるため、ローソク足の実体タイプを設定するためには別のバータイププロパティが使用されます。したがって、実体のサイズがゼロであるローソク足とゼロバーは同等であると見なすべきではありません。ゼロバーでは4つのOHLC価格すべてに対して1つの価格しかありませんが、実体のサイズがゼロであるローソク足には髭がある場合があり、ローソク足の形成を定義するときに位置と髭サイズが考慮されます。

バーのプロパティやその他のライブラリメッセージの説明を表示するには、新しいテキストメッセージが必要です。
\MQL5\Include\DoEasy\Datas.mqhファイルは新しいメッセージのインデックスを受け取ります。

   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()
  
  };
//+------------------------------------------------------------------+

また、新しく追加されたインデックスに対応するテキストも受け取りします。

   {"Код возврата вне заданного диапазона кодов ошибок","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()"},
   
  };
//+---------------------------------------------------------------------+

\MQL5\Include\DoEasy\Services\DELib.mqhサービス関数ファイルは、月の名前を返す関数時間枠の説明を返す関数を受け取ります。

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

月の名前を返す関数は月の番号を受け取り、そのテキストの説明はそれに従って返されます。

時間枠名を返す関数は時間枠を受け取ります。次に、時間枠の列挙値のテキスト表現を使用して、位置7から文字列の終わりまでの部分文字列を取得します。得られた結果をテキストとして返します。たとえば、H1の値は、PERIOD_H1時間の時間枠のテキスト表現から取得されます。

バーオブジェクトクラスを保存するには、ライブラリのオブジェクトカタログ(\MQL5\Include\DoEasy\Objects\)に新しいフォルダを作成します(\MQL5\Include\DoEasy\Objects\Series\)。次に、この新しいフォルダにCBarクラスのBar.mqhファイルを作成します。

クラス本体のリストとそのメソッドの実装について考えてみましょう。

//+------------------------------------------------------------------+
//|                                                          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);
//---
  };
//+------------------------------------------------------------------+

前に説明した資料について、クラスのコンテンツでメモリをリフレッシュしてみましょう。

クラスのprivateセクションの機能は、以下の通りです。

対応するバーオブジェクトプロパティ(整数、実数、文字列)を格納する3つの配列
適切な配列のオブジェクトプロパティの真のインデックスを計算するメソッド

追加のバーオブジェクトプロパティを計算して返すメソッド

クラスのpublicセクションの機能は、以下の通りです。

渡されたオブジェクトプロパティ値を整数、実数、文字列プロパティの配列に書き込むメソッド
配列から要求された整数、実数、または文字列プロパティの値を返すメソッド
各プロパティのオブジェクトによってプロパティをサポートするフラグを返す仮想メソッド。これらのメソッドは、バーオブジェクトの子孫オブジェクトに実装されることを意図しており、子孫オブジェクトが指定されたプロパティをサポートしていない場合にはfalseを返す必要があります。バーオブジェクトでは、すべてのプロパティがサポートされ、メソッドはtrueを返します。

最初の記事でライブラリオブジェクトの構造全体について説明したので、ここでは、残りのクラスメソッドの実装について簡単に説明します。

このクラスは3つのコンストラクタを備えています。

1. デフォルトでは、パラメータのないコンストラクタは、作成されたオブジェクトに必要なすべてのパラメータのその後の設定を伴うオブジェクトクラスの単純な宣言に使用されます。

2. 最初のパラメトリックコンストラクタは、銘柄、時間枠、バーインデックスの3つのパラメータを受け取ります。これらの3つのパラメータに基づいて、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]);
  }
//+------------------------------------------------------------------+

このコンストラクタは、バーオブジェクトを作成するときに時系列からデータを取得するのに一度だけ使用されます。

3. 2番目のパラメトリックコンストラクタは、MqlRates構造体の既に準備された配列からバーオブジェクトを作成するために使用されます。
言い換えると、これはMqlRates構造体配列をループで通過し、配列インデックスによってオブジェクトを作成することを意味します。

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

ここでは、MqlRates構造へのリンクが、銘柄、時間枠、インデックスとは別にコンストラクタに渡されます。バーオブジェクトは、このデータに基づいて作成されます。

Compare()仮想メソッドは、指定したプロパティで2つのオブジェクトを比較するためのものです。これは、標準ライブラリのCObject基本オブジェクトクラスで定義されており、値が等しい場合はゼロを返し、比較された値のいずれかがより高い/低い場合は1 /-1を返します。検索と並べ替えには標準ライブラリのSearch()メソッドが使用されます。メソッドは、子孫クラスで再定義する必要があります。

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

2つの類似バーオブジェクトを定義するメソッドは、2つのオブジェクトバーを比較するために使用されます。比較された2つのオブジェクトのすべてのフィールドが同一である場合にのみ、trueを返します。

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

以下は、時系列に銘柄、時間枠、バーオブジェクトインデックスを配置するメソッドです。

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

上記の3つのプロパティの設定とは別に、このメソッドは銘柄価格の小数点以下の桁数をm_digits変数に設定し、時間枠テキストの説明をm_period_description変数に設定します。これらは、バーオブジェクトを作成するときに一度指定するだけで十分です。

すべてのバーオブジェクトパラメータを設定するメソッドは、メソッドに渡されたMqlRates構造体からオブジェクトプロパティに値を書き込むだけでなく、適切なメソッドを使用して追加のオブジェクトプロパティのパラメータを計算します。

//+------------------------------------------------------------------+
//| 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());
  }
//+------------------------------------------------------------------+

以下は、すべてのバーオブジェクトプロパティの説明を表示するメソッドです。

//+------------------------------------------------------------------+
//| 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");
  }
//+------------------------------------------------------------------+

次の各プロパティの説明は、3つのループのオブジェクトプロパティ配列によって表示されます。プロパティがサポートされていない場合、full_propメソッドの入力がfalse(デフォルト)であれば、操作ログには表示されません。

以下は、操作ログにバーオブジェクトの短い説明を表示するメソッドです。

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

このメソッドは、バーの説明を次の形式で表示します。

Bar "銘柄" H4[INDEX]: YYYY.MM.DD HH:MM:SS, O: X.XXXXX, H: X.XXXXX, L: X.XXXXX, C: X.XXXXX, V: XXXX, BAR_TYPE

例として:

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

以下は、短いバー名を表示するメソッドです。

//+------------------------------------------------------------------+
//| 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)+"]"
     );
  }
//+------------------------------------------------------------------+

バー名を次の形式で表示します。

Bar "銘柄" H4[INDEX]

例として:

Bar "EURUSD" H4[6]

以下は、バーオブジェクトの整数プロパティの説明を返すメソッドです。

//+------------------------------------------------------------------+
//| 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')
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

このメソッドは整数プロパティを渡します。その値に応じて、Datas.mqhファイルに設定されたテキストの説明が返されます。

バーオブジェクトの実際のプロパティと文字列プロパティの説明を返すメソッドは、バーオブジェクトの整数プロパティの説明を返すメソッドに似ています。

//+------------------------------------------------------------------+
//| 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)+"\"" : "");
  }
//+------------------------------------------------------------------+

以下は、バータイプを返すメソッドです。

//+------------------------------------------------------------------+
//| 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)
     );
  }
//+------------------------------------------------------------------+

ここではすべて単純です。バーの終値が始値を超える場合、これは強気のバーですバーの終値が始値よりも低い場合、これは弱気ですローソク足の髭が両方ともゼロの場合、これは実体がゼロのバーですそれ以外の場合これは実体がゼロのローソク足です

以下は、バータイプの説明を返すメソッドです。

//+------------------------------------------------------------------+
//| 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)
     );
  }
//+------------------------------------------------------------------+

バータイプに応じて、メソッドはDatas.mqhファイルに設定されたテキストの説明を返します。

バーオブジェクトクラスの準備ができました。次に、必要な時系列の必要なバーごとにバーオブジェクトを作成します。ただし、オブジェクトだけでは、CopyRates()を使用して時系列バーを要求することによる通常のデータ受信に比べて、大きな利点はありません。

時系列データを必要に応じて管理できるようにするには、必要な時系列に対応するバーオブジェクトのリストを必要な数量で作成する必要があります。その後、リストデータを分析して、分析に必要なすべてのデータを検索できます。

つまり、バーオブジェクトを格納するための時系列リストを作成する必要があります。
さらに、新しいバーの開始を検出して別のバーオブジェクトをリストに追加し、その量に関係なく、銘柄と時間枠で新しいバーを開くことを常に通知するツールが必要です。

クラスオブジェクトはバーリストプロパティの1つであるため、バーオブジェクトを格納するリストを作成する前に、「新しいバー」クラスを記述します。

新しいバーオブジェクト

新しいバーを開くイベントを検出するには、現在のバーを開いた時間と前回のバーを開いた時間を比較します。それらが一致しない場合、新しいバーが開かれています。この場合、後続の比較のために、新しい時間を以前の時間として保存する必要があります。

NewBar = false;
if(PrevTime != Time)
  {
   NewBar = true;
   PrevTime = Time;
  }

この場合、新しいバーイベントは1回開かれますが、後続のすべてのコマンドは新しいバーで既に実行されています。

ただし、「新しいバー」の直後に特定のコマンドを実行する必要がある場合があります。イベントは、すべてのコマンドが完了するまで関連性を保つ必要があります。これを実現するには、前のバーに新しい値を割り当てる前に、新しいバーが表示されるまでに完了する必要があるすべてのコマンドを実行する必要があります。

NewBar = false;
if(PrevTime != Time)
  {
   NewBar = true;
   // ... commands to be
   // ... executed
   // ... when a new bar appears
   PrevTime = Time;
  }

したがって、最初のオプションは、新しいバーの開始フラグを返す独立した関数として実行できます。提示されたバージョンの2番目のオプションは、OnTick()ハンドラーのなるべく最初で一部である必要があります。新しいバーが表示されたときに実行されるすべてのコマンドが最初に実行されるようにします。次に、常に実行されるすべてのコマンドが続きます。

最も単純なケースでは、これは新しいバーの開始を制御するのに十分です。
ただし、これはライブラリのニーズには十分ではありません。説明されている2つのオプションで、銘柄と時間枠ごとに個別の新しいバー定義が必要です。

  1. 指定された銘柄と時間枠の自動制御と保存時間(各銘柄と時間枠の「新しいバー」フラグイベントを個別に返す)、
  2. 新しい値を保存する手動制御による指定された銘柄と時間枠の時間管理
    (「新しいバー」イベントを定義し、銘柄と時間枠ごとに次の新しいバーを後で制御するための新しい時間をいつ保存するかをユーザーが決定できるようにする)

現在の銘柄と時間枠でデータが要求されている場合は、時系列更新機能にアクセスすることはお勧めしません履歴データは、指標が機能するのと同じスレッドで更新されるため、このような要求は競合を引き起こす可能性があります。したがって、現在の銘柄や時間枠から時系列データを取得しようとしているかどうかを必ず確認してください。している場合は、そのような要求にSeriesInfoInteger()シリアルデータを返すその他の関数を適用する代わりに、別のメソッドを使用して、履歴の読み込みを開始するアクセスをおこないます。
このようなメソッドは存在し、非常に簡単です。

指標のOnCalculate()ハンドラパラメータには、必要な変数がすでに備わっています。

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 — 時系列で利用可能な履歴の数(パラメータなしのBars()関数と同様)
  • prev_calculated — 最後の呼び出しで既に計算されたデータの量
  • time[] — バー時間のデータを含む時系列配列

指標を使用すると、簡単な計算を使用して履歴データの変化を追跡できます。

  • (rates_total - prev_calculated)が1を超える場合、これは履歴が読み込まれ、指標を再描画する必要があることを意味します。
  • (rates_total - prev_calculated)が1の場合、現在の時間枠銘柄で新しいバーが開かれます。
  • 通常(rates_total - prev_calculated)式は、新しいティックごとに0になります。

指標では、time[]配列からのバー時間が現在の銘柄と時間枠のクラスメソッドに渡され、新しいバーを定義してその時間を指定します。他の場合では、クラスメソッド内で時間を受け取ります。

\MQL5\Include\DoEasy\Objects\Series\に、CNewBarObjクラスの新しいNewBarObj.mqhファイルを作成します。

//+------------------------------------------------------------------+
//|                                                    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);
  };
//+------------------------------------------------------------------+

ここではすべてが非常に単純だと思います。
銘柄と時間枠を保存するためのクラスメンバ変数は、privateセクションで宣言さ、オブジェクトが「新しいバー」イベントを定義します。
バーが開かれた新しい時間と以前の時間を格納するための変数は、自動手動の時間管理について別々です(これが必要な理由についてはすでに説明しました)。
GetLastBarDate()メソッドはバーが開かれた新しい時間を返し、以下で検討されます。
新しいバー時間を手動時間管理の以前の時間として保存するためのSaveNewBarTime()メソッドは、クラスのpublicセクションで宣言および実装されています。ライブラリユーザーは、必要なすべてのアクションが完了した後で、新しいバーの時間を格納できます。
残りのメソッドは自明であり、ここでそれらを説明する意味はありません。

このクラスは2つのコンストラクタを備えています。最初のコンストラクタにはパラメータがありません。現在の銘柄と時間枠は初期化リストで設定されますが、すべての新しいバーの時間値と以前のバーを開いた時間はリセットされます。このようなオブジェクトを作成したら、作成したクラスオブジェクトに必要な銘柄と時間枠を設定するメソッドを個別に呼び出す必要があります。
2番目のコンストラクタはパラメトリックで、必要な銘柄と時間枠をすぐに受け取ります。

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

作成されたクラスオブジェクトパラメータに渡される銘柄と時間枠は、その初期化リストで設定されます。次に、クラス本体のすべての時間変数にゼロ値が設定されます。

以下は、自動時間管理中に新しいバーの開始フラグを返すメソッドです。

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

このメソッドは、オブジェクトに設定された銘柄または時間枠で新しいバーが開かれるたびにtrueを返します。

以下は、手動時間管理中に新しいバーの開始フラグを返すメソッドです。

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

自動時間管理とは異なり、このメソッドは現在のバーが開かれた新しい時間をバーの前回の時間を格納する変数に設定しません。これにより、「新しいバー」イベントを検出した後、ユーザーが新しいバーを開くときに実行するすべてのアクションが完了し、次のことが必要になるまで、新しいティックで毎回新しいバーを開くフラグを送信できます。 新しいバーの開店時間を以前と同じように保存します。新しいバーの時間が両方のメソッドの入力に渡されます。次に、GetLastBarDate()メソッドを使用して、使用する時間を定義します。

//+------------------------------------------------------------------+
//| 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)
     );
  }
//+------------------------------------------------------------------+

これが指標の場合、「新しいバー」オブジェクトの銘柄と時間枠の両方が現在の銘柄と時間枠と一致しメソッドに渡される時間が返されます(指標では、時間はOnCalculate()パラメータつまりtime[]配列にあります。これは、新しいバーを定義するメソッドに時間を渡す配列です)、それ以外の場合SeriesInfoInteger()を使用して最後のバー時間を受毛撮ります。この場合、時間の代わりに任意の値を渡すことができます。

「新しいバー」オブジェクトクラスは、現在のニーズに対応しています。次に、バーオブジェクトのリストを作成します。

バーオブジェクトのリストは基本的に、MqlRatesの一部である履歴時系列データのセグメントです。なぜ別のリストが必要なのでしょうか。
まず、並び替え、検索、比較が高速になります。次に、MqlRates構造体フィールドとは別に、指定された数がリストに格納されているバーオブジェクトは、将来のさまざまなローソク足形成の検索を簡略化するための値を持つ追加フィールドを備えています。

バーオブジェクトのリスト、検索と並べ替え

時系列リスト(バーオブジェクトのリスト)には、標準ライブラリのCObjectクラスとその子孫のインスタンスへのポインタの動的配列のクラスを使用します。現在の記事では、バーオブジェクトの唯一のリストオブジェクトを実装して、銘柄と時間枠によって1つの時系列のみのバーを格納します。今後の記事では、ユーザプログラムで適用される個別の銘柄ごとに、時間枠ごとの時系列の収集の基礎としてこのリストを使用する予定です。時系列リストの同じ型のコレクションが複数あり、ライブラリユーザーが利用できる時系列コレクションの他のリストと分析および比較するために必要な情報をすばやく検索できるようになります。

バーオブジェクトの各リストには、ユーザ定義の数のバーオブジェクト(履歴の深さ)が含まれます。デフォルトでは、すべてのリストの履歴の深さは1000バーになります。作成する前に、各リストは取引サーバとのデータの同期を考慮する必要があります。各時系列の各リストには、使用可能な履歴バーの数を指定する値が含まれます。この値は、パラメーターなしでBars()関数によって返されます。ゼロを返す場合、履歴はまだ同期されていません。サーバとのデータの同期を待つ間、少しの間隔を空けて数回試行します。

Defines.mqhファイルで、適用された履歴の深さ履歴をサーバと同期する試行間の休止ミリ秒数同期イベントの取得試行回数を定義するマクロ置換を作成します。

//--- 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                                                       |
//+------------------------------------------------------------------+

コレクションリストをすばやく検索して並べ替えるために、サービス関数の\MQL5\Include\DoEasy\Services\フォルダで選択されたCSelectクラスととSelect.mqhファイルのクラスに機能を既に作成しています。

バーオブジェクトリストに検索と並べ替えのメソッドを追加します。
バーオブジェクトクラスファイルをインクルードします

//+------------------------------------------------------------------+
//|                                                       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"
//+------------------------------------------------------------------+

バーオブジェクトプロパティによる検索と並べ替えのメソッドの定義をクラス本体に追加します。

//+------------------------------------------------------------------+
//| 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);
//---
  };
//+------------------------------------------------------------------+

追加されたメソッドをクラス本体の外部に実装します。

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

前のシリーズの第3回の記事で同様のメソッドを検討したので、ここでは説明する意味がありません。ご質問がある場合は、コメント欄でお気軽にお問い合わせください。

\MQL5\Include\DoEasy\Objects\Series\で、CSeriesクラスのSeries.mqhファイルを作成し、新しく作成された「新しいバー」および「バー」オブジェクトスラスとともにCSelectクラスファイルをリンクします

//+------------------------------------------------------------------+
//|                                                       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"
//+------------------------------------------------------------------+

次に、必要なクラスメンバ変数をすべて追加し、クラス本体でクラスメソッドを宣言します。

//+------------------------------------------------------------------+
//|                                                       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);
  };
//+------------------------------------------------------------------+

リストを受け取るメソッドはすべてのオブジェクトコレクションクラスにあり、これらのメソッドの開発については、前シリーズの第3部で説明しました。

宣言されたメソッドのリストを見て、それらの実装を分析してみましょう。

最初のクラスコンストラクタにはパラメータがなく、現在の銘柄と時間枠のリストを作成するために使用されます。

//+------------------------------------------------------------------+
//| 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());
  }
//+------------------------------------------------------------------+

使用可能な時系列バーの数、リストに保存されているバーの数、およびサーバとのデータ同期のフラグが初期化リストでリセットされます
プログラムタイプが設定されバーオブジェクトのリストがクリアされバーインデックスによる並び替えフラグが設定されます。後に現在の銘柄と時間枠がリストに設定されます
バーオブジェクトリストを作成したら、SetAmountUsedData()メソッドを含むSetAmountUsedData()またはSyncData()メソッドを使用して、バーオブジェクトリストの使用バー数を設定してください。指標の場合は、rates_totalを2番目のパラメータとしてメソッドに渡してください。

クラスの2番目のコンストラクタは3つの入力(銘柄、時間枠、バーオブジェクトリストのサイズ)を備え、必要な履歴の深さを考慮して、指定された銘柄と時間枠のリストを作成するために使用されます。

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

使用可能な時系列バーの数、リストに保存されているバーの数、およびサーバとのデータ同期のフラグが初期化リストでリセットされます
プログラムタイプが設定されバーオブジェクトのリストがクリアされバーインデックスによる並び替えフラグが設定されます
後に現在の銘柄と時間枠がリストに設定されます
最後に、SetAmountUsedData()バーオブジェクトリストに必要なバーの数を設定するメソッドの結果が同期フラグに設定されます。リストは、コンストラクタのamount入力で指定された必要な履歴の深さを受け取ります。バーオブジェクトのリストを作成したら、プログラムからSyncData()メソッドを使用して、サーバとの同期を確認してください。

以下は、銘柄と時間枠を設定するメソッドです。

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

このメソッドは銘柄と時間枠を渡し、渡された値の有効性が確認されます。最終的に、現在の銘柄と時間枠、またはメソッドに渡された銘柄と時間枠のいずれかが設定されます。次に、変数に新しく保存された銘柄と時間枠が、バーリストの「新しいバー」オブジェクトに設定されます

以下は、バーオブジェクトリストに使用される時系列データの数を設定するメソッドです。

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

このメソッドは、バーオブジェクトリストに必要な量のデータと、現在の時系列の棒の総数(指標用)を受け取ります。
次に、プログラムタイプが確認され、m_bars変数の利用可能な履歴量のソースが、メソッドに渡された値(現在の指標銘柄と時系列)または環境から定義されます。次に、利用可能な必要な履歴の量に基づいて、m_amount変数に設定する値を定義します。

以下は、銘柄と時系列のデータをサーバデータと同期するメソッドです。

//+------------------------------------------------------------------+
//|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;
  }
//+------------------------------------------------------------------+

メソッドのロジックは、コードのコメントで説明されています。ここではすべてが明確であると思います。

バーオブジェクトリストの作成メソッドおよび更新メソッドは、リストで詳細にコメントされています。説明のために多くのスペースをとらないように、それら全体を考慮してみましょう。これらのメソッドについて質問がある場合は、コメントでお気軽に質問してください。

//+------------------------------------------------------------------+
//| 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]);
  }
//+------------------------------------------------------------------+

時系列リストを作成するには、まず、必要な履歴のサイズを設定し、SyncData()メソッドを使用してサーバとの同期のフラグを取得します。次にCreate()メソッドを呼び出します。バーオブジェクトリストデータを更新するには、各ティックで必ずRefresh()メソッドを呼び出します。このメソッドは、新しいバーが開かれると独自に検出し、新しいバーオブジェクトを時系列リストに追加します。リストのサイズは常にSyncData()メソッドで設定されたレベルのままになるように、最も古いバーオブジェクトがバーオブジェクトのリストから削除されます。

データを管理するには、時系列リストからバーオブジェクトを取得する必要があります。インデックスによる並び替えのフラグ(SORT_BY_BAR_INDEX)が時系列リストに設定されている場合、リスト内のバーオブジェクトのシーケンスは、時系列内の実際の棒の位置に対応します。ただし、リストに別の並べ替えフラグを設定すると、リスト内のオブジェクトの順序は、時系列内の実際のバーの位置に対応しなくなって、リストが並べ替えられるプロパティの昇順で配置されます。したがって、バーオブジェクトリストからオブジェクトを選択するには、時系列のインデックスによってオブジェクトバーを返すメソッドと、バーオブジェクトリストのインデックスによってオブジェクトバーを返すメソッドの2つがあります。

これら2つのメソッドについて考えてみましょう。

以下は、バーオブジェクトリスト内のバーオブジェクトをインデックスで返すメソッドです。

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

必要なバーオブジェクトのインデックスがメソッドに渡されます。渡されたインデックスは時系列と同じ方向を意味します。ゼロのインデックスはリストの最後のオブジェクトを示します。ただし、リスト内のオブジェクトはゼロインデックスからlist.Total()-1まで保存されます。つまり、リスト内の最新のバーを取得するには、list.Total()-1インデックスでリクエストします。 チャートの右端のバーを取得するには、インデックス0(逆インデックス)で要求します。
したがって、インデックスは適切な方法で再計算されます。渡されたインデックス1の数がリストサイズから差し引かれ、時系列のように、インデックスの方向に従って、計算されたインデックスに基づいてバーオブジェクトが返されます。

以下は、時系列のインデックスによってバーオブジェクトを返すメソッドです。

//+------------------------------------------------------------------+
//| 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));
  }
//+------------------------------------------------------------------+

必要なバーオブジェクトのインデックスがメソッドに渡されます。渡されたインデックスは、時系列と同じ方向を意味します。
時系列と同じバーインデックスを持つオブジェクトを取得するには、BAR_PROP_INDEXプロパティで選択する必要があります。バーオブジェクトのリストが必要なインデックスを持つバーを備えている場合、listは、返される必要がある単一のオブジェクトを備えています。
そのようなオブジェクトがない場合、NULLが返されます。エラーが発生した場合、これらのメソッドはどちらもNULLを返します。

以下は、インデックスによってバーオブジェクトのリストからバーオブジェクトの基本的なプロパティを返すメソッドです。

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

メソッドはバーインデックスリクエストされたインデックスが時系列のようにインデックスの方向に対応することを示すフラグ(true)を受け取ります
フラグ値に基づいて、GetBarBySeriesIndex()またはGetBarByListIndex()メソッドを使用してバーオブジェクトを取得します。その後、メソッドでリクエストされたプロパティの値を返します

ここで考慮されていない残りのCSeriesクラスメソッドは、クラスメンバ変数の値を設定または返すために使用されます。

現在の記事でおこなったことを確認するには、作成されたクラスについて外部プログラムに通知する必要があります。これには、CEngineライブラリメインオブジェクトファイルにCSeriesクラスファイルをインクルードします。

//+------------------------------------------------------------------+
//|                                                       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"
//+------------------------------------------------------------------+

これで、テストEAでCSeriesクラスタイプの変数を定義して、指定された数のバーオブジェクトを含む時系列リストを作成および使用できます(現在の実装では、リストはテストのみに使用されます)。やってみましょう。

テスト

テストを実行するには、前のシリーズ最終記事のEAを使用して、 \MQL5\Experts\TestDoEasy\Part35\TestDoEasyPart35.mq5として保存します。


CSeriesクラスオブジェクトの2つの変数(テスト用)を作成します: M1(2バー)と現在の時間枠(10バー)です。OnInit()ハンドラで、テストに必要なパラメータをすべて設定して、3つのリストを表示します。

  1. ローソク足のサイズ(バーの高値から安値)で並び替えられた現在の時系列のすべてのバーのリスト - バーオブジェクトの短い説明。
  2. バーのインデックス(バー0~9)で並び替えられた現在の時系列のすべてのバーのリスト - バーオブジェクトの短い説明。
  3. 現在の時系列の適切なインデックス1(前のバー)に対応するすべてのバーオブジェクトプロパティのリスト — すべてのバープロパティの完全なリスト。

OnTick()ハンドラで、各ティックの両方の時系列を更新し、これら2つの時系列リストのbar1と現在の2つの時系列のそれぞれに新しいバーを開くことに関するエントリを表示します。操作ログエントリとは別に、現在の時系列の新しいバーを開くときに標準の「news.wav」サウンドを再生します。

グローバルEA変数のリストで、CSeriesクラスタイプの2つの変数を定義します現在の時間枠の時系列リスト変数および 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;
//+------------------------------------------------------------------+

EAのOnInit()ハンドラで、時系列オブジェクトの両方の変数に必要なプロパティを設定し現在の時間枠のバーオブジェクトの作成リストにデータ全体を表示しますМ1の場合、リスト作成の成功に関するメッセージを表示するだけです

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

OnTick()ハンドラで、各ティックでCSeriesクラスオブジェクトの時系列リストを更新する一方、「新しいバー」イベント中には 2つのリストに対してこのイベントに関するメッセージを表示ます。さらに、現在の時間枠で新しいバーを開くイベントの音声を再生します

//+------------------------------------------------------------------+
//| 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)));
     }
  }
//+------------------------------------------------------------------+

EAをコンパイルし、銘柄チャートで起動します。
まず、操作ログは2本のバーのM1時系列リストの作成の開始のエントリを表示してから、ローソク足のサイズで並び替えられたバーのリストを表示します。次に、時系列のバーインデックスの順に並べ替えられたバーのリスト、最後に時系列のインデックスが1のすべてのオブジェクトバープロパティを表示します。

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]) =============


次に、М5でテスターのビジュアルモードでEAを起動し、新しいバーを開くことに関するテスター操作ログメッセージを確認します。

ご覧のように、5番目ごとのメッセージはМ5で新しいバーを開くことに関するものであり、М1で新しいバーを開くことに関するメッセージと断続的です。

次の段階

次の記事では、バーリストのコレクションクラスを作成します。 

現在のバージョンのライブラリのすべてのファイルは、テスト用EAファイルと一緒に以下に添付されているので、テストするにはダウンロードしてください。
質問や提案はコメント欄にお願いします。

目次に戻る

以前の連載記事:

第1部: 概念、データ管理
第2部: 過去の注文と取引のコレクション
第3部: 注文と取引のコレクション、検索と並び替え
第4部: 取引イベント概念
第5部: 取引イベントのクラスとコレクション取引イベントのプログラムへの送信
第6部: ネッティング勘定イベント
第7部: StopLimit注文発動イベント、注文およびポジション変更イベントの機能の準備
第8部: 注文とポジションの変更イベント
第9部: MQL4との互換性 - データの準備
第10部: MQL4との互換性 - ポジションオープンイベントと指値注文発動イベント
第11部: MQL4との互換性 - ポジション決済イベント
第12部: 口座オブジェクトクラスと口座オブジェクトコレクション
第13部: 口座オブジェクトイベント第14部: 銘柄オブジェクト
第15部: 銘柄オブジェクトコレクション
第16部: 銘柄コレクションイベント
第17部: ライブラリオブジェクトの相互作用
第18部:口座と他のライブラリオブジェクトの相互作用
第19部:ライブラリメッセージのクラス
第20部:プログラムリソースの作成と格納
第21部:取引クラス - 基本クロスプラットフォーム取引オブジェクト
第22部:取引クラス - 基本取引クラス、制限の検証
第23部:取引クラス - 基本取引クラス、パラメータ有効性の検証
第24部:取引クラス - 基本取引クラス、無効なパラメータの自動修正
第25部:取引クラス - 取引サーバによって返されたエラーを処理する基本取引クラス
第26部:未決取引リクエストの使用 - 初期実装(ポジションを開く)
第27部:未決取引リクエストの使用 - 指数注文
第28部:未決取引リクエストの使用 - 決済、削除、変更
第29部:未決取引リクエストの使用 - リクエストオブジェクトクラス
第30部:未決取引リクエスト - リクエストオブジェクトの管理
第31部:未決取引リクエスト - 特定の条件下でポジションを開く
第32部:未決取引リクエスト - 特定の条件下で指値注文を開く
第33部:未決取引リクエスト - 特定の条件下でのポジションの決済(完全、部分または反対のポジションによる)第34部:未決取引リクエスト - 特定の条件下での注文とポジションの削除と変更


MetaQuotes Software Corp.によりロシア語から翻訳された
元の記事: https://www.mql5.com/ru/articles/7594

添付されたファイル |
MQL5.zip (3683.65 KB)
MQL4.zip (3683.65 KB)
トレードシグナルの多通貨監視(パート2):アプリケーションのビジュアル部分の実装 トレードシグナルの多通貨監視(パート2):アプリケーションのビジュアル部分の実装

前回の記事では、アプリケーションフレームワークを作成し、以降のすべてのタスクの基礎としました。 このパートでは、開発工程を進めます: アプリケーションのビジュアル部分を作成し、インターフェイス要素の基本的な相互作用を構成します。

ネットワーク関数の適用、または DLL を使用しない MySQL: パートII - シグナル特性の変化を監視するプログラム ネットワーク関数の適用、または DLL を使用しない MySQL: パートII - シグナル特性の変化を監視するプログラム

前のパートでは、MySQLコネクタの実装をしました。 この記事では、シグナルプロパティを収集するためのサービスを実装し、時間の経過とともに変化を表示するためのプログラムを実装することで、そのアプリケーションを検討します。 実装された例は、ユーザーがシグナルのWebページに表示されないプロパティの変化を観察する必要がある場合、実用的な意味を持ちます。

連続ウォークフォワード最適化(パート4):最適化マネージャ(オートオプティマイザ) 連続ウォークフォワード最適化(パート4):最適化マネージャ(オートオプティマイザ)

この記事の主な目的は、アプリケーションとその機能を操作するメカニズムについて説明することです。 したがって、この記事は、アプリケーションの使用方法に関する説明書としても使うことができます。 アプリケーションの使用法においてありがちな落とし穴と詳細を扱っています。

このプロジェクトは、収益性の高いトレーディングロボットを作成する手助けになります! 少なくとも、そうなるでしょう。 このプロジェクトは、収益性の高いトレーディングロボットを作成する手助けになります! 少なくとも、そうなるでしょう。

大きなプログラムは小さなファイルから始まり、関数やオブジェクトを追加し続けるにつれてサイズが大きくなります。 ほとんどのトレードロボット開発者は、この問題を処理するためにインクルードファイルを利用しています。 しかし、より良い解決策があります。:それは、プロジェクト内の任意のトレードアプリケーションの開発を開始することです。 そうする理由はたくさんあります。