
単一チャート上の複数インジケータ(第03部): ユーザー向け定義の開発
はじめに
前回の「単一チャート上の複数のインジケータ」稿では、チャートのサブウィンドウで複数のインジケータを使用できるようにする基本的なコードについて検討しましたが、提示されたのは、はるかに大規模なシステムの出発点にすぎませんでした。 このモデルに基づいてできることはいくつかありますが、この連載の目的の1つはアイデアに基づいて独自のシステムを設計できるようにプログラミングする方法を学ぶことを奨励することであるため、段階的に進める必要があります。この記事では、機能を拡張します。システムが何ができるかがすでに気に入っているがもっとできることを望んでいる人にとってはこれは興味深いかもしれません。
計画
多くの場合、新しいシステムの実装を開始するときにはそれをどれだけ改善できるかがわからないため、新しいプロジェクトは常に将来の改善の対象となる可能性があることを念頭に置いて開始する必要があります。常に何かを計画し、将来の拡張と改善を想像することは、最初の段階にいる人にとって非常に重要です。
中核のコードはまったく変更されていません。これは、ある意味ですでに予想されていたものです。しかし、オブジェクトクラスコードは大幅に変更されています。これらの変更を行って、新しい機能を実装し、コードの再利用がさらに重要になるにつれて、さらに柔軟な方法で新しい改善を作成できるようにしました(これは、オブジェクト指向プログラミングの基本的な考え方の1つです。常に再利用し、新しいものは必要な場合のみ作成します )。それでは、新しいオブジェクトクラスを見てみましょう。わかりやすくするために、変更点を強調表示します。
privateクラス変数の新しい定義から始めましょう。
struct st { string szObjName, szSymbol; int width; }m_Info[def_MaxTemplates]; int m_IdSubWin, m_Counter, m_CPre, m_Aggregate; long m_Id, m_handle; ENUM_TIMEFRAMES m_Period;
使用される変数の数が大幅に増加しています。これは、新しい機能を適切に管理するために、より多くのデータが必要になるためです。変数のシステムに構造体ができました。このような構造体は関連する変数をグループ化するのに非常に適しています。データを操作するときに、データにすばやく簡単にアクセスできるようになります。
void SetBase(const string szSymbol, int iScale, int iSize) { #define macro_SetInteger(A, B) ObjectSetInteger(m_Id, m_Info[m_Counter].szObjName, A, B) if (m_IdSubWin < 0) { m_Id = ChartID(); m_IdSubWin = (int)ChartGetInteger(m_Id, CHART_WINDOWS_TOTAL) - 1; m_Aggregate = 0; } m_Info[m_Counter].szObjName = __FILE__ + (string) MathRand() + (string) ObjectsTotal(m_Id, -1, OBJ_CHART); ObjectCreate(m_Id, m_Info[m_Counter].szObjName, OBJ_CHART, m_IdSubWin, 0, 0); ObjectSetString(m_Id, m_Info[m_Counter].szObjName, OBJPROP_SYMBOL, (m_Info[m_Counter].szSymbol = szSymbol)); // .... macro_SetInteger(OBJPROP_PERIOD, m_Period); m_handle = ObjectGetInteger(m_Id, m_Info[m_Counter].szObjName, OBJPROP_CHART_ID); m_Aggregate += iSize; m_Info[m_Counter].width = iSize; m_CPre += (iSize > 0 ? 1 : 0); m_Counter++; #undef macro_SetInteger };
間もなくみることになる主な変更は、アセット名、オブジェクト名およびその幅を格納するための構造を使用していることです。これで、インジケータがサブウィンドウに表示される幅を指定することもできます。クラスの他の部分でそれらを使用するために、いくつかのメモも作成しましょう。最も変更された関数は以下のとおりです。
void Decode(string &szArg, int &iScale, int &iSize) { #define def_ScaleDefault 4 #define macro_GetData(A) \ b0 = false; \ for (c0++; (c0 < max) && (szArg[c0] == ' '); c0++); \ for (i0 = 0, i1 = c0; (c0 < max) && (szArg[c0] != A); i0 = (szArg[c0] != ' ' ? c0 - i1 + 1 : i0), c0++); \ if (szArg[c0] == A) sz1 = StringSubstr(szArg, i1, i0); else sz1 = ""; string sz1; int i0, i1, c1 = StringLen(szArg); bool b0 = true; StringToUpper(szArg); iScale = def_ScaleDefault; m_Period = _Period; for (int c0 = 0, max = StringLen(szArg); c0 < max; c0++) switch (szArg[c0]) { case ':': b0 = false; for (; (c0 < max) && ((szArg[c0] < '0') || (szArg[c0] > '9')); c0++); iScale = (int)(szArg[c0] - '0'); iScale = ((iScale > 5) || (iScale < 0) ? def_ScaleDefault : iScale); break; case ' ': break; case '<': macro_GetData('>'); if (sz1 == "1M") m_Period = PERIOD_M1; else //.... if (sz1 == "1MES") m_Period = PERIOD_MN1; break; case '[': macro_GetData(']'); iSize = (int) StringToInteger(sz1); break; default: c1 = (b0 ? c0 : c1); break; } szArg = StringSubstr(szArg, 0, c1 + 1); #undef macro_GetData #undef def_ScaleDefault }
緑はコードへの追加を示します。黄色は、ソースコードにすでに存在しているが実用的な理由で移動された行に使用されます。次に、これらすべての追加がコードで何をするか、そしてさらに重要なことに、元のシステムの機能の観点からそれらが何を改善するかを見てみましょう。実際、私たちはユーザーが特定のものをカスタマイズできるようにするための基礎を作成しています。既存の構文に新しいルールを追加することでこれを実行しようとしています(以下の表を参照)。
区切り | 機能性 | 例 | 結果 |
---|---|---|---|
< > | 使用するグラフィック期間を指定する | < 15m > | インジケータの期間を15分に固定します。元のチャートは異なる期間を使用できますが、インジケータは15分のデータでのみ表示されます。 |
[ ] | インディケータの幅を指定する | [ 350 ] | インジケータの幅を350ピクセルに固定します。 |
チャート期間を固定する区切り — インジケータウィンドウでのみ。ユーザーが行うことができる変更には影響しません。他のすべてのインジケータとメインチャートは、ユーザーが選択した新しいチャート期間に更新されますが、固定インジケータは新しいチャート期間に従いません。下の画像に示すように、これは興味深い場合があります。
これにより、取引画面に異なる期間の同じアセットのチャートを表示しておく必要がある場合のさまざまなの定が大幅に容易になります。チャートの幅を固定する区切りにより、より具体的な場合にこれが簡単になります。これには、別の記事で説明するもう1つの非常に大きな用途がありますが、今のところ、インジケータの幅を制御するために使用できます。
特定のルールがないため、すべての区切りを組み合わせて使用することも、インジケータで本当に必要な区切りだけを使用することもできます。唯一のルールは、インジケータ名を他のすべての前に配置することです。コードの説明に戻りましょう。次の行を見てください。
#define macro_GetData(A) \ b0 = false; \ for (c0++; (c0 < max) && (szArg[c0] == ' '); c0++); \ for (i0 = 0, i1 = c0; (c0 < max) && (szArg[c0] != A); i0 = (szArg[c0] != ' ' ? c0 - i1 + 1 : i0), c0++); \ if (szArg[c0] == A) sz1 = StringSubstr(szArg, i1, i0); else sz1 = ""; //.... case '<': macro_GetData('>');
ここでは、多くの人にとって奇妙に見えるかもしれない何かを定義していますが、「macro_GetData(A)」という名前がここで役立ちます。これはマクロであるコードの一部を作成します。コンパイラは、コード内でこの定義を見つけると宣言をマクロコードに置き換えます。これは、宣言間の変更が最小限な特定のコード部分を複数の場所で繰り返す場合に非常に役立ちます。前の例では、緑色の行が置き換えられ、コンパイラによって生成されるコードは次のようになります。
case '<': b0 = false; for (c0++; (c0 < max) && (szArg[c0] == ' '); c0++); for (i0 = 0, i1 = c0; (c0 < max) && (szArg[c0] != '>'); i0 = (szArg[c0] != ' ' ? c0 - i1 + 1 : i0), c0++); if (szArg[c0] == A) sz1 = StringSubstr(szArg, i1, i0); else sz1 = ""; //....
この種のことは、哲学の真の表現です。可能な限り再利用し、可能な限り新しいものは記述しません。次に、構文をより明確にしたい場合に何を替えられるかを見てみましょう。細かい部分ですが、個人のスタイルに合わせて調整すればさらに良い結果が得られます。次の行を見てください。
//..... if (sz1 == "1M") m_Period = PERIOD_M1; else //.....
強調表示された情報は重要な詳細です。私が使用したモデルによると、1分の期間を使用するには構文「MIN_1」を使用してこれを指定する必要があります。個別のスタイルが必要な場合は、独自の方法で指定できます。ただし、文字は大文字で、スペースを入れないでください。たとえば、選択した部分は「1MIN」、「MIN_1」、「1_MINUTE」またはさらに詳細な情報(例:LOCK_IN_1_MIN)に置き換えることができます。単語の間にスペースがない限り、お使いの言語に置き換えることもできます。実際、このスペース制限は取り除くことができますが、本当にそうする必要はないように思えます。プログラミングを知ることがどれほど素晴らしいかをご覧ください。独自のスタイルで物事を使用することができるのです。次に変更したコードは、デフォルトのデストラクタです。
~C_TemplateChart() { for (char c0 = 0; c0 < m_Counter; c0++) { ObjectDelete(m_Id, m_Info[c0].szObjName); SymbolSelect(m_Info[c0].szSymbol, false); } }
強調表示された行は、次の場合に追加されました。アセットが別のウィンドウで開かれていない場合、気配値表示には表示されなくなります。これにより、未使用のアセットが存在し、スペースを占有してウィンドウを汚染するのを防ぎます。さて、前回の記事で説明すると約束したことを見てみましょう。これは元のコードには存在しませんでしたが、将来的にはコードの一部になる予定です。
void AddTemplate(const eTypeChart type, const string szTemplate, int scale, int iSize) { if (m_Counter >= def_MaxTemplates) return; if (type == SYMBOL) SymbolSelect(szTemplate, true); SetBase((type == INDICATOR ? _Symbol : szTemplate), scale, iSize); if (!ChartApplyTemplate(m_handle, szTemplate + ".tpl")) if (type == SYMBOL) ChartApplyTemplate(m_handle, "Default.tpl"); ChartRedraw(m_handle); }
強調表示された行は、監視対象のすべてのアセットが同じ設定を使用している場合にデフォルトの設定ファイルを使用できるようにします。物事が期待通りに進まない場合、実際にどのように行動すべきかを本当に理解せずに何かをするのは良くありません。しかし、設定が完全に同じである場合は使用するべきです。指定されたアセットの設定ファイルが見つからない場合は、DEFAULT.TPLファイルで定義されているデフォルトのMetaTrader 5設定を使用します。これは、Profiles\Templateディレクトリにあります。しかし、まず重要なことを1つ理解する必要があります。ChartApplyTemplate関数でディレクトリを1つも指定しなかったのはなぜでしょうか。これは、MetaTrader 5が特定のロジックに従って検索を実行するためです。このロジックがどのように機能するかを知っていれば、状況をより興味深く、ストレスの少ない方法で理解できます。
強調表示された行を次の行に置き換える次のシナリオを想像してみてください。
if (!ChartApplyTemplate(m_handle, "MyTemplates\\" + szTemplate + ".tpl")) if (type == SYMBOL) ChartApplyTemplate(m_handle, ""MyTemplates\\Default.tpl");
MetaTrader 5は、最初に、カスタムインジケータ実行可能ファイルが配置されているディレクトリのMYTEMPLATESサブディレクトリで設定ファイルを検索します。つまり、複数のインジケータの作成に使用される実行可能ファイルが配置されているのと同じフォルダにMYTEMPLATESフォルダがある場合、MetaTrader5はその場所でファイルを検索します。ただし、そこに何も見つからない場合は、MQL5\Profiles\Templates\MyTemplatesディレクトリで同じファイルが検索されます。これが、これを今まで表示しなかった理由です。しかし、それだけではありません。同じコードに別の詳細があります。
if (!ChartApplyTemplate(m_handle, "\\MyTemplates\\" + szTemplate + ".tpl")) if (type == SYMBOL) ChartApplyTemplate(m_handle, ""\\MyTemplates\\Default.tpl");
すべてを変える1つの小さな詳細は、MetaTrader 5はまずMQL5 MyTemplatesディレクトリでファイルを見つけようとし、ファイルが見つからない場合は、 上記の手順に従うということです。MetaTrader 5の仕組みを知らない人を混乱させたくないので、この情報はChartApplyTemplateのドキュメントにあります。しかし、検索がどのように実行されるかを理解したので、バリエーションを作成し、ファイルを配置する場所を知ることができます。
大幅な変更が加えられたクラスの次の関数を以下に示します。
void Resize(void) { #define macro_SetInteger(A, B) ObjectSetInteger(m_Id, m_Info[c0].szObjName, A, B) int x0 = 0, x1, y = (int)(ChartGetInteger(m_Id, CHART_HEIGHT_IN_PIXELS, m_IdSubWin)); x1 = (int)((ChartGetInteger(m_Id, CHART_WIDTH_IN_PIXELS, m_IdSubWin) - m_Aggregate) / (m_Counter > 0 ? (m_CPre == m_Counter ? m_Counter : (m_Counter - m_CPre)) : 1)); for (char c0 = 0; c0 < m_Counter; x0 += (m_Info[c0].width > 0 ? m_Info[c0].width : x1), c0++) { macro_SetInteger(OBJPROP_XDISTANCE, x0); macro_SetInteger(OBJPROP_XSIZE, (m_Info[c0].width > 0 ? m_Info[c0].width : x1)); macro_SetInteger(OBJPROP_YSIZE, y); } ChartRedraw(); #undef macro_SetInteger }
選択した行は、この関数で最も重要な計算を示しています。ウィンドウはユーザーが望むように調整されますが、固定サイズに設定されたウィンドウは、残りのウィンドウが定義されたサイズになる空き領域を作成します。ただし、メインウィンドウの幅を狭くすると、青色の計算が固定サイズのウィンドウの幅を小さくすることができず、問題が発生する可能性がありますが、固定幅のウィンドウには、後で調査すべき他の利点があるため、今のところそのままにしておきましょう。クラスの最後の変更は次のとおりです。
void AddThese(const eTypeChart type, string szArg) { string szLoc; int i0, iSize; //.... Decode(szLoc, i0, iSize); AddTemplate(type, szLoc, i0, iSize); //.... }
唯一の変更点が強調表示されていて、特別なことは何もありません。
終わりに
この記事が、他の記事と同様に、構造化プログラミングがいかに興味深いかを示していることを願っています。少し調整すれば、安心してシステムに多くの機能を追加できます。完成したコードは使用すればするほど、バグが含まれる可能性が低くなるため、コードの再利用によってメンテナンスがはるかに簡単になります。次の記事では、このシステムを他の場所に持ち込み、多くの人にとってより興味深いものにし、プログラミングの詳細を理解しようとします。また逢いましょう...
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/10239





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