DoEasyライブラリの時系列(第38部): 時系列コレクション-リアルタイムの更新とプログラムからのデータへのアクセス
Artyom Trishkin | 27 7月, 2020
内容
概念
以前の記事では、チャート期間と銘柄の時系列を作成することに焦点を当て、プログラムで使用されるすべての銘柄の本格的な時系列コレクションクラスを作成し、高速な検索と並べ替えを実行できるように時系列の履歴データを入力する方法を学びました 。
このようなツールを使用すると、履歴内の価格データのさまざまな組み合わせを検索して比較できます。ただし、使用されている各銘柄の新しいティックごとに実行する必要がある現在のデータの更新についても考慮する必要があります。
最も単純なバージョンでも、プログラムのOnTimer()ミリ秒ハンドラですべての時系列を更新できます。ただし、これにより、時系列データを常にタイマーカウンターに従って正確に更新する必要があるかどうかという疑問が生じます。結局のところ、データは新しいティックの到着時にプログラムで変更されます。新しいティックの到着に関係なく単純にデータを更新するのは誤りです。これはパフォーマンスの点で非合理的です。
EAのOnTick()ハンドラまたは指標の現在の銘柄のOnCalculate()ハンドラに新しいティックが到着することは常にわかっていますが、これは 別の銘柄で起動されたプログラムによって追跡される他の銘柄には当てはまりません。このタスクでは、EA内または指標で必要なイベントを追跡する必要があります。
ここで、現在のライブラリのニーズを満たす最も簡単なオプションは、以前のティック時間と現在のティック時間を比較することです。以前のティック時間が現在のティックと異なる場合、新しいティックはプログラムによって追跡された銘柄に到達したが、そのネイティブではない(プログラムは別の銘柄のチャートで起動される)と見なされます。
「新しいティック」クラスとプログラムで使用されるすべての時系列のリアルタイム更新を開発する前に、既存のクラスを少し改善しましょう。
時系列クラスの改善
まず、Datas.mqhファイルはライブラリの新しいメッセージインデックスを受け取ります。
//--- CTimeSeries MSG_LIB_TEXT_TS_TEXT_FIRST_SET_SYMBOL, // First, set a symbol using SetSymbol() MSG_LIB_TEXT_TS_TEXT_IS_NOT_USE, // Timeseries is not used. Set the flag using SetAvailable() MSG_LIB_TEXT_TS_TEXT_UNKNOWN_TIMEFRAME, // Unknown timeframe
また、新しく追加したインデックスに対応するメッセージテキストも受け取ります。
{"Сначала нужно установить символ при помощи SetSymbol()","First you need to set Symbol using SetSymbol()"}, {"Таймсерия не используется. Нужно установить флаг использования при помощи SetAvailable()","Timeseries not used. Need to set usage flag using SetAvailable()"}, {"Неизвестный таймфрейм","Unknown timeframe"},
すべてのライブラリオブジェクトの基本オブジェクトのCBaseObjクラスには2つの変数があります。
//+------------------------------------------------------------------+ //| Base object class for all library objects | //+------------------------------------------------------------------+ class CBaseObj : public CObject { protected: ENUM_LOG_LEVEL m_log_level; // Logging level ENUM_PROGRAM_TYPE m_program; // Program type bool m_first_start; // First launch flag bool m_use_sound; // Flag of playing the sound set for an object bool m_available; // Flag of using a descendant object in the program int m_global_error; // Global error code long m_chart_id_main; // Control program chart ID long m_chart_id; // Chart ID string m_name; // Object name string m_folder_name; // Name of the folder storing CBaseObj descendant objects string m_sound_name; // Object sound file name int m_type; // Object type (corresponds to the collection IDs) public:
m_chart_id_main変数は、制御プログラムのチャートIDを格納します。これは、プログラムが起動された銘柄のチャートです。グラフは、ライブラリのコレクションとオブジェクトに登録されているすべてのイベントを取得するものです。
m_chart_idはCBaseObjクラスから派生したオブジェクトが何らかの形で関連しているチャートのIDを格納します。このプロパティはまだどこにも使用されていません。その時は後で来るでしょう。
m_chart_idより後にm_chart_id_main変数を追加したため、すべてのメッセージはm_chart_id変数に設定されたチャートIDに送信されます。これを修正しました。現在、すべての現在のチャートIDはm_chart_id_main変数に設定されています。ライブラリから制御プログラムチャートにメッセージを送信するすべてのクラスが変更されました。「m_chart_id」のすべてのインスタンスが「m_chart_id_main」に置き換えられました。
このような変更は、\MQL5\Include\DoEasy\Objects\Events\フォルダのすべてのイベントクラス、およびAccountsCollection.mqh、EventsCollection.mqh、SymbolsCollection.mqhの各コレクションクラスファイルに加えられました。 添付ファイルですべての変更を確認できます。
時系列コレクションから指定したバーのデータを表示するには、CBarクラスのバーパラメータの説明テキストを\MQL5\Include\DoEasy\Objects\Series\Bar.mqhに追加します。
オブジェクトプロパティの説明を含むコードブロックで、バーパラメータのテキスト説明を作成するメソッドを宣言します。
//+------------------------------------------------------------------+ //| 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 (1) short name and (2) description of bar object parameters virtual string Header(void); string ParameterDescription(void); //--- }; //+------------------------------------------------------------------+
クラス本体以外に、バーパラメータのテキスト説明を作成するメソッドを実装し、銘柄に短いバーの説明を表示するメソッドの実装を変更します。
//+------------------------------------------------------------------+ //| Return the description of the bar object parameters | //+------------------------------------------------------------------+ string CBar::ParameterDescription(void) { int dg=(this.m_digits>0 ? this.m_digits : 1); return ( ::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() ); } //+------------------------------------------------------------------+ //| Display a short bar description in the journal | //+------------------------------------------------------------------+ void CBar::PrintShort(void) { ::Print(this.Header(),": ",this.ParameterDescription()); } //+------------------------------------------------------------------+
ここでは、操作ログにバーパラメータを表示するメソッドからパラメータ記述コードを削除し、テキストメッセージを返す新しいメソッドに配置しました。操作ログにバーパラメータを表示する場合、短いバーオブジェクト名とそのパラメータで構成される複合メッセージを表示します。そのテキストの説明は、新しいメソッドParameterDescription()で生成されます。
「非ネイティブ」時系列(プログラムが起動されたものではない)を更新するために、「新しいティック」クラスを作成し、プログラムで使用される各銘柄に対して「新しいティック」イベントの到着時にのみそのような銘柄のデータを更新することにしました 。
「新しいティック」クラスとデータの更新
\MQL5\Include\DoEasy\Objects\で、すべてのCBaseObjライブラリオブジェクトの基本オブジェクトから派生したCNewTickObjクラスのNewTickObj.mqhファイルを備えたTicks\フォルダを作成し(ファイルはクラスファイルに含まれています)、 必要なデータを入力します。
//+------------------------------------------------------------------+ //| NewTickObj.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 "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+ //| "New tick" class | //+------------------------------------------------------------------+ class CNewTickObj : public CBaseObj { private: MqlTick m_tick; // Structure of the current prices MqlTick m_tick_prev; // Structure of the current prices during the previous check string m_symbol; // Object symbol bool m_new_tick; // New tick flag public: //--- Set a symbol void SetSymbol(const string symbol) { this.m_symbol=symbol; } //--- Return the new tick flag bool IsNewTick(void); //--- Update price data in the tick structure and set the "New tick" event flag if necessary void Refresh(void) { this.m_new_tick=this.IsNewTick(); } //--- Constructors CNewTickObj(void){;} CNewTickObj(const string symbol); }; //+------------------------------------------------------------------+
m_tick変数には、最後に到着したティックの価格データが格納されます。
m_tick_prev変数は前のティックの価格データを格納します。
m_symbol変数には、新しいティックが追跡される銘柄が格納されます。
m_new_tick変数の新しい目盛りフラグは後で使用されます。
現在のライブラリのニーズに対して、銘柄の「新しいティック」イベントは、IsNewTick()メソッドによって定義されます。
//+------------------------------------------------------------------+ //| Return the new tick flag | //+------------------------------------------------------------------+ bool CNewTickObj::IsNewTick(void) { //--- If failed to get the current prices to the tick structure, return 'false' if(!::SymbolInfoTick(this.m_symbol,this.m_tick)) return false; //--- If this is the first launch, copy data of the obtained tick to the previous tick data //--- reset the first launch flag and return 'false' if(this.m_first_start) { this.m_tick_prev=this.m_tick; this.m_first_start=false; return false; } //--- If the time of a new tick is not equal to the time of a tick during the previous check - //--- copy data of the obtained tick to the previous tick data and return 'true' if(this.m_tick.time_msc!=this.m_tick_prev.time_msc) { this.m_tick_prev=this.m_tick; return true; } //--- In all other cases, return 'false' return false; } //+------------------------------------------------------------------+
クラスには2つの定義済みコンストラクタがあります。
- パラメータのないデフォルトのコンストラクタは、別のクラス内の「新しいティック」オブジェクトを定義するために使用されます。この場合、SetSymbol()クラスメソッドを使用して、「新しいティック」イベントが定義されているCNewTickObjクラスオブジェクトの銘柄を設定します。
- パラメータコンストラクタを使用して、new演算子を介してクラスオブジェクトを作成します。この場合、オブジェクトが作成される銘柄は、オブジェクトの作成時にすぐに指定できます。
//+------------------------------------------------------------------+ //| Parametric constructor CNewTickObj | //+------------------------------------------------------------------+ CNewTickObj::CNewTickObj(const string symbol) : m_symbol(symbol) { //--- Reset the structures of the new and previous ticks ::ZeroMemory(this.m_tick); ::ZeroMemory(this.m_tick_prev); //--- If managed to get the current prices to the tick structure, //--- copy data of the obtained tick to the previous tick data and reset the first launch flag if(::SymbolInfoTick(this.m_symbol,this.m_tick)) { this.m_tick_prev=this.m_tick; this.m_first_start=false; } } //+------------------------------------------------------------------+
これは、新しいティックオブジェクトのクラス全体です。考え方は単純です。ティック構造に価格を取得し、到着したティックの時間を前のティックの時間と比較します。
これらの時間が等しくない場合は、新しいティックが到着しています。
EAではティックがスキップされる場合がありますが、ここでは重要ではありません。タイマーで常に確認するのではなく、新しいティックが到着したときにのみデータを更新するために、タイマーで「非ネイティブ」銘柄の新しいティックを追跡することができます。
指標がバッチで到着できるすべてのティックを追跡する場合、指標が起動された銘柄の現在の時系列データの更新は、OnCalculate()ハンドラで行う必要があります。「非ネイティブ」銘柄の新しいティックはタイマーで追跡されます(「非ネイティブ」銘柄の新しいティックイベントはOnOnCalculate()で受信できません)。したがって、 「非ネイティブ」銘柄の新しいティックと以前のティックにより、時系列の時系列データが更新されます。
CSeriesオブジェクトは、「新しいバー」イベントを制御プログラムに送信します。これにより、プログラムの任意の時系列からそのようなイベントを取得し、それらに応答できるようになります。
Defines.mqhファイルの最後に、考えられる時系列オブジェクトイベントのリストを含む新しい列挙を追加します。
//+------------------------------------------------------------------+ //| List of possible timeseries events | //+------------------------------------------------------------------+ enum ENUM_SERIES_EVENT { SERIES_EVENTS_NO_EVENT = SYMBOL_EVENTS_NEXT_CODE, // no event SERIES_EVENTS_NEW_BAR, // "New bar" event }; #define SERIES_EVENTS_NEXT_CODE (SERIES_EVENTS_NEW_BAR+1) // Code of the next event after the "New bar" event //+------------------------------------------------------------------+
ここでは、時系列イベントの状態はまだ2つしかありません。「イベントなし」と「新しいバー」イベントです。これらの列挙定数は、(CSeries 時系列の)バーコレクションリストで指定されたプロパティによってバーオブジェクトを検索するために必要です。
時系列オブジェクトはライブラリタイマーで更新されるため、時系列オブジェクトコレクション更新タイマーのパラメータを時系列コレクションリストIDとともにDefines.mqhリストファイルに追加します。
//--- Trading class timer parameters #define COLLECTION_REQ_PAUSE (300) // Trading class timer pause in milliseconds #define COLLECTION_REQ_COUNTER_STEP (16) // Trading class timer counter increment #define COLLECTION_REQ_COUNTER_ID (5) // Trading class timer counter ID //--- Parameters of the timeseries collection timer #define COLLECTION_TS_PAUSE (32) // Timeseries collection timer pause in milliseconds #define COLLECTION_TS_COUNTER_STEP (16) // Account timer counter increment #define COLLECTION_TS_COUNTER_ID (6) // Timeseries timer counter ID //--- Collection list IDs #define COLLECTION_HISTORY_ID (0x777A) // Historical collection list ID #define COLLECTION_MARKET_ID (0x777B) // Market collection list ID #define COLLECTION_EVENTS_ID (0x777C) // Event collection list ID #define COLLECTION_ACCOUNT_ID (0x777D) // Account collection list ID #define COLLECTION_SYMBOLS_ID (0x777E) // Symbol collection list ID #define COLLECTION_SERIES_ID (0x777F) // Timeseries collection list ID //--- Data parameters for file operations
CEngineライブラリベースオブジェクトを作成するときにコレクションタイマーパラメータを考慮しましたが、コレクションIDの目的は、ライブラリ構造を再配置するときに説明しました。
時系列オブジェクトはリストに属するバーオブジェクトへのポインタを含むリストであるため、時系列コレクションIDをすぐにバーオブジェクトに割り当てます。
\MQL5\Include\DoEasy\Objects\Series\Bar.mqhを再び開いてオブジェクトタイプを両方のコンストラクタに追加します。
//+------------------------------------------------------------------+ //| Constructor 1 | //+------------------------------------------------------------------+ CBar::CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index) { this.m_type=COLLECTION_SERIES_ID; MqlRates rates_array[1]; this.SetSymbolPeriod(symbol,timeframe,index); ::ResetLastError(); //--- If failed to write bar data to the MqlRates 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]); } //+------------------------------------------------------------------+ //| Constructor 2 | //+------------------------------------------------------------------+ CBar::CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const MqlRates &rates) { this.m_type=COLLECTION_SERIES_ID; 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); } //+------------------------------------------------------------------+
次に、\MQL5\Include\DoEasy\Objects\Series\Series.mqhにあるCSeries時系列オブジェクトクラスを改善します。
クラスのpublicセクションで、コントロールプログラムチャートにイベントを送信するための新しいメソッドを宣言します。
//--- (1) Create and (2) update the timeseries list int Create(const uint required=0); void Refresh(const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0); //--- Create and send the "New bar" event to the control program chart void SendEvent(void); //--- Return the timeseries name string Header(void); //--- Display (1) the timeseries description and (2) the brief timeseries description in the journal void Print(void); void PrintShort(void); //--- Constructors CSeries(void); CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0); }; //+------------------------------------------------------------------+
クラスリストの最後に、宣言したメソッドを実装します。
//+------------------------------------------------------------------+ //| Create and send the "New bar" event | //| to the control program chart | //+------------------------------------------------------------------+ void CSeries::SendEvent(void) { ::EventChartCustom(this.m_chart_id_main,SERIES_EVENTS_NEW_BAR,this.Time(0),this.Timeframe(),this.Symbol()); } //+------------------------------------------------------------------+
ここでは、イベントを作成して制御プログラムチャートに送信します。イベントは以下で構成されています。
イベントレシーバーチャートID
イベントID(新規バー)
新しいバーが開いた時間をlongイベントパラメータとして送信
イベントが発生したチャートの時間枠をdoubleイベントパラメータとして送信
銘柄の名前(時間枠でイベントが発生したもの)をstringパラメータとして送信
プログラムでの時系列の使用を示すフラグのチェックを時系列データ同期メソッドに追加します。
//+------------------------------------------------------------------+ //|Synchronize symbol and timeframe data with server data | //+------------------------------------------------------------------+ bool CSeries::SyncData(const uint required,const uint rates_total) { //--- If the timeseries is not used, notify of that and exit if(!this.m_available) { ::Print(DFUN,this.m_symbol," ",TimeframeDescription(this.m_timeframe),": ",CMessage::Text(MSG_LIB_TEXT_TS_TEXT_IS_NOT_USE)); return false; } //--- If managed to obtain the available number of bars in the timeseries
つまり、プログラムで時系列使用のフラグが設定されていない場合は、同期する必要はありません。使用状況フラグが設定されていないときに時系列が必要になる場合もあります。したがって、適切なメッセージが操作ログに送信されます。
同じチェックを 時系列作成メソッドに実装します。
//+------------------------------------------------------------------+ //| Create the timeseries list | //+------------------------------------------------------------------+ int CSeries::Create(const uint required=0) { //--- If the timeseries is not used, notify of that and return zero if(!this.m_available) { ::Print(DFUN,this.m_symbol," ",TimeframeDescription(this.m_timeframe),": ",CMessage::Text(MSG_LIB_TEXT_TS_TEXT_IS_NOT_USE)); return 0; } //--- If the required history depth is not set for the list yet,
時系列インデックスによってバーオブジェクトを返すメソッドがクラスで修正されました。以前は、メソッドは次のように表示されていました。
//+------------------------------------------------------------------+ //| 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)); } //+------------------------------------------------------------------+
つまり、必要なバーのコピーを含む新しいリストが作成され、そのコピーが返されました。リクエストされたバーのデータを単に受信したいだけならこれで十分ですが、バーのプロパティを変更する必要がある場合、このメソッドは、元のオブジェクトのバーのプロパティではなくバーのコピーのプロパティに変更が加えられるため機能しません。
新しいティックの到着時に現在のバーをリアルタイムで更新したいので、リストのコピーからバーではなく、バーコレクションリストにある元のバーオブジェクトにポインタを返すようにメソッドを変更しました。
//+------------------------------------------------------------------+ //| Return the bar object by index in the timeseries | //+------------------------------------------------------------------+ CBar *CSeries::GetBarBySeriesIndex(const uint index) { CBar *tmp=new CBar(this.m_symbol,this.m_timeframe,index); if(tmp==NULL) return NULL; this.m_list_series.Sort(SORT_BY_BAR_INDEX); int idx=this.m_list_series.Search(tmp); delete tmp; CBar *bar=this.m_list_series.At(idx); return(bar!=NULL ? bar : NULL); } //+------------------------------------------------------------------+
ここでは、現在の時系列オブジェクトチャートの銘柄と期間 、およびメソッドに渡されたバーインデックスを使用して、一時的なバーオブジェクトを作成します。 チャート時系列のバーインデックスは、バーインデックスで並び替えられた時系列リストで同じオブジェクトを検索するために必要です。 同じ時系列インデックスを持つバーを検索しているときに、リスト内のインデックスを取得 し(tこのインデックスは、リスト内のバーオブジェクトへのポインタを取得するために使用されます)、オブジェクトへのポインタを返します。
これで、このメソッドは、時系列リスト内の元のバーオブジェクトへのポインタを返します。リアルタイムのデータ更新中に変更できます。
今度は、CTimeSeries時系列オブジェクトクラスを改善して、新しいティックを追跡し、そのようなイベントを定義するときにデータを更新します。クラスオブジェクトは、1つの銘柄で使用されるすべてのチャート期間の時系列のセットです。これは、CTimeSeries時系列オブジェクト銘柄によって新しいティックを取得すると、オブジェクトに属するすべての期間のCSeries時系列オブジェクトデータの更新が開始されるため、オブジェクトが「新しいティック」クラスオブジェクトに最適な場所であることを意味します 。
新しいティック」オブジェクトクラスファイルを時系列オブジェクトクラスファイルに含めます。クラスのprivateセクションで、「新しいティック」クラスオブジェクトを定義します。
クラスのpublicセクションで、現在の時系列オブジェクト銘柄に新しいティックフラグを返すメソッドを追加します
。
//+------------------------------------------------------------------+ //| TimeSeries.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 "Series.mqh" #include "..\Ticks\NewTickObj.mqh" //+------------------------------------------------------------------+ //| Symbol timeseries class | //+------------------------------------------------------------------+ class CTimeSeries : public CBaseObj { private: string m_symbol; // Timeseries symbol CNewTickObj m_new_tick; // "New tick" object CArrayObj m_list_series; // List of timeseries by timeframes datetime m_server_firstdate; // The very first date in history by a server symbol datetime m_terminal_firstdate; // The very first date in history by a symbol in the client terminal //--- Return (1) the timeframe index in the list and (2) the timeframe by the list index char IndexTimeframe(const ENUM_TIMEFRAMES timeframe) const { return IndexEnumTimeframe(timeframe)-1; } ENUM_TIMEFRAMES TimeframeByIndex(const uchar index) const { return TimeframeByEnumIndex(uchar(index+1)); } //--- Set the very first date in history by symbol on the server and in the client terminal void SetTerminalServerDate(void) { this.m_server_firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,::Period(),SERIES_SERVER_FIRSTDATE); this.m_terminal_firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,::Period(),SERIES_TERMINAL_FIRSTDATE); } public: //--- Return (1) oneself, (2) the full list of timeseries, (3) specified timeseries object and (4) timeseries object by index CTimeSeries *GetObject(void) { return &this; } CArrayObj *GetListSeries(void) { return &this.m_list_series; } CSeries *GetSeries(const ENUM_TIMEFRAMES timeframe) { return this.m_list_series.At(this.IndexTimeframe(timeframe)); } CSeries *GetSeriesByIndex(const uchar index) { return this.m_list_series.At(index); } //--- Set/return timeseries symbol void SetSymbol(const string symbol) { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); } string Symbol(void) const { return this.m_symbol; } //--- Set the history depth (1) of a specified timeseries and (2) of all applied symbol timeseries bool SetRequiredUsedData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0); bool SetRequiredAllUsedData(const uint required=0,const int rates_total=0); //--- Return the flag of data synchronization with the server data of the (1) specified timeseries, (2) all timeseries bool SyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0); bool SyncAllData(const uint required=0,const int rates_total=0); //--- Return the very first date in history by symbol (1) on the server, (2) in the client terminal and (3) the new tick flag datetime ServerFirstDate(void) const { return this.m_server_firstdate; } datetime TerminalFirstDate(void) const { return this.m_terminal_firstdate; } bool IsNewTick(void) { return this.m_new_tick.IsNewTick(); } //--- Create (1) the specified timeseries list and (2) all timeseries lists bool Create(const ENUM_TIMEFRAMES timeframe,const uint required=0); bool CreateAll(const uint required=0); //--- Update (1) the specified timeseries list and (2) all timeseries lists void Refresh(const ENUM_TIMEFRAMES timeframe, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0); void RefreshAll(const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0); //--- Compare CTimeSeries objects (by symbol) virtual int Compare(const CObject *node,const int mode=0) const; //--- Display (1) description and (2) short symbol timeseries description in the journal void Print(const bool created=true); void PrintShort(const bool created=true); //--- Constructors CTimeSeries(void){;} CTimeSeries(const string symbol); }; //+------------------------------------------------------------------+
IsNewTick()メソッドは、m_new_tick「新しいティック」オブジェクトから新しいティックのデータをリクエストした結果を返します。
データが返される銘柄を「新しいティック」クラスオブジェクトに知らせるには、クラスコンストラクタで「新しいティック」クラスオブジェクトの銘柄を設定し、すぐに 現在のティック価格を読み取るためのデータを更新する必要があります。
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTimeSeries::CTimeSeries(const string symbol) : m_symbol(symbol) { this.m_list_series.Clear(); this.m_list_series.Sort(); for(int i=0;i<21;i++) { ENUM_TIMEFRAMES timeframe=this.TimeframeByIndex((uchar)i); CSeries *series_obj=new CSeries(this.m_symbol,timeframe); this.m_list_series.Add(series_obj); } this.SetTerminalServerDate(); this.m_new_tick.SetSymbol(this.m_symbol); this.m_new_tick.Refresh(); } //+------------------------------------------------------------------+
データ同期フラグを返すメソッドの時系列使用フラグを確認します。フラグがオフの場合、時系列はプログラムで使用されず、処理されるべきではありません。
//+------------------------------------------------------------------+ //| Return the flag of data synchronization | //| with the server data | //+------------------------------------------------------------------+ bool CTimeSeries::SyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0) { if(this.m_symbol==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_FIRST_SET_SYMBOL)); return false; } CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe)); if(series_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_FAILED_GET_SERIES_OBJ),this.m_symbol," ",TimeframeDescription(timeframe)); return false; } if(!series_obj.IsAvailable()) return false; return series_obj.SyncData(required,rates_total); } //+------------------------------------------------------------------+ //| Return the flag of data synchronization | //| of all timeseries with the server data | //+------------------------------------------------------------------+ bool CTimeSeries::SyncAllData(const uint required=0,const int rates_total=0) { if(this.m_symbol==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_FIRST_SET_SYMBOL)); return false; } bool res=true; for(int i=0;i<21;i++) { CSeries *series_obj=this.m_list_series.At(i); if(series_obj==NULL || !series_obj.IsAvailable()) continue; res &=series_obj.SyncData(required,rates_total); } return res; } //+------------------------------------------------------------------+
時系列作成メソッドで時系列使用フラグを強制的に設定します。
//+------------------------------------------------------------------+ //| Create a specified timeseries list | //+------------------------------------------------------------------+ bool CTimeSeries::Create(const ENUM_TIMEFRAMES timeframe,const uint required=0) { CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe)); if(series_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_FAILED_GET_SERIES_OBJ),this.m_symbol," ",TimeframeDescription(timeframe)); return false; } if(series_obj.RequiredUsedData()==0) { ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA)); return false; } series_obj.SetAvailable(true); return(series_obj.Create(required)>0); } //+------------------------------------------------------------------+ //| Create all timeseries lists | //+------------------------------------------------------------------+ bool CTimeSeries::CreateAll(const uint required=0) { bool res=true; for(int i=0;i<21;i++) { CSeries *series_obj=this.m_list_series.At(i); if(series_obj==NULL || series_obj.RequiredUsedData()==0) continue; series_obj.SetAvailable(true); res &=(series_obj.Create(required)>0); } return res; } //+------------------------------------------------------------------+
時系列の更新メソッド(「新しいバー」イベントが検出された場合)で、上記のCSeries 時系列オブジェクトのSendEvent()メソッドを使用して、イベントに関するメッセージの送信を制御プログラムチャートに追加します。
//+------------------------------------------------------------------+ //| Update a specified timeseries list | //+------------------------------------------------------------------+ void CTimeSeries::Refresh(const ENUM_TIMEFRAMES timeframe, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe)); if(series_obj==NULL || series_obj.DataTotal()==0) return; series_obj.Refresh(time,open,high,low,close,tick_volume,volume,spread); if(series_obj.IsNewBar(time)) { series_obj.SendEvent(); this.SetTerminalServerDate(); } } //+------------------------------------------------------------------+ //| Update all timeseries lists | //+------------------------------------------------------------------+ void CTimeSeries::RefreshAll(const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { bool upd=false; for(int i=0;i<21;i++) { CSeries *series_obj=this.m_list_series.At(i); if(series_obj==NULL || series_obj.DataTotal()==0) continue; series_obj.Refresh(time,open,high,low,close,tick_volume,volume,spread); if(series_obj.IsNewBar(time)) { series_obj.SendEvent(); upd &=true; } } if(upd) this.SetTerminalServerDate(); } //+------------------------------------------------------------------+
\MQL5\Include\DoEasy\Collections\TimeSeriesCollection.mqhのCTimeSeriesCollection時系列コレクションクラスを改善しましょう。
時系列コレクションタイプをCListObjクラスにします。
これにはCListObjクラスファイルをインクルードしてコレクションリストタイプをCArrayObjから CListObjに変えます。
//+------------------------------------------------------------------+ //| TimeSeriesCollection.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Objects\Series\TimeSeries.mqh" #include "..\Objects\Symbols\Symbol.mqh" //+------------------------------------------------------------------+ //| Symbol timeseries collection | //+------------------------------------------------------------------+ class CTimeSeriesCollection : public CObject { private: CListObj m_list; // List of applied symbol timeseries //--- Return the timeseries index by symbol name int IndexTimeSeries(const string symbol); public:
クラスのpublicセクションで、指定した時系列バーをグラフの時系列インデックスで返すメソッド、指定した時系列の新しいバーを開くフラグを返すメソッド 、および現在の銘柄に属していない時系列を更新するメソッドを宣言します。
//--- Return the flag of data synchronization with the server data of the (1) specified timeseries of the specified symbol, //--- (2) the specified timeseries of all symbols, (3) all timeseries of the specified symbol and (4) all timeseries of all symbols bool SyncData(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0); bool SyncData(const ENUM_TIMEFRAMES timeframe,const uint required=0,const int rates_total=0); bool SyncData(const string symbol,const uint required=0,const int rates_total=0); bool SyncData(const uint required=0,const int rates_total=0); //--- Return the bar of the specified timeseries of the specified symbol of the specified position CBar *GetBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const bool from_series=true); //--- Return the flag of opening a new bar of the specified timeseries of the specified symbol bool IsNewBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time=0); //--- Create (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols, //--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols void RefreshOther(void); //--- Display (1) the complete and (2) short collection description in the journal void Print(const bool created=true); void PrintShort(const bool created=true); //--- Constructor CTimeSeriesCollection(); }; //+------------------------------------------------------------------+
クラスコンストラクタで、時系列オブジェクトのリストに時系列コレクションIDを設定します。
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTimeSeriesCollection::CTimeSeriesCollection() { this.m_list.Clear(); this.m_list.Sort(); this.m_list.Type(COLLECTION_SERIES_ID); } //+------------------------------------------------------------------+
以下は、時系列インデックスによってバーオブジェクトを返すメソッドと指定された時系列リストからの新しいバーイベントを返すメソッドの実装です。
//+-----------------------------------------------------------------------+ //| Return the bar of the specified timeseries | //| of the specified symbol of the specified position | //| from_series=true - by the timeseries index, false - by the list index | //+-----------------------------------------------------------------------+ CBar *CTimeSeriesCollection::GetBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const bool from_series=true) { //--- Get the timeseries object index in the timeseries collection list by a symbol name int idx=this.IndexTimeSeries(symbol); if(idx==WRONG_VALUE) return NULL; //--- Get the pointer to the timeseries object from the collection list of timeseries objects by the obtained index CTimeSeries *timeseries=this.m_list.At(idx); if(timeseries==NULL) return NULL; //--- Get the specified timeseries from the symbol timeseries object by the specified timeframe CSeries *series=timeseries.GetSeries(timeframe); if(series==NULL) return NULL; //--- Depending on the from_series flag, return the pointer to the bar //--- either by the chart timeseries index or by the bar index in the timeseries list return(from_series ? series.GetBarBySeriesIndex(index) : series.GetBarByListIndex(index)); } //+------------------------------------------------------------------+ //| Return new bar opening flag | //| for a specified timeseries of a specified symbol | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::IsNewBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time=0) { //--- Get the timeseries object index in the timeseries collection list by a symbol name int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return false; //--- Get the pointer to the timeseries object from the collection list of timeseries objects by the obtained index CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return false; //--- Get the specified timeseries from the symbol timeseries object by the specified timeframe CSeries *series=timeseries.GetSeries(timeframe); if(series==NULL) return false; //--- Return the result of checking the new bar of the specified timeseries return series.IsNewBar(time); } //+------------------------------------------------------------------+
現在の銘柄時系列のすべての時系列リストを更新するメソッドを実装します。
//+------------------------------------------------------------------+ //| Update all timeseries except the current symbol | //+------------------------------------------------------------------+ void CTimeSeriesCollection::RefreshOther(void) { int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; if(timeseries.Symbol()==::Symbol() || !timeseries.IsNewTick()) continue; timeseries.RefreshAll(); } } //+------------------------------------------------------------------+
すべての時系列オブジェクトのリストによるループで、時系列オブジェクトを取得します。オブジェクト銘柄がプログラムが起動されるチャートの銘柄と等しい場合、そのような時系列オブジェクトはスキップされます。
このメソッドは、以下で説明する時系列更新メソッドと同様に、新しいティックフラグのチェックを備えています。 新しいティックがない場合、時系列はスキップされ、そのデータは更新されません。
//+------------------------------------------------------------------+ //| Update the specified timeseries of the specified symbol | //+------------------------------------------------------------------+ void CTimeSeriesCollection::Refresh(const string symbol,const ENUM_TIMEFRAMES timeframe, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return; CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return; if(!timeseries.IsNewTick()) return; timeseries.Refresh(timeframe,time,open,high,low,close,tick_volume,volume,spread); } //+------------------------------------------------------------------+ //| Update the specified timeseries of all symbols | //+------------------------------------------------------------------+ void CTimeSeriesCollection::Refresh(const ENUM_TIMEFRAMES timeframe, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; if(!timeseries.IsNewTick()) continue; timeseries.Refresh(timeframe,time,open,high,low,close,tick_volume,volume,spread); } } //+------------------------------------------------------------------+ //| Update all timeseries of the specified symbol | //+------------------------------------------------------------------+ void CTimeSeriesCollection::Refresh(const string symbol, const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { int index=this.IndexTimeSeries(symbol); if(index==WRONG_VALUE) return; CTimeSeries *timeseries=this.m_list.At(index); if(timeseries==NULL) return; if(!timeseries.IsNewTick()) return; timeseries.RefreshAll(time,open,high,low,close,tick_volume,volume,spread); } //+------------------------------------------------------------------+ //| Update all timeseries of all symbols | //+------------------------------------------------------------------+ void CTimeSeriesCollection::Refresh(const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { int total=this.m_list.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; if(!timeseries.IsNewTick()) continue; timeseries.RefreshAll(time,open,high,low,close,tick_volume,volume,spread); } } //+------------------------------------------------------------------+
最後のステップは、CEngineライブラリのメインオブジェクトクラスのファイルに必要な改善を加えることです。
クラスファイルを\MQL5\Include\DoEasy\Engine.mqhで開きます。
クラスのprivateセクションで、ライブラリに基づいて、プログラムのタイプを格納するための変数を宣言します。
//+------------------------------------------------------------------+ //| Library basis class | //+------------------------------------------------------------------+ class CEngine { private: CHistoryCollection m_history; // Collection of historical orders and deals CMarketCollection m_market; // Collection of market orders and deals CEventsCollection m_events; // Event collection CAccountsCollection m_accounts; // Account collection CSymbolsCollection m_symbols; // Symbol collection CTimeSeriesCollection m_series; // Timeseries collection CResourceCollection m_resource; // Resource list CTradingControl m_trading; // Trading management object CArrayObj m_list_counters; // List of timer counters int m_global_error; // Global error code bool m_first_start; // First launch flag bool m_is_hedge; // Hedge account flag bool m_is_tester; // Flag of working in the tester bool m_is_market_trade_event; // Account trading event flag bool m_is_history_trade_event; // Account history trading event flag bool m_is_account_event; // Account change event flag bool m_is_symbol_event; // Symbol change event flag ENUM_TRADE_EVENT m_last_trade_event; // Last account trading event int m_last_account_event; // Last event in the account properties int m_last_symbol_event; // Last event in the symbol properties ENUM_PROGRAM_TYPE m_program; // Program type
クラスのpublicセクションで、NewTick EAイベントを処理するメソッドを宣言します。
//--- (1) NewTick event timer and (2) handler void OnTimer(void); void OnTick(void);
クラスのpublicセクションで、チャートの時系列インデックスで指定された銘柄の指定された時系列のバーオブジェクトを返すメソッドを宣言 し、指定された銘柄の指定された時系列の新しいバーを開くフラグを返すメソッドを宣言します。
//--- Create (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols, //--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols bool SeriesCreate(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint required=0) { return this.m_series.CreateSeries(symbol,timeframe,required); } bool SeriesCreate(const ENUM_TIMEFRAMES timeframe,const uint required=0) { return this.m_series.CreateSeries(timeframe,required); } bool SeriesCreate(const string symbol,const uint required=0) { return this.m_series.CreateSeries(symbol,required); } bool SeriesCreate(const uint required=0) { return this.m_series.CreateSeries(required); } //--- Return the bar of the specified timeseries of the specified symbol of the specified position CBar *SeriesGetBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const bool from_series=true) { return this.m_series.GetBar(symbol,timeframe,index,from_series); } //--- Return the flag of opening a new bar of the specified timeseries of the specified symbol bool SeriesIsNewBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time=0) { return this.m_series.IsNewBar(symbol,timeframe,time); } //--- Update (1) the specified timeseries of the specified symbol, (2) the specified timeseries of all symbols, //--- (3) all timeseries of the specified symbol and (4) all timeseries of all symbols
同じクラスセクションで、指定された銘柄、時系列、およびチャート時系列(バーインデックス)でのポジションについて標準のバープロパティを返すメソッドを宣言します。
//--- Return (1) Open, (2) High, (3) Low, (4) Close, (5) Time, (6) TickVolume, //--- (7) RealVolume, (8) Spread of the specified bar of the specified symbol of the specified timeframe double SeriesOpen(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index); double SeriesHigh(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index); double SeriesLow(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index); double SeriesClose(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index); datetime SeriesTime(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index); long SeriesTickVolume(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index); long SeriesRealVolume(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index); int SeriesSpread(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index); //--- Set the following for the trading classes: //--- (1) correct filling policy, (2) filling policy, //--- (3) correct order expiration type, (4) order expiration type, //--- (5) magic number, (6) comment, (7) slippage, (8) volume, (9) order expiration date, //--- (10) the flag of asynchronous sending of a trading request, (11) logging level, (12) number of trading attempts
クラスコンストラクタで、実行中のプログラムのタイプを設定し、時間枠コレクションタイマーのカウンターを作成します。
//+------------------------------------------------------------------+ //| CEngine constructor | //+------------------------------------------------------------------+ CEngine::CEngine() : m_first_start(true), m_last_trade_event(TRADE_EVENT_NO_EVENT), m_last_account_event(WRONG_VALUE), m_last_symbol_event(WRONG_VALUE), m_global_error(ERR_SUCCESS) { this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif; this.m_is_tester=::MQLInfoInteger(MQL_TESTER); this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE); this.m_list_counters.Sort(); this.m_list_counters.Clear(); this.CreateCounter(COLLECTION_ORD_COUNTER_ID,COLLECTION_ORD_COUNTER_STEP,COLLECTION_ORD_PAUSE); this.CreateCounter(COLLECTION_ACC_COUNTER_ID,COLLECTION_ACC_COUNTER_STEP,COLLECTION_ACC_PAUSE); this.CreateCounter(COLLECTION_SYM_COUNTER_ID1,COLLECTION_SYM_COUNTER_STEP1,COLLECTION_SYM_PAUSE1); this.CreateCounter(COLLECTION_SYM_COUNTER_ID2,COLLECTION_SYM_COUNTER_STEP2,COLLECTION_SYM_PAUSE2); this.CreateCounter(COLLECTION_REQ_COUNTER_ID,COLLECTION_REQ_COUNTER_STEP,COLLECTION_REQ_PAUSE); this.CreateCounter(COLLECTION_TS_COUNTER_ID,COLLECTION_TS_COUNTER_STEP,COLLECTION_TS_PAUSE); ::ResetLastError(); #ifdef __MQL5__ if(!::EventSetMillisecondTimer(TIMER_FREQUENCY)) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError()); this.m_global_error=::GetLastError(); } //---__MQL4__ #else if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY)) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError()); this.m_global_error=::GetLastError(); } #endif //--- } //+------------------------------------------------------------------+
ライブラリのOnTimer()ハンドラーに、時系列コレクションタイマーの操作を追加します(過剰なコードは削除されます)。
//+------------------------------------------------------------------+ //| CEngine timer | //+------------------------------------------------------------------+ void CEngine::OnTimer(void) { //--- Timer of the collections of historical orders and deals, as well as of market orders and positions //... //--- Account collection timer //... //--- Timer 1 of the symbol collection (updating symbol quote data in the collection) //... //--- Timer 2 of the symbol collection (updating all data of all symbols in the collection and tracking symbl and symbol search events in the market watch window) //... //--- Trading class timer //... //--- Timeseries collection timer index=this.CounterIndex(COLLECTION_TS_COUNTER_ID); if(index>WRONG_VALUE) { CTimerCounter* counter=this.m_list_counters.At(index); if(counter!=NULL) { //--- If this is not a tester if(!this.IsTester()) { //--- If the pause is over, work with the timeseries list (except for the current symbol timeseries) if(counter.IsTimeDone()) this.m_series.RefreshOther(); } //--- In case of the tester, work with the timeseries list by tick (except for the current symbol timeseries) else this.m_series.RefreshOther(); } } } //+------------------------------------------------------------------+
コレクションタイマーカウンターとタイマー自体を使用して、CEngineライブラリのメインオブジェクトを作成するときに考慮されました.。他のすべてはコードへのコメントで説明されています。
タイマーは、プログラムが起動されたチャートの銘柄と銘柄が一致しない時系列のみを処理することに注意してください。
タイマーでは、「非ネイティブ」銘柄の「新しいティック」イベントを登録するときに時系列を更新します。したがって、これらはタイマーで検出されるイベントです。
EAのOnTick()ハンドラーから起動される>OnTick()メソッドは、現在の銘柄の時系列を更新するために使用されます。
//+------------------------------------------------------------------+ //| NewTick event handler | //+------------------------------------------------------------------+ void CEngine::OnTick(void) { //--- If this is not a EA, exit if(this.m_program!=PROGRAM_EXPERT) return; //--- Update the current symbol timeseries this.SeriesRefresh(NULL,PERIOD_CURRENT); } //+------------------------------------------------------------------+
指定された時系列の指定されたバーのメインプロパティを受け取るためのメソッドを実装します。
//+------------------------------------------------------------------+ //| Return the specified bar's Open | //| of the specified symbol of the specified timeframe | //+------------------------------------------------------------------+ double CEngine::SeriesOpen(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index) { CBar *bar=this.m_series.GetBar(symbol,timeframe,index); return(bar!=NULL ? bar.Open() : 0); } //+------------------------------------------------------------------+ //| Return the specified bar's High | //| of the specified symbol of the specified timeframe | //+------------------------------------------------------------------+ double CEngine::SeriesHigh(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index) { CBar *bar=this.m_series.GetBar(symbol,timeframe,index); return(bar!=NULL ? bar.High() : 0); } //+------------------------------------------------------------------+ //| Return the specified bar's Low | //| of the specified symbol of the specified timeframe | //+------------------------------------------------------------------+ double CEngine::SeriesLow(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index) { CBar *bar=this.m_series.GetBar(symbol,timeframe,index); return(bar!=NULL ? bar.Low() : 0); } //+------------------------------------------------------------------+ //| Return the specified bar's Close | //| of the specified symbol of the specified timeframe | //+------------------------------------------------------------------+ double CEngine::SeriesClose(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index) { CBar *bar=this.m_series.GetBar(symbol,timeframe,index); return(bar!=NULL ? bar.Close() : 0); } //+------------------------------------------------------------------+ //| Return the specified bar's Time | //| of the specified symbol of the specified timeframe | //+------------------------------------------------------------------+ datetime CEngine::SeriesTime(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index) { CBar *bar=this.m_series.GetBar(symbol,timeframe,index); return(bar!=NULL ? bar.Time() : 0); } //+------------------------------------------------------------------+ //| Return the specified bar's TickVolume | //| of the specified symbol of the specified timeframe | //+------------------------------------------------------------------+ long CEngine::SeriesTickVolume(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index) { CBar *bar=this.m_series.GetBar(symbol,timeframe,index); return(bar!=NULL ? bar.VolumeTick() : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return the specified bar's RealVolume | //| of the specified symbol of the specified timeframe | //+------------------------------------------------------------------+ long CEngine::SeriesRealVolume(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index) { CBar *bar=this.m_series.GetBar(symbol,timeframe,index); return(bar!=NULL ? bar.VolumeReal() : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Return the specified bar's Spread | //| of the specified symbol of the specified timeframe | //+------------------------------------------------------------------+ int CEngine::SeriesSpread(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index) { CBar *bar=this.m_series.GetBar(symbol,timeframe,index); return(bar!=NULL ? bar.Spread() : INT_MIN); } //+------------------------------------------------------------------+
ここではすべてが単純です。チャート時系列の指定されたインデックス(0 —現在のバー)から時系列銘柄と時間枠によってバーオブジェクトを受け取り、適切なバープロパティを返します。
これらは、プログラムで使用される時系列価格データの自動更新を作成し、イベントを制御プログラムチャートに送信し、プログラムで作成された時系列からデータを受信するために今日必要なすべての改善です。
テスト
次の方法でテストを実行してみましょう。3つの銘柄の現在の時間枠に対して3つの時系列を作成し、時系列コレクションオブジェクト(CTimeSeriesCollection)からゼロバーオブジェクト(CBarクラス)を取得し、バーオブジェクトの短い名前とバーオブジェクトパラメータの説明を返すメソッドを使用して、チャートコメントにバーデータを表示します。 2番目のコメント行は、ゼロバーデータを同様の形式で含めることです。ただし、この場合、データはCEngineライブラリのメインオブジェクトのメソッドを使用して生成され、指定された時間枠の指定された銘柄の指定されたバーのデータを返します。
データはテスターでリアルタイムに更新され、チャート上でEAが起動されます。
また、「新しいバー」イベントを制御プログラムチャートに送信するCSeriesクラスオブジェクトからのイベントの受信処理を実装し、銘柄チャートで起動されたプログラムでこれらのイベントを受信することを観察します。
テストを実行するには、前の記事のEAを\MQL5\Experts\TestDoEasy\Part38\でTestDoEasyPart38.mq5として保存します。
EAのOnTick()ハンドラーを次の方法で確認します。
//+------------------------------------------------------------------+ //| 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 } //--- Handle the NewTick event in the library engine.OnTick(); //--- If the trailing flag is set if(trailing_on) { TrailingPositions(); // Trailing positions TrailingOrders(); // Trailing of pending orders } //--- Bet the zero bar of the current timeseries CBar *bar=engine.SeriesGetBar(NULL,PERIOD_CURRENT,0); if(bar==NULL) return; //--- Create a string of parameters of the current bar similar to the one //--- displayed by the bar object description: //--- bar.Header()+": "+bar.ParameterDescription() string parameters= (TextByLanguage("Бар \"","Bar \"")+Symbol()+"\" "+TimeframeDescription((ENUM_TIMEFRAMES)Period())+"[0]: "+TimeToString(bar.Time(),TIME_DATE|TIME_MINUTES|TIME_SECONDS)+ ", O: "+DoubleToString(engine.SeriesOpen(NULL,PERIOD_CURRENT,0),Digits())+ ", H: "+DoubleToString(engine.SeriesHigh(NULL,PERIOD_CURRENT,0),Digits())+ ", L: "+DoubleToString(engine.SeriesLow(NULL,PERIOD_CURRENT,0),Digits())+ ", C: "+DoubleToString(engine.SeriesClose(NULL,PERIOD_CURRENT,0),Digits())+ ", V: "+(string)engine.SeriesTickVolume(NULL,PERIOD_CURRENT,0)+ ", Real: "+(string)engine.SeriesRealVolume(NULL,PERIOD_CURRENT,0)+ ", Spread: "+(string)engine.SeriesSpread(NULL,PERIOD_CURRENT,0) ); //--- Display the data received from the bar object in the first line of the chart comment, //--- while the second line contains the methods of receiving timeseries price data Comment(bar.Header(),": ",bar.ParameterDescription(),"\n",parameters); } //+------------------------------------------------------------------+
ここではすべてが簡単です。コードブロックは、DoEasyライブラリを操作するときの標準テンプレートです。現在の実装は、各ティックでライブラリによって処理されるNewTickイベントハンドラーを呼び出す機能です(現在、作成された時系列の更新を行います)。欠落しているすべての時間枠(宣言されているがCreate()メソッドによって作成されていない)はスキップされます(ライブラリによって更新されません)。今後、現在の時系列データを更新するには、EAのOnTick()ハンドラーからこのメソッドを呼び出す必要があります。
次に、現在の銘柄と期間の時系列からバーオブジェクトを受け取り、取得したバーデータの説明を表す文字列を作成し、コメントに2行を表示します。
最初の行はbarオブジェクトメソッドを使用して表示されます、
2番目のものは、リクエストされたバーデータを返すライブラリのメインオブジェクトのメソッドによって取得されたデータで構成されています。
OnInitDoEasy()ライブラリ初期化関数は、使用されるすべての銘柄の時系列を作成するためにわずかに変更されたコードブロックを備えています。
//--- Implement displaying the list of used timeframes only for MQL5 - MQL4 has no ArrayPrint() function #ifdef __MQL5__ if(InpModeUsedTFs!=TIMEFRAMES_MODE_CURRENT) ArrayPrint(array_used_periods); #endif //--- Create timeseries of all used symbols CArrayObj *list_timeseries=engine.GetListTimeSeries(); if(list_timeseries!=NULL) { int total=list_timeseries.Total(); for(int i=0;i<total;i++) { CTimeSeries *timeseries=list_timeseries.At(i); int total_periods=ArraySize(array_used_periods); for(int j=0;j<total_periods;j++) { ENUM_TIMEFRAMES timeframe=TimeframeByDescription(array_used_periods[j]); timeseries.SyncData(timeframe); timeseries.Create(timeframe); } } } //--- Check created timeseries - display descriptions of all created timeseries in the journal //--- (true - only created ones, false - created and declared ones) engine.GetTimeSeriesCollection().PrintShort(true); // Short descriptions //engine.GetTimeSeriesCollection().Print(true); // Full descriptions
ここでは、すべての時系列のリストを取得し、時系列リストによるループでループインデックスによって次の時系列オブジェクトを取得します。次に、使用された時間枠の数によるループで、 時系列データと履歴データを同期した後、必要な時系列リストを作成します。
OnDoEasyEvent()ライブラリイベントを処理する関数で、時系列イベントを処理するためのコードブロックを追加します(冗長なコードは削除されました)。
//+------------------------------------------------------------------+ //| Handling DoEasy library events | //+------------------------------------------------------------------+ void OnDoEasyEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { int idx=id-CHARTEVENT_CUSTOM; //--- Retrieve (1) event time milliseconds, (2) reason and (3) source from lparam, as well as (4) set the exact event time ushort msc=engine.EventMSC(lparam); ushort reason=engine.EventReason(lparam); ushort source=engine.EventSource(lparam); long time=TimeCurrent()*1000+msc; //--- Handling symbol events //... //--- Handling account events //... //--- Handling market watch window events //... //--- Handling timeseries events else if(idx>SERIES_EVENTS_NO_EVENT && idx<SERIES_EVENTS_NEXT_CODE) { //--- "New bar" event if(idx==SERIES_EVENTS_NEW_BAR) { Print(TextByLanguage("Новый бар на ","New Bar on "),sparam," ",TimeframeDescription((ENUM_TIMEFRAMES)dparam),": ",TimeToString(lparam)); } } //--- Handling trading events //... } //+------------------------------------------------------------------+
ここで、取得したイベントIDが時系列イベントID内にありs、これが「新規バー」イベントである場合、そのイベントに関するメッセージをターミナルの操作ログに表示します。
EAをコンパイルし、そのパラメーターを次のように設定します。
- 指定された銘柄リストを使用するために、使用される銘柄リストのモードを設定します。
- 使用されている銘柄のリスト(カンマ-区切り記号)に、3つの銘柄のみを残します。そのうちの1つはEURUSDで、
- 使用される時間枠のリストのモードで、現在の時間枠のみでの作業を選択します。次に例を示します。
チャートで起動します。しばらくすると、操作ログは現在の銘柄チャートで使用されている銘柄に関する「新しいバー」イベントメッセージを表示します。
New bar on EURUSD M5: 2020.03.11 12:55 New bar on EURAUD M5: 2020.03.11 12:55 New bar on AUDUSD M5: 2020.03.11 12:55 New bar on EURUSD M5: 2020.03.11 13:00 New bar on AUDUSD M5: 2020.03.11 13:00 New bar on EURAUD M5: 2020.03.11 13:00
EURUSDなどの設定で選択された銘柄の1つのチャートのビジュアルテスターモードでEAを起動し、チャートのコメントでゼロバーデータがどのように変化するかを確認します。
ご覧のように、異なる方法で取得されたデータを含む両方のラインは、受信したゼロバープロパティの値が同じであり、各ティックでリアルタイムに更新されます。
次の段階
次の記事では、本稿の完了時に検出された現在のライブラリバージョンのいくつかの欠点を修正し、指標の一部として機能するようにライブラリを準備することで、引き続き時系列での作業の概念を開発します。
現在のバージョンのライブラリのすべてのファイルは、テスト用EAファイルと一緒に以下に添付されているので、テストするにはダウンロードしてください。
質問や提案はコメント欄にお願いします。
シリーズのこれまでの記事:
DoEasyライブラリの時系列(第35部): バーオブジェクトと銘柄の時系列リスト
DoEasyライブラリの時系列(第36部): すべての使用銘柄期間の時系列オブジェクト
DoEasyライブラリの時系列(第37部): すべての使用銘柄期間の時系列オブジェクト