English Русский Español Deutsch Português
preview
初級から中級まで:オブジェクト(IV)

初級から中級まで:オブジェクト(IV)

MetaTrader 5 |
11 0
CODE X
CODE X

はじめに

前の「初級から中級まで:オブジェクト(III)」では、非常にシンプルな目的を持つインジケータの実装方法を紹介しました。それは、MetaTrader 5のメニューを開くことなく、簡単にチャート上へトレンドラインを配置することです。

この内容をまったく必要のないものだと考える人が多いことは理解しています。しかし、ここで覚えておいてほしいのは、目的は特定の一つの機能を実装することではなく、それをどのように実現できるかを示すことです。実際に実装するかどうか、そしてそれが実用的な利益をもたらすかどうかは、単なる一つの要素にすぎません。最も重要なのは、読者の皆さんが、この記事で示している内容を学び、適用することで、誰でもアイデアを実際の形にできるということを理解することです。

前回の記事で見たこと、そして実際におこなったことは、特定の目的のために自分自身のアプリケーションをプログラミングし始めたときに、本当に達成できることのほんの一部にすぎません。今回の記事では、非常に興味深いものを紹介します。それは、MetaTrader 5の中に隠れているにもかかわらず、ほとんど誰にも知られていないインジケータです。

それでは、いつものように進めていきましょう。記事を学習する際に集中を妨げる可能性のあるものはすべて脇に置き、次のセクションへ進んで、このインジケータが何なのかを確認しましょう。


MetaTrader 5に隠されたインジケータ

MetaTrader 5には、市場分析用のインジケータやツールが少ないと主張する人が多くいます。しかし、私は個人的にはこの意見に強く反対します。制限されているのはプラットフォームやその内容ではありません。むしろ、常に完成されたもの、そして自分が想像したとおりのものを探そうとする、多くの人々の視野の狭さのほうです。

MetaTrader 5には、ほとんど誰も気付かない機能が存在します。それは、別のインジケータの中に隠されているためです。実際には、正確にはインジケータではなく、分析用オブジェクトと言うほうが適切です。この隠されたインジケータ、つまりこの分析オブジェクトを表示するためには、このオブジェクトを表示させる必要があります。初期状態では、MetaTrader 5に実装されている形式のまま使用できます。しかし、正しく変更を加えることで、表示される情報ははるかに分かりやすいものになります。なぜなら、その目的が元々の用途とは完全に異なるものになるからです。

ここで私が具体的に指しているのは、フィボナッチオブジェクトです。もちろん、これから実装するものはトレンドラインを使って作成することもできます。しかし、フィボナッチオブジェクトを使用すれば、必要なオブジェクトをはるかに簡単に構築できます。

それでは最初に、前回の記事で作成したインジケータのコードを見てみましょう。以下に示すコードは、別の種類のインジケータを構築するための出発点になります。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. #define macro_NameObject  def_Prefix + (string)(ObjectsTotal(0) + 1)
07. //+------------------------------------------------------------------+
08. #include <Tutorial\File 01.mqh>
09. //+------------------------------------------------------------------+
10. st_Cross gl_Cross;
11. //+------------------------------------------------------------------+
12. int OnInit()
13. {
14.     gl_Cross.Init();
15. 
16.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
17.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
18.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
19. 
20.     return INIT_SUCCEEDED;
21. };
22. //+------------------------------------------------------------------+
23. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
24. {
25.     return rates_total;
26. };
27. //+------------------------------------------------------------------+
28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
29. {
30.     st_TimePrice tp;
31.     static string isPaint = "";
32. 
33.     switch (id)
34.     {
35.         case CHARTEVENT_KEYDOWN:
36.             if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) gl_Cross.Hide();
37.             break;
38.         case CHARTEVENT_MOUSE_MOVE:
39.             if (((uchar)sparam & MOUSE_MIDDLE) != 0)
40.             {
41.                 tp = gl_Cross.Move((ushort)lparam, (ushort)dparam);
42.                 if (isPaint == "")
43.                 {
44.                     ObjectCreate(0, isPaint = macro_NameObject, OBJ_TREND, 0, tp.Time, tp.Price);
45.                     ObjectSetInteger(0, isPaint, OBJPROP_SELECTABLE, true);
46.                     ObjectSetInteger(0, isPaint, OBJPROP_COLOR, clrMagenta);
47.                     ObjectSetInteger(0, isPaint, OBJPROP_WIDTH, 3);
48.                     ObjectSetInteger(0, isPaint, OBJPROP_RAY_RIGHT, true);
49.                 }
50.                 ObjectMove(0, isPaint, 1, tp.Time, tp.Price);
51.                 gl_Cross.Show();
52.             }else
53.             {
54.                 isPaint = "";
55.                 gl_Cross.Hide();
56.             }
57.             break;
58.         case CHARTEVENT_MOUSE_WHEEL:
59.             break;
60.     }
61.     ChartRedraw();
62. };
63. //+------------------------------------------------------------------+
64. void OnDeinit(const int reason)
65. {
66.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
67.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
68. 
69.     gl_Cross.Hide();
70. 
71.     if (reason == REASON_REMOVE)
72.         ObjectsDeleteAll(0, def_Prefix);
73. };
74. //+------------------------------------------------------------------+

