GUIによる汎用的なオシレーター

Dmitry Fedoseev | 26 1月, 2017


コンテンツ

イントロダクション

トレードシステムのインジケータは通常、グラフ上のさまざまなパラメータの徹底した監視後、選択されます。「ナビゲーター」ウィンドウからドラッグして、そのパラメータを変更し、インジケータのプロパティ ウィンドウを開く方法はあまりにも多くの時間がかかるでしょう。このプロセスを高速化するやり方があります。

インジケータのパラメータをシンプルなものに変更し、新しい結果を直ちに表示することができるように、グラフから直接アクセス可能なグラフィカルインターフェイスの作成をします。インジケータの高速スイッチは、グラフィカル インターフェイスを持つ 1つの一般的なインジケータに、様々 なインジケータを組み合わせることで実装できます。

問題の分析

汎用的なインジケータを作成するタスクは、特に難しいものではありません。オブジェクト指向プログラミングを少し必要とします。つまり、基本クラスと同じ型の子クラスがたくさん存在します。

各特定のインジケータのパラメータは、子クラスのコンストラクタに渡されます。この場合、オブジェクトを作成するときに、MetaEditorは開発プロセス (図 1) を大幅に容易にするパラメータ リストのプロンプトを開きます。

 
図1。コンストラクタのパラメータ、オブジェクトの作成時のヒント

主な困難の要因は、そのようなインジケータの活用に際して生じるでしょう。異なるオシレーターは、外部パラメータのセットにあります。各オシレーターと異なるプレフィックスの別のパラメータを用意したため、 手動でインジケータを使用することができますが、 iCustom()またはIndicatorCreate()関数のパラメータの数が多いために適していません。渡されたパラメータの数は IndicatorCreate() の 256 と iCustom() の 64 です。この値は、使用可能なパラメータの実際の数よりも小さいので、シンボルや表示名などの一般的なパラメータがあります。また一連のパラメータを使用できますが、インジケータは次のケースで使用すると便利になります: 特定のインジケータでどのパラメータを使用するか、確認する必要があります。

グラフィカルインターフェイスでは、この問題を解決することができます: ダイアログ ボックスは選択されたインジケータに対応する特定の関数を制御することができます。インジケータ プロパティ ウィンドウしたがって iCustom() または IndicatorCreate() を使用して、外部パラメータの小さいユニバーサル セットする必要があります。

パラメータのセット

外部パラメータの最小セットを定義。ターミナルでオシレーターのリストをチェック: メイン メニュー - 挿入 - インジケータ - オシレーター、テーブルに追加します。

表1。ターミナルのすべてのオシレーター

関数名前バッファパラメータ
iATR平均トゥルーレンジ1. ライン1. int ma_period-平均期間
iBearsPowerベアパワー1. ヒストグラム1. int ma_period-平均期間
iBullsPowerブルパワー 1. ライン1. int ma_period-平均期間
iCCIコモディティ チャネル インデックス1. ライン1. int ma_period-平均期間
2. ENUM_APPLIED_PRICE applied_price-価格タイプ
iChaikinチャイキン オシレーター1. ライン1. int fast_ma_period-ファスト期間
2. int slow_ma_period-スロー期間
3. ENUM_MA_METHOD ma_method-スムージングのタイプ
4. ENUM_APPLIED_VOLUME applied_volume-使用されるボリューム 
iDeMarkerDeMarker1. ライン1. int ma_period-平均期間
iForce勢力指数1. ライン1. int ma_period-平均期間
2. ENUM_MA_METHOD ma_method-スムージングのタイプ
3. ENUM_APPLIED_VOLUME applied_volume-の計算に使用されるボリュームのタイプ 
iMomentumモメンタム1. ライン1. int mom_period-平均期間
2. ENUM_APPLIED_PRICE applied_price-タイプの価格
iMACDMACD1. histogram
2. ライン
1. int fast_ema_period-高速 MA 期間
2. int slow_ema_period-スローMA 期間
3. int signal_period-平均期間の差
4. ENUM_APPLIED_PRICE applied_price-価格タイプ
iOsMA移動平均のオシレーター (MACD のヒストグラム)1. ヒストグラム1. int fast_ema_period-高速 MA 期間
2. int slow_ema_period-スローMA 期間
3. int signal_period-平均期間の差
4. ENUM_APPLIED_PRICE applied_price-価格タイプ
iRSI相対力指数1. ライン1. int ma_period-平均期間
2. ENUM_APPLIED_PRICE applied_price-タイプの価格
iRVI相対活性度指数1. line
2. ライン
1. int ma_period-平均期間
iStochasticストキャスティクスオシレーター1. line
2. ライン
1. int Kperiod-計算のバーの数
2. nt Dperiod-プライマリ期間を平滑化
3. nt 減速-最終的な平滑化
4. ENUM_MA_METHOD ma_method-スムージングのタイプ
5. ENUM_STO_PRICE price_field-ストキャスティクスの計算法 
iTriXトリプル指数移動平均オシレーター1. ライン1. int ma_period-平均期間
2. ENUM_APPLIED_PRICE applied_price-タイプの価格
iWPRウィリアムズの % の範囲1. ライン1. int calc_period-平均期間

パラメータ列に基づいて、パラメータのすべての型の一覧を作成し、最大数を決定します。

表2。型およびパラメータの数 

TypeNumber
int3
ENUM_APPLIED_PRICE1
ENUM_MA_METHOD1
ENUM_APPLIED_VOLUME1
ENUM_STO_PRICE1
2 つのインジケータバッファーを使用することができると、さまざまなインジケータが図面のさまざまなタイプを持つことができ、バッファーの列から見ることができます。もちろん、線としてそれらのすべてを描くことが、通常、ヒストグラムとして表示され、ターミナルの容量を可能にするので、インジケータのタイプが変更されたとき、対応する図面のタイプの変更しましょう。また、 (RSI、CCI) インジケータを追加する必要があるため、水平レベルの図面を提供するために必要です。

計画

小さな独立したタスクは、共通タスクに分けることができます。数が大きいほどより便利になります。したがって、 タスクは3つのステージで構成されます。

  1. 汎用的なオシレーターおよび GUI を使用しないオシレーターを作成するためのクラスを作成します。
  2. GUIのクラスを作成します。
  3. 汎用的なオシレーター、グラフィカルインターフェイスを組み合わせ。 

注意を払うべき重要なポイントは、既定の設定です。両方のグラフィカルインターフェイスを使用して、プロパティ ウィンドウからインジケータのパラメータを設定します。(汎用的なインジケータの最大限の汎用性)汎用的なパラメータの小さいセットが含まれている[プロパティ] ウィンドウでパラメータを構成する場合、すべてのパラメータの既定の設定が一般的なインジケータを提供する必要があります。

既定値の異なるオシレーターを検討してみましょう。たとえば、MACD(5, 3, 3) (最初のパラメータが 2 つ目よりも大きい)、及びMACD(12、26、9)(最初のパラメータが 2 番目のものより小さい) です。MACD の最初のパラメータは、短期移動平均、2 つ目は長期移動平均を表します。そのため、最初のパラメータは必ず2番目よりも小さくなくてはなりません。このパラメータの比率は、チャイキン オシレーター(で使用する長期と短期の MA期間) に適しています。この比率は確率に重要ではない、任意の値を持つ価格の動きに対応します。MACDで、最初のパラメータが2 つ目よりも大きい場合、インジケータは価格と逆の動きをします。

