DoEasyライブラリの時系列(第56部):カスタム指標オブジェクト、コレクション内指標オブジェクトからのデータ取得
Artyom Trishkin | 13 1月, 2021
目次
概念
ライブラリデータベースに基づいて作成されたEAの指標を処理する機能の作成を続行します。すでに標準の指標オブジェクトクラスを作成し、コレクションリストに配置し始めています。完全な「セット」を持つのに欠落しているのはカスタム指標オブジェクトのみです。今日、そのようなオブジェクトが作成されます。その構築の概念は、標準指標オブジェクトの作成概念とは異なります。
ターミナルには究極の標準指標のセットがあるので、各指標について明確かつ事前に知られているデータに基づき、標準指標オブジェクトを作成できます。それが私がしていることです。つまり、作成された指標に対応する厳密に設定されたパラメータの組み合わせを指定することです。一方、カスタム指標には、事前に知ることができないパラメータセットが含まれている場合があります。
このため、カスタム指標オブジェクトの作成の概念は、標準指標オブジェクトの作成とは異なります。
標準指標オブジェクトを作成するために入力に必要なすべてのプロパティを含む独自の作成メソッドを作成するだけで十分な場合、同時に、数と型が事前にわからないパラメータを持つカスタム指標を作成するためには事前に入力された指標入力構造体の配列をその作成メソッドに渡す必要があります。指標の作成に必要なすべてのパラメータとプロパティは、メソッドから取得されます。したがって、残念ながら、ライブラリユーザは、プログラムでカスタム指標を作成するために、このような入力構造体の配列を個別に入力する必要があります。
プログラムの作成を簡素化するためにライブラリを作成しようとしているので、いくつかのバリエーションから、プログラムで作成された指標を呼び出す次のバリエーションを選択しました。作成された各指標は独自の一意のIDで指定されます。作成された各指標はこのIDで呼び出すことができます。幸い、それらを作成してIDを設定し、設定されたIDのどれがプログラムで作成された指標のどれに対応するかはよくわかっています。
IDとは別に、すべてのパラメータによる指標呼び出しのメソッドがあります。つまりその指標オブジェクトが作成されたのと同じパラメータが指定された指標オブジェクトを取得し、受信したオブジェクトを処理することが可能になります。指標からデータを取得し(データ受信のメソッドは今日作成されます)、統計研究用に配列にコピーします(これは次の記事にあります)。
ライブラリクラスの改善
いつものように、まず、\MQL5\Include\DoEasy\Data.mqhファイルに新しいライブラリメッセージを入力します。
新しいメッセージのインデックスを追加します。
MSG_LIB_TEXT_IND_TEXT_GROUP, // Indicator group MSG_LIB_TEXT_IND_TEXT_GROUP_TREND, // Trend indicator MSG_LIB_TEXT_IND_TEXT_GROUP_OSCILLATOR, // Oscillator MSG_LIB_TEXT_IND_TEXT_GROUP_VOLUMES, // Volumes MSG_LIB_TEXT_IND_TEXT_GROUP_ARROWS, // Arrow indicator MSG_LIB_TEXT_IND_TEXT_ID, // Indicator ID MSG_LIB_TEXT_IND_TEXT_EMPTY_VALUE, // Empty value for plotting where nothing will be drawn MSG_LIB_TEXT_IND_TEXT_SYMBOL, // Indicator symbol MSG_LIB_TEXT_IND_TEXT_NAME, // Indicator name MSG_LIB_TEXT_IND_TEXT_SHORTNAME, // Indicator short name MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS, // Indicator parameters MSG_LIB_TEXT_IND_TEXT_APPLIED_VOLUME, // Volume type for calculation MSG_LIB_TEXT_IND_TEXT_PERIOD, // Averaging period MSG_LIB_TEXT_IND_TEXT_FAST_PERIOD, // Fast MA period MSG_LIB_TEXT_IND_TEXT_SLOW_PERIOD, // Slow MA period MSG_LIB_TEXT_IND_TEXT_SIGNAL, // Difference averaging period MSG_LIB_TEXT_IND_TEXT_TENKAN_PERIOD, // Tenkan-sen period MSG_LIB_TEXT_IND_TEXT_KIJUN_PERIOD, // Kijun-sen period MSG_LIB_TEXT_IND_TEXT_SPANB_PERIOD, // Senkou Span B period MSG_LIB_TEXT_IND_TEXT_JAW_PERIOD, // Period for jaw line calculation MSG_LIB_TEXT_IND_TEXT_TEETH_PERIOD, // Period for teeth line calculation MSG_LIB_TEXT_IND_TEXT_LIPS_PERIOD, // Period for lips line calculation MSG_LIB_TEXT_IND_TEXT_JAW_SHIFT, // Horizontal shift of jaws line MSG_LIB_TEXT_IND_TEXT_TEETH_SHIFT, // Horizontal shift of teeth line MSG_LIB_TEXT_IND_TEXT_LIPS_SHIFT, // Horizontal shift of lips line MSG_LIB_TEXT_IND_TEXT_SHIFT, // Horizontal shift of the indicator MSG_LIB_TEXT_IND_TEXT_MA_METHOD, // Smoothing type MSG_LIB_TEXT_IND_TEXT_APPLIED_PRICE, // Price type or handle MSG_LIB_TEXT_IND_TEXT_STD_DEVIATION, // Number of standard deviations MSG_LIB_TEXT_IND_TEXT_DEVIATION, // Deviation of channel borders from the central line MSG_LIB_TEXT_IND_TEXT_STEP, // Price change step — acceleration factor MSG_LIB_TEXT_IND_TEXT_MAXIMUM, // Maximum step MSG_LIB_TEXT_IND_TEXT_KPERIOD, // K-period (number of bars for calculation) MSG_LIB_TEXT_IND_TEXT_DPERIOD, // D-period (primary smoothing period) MSG_LIB_TEXT_IND_TEXT_SLOWING, // Final smoothing MSG_LIB_TEXT_IND_TEXT_PRICE_FIELD, // Stochastic calculation method MSG_LIB_TEXT_IND_TEXT_CMO_PERIOD, // Chande Momentum period MSG_LIB_TEXT_IND_TEXT_SMOOTHING_PERIOD, // Smoothing factor period MSG_LIB_TEXT_IND_TEXT_CUSTOM_PARAM, // Input parameter //--- CIndicatorsCollection MSG_LIB_SYS_FAILED_ADD_IND_TO_LIST, // Error. Failed to add indicator object to the list MSG_LIB_SYS_INVALID_IND_POINTER, // Error. Invalid pointer to indicator object is passed MSG_LIB_SYS_IND_ID_EXIST, // Error. Indicator object with ID already exists }; //+------------------------------------------------------------------+
また、新しく追加されたインデックスに対応するテキストも追加します。
{"Arrow indicator"}, {"Indicator ID"}, {"Empty value for plotting, for which there is no drawing"}, {"Indicator symbol"}, {"Indicator name"}, {"Indicator shortname"}, {"Indicator parameters"}, {"Volume type for calculation"}, {"Averaging period"}, {"Fast MA period"}, {"Slow MA period"}, {"Averaging period for their difference"}, {"Tenkan-sen period"}, {"Kijun-sen period"}, {"Senkou Span B period"}, {"Period for the calculation of jaws"}, {"Period for the calculation of teeth"}, {"Period for the calculation of lips"}, {"Horizontal shift of jaws"}, {"Horizontal shift of teeth"}, {"Horizontal shift of lips"}, {"Horizontal shift of the indicator"}, {"Smoothing type"}, {"Price type or handle"}, {"Number of standard deviations"}, {"Deviation of boundaries from the midline"}, {"Price increment step - acceleration factor"}, {"Maximum value of step"}, {,"K-period (number of bars for calculations)"}, {"D-period (period of first smoothing)"}, {"Final smoothing"}, {"Stochastic calculation method"}, {"Chande Momentum period"}, {"Smoothing factor period"}, {"Input parameter"}, {"Error. Failed to add indicator object to list"}, {"Error. Invalid pointer to indicator object passed"}, {"Error. There is already exist an indicator object with ID"}, }; //+---------------------------------------------------------------------+
標準指標オブジェクトを作成するときは、コンストラクタで、指標の種類(トレンド、矢印、オシレーター、ボリューム指標)に対応するグループを一度に設定します。作成される指標のタイプは事前にわかっているので、標準指標でも実行できます。カスタム指標の場合、そのタイプ(指標が属するグループ)を事前に把握して、オブジェクトクラスコンストラクタに設定することはできません。したがって、新しく作成されたオブジェクトに自動的に設定されるもう1つの指標グループ「any」を作成します。オブジェクトが作成された後、顧客にはカスタム指標アフィリエーショングループを設定する機能が提供されます。
\MQL5\Include\DoEasy\Defines.mqhファイルの指標グループ列挙に新しい定数を追加します。
//+------------------------------------------------------------------+ //| Indicator group | //+------------------------------------------------------------------+ enum ENUM_INDICATOR_GROUP { INDICATOR_GROUP_TREND, // Trend indicator INDICATOR_GROUP_OSCILLATOR, // Oscillator INDICATOR_GROUP_VOLUMES, // Volumes INDICATOR_GROUP_ARROWS, // Arrow indicator INDICATOR_GROUP_ANY, // Any indicator }; //+------------------------------------------------------------------+
作成された指標を一意のIDで呼び出すことにしたので、指標の整数プロパティの列挙に新しいプロパティを追加する必要があります。
//+------------------------------------------------------------------+ //| Indicator integer properties | //+------------------------------------------------------------------+ enum ENUM_INDICATOR_PROP_INTEGER { INDICATOR_PROP_STATUS = 0, // Indicator status (from enumeration ENUM_INDICATOR_STATUS) INDICATOR_PROP_TYPE, // Indicator type (from enumeration ENUM_INDICATOR) INDICATOR_PROP_TIMEFRAME, // Indicator timeframe INDICATOR_PROP_HANDLE, // Indicator handle INDICATOR_PROP_GROUP, // Indicator group INDICATOR_PROP_ID, // Indicator ID }; #define INDICATOR_PROP_INTEGER_TOTAL (6) // Total number of indicator integer properties #define INDICATOR_PROP_INTEGER_SKIP (0) // Number of indicator properties not used in sorting //+------------------------------------------------------------------+
そして、指標の整数プロパティの数を5から6に増やします。
リスト内の指標をIDで検索できるようにするために、IDで指標の新しい並べ替え基準を追加します。
//+------------------------------------------------------------------+ //| Possible indicator sorting criteria | //+------------------------------------------------------------------+ #define FIRST_INDICATOR_DBL_PROP (INDICATOR_PROP_INTEGER_TOTAL-INDICATOR_PROP_INTEGER_SKIP) #define FIRST_INDICATOR_STR_PROP (INDICATOR_PROP_INTEGER_TOTAL-INDICATOR_PROP_INTEGER_SKIP+INDICATOR_PROP_DOUBLE_TOTAL-INDICATOR_PROP_DOUBLE_SKIP) enum ENUM_SORT_INDICATOR_MODE { //--- Sort by integer properties SORT_BY_INDICATOR_INDEX_STATUS = 0, // Sort by indicator status SORT_BY_INDICATOR_TYPE, // Sort by indicator type SORT_BY_INDICATOR_TIMEFRAME, // Sort by indicator timeframe SORT_BY_INDICATOR_HANDLE, // Sort by indicator handle SORT_BY_INDICATOR_GROUP, // Sort by indicator group SORT_BY_INDICATOR_ID, // Sort by indicator ID //--- Sort by real properties SORT_BY_INDICATOR_EMPTY_VALUE = FIRST_INDICATOR_DBL_PROP,// Sort by the empty value for plotting where nothing will be drawn //--- Sort by string properties SORT_BY_INDICATOR_SYMBOL = FIRST_INDICATOR_STR_PROP, // Sort by indicator symbol SORT_BY_INDICATOR_NAME, // Sort by indicator name SORT_BY_INDICATOR_SHORTNAME, // Sort by indicator short name }; //+------------------------------------------------------------------+
指標オブジェクトに新しいプロパティが追加されたため、\MQL5\Include\DoEasy\Objects\Indicators\IndicatorDE.mqhの抽象指標オブジェクトクラスをわずかに改善します。
クラスのpublicセクションに、「指標ID」プロパティを設定および取得する2つのメソッドを記述します。
//--- Set indicator’s (1) group, (2) empty value of buffers, (3) name, (4) short name, (5) indicator ID void SetGroup(const ENUM_INDICATOR_GROUP group) { this.SetProperty(INDICATOR_PROP_GROUP,group); } void SetEmptyValue(const double value) { this.SetProperty(INDICATOR_PROP_EMPTY_VALUE,value); } void SetName(const string name) { this.SetProperty(INDICATOR_PROP_NAME,name); } void SetShortName(const string shortname) { this.SetProperty(INDICATOR_PROP_SHORTNAME,shortname); } void SetID(const int id) { this.SetProperty(INDICATOR_PROP_ID,id); } //--- Return indicator’s (1) status, (2) group, (3) timeframe, (4) type, (5) handle, (6) ID, //--- (7) empty value of buffers, (8) name, (9) short name, (10) symbol ENUM_INDICATOR_STATUS Status(void) const { return (ENUM_INDICATOR_STATUS)this.GetProperty(INDICATOR_PROP_STATUS);} ENUM_INDICATOR_GROUP Group(void) const { return (ENUM_INDICATOR_GROUP)this.GetProperty(INDICATOR_PROP_GROUP); } ENUM_TIMEFRAMES Timeframe(void) const { return (ENUM_TIMEFRAMES)this.GetProperty(INDICATOR_PROP_TIMEFRAME); } ENUM_INDICATOR TypeIndicator(void) const { return (ENUM_INDICATOR)this.GetProperty(INDICATOR_PROP_TYPE); } int Handle(void) const { return (int)this.GetProperty(INDICATOR_PROP_HANDLE); } int ID(void) const { return (int)this.GetProperty(INDICATOR_PROP_ID); } double EmptyValue(void) const { return this.GetProperty(INDICATOR_PROP_EMPTY_VALUE); } string Name(void) const { return this.GetProperty(INDICATOR_PROP_NAME); } string ShortName(void) const { return this.GetProperty(INDICATOR_PROP_SHORTNAME); } string Symbol(void) const { return this.GetProperty(INDICATOR_PROP_SYMBOL); }
標準の指標オブジェクトでは、特定の指標のどのパラメータが記述されているかが明確にわかっているため、各指標パラメータの説明を表示するメソッドを追加できます。カスタム指標パラメータの説明を表示するために、事前に不明な指標の各パラメータの目的を明確に知ることはできません。したがって、MqlParam指標のパラメータ配列から後続の各パラメータの説明を表示するだけです。
抽象指標オブジェクトクラスのpublicセクションで、MqlParam構造体パラメータの説明を表示するメソッドに加えて、インデックスオブジェクトからインデックスとバーの時刻でデータを取得する2つのメソッドを宣言します。
//--- Return description of (1) type, (2) status, (3) group, (4) timeframe, (5) empty value of indicator, (6) parameter of m_mql_param array string GetTypeDescription(void) const { return m_ind_type_description; } string GetStatusDescription(void) const; string GetGroupDescription(void) const; string GetTimeframeDescription(void) const; string GetEmptyValueDescription(void) const; string GetMqlParamDescription(const int index) const; //--- Display the description of indicator object properties in the journal (full_prop=true - all properties, false - supported ones only) void Print(const bool full_prop=false); //--- Display (1) a short description, (2) description of indicator object parameters in the journal (implementation in the descendants) virtual void PrintShort(void) {;} virtual void PrintParameters(void) {;} //--- Return data of specified buffer from specified bar (1) by index, (2) by bar time double GetDataBuffer(const int buffer_num,const int index); double GetDataBuffer(const int buffer_num,const datetime time); }; //+------------------------------------------------------------------+
これらのメソッドをクラス本体の外側で実装しましょう。
以下は、MqlParam構造体配列のパラメータの説明を返すメソッドです。
//+------------------------------------------------------------------+ //| Return the description of parameter of m_mql_param array | //+------------------------------------------------------------------+ string CIndicatorDE::GetMqlParamDescription(const int index) const { return "["+(string)index+"] "+MqlParameterDescription(this.m_mql_param[index]); } //+------------------------------------------------------------------+
メソッドには配列内のパラメータインデックスを渡します。さらに、指定された配列インデックスによって構造体に格納されているデータに対応して、配列インデックスとパラメータの説明から文字列を作成します。以下では、MqlParam構造体のデータの説明を返すMqlParameterDescription()関数を記述します。
以下は、インデックスとバー時刻で指標データを返すメソッドです。
//+------------------------------------------------------------------+ //| Return data of specified buffer from specified bar by index | //+------------------------------------------------------------------+ double CIndicatorDE::GetDataBuffer(const int buffer_num,const int index) { double array[1]={EMPTY_VALUE}; int copied=::CopyBuffer(this.Handle(),buffer_num,index,1,array); return(copied==1 ? array[0] : this.EmptyValue()); } //+------------------------------------------------------------------+ //| Return data of specified buffer from specified bar by time | //+------------------------------------------------------------------+ double CIndicatorDE::GetDataBuffer(const int buffer_num,const datetime time) { double array[1]={EMPTY_VALUE}; int copied=::CopyBuffer(this.Handle(),buffer_num,time,1,array); return(copied==1 ? array[0] : this.EmptyValue()); } //+------------------------------------------------------------------+
メソッドは、指標からデータを受信する必要があるインデックスまたはバー時刻を受信します。CopyBuffer()関数を使用して、インデックスまたは時刻で1つのバーをリクエストし、配列に書き込まれた受信結果を返します。何らかの理由でデータが受信されない場合、メソッドは指標オブジェクトに設定された空の値を返します。
クラスコンストラクタで、デフォルトの指標IDを設定します(-1)。
//+------------------------------------------------------------------+ //| Closed parametric constructor | //+------------------------------------------------------------------+ CIndicatorDE::CIndicatorDE(ENUM_INDICATOR ind_type, string symbol, ENUM_TIMEFRAMES timeframe, ENUM_INDICATOR_STATUS status, ENUM_INDICATOR_GROUP group, string name, string shortname, MqlParam &mql_params[]) { //--- Set collection ID for the object this.m_type=COLLECTION_INDICATORS_ID; //--- Write description of indicator type this.m_ind_type_description=IndicatorTypeDescription(ind_type); //--- If parameter array size passed to constructor is more than zero //--- fill in the array of object parameters with data from the array passed to constructor int count=::ArrayResize(this.m_mql_param,::ArraySize(mql_params)); for(int i=0;i<count;i++) { this.m_mql_param[i].type = mql_params[i].type; this.m_mql_param[i].double_value = mql_params[i].double_value; this.m_mql_param[i].integer_value= mql_params[i].integer_value; this.m_mql_param[i].string_value = mql_params[i].string_value; } //--- Create indicator handle int handle=::IndicatorCreate(symbol,timeframe,ind_type,count,this.m_mql_param); //--- Save integer properties this.m_long_prop[INDICATOR_PROP_STATUS] = status; this.m_long_prop[INDICATOR_PROP_TYPE] = ind_type; this.m_long_prop[INDICATOR_PROP_GROUP] = group; this.m_long_prop[INDICATOR_PROP_TIMEFRAME] = timeframe; this.m_long_prop[INDICATOR_PROP_HANDLE] = handle; this.m_long_prop[INDICATOR_PROP_ID] = WRONG_VALUE; //--- Save real properties this.m_double_prop[this.IndexProp(INDICATOR_PROP_EMPTY_VALUE)]=EMPTY_VALUE; //--- Save string properties this.m_string_prop[this.IndexProp(INDICATOR_PROP_SYMBOL)] = (symbol==NULL || symbol=="" ? ::Symbol() : symbol); this.m_string_prop[this.IndexProp(INDICATOR_PROP_NAME)] = name; this.m_string_prop[this.IndexProp(INDICATOR_PROP_SHORTNAME)]= shortname; } //+------------------------------------------------------------------+
各指標のハンドルは一意であり、IDは独自の裁量で設定するため、2つの指標のIDをパラメータで比較する場合は、上記の2つのパラメータをスキップする必要があります。そうしないと、各指標が一意として識別され、2つの指標のパラメータを正しく比較して同一性を確認できなくなります。
これを回避するには、2つの指標オブジェクトを比較するメソッドで「handle」プロパティと「ID」プロパティをスキップします。
//+------------------------------------------------------------------+ //| Compare CIndicatorDE objects with each other by all properties | //+------------------------------------------------------------------+ bool CIndicatorDE::IsEqual(CIndicatorDE *compared_obj) const { if(!IsEqualMqlParamArrays(compared_obj.m_mql_param)) return false; int beg=0, end=INDICATOR_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_INDICATOR_PROP_INTEGER prop=(ENUM_INDICATOR_PROP_INTEGER)i; if(prop==INDICATOR_PROP_HANDLE || prop==INDICATOR_PROP_ID) continue; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=INDICATOR_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_INDICATOR_PROP_DOUBLE prop=(ENUM_INDICATOR_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=INDICATOR_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_INDICATOR_PROP_STRING prop=(ENUM_INDICATOR_PROP_STRING)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
指標の整数プロパティの説明を返すメソッドに「ID」指標プロパティ説明の表示を追加します。
//+------------------------------------------------------------------+ //| Return description of indicator's integer property | //+------------------------------------------------------------------+ string CIndicatorDE::GetPropertyDescription(ENUM_INDICATOR_PROP_INTEGER property) { return ( property==INDICATOR_PROP_STATUS ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_STATUS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetStatusDescription() ) : property==INDICATOR_PROP_TYPE ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTypeDescription() ) : property==INDICATOR_PROP_GROUP ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_GROUP)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetGroupDescription() ) : property==INDICATOR_PROP_ID ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_ID)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==INDICATOR_PROP_TIMEFRAME ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TIMEFRAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTimeframeDescription() ) : property==INDICATOR_PROP_HANDLE ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_HANDLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : "" ); } //+------------------------------------------------------------------+
次に、サービス関数ファイル\MQL5\Include\DoEasy\Services\DELib.mqhに、MqlParam構造体の説明を表示する関数を追加します。
//+------------------------------------------------------------------+ //| Return the description of parameter MqlParam array | //+------------------------------------------------------------------+ string MqlParameterDescription(const MqlParam &mql_param) { int type=mql_param.type; string res=CMessage::Text(MSG_ORD_TYPE)+" "+typename(type)+": "; //--- type of parameter string if(type==TYPE_STRING) res+=mql_param.string_value; //--- type of parameter datetime else if(type==TYPE_DATETIME) res+=TimeToString(mql_param.integer_value,TIME_DATE|TIME_MINUTES|TIME_SECONDS); //--- type of parameter color else if(type==TYPE_COLOR) res+=ColorToString((color)mql_param.integer_value,true); //--- type of parameter bool else if(type==TYPE_BOOL) res+=(string)(bool)mql_param.integer_value; //--- integer types else if(type>TYPE_BOOL && type<TYPE_FLOAT) res+=(string)mql_param.integer_value; //--- real types else res+=DoubleToString(mql_param.double_value,8); return res; } //+------------------------------------------------------------------+
MqlParam構造体にはいくつかのフィールドがあります。そのうちの1つには、構造体に格納されているデータ型が含まれています。このデータ型により、どの構造体フィールドからデータを受信し、データ文字列表示のどの機能を操作ログのデータ表示に使用するかを理解できます。
データ型を取得し、"Type " +" "+ data type string description + ": "を含む文字列の形成を始めます。
さらに、データ型に応じて、作成済みの文字列に、構造体フィールドに格納されている値の説明を対応する型に追加します。 必要なデータ型を文字列として表示するには標準関数を使用します。
最終的に、抽象指標オブジェクトクラスのMqlParam構造体パラメータを表示するメソッドとターミナル操作ログで上記の分析されたサービス関数を使用する場合の4つのパラメータを持つカスタム指標のパラメータの説明は次のようになります。
--- Indicator parameters --- - [1] Type int: 13 - [2] Type int: 0 - [3] Type int: 0 - [4] Type int: 2
指標オブジェクトに新しいプロパティ(ID)が追加されたため、\MQL5\Include\DoEasy\Objects\Indicators\Standart\フォルダの指標オブジェクトのすべてのクラスの指標の短縮名を表示するメソッドに少し追加します。短縮名にID値を追加するだけです。
//+------------------------------------------------------------------+ //| Display a short description of indicator object in the journal | //+------------------------------------------------------------------+ void CIndAC::PrintShort(void) { string id=(this.ID()>WRONG_VALUE ? ", id #"+(string)this.ID()+"]" : "]"); ::Print(GetStatusDescription()," ",this.Name()," ",this.Symbol()," ",TimeframeDescription(this.Timeframe())," [handle ",this.Handle(),id); } //+------------------------------------------------------------------+
ここで、IDの説明を作成します。一方、ID値が-1より大きい場合、IDが表示されます。それ以外の場合、IDが欠落している(値が-1)場合、説明には表示されません( 角括弧を閉じるだけです)。さらに、受信した文字列を指標の短い説明に追加します。
このような改善は、すべての指標オブジェクトクラスですでに行われています。
カスタム指標オブジェクト
次に、カスタム指標オブジェクトクラスを追加しましょう。ライブラリの標準指標フォルダ(\MQL5\Include\DoEasy\Objects\Indicators\Standart\)に配置します。これを行う理由は、ターミナル指標リストにカスタム指標も含まれているということだけです。また、これは、ターミナルの指標リストにも属していることを意味します。
一般的にクラス全体を分析します。
//+------------------------------------------------------------------+ //| IndCustom.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 "..\\IndicatorDE.mqh" //+------------------------------------------------------------------+ //| Custom indicator | //+------------------------------------------------------------------+ class CIndCustom : public CIndicatorDE { private: public: //--- Constructor CIndCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,MqlParam &mql_param[]) : CIndicatorDE(IND_CUSTOM,symbol,timeframe, INDICATOR_STATUS_CUSTOM, INDICATOR_GROUP_ANY, mql_param[0].string_value, mql_param[0].string_value+"("+(symbol==NULL || symbol=="" ? ::Symbol() : symbol)+ ","+TimeframeDescription(timeframe)+")",mql_param) {} //--- Supported indicator properties (1) real, (2) integer virtual bool SupportProperty(ENUM_INDICATOR_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_INDICATOR_PROP_INTEGER property); //--- Display (1) a short description, (2) description of indicator object parameters in the journal virtual void PrintShort(void); virtual void PrintParameters(void); }; //+------------------------------------------------------------------+ //| Return 'true' if indicator supports a passed | //| integer property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CIndCustom::SupportProperty(ENUM_INDICATOR_PROP_INTEGER property) { return true; } //+------------------------------------------------------------------+ //| Return 'true' if indicator supports a passed | //| real property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CIndCustom::SupportProperty(ENUM_INDICATOR_PROP_DOUBLE property) { return true; } //+------------------------------------------------------------------+ //| Display a short description of indicator object in the journal | //+------------------------------------------------------------------+ void CIndCustom::PrintShort(void) { string id=(this.ID()>WRONG_VALUE ? ", id #"+(string)this.ID()+"]" : "]"); ::Print(GetStatusDescription()," ",this.Name()," ",this.Symbol()," ",TimeframeDescription(this.Timeframe())," [handle ",this.Handle(),id); } //+------------------------------------------------------------------+ //| Display parameter description of indicator object in the journal | //+------------------------------------------------------------------+ void CIndCustom::PrintParameters(void) { ::Print(" --- ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS)," --- "); int total=::ArraySize(this.m_mql_param); for(int i=1;i<total;i++) { ::Print(" - ",this.GetMqlParamDescription(i)); } } //+------------------------------------------------------------------+
標準指標オブジェクトクラスにより、すべてのメソッドがすでにわかっています。しかし、それらとは対照的に、ここではクラスコンストラクタに対して、入力ではなく、MqlParam入力構造体の事前に作成された配列で作成された指標のパラメータを取得します。また、抽象指標オブジェクトクラスのクローズドコンストラクタには、「カスタム指標」ステータス、「任意の指標」グループ、および名前としてパラメータ配列の最初の要素を渡します。カスタム指標を作成するときにはTYPE_STRING型が必須で、フィールドstring_valueの値にはカスタム指標名が含まれます。同様に、短い指標名を作成しますが、これを銘柄と時間枠の説明とともに行います。さらに、指標名と短い指標名は、親クラスのSetName()メソッドとSetShortName()メソッドを使用して変更できます。ただし、指標名にはすでにそのパスが含まれています。したがって(少なくとも、ライブラリ開発のこのフェーズでは)、作成済みのカスタム指標の名前を変更することはできません。
操作ログ内で指標オブジェクトパラメータの説明を表示するメソッドで、次の手順を実行します。最初にヘッダーを表示し、次に以前に分析されたメソッド(特に、親クラスのGetMqlParamDescription()メソッド)を使用して指標パラメータ配列を反復処理してそれぞれを表示します。
すべての指標オブジェクトは \MQL5\Include\DoEasy\Collections\IndicatorsCollection.mqhのクラスCIndicatorsCollectionのコレクションリストに格納されます。
指標をコレクションリストに追加するときは、そのIDの一意性をさらに確認する必要があります。また、カスタム指標クラスの可視性を追加して、指標のコレクションクラスがそれらを処理できるようにします。
カスタム指標クラスのファイルを指標オブジェクトコレクションクラスにインクルードします。
//+------------------------------------------------------------------+ //| IndicatorsCollection.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Objects\Indicators\Standart\IndAC.mqh" #include "..\Objects\Indicators\Standart\IndAD.mqh" #include "..\Objects\Indicators\Standart\IndADX.mqh" #include "..\Objects\Indicators\Standart\IndADXW.mqh" #include "..\Objects\Indicators\Standart\IndAlligator.mqh" #include "..\Objects\Indicators\Standart\IndAMA.mqh" #include "..\Objects\Indicators\Standart\IndAO.mqh" #include "..\Objects\Indicators\Standart\IndATR.mqh" #include "..\Objects\Indicators\Standart\IndBands.mqh" #include "..\Objects\Indicators\Standart\IndBears.mqh" #include "..\Objects\Indicators\Standart\IndBulls.mqh" #include "..\Objects\Indicators\Standart\IndBWMFI.mqh" #include "..\Objects\Indicators\Standart\IndCCI.mqh" #include "..\Objects\Indicators\Standart\IndChaikin.mqh" #include "..\Objects\Indicators\Standart\IndCustom.mqh" #include "..\Objects\Indicators\Standart\IndDEMA.mqh" #include "..\Objects\Indicators\Standart\IndDeMarker.mqh" #include "..\Objects\Indicators\Standart\IndEnvelopes.mqh" #include "..\Objects\Indicators\Standart\IndForce.mqh" #include "..\Objects\Indicators\Standart\IndFractals.mqh" #include "..\Objects\Indicators\Standart\IndFRAMA.mqh" #include "..\Objects\Indicators\Standart\IndGator.mqh" #include "..\Objects\Indicators\Standart\IndIchimoku.mqh" #include "..\Objects\Indicators\Standart\IndMA.mqh" #include "..\Objects\Indicators\Standart\IndMACD.mqh" #include "..\Objects\Indicators\Standart\IndMFI.mqh" #include "..\Objects\Indicators\Standart\IndMomentum.mqh" #include "..\Objects\Indicators\Standart\IndOBV.mqh" #include "..\Objects\Indicators\Standart\IndOsMA.mqh" #include "..\Objects\Indicators\Standart\IndRSI.mqh" #include "..\Objects\Indicators\Standart\IndRVI.mqh" #include "..\Objects\Indicators\Standart\IndSAR.mqh" #include "..\Objects\Indicators\Standart\IndStDev.mqh" #include "..\Objects\Indicators\Standart\IndStoch.mqh" #include "..\Objects\Indicators\Standart\IndTEMA.mqh" #include "..\Objects\Indicators\Standart\IndTRIX.mqh" #include "..\Objects\Indicators\Standart\IndVIDYA.mqh" #include "..\Objects\Indicators\Standart\IndVolumes.mqh" #include "..\Objects\Indicators\Standart\IndWPR.mqh" //+------------------------------------------------------------------+
クラスのprivateセクションで、コレクションに指標オブジェクトを追加するメソッドとリスト内でセットIDを持つ指標オブジェクトの可用性を確認するメソッドを宣言します。
//+------------------------------------------------------------------+ //| Indicator collection | //+------------------------------------------------------------------+ class CIndicatorsCollection : public CObject { private: CListObj m_list; // Indicator object list MqlParam m_mql_param[]; // Array of indicator parameters //--- (1) Create, (2) add to collection list a new indicator object and set an ID for it CIndicatorDE *CreateIndicator(const ENUM_INDICATOR ind_type,MqlParam &mql_param[],const string symbol_name=NULL,const ENUM_TIMEFRAMES period=PERIOD_CURRENT); int AddIndicatorToList(CIndicatorDE *indicator,const int id); //--- Return the indicator index in the list int Index(CIndicatorDE *compared_obj); //--- Check presence of indicator object with specified id in the list bool CheckID(const int id); public:
クラスのpublicセクションで、配列MqlParamで指定されたグループとパラメータによってカスタム指標オブジェクトへのポインタを返すメソッド を宣言します(標準指標とは対照的に、パラメータはそのような配列を渡すことによってのみ指定できます)。
CIndicatorDE *GetIndCCI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const ENUM_APPLIED_PRICE applied_price); CIndicatorDE *GetIndCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,ENUM_INDICATOR_GROUP group,MqlParam ¶m[]); CIndicatorDE *GetIndDEMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_APPLIED_PRICE applied_price);
指標作成メソッドの宣言ブロックで、まず各メソッドのパラメータに指標IDの表示を追加し、次にカスタム指標を作成するメソッドの宣言を追加します。これらのメソッドの完全な宣言コードは提示しません。代わりに、3つのメソッドのみを提示します(すべてのメソッドはすでに改善されており、添付ファイルにあります)。
int CreateCCI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id, const int ma_period=14, const ENUM_APPLIED_PRICE applied_price=PRICE_TYPICAL); int CreateCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id, ENUM_INDICATOR_GROUP group, MqlParam &mql_param[]); int CreateDEMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id, const int ma_period=14, const int ma_shift=0, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE);
銘柄と時間枠の入力後、すべてのメソッドで、作成時に必須の指標IDの指定が追加されました。
カスタム指標を作成するメソッドでは、さらに指標グループが指定され、事前に作成および入力された指標パラメータの配列が渡されます。カスタム指標はそれらに基づいて作成されます。
クラス本体の最後に、指定された指標のIDを設定するメソッドと指定されたIDで指標オブジェクトを返すメソッドを宣言します。
//--- Set ID for the specified indicator void SetID(CIndicatorDE *indicator,const int id); //--- Return indicator object by its ID CIndicatorDE *GetIndByID(const uint id); //--- Display (1) the complete and (2) short collection description in the journal void Print(void); void PrintShort(void); //--- Constructor CIndicatorsCollection(); }; //+------------------------------------------------------------------+
次に、宣言されたすべてのメソッドを分析しましょう。
指標オブジェクトを作成する新しいprivateメソッドで新しいオブジェクト(カスタム指標)の作成を追加します。
//+------------------------------------------------------------------+ //| Create new indicator object | //+------------------------------------------------------------------+ CIndicatorDE *CIndicatorsCollection::CreateIndicator(const ENUM_INDICATOR ind_type,MqlParam &mql_param[],const string symbol_name=NULL,const ENUM_TIMEFRAMES period=PERIOD_CURRENT) { string symbol=(symbol_name==NULL || symbol_name=="" ? ::Symbol() : symbol_name); ENUM_TIMEFRAMES timeframe=(period==PERIOD_CURRENT ? ::Period() : period); CIndicatorDE *indicator=NULL; switch(ind_type) { case IND_AC : indicator=new CIndAC(symbol,timeframe,mql_param); break; case IND_AD : indicator=new CIndAD(symbol,timeframe,mql_param); break; case IND_ADX : indicator=new CIndADX(symbol,timeframe,mql_param); break; case IND_ADXW : indicator=new CIndADXW(symbol,timeframe,mql_param); break; case IND_ALLIGATOR : indicator=new CIndAlligator(symbol,timeframe,mql_param); break; case IND_AMA : indicator=new CIndAMA(symbol,timeframe,mql_param); break; case IND_AO : indicator=new CIndAO(symbol,timeframe,mql_param); break; case IND_ATR : indicator=new CIndATR(symbol,timeframe,mql_param); break; case IND_BANDS : indicator=new CIndBands(symbol,timeframe,mql_param); break; case IND_BEARS : indicator=new CIndBears(symbol,timeframe,mql_param); break; case IND_BULLS : indicator=new CIndBulls(symbol,timeframe,mql_param); break; case IND_BWMFI : indicator=new CIndBWMFI(symbol,timeframe,mql_param); break; case IND_CCI : indicator=new CIndCCI(symbol,timeframe,mql_param); break; case IND_CHAIKIN : indicator=new CIndCHO(symbol,timeframe,mql_param); break; case IND_DEMA : indicator=new CIndDEMA(symbol,timeframe,mql_param); break; case IND_DEMARKER : indicator=new CIndDeMarker(symbol,timeframe,mql_param); break; case IND_ENVELOPES : indicator=new CIndEnvelopes(symbol,timeframe,mql_param); break; case IND_FORCE : indicator=new CIndForce(symbol,timeframe,mql_param); break; case IND_FRACTALS : indicator=new CIndFractals(symbol,timeframe,mql_param); break; case IND_FRAMA : indicator=new CIndFRAMA(symbol,timeframe,mql_param); break; case IND_GATOR : indicator=new CIndGator(symbol,timeframe,mql_param); break; case IND_ICHIMOKU : indicator=new CIndIchimoku(symbol,timeframe,mql_param); break; case IND_MA : indicator=new CIndMA(symbol,timeframe,mql_param); break; case IND_MACD : indicator=new CIndMACD(symbol,timeframe,mql_param); break; case IND_MFI : indicator=new CIndMFI(symbol,timeframe,mql_param); break; case IND_MOMENTUM : indicator=new CIndMomentum(symbol,timeframe,mql_param); break; case IND_OBV : indicator=new CIndOBV(symbol,timeframe,mql_param); break; case IND_OSMA : indicator=new CIndOsMA(symbol,timeframe,mql_param); break; case IND_RSI : indicator=new CIndRSI(symbol,timeframe,mql_param); break; case IND_RVI : indicator=new CIndRVI(symbol,timeframe,mql_param); break; case IND_SAR : indicator=new CIndSAR(symbol,timeframe,mql_param); break; case IND_STDDEV : indicator=new CIndStDev(symbol,timeframe,mql_param); break; case IND_STOCHASTIC : indicator=new CIndStoch(symbol,timeframe,mql_param); break; case IND_TEMA : indicator=new CIndTEMA(symbol,timeframe,mql_param); break; case IND_TRIX : indicator=new CIndTRIX(symbol,timeframe,mql_param); break; case IND_VIDYA : indicator=new CIndVIDYA(symbol,timeframe,mql_param); break; case IND_VOLUMES : indicator=new CIndVolumes(symbol,timeframe,mql_param); break; case IND_WPR : indicator=new CIndWPR(symbol,timeframe,mql_param); break; case IND_CUSTOM : indicator=new CIndCustom(symbol,timeframe,mql_param); break; default: break; } return indicator; } //+------------------------------------------------------------------+
コレクションリストに新しい指標オブジェクトを追加するメソッドを変更します。
//+------------------------------------------------------------------+ //| Add a new indicator object to collection list | //+------------------------------------------------------------------+ int CIndicatorsCollection::AddIndicatorToList(CIndicatorDE *indicator,const int id) { //--- If invalid indicator is passed to the object - return INVALID_HANDLE if(indicator==NULL) return INVALID_HANDLE; //--- If such indicator is already in the list int index=this.Index(indicator); if(index!=WRONG_VALUE) { //--- Remove the earlier created object, get indicator object from the list and return indicator handle delete indicator; indicator=this.m_list.At(index); } //--- If indicator object is not in the list yet else { //--- If failed to add indicator object to the list - display a corresponding message, //--- remove object and return INVALID_HANDLE if(!this.m_list.Add(indicator)) { ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_ADD_IND_TO_LIST)); delete indicator; return INVALID_HANDLE; } } //--- If indicator is successfully added to the list or is already there... //--- If indicator with specified ID (not -1) is not in the list - set ID if(id>WRONG_VALUE && !this.CheckID(id)) indicator.SetID(id); //--- Return handle of a new indicator added to the list return indicator.Handle(); } //+------------------------------------------------------------------+
これで、指標IDもメソッドに渡されます。そのようなIDの指標がまだコレクションリストにない場合は、指定されたIDが指標オブジェクトに設定されます。それ以外の場合、指標IDはデフォルトで-1に設定されます。
これで、指標オブジェクトを作成するためのすべてのメソッドが短くなりました。
例としてACおよびAlligator指標オブジェクトの作成を使用して分析してみましょう。
//+------------------------------------------------------------------+ //| Create a new indicator object Accelerator Oscillator | //| and place it to the collection list | //+------------------------------------------------------------------+ int CIndicatorsCollection::CreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id) { //--- AC indicator possesses no parameters - resize the array of parameter structures ::ArrayResize(this.m_mql_param,0); //--- Create indicator object CIndicatorDE *indicator=this.CreateIndicator(IND_AC,this.m_mql_param,symbol,timeframe); //--- Return indicator handle received as a result of adding the object to collection list return this.AddIndicatorToList(indicator,id); } //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| Create new indicator object Alligator | //| and place it to the collection list | //+------------------------------------------------------------------+ int CIndicatorsCollection::CreateAlligator(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id, const int jaw_period=13, const int jaw_shift=8, const int teeth_period=8, const int teeth_shift=5, const int lips_period=5, const int lips_shift=3, const ENUM_MA_METHOD ma_method=MODE_SMMA, const ENUM_APPLIED_PRICE applied_price=PRICE_MEDIAN) { //--- Add required indicator parameters to the array of parameter structures ::ArrayResize(this.m_mql_param,8); this.m_mql_param[0].type=TYPE_INT; this.m_mql_param[0].integer_value=jaw_period; this.m_mql_param[1].type=TYPE_INT; this.m_mql_param[1].integer_value=jaw_shift; this.m_mql_param[2].type=TYPE_INT; this.m_mql_param[2].integer_value=teeth_period; this.m_mql_param[3].type=TYPE_INT; this.m_mql_param[3].integer_value=teeth_shift; this.m_mql_param[4].type=TYPE_INT; this.m_mql_param[4].integer_value=lips_period; this.m_mql_param[5].type=TYPE_INT; this.m_mql_param[5].integer_value=lips_shift; this.m_mql_param[6].type=TYPE_INT; this.m_mql_param[6].integer_value=ma_method; this.m_mql_param[7].type=TYPE_INT; this.m_mql_param[7].integer_value=applied_price; //--- Create indicator object CIndicatorDE *indicator=this.CreateIndicator(IND_ALLIGATOR,this.m_mql_param,symbol,timeframe); //--- Return indicator handle received as a result of adding the object to collection list return this.AddIndicatorToList(indicator,id); } //+------------------------------------------------------------------+
これで、入力構造体を入力し、指標を作成して、コレクションリストに追加するメソッドを呼び出すだけで十分です。このような変更は、指標オブジェクト作成のすべてのメソッドで行なわれました。カスタム指標を作成するメソッド以外は分析しません。
//+------------------------------------------------------------------+ //| Create a new object - custom indicator | //| and place it to the collection list | //+------------------------------------------------------------------+ int CIndicatorsCollection::CreateCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id, ENUM_INDICATOR_GROUP group, MqlParam &mql_param[]) { //--- Create indicator object CIndicatorDE *indicator=this.CreateIndicator(IND_CUSTOM,mql_param,symbol,timeframe); if(indicator==NULL) return INVALID_HANDLE; //--- Set a group for indicator object indicator.SetGroup(group); //--- Return indicator handle received as a result of adding the object to collection list return this.AddIndicatorToList(indicator,id); } //+------------------------------------------------------------------+
ここでは少し異なります。ここでは、IDとは別に、作成メソッドは指標グループも取得します。また、作成されたカスタム指標のパラメータについて事前に知ることができないため、作成された指標のすべてのパラメータは、MqlParamパラメータの配列内で一度に渡されます。
各入力パラメータの標準デフォルト値は、標準指標を作成するすべてのメソッドに追加されました。したがって、デフォルトのパラメータを使用して標準指標を作成するには、銘柄、時間枠、IDを指定するだけで十分です。
以下は、オブジェクトカスタム指標へのポインタを返すメソッドの実装です。
//+------------------------------------------------------------------+ //| Return pointer to object- custom indicator | //+------------------------------------------------------------------+ CIndicatorDE *CIndicatorsCollection::GetIndCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,ENUM_INDICATOR_GROUP group,MqlParam ¶m[]) { CIndicatorDE *tmp=new CIndCustom(symbol,timeframe,param); if(tmp==NULL) return NULL; tmp.SetGroup(group); int index=this.Index(tmp); delete tmp; return(index>WRONG_VALUE ? this.m_list.At(index) : NULL); } //+------------------------------------------------------------------+
ここでは、一時的な指標オブジェクトを作成してコレクションリスト内の同じオブジェクトを検索し、グループを設定し、コレクションリストで見つかったオブジェクトのインデックスを取得します。次に、一時オブジェクトを削除し、リストで見つかったインデックスによってオブジェクトへのポインタを返す、またはそのようなオブジェクトがリストに見つからない場合はNULLを返します。
以下は、リスト内で指定されたIDを持つ指標オブジェクトの存在を確認するメソッドです。
//+------------------------------------------------------------------+ //| Check presence of indicator object with specified id in the list | //+------------------------------------------------------------------+ bool CIndicatorsCollection::CheckID(const int id) { CArrayObj *list=CSelect::ByIndicatorProperty(this.GetList(),INDICATOR_PROP_ID,id,EQUAL); return(list!=NULL && list.Total()!=0); } //+------------------------------------------------------------------+
指定されたIDを持つ指標オブジェクトのリストを取得し、リストが有効で空でないかどうかを確認するフラグを返します(リストには1つのオブジェクトが含まれている必要があります)。
以下は、指定された指標のIDを設定するメソッドです。
//+------------------------------------------------------------------+ //| Set ID for the specified indicator | //+------------------------------------------------------------------+ void CIndicatorsCollection::SetID(CIndicatorDE *indicator,const int id) { if(indicator==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_INVALID_IND_POINTER)); return; } if(id>WRONG_VALUE) { if(CheckID(id)) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_IND_ID_EXIST)," #",(string)id); return; } } indicator.SetID(id); } //+------------------------------------------------------------------+
メソッドは、パラメータによってメソッドに渡されるIDを設定する必要がある指標オブジェクトへのポインタを受け取ります。
無効なポインタが渡された場合は、メソッドに通知して終了します。
ID値が-1より大きい場合は同じIDの指標オブジェクトの存在を確認し、使用可能な場合は通知して終了します。
すべての確認に成功したら、オブジェクトのIDを設定します。メソッドに渡されるID値がゼロ未満の場合、そのようなIDは確認なしでオブジェクトに設定されます。指標オブジェクトのそのようなIDはその不在を意味します。
以下は、指定されたIDで指標オブジェクトを返すメソッドです。
//+------------------------------------------------------------------+ //| Return indicator object by its ID | //+------------------------------------------------------------------+ CIndicatorDE *CIndicatorsCollection::GetIndByID(const uint id) { CArrayObj *list=CSelect::ByIndicatorProperty(this.GetList(),INDICATOR_PROP_ID,id,EQUAL); return(list==NULL || list.Total()==0 ? NULL : list.At(list.Total()-1)); } //+------------------------------------------------------------------+
ここで、指定されたIDを持つ指標オブジェクトのリストを取得し、NULL(リストの取得に失敗した場合またはリストが空の場合)、または必要なIDを持つオブジェクトへのポインタを返します。指定されたIDを持つオブジェクトは1つだけであるため、受信したリストで指定されるインデックスは最初か最後かは関係ありません。ここでは最後のものを指定します。
EAでの指標作成のテストでは、問題が示されました。時間枠を変更すると、同じ追加の指標が作成されますが、別の時間枠が使用されます。そしてそれは真実です:同じ入力を持つが異なる時間枠で計算された指標は2つの異なる指標です。この問題を簡単に回避するには、プログラムの初期化解除中に作成された指標のリストをクリアするだけで十分です。この目的のために、CEngineライブラリのメインオブジェクトのファイル(\MQL5\Include\DoEasy\Engine.mqh)で新しいOnDeinit()ハンドラを宣言します。
//--- (1) Timer, event handler (2) NewTick, (3) Calculate, Deinit void OnTimer(SDataCalculate &data_calculate); void OnTick(SDataCalculate &data_calculate,const uint required=0); int OnCalculate(SDataCalculate &data_calculate,const uint required=0); void OnDeinit(void);
クラス本体の外側で実装します。
//+------------------------------------------------------------------+ //| Deinitialize library | //+------------------------------------------------------------------+ void CEngine::OnDeinit(void) { this.m_indicators.GetList().Clear(); } //+------------------------------------------------------------------+
このクラスメソッドは、プログラムのOnDeinit()ハンドラから呼び出されます。ここにあるのは、指標コレクションオブジェクトのコレクションリストを消去するメソッドの呼び出しです。
テスト
前の記事のEAを使用して、さまざまな指標の作成をテストし、作成された指標オブジェクトからデータを取得しましょう。
新しい\MQL5\Experts\TestDoEasy\Part56\フォルダで新しくTestDoEasyPart56.mq5として保存します。
EAで、移動平均を使用して2つのカスタム指標を作成しますが、パラメータは異なります(ターミナル標準配布の指標サンプルフォルダである\MQL5\Indicators\Examples\から指標を取得します)。また、入力が異なる2つの標準指標Adaptive MovingAverageを作成します。
グローバル領域で、マクロ置換を設定してIDによる指標の呼び出しを簡素化し、2つのカスタム指標を作成するためのパラメータの2つの配列を宣言します。
//+------------------------------------------------------------------+ //| TestDoEasyPart56.mq5 | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Engine.mqh> //--- enums enum ENUM_BUTTONS { BUTT_BUY, BUTT_BUY_LIMIT, BUTT_BUY_STOP, BUTT_BUY_STOP_LIMIT, BUTT_CLOSE_BUY, BUTT_CLOSE_BUY2, BUTT_CLOSE_BUY_BY_SELL, BUTT_SELL, BUTT_SELL_LIMIT, BUTT_SELL_STOP, BUTT_SELL_STOP_LIMIT, BUTT_CLOSE_SELL, BUTT_CLOSE_SELL2, BUTT_CLOSE_SELL_BY_BUY, BUTT_DELETE_PENDING, BUTT_CLOSE_ALL, BUTT_SET_STOP_LOSS, BUTT_SET_TAKE_PROFIT, BUTT_PROFIT_WITHDRAWAL, BUTT_TRAILING_ALL }; #define TOTAL_BUTT (20) #define MA1 (1) #define MA2 (2) #define AMA1 (3) #define AMA2 (4) //--- structures struct SDataButt { string name; string text; }; //--- input variables input ushort InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 150; // StopLoss in points input uint InpTakeProfit = 150; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpDistancePReq = 50; // Distance for Pending Request's activate (points) input uint InpBarsDelayPReq = 5; // Bars delay for Pending Request's activate (current timeframe) input uint InpSlippage = 5; // Slippage in points input uint InpSpreadMultiplier = 1; // Spread multiplier for adjusting stop-orders by StopLevel input uchar InpTotalAttempts = 5; // Number of trading attempts sinput double InpWithdrawal = 10; // Withdrawal funds (in tester) sinput uint InpButtShiftX = 0; // Buttons X shift sinput uint InpButtShiftY = 10; // Buttons Y shift input uint InpTrailingStop = 50; // Trailing Stop (points) input uint InpTrailingStep = 20; // Trailing Step (points) input uint InpTrailingStart = 0; // Trailing Start (points) input uint InpStopLossModify = 20; // StopLoss for modification (points) input uint InpTakeProfitModify = 60; // TakeProfit for modification (points) sinput ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; // Mode of used symbols list sinput string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; // List of used symbols (comma - separator) sinput ENUM_TIMEFRAMES_MODE InpModeUsedTFs = TIMEFRAMES_MODE_LIST; // Mode of used timeframes list sinput string InpUsedTFs = "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; // List of used timeframes (comma - separator) sinput bool InpUseSounds = true; // Use sounds //--- global variables CEngine engine; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ushort magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint distance_pending_request; uint bars_delay_pending_request; uint slippage; bool trailing_on; bool pressed_pending_buy; bool pressed_pending_buy_limit; bool pressed_pending_buy_stop; bool pressed_pending_buy_stoplimit; bool pressed_pending_close_buy; bool pressed_pending_close_buy2; bool pressed_pending_close_buy_by_sell; bool pressed_pending_sell; bool pressed_pending_sell_limit; bool pressed_pending_sell_stop; bool pressed_pending_sell_stoplimit; bool pressed_pending_close_sell; bool pressed_pending_close_sell2; bool pressed_pending_close_sell_by_buy; bool pressed_pending_delete_all; bool pressed_pending_close_all; bool pressed_pending_sl; bool pressed_pending_tp; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; int used_symbols_mode; string array_used_symbols[]; string array_used_periods[]; bool testing; uchar group1; uchar group2; double g_point; int g_digits; //--- Arrays of custom indicator parameters MqlParam param_ma1[]; MqlParam param_ma2[]; //+------------------------------------------------------------------+
実質的に、宣言されたマクロ置換は、指標の数値の説明です。指標IDを名前で呼び出す方が、値で呼び出すよりも簡単です。
EAのOnInit()ハンドラで、4つの指標すべてを作成し、作成されたすべての指標の操作ログデータに一度に表示します。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Calling the function displays the list of enumeration constants in the journal, //--- (the list is set in strings 22 and 25 of DELib.mqh file) for checking the constants validity //EnumNumbersTest(); //--- Set EA global variables prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_"; testing=engine.IsTester(); for(int i=0;i<TOTAL_BUTT;i++) { butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i); butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i); } lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0)); magic_number=InpMagic; stoploss=InpStopLoss; takeprofit=InpTakeProfit; distance_pending=InpDistance; distance_stoplimit=InpDistanceSL; slippage=InpSlippage; trailing_stop=InpTrailingStop*Point(); trailing_step=InpTrailingStep*Point(); trailing_start=InpTrailingStart; stoploss_to_modify=InpStopLossModify; takeprofit_to_modify=InpTakeProfitModify; distance_pending_request=(InpDistancePReq<5 ? 5 : InpDistancePReq); bars_delay_pending_request=(InpBarsDelayPReq<1 ? 1 : InpBarsDelayPReq); g_point=SymbolInfoDouble(NULL,SYMBOL_POINT); g_digits=(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS); //--- Initialize random group numbers group1=0; group2=0; srand(GetTickCount()); //--- Initialize DoEasy library OnInitDoEasy(); //--- Create indicators ArrayResize(param_ma1,4); //--- Name of indicator 1 param_ma1[0].type=TYPE_STRING; param_ma1[0].string_value="Examples\\Custom Moving Average.ex5"; //--- Calculation period param_ma1[1].type=TYPE_INT; param_ma1[1].integer_value=13; //--- Horizontal shift param_ma1[2].type=TYPE_INT; param_ma1[2].integer_value=0; //--- Smoothing method param_ma1[3].type=TYPE_INT; param_ma1[3].integer_value=MODE_SMA; //--- Create indicator 1 engine.GetIndicatorsCollection().CreateCustom(NULL,PERIOD_CURRENT,MA1,INDICATOR_GROUP_TREND,param_ma1); ArrayResize(param_ma2,5); //--- Name of indicator 2 param_ma2[0].type=TYPE_STRING; param_ma2[0].string_value="Examples\\Custom Moving Average.ex5"; //--- Calculation period param_ma2[1].type=TYPE_INT; param_ma2[1].integer_value=13; //--- Horizontal shift param_ma2[2].type=TYPE_INT; param_ma2[2].integer_value=0; //--- Smoothing method param_ma2[3].type=TYPE_INT; param_ma2[3].integer_value=MODE_SMA; //--- Calculation price param_ma2[4].type=TYPE_INT; param_ma2[4].integer_value=PRICE_OPEN; //--- Create indicator 2 engine.GetIndicatorsCollection().CreateCustom(NULL,PERIOD_CURRENT,MA2,INDICATOR_GROUP_TREND,param_ma2); //--- Create indicator 3 engine.GetIndicatorsCollection().CreateAMA(NULL,PERIOD_CURRENT,AMA1); //--- Create indicator 4 engine.GetIndicatorsCollection().CreateAMA(NULL,PERIOD_CURRENT,AMA2,14); //--- Display descriptions of created indicators engine.GetIndicatorsCollection().Print(); engine.GetIndicatorsCollection().PrintShort(); //--- Check and remove remaining EA graphical objects if(IsPresentObectByPrefix(prefix)) ObjectsDeleteAll(0,prefix); //--- Create the button panel if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- Set trailing activation button status ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on); //--- Reset states of the buttons for working using pending requests for(int i=0;i<14;i++) { ButtonState(butt_data[i].name+"_PRICE",false); ButtonState(butt_data[i].name+"_TIME",false); } //--- Check playing a standard sound by macro substitution and a custom sound by description engine.PlaySoundByDescription(SND_OK); //--- Wait for 600 milliseconds engine.Pause(600); engine.PlaySoundByDescription(TextByLanguage("The sound of a falling coin 2")); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
2番目のカスタム指標MAの計算価格PRICE_OPENを設定します。計算価格が明確に指定されていない場合、デフォルトで(MAの最初の指標で)PRICE_CLOSE価格が指標の計算に使用されます。
AMA指標を作成すると、最初の指標は計算期間を9(デフォルトで設定)として受け取ります。2番目のものは明示的に14の値を受け取ります。
したがって、作成された4つの指標はすべて、パラメータの異なる入力値を持っています。
EAのOnDeinit()ハンドラでライブラリのOnDeinit()ハンドラーを呼び出します。
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Remove EA graphical objects by an object name prefix ObjectsDeleteAll(0,prefix); Comment(""); //--- Deinitialize library engine.OnDeinit(); } //+------------------------------------------------------------------+
時間枠を切り替えるときにEAの初期化解除中に指標のコレクションリストが消去されます。これにより、不要な追加の指標オブジェクトを作成する必要がなくなります。
EAのOnTick()ハンドラで、作成された各指標オブジェクトへのアクセスを取得し、チャートのコメントに表示された各指標の現在のバーのデータを表示します。
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Handle the NewTick event in the library engine.OnTick(rates_data); //--- If work in tester if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(rates_data); // Work in timer PressButtonsControl(); // Button press control engine.EventsHandling(); // Work with events } //--- Get custom indicator objects CIndicatorDE *ma1=engine.GetIndicatorsCollection().GetIndByID(MA1); CIndicatorDE *ma2=engine.GetIndicatorsCollection().GetIndByID(MA2); CIndicatorDE *ama1=engine.GetIndicatorsCollection().GetIndByID(AMA1); CIndicatorDE *ama2=engine.GetIndicatorsCollection().GetIndByID(AMA2); Comment ( "ma1=",DoubleToString(ma1.GetDataBuffer(0,0),6), ", ma2=",DoubleToString(ma2.GetDataBuffer(0,0),6), "\nama1=",DoubleToString(ama1.GetDataBuffer(0,0),6), ", ama2=",DoubleToString(ama2.GetDataBuffer(0,0),6) ); //--- If the trailing flag is set if(trailing_on) { TrailingPositions(); // Trailing positions TrailingOrders(); // Trailing of pending orders } } //+------------------------------------------------------------------+
前回の記事では、ライブラリのOnInitDoEasy()初期化関数で指標オブジェクトを一時的に作成しました。これらの文字列を関数から削除します。
//--- Create timeseries of all symbols used engine.SeriesCreateAll(array_used_periods); //--- Check created timeseries - display descriptions of all created timeseries in the journal //--- (true - only created ones, false - created and declared ones) engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions //engine.GetTimeSeriesCollection().Print(true); // Full descriptions //--- Create indicators engine.GetIndicatorsCollection().CreateAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE); engine.GetIndicatorsCollection().CreateAMA(Symbol(),Period(),10,3,32,5,PRICE_CLOSE); engine.GetIndicatorsCollection().Print(); engine.GetIndicatorsCollection().PrintShort(); //--- Create resource text files
EAをコンパイルし、現在の銘柄と時間枠のみを使用するように設定が事前に設定されているチャートで起動します。操作ログには、作成されたすべての指標のパラメータの説明が表示されます。
--- Initialize "DoEasy" library --- Work with the current symbol only: "EURUSD" Work with the current timeframe only: H1 EURUSD symbol timeseries: - "EURUSD" H1 timeseries: Requested: 1000, Actually: 1000, Created: 1000, On the server: 6284 Library initialize time: 00:00:00.141 ============= Parameter list start: “Custom indicator" ============= Indicator status: Custom indicator Indicator type: CUSTOM Indicator timeframe: H1 Indicator handle: 10 Indicator group: Trend indicator Indicator ID: 1 ------ Empty value for plotting where nothing will be drawn: EMPTY_VALUE ------ Indicator symbol: EURUSD Indicator name: "Examples\Custom Moving Average.ex5" Indicator short name: "Examples\Custom Moving Average.ex5(EURUSD,H1)" --- Indicator parameters --- - [1] Type int: 13 - [2] Type int: 0 - [3] Type int: 0 ================== Parameter list end: "Custom indicator" ================== ============= Parameter list start: “Custom indicator" ============= Indicator status: Custom indicator Indicator type: CUSTOM Indicator timeframe: H1 Indicator handle: 11 Indicator group: Trend indicator Indicator ID: 2 ------ Empty value for plotting where nothing will be drawn: EMPTY_VALUE ------ Indicator symbol: EURUSD Indicator name: "Examples\Custom Moving Average.ex5" Indicator short name: "Examples\Custom Moving Average.ex5(EURUSD,H1)" --- Indicator parameters --- - [1] Type int: 13 - [2] Type int: 0 - [3] Type int: 0 - [4] Type int: 2 ================== Parameter list end: "Custom indicator" ================== ============= Parameter list start: "Standard indicator" ============= Indicator status: Standard indicator Indicator type: AMA Indicator timeframe: H1 Indicator handle: 12 Indicator group: Trend indicator Indicator ID: 3 ------ Empty value for plotting where nothing will be drawn: EMPTY_VALUE ------ Indicator symbol: EURUSD Indicator name: "Adaptive Moving Average" Indicator short name: "AMA(EURUSD,H1)" --- Indicator parameters --- - Averaging period: 9 - Fast MA period: 2 - Slow MA period: 30 - Horizontal shift of the indicator: 0 - Price type or handle: CLOSE ================== Parameter list end: "Standard indicator" ================== ============= Parameter list start: "Standard indicator" ============= Indicator status: Standard indicator Indicator type: AMA Indicator timeframe: H1 Indicator handle: 13 Indicator group: Trend indicator Indicator ID: 4 ------ Empty value for plotting where nothing will be drawn: EMPTY_VALUE ------ Indicator symbol: EURUSD Indicator name: "Adaptive Moving Average" Indicator short name: "AMA(EURUSD,H1)" --- Indicator parameters --- - Averaging period: 14 - Fast MA period: 2 - Slow MA period: 30 - Horizontal shift of the indicator: 0 - Price type or handle: CLOSE ================== Parameter list end: "Standard indicator" ================== Custom indicator Examples\Custom Moving Average.ex5 EURUSD H1 [handle 10, id #1] Custom indicator Examples\Custom Moving Average.ex5 EURUSD H1 [handle 11, id #2] Standard indicator Adaptive Moving Average EURUSD H1 [handle 12, id #3] Standard indicator Adaptive Moving Average EURUSD H1 [handle 13, id #4]
銘柄チャートには、作成されたすべての指標のバッファからのデータが表示されます。
チャートには、EAで作成されたものにパラメータで対応する必要な指標が追加される場合があります。チャートのコメントとデータウィンドウでこれらの指標の一致を確認できます。それらは一致します。
次の段階
次の記事では、EAの指標を処理する機能を引き続き作成します。次のいくつかの記事では、時系列クラスバーへの指標データのバインドを作成し、さまざまな統計調査のために指標からデータを取得する予定です。
現在のライブラリバージョンのすべてのファイルは、MQL5のテストEAファイルと一緒に以下に添付されています。ダウンロードし、すべてを検証することが可能です。
指標コレクションクラスは現在開発中であるため、プログラムは絶対使用しないでください。
質問や提案は記事のコメント欄にお願いします。
シリーズのこれまでの記事:
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部): 複数銘柄・複数期間の複合標準指標
DoEasyライブラリの時系列(第52部): 複数銘柄・複数期間の単一バッファ標準指標のクロスプラットフォーム化
DoEasyライブラリの時系列(第53部): 抽象基本指標クラス
DoEasyライブラリの時系列(第54部): 抽象基本指標の子孫クラス
DoEasyライブラリの時系列(第55部): 指標コレクションクラス