コード01

このコードは正常に動作しますが、多くのユーザーにとって小さな欠点があります。それは、チャートへ追加したいオブジェクトを作成するためにマウスの中央ボタンを使用している点です。この操作方法はあまり一般的ではありません。というのも、通常このような操作には左ボタンが使用されるからです。しかし、これは現時点では関係のない小さな問題にすぎません。左ボタンを使用できるようにするには、コードの動作方法にいくつか変更を加える必要がありますが、それについては後ほどおこないます。

現在のところ、私たちがおこないたいのはOBJ_TRENDオブジェクトをOBJ_FIBOに置き換えることです。この単純な置き換えをおこなうことで、次のような結果が得られます。

アニメーション01

ここで注目してほしいのは、トレンドラインからフィボナッチオブジェクトへ移行したという点です。しかし、作成されたオブジェクトをこの形式のまま使用するつもりはありません。なぜなら、これから開発するオブジェクトは、このフィボナッチオブジェクトを基にしていますが、まったく同じものではないからです。私たちは、フィボナッチオブジェクトを変更したものを作成しようとしています。この作業を簡単にするために、まず以下のコードから始めます。

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #define def_Prefix  "Demo"
005. //+------------------------------------------------------------------+
006. #define macro_NameObject  def_Prefix + (string)(ObjectsTotal(0) + 1)
007. //+------------------------------------------------------------------+
008. #include <Tutorial\File 01.mqh>
009. //+------------------------------------------------------------------+
010. st_Cross gl_Cross;
011. //+------------------------------------------------------------------+
012. int OnInit()
013. {
014.     gl_Cross.Init();
015. 
016.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
017.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
018.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, false);
019. 
020.     return INIT_SUCCEEDED;
021. };
022. //+------------------------------------------------------------------+
023. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
024. {
025.     return rates_total;
026. };
027. //+------------------------------------------------------------------+
028. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
029. {
030.     st_TimePrice tp;
031.     static string isPaint = "";
032. 
033.     switch (id)
034.     {
035.         case CHARTEVENT_KEYDOWN:
036.             if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) gl_Cross.Hide();
037.             break;
038.         case CHARTEVENT_MOUSE_MOVE:
039.             if (((uchar)sparam & MOUSE_MIDDLE) != 0)
040.             {
041.                 tp = gl_Cross.Move((ushort)lparam, (ushort)dparam);
042.                 if (isPaint == "")
043.                 {
044.                     ObjectCreate(0, isPaint = macro_NameObject, OBJ_FIBO, 0, tp.Time, tp.Price);
045.                     Modifier_OBJ_FIBO(isPaint);
046.                 }
047.                 ObjectMove(0, isPaint, 1, tp.Time, tp.Price);
048.                 gl_Cross.Show();
049.             }else
050.             {
051.                 isPaint = "";
052.                 gl_Cross.Hide();
053.             }
054.             break;
055.         case CHARTEVENT_MOUSE_WHEEL:
056.             break;
057.     }
058.     ChartRedraw();
059. };
060. //+------------------------------------------------------------------+
061. void OnDeinit(const int reason)
062. {
063.     ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);
064.     ChartSetInteger(0, CHART_CROSSHAIR_TOOL, true);
065. 
066.     gl_Cross.Hide();
067. 
068.     if (reason == REASON_REMOVE)
069.         ObjectsDeleteAll(0, def_Prefix);
070. };
071. //+------------------------------------------------------------------+
072. void Modifier_OBJ_FIBO(const string szNameObj)
073. {
074.     const double nLevels[] = 
075.     {
076.         0,
077.         1,
078.         2.5
079.     };
080.     const string sLevels[] =
081.     {
082.         "Stop",
083.         "Enter",
084.         "Take"
085.     };
086.     const color cLevels[] =
087.     {
088.         clrRed,
089.         clrBlue,
090.         clrGreen
091.     };
092.     
093.     ObjectSetInteger(0, szNameObj, OBJPROP_SELECTABLE, false);
094.     ObjectSetInteger(0, szNameObj, OBJPROP_COLOR, clrNONE);
095.     ObjectSetInteger(0, szNameObj, OBJPROP_WIDTH, 3);
096.     ObjectSetInteger(0, szNameObj, OBJPROP_RAY_RIGHT, true);
097. 
098.     ObjectSetInteger(0, szNameObj, OBJPROP_LEVELS, nLevels.Size());
099.     for (uint c = 0; c < nLevels.Size(); c++)
100.     {
101.         ObjectSetDouble(0, szNameObj, OBJPROP_LEVELVALUE, c, nLevels[c]);
102.         ObjectSetInteger(0, szNameObj, OBJPROP_LEVELCOLOR, c, cLevels[c]);
103.         ObjectSetInteger(0, szNameObj, OBJPROP_LEVELWIDTH, c, 2);
104.         ObjectSetString(0, szNameObj, OBJPROP_LEVELTEXT, c, sLevels[c]);
105.     }
106. }
107. //+------------------------------------------------------------------+

