単一チャート上の複数インジケータ(第02部): 実験1
はじめに
前回の「単一チャート上の複数インジケータ 」稿では、画面にさまざまな詳細を表示せずに、単一のチャートで複数のインジケータを使用する方法の概念と基本を説明しました。唯一の目的は、システム自体を紹介し、データベースを作成する方法とそのようなデータベースを利用する方法を示すことで、システムコードを提供しませんでした。ここではコードの実装を開始します。システムは有望であってさらなる改善の可能性が高いため、今後の記事ではその機能を拡張して、用途を広げ、完全なものにします。
計画
アイデアを理解しやすくするために、しかし何よりもシステムを拡張可能にするために、主要なコードはOOP(オブジェクト指向プログラミング)の原則を使用して、2つの別々のファイルに分割されています。これらすべてにより、システムを持続可能で安全かつ安定した方法で開発できるようになります。
この最初のステップでは、インジケータを使用するので、このウィンドウ用に1つ作成しましょう。
他のファイルタイプではなく、インジケータを使用するのはなぜでしょうか。その理由は、インジケータを使用すると、サブウィンドウを作成するために追加の内部ロジックを実装する必要がないためです。代わりに、インジケータにそれを実行するように指示できるため、時間を節約し、システム開発を加速できます。インジケータのヘッダーは次のとおりです。
#property indicator_plots 0 #property indicator_separate_window
これらの2行だけで、銘柄チャートにサブウィンドウを作成できます(仕組みがわからない場合は、下の表を参照してください)。
コード | 説明 |
---|---|
indicador_plots 0 | データ型を追跡しないことをコンパイラに通知して、コンパイラが警告メッセージを表示するのを防ぎます。 |
indicator_separate_window | サブウィンドウを作成するために必要なロジックを追加するようにコンパイラに指示します。 |
これは簡単なはずです。プログラミングに慣れていない人にとっては、ソースコードの一部が奇妙に見えるかもしれませんが、これはプログラミングコミュニティ全体で受け入れられている広く使用されているプロトコルに従っているだけです。MetaTrader 5ではC++に非常に似ているMQL5言語を使用します。わずかな違いはありますが、C++で使用するのと同じプログラミング方法を使用できます。物事ははるかに簡単になります。したがって、この事実を利用して、次のようにC言語ディレクティブを使用できます。
#include <Auxiliary\C_TemplateChart.mqh>
このディレクティブは、特定の場所に存在するヘッダーファイルをインクルードするようにコンパイラに指示します。確かにフルパスはIncludes\Auxiliary\C_TemplateChart.mqhなのですが、MQL5はヘッダーファイルが「includes」ディレクトリにあることをすでに認識しているため、最初の部分を省略できます。山括弧で囲まれているパスは絶対パスで、 引用符で囲まれているパスは相対的です。つまり<Auxiliary\C_TemplateChart. mqh> は"Auxiliary \ C_TemplateChart.mqh"とは異なります。
次に、以下の行があります。
input string user01 = "" ; //Used indicators input string user02 = "" ; //Assets to follow
ここに文字列値を入力できます。インジケータを開くときに使用するコマンドが明確にわかっている場合は、ここでデフォルト値を指定できます。たとえば、線幅が3のRSIと線幅が2のMACDを常に使用する必要がある場合は、デフォルトで次のように指定します。
input string user01 = "RSI:3;MACD:2" ; //Used indicators input string user02 = "" ; //Assets to follow
コマンドは後で変更できますが、デフォルト値はコマンドを使用するように事前構成されているため、インジケータを簡単に開くことができます。次の行はエイリアスを作成します。これを介して、すべての「重い」コードを含むオブジェクトクラスにアクセスし、そのpublic関数にアクセスできるようにします。
C_TemplateChart SubWin;
カスタムインジケータファイル内のコードはほぼ出来上がっており、完成して機能させるにはあと3行追加するだけです。もちろん、このオブジェクトクラスにエラーは含まれていませんが、このクラスではこれを内部で確認できます。したがって、インジケータファイルを完成させるには、緑色で強調表示されている次の行を追加します。
//+------------------------------------------------------------------+ int OnInit () { SubWin.AddThese(C_TemplateChart::INDICATOR, user01); SubWin.AddThese(C_TemplateChart::SYMBOL, user02); return INIT_SUCCEEDED ; } //+------------------------------------------------------------------+ //...... other lines are of no interest to us ...... //+------------------------------------------------------------------+ void OnChartEvent ( const int id, const long &lparam, const double &dparam, const string &sparam) { if (id == CHARTEVENT_CHART_CHANGE ) SubWin.Resize(); } //+------------------------------------------------------------------+
これはまさにカスタムインジケータの機能です。それでは、オブジェクトクラスを含むファイルのブラックボックスを詳しく見てみましょう。これからは2つの関数に注目する必要がありますが、簡単にするために、オブジェクトクラスに存在する関数を見て、それぞれが何に使用されるかを確認することから始めましょう。
関数 | 説明 |
---|---|
SetBase | インジケータデータの表示に必要なオブジェクトを作成 |
decode | 渡されたコマンドをデコード |
AddTemplate | 提示されたデータの種類に応じて調整 |
C_Template | デフォルトのクラスコンストラクタ |
~C_Template | クラスデストラクタ |
Resize | サブウィンドウのサイズを変更 |
AddThese | 内部オブジェクトへのアクセスと構築を担当する関数 |
以上です。ご覧のとおり、カスタムインジケータでは関数RESIZEおよびADDTHESEを使用していますが、現時点では、これらが唯一のpublic関数です。つまり、他のすべてがオブジェクト内に隠されていて必要なく変更されないため、心配する必要はほとんどありません。これにより、最終的なコードの信頼性が高くなります。次の定義で始まるコードを進めましょう。
#define def_MaxTemplates 6
この行は、オブジェクトクラスにとって非常に重要です。これは、作成できるポインタの最大数を定義します。増減するには、この数を変更するだけです。この単純なソリューションを使用すると、動的メモリ割り当てが可能になり、インジケータの数が制限されます。おそらく、これが変更が望まれる可能性のある唯一の点ですが、私は6がほとんどのユーザーと使用されるモニターに適した数だと思います。
次の行は、プログラムのいくつかのポイントでデータを管理しやすくする列挙です。
enum eTypeChart {INDICATOR, SYMBOL};
この行がクラス内にあるという事実は、別のクラスで同じ名前を持つことができることを保証しますが、その中で指定されたデータはこのオブジェクトクラスにのみ属します。したがって、この列挙型に正しくアクセスするには、カスタムインジケータファイルのOnInit関数で提供されているフォームを使用します。クラス名を省略すると、構文エラーと見なされ、コードはコンパイルされません。次の行は予約語です。
private :
これは、さらに続くすべてがこのオブジェクトクラスに対してprivateになり、クラスの外部には表示されないことを意味します。つまり クラス内にいない場合、それ以上のアクセスは不可能になります。これにより、コードのセキュリティが向上し、クラス固有のprivateデータに他の場所からアクセスできなくなります。クラスの最初の実際の関数に到達するまで、さらに行でいくつかの内部変数とprivate変数を宣言します。
void SetBase( const string szSymbol, int scale) { #define macro_SetInteger(A, B) ObjectSetInteger (m_Id, m_szObjName[m_Counter], A, B) ... ObjectCreate (m_Id, m_szObjName[m_Counter], OBJ_CHART , m_IdSubWin, 0 , 0 ); ObjectSetString (m_Id, m_szObjName[m_Counter], OBJPROP_SYMBOL , szSymbol); macro_SetInteger( OBJPROP_CHART_SCALE , scale); ... macro_SetInteger( OBJPROP_PERIOD , _Period ); m_handle = ObjectGetInteger (m_Id, m_szObjName[m_Counter], OBJPROP_CHART_ID ); m_Counter++; #undef macro_SetInteger };
このSetBaseコードセグメントについて詳しく見ていきましょう。マクロを宣言することから始めます。これは、マクロ名で簡略化されたコードをどのように解釈するかをコンパイラに指示します。つまり、何かを数回繰り返す必要がある場合は、C言語のこの機能を使用してより単純にできます。万が一、何かを変更する必要が出たら、変更するのはマクロだけです。これによって作業が大幅に加速し、コードで1つまたは別の引数のみが変更されるようになるためエラーが発生する可能性が低くなります。
そうすることで、CHART型のオブジェクトを作成します。これは奇妙に思えるかもしれません。変更できるものを使用しているのはなぜですか。確かにそうです。次のステップは、使用するアセットを宣言することです。ここでの最初のポイントは、チャットの保存時にアセットが存在しない場合、オブジェクトは現在使用されているアセットにリンクされるということです。それが特定のアセットチャートである場合、後で使用されるのはまさにこのアセットです。重要な詳細は、別のアセットを指定して一般的な設定を使用できるということです。これは、次の記事で詳しく説明します。現在不可能なことを実行できるように、このコードにいくつかの改善を実装するためです。次に、OBJPROP_CHART_SCALEプロパティに示されている情報の高密度化レベルがあります。0から5までの値を使用します。この範囲外の値を使用することもできますが、範囲内にすることをお勧めします。
次に注意する必要があるのは、OBJPROP_PERIODプロパティです。現在のチャート期間を使用していますが、これを変更すると、このプロパティも変更されます。将来的には、それをロックできるようにいくつかの変更を加える予定です。試してみたい場合は、MetaTrader 5で定義された期間(例: PERIOD_M10)を使用できます。これは、固定された10分間のデータの表示を示します。ただし、これは後で改善されます。その後、sub0indicatorの数を1つ増やし、マクロを破棄します。つまり 表現がなくなり、他の場所で使用するには再定義する必要があります。何か忘れていないでしょうか。忘れました。それはおそらくこのコードの最も重要な部分である行です。
m_handle = ObjectGetInteger (m_Id, m_szObjName[m_Counter], OBJPROP_CHART_ID );
この行は、実際にはポインタ自体ではないがポインタと見なすことができるものをキャプチャします。作成したOBJ_CHARTオブジェクトに対していくつかの追加の操作を行うことができます。オブジェクト内にいくつかの設定を適用するには、この値が必要です。これらは、前に作成した設定ファイルにあります。コードを続けると、次の関数が表示されます。以下で全体を確認できます。
void AddTemplate( const eTypeChart type, const string szTemplate, int scale) { if (m_Counter >= def_MaxTemplates) return ; if (type == SYMBOL) SymbolSelect (szTemplate, true ); SetBase((type == INDICATOR ? _Symbol : szTemplate), scale); ChartApplyTemplate (m_handle, szTemplate + ".tpl" ); ChartRedraw (m_handle); }
まず、新しいインジケータを追加できるかどうかを確認します。可能であれば、それがSYMBOLであるかどうかを確認します。SYMBOLである場合は気配値表示ウィンドウに存在する必要があります。これは関数によって保証されます。これに基づいて、情報を受け取るオブジェクトを作成します。実行時に、テンプレートがOBJ_CHARTに適用されたときに魔法が発生します。オブジェクトを再度呼び出しますが、OBJ_CHARTの定義に使用された設定ファイルに含まれる定義に従ったデータが含まれるようになります。シンプルで美しく、理解しやすいです。
これらの2つの関数を使用して多くのことができます。ただし、少なくとももう1つの関数が必要です。その完全なコードを以下に示します。
void Resize( void ) { int x0 = 0 , x1 = ( int )( ChartGetInteger (m_Id, CHART_WIDTH_IN_PIXELS , m_IdSubWin) / (m_Counter > 0 ? m_Counter : 1 )); for ( char c0 = 0 ; c0 < m_Counter; c0++, x0 += x1) { ObjectSetInteger (m_Id, m_szObjName[c0], OBJPROP_XDISTANCE , x0); ObjectSetInteger (m_Id, m_szObjName[c0], OBJPROP_XSIZE , x1); ObjectSetInteger (m_Id, m_szObjName[c0], OBJPROP_YSIZE , ChartGetInteger (m_Id, CHART_HEIGHT_IN_PIXELS , m_IdSubWin)); } ChartRedraw (); }
上記の関数はすべてをその場所に配置します。データは常にサブウィンドウ領域内に保持され追加するものは何もないということです。すべてが完全に機能するために必要なすべてのコードが完成しました。しかし、他の関数はどうでしょうか。心配無用です。残りのルーチンはまったく必要ありません。それらはコマンドラインの解釈をサポートするだけです。ただし、将来このコードを変更したいユーザーにとって重要なことを見てみましょう。オブジェクトクラスコードに表示される予約語です。
public :
この単語は、オブジェクトクラスの一部でなくても、この時点からすべてのデータと関数がコードの他の部分からアクセスおよび表示できることを保証します。したがって、ここでは、他のオブジェクトによって実際に変更またはアクセスできるものを宣言します。実際、優れたオブジェクト指向コードの動作は、オブジェクトのデータへの直接アクセスを決して許可しないことです。うまく設計されたコードでは、メソッドにのみアクセスできます。理由は単に「セキュリティ」です。外部コードがクラス内のデータを変更できるようにすると、データがオブジェクトの期待通りんいならあないというリスクがあります。すべてが正しく見えるときに不整合や欠陥を解決しようとすると、多くの問題や頭痛の種が発生します。C ++で何年もプログラミングしてきたユーザーにアドバイスできることは、作成したクラスのデータに、外部オブジェクトに変更させたり直接アクセスさせたりすることは絶対にしていけないということです。データにアクセスできるように関数またはルーチンを提供しますが、データへの直接アクセスを許可してはいけません。また、作成したクラスが期待するとおりに関数とルーチンがデータをサポートするようにするべきです。これを念頭に置いて、チュートリアルの最後の2つの関数に移りましょう。1つはpublic (AddThese)で、もう1つはprivate (Decode)です。以下が完全なコードです。
void Decode( string &szArg, int &iScale) { #define def_ScaleDefault 4 StringToUpper (szArg); iScale = def_ScaleDefault; for ( int c0 = 0 , c1 = 0 , max = StringLen (szArg); c0 < max; c0++) switch (szArg[c0]) { case ':' : for (; (c0 < max) && ((szArg[c0] < '0' ) || (szArg[c0] > '9' )); c0++); iScale = ( int )(szArg[c0] - '0' ); iScale = ((iScale > 5 ) || (iScale < 0 ) ? def_ScaleDefault : iScale); szArg = StringSubstr (szArg, 0 , c1 + 1 ); return ; case ' ' : break ; default : c1 = c0; break ; } #undef def_ScaleDefault } //+------------------------------------------------------------------+ // ... Codes not related to this part... //+------------------------------------------------------------------+ void AddThese( const eTypeChart type, string szArg) { string szLoc; int i0; StringToUpper (szArg); StringAdd (szArg, ";" ); for ( int c0 = 0 , c1 = 0 , c2 = 0 , max = StringLen (szArg); c0 < max; c0++) switch (szArg[c0]) { case ';' : if (c1 != c2) { szLoc = StringSubstr (szArg, c1, c2 - c1 + 1 ); Decode(szLoc, i0); AddTemplate(type, szLoc, i0); } c1 = c2 = (c0 + 1 ); break ; case ' ' : c1 = (c1 >= c2 ? c0 + 1 : c1); break ; default : c2 = c0; break ; } }
これらの2つの関数は、上記で説明したとおりに機能します。一貫性のないデータがクラスの内部データの一部になるのを防ぐことで、オブジェクトのクラス内でデータの整合性を強化します。コマンドラインを受け取り、事前定義された構文に従ってデコードしますが、受信したコマンドにエラーがあっても、目的外であるため黙っています。それらの目的は、一貫性のないデータがオブジェクトに入らないようにし、検出と修正が困難な副作用を引き起こさないようにすることです。
最終結果は次のようになります。
終わりに
このコードがインスピレーションを与えてくれることを願っています。プログラムは美しくてエキサイティングです。これが、私がプログラミングに興味を持った理由です。特別な結果を達成したいときは頭痛の種になることもありますが。ほとんどの場合、それだけの価値があります。次の記事では、これらすべてをさらに興味深くする方法を説明します。この記事の添付ファイルには、インジケータの完全なコードが含まれています。この記事と前の記事で説明したように、これはすでに使用できます。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/10230
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索