グラフィカルインターフェースを使うときには、インジケータは一般的なパラメータをデフォルトに使う必要があります。MACDでいえば、(12、26、9)、(5、3、3)などです。さらに、既定のパラメータを持つ、または以前のインジケータの同じパラメータで操作を開始する新しい選択されたインジケータを有効にすることをお勧めします。たとえば、RSIとCCIを分析するとき、同じパラメータを使って異なる線の変化を見たいと思うでしょう。クラスを実装する場合、このことを考慮する必要があります。

インジケータの基本クラスを作成します。

インクルード フォルダーの 'UniOsc' の新しいフォルダーを作成します。すべての追加のインジケータのファイルは、この新しいフォルダーに配置されます。使用されるオシレーターのセットは、表 1 に定義されます。対応する列挙型の選択を作成してみましょう。インジケータファイルに加えて、列挙型を使用する必要があります、したがって別のファイル (フォルダー 'UniOsc') の UniOscDefines.mqh に追加されます。 

enum EOscUnyType{
   OscUni_ATR,
   OscUni_BearsPower,
   OscUni_BullsPower,  
   OscUni_CCI,
   OscUni_Chaikin,
   OscUni_DeMarker,
   OscUni_Force,
   OscUni_Momentum,
   OscUni_MACD,
   OscUni_OsMA,
   OscUni_RSI,
   OscUni_RVI,
   OscUni_Stochastic,
   OscUni_TriX,
   OscUni_WPR
};

何もこのファイルに追加されません。

インジケータファイルの"CUniOsc.mqh"を作成して COscUni クラス テンプレートを書き込みます。

class COscUni{
   protected:
    
   public:

};

保護' セクションは、クラス メンバーの一部を保護する必要がありますが、子クラスにアクセスする必要がありますので、テンプレートで決定されます。('private' セクションのメンバーは保護されますが、子クラスに利用できない)

インジケータの OnCalculate() 関数に対応するメソッド、基本クラスの main メソッドは、Calculate() です。メソッドの最初の 2 つのパラメータは、OnCalculate() の適切なパラメータに対応しています。つまり、 rates_total (バーの合計数) と prew_calculate (計算されたバーの数)。別のインジケータのデータが使用されるため、Calculate() メソッドにデータの配列を渡す必要はありません。しかし、データを格納する 2 つのインジケータバッファーを渡す必要があります。インジケータを使用すると、Calculate() メソッドに渡されるのでいずれの場合も、2 つのインジケータを制御する必要があります。どちらか一つのインジケータバッファーは、Calculate() のコードが使用されるオシレーターのタイプによって異なります: よって、Calculate() メソッドが仮想になります。

virtual int Calculate( const int rates_total,
               const int prev_calculated,
               double & buffer0[],
               double & buffer1[]
){
   return(rates_total);
}

さまざまなインジケータを読み込んだとき、インジケータ ハンドルを格納するため、変数が必要です。保護セクションで宣言します。

さらに、さまざまなバッファー表示プロパティのより多くの変数が必要です。プロパティが決定される それぞれの個々のインジケータを読み込むとき、すなわち変数の値は子クラスで設定されます。

int m_handle;           //インジケータ ハンドル
int m_bufferscnt;       //使用されるバッファーの数
string m_name;          //インジケータの名前
string m_label1;        //バッファー 1 の名前
string m_label2;        //バッファー2の名前
int m_drawtype1;        //描画バッファー 1 のタイプ
int m_drawtype2;        //描画バッファー 2 タイプ
string m_help;          //インジケータのパラメータに関するヒント
int m_digits;           //インジケータ値の小数点以下の桁数の数
int m_levels_total;     //レベルの数
double m_level_value[]; //レベルの値を配列

インジケータ ハンドルをチェックし、適切なメソッドが要るので、インジケータが正常に読み込まれているかどうかをチェックする必要があります。

bool CheckHandle(){
   return(m_handle!=INVALID_HANDLE);
}

グラフィカルインターフェイスを介してオシレーターを変更する場合、インジケータの計算は完全かどうか決定する必要があります。これを行うことができる呼び出しは、インジケータ ハンドルを必要とする、 BarsCalculated()関数です。よって、ハンドルを取得するメソッドを追加します。  

intHandle() {
    return(m_handle);
}

クラスのコンストラクタには、デストラクターをチェックし、必要な場合は、 IndicatorRelease()を呼び出し、ハンドルを初期化する必要があります。

void COscUni(){
   m_handle=INVALID_HANDLE;
}

void 〜 COscUni() {
   if(m_handle!=INVALID_HANDLE){
      IndicatorRelease(m_handle);
   }
} 

様々なインジケータの表示を決定する変数の残りの部分へのアクセスを提供し、その値を受信するためのメソッドを作成してみましょう。

string Name(){ //オシレーターの名前
   return(m_name);
}    
  
int BuffersCount(){ //バッファー数
   return(m_bufferscnt);
}

string Label1(){ //最初のバッファーの名前
   return(m_label1);
}

string Label2(){ //2番目のバッファーの名前
   return(m_label2);
}

int DrawType1(){ //最初のバッファーのタイプ
   return(m_drawtype1);
}

int DrawType2(){ //2 番目のバッファーの図面のタイプ
   return(m_drawtype2);
}  

string Help(){ //パラメータの使用に関するヒント
   return(m_help);
}

int Digits(){ //インジケータ値の小数点以下の桁数の数
   return(m_digits);
}

int LevelsTotal(){ //インジケータ レベルの数
   return(m_levels_total);
}

double LevelValue(int index){ //指定したインデックスのレベルの値を取得
   return(m_level_value[index]);
}

すべてのメソッドは、変数に対応する値を返し、オシレーターの子クラスで変数の値を代入します。  

計算' の子クラス

2つの子クラスを作成: 1 つのバッファーのインジケータとインジケータ 2 つのバッファー。1つのバッファーを持つインジケータ。

class COscUni_Calculate1:public COscUni{
   public:
      void COscUni_Calculate1(){
         m_bufferscnt=1;
      }
      virtual int Calculate( const int rates_total,
                     const int prev_calculated,
                     double & buffer0[],
                     double & buffer1[]
      ){
        
         int cnt,start;
        
         if(prev_calculated==0){
            cnt=rates_total;
            start=0;
         }
         else{
            cnt=rates_total-prev_calculated+1;
            start=prev_calculated-1;
         }  
        
         if(CopyBuffer(m_handle,0,0,cnt,buffer0)<=0){
            return(0);
         }
        
         for(int i=start;i<rates_total;i++){
            buffer1[i]=EMPTY_VALUE;
         }        
        
         return(rates_total);
      }
};

このクラスを考えてみましょう。このクラスには、COscUni_Calculate1 コンストラクタ、(この場合は 1) のバッファーの数が、コンストラクタで設定します。コピー (変数 'cnt') ・ バーのインデックス バッファーの要素の数を開始し、2番目をクリアする必要があるバッファー (変数 '開始')、rates_total と prev_calculate の値に応じて、Calculate() メソッドで計算されます。データのコピーが失敗した場合、(CopyBuffer()) 0 を呼び出すことは、次のティックで非常に最初からすべての計算を実行するために、メソッドから返されます。メソッドの終わりには、rates_total が返されます。