コード02

コード02は非常に興味深いものです。特に、私が紹介したいオブジェクトをほぼ完全に実装している点が重要です。なぜそのようなことが可能なのでしょうか。読者の皆さん、注意深く見てみると、45行目で72行目にある関数を呼び出していることが分かります。この手続きは、少し整理されていないように見えるかもしれません。というのも、最初に私が望む形よりも、はるかに構造化されていないからです。しかし、それでも、このほうが動作を理解しやすくなると思います。

この手続きがどのように動作するのかを説明する前に、まず以下のアニメーションに示されている結果を見てみましょう。

アニメーション02

ご覧のように、私たちが実装しようとしている目的は非常にシンプルです。では、このツールは何に使用できるのでしょうか。多くの人は、市場で特定の種類のトレード戦略を使用しています。そして、そのような戦略の多くでは、利確目標と損切り位置が非常に明確に定義されています。ここで何が実装されているのか、そして何よりも、この実装を特定の目的に合わせてどのように変更できるのかを理解するためには、この点を理解することが重要です。

この変更されたOBJ_FIBOは、まさにこのような条件を確認することを可能にします。つまり、取引に入る前に、エントリー注文がどこに配置されるのか、ストップをどこに置くべきなのか、そしてターゲットがどこになるのかを確認できます。これにより、そのトレードが実行可能かどうかを判断できます。正しく配置されていないストップは確実にヒットしますし、正しく計算されていないターゲットは、おそらく到達することはありません。

一部のプロトレーダーは、実際に取引へ入る数分前に分析します。そのため、このツールは非常に有用であり、不可欠なものになります。なぜなら、トレード中に何が起こる可能性があるのかを正確に把握できるからです。

さて、それではこのツールはどのように作成されたのでしょうか。そして、どのように改善したり、自分自身の作業スタイルに合わせて変更したりできるのでしょうか。これを理解するために、コード02の72行目から始まるModifier_OBJ_FIBO関数に注目しましょう。

ここでは、3つの配列が作成されていることが分かります。それぞれには独自の目的があり、互いに関連しています。それぞれの役割を見る前に、同じ手続き内の別の部分について確認しましょう。93行目では、OBJ_FIBOオブジェクトのプロパティを変更し、ユーザーがマウスクリックで選択できないようにしています。これは重要です。なぜなら、一度配置されたオブジェクトを移動させたくないからです。

しかし、この変更によって別の問題が発生します。それは、オブジェクトをクリックしてからDELETEキーを押すことで削除できなくなることです。ただし、チャートオブジェクト一覧を表示するウィンドウから削除することはできます。または、チャートからインジケータを削除することでも消去できます。

94行目では、点線がチャート上に表示されないようにしています。この点線はアニメーション01ではマゼンタ色で表示されています。しかし、これにclrNONEカラーを設定すると、オブジェクト自体のラインは存在したままですが、表示されなくなります。続く95行目と96行目では、OBJ_FIBOオブジェクトのプロパティを変更しているだけです。ここについて特別に説明することはありません。そして、次に本当に重要な部分、つまり98行目へ進みます。

OBJ_FIBOオブジェクトは、レベルによって構成されています。どの種類のフィボナッチオブジェクトを使用するかに関係なく、すべての場合において、これらのレベルから作成されます。MetaTrader 5は、いくつのレベルを作成すべきかを自動的には知りません。そのため、98行目を使用して指定します。この時点で、プラットフォームは必要なレベル数を認識します。もちろん、より多くのレベル、またはより少ないレベルを指定することもできます。しかし、ここではアニメーション02で示した変更を作成することが目的なので、使用するレベルは3つだけにします。

ここで注意してください。各レベルには、それぞれ独自のプロパティがあります。これらのレベルは、同じ目的を持つトレンドラインや、場合によっては曲線として表現することもできます。そのため、それぞれのラインをチャート上でどのように描画するかを指定し、目的のパターンを作成できます。

では、このパターンはどのように形成されるのでしょうか。ここで、変更しているオブジェクトが興味深いものになります。ループの内部では、その直前に定義された配列を順番に処理していることに注目してください。これらの配列はそれぞれ、作成されるラインのプロパティの一つに値を割り当てています。この部分は比較的理解しやすいでしょう。しかし、一つだけ少し難しい点があります。それが74行目で定義されているnLevels配列です。

