DoEasyライブラリの時系列(第52部): 複数銘柄・複数期間の単一バッファ標準指標のクロスプラットフォーム化
目次
概念
第39部から始めて、独自の複数銘柄・複数期間指標を構築するための機能を段階的に作成してきました。作成された基準に基づいて、標準指標をマルチモードでも機能させるのは自然なことでした。そして、第47部から始めて、そのような機能を作成してきました(欠点は徐々に見つけて修正するには)。しかし、すべてはMetaTrader 5プラットフォームのために行われました。
指標に関してライブラリクラスをわずかに改善し、このライブラリに基づいて古いMetaTrader 4プラットフォーム用に開発されたプログラムが、MetaTrader5に切り替えたときに正常に機能するようにします。
MQL5とは対照的に、MQL4では、単一のバッファに対して複数の色で描画を行うことはできません。MetaTrader 4では、すべての指標バッファはモノクロです。この制限は、MetaTrader4のマルチバッファ構築の概念にも影響します。ここでは、特定のバーの描画色を指定することはできません。一方、これはMetaTrader5では簡単です。ここでは、色ごとに1つのモノクロ指標バッファを使用する必要があります。一方、チャートの指定された銘柄/期間からの指標データを格納するために追加の計算バッファを作成する必要はありません。MQL4では指標へのリクエストのすべての関数は、指定された銘柄/期間から指定された指標バーの値を返します。また、MQL5では、指標ハンドルを作成し、そのハンドルから必要な量をターゲット配列にコピーしてデータを要求する必要があります。指標の場合、それは計算されたバッファです。その後、必要なバーインデックスによってその配列から指定された指標のデータを取得します。一方、MQL5では指標データへのアクセスが高速化されます。
したがって、MQL4標準指標ではバッファオブジェクトの構成は異なります。現在のチャートに表示される指標のデータを含む情報を保存するために、追加の計算バッファを作成する必要はありません。しかし、一見単純化したように見えますが、柔軟性が失われます。各色の色付きバッファを作成するには、独自の単色指標バッファが必要です。必要なバーの色を指定するときに、色に対応するバッファを表示する必要があります。残りのバッファは非表示になります。そして、これは厄介な問題です。
上記の指定に基づいて、MQL4のマルチバッファ構築の概念は次のようになります。
- 1つの指標ラインの色ごとに個別の指標バッファがあります。
- ラインの色を切り替えるには、必要な色に対応する1つの指標ライン(バッファ)が表示される 一方、他の色を参照する同じ指標の残りのすべてのラインが非表示になります
上記を要約すると、色付きの複数銘柄・複数期間移動平均指標ではラインを表示するための3つの色があります。
MQL5には、3つのデータ配列(3つのバッファ)があります。
- 描画バッファ(データはデータウィンドウに表示)
- カラーバッファ(各バーに1本のラインを描画するカラーバッファを指定、データウィンドウでは非表示、)
- 計算バッファ(指定された銘柄/期間からの移動平均のデータストレージ用、データウィンドウでは非表示)
MQL4には、3つのデータ配列(3つのバッファ)があります。
- カラー1の描画バッファ(データはデータウィンドウに表示)
- カラー2の描画バッファ(データはデータウィンドウに表示)
- カラー3の描画バッファ(データはデータウィンドウに表示)
MQL4のバッファの数は、色の数が減ると減り、増えると増えます。MQL5では、この例のバッファの数は常に3になります。一方、MQL5では、最大64色の数を動的に変更できます。MQL4では、指標ラインへの描画はそれほど単純ではありません。その理由は、64色に対して64バッファを作成する必要があるためです。そして、これは1本のラインについてです。指標にラインが4本ある場合、256個の指標バッファ配列が必要になります。8本の場合、512個のバッファが必要になります。これが上限です。MQL5の場合、各バーに対応する色のインデックスが指定され、そのバーで線が指定された色に描画されます。MQL4の場合、色に対応するバッファが表示され、その他は非表示になります。また、MQL4では、各色のすべてのバッファがターミナルのデータウィンドウに表示されます。MQL5では、この例のように、1つのバッファがターミナルのデータウィンドウに表示されます。これは正しいです。1本の指標ライン= ターミナルのデータウィンドウでの1つの値です。
すでに行われていることを今すぐに修正するつもりはありません。代わりに、単純なものから複雑なものへと段階的に段階的にライブラリクラスを改善して、MQL4を使用したライブラリ内の指標を使用した作業の特定の側面の互換性を実現します。今日は、例としてAccumulation/Distribution指標を使用して、ライブラリを使用したMQL4での単一バッファ複数銘柄・複数期間標準指標の作成を確認します。
ライブラリクラスの改善
通常どおり、最初に必要なテキストメッセージを追加します。以前、最後の指標プログラムで、ライブラリによって作成された指標バッファと、必要な指標バッファの数に関する指標コードのエントリとの対応を確認しました。
#property indicator_buffers 3 #property indicator_plots 1
さらに、ライブラリが必要なすべてのバッファを作成した後のコードで、チェックが実行されました。
//--- Check the number of buffers specified in the 'properties' block if(engine.BuffersPropertyPlotsTotal()!=indicator_plots) Alert(TextByLanguage("Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal()); if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers) Alert(TextByLanguage("Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal());
MQL4の互換性のためにわずかに改善されたこのチェックをライブラリに移動します。チェック内に表示されたテキストを、ライブラリ内の対応する場所、つまり\MQL5\Include\DoEasy\Data.mqhファイルに配置します。そして、新しいメッセージのインデックスを追加します。
//--- CEngine MSG_ENG_NO_TRADE_EVENTS, // There have been no trade events since the last launch of EA MSG_ENG_FAILED_GET_LAST_TRADE_EVENT_DESCR, // Failed to get description of the last trading event MSG_ENG_FAILED_GET_MARKET_POS_LIST, // Failed to get the list of open positions MSG_ENG_FAILED_GET_PENDING_ORD_LIST, // Failed to get the list of placed orders MSG_ENG_NO_OPEN_POSITIONS, // No open positions MSG_ENG_NO_PLACED_ORDERS, // No placed orders MSG_ENG_ERR_VALUE_PLOTS, // Attention! Value \"indicator_plots\" must be MSG_ENG_ERR_VALUE_ORDERS, // Attention! Value \"indicator_buffers\" must be
また、新しく追加したインデックスに対応するメッセージテキストも追加します。
//--- CEngine {"There have been no trade events since the last launch of EA"}, {"Failed to get the description of the last trading event"}, {"Failed to get open positions list"}, {"Failed to get pending orders list"}, {"No open positions"}, {"No placed orders"}, {"Attention! Value of \"indicator_plots\" should be "}, {"Attention! Value of \"indicator_buffers\" should be "},
プログラム入力データを含むファイルは、InpDatas.mqhと呼ばれていますが、英文法の正しい名前に変更します(ファイルに名前を付けたときに間違えました)。\MQL5\Include\DoEasy\InpData.mqhにします。
フォルダで名前を変更するだけです。
このファイルは、現在編集中のData.mqhファイルでライブラリに接続されているので、インクルード文字列を調整します。
//+------------------------------------------------------------------+ //| Data.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "InpData.mqh" //+------------------------------------------------------------------+
クロスプラットフォーム化の実装を始めましょう。
MetaTrader 4(Engine.mqhファイルでF7)でMetaEditorでライブラリをコンパイルしようとすると、多くのエラーが発生します。
まあ、それは驚きではありません。単に、順番にみていきます。まず、MQL4がいくつかの定数と列挙を知らないことが明らかです。
新しい定数と列挙を\MQL5\Include\DoEasy\ToMQL4.mqhファイルに追加します。
//+------------------------------------------------------------------+ //| ToMQL4.mqh | //| Copyright 2017, Artem A. Trishkin, Skype artmedia70 | //| https://www.mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2017, Artem A. Trishkin, Skype artmedia70" #property link "https://www.mql5.com/en/users/artmedia70" #property strict #ifdef __MQL4__ //+------------------------------------------------------------------+ //| Error codes | //+------------------------------------------------------------------+ #define ERR_SUCCESS (ERR_NO_ERROR) #define ERR_MARKET_UNKNOWN_SYMBOL (ERR_UNKNOWN_SYMBOL) #define ERR_ZEROSIZE_ARRAY (ERR_ARRAY_INVALID) #define ERR_MAIL_SEND_FAILED (ERR_SEND_MAIL_ERROR) #define ERR_NOTIFICATION_SEND_FAILED (ERR_NOTIFICATION_ERROR) #define ERR_FTP_SEND_FAILED (ERR_FTP_ERROR) //+------------------------------------------------------------------+ //| Order types, filling policy, period, reasons | //+------------------------------------------------------------------+ #define ORDER_TYPE_CLOSE_BY (8) #define ORDER_TYPE_BUY_STOP_LIMIT (9) #define ORDER_TYPE_SELL_STOP_LIMIT (10) #define ORDER_REASON_EXPERT (3) #define ORDER_REASON_SL (4) #define ORDER_REASON_TP (5) #define ORDER_REASON_BALANCE (6) #define ORDER_REASON_CREDIT (7) //+------------------------------------------------------------------+ //| Flags of allowed order expiration modes | //+------------------------------------------------------------------+ #define SYMBOL_EXPIRATION_GTC (1) #define SYMBOL_EXPIRATION_DAY (2) #define SYMBOL_EXPIRATION_SPECIFIED (4) #define SYMBOL_EXPIRATION_SPECIFIED_DAY (8) //+------------------------------------------------------------------+ //| Flags of allowed order filling modes | //+------------------------------------------------------------------+ #define SYMBOL_FILLING_FOK (1) #define SYMBOL_FILLING_IOC (2) //+------------------------------------------------------------------+ //| The flags of allowed order types | //+------------------------------------------------------------------+ #define SYMBOL_ORDER_MARKET (1) #define SYMBOL_ORDER_LIMIT (2) #define SYMBOL_ORDER_STOP (4) #define SYMBOL_ORDER_STOP_LIMIT (8) #define SYMBOL_ORDER_SL (16) #define SYMBOL_ORDER_TP (32) #define SYMBOL_ORDER_CLOSEBY (64) //+------------------------------------------------------------------+ //| Indicator lines IDs | //+------------------------------------------------------------------+ #define TENKANSEN_LINE (0) #define KIJUNSEN_LINE (1) #define SENKOUSPANA_LINE (2) #define SENKOUSPANB_LINE (3) #define CHIKOUSPAN_LINE (4) //+------------------------------------------------------------------+ //| Deal types of MQL5 | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Position change method | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Open position direction | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Order status | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Margin calculation mode | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Prices on which basis chart by symbol is constructed | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Life span of pending orders and | //| set levels of StopLoss/TakeProfit | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Options types | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Right provided by an option | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Method of margin value calculation for an instrument | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Swap charge methods at position move | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Trading operations types | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Order filling policies | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Order life span | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Integer properties of selected position | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Real properties of selected position | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| String properties of selected position | //+------------------------------------------------------------------+ //--- the code has been removed for the sake of space //+------------------------------------------------------------------+ //| Technical indicator types | //+------------------------------------------------------------------+ enum ENUM_INDICATOR { IND_AC = 5, IND_AD = 6, IND_ALLIGATOR = 7, IND_ADX = 8, IND_ADXW = 9, IND_ATR = 10, IND_AO = 11, IND_BEARS = 12, IND_BANDS = 13, IND_BULLS = 14, IND_CCI = 15, IND_DEMARKER = 16, IND_ENVELOPES = 17, IND_FORCE = 18, IND_FRACTALS = 19, IND_GATOR = 20, IND_ICHIMOKU = 21, IND_BWMFI = 22, IND_MACD = 23, IND_MOMENTUM = 24, IND_MFI = 25, IND_MA = 26, IND_OSMA = 27, IND_OBV = 28, IND_SAR = 29, IND_RSI = 30, IND_RVI = 31, IND_STDDEV = 32, IND_STOCHASTIC = 33, IND_VOLUMES = 34, IND_WPR = 35, IND_DEMA = 36, IND_TEMA = 37, IND_TRIX = 38, IND_FRAMA = 39, IND_AMA = 40, IND_CHAIKIN = 41, IND_VIDYA = 42, IND_CUSTOM = 43, }; //+------------------------------------------------------------------+ //| Drawing style | //+------------------------------------------------------------------+ enum ENUM_DRAW_TYPE { DRAW_COLOR_LINE = DRAW_LINE, // MQL5 = 1, MQL4 = 0 DRAW_COLOR_HISTOGRAM = DRAW_HISTOGRAM, // MQL5 = 2, MQL4 = 2 DRAW_COLOR_ARROW = DRAW_ARROW, // MQL5 = 3, MQL4 = 3 DRAW_COLOR_SECTION = DRAW_SECTION, // MQL5 = 4, MQL4 = 1 DRAW_COLOR_HISTOGRAM2 = DRAW_NONE, // MQL5 = 0, MQL4 = 12 DRAW_COLOR_ZIGZAG = DRAW_ZIGZAG, // MQL5 = 6, MQL4 = 4 DRAW_COLOR_BARS = DRAW_NONE, // MQL5 = 0, MQL4 = 12 DRAW_COLOR_CANDLES = DRAW_NONE, // MQL5 = 0, MQL4 = 12 // DRAW_FILLING MQL4 = 5 DRAW_COLOR_NONE = DRAW_NONE, // MQL5 = 0, MQL4 = 12 }; //+------------------------------------------------------------------+ //| Volume type | //+------------------------------------------------------------------+ enum ENUM_APPLIED_VOLUME { VOLUME_TICK, VOLUME_REAL }; //+------------------------------------------------------------------+ //| Trade request structure | //+------------------------------------------------------------------+ //--- the code has been further removed for the sake of space #endif
さらに、次のコンパイルで、MQL4にmql5関数がないというエラーが発生します。特に、BarsCalculated()です。この関数は、指標によってハンドルによって計算されたバーの数を返します。MQL4の場合、これは不明です。指定された時系列の使用可能なバーの数を返すmql4-function Bars()は、その意味の点でBarsCalculated()に最も近い関数になります。
MQL4では、指標はリクエスト中にすでに計算されていると見なされるため、指標の計算データの量(MQL5 BarsCalculated())を時系列の使用可能なバーの数(MQL4 Bars())で置き換えることができます。いずれの場合も、指標データを取得すると、ライブラリメソッドは受信したデータを返し、それらを検証します。したがって、利用可能な時系列バーの指定により、MQL4指標の計算データの量を置き換えることができると考えてみましょう。
BarsCalculated()関数を使用するIndicatorBarsCalculated()メソッドは、\MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqhファイルにあります。そして、同じ場所で一度に、指標を操作する他のメソッドに多くの改善を加える必要があります。
以前は、メソッドはクラス本体に完全に記述されていました。そこでは、計算されたデータの量がすぐに返されました。
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); } string IndicatorShortName(void) const { return this.GetProperty(BUFFER_PROP_IND_NAME_SHORT); } int IndicatorBarsCalculated(void) const { return ::BarsCalculated((int)this.GetProperty(BUFFER_PROP_IND_HANDLE));} int IndicatorLineAdditionalNumber(void) const { return (int)this.GetProperty(BUFFER_PROP_IND_LINE_ADDITIONAL_NUM); } int IndicatorLineMode(void) const { return (int)this.GetProperty(BUFFER_PROP_IND_LINE_MODE); }
メソッドの宣言のみをそのままにして
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); } string IndicatorShortName(void) const { return this.GetProperty(BUFFER_PROP_IND_NAME_SHORT); } int IndicatorLineAdditionalNumber(void) const { return (int)this.GetProperty(BUFFER_PROP_IND_LINE_ADDITIONAL_NUM); } int IndicatorLineMode(void) const { return (int)this.GetProperty(BUFFER_PROP_IND_LINE_MODE); } int IndicatorBarsCalculated(void);
実装をクラス本体の外に移動します。
//+------------------------------------------------------------------+ //| Return the number of standard indicator calculated bars | //+------------------------------------------------------------------+ int CBuffer::IndicatorBarsCalculated(void) { return(#ifdef __MQL5__ ::BarsCalculated((int)this.GetProperty(BUFFER_PROP_IND_HANDLE)) #else ::Bars(this.Symbol(),this.Timeframe()) #endif); } //+------------------------------------------------------------------+
ここで、MQL5の場合は計算された指標データの量を返し、MQL4の場合は —利用可能な時系列データの量を返します。
閉じたパラメトリッククラスコンストラクタを2つの部分に分割します。
最初の部分 — MQL5のみについてはすでに利用可能な部分を維持し、MQL4についてはmql5コードのコピーを作成し、そこから不要なものを削除します。
//+------------------------------------------------------------------+ //| 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) { #ifdef __MQL5__ 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_LINE_MODE] = INDICATOR_LINE_MODE_MAIN; this.m_long_prop[BUFFER_PROP_IND_HANDLE] = INVALID_HANDLE; this.m_long_prop[BUFFER_PROP_IND_TYPE] = WRONG_VALUE; this.m_long_prop[BUFFER_PROP_IND_LINE_ADDITIONAL_NUM] = 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; this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME_SHORT)]=NULL; //--- If failed to change the size of the indicator buffer array, display the appropriate message indicating the string if(::ArrayResize(this.DataBuffer,(int)this.GetProperty(BUFFER_PROP_NUM_DATAS))==WRONG_VALUE) ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError()); //--- If failed to change the size of the color array (only for a non-calculated buffer), display the appropriate message indicating the string if(this.TypeBuffer()>BUFFER_TYPE_CALCULATE) if(::ArrayResize(this.ArrayColors,(int)this.ColorsTotal())==WRONG_VALUE) ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError()); //--- For DRAW_FILLING, fill in the color array with two default colors if(this.Status()==BUFFER_STATUS_FILLING) { this.SetColor(clrBlue,0); this.SetColor(clrRed,1); } //--- Bind indicator buffers with arrays //--- In a loop by the number of indicator buffers int total=::ArraySize(DataBuffer); for(int i=0;i<total;i++) { //--- calculate the index of the next array and //--- bind the indicator buffer by the calculated index with the dynamic array //--- located by the i loop index in the DataBuffer array int index=(int)this.GetProperty(BUFFER_PROP_INDEX_BASE)+i; ::SetIndexBuffer(index,this.DataBuffer[i].Array,(this.TypeBuffer()==BUFFER_TYPE_DATA ? INDICATOR_DATA : INDICATOR_CALCULATIONS)); //--- Set indexation flag as in the timeseries to all buffer arrays ::ArraySetAsSeries(this.DataBuffer[i].Array,true); } //--- Bind the color buffer with the array (only for a non-calculated buffer and not for the filling buffer) if(this.Status()!=BUFFER_STATUS_FILLING && this.TypeBuffer()!=BUFFER_TYPE_CALCULATE) { ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_COLOR),this.ColorBufferArray,INDICATOR_COLOR_INDEX); ::ArraySetAsSeries(this.ColorBufferArray,true); } //--- Done if this is a calculated buffer if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; //--- Set integer parameters of the graphical series ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH)); this.SetColor((color)this.GetProperty(BUFFER_PROP_COLOR)); //--- Set real parameters of the graphical series ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,this.GetProperty(BUFFER_PROP_EMPTY_VALUE)); //--- Set string parameters of the graphical series ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,this.GetProperty(BUFFER_PROP_LABEL)); //--- MQL4 #else this.m_type=COLLECTION_BUFFERS_ID; this.m_act_state_trigger=true; this.m_total_arrays=1; //--- 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_LINE_MODE] = INDICATOR_LINE_MODE_MAIN; this.m_long_prop[BUFFER_PROP_IND_HANDLE] = INVALID_HANDLE; this.m_long_prop[BUFFER_PROP_IND_TYPE] = WRONG_VALUE; this.m_long_prop[BUFFER_PROP_IND_LINE_ADDITIONAL_NUM] = WRONG_VALUE; ENUM_DRAW_TYPE type=DRAW_COLOR_NONE; switch((int)this.Status()) { case BUFFER_STATUS_LINE : type=DRAW_COLOR_LINE; break; case BUFFER_STATUS_HISTOGRAM : type=DRAW_COLOR_HISTOGRAM; break; case BUFFER_STATUS_ARROW : type=DRAW_COLOR_ARROW; break; case BUFFER_STATUS_SECTION : type=DRAW_COLOR_SECTION; break; case BUFFER_STATUS_ZIGZAG : type=DRAW_COLOR_ZIGZAG; break; case BUFFER_STATUS_NONE : type=DRAW_COLOR_NONE; break; case BUFFER_STATUS_FILLING : type=DRAW_COLOR_NONE; break; case BUFFER_STATUS_HISTOGRAM2 : type=DRAW_COLOR_NONE; break; case BUFFER_STATUS_BARS : type=DRAW_COLOR_NONE; break; case BUFFER_STATUS_CANDLES : type=DRAW_COLOR_NONE; break; default : type=DRAW_COLOR_NONE; break; } 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.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; this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME_SHORT)]=NULL; //--- If failed to change the size of the indicator buffer array, display the appropriate message indicating the string if(::ArrayResize(this.DataBuffer,(int)this.GetProperty(BUFFER_PROP_NUM_DATAS))==WRONG_VALUE) ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError()); //--- Bind indicator buffers with arrays //--- In a loop by the number of indicator buffers int total=::ArraySize(DataBuffer); for(int i=0;i<total;i++) { //--- calculate the index of the next array and //--- bind the indicator buffer by the calculated index with the dynamic array //--- located by the i loop index in the DataBuffer array int index=(int)this.GetProperty(BUFFER_PROP_INDEX_BASE)+i; ::SetIndexBuffer(index,this.DataBuffer[i].Array,(this.TypeBuffer()==BUFFER_TYPE_DATA ? INDICATOR_DATA : INDICATOR_CALCULATIONS)); //--- Set indexation flag as in the timeseries to all buffer arrays ::ArraySetAsSeries(this.DataBuffer[i].Array,true); } //--- Done if this is a calculated buffer if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; //--- Set integer parameters of the graphical series this.SetDrawType(type); ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT), (ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE), (ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE), (ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH), (ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_COLOR)); ::SetIndexArrow((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE)); ::SetIndexShift((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT)); ::SetIndexDrawBegin((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN)); ::SetIndexShift((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA)); //--- Set real parameters of the graphical series ::SetIndexEmptyValue((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.GetProperty(BUFFER_PROP_EMPTY_VALUE)); //--- Set string parameters of the graphical series ::SetIndexLabel((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.GetProperty(BUFFER_PROP_LABEL)); #endif } //+------------------------------------------------------------------+
ここでの主な違いは、描画タイプの計算にあります。MQL5の場合、バッファタイプ(ステータス)から計算しますが、ここでは対応する値の設定が簡単でした。指標バッファに必要な値を設定するには対応するmql4関数を使用します。これは、PlotIndexSetInteger()、PlotIndexSetDouble()、 PlotIndexSetString( )mql5関数はコンパイルエラーを引き起こしませんが、同時にMQL4の指標バッファに必要な値を設定しないからです。
同様に、指標バッファの特定のプロパティを設定するメソッドでは、各言語に対応する関数を使用して、コードをmql5用とmql4用に分離します。
//+------------------------------------------------------------------+ //--- Set the graphical construction type by type and status | //+------------------------------------------------------------------+ void CBuffer::SetDrawType(void) { ENUM_DRAW_TYPE type=(!this.TypeBuffer() || !this.Status() ? (ENUM_DRAW_TYPE)DRAW_NONE : this.Status()==BUFFER_STATUS_FILLING ? (ENUM_DRAW_TYPE)DRAW_FILLING : ENUM_DRAW_TYPE(this.Status()+8)); this.SetProperty(BUFFER_PROP_DRAW_TYPE,type); #ifdef __MQL5__ ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,type); #else ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),type,EMPTY,EMPTY,EMPTY); #endif } //+------------------------------------------------------------------+ //| Set the passed graphical construction type | //+------------------------------------------------------------------+ void CBuffer::SetDrawType(const ENUM_DRAW_TYPE draw_type) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; this.SetProperty(BUFFER_PROP_DRAW_TYPE,draw_type); #ifdef __MQL5__ ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,draw_type); #else ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),draw_type,EMPTY,EMPTY,EMPTY); #endif } //+------------------------------------------------------------------+ //| Set the number of initial bars | //| without drawing and values in DataWindow | //+------------------------------------------------------------------+ void CBuffer::SetDrawBegin(const int value) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; this.SetProperty(BUFFER_PROP_DRAW_BEGIN,value); #ifdef __MQL5__ ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,value); #else ::SetIndexDrawBegin((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),value); #endif } //+------------------------------------------------------------------+ //| Set the flag of displaying | //| construction values in DataWindow | //+------------------------------------------------------------------+ void CBuffer::SetShowData(const bool flag) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; this.SetProperty(BUFFER_PROP_SHOW_DATA,flag); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,flag); } //+------------------------------------------------------------------+ //| Set the indicator graphical construction shift | //+------------------------------------------------------------------+ void CBuffer::SetShift(const int shift) { this.SetProperty(BUFFER_PROP_SHIFT,shift); if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; #ifdef __MQL5__ ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,shift); #else ::SetIndexShift((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),shift); #endif } //+------------------------------------------------------------------+ //| Set the line style | //+------------------------------------------------------------------+ void CBuffer::SetStyle(const ENUM_LINE_STYLE style) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; this.SetProperty(BUFFER_PROP_LINE_STYLE,style); #ifdef __MQL5__ ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,style); #else ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.DrawType(),style,EMPTY,EMPTY); #endif } //+------------------------------------------------------------------+ //| Set the line width | //+------------------------------------------------------------------+ void CBuffer::SetWidth(const int width) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; this.SetProperty(BUFFER_PROP_LINE_WIDTH,width); #ifdef __MQL5__ ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,width); #else ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.DrawType(),EMPTY,width,EMPTY); #endif } //+------------------------------------------------------------------+ //| Set the number of colors | //+------------------------------------------------------------------+ void CBuffer::SetColorNumbers(const int number) { if(number>IND_COLORS_TOTAL || this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; int n=(this.Status()!=BUFFER_STATUS_FILLING ? number : 2); this.SetProperty(BUFFER_PROP_COLOR_INDEXES,n); ::ArrayResize(this.ArrayColors,n); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,n); } //+------------------------------------------------------------------+ //| Set a single specified drawing color for the buffer | //+------------------------------------------------------------------+ void CBuffer::SetColor(const color colour) { if(this.Status()==BUFFER_STATUS_FILLING || this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; this.SetColorNumbers(1); this.SetProperty(BUFFER_PROP_COLOR,colour); this.ArrayColors[0]=colour; #ifdef __MQL5__ ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,0,this.ArrayColors[0]); #else ::SetIndexStyle((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),this.DrawType(),EMPTY,EMPTY,this.ArrayColors[0]); #endif } //+------------------------------------------------------------------+ //| Set the drawing color to the specified color index | //+------------------------------------------------------------------+ void CBuffer::SetColor(const color colour,const uchar index) { #ifdef __MQL5__ if(index>IND_COLORS_TOTAL-1 || this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; if(index>this.ColorsTotal()-1) this.SetColorNumbers(index+1); this.ArrayColors[index]=colour; if(index==0) this.SetProperty(BUFFER_PROP_COLOR,(color)this.ArrayColors[0]); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,index,this.ArrayColors[index]); #else #endif } //+------------------------------------------------------------------+ //| Set drawing colors from the color array | //+------------------------------------------------------------------+ void CBuffer::SetColors(const color &array_colors[]) { #ifdef __MQL5__ //--- Exit if the passed array is empty if(::ArraySize(array_colors)==0 || this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; //--- Copy the passed array to the array of buffer object colors ::ArrayCopy(this.ArrayColors,array_colors,0,0,IND_COLORS_TOTAL); //--- Exit if the color array was empty and not copied for some reason int total=::ArraySize(this.ArrayColors); if(total==0) return; //--- If the drawing style is not DRAW_FILLING if(this.Status()!=BUFFER_STATUS_FILLING) { //--- if the new number of colors exceeds the currently set one, //--- set the new value for the number of colors if(total>this.ColorsTotal()) this.SetColorNumbers(total); } //--- If the drawing style is DRAW_FILLING, set the number of colors equal to 2 else total=2; //--- Set the very first color from the color array (for a single color) to the buffer object color property this.SetProperty(BUFFER_PROP_COLOR,(color)this.ArrayColors[0]); //--- Set the new number of colors for the indicator buffer ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_COLOR_INDEXES,total); //--- In the loop by the new number of colors, set all colors by their indices for the indicator buffer for(int i=0;i<total;i++) ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_COLOR,i,this.ArrayColors[i]); #else #endif } //+------------------------------------------------------------------+ //| 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) #ifdef __MQL5__ ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,value); #else ::SetIndexEmptyValue((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),value); #endif } //+------------------------------------------------------------------+ //| Set the name for the graphical indicator series | //+------------------------------------------------------------------+ void CBuffer::SetLabel(const string label) { this.SetProperty(BUFFER_PROP_LABEL,label); if(this.TypeBuffer()!=BUFFER_TYPE_CALCULATE) #ifdef __MQL5__ ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,label); #else ::SetIndexLabel((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),label); #endif } //+------------------------------------------------------------------
//+------------------------------------------------------------------+ //| Set the color index to the specified timeseries index | //| of the color buffer array | //+------------------------------------------------------------------+ void CBuffer::SetBufferColorIndex(const uint series_index,const uchar color_index) { #ifdef __MQL4__ return; #endif if(this.GetDataTotal(0)==0 || color_index>this.ColorsTotal()-1 || this.Status()==BUFFER_STATUS_FILLING || this.Status()==BUFFER_STATUS_NONE) return; int data_total=this.GetDataTotal(0); int data_index=((int)series_index<data_total ? (int)series_index : data_total-1); if(::ArraySize(this.ColorBufferArray)==0) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF)); if(data_index<0) return; this.ColorBufferArray[data_index]=color_index; } //+------------------------------------------------------------------+
\MQL5\Include\DoEasy\Objects\Indicators\BufferCalculate.mqhファイルのCBufferCalculateクラスで、計算されたバッファには指標ハンドルから計算されたバッファオブジェクトのデータ配列にデータをコピーする3つのメソッドがあります。メソッドは、コピーされたデータの量を返します。MQL4の場合、指標ハンドルからデータをコピーする必要はなく、銘柄、時間枠、バー番号を指定した対応する標準のmql4関数を使用してデータを取得するだけなので、これらのメソッドでは、コピーが成功したことを示す特定のダミー値を返す必要があります。
メソッドでは、コピーに必要なバーの数を渡し、コピーされたデータがその値と等しいことを示すフラグを返します。
MQL4の場合は、単にそれを返します。
//+------------------------------------------------------------------+ //| Copy data of the specified indicator to the buffer object array | //+------------------------------------------------------------------+ int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const int start_pos,const int count) { return(#ifdef __MQL5__ ::CopyBuffer(indicator_handle,buffer_num,-start_pos,count,this.DataBuffer[0].Array) #else count #endif ); } //+------------------------------------------------------------------+ int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const datetime start_time,const int count) { return(#ifdef __MQL5__ ::CopyBuffer(indicator_handle,buffer_num,start_time,count,this.DataBuffer[0].Array) #else count #endif ); } //+------------------------------------------------------------------+ int CBufferCalculate::FillAsSeries(const int indicator_handle,const int buffer_num,const datetime start_time,const datetime stop_time) { return ( #ifdef __MQL5__ ::CopyBuffer(indicator_handle,buffer_num,start_time,stop_time,this.DataBuffer[0].Array) #else int(::fabs(start_time-stop_time)/::PeriodSeconds(this.Timeframe())+1) #endif ); } //+------------------------------------------------------------------+
コピーするデータの量を指定せず、必要なデータの開始時刻と終了時刻を指定する最後のメソッドの場合、
MQL4の場合、必要なデータの開始時刻と終了時刻の間のバーの数を計算し、計算された値を返します。
指標バッファコレクションクラスに標準指標のすべてのバッファオブジェクトを作成します
(\MQL5\Include\DoEasy\Collections\BuffersCollection.mqhファイルのバッファオブジェクトコレクションクラス)。
このクラスもMQL4の互換性のために改善されました。
MQL5では、標準の指標オブジェクトを作成するメソッドで、必要な指標のハンドルが最初に作成されます。作成が成功した場合は、オブジェクト自体が作成されます 。MQL4ではハンドルを作成する必要がないため、作成された指標のダミーハンドルをこれらすべてのメソッドに追加します。
//+------------------------------------------------------------------+ //| Create multi-symbol multi-period AC | //+------------------------------------------------------------------+ int CBuffersCollection::CreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id=WRONG_VALUE) { //--- Create indicator handle and set default ID int handle= #ifdef __MQL5__ ::iAC(symbol,timeframe) #else 0 #endif ; int identifier=(id==WRONG_VALUE ? IND_AC : id); color array_colors[3]={clrGreen,clrRed,clrGreen}; CBuffer *buff=NULL; if(handle!=INVALID_HANDLE) { //--- Create histogram buffer from the zero line this.CreateHistogram();
その間、ハンドル値としてゼロを追加しました。さらに、おそらく、指標ハンドルの作成をエミュレートして、同じ入力を持つ2つの同じ標準指標オブジェクトの作成を除外します。ただし、これが事実上行われるべきかどうかは実践で示します。
ハンドル作成エミュレーションを含む文字列は、標準の指標オブジェクトを作成するすべてのメソッドにすでに追加されています。
ここではそれらについて詳しく説明しません。代わりに、例としてAD指標の作成メソッドを使用して、MQL4の単一バッファモノクロ標準指標を作成するために必要な変更を検討します。
//+------------------------------------------------------------------+ //| 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 indicator handle and set default ID int handle= #ifdef __MQL5__ ::iAD(symbol,timeframe,applied_volume) #else 0 #endif ; int identifier=(id==WRONG_VALUE ? IND_AD : id); color array_colors[1]={clrLightSeaGreen}; CBuffer *buff=NULL; if(handle!=INVALID_HANDLE) { //--- Create line buffer this.CreateLine(); //--- Get the last created buffer object (drawn) and set to it all necessary parameters buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_AD); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetShowData(true); buff.SetLabel("A/D("+symbol+","+TimeframeDescription(timeframe)+")"); buff.SetIndicatorName("Accumulation/Distribution"); buff.SetIndicatorShortName("A/D("+symbol+","+TimeframeDescription(timeframe)+")"); #ifdef __MQL5__ buff.SetColors(array_colors); #else buff.SetColor(array_colors[0]); #endif //--- MQL5 #ifdef __MQL5__ //--- Create calculated buffer, in which standard indicator data will be stored this.CreateCalculate(); //--- Get the last created buffer object (calculated) and set to it all necessary parameters buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_AD); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetEmptyValue(EMPTY_VALUE); buff.SetLabel("A/D("+symbol+","+TimeframeDescription(timeframe)+")"); buff.SetIndicatorName("Accumulation/Distribution"); buff.SetIndicatorShortName("A/D("+symbol+","+TimeframeDescription(timeframe)+")"); #endif } return handle; } //+------------------------------------------------------------------+
ここで、MQL5の場合、カラー配列を使用してオブジェクトに渡すメソッドによってその色のセットをバッファに設定し、MQL4の場合は1つの色のみを設定します(カラー配列の最初の色)。計算されたバッファはMQL5のみで必要です。これは、指定された銘柄とチャート期間に作成されたAD指標のデータを格納します。MQL4の場合、iAD()指標の呼び出し関数からすべてのデータを直接取得するため、このようなバッファは必要ありません。
現在の銘柄チャートに値を設定するために指定された標準指標のデータを準備するメソッド。 MQL5の場合、計算されたバッファからデータを読み取ります。MQL4の場合、要求されたデータを標準指標データの呼び出し関数から取得するのは非常に簡単です。
//+------------------------------------------------------------------+ //| Prepare data of the specified standard indicator | //| for setting values on the current symbol chart | //+------------------------------------------------------------------+ int CBuffersCollection::PreparingSetDataStdInd(CBuffer *buffer_data0,CBuffer *buffer_data1,CBuffer *buffer_data2,CBuffer *buffer_data3,CBuffer *buffer_data4, CBuffer *buffer_calc0,CBuffer *buffer_calc1,CBuffer *buffer_calc2,CBuffer *buffer_calc3,CBuffer *buffer_calc4, const ENUM_INDICATOR ind_type, const int series_index, const datetime series_time, int &index_period, int &num_bars, double &value00, double &value01, double &value10, double &value11, double &value20, double &value21, double &value30, double &value31, double &value40, double &value41) { //--- Find bar index on a period which corresponds to the time of current bar start index_period=::iBarShift(buffer_data0.Symbol(),buffer_data0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || #ifdef __MQL5__ index_period>buffer_calc0.GetDataTotal()-1 #else index_period>buffer_data0.GetDataTotal()-1 #endif ) return WRONG_VALUE; //--- For MQL5 #ifdef __MQL5__ //--- Get the value by this index from indicator buffer if(buffer_calc0!=NULL) value00=buffer_calc0.GetDataBufferValue(0,index_period); if(buffer_calc1!=NULL) value10=buffer_calc1.GetDataBufferValue(0,index_period); if(buffer_calc2!=NULL) value20=buffer_calc2.GetDataBufferValue(0,index_period); if(buffer_calc3!=NULL) value30=buffer_calc3.GetDataBufferValue(0,index_period); if(buffer_calc4!=NULL) value40=buffer_calc4.GetDataBufferValue(0,index_period); //--- for MQL4 #else switch((int)ind_type) { //--- Single-buffer standard indicators case IND_AC : value00=::iAC(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period); break; case IND_AD : value00=::iAD(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period); break; case IND_AMA : break; case IND_AO : value00=::iAO(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period); break; case IND_ATR : break; case IND_BEARS : break; case IND_BULLS : break; case IND_BWMFI : value00=::iBWMFI(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period); break; case IND_CCI : break; case IND_CHAIKIN : break; case IND_DEMA : break; case IND_DEMARKER : break; case IND_FORCE : break; case IND_FRAMA : break; case IND_MA : break; case IND_MFI : break; case IND_MOMENTUM : break; case IND_OBV : break; case IND_OSMA : break; case IND_RSI : break; case IND_SAR : break; case IND_STDDEV : break; case IND_TEMA : break; case IND_TRIX : break; case IND_VIDYA : break; case IND_VOLUMES : value00=(double)::iVolume(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period); break; case IND_WPR : break; //--- Multi-buffer standard indicators case IND_ENVELOPES : break; case IND_FRACTALS : break; case IND_ADX : break; case IND_ADXW : break; case IND_BANDS : break; case IND_MACD : break; case IND_RVI : break; case IND_STOCHASTIC : break; case IND_ALLIGATOR : break; case IND_ICHIMOKU : break; case IND_GATOR : break; default: break; } #endif int series_index_start=series_index; //--- For the current chart we don’t need to calculate the number of bars processed - only one bar is available if(buffer_data0.Symbol()==::Symbol() && buffer_data0.Timeframe()==::Period()) { series_index_start=series_index; num_bars=1; } else { //--- Get the bar time which the bar with index_period index falls into on a period and symbol of calculated buffer datetime time_period=::iTime(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period); if(time_period==0) return false; //--- Get the current chart bar which corresponds to the time series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true); if(series_index_start==WRONG_VALUE) return WRONG_VALUE; //--- Calculate the number of bars on the current chart which are to be filled in with calculated buffer data num_bars=::PeriodSeconds(buffer_data0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Take values for color calculation if(buffer_data0!=NULL) value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); if(buffer_data1!=NULL) value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(0,series_index_start+num_bars)); if(buffer_data2!=NULL) value21=(series_index_start+num_bars>buffer_data2.GetDataTotal()-1 ? value20 : buffer_data2.GetDataBufferValue(0,series_index_start+num_bars)); if(buffer_data3!=NULL) value31=(series_index_start+num_bars>buffer_data3.GetDataTotal()-1 ? value30 : buffer_data3.GetDataBufferValue(0,series_index_start+num_bars)); if(buffer_data4!=NULL) value41=(series_index_start+num_bars>buffer_data4.GetDataTotal()-1 ? value40 : buffer_data4.GetDataBufferValue(0,series_index_start+num_bars)); return series_index_start; } //+------------------------------------------------------------------+
今のところ、MQL4では、銘柄とチャート期間以外の入力がない単一バッファの標準指標からのみデータを取得するように実装されています。残りの標準指標は、次の記事で実装されます。
MQL4のチェックの除外に関する小さな変更は、バッファオブジェクトの銘柄/期間に従って時系列インデックスによって指定された標準指標のバッファに現在のチャートの値を設定するメソッドで行われます。
//+------------------------------------------------------------------+ //| Set values for the current chart to buffers of the specified | //| standard indicator by the timeseries index in accordance | //| with 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 by type and ID CArrayObj *list=this.GetListBufferByTypeID(ind_type,id); if(list==NULL || list.Total()==0) { ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ)); return false; } //--- Get the list of drawn buffers 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); //--- Leave if any of the lists is empty if(list_data.Total()==0 #ifdef __MQL5__ || list_calc.Total()==0 #endif ) return false; //--- Declare necessary objects and variables CBuffer *buffer_data0=NULL,*buffer_data1=NULL,*buffer_data2=NULL,*buffer_data3=NULL,*buffer_data4=NULL,*buffer_tmp0=NULL,*buffer_tmp1=NULL; CBuffer *buffer_calc0=NULL,*buffer_calc1=NULL,*buffer_calc2=NULL,*buffer_calc3=NULL,*buffer_calc4=NULL; double value00=EMPTY_VALUE, value01=EMPTY_VALUE; double value10=EMPTY_VALUE, value11=EMPTY_VALUE; double value20=EMPTY_VALUE, value21=EMPTY_VALUE; double value30=EMPTY_VALUE, value31=EMPTY_VALUE; double value40=EMPTY_VALUE, value41=EMPTY_VALUE; double value_tmp0=EMPTY_VALUE,value_tmp1=EMPTY_VALUE; long vol0=0,vol1=0; int series_index_start=series_index,index_period=0, index=0,num_bars=1; uchar clr=0; //--- Depending on standard indicator type switch((int)ind_type) { //--- Single-buffer standard indicators case IND_AC : case IND_AD : case IND_AMA : case IND_AO : case IND_ATR : case IND_BEARS : case IND_BULLS : case IND_BWMFI : case IND_CCI : case IND_CHAIKIN : case IND_DEMA : case IND_DEMARKER : case IND_FORCE : case IND_FRAMA : case IND_MA : case IND_MFI : case IND_MOMENTUM : case IND_OBV : case IND_OSMA : case IND_RSI : case IND_SAR : case IND_STDDEV : case IND_TEMA : case IND_TRIX : case IND_VIDYA : case IND_VOLUMES : case IND_WPR : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_data0=list.At(0); #ifdef __MQL5__ list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_calc0=list.At(0); #endif if(buffer_data0==NULL #ifdef __MQL5__ || buffer_calc0==NULL || buffer_calc0.GetDataTotal(0)==0 #endif ) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- In a loop, by the number of bars in num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index //--- and set the drawn buffer color depending on a proportion of value00 and value01 values for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); if(ind_type!=IND_BWMFI) clr=(color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); else { vol0=::iVolume(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); vol1=::iVolume(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period+1); clr= ( value00>value01 && vol0>vol1 ? 0 : value00<value01 && vol0<vol1 ? 1 : value00>value01 && vol0<vol1 ? 2 : value00<value01 && vol0>vol1 ? 3 : 4 ); } buffer_data0.SetBufferColorIndex(index,clr); } return true; //--- Multi-buffer standard indicators case IND_ENVELOPES : case IND_FRACTALS : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_calc1=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- In a loop, by the number of bars in num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index //--- and set the drawn buffer color depending on a proportion of value00 and value01 values for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(1,index,value10); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); } return true; case IND_ADX : case IND_ADXW : case IND_BANDS : case IND_MACD : case IND_RVI : case IND_STOCHASTIC : case IND_ALLIGATOR : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,2,EQUAL); buffer_data2=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_calc1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,2,EQUAL); buffer_calc2=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- In a loop, by the number of bars in num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index //--- and set the drawn buffer color depending on a proportion of value00 and value01 values for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(0,index,value10); buffer_data2.SetBufferValue(0,index,value20); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index); } return true; case IND_ICHIMOKU : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TENKAN_SEN,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_KIJUN_SEN,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANA,EQUAL); buffer_data2=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANB,EQUAL); buffer_data3=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_CHIKOU_SPAN,EQUAL); buffer_data4=list.At(0); //--- Get the list of buffer objects which have ID of auxiliary line, and from it - buffer object with line number as 0 list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_ADDITIONAL,EQUAL); list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,0,EQUAL); buffer_tmp0=list.At(0); //--- Get the list of buffer objects which have ID of auxiliary line, and from it - buffer object with line number as 1 list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_ADDITIONAL,EQUAL); list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,1,EQUAL); buffer_tmp1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TENKAN_SEN,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_KIJUN_SEN,EQUAL); buffer_calc1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANA,EQUAL); buffer_calc2=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANB,EQUAL); buffer_calc3=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_CHIKOU_SPAN,EQUAL); buffer_calc4=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0) return false; if(buffer_calc3==NULL || buffer_data3==NULL || buffer_calc3.GetDataTotal(0)==0) return false; if(buffer_calc4==NULL || buffer_data4==NULL || buffer_calc4.GetDataTotal(0)==0) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- In a loop, by the number of bars in num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index //--- and set the drawn buffer color depending on a proportion of value00 and value01 values for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(0,index,value10); buffer_data2.SetBufferValue(0,index,value20); buffer_data3.SetBufferValue(0,index,value30); buffer_data4.SetBufferValue(0,index,value40); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index); buffer_data3.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value30>value31 ? 0 : value30<value31 ? 1 : 2) : color_index); buffer_data4.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value40>value41 ? 0 : value40<value41 ? 1 : 2) : color_index); //--- Set values for indicator auxiliary lines depending on mutual position of Senkou Span A and Senkou Span B lines value_tmp0=buffer_data2.GetDataBufferValue(0,index); value_tmp1=buffer_data3.GetDataBufferValue(0,index); if(value_tmp0<value_tmp1) { buffer_tmp0.SetBufferValue(0,index,buffer_tmp0.EmptyValue()); buffer_tmp0.SetBufferValue(1,index,buffer_tmp0.EmptyValue()); buffer_tmp1.SetBufferValue(0,index,value_tmp0); buffer_tmp1.SetBufferValue(1,index,value_tmp1); } else { buffer_tmp0.SetBufferValue(0,index,value_tmp0); buffer_tmp0.SetBufferValue(1,index,value_tmp1); buffer_tmp1.SetBufferValue(0,index,buffer_tmp1.EmptyValue()); buffer_tmp1.SetBufferValue(1,index,buffer_tmp1.EmptyValue()); } } return true; case IND_GATOR : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_calc1=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- In a loop, by the number of bars in num_bars fill in the drawn buffer with a value from the calculated buffer taken by index_period index //--- and set the drawn buffer color depending on a proportion of value00 and value01 values for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(1,index,value10); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10<value11 ? 0 : value10>value11 ? 1 : 2) : color_index); } return true; default: break; } return false; } //+------------------------------------------------------------------+
プログラムの#propertyに値を持つライブラリによって作成されたバッファオブジェクトの数の対応チェックを移動することにしたので、\MQL5\Include\DoEasy\Engine.mqhファイルのライブラリメインオブジェクトCEngineのクラスにそのようなメソッドを追加します。
クラスのpublicセクションでメソッドを宣言します。
//--- Display short description of all indicator buffers of the buffer collection void BuffersPrintShort(void); //--- Specify the required number of buffers for indicators void CheckIndicatorsBuffers(const int buffers,const int plots #ifdef __MQL4__ =1 #endif ); //--- Return the bar index on the specified timeframe chart by the current chart's bar index
クラス本体の外側で実装します。
//+------------------------------------------------------------------+ //| Specify the required number of buffers for indicators | //+------------------------------------------------------------------+ void CEngine::CheckIndicatorsBuffers(const int buffers,const int plots #ifdef __MQL4__ =1 #endif ) { #ifdef __MQL5__ if(this.BuffersPropertyPlotsTotal()!=plots) ::Alert(CMessage::Text(MSG_ENG_ERR_VALUE_PLOTS),this.BuffersPropertyPlotsTotal()); if(this.BuffersPropertyBuffersTotal()!=buffers) ::Alert(CMessage::Text(MSG_ENG_ERR_VALUE_ORDERS),this.BuffersPropertyBuffersTotal()); #else if(buffers!=this.BuffersPropertyPlotsTotal()) ::Alert(CMessage::Text(MSG_ENG_ERR_VALUE_ORDERS),this.BuffersPropertyPlotsTotal()); ::IndicatorBuffers(this.BuffersPropertyBuffersTotal()); #endif } //+------------------------------------------------------------------+
MQL5の場合、指標プログラムの#propertyで指定された値を持つ作成された数の指標バッファ(描画および計算)の不一致に関する警告通知を表示するだけです。
MQL4の場合、#property Indicator_buffersで指定された値が一致しない場合は、その通知を表示し、 ライブラリによって作成されたすべてのバッファの総数に従って、すべての指標バッファの総数を設定(描画および計算)します。
次に、MQL4指標に表示されるデータの容量を設定します。これを行うには、\MQL5\Include\DoEasy\Services\DELib.mqhファイルのライブラリサービス関数にある標準指標の量とレベルを設定するための関数を改善します。
//+------------------------------------------------------------------+ //| Set capacity and levels for standard indicator | //+------------------------------------------------------------------+ void SetIndicatorLevels(const string symbol,const ENUM_INDICATOR ind_type) { int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); switch(ind_type) { case IND_AD : case IND_CHAIKIN : case IND_OBV : case IND_VOLUMES : digits=0; break; case IND_AO : case IND_BEARS : case IND_BULLS : case IND_FORCE : case IND_STDDEV : case IND_AMA : case IND_DEMA : case IND_FRAMA : case IND_MA : case IND_TEMA : case IND_VIDYA : case IND_BANDS : case IND_ENVELOPES : case IND_MACD : digits+=1; break; case IND_AC : case IND_OSMA : digits+=2; break; case IND_MOMENTUM : digits=2; break; case IND_CCI : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,100); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-100); digits=2; break; case IND_DEMARKER : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,0.7); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,0.3); digits=3; break; case IND_MFI : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20); break; case IND_RSI : IndicatorSetInteger(INDICATOR_LEVELS,3); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,70); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,50); IndicatorSetDouble(INDICATOR_LEVELVALUE,2,30); digits=2; break; case IND_STOCHASTIC : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20); digits=2; break; case IND_WPR : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,-80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-20); digits=2; break; case IND_ATR : break; case IND_SAR : break; case IND_TRIX : break; default: IndicatorSetInteger(INDICATOR_LEVELS,0); break; } #ifdef __MQL5__ IndicatorSetInteger(INDICATOR_DIGITS,digits); #else IndicatorDigits(digits); #endif } //+------------------------------------------------------------------+
ここで、MQL4で表示される指標データの量を設定するには、標準のmql4関数IndicatorDigits()を使用します。
これで、単一バッファ複数銘柄・複数期間標準指標を作成するためのライブラリクラスの改善が完了しました。
テスト
テストを実行するには、前の記事の2番目の指標(TestDoEasyPart51_2.mq5)を取得して、
MetaTrader 4ターミナル指標フォルダ(\MQL4\Indicators\TestDoEasy\Part52\)でTestDoEasyPart52.mq4として保存します。
以前のテスト指標では、複数銘柄・複数期間のGatorOscillator標準指標を作成しました。今回作成したいのAccumulation/Distribution指標です。
ファイルヘッダで、MQL4に必要な指標バッファの数を設定します。
//+------------------------------------------------------------------+ //| TestDoEasyPart52.mq4 | //| 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 1 #property indicator_plots 1 //--- classes //--- enums //--- defines //--- structures //--- input variables sinput string InpUsedSymbols = "EURUSD"; // Used symbol (one only) sinput ENUM_TIMEFRAMES InpPeriod = PERIOD_H4; // 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 //+------------------------------------------------------------------+
ハンドラOnInit()で標準指標オブジェクトAccumulation/Distributionを作成し、AD指標タイプが必要な場所を指定します。
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Write the name of the working timeframe selected in the settings to 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 the selected standard indicator if(!engine.BufferCreateAD(InpUsedSymbols,InpPeriod,VOLUME_TICK,1)) { Print(TextByLanguage("Error. Indicator not created")); return INIT_FAILED; } //--- Check the number of buffers specified in the 'properties' block engine.CheckIndicatorsBuffers(indicator_buffers,indicator_plots); //--- Create the color array and set non-default colors to all buffers within the collection //--- (commented out since default colors are already set in methods of standard indicator creation) //--- (we can always set required colors either for all indicators like here or for each one individually) //color array_colors[]={clrGreen,clrRed,clrGray}; //engine.BuffersSetColors(array_colors); //--- Display short descriptions of created indicator buffers engine.BuffersPrintShort(); //--- Set a short name for the indicator, data capacity and levels string label=engine.BufferGetIndicatorShortNameByTypeID(IND_AD,1); IndicatorSetString(INDICATOR_SHORTNAME,label); SetIndicatorLevels(InpUsedSymbols,IND_AD); //--- Successful return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
以前は、指定および作成された指標バッファの数の対応は、ハンドラーOnInit()で確認されていました。
//--- Check the number of buffers specified in the 'properties' block if(engine.BuffersPropertyPlotsTotal()!=indicator_plots) Alert(TextByLanguage("Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal()); if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers) Alert(TextByLanguage("Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal());
これは、対応するライブラリメソッドの呼び出しに置き換えられています。
ハンドラーOnCalculate()では、GatorOscillatorデータの書き込みをメインプログラムループでのAccumulation/Distributionデータの書き込みに置き換えるだけで十分です。
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //+------------------------------------------------------------------+ //| 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.OnCalculate(rates_data)==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 //--- If 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 //--- Fill in calculated buffers of all created standard indicators with data int bars_total=engine.SeriesGetBarsTotal(InpUsedSymbols,InpPeriod); int total_copy=(limit<min_bars ? min_bars : fmin(limit,bars_total)); if(!engine.BufferPreparingDataAllBuffersStdInd()) return 0; //--- Calculate the indicator //--- Main calculation loop of the indicator for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--) { engine.GetBuffersCollection().SetDataBufferStdInd(IND_AD,1,i,time[i]); } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
mql4-versionではなく指標のmql5-versionの場合、#propertyで指定された描画および計算されたバッファの数を変更する必要があります。
//+------------------------------------------------------------------+ //| TestDoEasyPart52.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
指標をコンパイルし、MetaTrader4ターミナルのEURUSDH1チャートで、EURUSD銘柄とH4期間の指標入力の値を使用して起動します。したがって、MetaTrader4ターミナルの1H EURUSDチャートにEURUSDH4に対して計算されたAD指標を表示します。
次の段階
次の記事では、引き続きMetaTrader 5指標で作業し、早々にライブラリのクロスプラットフォーム化に取り組みます。
ライブラリの現在のバージョンのすべてのファイルは、テスト指標ファイルと一緒に以下に添付されています。ダウンロードし、すべてを検証することが可能です。
質問や提案は記事のコメント欄にお願いします。
以前のプラットフォームとの互換性に関するすべての作業は、より多くの利点と機能がある場合に、元々MQL5用に作成されたライブラリのマルチプラットフォーム化をサポートするためにのみ行われています。
このライブラリを使用したMQL4での作業に関する個別の記事は計画されておらず、書かれません。MetaTrader 4を使用するときにこのライブラリで不十分となったものはすべて、ご自分で「オーダーメイド」ベースで個別に開発してください。引き続きライブラリを両方のプラットフォームと互換性のあるものにしていきますが、その理由は、ライブラリユーザーがすべてのライブラリベースのプログラムをMetaTrader4で動作するように簡単に移動できるるようにすることだけです。
シリーズのこれまでの記事:
DoEasyライブラリの時系列(第35部): バーオブジェクトと銘柄の時系列リスト
DoEasyライブラリの時系列(第36部): すべての使用銘柄期間の時系列オブジェクト
DoEasyライブラリの時系列(第37部): すべての使用銘柄期間の時系列オブジェクト
DoEasyライブラリの時系列(第38部): 時系列コレクション-リアルタイムの更新とプログラムからのデータへのアクセス
DoEasyライブラリの時系列(第39部): ライブラリに基づいた指標 - データイベントと時系列イベントの準備
DoEasyライブラリの時系列(第40部): ライブラリに基づいた指標 - 実時間でのデータ更新
DoEasyライブラリの時系列(第41部): 複数銘柄・複数期間指標の例
DoEasyライブラリの時系列(第42部): 抽象指標バッファオブジェクトクラス
DoEasyライブラリの時系列(第43部): 指標バッファオブジェクトクラス
DoEasyライブラリの時系列(第44部): 指標バッファオブジェクトのコレクションクラス
DoEasyライブラリの時系列(第45部): 複数期間指標バッファ
DoEasyライブラリの時系列(第46部): 複数銘柄・複数期間指標バッファ
DoEasyライブラリの時系列(第47部): 複数銘柄・複数期間標準指標
DoEasyライブラリの時系列(第48部): 単一サブウィンドウでの単一バッファ複数銘柄・複数期間指標
DoEasyライブラリの時系列(第49部): 複数銘柄・複数期間の複数バッファ標準指標
DoEasyライブラリの時系列(第50部): シフト付き複数銘柄・複数期間標準指標
DoEasyライブラリの時系列(第51部): 複数銘柄・複数期間の複合標準指標
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/8399
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索