2 つのバッファーのインジケータの子クラス:

class COscUni_Calculate2:public COscUni{
   public:
      void COscUni_Calculate2(){
         m_bufferscnt=2;
      }  
      virtual int Calculate( const int rates_total,
                     const int prev_calculated,
                     double & buffer0[],
                     double & buffer1[]
      ){
         int cnt;
         if(prev_calculated==0){
            cnt=rates_total;
         }
         else{
            cnt=rates_total-prev_calculated+1;
         }          
         if(CopyBuffer(m_handle,0,0,cnt,buffer0)<=0){
            return(0);
         }
         if(CopyBuffer(m_handle,1,0,cnt,buffer1)<=0){
            return(0);

         return(rates_total);
      }
};

このクラスは、単一バッファーのインジケータよりも簡単です。Calculate() メソッドの先頭に (変数「cnt」) をコピーする要素の数を計算しバッファーにコピーされます。 

インジケータの子クラス

今の子のオシレーターのクラスを作成します。COscUni_Calculate1 または COscUni_Calculate2 の子クラスになります。すべてのクラスに1つのコンストラクタがあります。適切なオシレーターと追加のパラメータに対応するパラメータ、各クラスのコンストラクタに渡されます。追加のパラメータは、コンストラクタに渡されるパラメータの値を使用するか、既定値 ('use_default' 変数) を設定するかを決定します。2番目のパラメータ keep_previous は、すべてのインジケータのパラメータ、またはまだ使用されていないものに既定値に設定するかどうかを決定します。

リストの最初のインジケータATRの子クラスを書いてみましょう。最初は、クラス テンプレートです。

class COscUni_ATR:public COscUni_Calculate1{
   public:
   void COscUni_ATR(bool use_default,bool keep_previous,int & ma_period){

   }
};

インジケータに既定のパラメータを設定するときに、汎用的なオシレーターのパラメータ値へのアクセスを提供するために、ma_period パラメータは参照によって渡されることに注意してください。

コンストラクタのコードの記述:

if(use_default){
   if(keep_previous){
      if(ma_period==-1)ma_period=14;
   }
   else{
      ma_period=14;
   }      
}  

use_default = true の場合、既定値は、コードのこの部分でされています。keep_previous = true の場合、結果のパラメータが-1 かどうかセットします。したがって、汎用的なオシレーターの初期化中に、すべてのパラメータに-1 の値を割り当てる必要があります。

インジケータのロードが含まれている子のクラス コンストラクタのコードの最も重要な行を分析します。

m_handle=iATR(Symbol(),Period(),ma_period);

表示パラメータを設定する行。

m_name=StringFormat("ATR(%i)",ma_period); //インジケータの名前
m_label1="ATR"; //バッファー名
m_drawtype1=DRAW_LINE;  //図面のタイプ
m_help=StringFormat("ma_period - Period1(%i)",ma_period); //ヒント
m_digits=_Digits+1; //インジケータ値の小数点以下の桁数の数
m_levels_total=0; //レベルの数

複雑なインジケータ MACD の子クラスの作成の手順のいくつか分析してみましょう。この場合、作成の原則は同じです。したがって、フラグメントを考えてみましょう。既定のパラメータを設定します。

if(use_default){
   if(keep_previous){
      if(fast_ema_period==-1)fast_ema_period=12;
      if(slow_ema_period==-1)slow_ema_period=26;
      if(signal_period==-1)signal_period=9;
      if(applied_price==-1)applied_price=PRICE_CLOSE;            
   }
   else{
      fast_ema_period=12;
      slow_ema_period=26;
      signal_period=9;
      applied_price=PRICE_CLOSE;
   }      
}

表示パラメータの設定。

m_handle=iMACD(Symbol(),
               Period(),
               fast_ema_period,
               slow_ema_period,
               signal_period,
               (ENUM_APPLIED_PRICE)applied_price);

m_name=StringFormat( "iMACD(%i,%i,%i,%s)",
                     fast_ema_period,
                     slow_ema_period,
                     signal_period,
                     EnumToString((ENUM_APPLIED_PRICE)applied_price));
                    
m_label1="Main";
m_label2="Signal";      
m_drawtype1=DRAW_HISTOGRAM;            
m_drawtype2=DRAW_LINE;

m_help=StringFormat( "fast_ema_period - Period1(%i), "+
                     "slow_ema_period - Period2(%i), "+
                     "signal_period - Period3(%i), "+
                     "applied_price - Price(%s)",
                     fast_ema_period,
                     slow_ema_period,
                     signal_period,
                     EnumToString((ENUM_APPLIED_PRICE)applied_price));  
                    
m_digits=_Digits+1;

コンストラクタのパラメータ:

void COscUni_MACD(bool use_default,
                  bool keep_previous,
                  int & fast_ema_period,
                  int & slow_ema_period,
                  int & signal_period,
                  long & applied_price
){

標準の ENUM_APPLIED_PRICE 列挙体の applied_price 変数が宣言されることに注意してください。パラメータが使用されていないことを示すために、変数を-1 に設定します。

RSIインジケータのクラスから別のフラグメントを見てみましょう。レベルが設定されているコードの一部があります。 

m_levels_total=3;
ArrayResize(m_level_value,3);
m_level_value[0]=30;
m_level_value[1]=50;
m_level_value[2]=70;

配列のサイズを変更、レベルの値を持つ塗りつぶしのレベル数を設定します。

ここでオシレーターの他のクラスを作成するメソッド説明します。この記事の添付ファイルには、オシレーター (CUniOsc.mqh ファイル) の完全なセットを持つクラスがあります。

汎用的なオシレーター (先頭) を作成します。

オシレータークラスの準備ができ、 グラフィカルなオシレーターなしで、汎用的なオシレーターを作成できます。

新しいインジケータ、例えば"iUniOsc"を作成します。インジケータの作成ウィザードでOnCalculate(...open,high,low,close)を選択し、1 つの外部変数 (外部変数の場所を見つけること容易になります) 及び2つのバッファーを作成します。

外部変数の前には、列挙体とオシレータークラスのファイルを接続する必要があります。 

#include <UniOsc/UniOscDefines.mqh>
#include <UniOsc/CUniOsc.mqh>

オシレーターのタイプを選択するための外部変数を作成します。

input EOscUnyType          Type        =  OscUni_ATR;

UseDefault と KeepPrevious:

input bool                 UseDefault  =  true;
input bool                 KeepPrev    =  true;

オシレーターのパラメータの汎用的な変数:

input int                  Period1     =  14;
input int                  Period2     =  14;
input int                  Period3     =  14;
input ENUM_MA_METHOD       MaMethod    =  MODE_EMA;
input ENUM_APPLIED_PRICE   Price       =  PRICE_CLOSE;  
input ENUM_APPLIED_VOLUME  Volume      =  VOLUME_TICK;  
input ENUM_STO_PRICE       StPrice     =  STO_LOWHIGH;

インジケータは、1 つの線を引きますが、2つ引くものもあります。最初のバッファーは、ラインとして表示されることもあれば、ヒストグラムとして描画されることもあります。ラインを明るくし、ヒストグラムは灰色にし、3つの色を作ります。

input color                ColorLine1  =  clrLightSeaGreen;
input color                ColorLine2  =  clrRed;
input color                ColorHisto  =  clrGray;

GUI を作成するので、インジケータを再起動することなくオシレーターとパラメータを変更できるようにする必要があります。つまり、タイプと変数をコピーします。 

int                  _Period1;
int                  _Period2;
int                  _Period3;
long                 _MaMethod;
long                 _Price;  
long                 _Volume;  
long                 _StPrice;
EOscUnyType          _Type;

汎用的なオシレーターオブジェクトのポインター変数を宣言します。

COscUni * osc;

より多くの変数を宣言する:

string ProgName;
string ShortName;

これらの変数は、サブ ウィンドウの左上隅に表示されるインジケータの名前の生成に使用されます。 

OnInit() 関数の最後にコードを追加します。UseDefault と KeepPrevious (と _type データ変数)の値に従ってオシレーターパラメータ に値を割り当て、関数として書きます。

void PrepareParameters(){

   _Type=Type;

   if(UseDefault && KeepPrev){
      _Period1=-1;
      _Period2=-1;
      _Period3=-1;
      _MaMethod=-1;
      _Volume=-1;
      _Price=-1;  
      _StPrice=-1;
   }
   else{  
      _Period1=Period1;
      _Period2=Period2;
      _Period3=Period3;
      _MaMethod=MaMethod;
      _Volume=Volume;
      _Price=Price;  
      _StPrice=StPrice;
   }
}

UseDefault と KeepPrevious を使用する場合、-1 の値は、 使用していない変数クラスのコンストラクタを参照してください。プロパティ ウィンドウの値は他のすべてのケースで割り当てられます。同様に、オブジェクトが作成されるときに既定値で置換されます。  

パラメータを準備した後、選択したオシレーターをロードします。読み込み用のコードが関数として書き込まれます。

void LoadOscillator(){
   switch(_Type){
      case OscUni_ATR:
         osc=new COscUni_ATR(UseDefault,KeepPrev,_Period1);
      break;
      case OscUni_BearsPower:
         osc=new COscUni_BearsPower(UseDefault,KeepPrev,_Period1);
      break;
      case OscUni_BullsPower:
         osc=new COscUni_BullsPower(UseDefault,KeepPrev,_Period1);
      break;      
      ...
   }  
}

オシレーターをロードした後に、ハンドルを確認する必要があります。

if(!osc.CheckHandle()){
   Alert("indicator loading error "+osc.Name());
   return(INIT_FAILED);
}

正常に読み込まれた場合は、適切なオブジェクトのメソッドを介して受信した描画スタイルを設定します。このコードは、関数としても実装されます。

void SetStyles(){  

   //スタイルの設定
   if(osc.BuffersCount()==2){
      PlotIndexSetInteger(0,PLOT_DRAW_TYPE,osc.DrawType1());
      PlotIndexSetInteger(1,PLOT_DRAW_TYPE,osc.DrawType2());
PlotIndexSetInteger(0PLOT_SHOW_DATA);
PlotIndexSetInteger(1,PLOT_SHOW_DATA,true);
PlotIndexSetString(0PLOT_LABELosc。Label1());
PlotIndexSetString(1PLOT_LABELosc。Label2());
      if(osc.DrawType1()==DRAW_HISTOGRAM){
PlotIndexSetInteger(0PLOT_LINE_COLORColorHisto);
      }
      else{
PlotIndexSetInteger(0PLOT_LINE_COLORColorLine1)。
      }
      PlotIndexSetInteger(1,PLOT_LINE_COLOR,ColorLine2);  
   }
   else{
      PlotIndexSetInteger(0,PLOT_DRAW_TYPE,osc.DrawType1());
PlotIndexSetInteger(1PLOT_DRAW_TYPEDRAW_NONE)。
      PlotIndexSetInteger(0,PLOT_SHOW_DATA,true);
      PlotIndexSetInteger(1,PLOT_SHOW_DATA,false);  
      PlotIndexSetString(0,PLOT_LABEL,osc.Label1());
      PlotIndexSetString(1,PLOT_LABEL,"");
      if(osc.DrawType1()==DRAW_HISTOGRAM){
         PlotIndexSetInteger(0,PLOT_LINE_COLOR,ColorHisto);
      }
      else{
         PlotIndexSetInteger(0,PLOT_LINE_COLOR,ColorLine1);        
      }        
   }
  
   //桁の設定
   IndicatorSetInteger(INDICATOR_DIGITS,osc.Digits());

   //レベルの設定
   int levels=osc.LevelsTotal();
   IndicatorSetInteger(INDICATOR_LEVELS,levels);
   for(int i=0;i<levels;i++){
      IndicatorSetDouble(INDICATOR_LEVELVALUE,i,osc.LevelValue(i));
   }

}    

オシレーターのバッファーの数によって 2 つの利用可能な設定オプションのいずれかが実装されます。最初のバッファー、ヒストグラムは、適切なバッファーの型が設定されます。インジケータ値の小数点以下桁数を設定します。レベルは、最後に設定されます。

新しく作成された関数の呼び出しが含まれている OnInit() の完全なコードです。

intOnInit() {

   SetIndexBuffer(0,Label1Buffer,INDICATOR_DATA);
   SetIndexBuffer(1,Label2Buffer,INDICATOR_DATA);

   PrepareParameters();

   LoadOscillator();
  
   if(!osc.CheckHandle()){
      Alert("Error while loading indicator "+osc.Name());
      return(INIT_FAILED);
   }

   SetStyles();
  
   Print("Parameters matching: "+osc.Help());
  
   ShortName=ProgName+": "+osc.Name();  
   IndicatorSetString(INDICATOR_SHORTNAME,ShortName);
  
   return(INIT_SUCCEEDED);
}

プロパティウィンドウの使用パラメータについて含まれている短い表示名の設定であること、Printは関数の末尾で呼び出されることに注意してください。

汎用的なオシレーターを作成するためのプロジェクトの最初の段階が完了したら、テストすることができます。次は、GUI クラスを作成します。

UniOsc と呼ばれるインジケータが接続されています。(度々細かい修正が行われます) 

グラフィカルインターフェイスの作成計画

グラフィカルインターフェイスを作成するために列挙型パラメータ (ドロップ ダウン リスト) を数値の値、およびボタンをエントリーするために、エントリーフィールドを含むグラフィカルなオブジェクトを使用できます。ただし、これは困難なアプローチでしょう。グラフィカルインターフェイスを作成するため、mql5ライブラリを検索します。このライブラリでは、標準コントロール、ダイアログボックス、スピンボックス、ドロップダウンリストとエントリーフィールドを作成します。ターミナルには、パネルやダイアログを作成するための標準的なクラスのセットがあります。「記事」セクションでは、グラフィカルインターフェイスの作成に関連する一連の記事があります。

3 つの記事のシリーズでは、(記事 1記事 2記事 3) グラフィカル ・ インタ フェースを作成する非常にシンプルかつ高速なメソッドについて説明します。理論だけでなく、ライブラリも、記事で作成しています。このライブラリで、グラフィカルオブジェクトを使用して、グラフィカルインターフェイスを作成することができます。この記事を書くとき、上記のすべての長所と短所を考慮に入れました。最後に、上記の最後のオプションを選択 (incGUI ライブラリ)します。

MT5ターミナルは積極的に開発され、スクロールバーなどが廃止されているが、まだ、このライブラリからコントロールできます。ライブラリを使用するには、「カスタム グラフィカル コントロール の添付ファイルをダウンロードします。その3。MT5 のフォーム」、ターミナルデータ ディレクトリで利用可能なインクルード フォルダーに incGUI_v3.mqh ファイルを保存します。

フォーム クラス

グラフィカルインターフェイスの作成は、別のファイル"UniOscGUI.mqh"に実装されます。まず、ライブラリを含める必要があります。

#include <IncGUI_v3.mqh>

コンパイルします。警告メッセージは、コンパイル時に表示されます。新しいコンパイラはコードの部分を明らかにし、それに従って修正することができます。修正した「inc_GUI_v4」ファイルは、この記事に添付されています。IncGUI_v3.mqhの代わりに、IncGUI_v4.mqh と UniOscDefines.mqh もあります。 

#include<IncGUI_v4.mqh>
#include <UniOsc/UniOscDefines.mqh>

UniOsc のコピーを iUniOscGUI として保存しましょう。その後 iUniOsc インジケータはUseDefaultと KeepPrev のパラメータを非表示にして編集できます。GUI なしでインジケータに意味はありません。

bool                 UseDefault  =  false;
bool                 KeepPrev    =  false;

その後 iUniOsc インジケータは完全に完了と見なされます。

UniOscGUI インジケータでの作業を続けましょう。UniOscGUI.mqh ファイルが含まれます。合計で 3 つのファイルを含める必要があります。

#include <UniOsc/UniOscDefines.mqh>
#include <UniOsc/CUniOsc.mqh>
#include <UniOsc/UniOscGUI.mqh>

インジケータをコンパイルした後は、コードを確認し、すぐにGUI を参照してください。今のところは、すべての作業は、UniOscGUI.mqh ファイルで行います。 

GUI は、ダイアログ ボックスとして表されます。以下は各オシレーターの適切なコントロール セットがある、その上部でのオシレーターのドロップ ダウン リストです。ファイルに、フォーム、およびクラス (親クラスと子クラスのいくつか) このフォーム上のコントロールを作成するためのグループを作成するためのクラスが配置されます。

フォームから始めましょう。フォームの作成プロセスの詳細な説明は、「カスタム グラフィカル コントロール」にあります。その3。MT5 のフォーム」。ここで特定のタスクのこのプロセスを経る。

1. まずは、UniOscGUI.mqh、IncGUI_v4.mqh ファイルから CFormTemplate クラスにコピーし、CUniOscForm に名前を変更する必要があります。

2. プロパティを設定します。CUniOscForm クラスの MainProperties() メソッドで行われます。次のプロパティを設定します。

void MainProperties(){
      m_Name         =  "UniOscForm";
      m_Width        =  FORM_WIDTH;
      m_Height       =  150;
      m_Type         =  0;
      m_Caption      =  "UniOsc";
      m_Movable      =  true;
      m_Resizable    =  true;
      m_CloseButton  =  true;
}

M_Heigh 変数が FORM_WIDTH に設定されていることに注意してください。最終段階では、コントロールの適切なサイズと形状を見つけるので、ファイルの先頭に次の定数を追加する必要があります。

#define FORM_WIDTH 210        //フォーム幅
#define SPIN_BOX_WIDTH 110    //スピン ボックスの幅
#define COMBO_BOX_WIDTH 110   //ドロップ ダウン リストの幅

その後、インジケータの形式を適用できます。その後、デフォルトの外部変数 UseGUI 値 'true' ([プロパティ] ウィンドウの先頭に)を宣言します。

input bool                 UseGUI      =  true;

外部変数の後に、フォーム クラスへのポインターを宣言する必要があります。

CUniOscForm * frm;

UseGUI = true の場合、インジケータの OnInit() でオブジェクトを作成し、追加のプロパティを設定するメソッドを呼び出します。

frm=new CUniOscForm();  //オブジェクトを作成する
frm.Init();             //初期化
frm.SetSubWindow(0);    //フォームを表示するサブウィンドウを作成する
frm.SetPos(10,30);      //フォームの初期ポジションの設定
frm.Show();             //フォームの表示/非表示を有効にする

OnDeinit() 関数では、フォームを非表示し、オブジェクトを削除します。

if(CheckPointer(frm)==POINTER_DYNAMIC){
   frm.Hide();
   delete(frm);
}

OnChartEvent() 関数からイベント () メソッドを呼び出します。

void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   frm.Event(id,lparam,dparam,sparam);  
}

チャートにインジケータをアタッチすると、フォーム (図 2) が表示されます。


図 2。グラフ上で iUniOscGUI インジケータを実行した後、CUniOscForm によって作成されたフォーム 


フォーム上のすべてのボタンが有効です。つまり、左上隅のボタンを使用して、フォームを移動(ボタンをクリックし、新しい場所でのクリックによってポイント)、することができます (右上隅の四角形とボタン)。チャートからインジケータを削除する必要がある場合、十字ボタンをクリックしてフォームを閉じます。インジケータは、 ChartIndicatorDelete()関数を使用して削除できます。インジケータの短い名前を必要とするChartWindowFind()関数を使用して、インジケータ サブウィンドウのインデックスを知っておきます。

フォームの閉じるボタンをクリックすると、Event() メソッドは 1 を返します。戻り値をチェックし、必要に応じて、グラフからインジケータを削除します。

int win=ChartWindowFind(0,ShortName);  //サブウィンドウを見つける
ChartIndicatorDelete(0,win,ShortName); //インジケータを削除
ChartRedraw();                         //グラフの再描画を高速化

十字架をクリックしてフォームを閉じ、さらにグラフからインジケータを削除します。  

メインコントロールをフォームに追加してみましょう。オシレーターのタイプを選択するためのドロップ ダウン リスト。CComBox クラスを使用して作成できます。CUniOscForm クラスにコードを追加します。オブジェクトの変数を宣言します。

CComBox m_cmb_main;

OnInitEvent() メソッドで Init() メソッドを呼び出してください。

m_cmb_main.Init("cb_main",100," select oscillator");

コントロールの名前は、コントロールとラベルの幅 (グラフィカル オブジェクトの名前のプレフィックス)に渡されます。 

OnShowEvent() のShow() メソッドを呼び出す。

m_cmb_main.Show(aLeft+10,aTop+10);

ここで、フォーム内のコントロールのポジションの座標は、(フォーム領域の左上隅から 10 ピクセル単位のインデント) で指定されます。 

OnHideEvent() では、Hide() メソッドを呼び出します。

m_cmb_main.Hide();

選択の変更のイベントの後に、別のインジケータの読み込みが続きます。フォームの EventsHandler() メソッドではなく、インジケータの OnChartEvent() 関数から、オシレーターの一覧のメソッドを呼び出す必要があります。また、イベントを処理する必要があります。

int me=frm.m_cmb_main.Event(id,lparam,dparam,sparam);
if(me==1){
   Alert(frm.m_cmb_main.SelectedText());
}  

グラフ イベントの標準パラメータがメソッドに渡され、メソッドには、1 が返されたメッセージ ボックスが開かれます。

このリストは、オプションで格納する必要があります。いくつかのアプローチが考えられます。