この配列で最も難しい部分は、まさにその値です。「なぜこれらの値を使用するのでしょうか。他の値を使用することはできないのでしょうか。」これを理解するには、まずフィボナッチ数に関する別の側面を理解する必要があります。フィボナッチオブジェクトでは、0から1までの値を扱います。この範囲内のすべての値は、誰もが知っている標準的なフィボナッチグリッド内に表示されます。しかし、0未満や1より大きい値を設定することも可能です。そうすることで、オブジェクト自体の拡張プロジェクションを作成できます。次の点に注目してください。チャート上にOBJ_FIBOオブジェクトを描画し始めると、最初のアンカーポイントは値1に対応します。そして、ドラッグして作成すると、0の値に対応する位置が決まります。

これは奇妙に感じるかもしれません。しかし、これが実際の動作です。つまり、78行目に示されているように値2.5を追加した場合、1と0の間の距離の2.5倍ではなく、基準区間を1つ分超えた、追加の1.5倍の位置になります

このようにして、可能なトレードのターゲットが存在する位置へのプロジェクションを計算します。もし1:1のターゲット比率を使用したい場合は、78行目で値2を指定してください。そうすると、エントリーポイントとストップポイントの間に存在する距離と同じ距離が、トレードのターゲット距離として投影されます。

「これはかなり無茶ではないか」と思うかもしれません。実際、その通りです。なぜなら、例えば部分的なレベルを追加することも非常に簡単だからです。74行目で宣言されている配列に対応する値を追加するだけです。ただし、MetaTrader 5でカスタムインジケータを使用した際に、チャート上の表示が期待どおりになるよう、残り2つの配列も調整する必要があることを忘れないでください。

どうでしょうか。必要なのは、それほど多くありません。本当に必要なのは、創造力と何かを作りたいという意欲だけです。そして、本連載の他の記事で紹介した基本的な知識を応用することで、自分のアイデアを発展させ、実装できます。それほど難しいことではありません。

ここでおこなっていることは非常に興味深いため、先ほど示した内容だけに制限されません。さらに大きな可能性があります。それを証明するために、次のことをおこなってみましょう。まず、コード02として示したコードは、アニメーション02で見たものと似た結果を生成します。しかし、以下のコード断片のように、Modifier_OBJ_FIBO手続きの内容だけを別のものに置き換えた場合、何が起こるでしょうか。

                   .
                   .
                   .
71. //+------------------------------------------------------------------+
72. void Modifier_OBJ_FIBO(const string szNameObj)
73. {
74. #define macro_Mod_OBJ_FIBO(txt, pos, cor, width, style) {                       \
75.             ObjectSetDouble(0, szNameObj, OBJPROP_LEVELVALUE, levels, pos);     \
76.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELCOLOR, levels, cor);    \
77.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELWIDTH, levels, width);  \
78.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELSTYLE, levels, style);  \
79.             ObjectSetString(0, szNameObj, OBJPROP_LEVELTEXT, levels, txt);      \
80.             levels++;                                                           \
81.                                                         }
82. 
83.     int levels = 0;
84. 
85.     ObjectSetInteger(0, szNameObj, OBJPROP_SELECTABLE, false);
86.     ObjectSetInteger(0, szNameObj, OBJPROP_COLOR, clrNONE);
87.     ObjectSetInteger(0, szNameObj, OBJPROP_RAY_RIGHT, true);
88. 
89.     macro_Mod_OBJ_FIBO("Stop", 0, clrRed, 2, STYLE_SOLID);
90.     macro_Mod_OBJ_FIBO("Enter", 1, clrBlue, 2, STYLE_DASH);
91.     macro_Mod_OBJ_FIBO("Partial", 1.5, clrYellowGreen, 1, STYLE_DASHDOTDOT);
92.     macro_Mod_OBJ_FIBO("Take", 2, clrGreen, 2, STYLE_SOLID);
93.     ObjectSetInteger(0, szNameObj, OBJPROP_LEVELS, levels);
94. 
95. #undef macro_Mod_OBJ_FIBO
96. }
97. //+------------------------------------------------------------------+

コード03

残りのコードは変更されていないため、コード03では本当に重要な部分だけを示しています。しかし、コード03でおこなった変更を適用してプログラムを実行すると、結果は完全に異なるものになります。以下の図で確認できます。

図01

図01で示されている内容は、フィボナッチオブジェクトをチャート上で直接操作するだけでは実現できないことに注目してください。この場合、ストップポイントとターゲットの比率は1:1であり、距離の50%地点で部分利確する設定です。

最も難しい部分は、まさに色とラインの組み合わせです。信じられない場合は、MetaTrader 5のメニューから挿入したフィボナッチオブジェクトを使って、これと同じものを再現してみてください。不可能であることが分かるでしょう。

