
DoEasyライブラリの時系列(第47部): 複数銘柄・複数期間標準指標
内容
概念
従来のターミナルについてくる標準的な指標については、誰もが知っていると思います。これらの指標は、現在の銘柄/期間チャートを使用して、同じ銘柄/期間のデータを表示します。
この記事で実装を開始するのは、現在の銘柄/期間チャートで、指定された銘柄/期間に対して計算されたすべての標準指標のデータを表示するカスタム指標を作成する機能です。
本稿では、標準のAC(Accelerator Oscillator)指標に基づいてカスタム指標を作成するために必要なメソッドを作成することを検討します。すべてのメソッドは、他の標準指標でも使用できるようになっていますが、わずかな変更が加えられています。これは、次の記事で実装します。
バッファオブジェクトの新しいプロパティを追加して、標準指標のデータを操作するためのバッファオブジェクトを作成および識別しましょう。
- 1つの指標の複数のバッファの識別子により、これらのバッファを使用して、単一の標準指標に属するすべてのバッファオブジェクトを識別および選択できます。1つのカスタム指標は、異なるパラメータを持つ複数の同一の標準指標を適用する場合があります(複数の標準指標に基づいて複雑なカスタム指標を作成する場合)。この識別子を使用すると、標準指標に属することにより、適用された各バッファオブジェクトを定義できます。
- バッファを使用した指標のハンドル - 標準指標の計算に使用される各バッファオブジェクトは、この指標に属するバッファオブジェクトから操作するために作成された標準指標のハンドルを特徴とします。
- バッファを使用する指標のタイプ - ENUM_INDICATOR指標タイプ列挙の指標タイプをここで指定します。これにより、標準指標タイプに属するバッファオブジェクトを定義および選択することもできます。
- バッファを使用する標準の名前 - バッファオブジェクトを適用してその説明を表示する標準標準の名前をここに保存します。
標準指標のデータを操作するためのデータベースを作成することに加えて、スキップされた履歴バーを追跡し、「スキップされたバー」イベントをプログラムに送信するための「新しいバー」オブジェクトと時系列クラスを少し改善します。
接続が失われた場合、スリープモードの有効化/無効化、および回復に時間がかかるその他の異常なイベントの場合、プログラムが動作を再開した後、ライブラリデータベースで一部の履歴バーがスキップされることがわかります。スキップされたバーの数を追跡し、「スキップされたバー」イベントをプログラムに送信して、ユーザがプログラムでそのような状況に対処できるようにするメソッドを作成しましょう。
ライブラリクラスの改善
まず、メッセージを表示するためのデータを\MQL5\Include\DoEasy\Datas.mqhに追加しましょう。
新しいメッセージIDを追加します。
MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_PLOT, // Index of the next drawn buffer MSG_LIB_TEXT_BUFFER_TEXT_ID, // Indicator buffer ID MSG_LIB_TEXT_BUFFER_TEXT_IND_HANDLE, // Handle of an indicator using a buffer MSG_LIB_TEXT_BUFFER_TEXT_IND_TYPE, // Type of an indicator using a buffer MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME, // Buffer (timeframe) data period MSG_LIB_TEXT_BUFFER_TEXT_STATUS, // Buffer status MSG_LIB_TEXT_BUFFER_TEXT_TYPE, // Buffer type MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE, // Active MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE, // Arrow code MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT, // The vertical shift of the arrows MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN, // The number of initial bars that are not drawn and values in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE, // Graphical construction type MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA, // Display construction values in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_SHIFT, // Indicator graphical construction shift by time axis in bars MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE, // Line style MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH, // Line width MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SIZE, // Arrow size MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM, // Number of colors MSG_LIB_TEXT_BUFFER_TEXT_COLOR, // Drawing color MSG_LIB_TEXT_BUFFER_TEXT_EMPTY_VALUE, // Empty value for plotting where nothing will be drawn MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL, // Buffer symbol MSG_LIB_TEXT_BUFFER_TEXT_LABEL, // Name of the graphical indicator series displayed in DataWindow MSG_LIB_TEXT_BUFFER_TEXT_IND_NAME, // Name of an indicator using a buffer
また、新しく追加されたインデックスに対応するテキストメッセージも追加します。
{"Индекс следующего по счёту рисуемого буфера","Index of the next drawable buffer"}, {"Идентификатор буферов индикатора","Indicator Buffer Id"}, {"Хэндл индикатора, использующего буфер","Indicator handle that uses buffer"}, {"Тип индикатора, использующего буфер","Indicator type that uses buffer"}, {"Период данных буфера (таймфрейм)","Buffer data Period (Timeframe)"}, {"Статус буфера","Buffer status"}, {"Тип буфера","Buffer type"}, {"Активен","Active"}, {"Код стрелки","Arrow code"}, {"Смещение стрелок по вертикали","Vertical shift of arrows"}, {"Количество начальных баров без отрисовки и значений в DataWindow","Number of initial bars without drawing and values in DataWindow"}, {"Тип графического построения","Type of graphical construction"}, {"Отображение значений построения в окне DataWindow","Display construction values in DataWindow"}, {"Сдвиг графического построения индикатора по оси времени в барах","Shift of indicator plotting along time axis in bars"}, {"Стиль линии отрисовки","Drawing line style "}, {"Толщина линии отрисовки","Thickness of drawing line"}, {"Размер значка стрелки","Arrow icon size"}, {"Количество цветов","Number of colors"}, {"Цвет отрисовки","Index of buffer containing drawing color"}, {"Пустое значение для построения, для которого нет отрисовки","Empty value for plotting, for which there is no drawing"}, {"Символ буфера","Buffer Symbol"}, {"Имя индикаторной графической серии, отображаемое в окне DataWindow","Name of indicator graphical series to display in DataWindow"}, {"Наименование индикатора, использующего буфер","Name of indicator that uses buffer"}, {"Индикаторный буфер с типом графического построения","Indicator buffer with graphic plot type"}, {"Неправильно указано количество буферов индикатора (#property indicator_buffers)","Number of indicator buffers incorrect (#property indicator_buffers)"}, {"Достигнуто максимально возможное количество индикаторных буферов","Maximum number of indicator buffers reached"},
現在のタスクに必要なすべての追加を\MQL5\Include\DoEasy\Defines.mqhで設定します。
[マクロ置換]セクションで、デフォルトの取引試行の値を格納する定数の名前をより有益なものに変更します。
//+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ //--- Describe the function with the error line number #define DFUN_ERR_LINE (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(string)__LINE__+": ") #define DFUN (__FUNCTION__+": ") // "Function description" #define COUNTRY_LANG ("Russian") // Country language #define END_TIME (D'31.12.3000 23:59:59') // End date for account history data requests #define TIMER_FREQUENCY (16) // Minimal frequency of the library timer in milliseconds #define TOTAL_TRADE_TRY (5) // Default number of trading attempts #define IND_COLORS_TOTAL (64) // Maximum possible number of indicator buffer colors #define IND_BUFFERS_MAX (512) // Maximum possible number of indicator buffers //--- Standard sounds
以前は、定数の名前はTOTAL_TRYでしたが、これは有益ではありませんでした。試行回数を指定する他の定数がある可能性があるため、特定のアクションへの試行の所属(ここでは「TRADE」—取引試行との所属)を定数名に追加すると、より有益です。他の「試行回数」に新しい定数を追加するときに、定数の名前を変更する必要がなくなります。
可能な時系列イベントの列挙に新しいイベントを追加します。
//+------------------------------------------------------------------+ //| 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 SERIES_EVENTS_MISSING_BARS, // "Bars skipped" event }; #define SERIES_EVENTS_NEXT_CODE (SERIES_EVENTS_MISSING_BARS+1) // Code of the next event after the "Bars skipped" event //+------------------------------------------------------------------+
これに対応して、次のイベントのコードは新しい定数に基づいています。
バッファオブジェクトに新しいプロパティを追加することについてはすでに説明しました。バッファオブジェクトの整数プロパティと文字列プロパティの列挙にそれらを設定しましょう。
//+------------------------------------------------------------------+ //| Buffer integer properties | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_INTEGER { BUFFER_PROP_INDEX_PLOT = 0, // Plotted buffer serial number BUFFER_PROP_STATUS, // Buffer status (by drawing style) from the ENUM_BUFFER_STATUS enumeration BUFFER_PROP_TYPE, // Buffer type (from the ENUM_BUFFER_TYPE enumeration) BUFFER_PROP_TIMEFRAME, // Buffer period data (timeframe) BUFFER_PROP_ACTIVE, // Buffer usage flag BUFFER_PROP_DRAW_TYPE, // Graphical construction type (from the ENUM_DRAW_TYPE enumeration) BUFFER_PROP_ARROW_CODE, // Arrow code for DRAW_ARROW style BUFFER_PROP_ARROW_SHIFT, // The vertical shift of the arrows for DRAW_ARROW style BUFFER_PROP_LINE_STYLE, // Line style BUFFER_PROP_LINE_WIDTH, // Line width BUFFER_PROP_DRAW_BEGIN, // The number of initial bars that are not drawn and values in DataWindow BUFFER_PROP_SHOW_DATA, // Flag of displaying construction values in DataWindow BUFFER_PROP_SHIFT, // Indicator graphical construction shift by time axis in bars BUFFER_PROP_COLOR_INDEXES, // Number of colors BUFFER_PROP_COLOR, // Drawing color BUFFER_PROP_INDEX_BASE, // Base data buffer index BUFFER_PROP_INDEX_NEXT_BASE, // Index of the array to be assigned as the next indicator buffer BUFFER_PROP_INDEX_NEXT_PLOT, // Index of the next drawn buffer BUFFER_PROP_ID, // ID of multiple buffers of a single indicator BUFFER_PROP_IND_HANDLE, // Handle of an indicator using a buffer BUFFER_PROP_IND_TYPE, // Type of an indicator using a buffer BUFFER_PROP_NUM_DATAS, // Number of data buffers BUFFER_PROP_INDEX_COLOR, // Color buffer index }; #define BUFFER_PROP_INTEGER_TOTAL (23) // Total number of integer bar properties #define BUFFER_PROP_INTEGER_SKIP (2) // Number of buffer properties not used in sorting //+------------------------------------------------------------------+ //| Buffer real properties | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_DOUBLE { BUFFER_PROP_EMPTY_VALUE = BUFFER_PROP_INTEGER_TOTAL, // Empty value for plotting where nothing will be drawn }; #define BUFFER_PROP_DOUBLE_TOTAL (1) // Total number of real buffer properties #define BUFFER_PROP_DOUBLE_SKIP (0) // Number of buffer properties not used in sorting //+------------------------------------------------------------------+ //| Buffer string properties | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_STRING { BUFFER_PROP_SYMBOL = (BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL), // Buffer symbol BUFFER_PROP_LABEL, // Name of the graphical indicator series displayed in DataWindow BUFFER_PROP_IND_NAME, // Name of an indicator using a buffer }; #define BUFFER_PROP_STRING_TOTAL (3) // Total number of string buffer properties //+------------------------------------------------------------------+
整数プロパティの総数を20から23に増やし、文字列プロパティの数を2から3に増やします。
新しいプロパティを追加したので、これらのプロパティで並べ替えて選択する機能も追加する必要があります。
バッファオブジェクトの新しいタイプの並べ替えを、可能な並べ替え基準の列挙に追加します。
//+------------------------------------------------------------------+ //| Possible buffer sorting criteria | //+------------------------------------------------------------------+ #define FIRST_BUFFER_DBL_PROP (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP) #define FIRST_BUFFER_STR_PROP (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP+BUFFER_PROP_DOUBLE_TOTAL-BUFFER_PROP_DOUBLE_SKIP) enum ENUM_SORT_BUFFER_MODE { //--- Sort by integer properties SORT_BY_BUFFER_INDEX_PLOT = 0, // Sort by the plotted buffer serial number SORT_BY_BUFFER_STATUS, // Sort by buffer drawing style (status) from the ENUM_BUFFER_STATUS enumeration SORT_BY_BUFFER_TYPE, // Sort by buffer type (from the ENUM_BUFFER_TYPE enumeration) SORT_BY_BUFFER_TIMEFRAME, // Sort by the buffer data period (timeframe) SORT_BY_BUFFER_ACTIVE, // Sort by the buffer usage flag SORT_BY_BUFFER_DRAW_TYPE, // Sort by graphical construction type (from the ENUM_DRAW_TYPE enumeration) SORT_BY_BUFFER_ARROW_CODE, // Sort by the arrow code for DRAW_ARROW style SORT_BY_BUFFER_ARROW_SHIFT, // Sort by the vertical shift of the arrows for DRAW_ARROW style SORT_BY_BUFFER_LINE_STYLE, // Sort by the line style SORT_BY_BUFFER_LINE_WIDTH, // Sort by the line width SORT_BY_BUFFER_DRAW_BEGIN, // Sort by the number of initial bars that are not drawn and values in DataWindow SORT_BY_BUFFER_SHOW_DATA, // Sort by the flag of displaying construction values in DataWindow SORT_BY_BUFFER_SHIFT, // Sort by the indicator graphical construction shift by time axis in bars SORT_BY_BUFFER_COLOR_INDEXES, // Sort by a number of attempts SORT_BY_BUFFER_COLOR, // Sort by the drawing color SORT_BY_BUFFER_INDEX_BASE, // Sort by the basic data buffer index SORT_BY_BUFFER_INDEX_NEXT_BASE, // Sort by the index of the array to be assigned as the next indicator buffer SORT_BY_BUFFER_INDEX_NEXT_PLOT, // Sort by the index of the next drawn buffer SORT_BY_BUFFER_ID, // Sort by ID of multiple buffers of a single indicator SORT_BY_BUFFER_IND_HANDLE, // Sort by handle of an indicator using a buffer SORT_BY_BUFFER_IND_TYPE, // Sort by type of an indicator using a buffer //--- Sort by real properties SORT_BY_BUFFER_EMPTY_VALUE = FIRST_BUFFER_DBL_PROP, // Sort by the empty value for plotting where nothing will be drawn //--- Sort by string properties SORT_BY_BUFFER_SYMBOL = FIRST_BUFFER_STR_PROP, // Sort by the buffer symbol SORT_BY_BUFFER_LABEL, // Sort by the name of the graphical indicator series displayed in DataWindow SORT_BY_BUFFER_IND_NAME, // Sort by name of an indicator using a buffer }; //+------------------------------------------------------------------+
スキップされたバーを検出するには(たとえば、接続が失われた後)、\MQL5\Include\DoEasy\Objects\Series\NewBarObj.mqhの「新しいバー」オブジェクトクラスを少し改善する必要があります。ここでする必要があるのは、2つの「新しいバー」イベントの間にバーの数のカウントを追加することです。1を超える値は、履歴バーがスキップされたか、サーバにまったく存在しないことを示します(この状況はまだ考慮されていません)。
クラスのprivateセクションで、手動および自動時間管理の前の「新しいバー」イベント時間の時間を保存し、 2つの「新しいバー」イベント間の秒数とバーを保存するための4つの新しいクラスメンバー変数を追加します。
//+------------------------------------------------------------------+ //| "New bar" object class | //+------------------------------------------------------------------+ class CNewBarObj : public CBaseObj { 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 datetime m_prev_new_bar_time; // Previous new bar time for auto time management datetime m_prev_new_bar_time_manual; // Previous new bar time for manual time management long m_seconds_between; // Number of seconds between two "New bar" events int m_bars_between; // Number of bars between two "New bar" events //--- Return the current bar data datetime GetLastBarDate(const datetime time); public:
クラスのpublicセクションで、オブジェクトの時間枠を設定して返すためのメソッド(以前は期間が使用されていましたが、時間枠を格納するために時間枠を使用する方が有益です)を名前変更し、新しく宣言された変数の値を返すメソッドを追加します 。
public: //--- Set (1) symbol and (2) timeframe void SetSymbol(const string symbol) { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); } void SetTimeframe(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 Timeframe(void) const { return this.m_timeframe; } //--- Return (1) new bar time, (2) previous new bar time, number of (3) seconds, (4) number of bars between the two last events datetime TimeNewBar(void) const { return this.m_new_bar_time; } datetime TimePrevNewBar(void) const { return this.m_prev_new_bar_time; } long SecondsBetweenNewBars(void) const { return this.m_seconds_between; } int BarsBetweenNewBars(void) const { return this.m_bars_between; } //--- 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); }; //+------------------------------------------------------------------+
クラスコンストラクタの初期化リストで秒とバーの数の初期化値を設定し、他の新しい変数をコンストラクタ本体でゼロを使用して初期化します。
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CNewBarObj::CNewBarObj(const string symbol,const ENUM_TIMEFRAMES timeframe) : m_symbol(symbol), m_timeframe(timeframe), m_seconds_between(0), m_bars_between(0) { this.m_prev_new_bar_time=this.m_prev_new_bar_time_manual=this.m_prev_time=this.m_prev_time_manual=this.m_new_bar_time=this.m_new_bar_time_manual=0; } //+------------------------------------------------------------------+
自動時間管理中に新しいバーを開くフラグを返すメソッドでは、新しいバーが形成されたときに前の新しいバーの時間を保存し、秒数および2つの「新しいバー」イベント間のバー数を計算します。
//+------------------------------------------------------------------+ //| Return new bar opening flag | //+------------------------------------------------------------------+ bool CNewBarObj::IsNewBar(const datetime time) { //--- Get the current bar time datetime tm=this.GetLastBarDate(time); if(tm<=0) return false; //--- If the previous and current time are equal to zero, this is the first launch if(this.m_prev_time+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 less than the current bar open time, this is a new bar if(this.m_prev_time>0 && this.m_prev_time<tm) { this.m_prev_new_bar_time=this.m_prev_time; this.m_seconds_between=tm-m_prev_time; this.m_bars_between=int(this.m_seconds_between/::PeriodSeconds(this.m_timeframe)); //--- 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; } //+------------------------------------------------------------------+
手動管理の場合に新しいバーフラグを返すメソッドでは、データを計算する必要はありません。スキップされたバーのデータは常に自動的に計算されます。ただし、この方法では、手動管理の場合に以前の「新しいバー」の時間を節約し、新しいバーの時間を割り当てるエラーを修正します(以前は 、時間は自動時間管理のために変数に保存されました)。
//+------------------------------------------------------------------+ //| 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(tm<=0) return false; //--- If the previous and current time are equal to zero, this is the first launch if(this.m_prev_time_manual+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 less than the current bar open time, this is a new bar if(this.m_prev_time_manual>0 && this.m_prev_time_manual<tm) { this.m_prev_new_bar_time_manual=this.m_prev_time_manual; //--- 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_manual=tm; return true; } //--- in other cases, return 'false' return false; } //+------------------------------------------------------------------+
ターミナル操作ログで履歴バーの受信エラーに関するライブラリエントリをよく目にします。これは、特定の銘柄に特定の銘柄の履歴データがない場合でも、ライブラリが履歴全体を表示するために発生します。適切なエントリが表示され、システムは次の履歴バーに移動します。これは、時系列を操作するときにライブラリメソッドをデバッグする機能のために行われます。履歴データの取得エラーを表示する必要がまったくない場合は、これらのエントリを削除します。これを行うには、Barオブジェクトクラスの\MQL5\Include\DoEasy\Objects\Series\Bar.mqhが、パラメータなしでさらに別のコンストラクタを受け取る必要があります。
//+------------------------------------------------------------------+ //| Bar class | //+------------------------------------------------------------------+ class CBar : public CBaseObj { 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 time, (2) bar object parameters void SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time); 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 datetime time,const string source); CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const MqlRates &rates); //+------------------------------------------------------------------+
銘柄ごとに時系列リストを作成する場合、コンストラクタを使用して、指定した銘柄時系列に属する新しいバーオブジェクトを作成します。以前は、パラメトリックコンストラクタは、履歴内に必要な新しく作成されたバーオブジェクトデータを独自に取得しようとしました。コンストラクタから履歴を取得中にエラーが発生した場合は、デバッグエントリが操作ログに送信されました。パラメータのない単純なコンストラクタは、空のバーオブジェクトを作成します。このオブジェクトは、正常に作成された後にデータを入力する必要があります。これは、CSeriesDEクラスのメソッドで発生します。
\MQL5\Include\DoEasy\Objects\Series\SeriesDE.mqhのクラスリストに加える必要のある変更について考えてみましょう。
クラスのpublicセクションで、クラス時系列に属する「Newbar」クラスオブジェクトへのポインタを返すメソッドを追加します。
//+------------------------------------------------------------------+ //| Timeseries class | //+------------------------------------------------------------------+ class CSeriesDE : public CBaseObj { private: ENUM_TIMEFRAMES m_timeframe; // Timeframe string m_symbol; // Symbol string m_period_description; // Timeframe string description datetime m_firstdate; // The very first date by a period symbol at the moment datetime m_lastbar_date; // Time of opening the last bar by period symbol uint m_amount; // Amount of applied timeseries data uint m_required; // Required amount of applied timeseries data uint m_bars; // Number of bars in history by symbol and timeframe bool m_sync; // Synchronized data flag CArrayObj m_list_series; // Timeseries list CNewBarObj m_new_bar_obj; // "New bar" object //--- Set the very first date by a period symbol at the moment and the new time of opening the last bar by a period symbol void SetServerDate(void) { this.m_firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_FIRSTDATE); this.m_lastbar_date=(datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_LASTBAR_DATE); } public: //--- Return (1) itself, (2) timeseries list, (3) timeseries "New bar" object CSeriesDE *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &m_list_series; } CNewBarObj *GetNewBarObj(void) { return &this.m_new_bar_obj; } //--- Return the list of bars by selected (1) double, (2) integer and (3) string property fitting a compared condition
現在、2つの時系列イベント(「新しいバー」と「スキップされたバー」)があるため、時系列イベントを作成して制御プログラムチャートに送信する方法を改善する必要があります。メソッド宣言で、作成および送信される時系列イベントを渡す入力パラメータを追加します。
//--- Create and send the timeseries event to the control program chart void SendEvent(ENUM_SERIES_EVENT event);
クラス本体の外側にあるメソッドを改善します。
//+------------------------------------------------------------------+ //| Create and send the timeseries event | //| to the control program chart | //+------------------------------------------------------------------+ void CSeriesDE::SendEvent(ENUM_SERIES_EVENT event) { if(event==SERIES_EVENTS_NEW_BAR) { int index=CSelect::FindBarMax(this.GetList(),BAR_PROP_TIME); CBar *bar=this.m_list_series.At(index); if(bar==NULL) return; ::EventChartCustom(this.m_chart_id_main,SERIES_EVENTS_NEW_BAR,bar.Time(),this.Timeframe(),this.Symbol()); } else if(event==SERIES_EVENTS_MISSING_BARS) { ::EventChartCustom(this.m_chart_id_main,SERIES_EVENTS_MISSING_BARS,this.m_new_bar_obj.BarsBetweenNewBars(),this.Timeframe(),this.Symbol()); } } //+------------------------------------------------------------------+
ここでは、メソッドに渡された値によって、必要なイベントを作成して制御プログラムチャートに送信します。「スキップされたバー」イベントが作成された場合、スキップされた履歴バーの数をEventChartCustom()関数のlparam値に渡します。
操作ログでエラーを受信する不要な履歴データを取り除くには、時系列の時間ごとにバーオブジェクトを返すメソッドを開発する必要があります。
//+------------------------------------------------------------------+ //| Return the bar object by time in the timeseries | //+------------------------------------------------------------------+ CBar *CSeriesDE::GetBar(const datetime time) { CBar *obj=new CBar(); if(obj==NULL) return NULL; obj.SetSymbolPeriod(this.m_symbol,this.m_timeframe,time); this.m_list_series.Sort(SORT_BY_BAR_TIME); int index=this.m_list_series.Search(obj); delete obj; return this.m_list_series.At(index); } //+------------------------------------------------------------------+
CBarクラスにパラメータのないコンストラクタがあるので、コンストラクタを使用して新しいバーオブジェクトを作成し、必要なバーを検索します。
ここでは、一時的に空のバーオブジェクトを作成し、必要な銘柄、時間枠、バー時間を設定します。
残りは簡単です。バーオブジェクトのリストを時間で並べ替えし、作成した一時バーオブジェクトに設定したデータと一致するデータを持つオブジェクトをバーオブジェクトのリストで検索します。
Search()メソッドは、取得したオブジェクトインデックスをリストに返し、At()メソッドは、インデックスごとにオブジェクトへのポインタを返します。オブジェクトが見つからない場合、インデックスの値は-1ですが、At()はNULLを返します。
\MQL5\Include\DoEasy\Objects\Series\TimeSeriesDE.mqhにある既存のすべてのCTimeSeriesDEクラスの時系列を更新するメソッドで、新しいバーイベントとバースキップイベントが検出されるようになりました。
「スキップされたバー」イベントを定義するためのコードブロックを追加することにより、時系列を更新する2つの方法を改善しましょう。
//+------------------------------------------------------------------+ //| Update a specified timeseries list | //+------------------------------------------------------------------+ void CTimeSeriesDE::Refresh(const ENUM_TIMEFRAMES timeframe,SDataCalculate &data_calculate) { //--- Reset the timeseries event flag and clear the list of all timeseries events this.m_is_event=false; this.m_list_events.Clear(); //--- Get the timeseries from the list by its timeframe CSeriesDE *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe)); if(series_obj==NULL || series_obj.DataTotal()==0 || !series_obj.IsAvailable()) return; //--- Update the timeseries list series_obj.Refresh(data_calculate); datetime time= ( this.m_program==PROGRAM_INDICATOR && series_obj.Symbol()==::Symbol() && series_obj.Timeframe()==(ENUM_TIMEFRAMES)::Period() ? data_calculate.rates.time : series_obj.LastBarDate() ); //--- If the timeseries object features the New bar event if(series_obj.IsNewBar(time)) { //--- send the "New bar" event to the control program chart series_obj.SendEvent(SERIES_EVENTS_NEW_BAR); //--- set the values of the first date in history on the server and in the terminal this.SetTerminalServerDate(); //--- add the "New bar" event to the list of timeseries events //--- in case of successful addition, set the event flag for the timeseries if(this.EventAdd(SERIES_EVENTS_NEW_BAR,time,series_obj.Timeframe(),series_obj.Symbol())) this.m_is_event=true; //--- Check skipped bars int missing=series_obj.GetNewBarObj().BarsBetweenNewBars(); if(missing>1) { //--- send the "Bars skipped" event to the control program chart series_obj.SendEvent(SERIES_EVENTS_MISSING_BARS); //--- add the "Bars skipped" event to the list of timeseries events this.EventAdd(SERIES_EVENTS_MISSING_BARS,missing,series_obj.Timeframe(),series_obj.Symbol()); } } } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Update all timeseries lists | //+------------------------------------------------------------------+ void CTimeSeriesDE::RefreshAll(SDataCalculate &data_calculate) { //--- Reset the flags indicating the necessity to set the first date in history on the server and in the terminal //--- and the timeseries event flag, and clear the list of all timeseries events bool upd=false; this.m_is_event=false; this.m_list_events.Clear(); //--- In the loop by the list of all used timeseries, int total=this.m_list_series.Total(); for(int i=0;i<total;i++) { //--- get the next timeseries object by the loop index CSeriesDE *series_obj=this.m_list_series.At(i); if(series_obj==NULL || !series_obj.IsAvailable() || series_obj.DataTotal()==0) continue; //--- update the timeseries list series_obj.Refresh(data_calculate); datetime time= ( this.m_program==PROGRAM_INDICATOR && series_obj.Symbol()==::Symbol() && series_obj.Timeframe()==(ENUM_TIMEFRAMES)::Period() ? data_calculate.rates.time : series_obj.LastBarDate() ); //--- If the timeseries object features the New bar event if(series_obj.IsNewBar(time)) { //--- send the "New bar" event to the control program chart, series_obj.SendEvent(SERIES_EVENTS_NEW_BAR); //--- set the flag indicating the necessity to set the first date in history on the server and in the terminal upd=true; //--- add the "New bar" event to the list of timeseries events //--- in case of successful addition, set the event flag for the timeseries if(this.EventAdd(SERIES_EVENTS_NEW_BAR,time,series_obj.Timeframe(),series_obj.Symbol())) this.m_is_event=true; //--- Check skipped bars int missing=series_obj.GetNewBarObj().BarsBetweenNewBars(); if(missing>1) { //--- send the "Bars skipped" event to the control program chart series_obj.SendEvent(SERIES_EVENTS_MISSING_BARS); //--- add the "Bars skipped" event to the list of timeseries events this.EventAdd(SERIES_EVENTS_MISSING_BARS,missing,series_obj.Timeframe(),series_obj.Symbol()); } } } //--- if the flag indicating the necessity to set the first date in history on the server and in the terminal is enabled, //--- set the values of the first date in history on the server and in the terminal if(upd) this.SetTerminalServerDate(); } //+------------------------------------------------------------------+
「新しいバー」イベントを定義するとき、以前に変更したメソッドを呼び出して、新しい時系列イベントを作成します。このメソッドに「新しいバー」イベントを渡します。バーが欠落している場合は、適切なイベントも作成します。
\MQL5\Include\DoEasy\Collections\TimeSeriesCollection.mqh内のすべての時系列オブジェクトのCTimeSeriesCollectionコレクションクラスのpublicセクションで、すべての時系列を再作成するためのメソッドの宣言を追加します。
//--- (1) Create, (2) re-create a specified timeseries of a specified symbol, (3) re-create all timeseries bool CreateSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const int rates_total=0,const uint required=0); bool ReCreateSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const int rates_total=0,const uint required=0); bool ReCreateSeriesAll(const int rates_total=0,const uint required=0); //--- Return (1) an empty, (2) partially filled timeseries
クラス本体の外側で実装しましょう。
//+------------------------------------------------------------------+ //| Re-create all timeseries | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::ReCreateSeriesAll(const int rates_total=0,const uint required=0) { //--- In the loop by all symbol timeseries objects in the collection, int total=this.m_list.Total(); for(int i=0;i<total;i++) { //--- get the next symbol timeseries object CTimeSeriesDE *timeseries=this.m_list.At(i); if(timeseries==NULL) continue; //--- Get the list of all symbol timeseries CArrayObj *list=timeseries.GetListSeries(); if(list==NULL) continue; //--- In a loop by all symbol timeseries int total_series=list.Total(); for(int j=0;j<total_series;j++) { //--- Get the next timeseries CSeriesDE *series=list.At(j); if(series==NULL) continue; //--- check timeseries synchronization and re-create it if(!series.SyncData(required,rates_total)) return false; if(series.Create(required)==0) return false; } } return true; } //+------------------------------------------------------------------+
このメソッドは、コレクション内の使用可能なすべての時系列を再作成するだけです。これまでのところ、このメソッドはどこにも適用されていませんが、将来、既存の時系列コレクションを再作成する必要がある場合に役立ちます。たとえば、プログラムが多くの銘柄/期間を使用する場合に、多数のバーをスキップすることを定義するときに必要になる場合があります。この場合、各時系列でスキップされたバーの数を定義してそれぞれを個別に再作成するよりも、1つのメソッドを呼び出してすべてのコレクション時系列を再作成する方がはるかに簡単です。さらに、これはサーバへの接続を復元するとき、または新しいバーでのみ発生します。
時系列とバーの処理をわずかに改善するすべての準備手順を完了しました。標準的な指標を操作するためのメソッドの作成を開始する時が来ました。
標準指標を使用するためのメソッド
まず、\MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqhの抽象バッファオブジェクトクラスを改善しましょう。
クラスのpublicセクションに、4つの新しいバッファオブジェクトプロパティを設定するメソッドと返すメソッドを記述します。
//--- Set (1) the arrow code, (2) vertical shift of arrows, (3) symbol, (4) timeframe, (5) buffer activity flag //--- (6) drawing type, (7) number of initial bars without drawing, (8) flag of displaying construction values in DataWindow, //--- (9) shift of the indicator graphical construction along the time axis, (10) line style, (11) line width, //--- (12) total number of colors, (13) one drawing color, (14) color of drawing in the specified color index, //--- (15) drawing colors from the color array, (16) empty value, (17) name of the graphical series displayed in DataWindow virtual void SetArrowCode(const uchar code) { return; } virtual void SetArrowShift(const int shift) { return; } void SetSymbol(const string symbol) { this.SetProperty(BUFFER_PROP_SYMBOL,symbol); } void SetTimeframe(const ENUM_TIMEFRAMES timeframe) { this.SetProperty(BUFFER_PROP_TIMEFRAME,timeframe); } void SetActive(const bool flag) { this.SetProperty(BUFFER_PROP_ACTIVE,flag); } void SetDrawType(const ENUM_DRAW_TYPE draw_type); void SetDrawBegin(const int value); void SetShowData(const bool flag); void SetShift(const int shift); void SetStyle(const ENUM_LINE_STYLE style); void SetWidth(const int width); void SetColorNumbers(const int number); void SetColor(const color colour); void SetColor(const color colour,const uchar index); void SetColors(const color &array_colors[]); void SetEmptyValue(const double value); virtual void SetLabel(const string label); void SetID(const int id) { this.SetProperty(BUFFER_PROP_ID,id); } void SetIndicatorHandle(const int handle) { this.SetProperty(BUFFER_PROP_IND_HANDLE,handle); } void SetIndicatorType(const ENUM_INDICATOR type) { this.SetProperty(BUFFER_PROP_IND_TYPE,type); } void SetIndicatorName(const string name) { this.SetProperty(BUFFER_PROP_IND_NAME,name); } //--- Return (1) the serial number of the drawn buffer, (2) bound array index, (3) color buffer index, //--- (4) index of the first free bound array, (5) index of the next drawn buffer, (6) buffer data period, (7) buffer status, //--- (8) buffer type, (9) buffer usage flag, (10) arrow code, (11) arrow shift for DRAW_ARROW style, //--- (12) number of initial bars that are not drawn and values in DataWindow, (13) graphical construction type, //--- (14) flag of displaying construction values in DataWindow, (15) indicator graphical construction shift along the time axis, //--- (16) drawing line style, (17) drawing line width, (18) number of colors, (19) drawing color, number of buffers for construction //--- (20) set empty value, (21) buffer symbol, (22) name of the indicator graphical series displayed in DataWindow int IndexPlot(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_PLOT); } int IndexBase(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_BASE); } int IndexColor(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_COLOR); } int IndexNextBaseBuffer(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_BASE); } int IndexNextPlotBuffer(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_PLOT); } ENUM_TIMEFRAMES Timeframe(void) const { return (ENUM_TIMEFRAMES)this.GetProperty(BUFFER_PROP_TIMEFRAME); } ENUM_BUFFER_STATUS Status(void) const { return (ENUM_BUFFER_STATUS)this.GetProperty(BUFFER_PROP_STATUS); } ENUM_BUFFER_TYPE TypeBuffer(void) const { return (ENUM_BUFFER_TYPE)this.GetProperty(BUFFER_PROP_TYPE); } bool IsActive(void) const { return (bool)this.GetProperty(BUFFER_PROP_ACTIVE); } uchar ArrowCode(void) const { return (uchar)this.GetProperty(BUFFER_PROP_ARROW_CODE); } int ArrowShift(void) const { return (int)this.GetProperty(BUFFER_PROP_ARROW_SHIFT); } int DrawBegin(void) const { return (int)this.GetProperty(BUFFER_PROP_DRAW_BEGIN); } ENUM_DRAW_TYPE DrawType(void) const { return (ENUM_DRAW_TYPE)this.GetProperty(BUFFER_PROP_DRAW_TYPE); } bool IsShowData(void) const { return (bool)this.GetProperty(BUFFER_PROP_SHOW_DATA); } int Shift(void) const { return (int)this.GetProperty(BUFFER_PROP_SHIFT); } ENUM_LINE_STYLE LineStyle(void) const { return (ENUM_LINE_STYLE)this.GetProperty(BUFFER_PROP_LINE_STYLE); } int LineWidth(void) const { return (int)this.GetProperty(BUFFER_PROP_LINE_WIDTH); } int ColorsTotal(void) const { return (int)this.GetProperty(BUFFER_PROP_COLOR_INDEXES); } color Color(void) const { return (color)this.GetProperty(BUFFER_PROP_COLOR); } int BuffersTotal(void) const { return (int)this.GetProperty(BUFFER_PROP_NUM_DATAS); } double EmptyValue(void) const { return this.GetProperty(BUFFER_PROP_EMPTY_VALUE); } string Symbol(void) const { return this.GetProperty(BUFFER_PROP_SYMBOL); } string Label(void) const { return this.GetProperty(BUFFER_PROP_LABEL); } int ID(void) const { return (int)this.GetProperty(BUFFER_PROP_ID); } int IndicatorHandle(void) const { return (int)this.GetProperty(BUFFER_PROP_IND_HANDLE); } ENUM_INDICATOR IndicatorType(void) const { return (ENUM_INDICATOR)this.GetProperty(BUFFER_PROP_IND_TYPE); } string IndicatorName(void) const { return this.GetProperty(BUFFER_PROP_IND_NAME); } int IndicatorBarsCalculated(void) const { return ::BarsCalculated((int)this.GetProperty(BUFFER_PROP_IND_HANDLE));}
クラスコンストラクタで、デフォルト値を新しいプロパティに設定します。
//+------------------------------------------------------------------+ //| Closed parametric constructor | //+------------------------------------------------------------------+ CBuffer::CBuffer(ENUM_BUFFER_STATUS buffer_status, ENUM_BUFFER_TYPE buffer_type, const uint index_plot, const uint index_base_array, const int num_datas, const uchar total_arrays, const int width, const string label) { this.m_type=COLLECTION_BUFFERS_ID; this.m_act_state_trigger=true; this.m_total_arrays=total_arrays; //--- Save integer properties this.m_long_prop[BUFFER_PROP_STATUS] = buffer_status; this.m_long_prop[BUFFER_PROP_TYPE] = buffer_type; this.m_long_prop[BUFFER_PROP_ID] = WRONG_VALUE; this.m_long_prop[BUFFER_PROP_IND_HANDLE] = INVALID_HANDLE; this.m_long_prop[BUFFER_PROP_IND_TYPE] = WRONG_VALUE; ENUM_DRAW_TYPE type= ( !this.TypeBuffer() || !this.Status() ? DRAW_NONE : this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING : ENUM_DRAW_TYPE(this.Status()+8) ); this.m_long_prop[BUFFER_PROP_DRAW_TYPE] = type; this.m_long_prop[BUFFER_PROP_TIMEFRAME] = PERIOD_CURRENT; this.m_long_prop[BUFFER_PROP_ACTIVE] = true; this.m_long_prop[BUFFER_PROP_ARROW_CODE] = 0x9F; this.m_long_prop[BUFFER_PROP_ARROW_SHIFT] = 0; this.m_long_prop[BUFFER_PROP_DRAW_BEGIN] = 0; this.m_long_prop[BUFFER_PROP_SHOW_DATA] = (buffer_type>BUFFER_TYPE_CALCULATE ? true : false); this.m_long_prop[BUFFER_PROP_SHIFT] = 0; this.m_long_prop[BUFFER_PROP_LINE_STYLE] = STYLE_SOLID; this.m_long_prop[BUFFER_PROP_LINE_WIDTH] = width; this.m_long_prop[BUFFER_PROP_COLOR_INDEXES] = (this.Status()>BUFFER_STATUS_NONE ? (this.Status()!=BUFFER_STATUS_FILLING ? 1 : 2) : 0); this.m_long_prop[BUFFER_PROP_COLOR] = clrRed; this.m_long_prop[BUFFER_PROP_NUM_DATAS] = num_datas; this.m_long_prop[BUFFER_PROP_INDEX_PLOT] = index_plot; this.m_long_prop[BUFFER_PROP_INDEX_BASE] = index_base_array; this.m_long_prop[BUFFER_PROP_INDEX_COLOR] = this.GetProperty(BUFFER_PROP_INDEX_BASE)+ (this.TypeBuffer()!=BUFFER_TYPE_CALCULATE ? this.GetProperty(BUFFER_PROP_NUM_DATAS) : 0); this.m_long_prop[BUFFER_PROP_INDEX_NEXT_BASE] = index_base_array+this.m_total_arrays; this.m_long_prop[BUFFER_PROP_INDEX_NEXT_PLOT] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? index_plot+1 : index_plot); //--- Save real properties this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? EMPTY_VALUE : 0); //--- Save string properties this.m_string_prop[this.IndexProp(BUFFER_PROP_SYMBOL)] = ::Symbol(); this.m_string_prop[this.IndexProp(BUFFER_PROP_LABEL)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? label : NULL); this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME)] = NULL; //--- If failed to change the size of the indicator buffer array, display the appropriate message indicating the string
これらの新しいプロパティのこのような値は、標準指標では機能しないバッファオブジェクトに属します。標準指標に属するバッファオブジェクトを作成する場合、これらのパラメータは、作成時にライブラリによって入力されます(後で実装されます)。
新しい整数プロパティの表示説明を追加して、バッファ整数プロパティの説明を返すメソッドを追加します。
//+------------------------------------------------------------------+ //| Return description of a buffer's integer property | //+------------------------------------------------------------------+ string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property) { return ( property==BUFFER_PROP_INDEX_PLOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_STATUS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetStatusDescription() ) : property==BUFFER_PROP_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTypeBufferDescription() ) : property==BUFFER_PROP_TIMEFRAME ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTimeframeDescription() ) : property==BUFFER_PROP_ACTIVE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetActiveDescription() ) : property==BUFFER_PROP_DRAW_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetDrawTypeDescription() ) : property==BUFFER_PROP_ARROW_CODE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_ARROW_SHIFT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_LINE_STYLE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetLineStyleDescription() ) : property==BUFFER_PROP_LINE_WIDTH ? (this.Status()==BUFFER_STATUS_ARROW ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SIZE) : CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH))+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_DRAW_BEGIN ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_SHOW_DATA ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetShowDataDescription() ) : property==BUFFER_PROP_SHIFT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHIFT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_COLOR_INDEXES ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_COLOR ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_BASE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_NEXT_BASE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_BASE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_NEXT_PLOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_PLOT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_ID ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ID)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_IND_HANDLE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_HANDLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_IND_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_NUM_DATAS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_COLOR ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetColorsDescription() ) : "" ); } //+------------------------------------------------------------------+
新しい文字列プロパティの表示説明を追加して、バッファ文字列プロパティの説明を返すメソッドを追加します。
//+------------------------------------------------------------------+ //| Return description of a buffer's string property | //+------------------------------------------------------------------+ string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_STRING property) { return ( property==BUFFER_PROP_SYMBOL ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.Symbol() ) : property==BUFFER_PROP_LABEL ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LABEL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.Label()==NULL || this.Label()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.Label()+"\"") ) : property==BUFFER_PROP_IND_NAME ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_NAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.IndicatorName()==NULL || this.IndicatorName()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.IndicatorName()+"\"") ) : "" ); } //+------------------------------------------------------------------+
空の値の設定方法とグラフィカルなシリーズ名を変更してみましょう。以前は、これらの値は計算バッファに設定されていませんでした。計算バッファの場合、値がバッファオブジェクトのプロパティにのみ設定されるようにしましょう。
描画バッファの場合、値はオブジェクトとバッファプロパティの両方に設定する必要があります。
//+------------------------------------------------------------------+ //| Set the "empty" value for construction | //| without drawing | //+------------------------------------------------------------------+ void CBuffer::SetEmptyValue(const double value) { this.SetProperty(BUFFER_PROP_EMPTY_VALUE,value); if(this.TypeBuffer()!=BUFFER_TYPE_CALCULATE) ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,value); } //+------------------------------------------------------------------+ //| Set the indicator graphical series name | //+------------------------------------------------------------------+ void CBuffer::SetLabel(const string label) { this.SetProperty(BUFFER_PROP_LABEL,label); if(this.TypeBuffer()!=BUFFER_TYPE_CALCULATE) ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,label); } //+------------------------------------------------------------------+
時系列インデックスによって値を返すメソッドに、ゼロ未満のインデックス値の制御を追加します。
//+------------------------------------------------------------------+ //| Return the value from the specified timeseries index | //| of the specified data buffer array | //+------------------------------------------------------------------+ double CBuffer::GetDataBufferValue(const uint buffer_index,const int series_index) const { int correct_buff_index=this.GetCorrectIndexBuffer(buffer_index); int data_total=this.GetDataTotal(correct_buff_index); if(data_total==0 || series_index<0) return this.EmptyValue(); int data_index=((int)series_index<data_total ? (int)series_index : data_total-1); return this.DataBuffer[correct_buff_index].Array[data_index]; } //+------------------------------------------------------------------+ //| Return the color index value from the specified timeseries index | //| of the specified color buffer array | //+------------------------------------------------------------------+ int CBuffer::GetColorBufferValueIndex(const int series_index) const { int data_total=this.GetDataTotal(0); if(data_total==0 || series_index<0) return WRONG_VALUE; int data_index=((int)series_index<data_total ? (int)series_index : data_total-1); return(this.ColorsTotal()==1 ? 0 : (int)this.ColorBufferArray[data_index]); } //+------------------------------------------------------------------+ //| Return the color value from the specified timeseries index | //| of the specified color buffer array | //+------------------------------------------------------------------+ color CBuffer::GetColorBufferValueColor(const int series_index) const { int data_total=this.GetDataTotal(0); if(data_total==0 || series_index<0) return clrNONE; int color_index=this.GetColorBufferValueIndex(series_index); return(color_index>WRONG_VALUE ? (color)this.ArrayColors[color_index] : clrNONE); } //+------------------------------------------------------------------+
したがって、間違ったインデックスがメソッドに渡された場合、メソッドの終了は、メソッドごとに異なる「空の」値を返しながら実行されます。
\MQL5\Include\DoEasy\Objects\Indicators\BufferCalculate.mqhの計算バッファオブジェクトクラスを改善しましょう。
バッファオブジェクトによって実数プロパティと文字列プロパティをサポートするフラグを返すメソッドは、以前にはfalseを返していました。つまり、計算バッファはこのタイプのプロパティをサポートしていませんでした。これらの各プロパティをサポートするようにしましょう。オブジェクトによって整数プロパティをサポートするフラグを返すメソッドで、計算バッファオブジェクトを使用してそれらをサポートするための新しい整数プロパティを追加します。
//+------------------------------------------------------------------+ //| Return 'true' if a buffer supports a passed | //| integer property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CBufferCalculate::SupportProperty(ENUM_BUFFER_PROP_INTEGER property) { if( property==BUFFER_PROP_INDEX_PLOT || property==BUFFER_PROP_STATUS || property==BUFFER_PROP_TYPE || property==BUFFER_PROP_INDEX_BASE || property==BUFFER_PROP_ID || property==BUFFER_PROP_IND_HANDLE || property==BUFFER_PROP_IND_TYPE || property==BUFFER_PROP_INDEX_NEXT_BASE ) return true; return false; } //+------------------------------------------------------------------+ //| Return 'true' if a buffer supports a passed | //| real property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CBufferCalculate::SupportProperty(ENUM_BUFFER_PROP_DOUBLE property) { return true; } //+------------------------------------------------------------------+ //| Return 'true' if a buffer supports a passed | //| string property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CBufferCalculate::SupportProperty(ENUM_BUFFER_PROP_STRING property) { return true; } //+------------------------------------------------------------------+
したがって、間違ったインデックスがメソッドに渡された場合、メソッドの終了は、メソッドごとに異なる「空の」値を返しながら実行されます。後で、サポートされているプロパティのリストからそれらのいくつかを削除します。
標準指標の指標バッファの処理全体は、\MQL5\Include\DoEasy\Collections\BuffersCollection.mqhの指標バッファのCBuffersCollectionコレクションクラスに配置されています。
今日は、AC(Accelerator Oscillator)複数銘柄・複数期間指標バッファを作成して維持します。次の記事では、テストされた機能に基づいて、他の標準指標を作成して操作する機能を追加します。
標準指標を操作するすべてのバッファオブジェクトはIDを取得し、必要なバッファを見つけて操作できるようにします。
クラスのpublicセクションで、このようなIDを持つバッファオブジェクトのリストを返すメソッドを宣言します。
//+------------------------------------------------------------------+ //| Collection of indicator buffers | //+------------------------------------------------------------------+ class CBuffersCollection : public CObject { private: CListObj m_list; // Buffer object list CTimeSeriesCollection *m_timeseries; // Pointer to the timeseries collection object //--- Return the index of the (1) last, (2) next drawn and (3) basic buffer int GetIndexLastPlot(void); int GetIndexNextPlot(void); int GetIndexNextBase(void); //--- Create a new buffer object and place it to the collection list bool CreateBuffer(ENUM_BUFFER_STATUS status); //--- Get data of the necessary timeseries and bars for working with a single buffer bar, and return the number of bars int GetBarsData(CBuffer *buffer,const int series_index,int &index_bar_period); public: //--- Return (1) itself, (2) timeseries list, (3) indicator buffer list (featuring the ID of belonging to an indicator) CBuffersCollection *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &this.m_list; } CArrayObj *GetListBuffersWithID(void);
クラス本体の外側で実装しましょう。
//+------------------------------------------------------------------+ //| Return the list of indicator buffers | //| (featuring the ID of belonging to an indicator) | //+------------------------------------------------------------------+ CArrayObj *CBuffersCollection::GetListBuffersWithID(void) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_ID,WRONG_VALUE,NO_EQUAL); return list; } //+------------------------------------------------------------------+
ここではすべて簡単です。CSelectクラスを使用して、ID値が -1と等しくないバッファオブジェクトのリストを取得 し、取得したリストへのポインタを返します。
リストが正常に取得されると、-1以外のIDを持つすべてのバッファオブジェクトが表示されます。これは、リストに、標準指標を操作するために作成されたすべてのバッファオブジェクトが含まれることを意味します。これには、標準指標タイプの計算および描画オブジェクトも含まれます。
特定の指標に属するバッファオブジェクトを検索するには、リストを標準指標タイプ、ID、バッファタイプでさらに並べ替える必要があります。
標準指標を処理するバッファオブジェクトを作成するためのメソッドの宣言をクラスのpublicセクションに追加します。
//--- Create the new buffer (1) "Drawing with arrows", (2) "Line", (3) "Sections", (4) "Histogram from the zero line", //--- (5) "Histogram on two indicator buffers", (6) "Zigzag", (7) "Color filling between two levels", //--- (8) "Display as bars", (9) "Display as candles", calculated buffer bool CreateArrow(void) { return this.CreateBuffer(BUFFER_STATUS_ARROW); } bool CreateLine(void) { return this.CreateBuffer(BUFFER_STATUS_LINE); } bool CreateSection(void) { return this.CreateBuffer(BUFFER_STATUS_SECTION); } bool CreateHistogram(void) { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM); } bool CreateHistogram2(void) { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM2); } bool CreateZigZag(void) { return this.CreateBuffer(BUFFER_STATUS_ZIGZAG); } bool CreateFilling(void) { return this.CreateBuffer(BUFFER_STATUS_FILLING); } bool CreateBars(void) { return this.CreateBuffer(BUFFER_STATUS_BARS); } bool CreateCandles(void) { return this.CreateBuffer(BUFFER_STATUS_CANDLES); } bool CreateCalculate(void) { return this.CreateBuffer(BUFFER_STATUS_NONE); } //--- Create a multi-symbol multi-period indicator int CreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id=WRONG_VALUE); int CreateAD(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume,const int id=WRONG_VALUE); int CreateADX(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period,const int id=WRONG_VALUE); int CreateADXWilder(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period,const int id=WRONG_VALUE); int CreateAlligator(const string symbol,const ENUM_TIMEFRAMES timeframe, const int jaw_period, const int jaw_shift, const int teeth_period, const int teeth_shift, const int lips_period, const int lips_shift, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateAMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ama_period, const int fast_ma_period, const int slow_ma_period, const int ama_shift, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateAO(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id=WRONG_VALUE); int CreateATR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period,const int id=WRONG_VALUE); int CreateBearsPower(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period,const int id=WRONG_VALUE); int CreateBands(const string symbol,const ENUM_TIMEFRAMES timeframe, const int bands_period, const int bands_shift, const double deviation, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateBullsPower(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period,const int id=WRONG_VALUE); int CreateCCI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateChaikin(const string symbol,const ENUM_TIMEFRAMES timeframe, const int fast_ma_period, const int slow_ma_period, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_VOLUME applied_volume, const int id=WRONG_VALUE); int CreateDEMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateDeMarker(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period,const int id=WRONG_VALUE); int CreateEnvelopes(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_PRICE applied_price, const double deviation, const int id=WRONG_VALUE); int CreateForce(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_VOLUME applied_volume, const int id=WRONG_VALUE); int CreateFractals(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id=WRONG_VALUE); int CreateFrAMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateGator(const string symbol,const ENUM_TIMEFRAMES timeframe, const int jaw_period, const int jaw_shift, const int teeth_period, const int teeth_shift, const int lips_period, const int lips_shift, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateIchimoku(const string symbol,const ENUM_TIMEFRAMES timeframe, const int tenkan_sen, const int kijun_sen, const int senkou_span_b, const int id=WRONG_VALUE); int CreateBWMFI(const string symbol,const ENUM_TIMEFRAMES timeframe, const ENUM_APPLIED_VOLUME applied_volume, const int id=WRONG_VALUE); int CreateMomentum(const string symbol,const ENUM_TIMEFRAMES timeframe, const int mom_period, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateMFI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const ENUM_APPLIED_VOLUME applied_volume, const int id=WRONG_VALUE); int CreateMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateOsMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int fast_ema_period, const int slow_ema_period, const int signal_period, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateMACD(const string symbol,const ENUM_TIMEFRAMES timeframe, const int fast_ema_period, const int slow_ema_period, const int signal_period, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateOBV(const string symbol,const ENUM_TIMEFRAMES timeframe, const ENUM_APPLIED_VOLUME applied_volume, const int id=WRONG_VALUE); int CreateSAR(const string symbol,const ENUM_TIMEFRAMES timeframe, const double step, const double maximum, const int id=WRONG_VALUE); int CreateRSI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateRVI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period,const int id=WRONG_VALUE); int CreateStdDev(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateStochastic(const string symbol,const ENUM_TIMEFRAMES timeframe, const int Kperiod, const int Dperiod, const int slowing, const ENUM_MA_METHOD ma_method, const ENUM_STO_PRICE price_field, const int id=WRONG_VALUE); int CreateTEMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateTriX(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateWPR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int calc_period,const int id=WRONG_VALUE); int CreateVIDYA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int cmo_period, const int ema_period, const int ma_shift, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE); int CreateVolumes(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume,const int id=WRONG_VALUE);
特定の標準指標タイプはそれぞれ、適切な指標と必要なバッファオブジェクトを作成する独自の方法を使用します。
例として、ここではAC指標の処理を実装します。クラス本体を超えてAC指標とそのバッファを作成するメソッドを書いてみましょう。
//+------------------------------------------------------------------+ //| Create multi-symbol multi-period AC | //+------------------------------------------------------------------+ int CBuffersCollection::CreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id=WRONG_VALUE) { //--- Create the indicator handle and set the default ID int handle=::iAC(symbol,timeframe); int identifier=(id==WRONG_VALUE ? IND_AC : id); if(handle!=INVALID_HANDLE) { //--- Create the histogram buffer from the zero line this.CreateHistogram(); //--- Get the last created (drawn) buffer object and set all the necessary parameters to it CBuffer *buff=this.GetLastCreateBuffer(); buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_AC); buff.SetShowData(true); buff.SetLabel("AC("+symbol+","+TimeframeDescription(timeframe)+")"); buff.SetIndicatorName("Accelerator Oscillator"); //--- Create a calculated buffer storing standard indicator data this.CreateCalculate(); //--- Get the last created (calculated) buffer object and set all the necessary parameters to it buff=this.GetLastCreateBuffer(); buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_AC); buff.SetEmptyValue(EMPTY_VALUE); buff.SetLabel("AC("+symbol+","+TimeframeDescription(timeframe)+")"); buff.SetIndicatorName("Accelerator Oscillator"); } return handle; } //+------------------------------------------------------------------+
ご覧のとおり、ここではすべてが簡単です。IDとして-1が渡された場合、IDは標準指標タイプ定数と等しくなります。指標が正常に作成された場合(そのハンドルがINVALID_HANDLEと等しくない場合)、「ゼロ線からのヒストグラム」描画タイプのバッファオブジェクトを作成し、GetLastCreateBuffer()メソッドを使用して最後に作成されたバッファへのポインタを返し(メソッドは後で検討します)、ヒストグラムバッファオブジェクトへのポインタを取得し、標準AC指標のデータを描画するためのバッファとしての識別に必要なパラメータを設定します。
次に、計算バッファについても同じことを行います。計算バッファに、ハンドルにアクセスしたときに取得したAC指標データを書き込みます。作成された指標のハンドルは、バッファオブジェクトのプロパティで設定されます。これは、描画バッファオブジェクトと計算バッファオブジェクトの両方に当てはまります。つまり、これらのバッファオブジェクトのいずれかを取得し、オブジェクトに設定されたハンドルで指標にアクセスし、指標を操作できます。
必要なバッファオブジェクトを使用してAD指標を作成するメソッドの実装を追加して、さまざまなタイプの標準指標のメソッドの実装の違いを確認します。
//+------------------------------------------------------------------+ //| Create multi-symbol multi-period AD | //+------------------------------------------------------------------+ int CBuffersCollection::CreateAD(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume,const int id=WRONG_VALUE) { //--- Create the indicator handle and set the default ID int handle=::iAD(symbol,timeframe,applied_volume); int identifier=(id==WRONG_VALUE ? IND_AD : id); if(handle!=INVALID_HANDLE) { //--- Create the line buffer this.CreateLine(); //--- Get the last created (drawn) buffer object and set all the necessary parameters to it CBuffer *buff=this.GetLastCreateBuffer(); buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_AD); buff.SetShowData(true); buff.SetLabel("AD("+symbol+","+TimeframeDescription(timeframe)+")"); buff.SetIndicatorName("Accumulation/Distribution"); //--- Create a calculated buffer storing standard indicator data this.CreateCalculate(); //--- Get the last created (calculated) buffer object and set all the necessary parameters to it buff=this.GetLastCreateBuffer(); buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_AD); buff.SetEmptyValue(EMPTY_VALUE); buff.SetLabel("AD("+symbol+","+TimeframeDescription(timeframe)+")"); buff.SetIndicatorName("Accumulation/Distribution"); } return handle; } //+------------------------------------------------------------------+
違いは小さく、主に描画バッファタイプ、標準指標タイプ、グラフィカルシリーズ名、指標名に関連しています。他のタイプの標準指標では、標準指標を処理するために、(必要に応じて)描画バッファオブジェクトと計算バッファオブジェクトの数が異なります。
標準指標を作成するメソッドを宣言した直後に残りのメソッドの宣言を追加します。
//--- Prepare calculated buffer data of the specified standard indicator int PreparingDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int total_copy); //--- Clear buffer data of the specified standard indicator by the timeseries index void ClearDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int series_index); //--- Set the values for the current chart to the specified standard indicator buffer by the timeseries index according to the buffer object period/symbol bool SetDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int series_index,const datetime series_time,const char color_index=WRONG_VALUE); //--- Return the buffer (1) by the graphical series name, (2) by timeframe, //--- (3) by Plot index, (4) by object index in the collection list, (5) the last created one, //--- list of buffers (6) by ID, (7) by standard indicator type, (8) by type and ID CBuffer *GetBufferByLabel(const string plot_label); CBuffer *GetBufferByTimeframe(const ENUM_TIMEFRAMES timeframe); CBuffer *GetBufferByPlot(const int plot_index); CBuffer *GetBufferByListIndex(const int index_list); CBuffer *GetLastCreateBuffer(void); CArrayObj *GetListBufferByID(const int id); CArrayObj *GetListBufferByIndType(const ENUM_INDICATOR indicator_type); CArrayObj *GetListBufferByTypeID(const ENUM_INDICATOR indicator_type,const int id);
宣言されたすべてのメソッドは、コメントで名前が付けられています。クラス本体の外での実装について考えてみましょう。
以下は、最後に作成されたバッファオブジェクトを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the last created buffer | //+------------------------------------------------------------------+ CBuffer *CBuffersCollection::GetLastCreateBuffer(void) { return this.m_list.At(this.m_list.Total()-1); } //+------------------------------------------------------------------+
このメソッドは、バッファオブジェクトのリストの最後にあるバッファオブジェクトへのポインタを返すだけです。
以下は、IDごとにバッファオブジェクトのリストを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the list of buffers by ID | //+------------------------------------------------------------------+ CArrayObj *CBuffersCollection::GetListBufferByID(const int id) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_ID,id,EQUAL); return list; } //+------------------------------------------------------------------+
メソッドに渡されたものと等しいIDを備えたバッファオブジェクトのリストを取得します。
メソッドから取得したリストへのポインタを返します。
以下は、標準指標タイプごとにバッファオブジェクトのリストを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the list of buffers by the standard indicator type | //+------------------------------------------------------------------+ CArrayObj *CBuffersCollection::GetListBufferByIndType(const ENUM_INDICATOR indicator_type) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_IND_TYPE,indicator_type,EQUAL); return list; } //+------------------------------------------------------------------+
メソッドに渡されたものと等しい標準指標タイプを備えたバッファオブジェクトのリストを取得します。
メソッドから取得したリストへのポインタを返します。
以下は、標準指標タイプとIDごとにバッファオブジェクトのリストを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the list of buffers by type and ID | //+------------------------------------------------------------------+ CArrayObj *CBuffersCollection::GetListBufferByTypeID(const ENUM_INDICATOR indicator_type,const int id) { CArrayObj *list=this.GetListBufferByIndType(indicator_type); list=CSelect::ByBufferProperty(list,BUFFER_PROP_ID,id,EQUAL); return list; } //+------------------------------------------------------------------+
まず、プロパティに指定されたタイプの標準指標を含むバッファオブジェクトのリストを取得します。次に、取得したリストを、プロパティが指定されたIDを特徴とするバッファオブジェクトで並べ替えます。
結果のリストへのポインタがメソッドから返されます。
以下は、標準指標タイプにバッファオブジェクトのリストを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the list of indicator buffers | //| (featuring the ID of belonging to an indicator) | //+------------------------------------------------------------------+ CArrayObj *CBuffersCollection::GetListBuffersWithID(void) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_ID,WRONG_VALUE,NO_EQUAL); return list; } //+------------------------------------------------------------------+
バッファオブジェクトのコレクションリストから、IDプロパティが-1と等しくないオブジェクトのリストを取得します 。
メソッドから取得したリストへのポインタを返します。
CEngineライブラリのメインオブジェクトクラスは、プログラムとライブラリの間のリンクです。
クラスファイル(\MQL5\Include\DoEasy\Engine.mqh)に必要な改善を加えましょう。
すべての時間列を再作成するメソッドをクラスのpublicセクションに追加します。
//--- Re-create (1) the specified timeseries of the specified symbol, (2) all collection timeseries bool SeriesReCreate(const string symbol,const ENUM_TIMEFRAMES timeframe,const int rates_total=0,const uint required=0) { return this.m_time_series.ReCreateSeries(symbol,timeframe,rates_total,required); } bool SeriesReCreateAll(const int rates_total=0,const uint required=0) { return this.m_time_series.ReCreateSeriesAll(rates_total,required); }
このメソッドは、上記で追加した同じ名前の時系列収集メソッドの結果を返すだけです。
クラスのpublicセクションで、指定された時系列のバーの数を返すメソッドを追加します。
//--- Return (1) an empty, (2) partially filled timeseries CSeriesDE *SeriesGetSeriesEmpty(void) { return this.m_time_series.GetSeriesEmpty(); } CSeriesDE *SeriesGetSeriesIncompleted(void) { return this.m_time_series.GetSeriesIncompleted(); } //--- Return the umber of bars of the timeseries of the specified symbol/period int SeriesGetBarsTotal(const string symbol,const ENUM_TIMEFRAMES timeframe);
クラス本体の外にメソッドを実装します。
//+---------------------------------------------------------------------------+ //| Return the umber of bars of the timeseries of the specified symbol/period | //+---------------------------------------------------------------------------+ int CEngine::SeriesGetBarsTotal(const string symbol,const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.SeriesGetSeries(symbol,timeframe); if(series==NULL) return WRONG_VALUE; return (int)series.Bars(); } //+------------------------------------------------------------------+
時系列コレクションクラスから指定された時系列を取得し、時系列バーの数を返します。
以前は、最後に作成されたバッファを返すメソッドがありました。
//--- Return the buffer by (1) the graphical series name, (2) timeframe, (3) Plot index, (4) collection list and (5) the last one in the list CBuffer *GetBufferByLabel(const string plot_label) { return this.m_buffers.GetBufferByLabel(plot_label); } CBuffer *GetBufferByTimeframe(const ENUM_TIMEFRAMES timeframe) { return this.m_buffers.GetBufferByTimeframe(timeframe);} CBuffer *GetBufferByPlot(const int plot_index) { return this.m_buffers.GetBufferByPlot(plot_index); } CBuffer *GetBufferByListIndex(const int index_list) { return this.m_buffers.GetBufferByListIndex(index_list);} CBuffer *GetLastBuffer(void);
以下が実装です。
//+------------------------------------------------------------------+ //| Return the last indicator buffer | //| in the indicator buffer collection list | //+------------------------------------------------------------------+ CBuffer *CEngine::GetLastBuffer(void) { CArrayObj *list=this.GetListBuffers(); if(list==NULL) return NULL; return list.At(list.Total()-1); } //+------------------------------------------------------------------+
クラスリストからメソッド実装を削除して、宣言を新しいメソッドに置き換えます。
//--- Return the buffer by (1) the graphical series name, (2) timeframe, (3) Plot index, (4) collection list and (5) the last one in the list CBuffer *GetBufferByLabel(const string plot_label) { return this.m_buffers.GetBufferByLabel(plot_label); } CBuffer *GetBufferByTimeframe(const ENUM_TIMEFRAMES timeframe) { return this.m_buffers.GetBufferByTimeframe(timeframe);} CBuffer *GetBufferByPlot(const int plot_index) { return this.m_buffers.GetBufferByPlot(plot_index); } CBuffer *GetBufferByListIndex(const int index_list) { return this.m_buffers.GetBufferByListIndex(index_list);} CBuffer *GetLastCreateBuffer(void) { return this.m_buffers.GetLastCreateBuffer(); }
このメソッドは、上記で検討した同じ名前のバッファコレクションクラスメソッドの結果を返します。
クラスのpublicセクションで、その操作用のAC標準指標とバッファを作成するための1つのメソッドを追加します。
//--- Create the new buffer (1) "Drawing with arrows", (2) "Line", (3) "Sections", (4) "Histogram from the zero line", //--- (5) "Histogram on two indicator buffers", (6) "Zigzag", (7) "Color filling between two levels", //--- (8) "Display as bars", (9) "Display as candles", calculated buffer bool BufferCreateArrow(void) { return this.m_buffers.CreateArrow(); } bool BufferCreateLine(void) { return this.m_buffers.CreateLine(); } bool BufferCreateSection(void) { return this.m_buffers.CreateSection(); } bool BufferCreateHistogram(void) { return this.m_buffers.CreateHistogram(); } bool BufferCreateHistogram2(void) { return this.m_buffers.CreateHistogram2(); } bool BufferCreateZigZag(void) { return this.m_buffers.CreateZigZag(); } bool BufferCreateFilling(void) { return this.m_buffers.CreateFilling(); } bool BufferCreateBars(void) { return this.m_buffers.CreateBars(); } bool BufferCreateCandles(void) { return this.m_buffers.CreateCandles(); } bool BufferCreateCalculate(void) { return this.m_buffers.CreateCalculate(); } //--- The methods of creating standard indicators and buffer objects for them bool BufferCreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id) { return(this.m_buffers.CreateAC(symbol,timeframe,id)!=INVALID_HANDLE); } //--- Initialize all drawn buffers by a (1) specified value, (2) empty value set for the buffer object
このメソッドは、上記で検討した指標バッファコレクションクラスから同じ名前のAC指標作成メソッドの結果を返します。他の標準指標を作成するメソッドは、次の記事に追加されます。
以下は、標準指標用の計算バッファデータを準備するメソッドの実装です(これまでのところACのみ)。
//+------------------------------------------------------------------+ //| Prepare the calculated buffer data | //| of the specified standard indicator | //+------------------------------------------------------------------+ int CBuffersCollection::PreparingDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int total_copy) { CArrayObj *list=this.GetListBufferByTypeID(std_ind,id); list=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL); if(list==NULL || list.Total()==0) return 0; CBufferCalculate *buffer=NULL; int copies=WRONG_VALUE; switch((int)std_ind) { case IND_AC : buffer=list.At(0); if(buffer==NULL) return 0; copies=buffer.FillAsSeries(buffer.IndicatorHandle(),0,0,total_copy); return copies; case IND_AD : break; case IND_ADX : break; case IND_ADXW : break; case IND_ALLIGATOR : break; case IND_AMA : break; case IND_AO : break; case IND_ATR : break; case IND_BANDS : break; case IND_BEARS : break; case IND_BULLS : break; case IND_BWMFI : break; case IND_CCI : break; case IND_CHAIKIN : break; case IND_DEMA : break; case IND_DEMARKER : break; case IND_ENVELOPES : break; case IND_FORCE : break; case IND_FRACTALS : break; case IND_FRAMA : break; case IND_GATOR : break; case IND_ICHIMOKU : break; case IND_MA : break; case IND_MACD : break; case IND_MFI : break; case IND_MOMENTUM : break; case IND_OBV : break; case IND_OSMA : break; case IND_RSI : break; case IND_RVI : break; case IND_SAR : break; case IND_STDDEV : break; case IND_STOCHASTIC : break; case IND_TEMA : break; case IND_TRIX : break; case IND_VIDYA : break; case IND_VOLUMES : break; case IND_WPR : break; default: break; } return 0; } //+------------------------------------------------------------------+
指標タイプとIDでバッファオブジェクトのリストを取得し、取得したリストに計算されたバッファのみを残し、最初の(ACの場合のみ)計算バッファを取得し、リストから、計算バッファに指定された量のデータを入力し、正常にコピーされたデータの数を指標ハンドルから 計算バッファに返します。
以下は、指定されたインデックスによって標準指標の計算バッファデータをクリアするメソッドの実装(これまでのところACのみ)です。
//+------------------------------------------------------------------+ //| Clear buffer data of the specified standard indicator | //| by the timeseries index | //+------------------------------------------------------------------+ void CBuffersCollection::ClearDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int series_index) { CArrayObj *list=this.GetListBufferByID(id); if(list==NULL) return; list=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL); if(list.Total()==0) return; CBuffer *buffer=NULL; switch((int)std_ind) { case IND_AC : buffer=list.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_AD : break; case IND_ADX : break; case IND_ADXW : break; case IND_ALLIGATOR : break; case IND_AMA : break; case IND_AO : break; case IND_ATR : break; case IND_BANDS : break; case IND_BEARS : break; case IND_BULLS : break; case IND_BWMFI : break; case IND_CCI : break; case IND_CHAIKIN : break; case IND_DEMA : break; case IND_DEMARKER : break; case IND_ENVELOPES : break; case IND_FORCE : break; case IND_FRACTALS : break; case IND_FRAMA : break; case IND_GATOR : break; case IND_ICHIMOKU : break; case IND_MA : break; case IND_MACD : break; case IND_MFI : break; case IND_MOMENTUM : break; case IND_OBV : break; case IND_OSMA : break; case IND_RSI : break; case IND_RVI : break; case IND_SAR : break; case IND_STDDEV : break; case IND_STOCHASTIC : break; case IND_TEMA : break; case IND_TRIX : break; case IND_VIDYA : break; case IND_VOLUMES : break; case IND_WPR : break; default: break; } } //+------------------------------------------------------------------+
このメソッドは、データ準備方法と同様に機能します。ただし、指標ハンドルから計算バッファにデータをコピーする代わりに、指定されたインデックスによって描画バッファにバッファオブジェクトに設定された空の値がここで指定されます。
以下は、現在のチャートに描画バッファを任意の銘柄/時間枠からの標準指標データで埋めるメソッドの実装です(これまでのところACのみ)。
//+------------------------------------------------------------------+ //| Set values for the current chart to the specified buffer | //| of the standard indicator by the timeseries index according to | //| the buffer object symbol/period | //+------------------------------------------------------------------+ bool CBuffersCollection::SetDataBufferStdInd(const ENUM_INDICATOR ind_type,const int id,const int series_index,const datetime series_time,const char color_index=WRONG_VALUE) { //--- Get the list of buffer objects with ID CArrayObj *list=this.GetListBufferByTypeID(ind_type,id); if(list==NULL) return false; //--- Get the list of drawn objects with ID CArrayObj *list_data=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL); list_data=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_TYPE,ind_type,EQUAL); //--- Get the list of calculated buffers with ID CArrayObj *list_calc=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL); list_calc=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_TYPE,ind_type,EQUAL); //--- Exit if any of the lists is empty if(list_data.Total()==0 || list_calc.Total()==0) return false; //--- Declare the necessary objects and variables CBuffer *buffer_data=NULL; CBuffer *buffer_calc=NULL; int index_period=0; int series_index_start=0; int num_bars=1,index=0; datetime time_period=0; double value0=EMPTY_VALUE, value1=EMPTY_VALUE; //--- Depending on the standard indicator type switch((int)ind_type) { case IND_AC : //--- Get drawn and calculated buffer objects buffer_data=list_data.At(0); buffer_calc=list_calc.At(0); if(buffer_calc==NULL || buffer_data==NULL || buffer_calc.GetDataTotal(0)==0) return false; //--- Find the bar index corresponding to the current bar start time index_period=::iBarShift(buffer_calc.Symbol(),buffer_calc.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc.GetDataTotal()-1) return false; //--- Get the value by the index from the indicator buffer value0=buffer_calc.GetDataBufferValue(0,index_period); if(buffer_calc.Symbol()==::Symbol() && buffer_calc.Timeframe()==::Period()) { series_index_start=series_index; num_bars=1; } else { //--- Get the bar time the bar with the index_period index falls into on the calculated buffer period and symbol time_period=::iTime(buffer_calc.Symbol(),buffer_calc.Timeframe(),index_period); if(time_period==0) return false; //--- Get the appropriate current chart bar series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true); if(series_index_start==WRONG_VALUE) return false; //--- Calculate the number of bars on the current chart which should be filled with calculated buffer data num_bars=::PeriodSeconds(buffer_calc.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Take values to calculate colors value1=(series_index_start+num_bars>buffer_data.GetDataTotal()-1 ? value0 : buffer_data.GetDataBufferValue(0,series_index_start+num_bars)); //--- In the loop by the number of bars in num_bars, fill in the drawn buffer with the calculated buffer value taken by the index_period index //--- and set the color of the drawn buffer depending on the value0 and value1 values ratio for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data.SetBufferValue(0,index,value0); buffer_data.SetBufferColorIndex(index,uchar(value0>value1 ? 0 : value0<value1 ? 1 : 2)); } break; case IND_AD : break; case IND_ADX : break; case IND_ADXW : break; case IND_ALLIGATOR : break; case IND_AMA : break; case IND_AO : break; case IND_ATR : break; case IND_BANDS : break; case IND_BEARS : break; case IND_BULLS : break; case IND_BWMFI : break; case IND_CCI : break; case IND_CHAIKIN : break; case IND_DEMA : break; case IND_DEMARKER : break; case IND_ENVELOPES : break; case IND_FORCE : break; case IND_FRACTALS : break; case IND_FRAMA : break; case IND_GATOR : break; case IND_ICHIMOKU : break; case IND_MA : break; case IND_MACD : break; case IND_MFI : break; case IND_MOMENTUM : break; case IND_OBV : break; case IND_OSMA : break; case IND_RSI : break; case IND_RVI : break; case IND_SAR : break; case IND_STDDEV : break; case IND_STOCHASTIC : break; case IND_TEMA : break; case IND_TRIX : break; case IND_VIDYA : break; case IND_VOLUMES : break; case IND_WPR : break; default: break; } return true; } //+------------------------------------------------------------------+
ACのデータ計算に関連するメソッドロジック全体は、コメントで詳しく説明されています。
クラス本体の最後で、ライブラリイベントを処理するための2つのメソッドを宣言します。
public: //--- Create and return the composite magic number from the specified magic number value, the first and second group IDs and the pending request ID uint SetCompositeMagicNumber(ushort magic_id,const uchar group_id1=0,const uchar group_id2=0,const uchar pending_req_id=0); //--- Handling DoEasy library events void OnDoEasyEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- Working with events in the tester void EventsHandling(void); }; //+------------------------------------------------------------------+
以前は、カスタムプログラムでライブラリイベントを処理するために、新しく宣言されたメソッドと同じ名前の関数を使用していました。これらの関数を変更せずに、プログラム間で渡しました。これは、これらのハンドラをライブラリに転送できることを示していますが、プログラムでは、発生したイベントのフラグを簡単に受け取ることができます(フラグ、イベントフラグ自体、およびカスタムプログラムでイベントを処理する機能を受け取ることは次のとおりです。 後で実装されます)。
これらの関数をテスト指標からCEngineクラスリストに移動し、上記で宣言されたメソッドを実装しました。
//+------------------------------------------------------------------+ //| Handling DoEasy library events | //+------------------------------------------------------------------+ void CEngine::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=this.EventMSC(lparam); ushort reason=this.EventReason(lparam); ushort source=this.EventSource(lparam); long time=::TimeCurrent()*1000+msc; //--- Handling symbol events if(source==COLLECTION_SYMBOLS_ID) { CSymbol *symbol=this.GetSymbolObjByName(sparam); if(symbol==NULL) return; //--- Number of decimal places in the event value - in case of a 'long' event, it is 0, otherwise - Digits() of a symbol int digits=(idx<SYMBOL_PROP_INTEGER_TOTAL ? 0 : symbol.Digits()); //--- Event text description string id_descr=(idx<SYMBOL_PROP_INTEGER_TOTAL ? symbol.GetPropertyDescription((ENUM_SYMBOL_PROP_INTEGER)idx) : symbol.GetPropertyDescription((ENUM_SYMBOL_PROP_DOUBLE)idx)); //--- Property change text value string value=::DoubleToString(dparam,digits); //--- Check event reasons and display its description in the journal if(reason==BASE_EVENT_REASON_INC) { ::Print(DFUN,symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_DEC) { ::Print(DFUN,symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_MORE_THEN) { ::Print(DFUN,symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_LESS_THEN) { ::Print(DFUN,symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_EQUALS) { ::Print(DFUN,symbol.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } } //--- Handling account events else if(source==COLLECTION_ACCOUNT_ID) { CAccount *account=this.GetAccountCurrent(); if(account==NULL) return; //--- Number of decimal places in the event value - in case of a 'long' event, it is 0, otherwise - Digits() of a symbol int digits=int(idx<ACCOUNT_PROP_INTEGER_TOTAL ? 0 : account.CurrencyDigits()); //--- Event text description string id_descr=(idx<ACCOUNT_PROP_INTEGER_TOTAL ? account.GetPropertyDescription((ENUM_ACCOUNT_PROP_INTEGER)idx) : account.GetPropertyDescription((ENUM_ACCOUNT_PROP_DOUBLE)idx)); //--- Property change text value string value=::DoubleToString(dparam,digits); //--- Checking event reasons and handling the increase of funds by a specified value, //--- Display an event in the journal if(reason==BASE_EVENT_REASON_INC) { ::Print(DFUN,account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_DEC) { ::Print(DFUN,account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_MORE_THEN) { ::Print(DFUN,account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_LESS_THEN) { ::Print(DFUN,account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } if(reason==BASE_EVENT_REASON_EQUALS) { ::Print(DFUN,account.EventDescription(idx,(ENUM_BASE_EVENT_REASON)reason,source,value,id_descr,digits)); } } //--- Handling market watch window events else if(idx>MARKET_WATCH_EVENT_NO_EVENT && idx<SYMBOL_EVENTS_NEXT_CODE) { //--- Market Watch window event string descr=this.GetMWEventDescription((ENUM_MW_EVENT)idx); string name=(idx==MARKET_WATCH_EVENT_SYMBOL_SORT ? "" : ": "+sparam); Print(TimeMSCtoString(lparam)," ",descr,name); } //--- 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(DFUN,TextByLanguage("Новый бар на ","New Bar on "),sparam," ",TimeframeDescription((ENUM_TIMEFRAMES)dparam),": ",TimeToString(lparam)); CArrayObj *list=this.m_buffers.GetListBuffersWithID(); if(list!=NULL) { int total=list.Total(); for(int i=0;i<total;i++) { CBuffer *buff=list.At(i); if(buff==NULL) continue; string symbol=sparam; ENUM_TIMEFRAMES timeframe=(ENUM_TIMEFRAMES)dparam; if(buff.TypeBuffer()==BUFFER_TYPE_DATA || buff.IndicatorType()==WRONG_VALUE) continue; if(buff.Symbol()==symbol && buff.Timeframe()==timeframe ) { CSeriesDE *series=this.SeriesGetSeries(symbol,timeframe); if(series==NULL) continue; int count=::fmin(buff.GetDataTotal(),buff.IndicatorBarsCalculated()); this.m_buffers.PreparingDataBufferStdInd(buff.IndicatorType(),buff.ID(),count); } } } } //--- "Bars skipped" event if(idx==SERIES_EVENTS_MISSING_BARS) { ::Print(DFUN,TextByLanguage("Пропущены бары на ","Missed bars on "),sparam," ",TimeframeDescription((ENUM_TIMEFRAMES)dparam),": ",(string)lparam); } } //--- Handling trading events else if(idx>TRADE_EVENT_NO_EVENT && idx<TRADE_EVENTS_NEXT_CODE) { //--- Get the list of trading events CArrayObj *list=this.GetListAllOrdersEvents(); if(list==NULL) return; //--- get the event index shift relative to the end of the list //--- in the tester, the shift is passed by the lparam parameter to the event handler //--- outside the tester, events are sent one by one and handled in OnChartEvent() int shift=(this.IsTester() ? (int)lparam : 0); CEvent *event=list.At(list.Total()-1-shift); if(event==NULL) return; //--- Accrue the credit if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_CREDIT) { ::Print(DFUN,event.TypeEventDescription()); } //--- Additional charges if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_CHARGE) { ::Print(DFUN,event.TypeEventDescription()); } //--- Correction if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_CORRECTION) { ::Print(DFUN,event.TypeEventDescription()); } //--- Enumerate bonuses if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_BONUS) { ::Print(DFUN,event.TypeEventDescription()); } //--- Additional commissions if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION) { ::Print(DFUN,event.TypeEventDescription()); } //--- Daily commission if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION_DAILY) { ::Print(DFUN,event.TypeEventDescription()); } //--- Monthly commission if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY) { ::Print(DFUN,event.TypeEventDescription()); } //--- Daily agent commission if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY) { ::Print(DFUN,event.TypeEventDescription()); } //--- Monthly agent commission if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY) { ::Print(DFUN,event.TypeEventDescription()); } //--- Interest rate if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_INTEREST) { ::Print(DFUN,event.TypeEventDescription()); } //--- Canceled buy deal if(event.TypeEvent()==TRADE_EVENT_BUY_CANCELLED) { ::Print(DFUN,event.TypeEventDescription()); } //--- Canceled sell deal if(event.TypeEvent()==TRADE_EVENT_SELL_CANCELLED) { ::Print(DFUN,event.TypeEventDescription()); } //--- Dividend operations if(event.TypeEvent()==TRADE_EVENT_DIVIDENT) { ::Print(DFUN,event.TypeEventDescription()); } //--- Accrual of franked dividend if(event.TypeEvent()==TRADE_EVENT_DIVIDENT_FRANKED) { ::Print(DFUN,event.TypeEventDescription()); } //--- Tax charges if(event.TypeEvent()==TRADE_EVENT_TAX) { ::Print(DFUN,event.TypeEventDescription()); } //--- Replenishing account balance if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_BALANCE_REFILL) { ::Print(DFUN,event.TypeEventDescription()); } //--- Withdrawing funds from balance if(event.TypeEvent()==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL) { ::Print(DFUN,event.TypeEventDescription()); } //--- Pending order placed if(event.TypeEvent()==TRADE_EVENT_PENDING_ORDER_PLASED) { ::Print(DFUN,event.TypeEventDescription()); } //--- Pending order removed if(event.TypeEvent()==TRADE_EVENT_PENDING_ORDER_REMOVED) { ::Print(DFUN,event.TypeEventDescription()); } //--- Pending order activated by price if(event.TypeEvent()==TRADE_EVENT_PENDING_ORDER_ACTIVATED) { ::Print(DFUN,event.TypeEventDescription()); } //--- Pending order partially activated by price if(event.TypeEvent()==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL) { ::Print(DFUN,event.TypeEventDescription()); } //--- Position opened if(event.TypeEvent()==TRADE_EVENT_POSITION_OPENED) { ::Print(DFUN,event.TypeEventDescription()); } //--- Position opened partially if(event.TypeEvent()==TRADE_EVENT_POSITION_OPENED_PARTIAL) { ::Print(DFUN,event.TypeEventDescription()); } //--- Position closed if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED) { ::Print(DFUN,event.TypeEventDescription()); } //--- Position closed by an opposite one if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_BY_POS) { ::Print(DFUN,event.TypeEventDescription()); } //--- Position closed by StopLoss if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_BY_SL) { ::Print(DFUN,event.TypeEventDescription()); } //--- Position closed by TakeProfit if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_BY_TP) { ::Print(DFUN,event.TypeEventDescription()); } //--- Position reversal by a new deal (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_REVERSED_BY_MARKET) { ::Print(DFUN,event.TypeEventDescription()); } //--- Position reversal by activating a pending order (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_REVERSED_BY_PENDING) { ::Print(DFUN,event.TypeEventDescription()); } //--- Position reversal by partial market order execution (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL) { ::Print(DFUN,event.TypeEventDescription()); } //--- Position reversal by activating a pending order (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL) { ::Print(DFUN,event.TypeEventDescription()); } //--- Added volume to a position by a new deal (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET) { ::Print(DFUN,event.TypeEventDescription()); } //--- Added volume to a position by partial execution of a market order (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL) { ::Print(DFUN,event.TypeEventDescription()); } //--- Added volume to a position by activating a pending order (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING) { ::Print(DFUN,event.TypeEventDescription()); } //--- Added volume to a position by partial activation of a pending order (netting) if(event.TypeEvent()==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL) { ::Print(DFUN,event.TypeEventDescription()); } //--- Position closed partially if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL) { ::Print(DFUN,event.TypeEventDescription()); } //--- Position partially closed by an opposite one if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS) { ::Print(DFUN,event.TypeEventDescription()); } //--- Position closed partially by StopLoss if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL) { ::Print(DFUN,event.TypeEventDescription()); } //--- Position closed partially by TakeProfit if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP) { ::Print(DFUN,event.TypeEventDescription()); } //--- StopLimit order activation if(event.TypeEvent()==TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER) { ::Print(DFUN,event.TypeEventDescription()); } //--- Changing order price if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE) { ::Print(DFUN,event.TypeEventDescription()); } //--- Changing order and StopLoss price if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_SL) { ::Print(DFUN,event.TypeEventDescription()); } //--- Changing order and TakeProfit price if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_TP) { ::Print(DFUN,event.TypeEventDescription()); } //--- Changing order, StopLoss and TakeProfit price if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_SL_TP) { ::Print(DFUN,event.TypeEventDescription()); } //--- Changing order's StopLoss and TakeProfit price if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_SL_TP) { ::Print(DFUN,event.TypeEventDescription()); } //--- Changing order's StopLoss if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_SL) { ::Print(DFUN,event.TypeEventDescription()); } //--- Changing order's TakeProfit if(event.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_TP) { ::Print(DFUN,event.TypeEventDescription()); } //--- Changing position's StopLoss and TakeProfit if(event.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_SL_TP) { ::Print(DFUN,event.TypeEventDescription()); } //--- Changing position StopLoss if(event.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_SL) { ::Print(DFUN,event.TypeEventDescription()); } //--- Changing position TakeProfit if(event.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_TP) { ::Print(DFUN,event.TypeEventDescription()); } } } //+------------------------------------------------------------------+ //| Working with events in the tester | //+------------------------------------------------------------------+ void CEngine::EventsHandling(void) { //--- If a trading event is present if(this.IsTradeEvent()) { //--- Number of trading events occurred simultaneously int total=this.GetTradeEventsTotal(); for(int i=0;i<total;i++) { //--- Get the next event from the list of simultaneously occurred events by index CEventBaseObj *event=this.GetTradeEventByIndex(i); if(event==NULL) continue; long lparam=i; double dparam=event.DParam(); string sparam=event.SParam(); this.OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam); } } //--- If there is an account event if(this.IsAccountsEvent()) { //--- Get the list of all account events occurred simultaneously CArrayObj* list=this.GetListAccountEvents(); if(list!=NULL) { //--- Get the next event in a loop int total=list.Total(); for(int i=0;i<total;i++) { //--- take an event from the list CEventBaseObj *event=list.At(i); if(event==NULL) continue; //--- Send an event to the event handler long lparam=event.LParam(); double dparam=event.DParam(); string sparam=event.SParam(); this.OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam); } } } //--- If there is a symbol collection event if(this.IsSymbolsEvent()) { //--- Get the list of all symbol events occurred simultaneously CArrayObj* list=this.GetListSymbolsEvents(); if(list!=NULL) { //--- Get the next event in a loop int total=list.Total(); for(int i=0;i<total;i++) { //--- take an event from the list CEventBaseObj *event=list.At(i); if(event==NULL) continue; //--- Send an event to the event handler long lparam=event.LParam(); double dparam=event.DParam(); string sparam=event.SParam(); this.OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam); } } } //--- If there is a timeseries collection event if(this.IsSeriesEvent()) { //--- Get the list of all timeseries events occurred simultaneously CArrayObj* list=this.GetListSeriesEvents(); if(list!=NULL) { //--- Get the next event in a loop int total=list.Total(); for(int i=0;i<total;i++) { //--- take an event from the list CEventBaseObj *event=list.At(i); if(event==NULL) continue; //--- Send an event to the event handler long lparam=event.LParam(); double dparam=event.DParam(); string sparam=event.SParam(); this.OnDoEasyEvent(CHARTEVENT_CUSTOM+event.ID(),lparam,dparam,sparam); } } } } //+------------------------------------------------------------------+
テストEAを開発する際に、ライブラリの最初の説明記事でこれらの関数(現在はCEngineクラスのメソッド)をすでに検討しました。メソッドリストは、ほぼすべてのイベントに操作ログエントリが付随していることを明確に示しています。したがって、グローバル可視性スコープでイベントフラグのリストを作成し、必要なフラグを設定するだけで済みます。カスタムプログラムでは、アクティブ化された各フラグのハンドラを実装する方が簡単です。これは後で行います。
したがって、各カスタムプログラムでこれらのハンドラを指定する必要がなくなりました。
このクラスは、指標から呼び出される計算イベントハンドラを備えています。ハンドラによって返される値がゼロに等しい場合、これは。指標で使用されるすべての時系列がまだ構築されていないことを意味します。指標は、戻りコード0でOnCalculate()を終了する必要があります。これは、次のティックを待機し、データが計算されていないことを示します。
標準指標の処理を追加しているので、作成した指標が計算されていることを確認する必要があります。
計算されたデータの量を見つけるために、BarsCalculated()関数を使用して、指標によってすでに計算されたデータの量を返すことができます。データがまだ計算されていない場合、関数は-1を返します。
「計算」イベントを処理するメソッドにバッファコレクションに作成されたすべての標準指標が正常に計算されるかどうかの確認を追加します。
//+------------------------------------------------------------------+ //| Calculate event handler | //+------------------------------------------------------------------+ int CEngine::OnCalculate(SDataCalculate &data_calculate,const uint required=0) { //--- If this is not an indicator, exit if(this.m_program!=PROGRAM_INDICATOR) return 0; //--- Re-create empty timeseries //--- If at least one of the timeseries is not synchronized, return zero if(!this.SeriesSync(data_calculate,required)) return 0; //--- Update the timeseries of the current symbol (not in the tester) and //--- return either 0 (in case there are empty timeseries), or rates_total if(!this.IsTester()) this.SeriesRefresh(NULL,data_calculate); int res=(this.SeriesGetSeriesEmpty()==NULL ? data_calculate.rates_total : 0); //--- Check the amount of calculated standard indicator data CArrayObj *list=m_buffers.GetListBuffersWithID(); if(list!=NULL) { //--- In a loop by the number of buffers having an ID int total=list.Total(); for(int i=0;i<total;i++) { //--- get the next calculated buffer using the standard indicator CBuffer *buff=list.At(i); if(buff==NULL || buff.TypeBuffer()==BUFFER_TYPE_DATA || buff.IndicatorHandle()==INVALID_HANDLE) continue; //--- if the indicator data is not calculated yet, return zero if(buff.IndicatorBarsCalculated()==WRONG_VALUE) return 0; } } return res; } //+------------------------------------------------------------------+
作成された指標データを処理するロジックは、メソッドリストに記載されています。
本稿のライブラリの改訂の最後の仕上げとして、現在のチャート期間を使用時間枠のリストに追加します。
\MetaQuotes\MetaTrader 5\MQL5\Include\DoEasy\Services\DELib.mqhのライブラリサービス関数ファイルは、使用された時間枠のリストを準備する関数を備えています。現在のチャート期間がプログラム設定で指定されていない場合、ライブラリはその時系列を作成しません。ただし、時系列は常に必要です。
使用されている時間枠のリストで現在のグラフの期間を指定するためのコードブロックを追加して、CreateUsedTimeframesArray()関数を改善しましょう。
//+------------------------------------------------------------------+ //| Prepare the array of timeframes for the timeseries collection | //+------------------------------------------------------------------+ bool CreateUsedTimeframesArray(const ENUM_TIMEFRAMES_MODE mode_used_periods,string defined_used_periods,string &used_periods_array[]) { //--- If working with the current chart period, fill the array with the current timeframe description string if(mode_used_periods==TIMEFRAMES_MODE_CURRENT) { ArrayResize(used_periods_array,1,21); used_periods_array[0]=TimeframeDescription((ENUM_TIMEFRAMES)Period()); return true; } //--- If working with a predefined set of chart periods (from the defined_used_periods string) else if(mode_used_periods==TIMEFRAMES_MODE_LIST) { //--- Set comma as a separator (defined in the Datas.mqh file, page 11) string separator=INPUT_SEPARATOR; //--- Fill in the array of parameters from the string with predefined timeframes int n=StringParamsPrepare(defined_used_periods,separator,used_periods_array); //--- if nothing is found, display the appropriate message (working with the current period is selected automatically) if(n<1) { int err_code=GetLastError(); string err= (n==0 ? DFUN_ERR_LINE+CMessage::Text(MSG_LIB_SYS_ERROR_EMPTY_PERIODS_STRING)+TimeframeDescription((ENUM_TIMEFRAMES)Period()) : DFUN_ERR_LINE+CMessage::Text(MSG_LIB_SYS_FAILED_PREPARING_PERIODS_ARRAY)+(string)err_code+": "+CMessage::Text(err_code) ); Print(err); //--- Set the current period to the array ArrayResize(used_periods_array,1,21); used_periods_array[0]=TimeframeDescription((ENUM_TIMEFRAMES)Period()); return false; } } //--- If working with the full list of timeframes, fill in the array with strings describing all timeframes else { ArrayResize(used_periods_array,21,21); for(int i=0;i<21;i++) used_periods_array[i]=TimeframeDescription(TimeframeByEnumIndex(uchar(i+1))); } //--- Add the current chart timeframe to the list of used periods bool f=false; for(int i=0;i<ArraySize(used_periods_array);i++) { if(used_periods_array[i]==TimeframeDescription((ENUM_TIMEFRAMES)Period())) { f=true; break; } } //--- If the list of used periods features no timeframe of the current chart if(!f) { //--- Increase the array of used periods by 1 and add the current chart period to it ArrayResize(used_periods_array,ArraySize(used_periods_array)+1); used_periods_array[ArraySize(used_periods_array)-1]=TimeframeDescription((ENUM_TIMEFRAMES)Period()); } //--- All is successful return true; } //+------------------------------------------------------------------+
これで、ライブラリクラスの改善は完了です。
複数銘柄・複数期間標準Accelerator Oscillator指標の開発をテストする時が来ました。
検証
テストを実行するには、前の記事の指標を使用して、\MQL5\Indicators\TestDoEasy\Part47\にTestDoEasyPart47.mq5として保存します。
指標設定で標準のAcceleratorOscillator指標を計算するときに使用する銘柄と時間枠を指定する必要があります。指標は、現在のチャートサブウィンドウにそのデータを表示します。
指標ヘッダは次のようになります。
//+------------------------------------------------------------------+ //| TestDoEasyPart47.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> //--- properties #property indicator_separate_window #property indicator_buffers 3 #property indicator_plots 1 //--- classes //--- enums //--- defines //--- structures //--- input variables sinput string InpUsedSymbols = "GBPUSD"; // Used symbol (one only) sinput ENUM_TIMEFRAMES InpPeriod = PERIOD_M30; // Used chart period //--- sinput bool InpUseSounds = true; // Use sounds //--- indicator buffers //--- global variables ENUM_SYMBOLS_MODE InpModeUsedSymbols= SYMBOLS_MODE_DEFINES; // Mode of used symbols list ENUM_TIMEFRAMES_MODE InpModeUsedTFs = TIMEFRAMES_MODE_LIST; // Mode of used timeframes list string InpUsedTFs; // List of used timeframes CEngine engine; // CEngine library main object string prefix; // Prefix of graphical object names int min_bars; // The minimum number of bars for the indicator calculation int used_symbols_mode; // Mode of working with symbols string array_used_symbols[]; // The array for passing used symbols to the library string array_used_periods[]; // The array for passing used timeframes to the library //+------------------------------------------------------------------+
AC指標の計算に使用する銘柄と銘柄チャートの期間を1つだけ指定します。
OnInit()ハンドラで、指標入力で指定されたパラメータ、そのID(1に等しい)、およびそれを操作するためのバッファを備えた標準AC指標を作成します。
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Write the name of the working timeframe selected in the settings to the InpUsedTFs variable InpUsedTFs=TimeframeDescription(InpPeriod); //--- Initialize DoEasy library OnInitDoEasy(); //--- Set indicator global variables prefix=engine.Name()+"_"; //--- calculate the number of bars of the current period fitting in the maximum used period //--- Use the obtained value if it exceeds 2, otherwise use 2 int num_bars=NumberBarsInTimeframe(InpPeriod); min_bars=(num_bars>2 ? num_bars : 2); //--- Check and remove remaining indicator graphical objects if(IsPresentObectByPrefix(prefix)) ObjectsDeleteAll(0,prefix); //--- Create the button panel //--- Check playing a standard sound using macro substitutions engine.PlaySoundByDescription(SND_OK); //--- Wait for 600 milliseconds engine.Pause(600); engine.PlaySoundByDescription(SND_NEWS); //--- indicator buffers mapping //--- Create all the necessary buffer objects for constructing AO engine.BufferCreateAC(InpUsedSymbols,InpPeriod,1); //--- Check the number of buffers specified in the 'properties' block if(engine.BuffersPropertyPlotsTotal()!=indicator_plots) Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal()); if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers) Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal()); //--- Create the color array and set non-default colors to all buffers within the collection color array_colors[]={clrGreen,clrRed,clrGray}; engine.BuffersSetColors(array_colors); //--- Display short descriptions of created indicator buffers engine.BuffersPrintShort(); //--- Set the short name for the indicator and bit depth IndicatorSetString(INDICATOR_SHORTNAME,"AC("+InpUsedSymbols+","+TimeframeDescription(InpPeriod)+")"); IndicatorSetInteger(INDICATOR_DIGITS,(int)SymbolInfoInteger(InpUsedSymbols,SYMBOL_DIGITS)+2); //--- Successful return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
OnCalculate()で、まずAC指標計算バッファデータを準備します。次に、メイン指標ループで、現在のチャートに描画バッファデータにAC指標計算バッファからのデータを入力します。
//+------------------------------------------------------------------+ //| OnCalculate code block for working with the library: | //+------------------------------------------------------------------+ //--- Pass the current symbol data from OnCalculate() to the price structure and set the "as timeseries" flag to the arrays CopyDataAsSeries(rates_total,prev_calculated,time,open,high,low,close,tick_volume,volume,spread); //--- Check for the minimum number of bars for calculation if(rates_total<min_bars || Point()==0) return 0; //--- Handle the Calculate event in the library //--- If the OnCalculate() method of the library returns zero, not all timeseries are ready - leave till the next tick if(engine.0) return 0; //--- If working in the tester if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(rates_data); // Working in the library timer engine.EventsHandling(); // Working with library events } //+------------------------------------------------------------------+ //| OnCalculate code block for working with the indicator: | //+------------------------------------------------------------------+ //--- Check and calculate the number of calculated bars //--- If limit = 0, there are no new bars - calculate the current one //--- If limit = 1, a new bar has appeared - calculate the first and the current ones //--- limit > 1 means the first launch or changes in history - the full recalculation of all data int limit=rates_total-prev_calculated; //--- Recalculate the entire history if(limit>1) { limit=rates_total-1; engine.BuffersInitPlots(); engine.BuffersInitCalculates(); } //--- Prepare data int bars_total=engine.SeriesGetBarsTotal(InpUsedSymbols,InpPeriod); int total_copy=(limit<min_bars ? min_bars : fmin(limit,bars_total)); //--- Fill in the calculated buffer with AO data CArrayObj *list=engine.GetBuffersCollection().GetListBuffersWithID(); if(list!=NULL) { for(int i=0;i<list.Total();i++) { CBuffer *buff=list.At(i); if(buff==NULL || buff.TypeBuffer()==BUFFER_TYPE_DATA || buff.IndicatorType()==WRONG_VALUE) continue; CSeriesDE *series=engine.SeriesGetSeries(buff.Symbol(),buff.Timeframe()); if(series==NULL) return 0; ulong used_data=series.AvailableUsedData(); int copied=engine.GetBuffersCollection().PreparingDataBufferStdInd(IND_AC,1,(int)used_data); if(copied<(int)used_data) return 0; } } //--- Calculate the indicator CBar *bar=NULL; // Bar object for defining the candle direction uchar color_index=0; // Color index to be set for the buffer depending on the candle direction //--- Main calculation loop of the indicator for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--) { engine.GetBuffersCollection().SetDataBufferStdInd(IND_AC,1,i,time[i]); } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
任意の銘柄/時間枠で計算された現在のチャートで標準AC指標を計算して表示するのに必要なのはこれですべてです。
標準指標のデータ準備ブロックが改善され(概念を確認するためだけに開発されたため、現在の実装では最適ではありません)、後続の記事でライブラリに移動されます。
完全な指標コードは、以下に添付されているファイルで提供されます。
指標をコンパイルし、指標設定でGBPUSD M5を設定した後、EURUSD M1で起動します。これは、現在のEURUSD分足チャートにAC指標データ(GBPUSD M5で計算)を表示することを意味します。
標準のAC指標を備えたGBPUSDM5も比較のために開かれています。
次の段階
次の記事では、複数銘柄・複数期間標準指標の開発を続けます。
現在のバージョンのライブラリのすべてのファイルは、テスト用EAファイルと一緒に以下に添付されているので、テストするにはダウンロードしてください。
質問、コメント、提案はコメント欄にお願いします。テスト指標はMQL5向けに開発されたのでご注意ください。
添付ファイルはMetaTrader 5のみを対象としています。現在のライブラリバージョンはMetaTrader 4ではテストされていません。
指標バッファ操作を開発してテストした後で、MetaTrader 4にいくつかのMQL5機能を実装してみます。
シリーズのこれまでの記事:
DoEasyライブラリの時系列(第35部): バーオブジェクトと銘柄の時系列リストDoEasyライブラリの時系列(第36部): すべての使用銘柄期間の時系列オブジェクト
DoEasyライブラリの時系列(第37部): すべての使用銘柄期間の時系列オブジェクト
DoEasyライブラリの時系列(第38部): 時系列コレクション-リアルタイムの更新とプログラムからのデータへのアクセス
DoEasyライブラリの時系列(第39部): ライブラリに基づいた指標 - データイベントと時系列イベントの準備
DoEasyライブラリの時系列(第40部): ライブラリに基づいた指標 - 実時間でのデータ更新
DoEasyライブラリの時系列(第41部): 複数銘柄・複数期間指標の例
DoEasyライブラリの時系列(第42部): 抽象指標バッファオブジェクトクラス
DoEasyライブラリの時系列(第43部): 指標バッファオブジェクトクラス
DoEasyライブラリの時系列(第44部): 指標バッファオブジェクトのコレクションクラス
DoEasyライブラリの時系列(第45部): 複数期間指標バッファ
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/8207





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索