  • すべては、フォームの OnInitEvent() メソッドで行うことができます。
  • 追加メソッドは、フォーム クラスに追加ことができ、Init() メソッドの後から呼び出すことができます。
  • このリストのメソッドは、インジケータから直接アクセスできます。

3番目のオプションは、以下のコードが必要です。まず、インジケータでオシレーターのタイプの配列を作成します。

EOscUniType osctype[]={
   OscUni_ATR,
   OscUni_BearsPower,
   OscUni_BullsPower,  
   OscUni_CCI,
   OscUni_Chaikin,
   OscUni_DeMarker,
   OscUni_Force,
   OscUni_Momentum,
   OscUni_MACD,
   OscUni_OsMA,
   OscUni_RSI,
   OscUni_RVI,
   OscUni_Stochastic,
   OscUni_TriX,
   OscUni_WPR
};

frm の呼び出し後、インジケータで既定のオプションを設定します。

for(int i=0;i<ArraySize(osctype);i++){
   frm.m_cmb_main.AddItem(EnumToString(osctype[i]));
}
frm.m_cmb_main.SetSelectedIndex(0);

チェックは、この段階で実行できます。ドロップダウン リストは、フォームに表示する必要があります。選択範囲が変更されたとき、ボックスに適切なテキストをする必要があります (図 3) 。

 
図3。オシレーターと別のアイテムを選択した後にメッセージボックスのリストを含むフォーム 

フォーム上のコントロール

この記事の初めに、タイプ (標準列挙型の数値と4つのパラメータをエントリーするための3つのパラメータ) によって外部パラメータの最大数を定義します。数値をエントリーするために、incGUI ライブラリの CSpinInputBox 要素 (ボタン、エントリーフィールド) を使用します。CComBox 要素 (ドロップ ダウン リスト) は、標準的な列挙体に対して使用されます。

グラフィカルインターフェイスのクラスを使用してファイルの先頭に標準の列挙型の値を持つ配列を宣言します。

ENUM_APPLIED_PRICE e_price[]={   PRICE_CLOSE,
                                 PRICE_OPEN,
                                 PRICE_HIGH,
                                 PRICE_LOW,
                                 PRICE_MEDIAN,
                                 PRICE_TYPICAL,
                                 PRICE_WEIGHTED
};

ENUM_MA_METHOD e_method[]={MODE_SMA,MODE_EMA,MODE_SMMA,MODE_LWMA};

ENUM_APPLIED_VOLUME e_volume[]={VOLUME_TICK,VOLUME_REAL};

ENUM_STO_PRICE e_sto_price[]={STO_LOWHIGH,STO_CLOSECLOSE};

フォームクラスで、コントロール (CSpinInputBox の 3 つの変数)、CComBox の4つの変数を宣言します。 

CSpinInputBox m_value1;
CSpinInputBox m_value2;      
CSpinInputBox m_value3;
      
CComBox m_price;
CComBox m_method;
CComBox m_volume
CComBox m_sto_price;

フォームクラスの OnInitEvent() メソッドでは、ドロップダウン リスト (CComBox クラスのオブジェクト) を初期化し、配列を宣言しました。

m_price.Init("price",COMBO_BOX_WIDTH," price");
m_method.Init("method",COMBO_BOX_WIDTH," method");
m_volume.Init("volume",COMBO_BOX_WIDTH," volume");
m_sto_price.Init("sto_price",COMBO_BOX_WIDTH," price");              

for(int i=0;i<ArraySize(e_price);i++){
   m_price.AddItem(EnumToString(e_price[i]));            
}
for(int i=0;i<ArraySize(e_method);i++){
   m_method.AddItem(EnumToString(e_method[i]));              
}            
for(int i=0;i<ArraySize(e_volume);i++){
   m_volume.AddItem(EnumToString(e_volume[i]));            
}            
for(int i=0;i<ArraySize(e_sto_price);i++){
   m_sto_price.AddItem(EnumToString(e_sto_price[i]));            
}

さまざまなインジケータを表示するコントロールのセットが異なっているので、セットを形成するクラス (ベースと子クラス) を作成しましょう。この基本クラスは CUniOscControlsです。

class CUniOscControls{
   protected:
      CSpinInputBox * m_value1;
      CSpinInputBox * m_value2;      
      CSpinInputBox * m_value3;
      CComBox * m_price;
      CComBox * m_method;
      CComBox * m_volume;
      CComBox * m_sto_price;
   public:
   void SetPointers(CSpinInputBox & value1,
                        CSpinInputBox & value2,      
                        CSpinInputBox & value3,
                        CComBox & price,
                        CComBox & method,
                        CComBox & volume,
                        CComBox & sto_price){
       ...
   }
   void Hide(){
       ...
   }
   int Event(int id,long lparam,double dparam,string sparam){
      ...
      return(0);
   }
   virtual void InitControls(){
   }  
   virtual void Show(int x,int y){
   }  
   virtual int FormHeight(){
      return(0);
   }
};

このクラスのオブジェクトの使用の初めに SetPointers() メソッドが呼び出されます。すべてのコントロールへのポインタは、このメソッドに渡され、メソッド内でクラス変数に保存されます。 

void SetPointers(CSpinInputBox & value1,
                     CSpinInputBox & value2,      
                     CSpinInputBox & value3,
                     CComBox & price,
                     CComBox & method,
                     CComBox & volume,
                     CComBox & sto_price){
   m_value1=GetPointer(value1);
   m_value2=GetPointer(value2);      
   m_value3=GetPointer(value3);            
   m_price=GetPointer(price);
   m_method=GetPointer(method);
   m_volume=GetPointer(volume);
   m_sto_price=GetPointer(sto_price);
}

ポインタは、(Hide() メソッド) のすべてのコントロールを非表示にされます。

void Hide(){
   m_value1.Hide();
   m_value2.Hide();
   m_value3.Hide();
   m_price.Hide();
   m_method.Hide();
   m_volume.Hide();
   m_sto_price.Hide();
}

イベント (イベント () メソッド) を処理する必要があります。

int Event(int id,long lparam,double dparam,string sparam){
   int e1=m_value1.Event(id,lparam,dparam,sparam);
   int e2=m_value2.Event(id,lparam,dparam,sparam);
   int e3=m_value3.Event(id,lparam,dparam,sparam);
   int e4=m_price.Event(id,lparam,dparam,sparam);
   int e5=m_method.Event(id,lparam,dparam,sparam);
   int e6=m_volume.Event(id,lparam,dparam,sparam);
   int e7=m_sto_price.Event(id,lparam,dparam,sparam);
   if(e1!=0 || e2!=0 || e3!=0 || e4!=0 || e5!=0 ||e6!=0 || e7!=0){
      return(1);
   }
   return(0);
}

その他のメソッドは仮想で、すべてのオシレーターは子クラスで特定のコードがあります。Show() メソッドは、コントロールを表示するために使用されます。FormHeight() フォームの高さが返されます。InitControls() メソッドのみ、コントロール (図 4) の横に表示されるテキストを変更できます。


図4。異なるオシレーターコントロールの横に表示されるテキスト 

実際、incGUI ライブラリからコントロールは、最低限必要なセットのみがテキストを変更するためのメソッドではありません。クラスは、Init() メソッドを呼び出して、必要に応じて、テキストを変更することができるように設計されています。Init() を使用してテキストを変更すると、InitControls() が呼び出されます。  

子クラスを検討してみましょう。ATRインジケータは、最も簡単で、ストキャスティクスは最も困難です。

For ATR:

class CUniOscControls_ATR:public CUniOscControls{
   void InitControls(){
      m_value1.Init("value1",SPIN_BOX_WIDTH,1," ma_period");
   }
   void Show(int x,int y){
      m_value1.Show(x,y);
   }  
   int FormHeight(){
      return(70);
   }  
};

コントロールの Init() メソッドは InitControls() で呼び出されます。この仮想メソッドを準備していた最も重要なことは、"ma_period"コントロールの横に表示されるテキストを渡すことです。

フォームクラスの Show() メソッドで CUniOscControls クラスの Show() メソッドが呼び出されます。その呼び出し中に最初 (上部) のコントロール ユニットの左上隅の座標が指定されます。FormHeight() メソッドは、シンプルに、値を返します。

For Stochastic:

class CUniOscControls_Stochastic:public CUniOscControls{
   void InitControls(){
      m_value1.Init("value1",SPIN_BOX_WIDTH,1," Kperiod");
      m_value2.Init("value2",SPIN_BOX_WIDTH,1," Dperiod");  
      m_value3.Init("value3",SPIN_BOX_WIDTH,1," slowing");          
   }
   void Show(int x,int y){
      m_value1.Show(x,y);
      m_value2.Show(x,y+20);      
      m_value3.Show(x,y+40);
      m_method.Show(x,y+60);      
      m_sto_price.Show(x,y+80);
   }
   int FormHeight(){
      return(150);
   }    
};

すべてのコントロールの座標は、残りの部分を明確にする必要があります。

最後に、フォームにコントロールを追加するメソッドを表示しましょう。フォームクラスのコントロール要素を持つクラスのポインターを宣言します。

CUniOscControls * m_controls;

デストラクター内のオブジェクトを削除します。

void ~CUniOscForm(){
   delete(m_controls);
}

クラスに SetType() メソッドの追加。使用されるオシレーターのタイプを指定するメソッドが呼び出されます。 