このコード03の状況では、Modifier_OBJ_FIBO手続きは、私の考えでは、変更や設定がはるかに簡単です。レベルを非常に簡単に作成できるマクロを使用しているからです。新しいレベルを追加したい場合は、93行目より前に配置するだけです。そうすることで、MetaTrader 5は、いくつのレベルを作成する必要があるのか、そしてどのレベルをチャート上に表示すべきなのかを認識できます。

「あなたが示した内容は非常に興味深いですが、一つ質問があります。これらの記事の目的が完全なアプリケーションの作成方法を教えることではないことは理解しています。しかし、もし最後に示したこのインジケータでユーザーが操作できるようにしたい場合、どのように実現すればよいのでしょうか。コンパイル後は、設定された値を変更できなくなります。少なくともストップとターゲットの比率だけでも調整できるようにするには、どうすればよいのでしょうか」これは簡単に説明できます。この方法を実現するために、まだ十分な時間があります。内容を分けるために、ここではもう少し詳しく見ていきましょう。


ストップターゲット比の調整

ユーザーがストップとターゲットの比率を指定できるようにする方法はいくつかあります。シンプルな方法の一つは、以下に示すようにdouble型の値を使用することです。

                   .
                   .
                   .
09. //+------------------------------------------------------------------+
10. input double user01 = 1.5;                  //Stop-Target Relationship
11. //+------------------------------------------------------------------+
                   .
                   .
                   .
73. //+------------------------------------------------------------------+
74. void Modifier_OBJ_FIBO(const string szNameObj)
75. {
76. #define macro_Mod_OBJ_FIBO(txt, pos, cor, width, style) {                       \
77.             ObjectSetDouble(0, szNameObj, OBJPROP_LEVELVALUE, levels, pos);     \
78.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELCOLOR, levels, cor);    \
79.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELWIDTH, levels, width);  \
80.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELSTYLE, levels, style);  \
81.             ObjectSetString(0, szNameObj, OBJPROP_LEVELTEXT, levels, txt);      \
82.             levels++;                                                           \
83.                                                         }
84. 
85.     int levels = 0;
86. 
87.     ObjectSetInteger(0, szNameObj, OBJPROP_SELECTABLE, false);
88.     ObjectSetInteger(0, szNameObj, OBJPROP_COLOR, clrNONE);
89.     ObjectSetInteger(0, szNameObj, OBJPROP_RAY_RIGHT, true);
90. 
91.     macro_Mod_OBJ_FIBO("Stop", 0, clrRed, 2, STYLE_SOLID);
92.     macro_Mod_OBJ_FIBO("Enter", 1, clrBlue, 2, STYLE_DASH);
93.     macro_Mod_OBJ_FIBO("Partial", 1 + (user01 / 2), clrYellowGreen, 1, STYLE_DASHDOTDOT);
94.     macro_Mod_OBJ_FIBO("Take", 1 + user01, clrGreen, 2, STYLE_SOLID);
95.     ObjectSetInteger(0, szNameObj, OBJPROP_LEVELS, levels);
96. 
97. #undef macro_Mod_OBJ_FIBO
98. }
99. //+------------------------------------------------------------------+

コード04

このコード04の断片は、ストップとそのターゲットの間に直接的な関係を設定する最もシンプルな方法です。必要だったのは、この断片に10行目を追加し、93行目と94行目を調整して比率を生成することだけです。コード04を実行すると、次の図に示されている結果が得られます。

図02

図02で示した比率を調整した後、それをチャート上で直接使用できます。その結果は、以下の図に示すものと同様になります。

図03

このアプローチが非常にシンプルであることを考えると、本当に注目すべき結果です。ここで、「もしユーザーが1未満の比率を入力した場合はどうなるのか」と疑問に思うかもしれません。つまり、ストップとターゲットの比率が不利になる状況で取引したい場合です。その場合、どうなるのでしょうか。また、インジケータを動作させるためにはどのような値を指定すればよいのでしょうか。私は、このような状況を推奨したいわけではありません。しかし、最終的なターゲットよりもストップのほうが大きくなる比率を取得するには、以下の図に示すような方法を使用できます。

図04

値が1未満であるため、この比率は不利なものになります。このインジケータをチャートに適用すると、次のような結果が表示されます。

図05

この例では、エントリーポイントとして同じデータを使用しました。しかし、図を比較すると分かるように、生成される結果は大きく異なります。このようにして、実用的で機能的なツールを得ることができます。つまり、アプリケーションをチャート上に配置し、分析を実行し、値を変更し(図04のように)、再び分析できます。これにより、複数の分析結果を同時にチャート上へ保持できます。

唯一の欠点として考えられるのは、ラインが交差する可能性があることです。しかし、これはユーザーがラインを右方向に延長するかどうかを選択できるようにすることで簡単に解決できます。これを実現するには、以下に示すようにコードを変更するだけで十分です。

                   .
                   .
                   .
09. //+------------------------------------------------------------------+
10. input double    user01 = 1.5;                   //Stop-Target Relationship
11. input bool      user02 = true;                  //Extend lines to the right
12. //+------------------------------------------------------------------+
                   .
                   .
                   .
73. //+------------------------------------------------------------------+
74. void Modifier_OBJ_FIBO(const string szNameObj)
75. {
76. #define macro_Mod_OBJ_FIBO(txt, pos, cor, width, style) {                       \
77.             ObjectSetDouble(0, szNameObj, OBJPROP_LEVELVALUE, levels, pos);     \
78.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELCOLOR, levels, cor);    \
79.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELWIDTH, levels, width);  \
80.             ObjectSetInteger(0, szNameObj, OBJPROP_LEVELSTYLE, levels, style);  \
81.             ObjectSetString(0, szNameObj, OBJPROP_LEVELTEXT, levels, txt);      \
82.             levels++;                                                           \
83.                                                         }
84. 
85.     int levels = 0;
86. 
87.     ObjectSetInteger(0, szNameObj, OBJPROP_SELECTABLE, false);
88.     ObjectSetInteger(0, szNameObj, OBJPROP_COLOR, clrNONE);
89.     ObjectSetInteger(0, szNameObj, OBJPROP_RAY_RIGHT, user02);
90. 
91.     macro_Mod_OBJ_FIBO("Stop", 0, clrRed, 2, STYLE_SOLID);
92.     macro_Mod_OBJ_FIBO("Enter", 1, clrBlue, 2, STYLE_DASH);
93.     macro_Mod_OBJ_FIBO("Partial", 1 + (user01 / 2), clrYellowGreen, 1, STYLE_DASHDOTDOT);
94.     macro_Mod_OBJ_FIBO("Take", 1 + user01, clrGreen, 2, STYLE_SOLID);
95.     ObjectSetInteger(0, szNameObj, OBJPROP_LEVELS, levels);
96. 
97. #undef macro_Mod_OBJ_FIBO
98. }
99. //+------------------------------------------------------------------+

コード05

ここでも、コード05に対する単純な変更です。この場合、11行目を追加しています。その値は89行目で使用され、ラインを右端まで延長するかどうかを制御します。以下の図は、ラインを延長しない設定を示しています。

図06

その結果、チャート上では次のような表示になります。

図07

ここで注目してください。図05では新しい分析を追加しています。そして、チャートから削除することなくインジケータの設定(図06)を変更したにもかかわらず、すでに存在していたものが削除されたり影響を受けたりすることはありません。ほんの少しの努力で、本当に興味深いものを作成することができました。

MetaTrader 5では不可能なことがあると主張する人は多くいます。しかし、私たちはここで、特別な裏技や複雑な方法を使わなくても、最小限の基本的な知識だけで、ほとんど何でも実現できることを確認しました。

さて、ここまで非常に楽しい内容でしたが、次のトピックでもう一つ確認しておきたい点があります。


描画を作成するために左マウスボタンを使用する

チャート上に描画するためにマウスの中央ボタンを使用することは、不自然に感じるかもしれません。ほとんどのユーザーは左ボタンで操作することに慣れているため、中央ボタンは馴染みのないものに感じられます。これまでテストしたコードではすべて正常に動作していますが、最も一般的な操作方法とは言えません。そして、プログラマーである私たちは、この点を改善する必要があります。しかし、ここには小さな注意点があります。

マウスの中央ボタンが押されると、コード02に示されているように、すべてのマウスイベントが38行目の処理に渡されます。ボタンが押されている間、十字カーソルは表示されたままになり、描画を作成できます。中央ボタンを離すと、コード02の52行目で十字カーソルが削除されます。これは、描画システムとの操作ができなくなったことを意味します。

この仕組みを理解すれば、必要な処理が明確になります。中央マウスボタンが押されたときにイベントを発生させ、そのイベントは2つ目のイベントを待機する必要があります。その2つ目のイベントとは、ESCAPEキーを押すこと、または左マウスボタンをクリックすることです。これによって、以前と同じように描画を作成し、オブジェクトを配置できるようにします。ただし、今回は中央ボタンではなく左ボタンを使用します。

これが目的です。この処理を実現する方法は数多くあります。その中には、より多くの作業を必要とする方法もあります。しかし、ここでは教育目的での分かりやすさを最優先するため、すべての状況で理想的ではないことを理解したうえで、最もシンプルな方法を選択します。これはコード06で確認できます。

                   .
                   .
                   .