      void SetType(long type){
         if(CheckPointer(m_controls)==POINTER_DYNAMIC){
            delete(m_controls);
            m_controls=NULL;
         }
        
         switch((EOscUniType)type){
            case OscUni_ATR:
               m_controls=new CUniOscControls_ATR();
            break;
            case OscUni_BearsPower:
               m_controls=new CUniOscControls_BearsPower();
            break;
            case OscUni_BullsPower:
               m_controls=new CUniOscControls_BullsPower();
            break;
            case OscUni_CCI:
               m_controls=new CUniOscControls_CCI();
            break;
            case OscUni_Chaikin:
               m_controls=new CUniOscControls_Chaikin();
            break;
            case OscUni_DeMarker:
               m_controls=new CUniOscControls_DeMarker();
            break;
            case OscUni_Force:
               m_controls=new CUniOscControls_Force();
            break;
            case OscUni_Momentum:
               m_controls=new CUniOscControls_Momentum();
            break;
            case OscUni_MACD:
               m_controls=new CUniOscControls_MACD();
            break;
            case OscUni_OsMA:
               m_controls=new CUniOscControls_OsMA();
            break;
            case OscUni_RSI:
               m_controls=new CUniOscControls_RSI();
            break;
            case OscUni_RVI:
               m_controls=new CUniOscControls_RVI();
            break;
            case OscUni_Stochastic:
               m_controls=new CUniOscControls_Stochastic();
            break;
            case OscUni_TriX:
               m_controls=new CUniOscControls_TriX();
            break;
            case OscUni_WPR:
               m_controls=new CUniOscControls_WPR();
            break;
         }
        
         m_controls.SetPointers(m_value1,m_value2,m_value3,m_price,m_method,m_volume,m_sto_price);
         m_controls.InitControls();
        
         m_value1.SetReadOnly(false);
         m_value2.SetReadOnly(false);
         m_value3.SetReadOnly(false);
        
         m_value1.SetMinValue(1);        
         m_value2.SetMinValue(1);
         m_value3.SetMinValue(1);
        
         m_Height=m_controls.FormHeight();        
        
      }  

オブジェクトがあった場合は、メソッドの先頭で削除する必要があります。その後、インジケータのタイプによって適切なクラスが読み込まれます。最後に SetPointers() と InitControls() が呼ばれます。追加のアクションを実行し、: スピン ボックス オブジェクトのキーボードから値をエントリーする可能性が有効になっている (ReadOnly() メソッドの呼び出し)、最小値が設定されます。 m_Height の SetMinValue() の呼び出しとフォームの新しい値が設定されます。

OnShowEvent() と OnHideEvent() で、m_controls オブジェクトの適切なメソッドを呼び出す必要があります。

void OnShowEvent(int aLeft, int aTop){
   m_cmb_main.Show(aLeft+10,aTop+10);
   m_controls.Show(aLeft+10,aTop+10+20);
}
void OnHideEvent(){
   m_cmb_main.Hide();            
   m_controls.Hide();          
}  

m_controls オブジェクトのイベントをアクティベートする必要があります。OnChartEvent() 関数のイベント () の呼び出しを追加します。

int ce=frm.m_controls.Event(id,lparam,dparam,sparam);

SetSelectedIndex()の呼び出しの後、インジケータの OnInit() にフォームの SetType() メソッドの呼び出しを追加します。

frm.SetType(_Type);

オシレーターをロードした後、パラメータの値がフォームに表示されるので、フォームクラスに SetValues() メソッドを追加します。

void SetValues(int period1,
               int period2,
               int period3,
               long method,
               long price,
               long volume,  
               long sto_price  
){
  
   m_value1.SetValue(period1);
   m_value2.SetValue(period2);      
   m_value3.SetValue(period3);
  
   for(int i=0;i<ArraySize(e_price);i++){
      if(price==e_price[i]){
         m_price.SetSelectedIndex(i);
         break;
      }
   }
  
   for(int i=0;i<ArraySize(e_method);i++){
      if(method==e_method[i]){
         m_method.SetSelectedIndex(i);
         break;
      }
   }            

   for(int i=0;i<ArraySize(e_volume);i++){
      if(volume==e_volume[i]){
         m_volume.SetSelectedIndex(i);
         break;
      }
   }
  
   for(int i=0;i<ArraySize(e_sto_price);i++){
      if(sto_price==e_sto_price[i]){
         m_sto_price.SetSelectedIndex(i);
         break;
      }
   }

}      

SetValues() メソッドは、列挙体の列挙値を配列内のインデックスを検索しながら、スピン ボックス コントロールの値が設定します。SetType() の呼び出しの後に、SetValues() メソッドを呼び出します。

frm.SetValues(_Period1,_Period2,_Period3,_MaMethod,_Price,_Volume,_StPrice);

この時点で GUI が完全に完了 (図 5) し、インジケータはまだ反応するメソッドを知っていないと仮定することができます。


図5。ATRインジケータのコントロールがあるウィンドウ

汎用的なオシレーターを完了

オシレータークラスの準備が整いました。GUI クラスも準備が整ったので、それらを結合する必要があります。 

この段階で、OnChatEvent() 関数は次のようにする必要があります。

void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{

   int e=frm.Event(id,lparam,dparam,sparam);  
   if(e==1){
      int win=ChartWindowFind(0,ShortName);
      ChartIndicatorDelete(0,win,ShortName);
      ChartRedraw();
   }
  
   int me=frm.m_cmb_main.Event(id,lparam,dparam,sparam);
  
   int ce=frm.m_controls.Event(id,lparam,dparam,sparam);

}

パラメータ (変数 ce) の変更だけでなく、インジケータの変更のイベントを処理する必要があります。

インジケータを変更します。

if(me==1){

   //インジケータ リロード
  
   _Type=osctype[frm.m_cmb_main.SelectedIndex()]; //新しいタイプ
  
   delete(osc); //古いオブジェクトを削除する
   LoadOscillator(); //新しいインジケータの読み込み
  
   if(!osc.CheckHandle()){
      Alert("Error while loading indicator "+osc.Name());
   }
  
   SetStyles(); //スタイルの設定
  
   //短い名前を設定
   ShortName=ProgName+": "+osc.Name();  
   IndicatorSetString(INDICATOR_SHORTNAME,ShortName);

   //フォームは更新

   frm.SetType(osctype[frm.m_cmb_main.SelectedIndex()]); //タイプを設定
   frm.SetValues(_Period1,_Period2,_Period3,_MaMethod,_Price,_Volume,_StPrice); //値を設定する
   frm.Refresh(); //フォームの更新
  
   //インジケータの再計算
   EventSetMillisecondTimer(100);

}  

コードの詳細を見てみましょう。インジケータを選択すると、イベント () メソッドは 1 を返します。_Type データ変数に新しい型の値を代入し、古いオブジェクトが削除されて、新しいオブジェクトが読み込まれ、スタイルと短い形式の名前が設定されます。フォームの外観がインジケータのロードの終わりに更新されます。つまり、タイプおよびパラメータを設定すると Refresh() メソッドが新しいパラメータに基づいてフォームの外観を変更するために呼び出されます。タイマーが開始されます。

パラメータが変更されたコードの一部を考えてみましょう。 

if(ce==1){
  
   if((int)frm.m_value1.Value()>0){
      _Period1=(int)frm.m_value1.Value();
   }
   if((int)frm.m_value2.Value()>0){
      _Period2=(int)frm.m_value2.Value();
   }
   if((int)frm.m_value3.Value()>0){
      _Period3=(int)frm.m_value3.Value();
   }      
   if(frm.m_method.SelectedIndex()!=-1){
      _MaMethod=e_method[frm.m_method.SelectedIndex()];
   }
   if(frm.m_price.SelectedIndex()!=-1){
      _Price=e_price[frm.m_price.SelectedIndex()];
   }
   if(frm.m_volume.SelectedIndex()!=-1){
      _Volume=e_volume[frm.m_volume.SelectedIndex()];
   }
   if(frm.m_sto_price.SelectedIndex()!=-1){
      _StPrice=e_sto_price[frm.m_sto_price.SelectedIndex()];
   }
  
   delete(osc);
   LoadOscillator();
   if(!osc.CheckHandle()){
      Alert("Error while loading indicator "+osc.Name());
   }  
  
   ShortName=ProgName+": "+osc.Name();  
   IndicatorSetString(INDICATOR_SHORTNAME,ShortName);
  
   EventSetMillisecondTimer(100);
    
}

パラメータを変更すると、コントロール クラスのイベント () メソッドは 1 を返します。この場合、新しい値は、確認後すべての変数に割り当てられます。ドロップダウンリストの値は-1 にできません。スピン ボックス コントロールの値は 0 より大きくなければなりません。さらにコードは、別のインジケータを選択することに近いです。

タイマーについて。インジケータの計算には、時間が必要です。したがって、関数BarsCalculated()を使用して、定期的にインジケータが準備ができているか確認してください。戻り値が0より大きい場合、インジケータの計算が完了し、osc オブジェクトの Calculate() メソッドが呼び出されます。

void OnTimer(){
   if(BarsCalculated(osc.Handle())>0){
      if(osc.Calculate(Bars(Symbol(),Period()),0,Label1Buffer,Label2Buffer)!=0){
         ChartRedraw();    
         EventKillTimer();
      }
   }
}

バーの数は最初のパラメータとして Calculate() に渡され、2 番目のパラメータは、インジケータの計算を可能にします。その後、グラフが再描画され (ChartRedaraw()) タイマーは無効になります。 

インジケータは、GUI に応じるべきです。マークはほぼ準備ができています。

少し仕上げを追加します。GUI なしで動作するインジケータの可能性を考えましょう。これを行うには、外部変数 UseGUI を追加します。

input bool                 UseGUI      =  true;

フォームの作成と接続されている OnInit() コードの一部は、UseGUI 変数が有効な場合にのみ実行されます。

if(UseGUI){
   frm=new CUniOscForm();
   frm.Init();
   int ind=0;
  
   for(int i=0;i<ArraySize(osctype);i++){
      frm.m_cmb_main.AddItem(EnumToString(osctype[i]));
      if(osctype[i]==_Type){
         ind=i;
      }
   }
  
   frm.m_cmb_main.SetSelectedIndex(ind);      
   frm.SetType(_Type);
   frm.SetValues(_Period1,_Period2,_Period3,_MaMethod,_Price,_Volume,_StPrice);
  
   frm.SetSubWindow(0);
   frm.SetPos(10,30);
   frm.Show();
}

もう仕上げましょう。ncGUIライブラリは、コントロールの配色の変更をサポートします。これを利用しましょう。

外部パラメータの後に、次のコードを追加します。

enum eColorScheme {
   DefaultScheme=0,
   YellowBrownScheme=1,
   BlueScheme=2,
   GreenScheme=3,
   YellowBlackScheme=4,
   LimeBlackScheme=5,
   AquaBlackScheme=6
};

input eColorScheme ColorScheme=DefaultScheme;

このコードは、配色パターンを選択するためのドロップ ダウン リスト インジケータの [プロパティ] ウィンドウに追加します。OnInit() 関数の先頭行を追加します。

ClrScheme.SetScheme(ColorScheme);

iUniOscGUI インジケータは完全に完了し、グラフィカル インターフェイス (図 6) の異なる色を持つことができます。

 
図6。UniOscGUI インジケータの異なる GUI 配色 

結論

インジケータだけでなく様々なインジケータ値を比較することができますが、外部パラメータのインジケータに及ぼす影響を観察することもできます。インジケータの外観は、そのパラメータを変更したら即座に変化します。プロパティウィンドウを使用する場合、この効果を得られず、その外観に及ぼすインジケータのパラメータに同じような印象を得ることができないためです。

添付

  • UniOscDefines.mqh: このファイルには、オシレーターのタイプの列挙体があります。
  • CUniOsc.mqh: 汎用的なオシレータークラス。
  • UniOsc.mq5: GUI のない汎用的なオシレーター。
  • UniOscGUI.mqh: オシレーターのグラフィカルインターフェイスを作成するためのクラス。 
  • UniOscGUI.mq5: GUI の汎用的なオシレーター。 
  • ncGUI_v4.mqh: グラフィカルなオブジェクトを使用して、グラフィカルインターフェイスを作成するためのライブラリ。ライブラリのバージョンで混乱がありました。CTable のテーブルを作成するために更新されたクラスのコードベースでの記事で、同じ名前のバージョン3 の2つのファイルがありました。修正に加えて、IncGUI_v4 ファイルには、新しいテーブル (コードベースで利用可能) を作成するためのクラスがあります。