029. //+------------------------------------------------------------------+
030. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
031. {
032. #define macro_CLEAN_EVENT   {                               \
033.             isPaint = "";                                   \
034.             gl_Cross.Hide();                                \
035.             bMouseL = false;                                \
036.                             }
037. 
038.     st_TimePrice    tp;
039.     static string   isPaint = "";
040.     static bool     bMouseL = false;
041. 
042.     switch (id)
043.     {
044.         case CHARTEVENT_KEYDOWN:
045.             if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) macro_CLEAN_EVENT;
046.             break;
047.         case CHARTEVENT_MOUSE_MOVE:
048.             tp = gl_Cross.Move((ushort)lparam, (ushort)dparam);
049.             if (((uchar)sparam & MOUSE_LEFT) != 0)
050.             {
051.                 if (isPaint != "")
052.                 {
053.                     bMouseL = true;
054.                     if (!ObjectGetInteger(0, isPaint, OBJPROP_TIME)) ObjectMove(0, isPaint, 0, tp.Time, tp.Price);
055.                     ObjectMove(0, isPaint, 1, tp.Time, tp.Price);
056.                 }
057.             }else if (bMouseL) macro_CLEAN_EVENT;
058.             if ((((uchar)sparam & MOUSE_MIDDLE) != 0) && (isPaint == ""))
059.             {
060.                 ObjectCreate(0, isPaint = macro_NameObject, OBJ_FIBO, 0, 0, 0);
061.                 Modifier_OBJ_FIBO(isPaint);
062.                 gl_Cross.Show();
063.             }
064.             break;
065.         case CHARTEVENT_MOUSE_WHEEL:
066.             break;
067.     }
068.     ChartRedraw();
069. 
070. #undef macro_CLEAN_EVENT
071. };
072. //+------------------------------------------------------------------+
                   .
                   .
                   .

コード06

この処理では、中央ボタンによって描画機能を有効にしますが、実際の描画は左ボタンが押されたときだけおこなわれます。ただし、すべてが完璧というわけではありません。考え方自体は機能していますが、アニメーション03で確認できる問題があります。

アニメーション03

このアニメーションでは、チャートが移動してしまうため、描画が難しくなっていることが分かります。このような動きは場合によっては望ましいものですが、今回のケースでは役立つどころか、むしろ作業の妨げになります。この問題をどのように解決するかを説明する前に、まずアニメーション03で示したように、左ボタンを使用できるようにするために必要な変更点を見ていきましょう。

まず、32行目にマクロを追加しました。これにより、現在の状態で描画作成イベントを完了できるようになります。次に、マウスイベント処理部分では、イベントが正しく処理されるように実行順序を変更しました。最初に、マウスの中央ボタンが押されます。その時点で、60行目がOBJ_FIBOオブジェクトを作成し、必要な設定をおこない、十字カーソルを表示します。

この状態で左マウスボタンを押すと、51行目のチェックが実行され、53行目で新たな状態フラグが立てられます。同時に、54行目と55行目によってOBJ_FIBOオブジェクトがチャート上に配置されます。53行目で作成されたマーカーにより、57行目ではOBJ_FIBOオブジェクトの描画が完了した直後、つまり左ボタンが離された時点で、十字カーソルをチャートから削除できます。

アニメーション03で示した問題を解決するには、コード06の断片を変更し、2行を追加する必要があります。変更内容は、以下のコード断片に反映されています。

                   .
                   .
                   .
029. //+------------------------------------------------------------------+
030. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
031. {
032. #define macro_CLEAN_EVENT   {                               \
033.             isPaint = "";                                   \
034.             gl_Cross.Hide();                                \
035.             bMouseL = false;                                \
036.             ChartSetInteger(0, CHART_MOUSE_SCROLL, true);   \
037.                             }
038. 
039.     st_TimePrice    tp;
040.     static string   isPaint = "";
041.     static bool     bMouseL = false;
042. 
043.     switch (id)
044.     {
045.         case CHARTEVENT_KEYDOWN:
046.             if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) macro_CLEAN_EVENT;
047.             break;
048.         case CHARTEVENT_MOUSE_MOVE:
049.             tp = gl_Cross.Move((ushort)lparam, (ushort)dparam);
050.             if (((uchar)sparam & MOUSE_LEFT) != 0)
051.             {
052.                 if (isPaint != "")
053.                 {
054.                     bMouseL = true;
055.                     if (!ObjectGetInteger(0, isPaint, OBJPROP_TIME)) ObjectMove(0, isPaint, 0, tp.Time, tp.Price);
056.                     ObjectMove(0, isPaint, 1, tp.Time, tp.Price);
057.                 }
058.             }else if (bMouseL) macro_CLEAN_EVENT;
059.             if ((((uchar)sparam & MOUSE_MIDDLE) != 0) && (isPaint == ""))
060.             {
061.                 ObjectCreate(0, isPaint = macro_NameObject, OBJ_FIBO, 0, 0, 0);
062.                 ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
063.                 Modifier_OBJ_FIBO(isPaint);
064.                 gl_Cross.Show();
065.             }
066.             break;
067.         case CHARTEVENT_MOUSE_WHEEL:
068.             break;
069.     }
070.     ChartRedraw();
071. 
072. #undef macro_CLEAN_EVENT
073. };
074. //+------------------------------------------------------------------+
                   .
                   .
                   .

コード07

コード07を使用すると、次のアニメーションに示されている結果が得られます。

アニメーション04

コード07にある36行目と62行目を単純に使用するだけで、アニメーション03で発生した問題が解決されることは、少し興味深く感じるかもしれません。もしそう感じるのであれば、おそらく以前の記事で紹介した内容をまだ試していないからかもしれません。そこでは、これらの関数を含め、いくつかの機能についてすでに説明していました。ただし、その時点では、現在のようなインタラクションを実現するために、MetaTrader 5の標準設定を有効化・無効化する方法についてはまだ扱っていませんでした。


最終的な考察

おそらく、この記事はこれまでで最も楽しめる内容だったでしょう。ここまで見てきた内容や説明した内容のほんの一部を使用するだけで、MetaTrader 5には標準では存在しないものを実装することができました。そして、プラットフォーム上に存在する既存のオブジェクトを別の目的のために変更することで、非常に印象的なインジケータを作成することができました。

多くの人は、このように興味深く、そして同時に楽しいものをプログラミングするためには、もっと多くのことを学ぶ必要があると考えていたことでしょう。しかし、作業はここで終わりではありません。今回紹介したインジケータには、まだちょっとした不備が残っています。私たちは意図的にこの状態にしています。なぜなら、皆さん自身に不具合を直す練習をしてもらいたいからです。問題が何であるかについては説明しますが、解決方法は皆さんに任せます。

コード07では、61行目でOBJ_FIBOオブジェクトを作成し、その後64行目で十字カーソルを表示しています。問題は、ユーザー、またはあなた自身がESCAPEキーを押した場合です。その場合、46行目が実行され、十字カーソルはチャートから削除されます。しかし、61行目で作成されたOBJ_FIBOオブジェクトは、表示位置が設定されていないため実際には見えないにもかかわらず、オブジェクト一覧には残ったままになります。

あなたの課題は、中央マウスボタンが押された後に左ボタンが押された場合にのみ、OBJ_FIBOオブジェクトがオブジェクト一覧へ追加されるように修正することです。十字カーソルを生成した後にオブジェクトを作成した際に発生した問題と、それを解決する方法を示したことを覚えていますか。

今回の問題の解決方法も非常によく似ています。必要なのは、コード07の断片内にある一部の処理の実行順序を変更することだけです。サブルーチンを作成したり、新しい関数や手続きを追加したりする必要はありません。処理の順序を正しく変更すれば、十字カーソルを削除できるようになり、不要なOBJ_FIBOオブジェクトの作成も防ぐことができます。これにより、ユーザーはESCAPEキーを押すことができ、その場合でもチャート上に表示されない不要なオブジェクトがオブジェクト一覧に残ることはありません。

この記事で使用したコードは添付ファイルにあります。この課題を解決する練習をしてみてください。それでは、次の記事でお会いしましょう。

MQ5ファイル 説明
コード01 オブジェクトの動作例
コード02
オブジェクトの動作例
コード03 オブジェクトの動作例
コード04 オブジェクトの動作例
コード05 オブジェクトの動作例

MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/16026

添付されたファイル |
Anexo.zip (7.47 KB)
時間帯別・曜日別・月の日付別の季節性インジケータ 時間帯別・曜日別・月の日付別の季節性インジケータ
金融市場における繰り返し発生する価格パターンを分析するためのツールの開発方法について説明します。分析対象となるのは、月の日付(1日~31日)、曜日(月曜日~日曜日)、または1日の時間帯(0時~23時)です。このインジケータは過去のデータを分析し、それぞれの期間における平均リターンを計算します。そして、その結果をヒストグラムと予測表示によって可視化します。また、季節性タイプ、分析するバー数、パーセント表示または絶対値表示、チャートカラーなど、ユーザーが変更可能なパラメータも備えています。
初級から中級まで:オブジェクト(III) 初級から中級まで:オブジェクト(III)
本日の記事では、特にMQL5でプログラミングを学び始めたばかりの方に向けて、非常に魅力的で興味深いインタラクションシステムの実装方法を紹介します。ここで取り上げる内容自体に、根本的に新しいものがあるわけではありません。しかし、本記事では私なりのアプローチで解説を進めるため、内容をより理解しやすく感じていただけると思います。実践的で興味を持って取り組める題材を通して、構造化アプローチを用いたプログラム開発の流れを実際に確認していきます。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
初級から中級まで:マウスイベントの処理 初級から中級まで:マウスイベントの処理
本記事は、コードを眺めて学習するだけでは、そこでおこなわれている処理を十分に理解することが難しい内容に分類されます。実際には、実行可能なアプリケーションを作成し、それを任意のチャート上で動作させる必要があります。そうすることで、キーボードとマウスを組み合わせて特定の要素を作成する方法など、コードを読むだけでは非常に理解しにくい細かな点まで把握できるようになります。