English Русский Español Deutsch Português
preview
市場シミュレーション(第24回):ポジション表示(II)

市場シミュレーション(第24回):ポジション表示(II)

MetaTrader 5テスター |
25 0
Daniel Jose
Daniel Jose

はじめに

皆さん、こんにちは。そして、リプレイ/シミュレーションシステムの作成方法に関する連載の新しい記事へようこそ。

前回の「市場シミュレーション:ポジション表示(I)」では、銘柄チャート上で未決済ポジションを直接表示できるインジケータを作成し、実装する可能性について説明し始めました。MetaTrader 5にはすでにこのような機能が用意されているため、多くの人はこれを必要のないものだと考えるかもしれません。しかし、クロス注文型の執行モデルを使用する場合、あるいは私たちの具体的なケースのように、リプレイ/シミュレーションシステムを使用する場合には、この方法は適用できません。

先ほど述べたように、チャート上で大量のオブジェクトを扱い始めると、いくつかの問題が発生します。特に、それらのオブジェクトが何らかの形で互いに関連付けられている場合、全体の実装はより複雑になります。とはいえ、ここでの目的は、私たちが直面する問題について詳しく説明することではありません。私たちが実装することにした解決策を示すことが目的です。これから見ていく内容は、以前にも述べたとおり、最終形でもなければ唯一の方法でもありません。読者の皆さんには、明確な必要性もないままコードの断片を次々と書き始める前に、少し立ち止まって考えてみることをお勧めします。

これから扱うテーマはかなり広いものなので、この導入部分では詳しく説明しません。この記事が終わる頃には、前回の記事で扱ったものよりも少し良いものを作り上げることが、私の意図です。それでは、作業に取りかかりましょう。


実装の準備

さて、前回の記事で扱ったインジケータは、1つの未決済ポジションに関するデータを表示していました。しかし、それはターミナル内にテキストとして表示するものでした。これはあまり魅力的ではありません。私を含め、多くの人は情報をチャート上に直接表示したいと考えるからです。それが、この記事の目的になります。必要なのは非常にシンプルです。価格を座標として持つ水平線オブジェクトを作成し、その座標に、前回の記事で取得した価格値を設定するだけです。非常にシンプルで、理解しやすいものです。しかし、いくつか問題が発生する可能性があります。ただし、ここではすべてを一つずつ順番に進めていきます。

まずは、これらの線を実装します。そして、そうです。3本の線が必要です。未決済ポジションのオープン価格を示す線が1本。そして、追加で2本の線が必要です。1本はストップロスレベルを示す線、もう1本はテイクプロフィットレベルを示す線です。ただし、これらの線を作成するために、私たちのリプレイ/シミュレーションコードですでに実装されているものを利用します。

さて、読者の皆さんは、これを別の方法で整理したいと思うかもしれません。そこで、まずは優先順位のリストを作成することから始めましょう。それは以下のコードで確認できます。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_VERSION_DEBUG
05. //+------------------------------------------------------------------+
06. #ifdef def_VERSION_DEBUG
07.     #define macro_DEBUG_MODE(A) \
08.                     Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A));
09. #else
10.     #define macro_DEBUG_MODE(A)
11. #endif
12. //+------------------------------------------------------------------+
13. #define def_SymbolReplay           "RePlay"
14. #define def_MaxPosSlider           400
15. #define def_MaskTimeService        0xFED00000
16. #define def_IndicatorTimeFrame     (_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96))))
17. #define def_IndexTimeFrame         4
18. //+------------------------------------------------------------------+
19. union uCast_Double
20. {
21.     double   dValue;
22.     long     _long;                                   // 1 Information
23.     datetime _datetime;                               // 1 Information
24.     uint     _32b[sizeof(double) / sizeof(uint)];     // 2 Informations
25.     ushort   _16b[sizeof(double) / sizeof(ushort)];   // 4 Informations
26.     uchar    _8b [sizeof(double) / sizeof(uchar)];    // 8 Informations
27. };
28. //+------------------------------------------------------------------+
29. enum EnumEvents     {
30.             evTicTac,                         //Tick-tock event
31.             evHideMouse,                      //Hide mouse price line
32.             evShowMouse,                      //Show mouse price line
33.             evHideBarTime,                    //Hide bar time
34.             evShowBarTime,                    //Show bar time
35.             evHideDailyVar,                   //Hide daily variation
36.             evShowDailyVar,                   //Show daily variation
37.             evHidePriceVar,                   //Hide instantaneous variation
38.             evShowPriceVar,                   //Show instantaneous variation
39.             evCtrlReplayInit,                 //Initialize replay control
40.             evChartTradeBuy,                  //Market buy event
41.             evChartTradeSell,                 //Market sell event 
42.             evChartTradeCloseAll,             //Event to close positions
43.             evChartTrade_At_EA,               //Event for communication with the EA
44.             evEA_At_ChartTrade,               //Event for communication with Chart Trade
45.             evChatWriteSocket,                //Event to Mini Chat
46.             evChatReadSocket                  //Event To Mini Chat
47.                         };
48. //+------------------------------------------------------------------+
49. enum EnumPriority {                           //Priority list on objects
50.             ePriorityNull = -1,
51.             ePriorityDefault = 0,
52.             ePriorityChartTrade = 5,
53.             ePriorityOrders = 10
54.                         };
55. //+------------------------------------------------------------------+

Defines.mqhファイルのソースコード

Defines.mqhファイルに加えた変更に注目してください。変更内容は、49行目に列挙型を追加したことです。では、なぜでしょうか。理由は単純です。前回の記事で説明したように、グラフィカルオブジェクトは互いに重なってしまうことがあります。最も厄介なのは、2つの線タイプのオブジェクトが完全に重なってしまう場合です。このような場合、どちらか一方のオブジェクトを素早く簡単に選択する方法はありません。しかし、同じタイプのオブジェクトであっても、それぞれに異なる優先度を持たせるようMetaTrader 5に指示すれば、MetaTrader 5は重なっているオブジェクトを選択するときに、この優先度を考慮します。そして、より高い値を持つオブジェクトを優先します。とはいえ、これを行う際には注意してください。他のオブジェクトを差し置いて、自分で作成したオブジェクトにどんどん高い優先度を割り当てる癖をつけてほしくはありません。

ここでは、4つの優先度レベルを定義しています。最初のレベルは、高い優先度を必要としない、私たちのアプリケーションによって作成されるすべてのオブジェクトに割り当てるものです。それは値-1です。一方、51行目は、ユーザーがチャートにオブジェクトを追加したときにMetaTrader 5が通常使用する値を示しています。つまり、このようなオブジェクトには0という値が割り当てられます。一部のオブジェクトには、ある程度の透明度が設定されている場合があり、チャートの再描画時にリストの上位に来ることがあります。そのため、これらのオブジェクトによってChart Tradeが部分的に隠されてしまう可能性があります。ただし、注意してください。Chart Tradeパネルが完全に隠れていて、つまり、何らかのオブジェクトの背後に位置していたとしても、そのパネルがある位置をクリックすると、MetaTrader 5は前面にあるオブジェクトではなく、Chart Tradeを優先します。

これは、Chart Tradeに5という優先度を割り当てることで実現しています。53行目でも同じことがおこなわれています。ここで、Chart Tradeと、これから作成するオブジェクトとの間には、優先度に差があることに注目してください。この余裕を設けることで、必要になった場合に、さらに別の優先度レベルを追加できます。したがって、ここで示したモデルに従うと、注文とポジションの表示システムの一部となるオブジェクトには、すべての中で最も高い優先度が与えられることになります。必要であれば、この設定を変更しても構いません。ただし、今説明したことを覚えておいてください。

さて、これで、既存のコードにさらに2つの変更を加える必要があります。しかし、心配したり、身構えたりする必要はありません。どちらも非常にシンプルな変更です。このリストを利用できるようにするためだけに必要なものです。それでは、C_Terminalクラスのコードを変更しましょう。変更する場所は、次のコードで確認できます。

201. //+------------------------------------------------------------------+
202. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const EnumPriority zOrder = ePriorityNull) const
203.             {
204.                 ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false);
205.                  ObjectCreate(m_Infos.ID, szName, obj, m_Infos.SubWin, 0, 0);
206. 

C_Terminalクラスからの抜粋

202行目を、ここで示しているバージョンに更新する必要があります。ここでおこなっていることは単純です。先ほど定義した優先度リストを、この関数で使用するようにしているだけです。もう1つ変更する必要がある箇所も、次のコードで確認できます。これはC_ChartFloatingRADクラスにあります。

164. //+------------------------------------------------------------------+
165.         template <typename T >
166.         void CreateObjectEditable(eObjectsIDE arg, T value)
167.             {
168.                 DeleteObjectEdit();
169.                 CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, ePriorityChartTrade);
170.                 ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3);
171.                 ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3);

C_ChartFloatingRADクラスのフラグメント

変更は169行目でおこなう必要があります。元の行を、前のコード断片で示した行に置き換えます。これにより、今後、優先度リストを変更してアプリケーションを再コンパイルした場合、Chart Tradeインジケータ自身の優先度が変更されていれば、その新しい優先度を自動的に使用するようになります。その結果、既存のコードについては、これ以上何も変更する必要がありません。そして、ここから新しいインジケータの作業を始めることができます。


新しいオブジェクトの実装を開始する

より複雑なコードに進む前に、少しペースを落として、私がどのように考えているのかを追っていただきたいと思います。そこで、まずは以下のコードから始めます。これは、前回の記事で扱ったコードを単純に改善したものです。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Indicator for tracking an open position on the server."
04. #property version   "1.00"
05. #property indicator_chart_window
06. #property indicator_plots 0
07. //+------------------------------------------------------------------+
08. #define def_SufixLinePrice   "Price"
09. #define def_SufixLineTake    "Take"
10. #define def_SufixLineStop    "Stop"
11. //+------------------------------------------------------------------+
12. #include <Market Replay\Auxiliar\C_Terminal.mqh>
13. //+------------------------------------------------------------------+
14. input color user00 = clrBlue;               //Color Line Price
15. input color user01 = clrForestGreen;        //Color Line Take Profit
16. input color user02 = clrFireBrick;          //Color Line Stop Loss
17. //+------------------------------------------------------------------+
18. C_Terminal *Terminal;
19. struct st
20. {
21.     long       id;
22.     string     szPrefixName;
23. }glVariables;
24. //+------------------------------------------------------------------+
25. void CreateLinePriceOpen(const string szObjName)
26. {
27.     (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull);
28.     ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN));
29. }
30. //+------------------------------------------------------------------+
31. int OnInit()
32. {
33.     ZeroMemory(glVariables);
34.     Terminal = new C_Terminal();
35.     if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED;
36.     glVariables.id = (*Terminal).GetInfoTerminal().ID;
37.     glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET));
38.     CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice);
39.     
40.     return INIT_SUCCEEDED;
41. }
42. //+------------------------------------------------------------------+
43. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
44. {
45.     return rates_total;
46. }
47. //+------------------------------------------------------------------+
48. void OnDeinit(const int reason)
49. {
50.     delete Terminal;
51.     if (glVariables.id > 0)
52.         ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName);
53. }
54. //+------------------------------------------------------------------+

ポジション表示インジケータのソースコード

前のコードを見ていると、皆さんはおそらくすでにこう考えているのではないでしょうか。「さて、いよいよ本当に複雑になってきたな。」しかし、そうではありません。まだ基本的なレベルです。上のコードでは、インジケータを設置したチャートの銘柄に未決済ポジションが存在する場合、そのチャート上に1本の線だけが作成されます。このコードは依然として非常にシンプルでコンパクトであり、チェック処理もありません。プログラムは単純にH_LINEオブジェクトを作成し、ユーザーが指定した色を使ってチャート上に表示するだけです。十分にシンプルです。しかし、MQL5を使ってチャート上にオブジェクトを配置することに慣れているのであれば、普段見慣れている呼び出しがここには1つも存在しないことに驚くかもしれません。そこで、次の疑問が生じます。このコードは本当に動作するのでしょうか。そして、動作するのであれば、どのように動作しているのでしょうか。

まず08行目から始めましょう。ここでは定義を1つ置いています。同様に、その次の2行でも、これから作成するラインオブジェクトのサフィックスを定義しています。それをおこなう理由については、後ほど明らかになります。14行目から16行目では、ラインに使用する色をユーザーが変更できるようにしています。ここまでは、それほど複雑ではありません。しかし、ここから面白くなってきます。それが19行目です。ここで、後になくなる構造体の定義を開始しています。ただし、今はこの方法で進めます。なぜなら、読者の皆さんには、私がどのような考え方でこのコードを組み立てているのかを理解していただく必要があるからです。そうすることで、なぜ私がこのような形でコードを構成しているのかを理解できるようになります。

25行目の関数はいったん飛ばして、31行目へ進みましょう。ここから、インジケータがMetaTrader 5上で動作を開始します。最初におこなっていることに注目してください。glVariables構造体のメモリをゼロで初期化しています。なぜこのようなことをするのでしょうか。すでに説明したように、この構造体は後になくなるからです。しかし、たとえこの構造体がそのまま残るとしても、構造体に存在するすべての変数が初期値として0を持っていることを確実にする必要があります。この呼び出しを使うほうが、変数を1つずつ初期化するより簡単です。そして、もし変数の1つを初期化するのを忘れてしまったとしても、その変数は確実に0の値になります。

さて、34行目と35行目については、前回の記事ですでに説明しているので、ここでは飛ばします。一方、36行目では、C_Terminalクラスに格納されている値を、後で使用するために保存しているだけです。ここからが主要な行になります。まず最初の行、つまり37行目では、作成するオブジェクト、この場合はHLINEオブジェクトの名前に使用するプレフィックスの値を定義しています。このプレフィックスの値は、まさにポジションチケットを識別する数値です。これによって、チャート上にこの名前を持つオブジェクトが1つだけ存在することが保証されます。

しかし、後でこの同じ値を再利用することになります。そのため、MetaTrader 5が、これからチャート上に配置するオブジェクトを混同して、「このオブジェクトはすでに存在する」と判断しないようにする必要があります。ここで、08行目から10行目にある定義の意味が分かり始めてきたのではないでしょうか。では、コードに戻りましょう。現在は38行目です。ここでは、25行目にある関数を呼び出しています。その目的は、チャート上にラインを作成し、ポジションのオープン価格の位置に配置することです。

それでは、25行目の関数に戻って、いくつかの点を理解しましょう。この段階では、コードはまだ非常にシンプルで理解しやすいものです。この関数は、HLINEオブジェクトに割り当てる名前を受け取ります。では、このオブジェクトはどこで作成しているのでしょうか。C_Terminalクラスを通して作成しています。具体的には、27行目です。ここからは、特によく注意してください。27行目の4番目のパラメータでは、非常に低い優先度を使用しています。実際、これはリストの中で最も低い優先度です。なぜでしょうか。

皆さんは、こう考えるかもしれません。「しかし、今作成しているこのラインは、ポジションと注文のインジケータの一部ではないのですか。なぜこのオブジェクトには、ほかのオブジェクトよりも低い選択優先度が割り当てられているのでしょうか。意味がありません。ポジションと注文のインジケータによって作成されるオブジェクト用に設定した優先度を使用するべきではないでしょうか。」いいえ、読者の皆さん。特に今回のケースでは、そのように考えるのは間違いです。理由は単純です。この水平線、そしてこれは絶対に明確にしておきたいのですが、この線だけは、可能な限り低い優先度を持たなければなりません。理由は簡単です。この線は移動させるものではなく、イベントを受け取るべきでもないからです。この線は、取引サーバーが未決済ポジションのオープン価格として示している価格、つまりその位置を表しているからです。これで、なぜこの優先度を割り当てたのか分かったでしょうか。

しかし、まだ安心してはいけません。私たちはラインを作成し、それをチャート上に配置しただけです。この配置は、先ほどのコードの28行目でおこなわれています。それだけです。この状態では、ユーザーがチャート上でこのラインを削除したり、移動したり、あるいは選択したりすると、そのラインを移動できてしまいます。これは、このインジケータで実現しようとしている原則と完全に矛盾します。この問題を解決するには、さらにコードを実装する必要があります。しかし、ここでの目的は、物事をできるだけ明確に説明することです。そのため、段階的に進めていきます。そうすることで、今回のオブジェクト、つまり価格ラインを移動させてはいけない理由を理解できるようになります

いずれにしても、インジケータがチャートから削除されると、51行目のチェックが実行されます。条件を満たしている場合、52行目では、このコードで作成し、チャート上に表示したオブジェクトのうち、名前がこのコードで使用しているプレフィックスで始まるものをすべて削除します。ここまでの部分が明確であればよいのですが、同じ原理を使って、ストップロスとテイクプロフィットが設定されている位置を示す水平線も追加します。ただし、いくつか違いがあります。それは、次のコードで確認できます。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Indicator for tracking an open position on the server."
04. #property version   "1.00"
05. #property indicator_chart_window
06. #property indicator_plots 0
07. //+------------------------------------------------------------------+
08. #define def_SufixLinePrice   "Price"
09. #define def_SufixLineTake    "Take"
10. #define def_SufixLineStop    "Stop"
11. //+------------------------------------------------------------------+
12. #include <Market Replay\Auxiliar\C_Terminal.mqh>
13. //+------------------------------------------------------------------+
14. input color user00 = clrBlue;               //Color Line Price
15. input color user01 = clrForestGreen;        //Color Line Take Profit
16. input color user02 = clrFireBrick;          //Color Line Stop Loss
17. //+------------------------------------------------------------------+
18. C_Terminal *Terminal;
19. struct st
20. {
21.     long       id;
22.     string     szPrefixName;
23. }glVariables;
24. //+------------------------------------------------------------------+
25. void CreateLinePriceOpen(const string szObjName)
26. {
27.     (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull);
28.     ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN));
29. }
30. //+------------------------------------------------------------------+
31. void CreateLineStopAndTake(const string szObjName, const double price, const color cor)
32. {
33.     if (price <= 0) return;
34.     (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, ePriorityOrders);
35.     ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price);
36. }
37. //+------------------------------------------------------------------+
38. int OnInit()
39. {
40.     ZeroMemory(glVariables);
41.     Terminal = new C_Terminal();
42.     if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED;
43.     glVariables.id = (*Terminal).GetInfoTerminal().ID;
44.     glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET));
45.     CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice);
46.     CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01);
47.     CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02);
48.     
49.     return INIT_SUCCEEDED;
50. }
51. //+------------------------------------------------------------------+
52. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
53. {
54.     return rates_total;
55. }
56. //+------------------------------------------------------------------+
57. void OnDeinit(const int reason)
58. {
59.     delete Terminal;
60.     if (glVariables.id > 0)
61.         ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName);
62. }
63. //+------------------------------------------------------------------+

ポジション表示インジケータのソースコード

ここで追加したコードが非常に少ないことに注目してください。しかし、これで、銘柄について1つだけ未決済ポジションが存在するチャート上でこのアプリケーションを使用すると、オープン価格、テイクプロフィット、ストップロスがそれぞれどこに位置しているのかを確認できるようになります。それでは、この表示を可能にするために、コードに何を追加したのかを見てみましょう。

まず、31行目に新しい関数があります。これは、テイクプロフィットとストップロスの水平線を作成する関数です。これは、オープン価格のラインを作成する関数とほぼ同じであることに注目してください。では、なぜこれらを分けたのでしょうか。それは、オープン価格のラインには、ほかのライン、つまり今回の場合はテイクプロフィットとストップロスのラインとは異なる優先度を設定する必要があることを理解してもらうためです。もう1つ細かな点があります。33行目では、価格の値が0と等しくないこと、そして0未満ではないことをチェックしています。0以下では意味がないからです。この条件に該当する場合、水平線オブジェクトは作成されません。

しかし、インジケータがチャート上にさらに2本のラインを配置できるようになった後も、インジケータを削除すれば、それによって作成されたすべてのラインも削除されます。したがって、少なくとも最も基本的な形では、コードに46行目と47行目を追加するだけですべてが完成します。その結果、インジケータがチャート上にあり、かつ未決済ポジションが存在する場合、次の画像を見ることができます。

しかし、さらに複雑なことに進む前に、チャート上のそれぞれのラインが何を表しているのか分かるように、テキストを追加しましょう。これがないと、ユーザーはそのラインの目的が分からず、MetaTrader 5の「オブジェクト」ウィンドウから手動で削除してしまいたくなるかもしれません。このウィンドウは、次の画像で確認できます。

ここでは、チャート上に存在するすべてのオブジェクトを確認できます。それが非表示になっているかどうかは関係ありません。チャート上に何らかの形でオブジェクトが存在していれば、このリストに表示されます。しかし、ユーザーがその目的を知らないというだけの理由で、これらのオブジェクトの1つを削除してしまうことは避けたいところです。そこで、コードに小さな変更を加えます。これは2か所のいずれかで変更することができます。ここ、つまりインジケータ側でもできますし、C_Terminalクラス側でもできます。しかし、正直なところ、すでに完全に安定しているクラスに、十分な理由もなく変更を加えて、そこに不安定要素を持ち込むようなことはしたくありません。そこで、新しく作成したオブジェクトのいくつかのプロパティの状態を変更する必要があるため、今回はここでローカルに処理することにしましょう。


コードのさらなる改善

あるオブジェクトが何なのか、そして何のために存在するのかをユーザーが理解できるようにするために追加できるものの1つが、オブジェクトの説明です。これは非常に簡単にできます。上のコードを、下に示すコードに置き換えるだけです。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Indicator for tracking an open position on the server."
04. #property version   "1.00"
05. #property indicator_chart_window
06. #property indicator_plots 0
07. //+------------------------------------------------------------------+
08. #define def_SufixLinePrice   "Price"
09. #define def_SufixLineTake    "Take"
10. #define def_SufixLineStop    "Stop"
11. //+------------------------------------------------------------------+
12. #include <Market Replay\Auxiliar\C_Terminal.mqh>
13. //+------------------------------------------------------------------+
14. input color user00 = clrRoyalBlue;          //Color Line Price
15. input color user01 = clrForestGreen;        //Color Line Take Profit
16. input color user02 = clrFireBrick;          //Color Line Stop Loss
17. //+------------------------------------------------------------------+
18. C_Terminal *Terminal;
19. struct st
20. {
21.     long       id;
22.     string     szPrefixName;
23. }glVariables;
24. //+------------------------------------------------------------------+
25. void CreateLinePriceOpen(const string szObjName, const string szDescription = "\n")
26. {
27.     (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull);
28.     ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN));
29.     ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription);
30.     ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription);
31. }
32. //+------------------------------------------------------------------+
33. void CreateLineStopAndTake(const string szObjName, const double price, const color cor, const string szDescription = "\n")
34. {
35.     if (price <= 0) return;
36.     (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, ePriorityOrders);
37.     ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price);
38.     ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription);
39.     ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription);
40. }
41. //+------------------------------------------------------------------+
42. int OnInit()
43. {
44.     ZeroMemory(glVariables);
45.     Terminal = new C_Terminal();    
46.     if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED;
47.     glVariables.id = (*Terminal).GetInfoTerminal().ID;
48.     glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET));
49.     CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice, "Position opening price.");
50.     CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point.");
51.     CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point.");
52.     
53.     return INIT_SUCCEEDED;
54. }
55. //+------------------------------------------------------------------+
56. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
57. {
58.     return rates_total;
59. }
60. //+------------------------------------------------------------------+
61. void OnDeinit(const int reason)
62. {
63.     delete Terminal;
64.     if (glVariables.id > 0)
65.         ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName);
66. }
67. //+------------------------------------------------------------------+

ポジション表示インジケータのソースコード

ここでも、次の画像に示す結果を得るために追加したコードが非常に少ないことに注目してください。

また、オブジェクトのリストにも、現在は次のように表示されるようになっています。

覚えておいてください。上の画像に示されているように、テキストをチャート上に表示するには、チャート設定で次のオプションを有効にする必要があります。

このプロパティは、MQL5のコードから有効にすることもできます。そのためには、プログラムに次の1行を追加するだけです。

41. //+------------------------------------------------------------------+
42. int OnInit()
43. {
44.     ZeroMemory(glVariables);
45.     Terminal = new C_Terminal();
46.     if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED;
47.     glVariables.id = (*Terminal).GetInfoTerminal().ID;
48.     ChartSetInteger(glVariables.id, CHART_SHOW_OBJECT_DESCR, true);    
49.     glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET));
50.     CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice, "Position opening price.");
51.     CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point.");
52.     CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point.");
53.     
54.     return INIT_SUCCEEDED;
55. }
56. //+------------------------------------------------------------------+

メインコード断片

ここでも、追加された行が48行目であることに注目してください。これによって、先ほど画像で示したものと同じオプションが有効になります。しかし、この処理を追加すると、ユーザー設定を変更しないようにするために、コードが解析しなければならない要素が1つ増えることになります。皆さんがプログラマーとして変更したくない設定を、意図せず変更してしまう可能性があるわけです。そのため、最終版のコードではこれを使用しません。ただし、もしこの機能を使いたいのであれば、問題なく実装できます。同様に、テキストを表示する色も変更できます。これは、下の画像で示されている次の項目で設定します。

このプロパティもMQL5のコードから変更できます。しかし、ここでも、ユーザーがすでに慣れている設定に干渉したり、それを変更したりすることは避けたいと思います。目的を達成するために、できるだけ少ない手間で済ませたいのです。さて、メインコードに戻りましょう。読者の皆さん、ここで1つ注意してほしいことがあります。CreateLinePriceOpen関数の宣言でも、CreateLineStopAndTake関数の宣言でも、szDescriptionの値には特定の値が設定されています。オブジェクトに説明が指定されていない場合、説明には「\n」という値が設定されます。これはMQL5のドキュメントにも記載されており、次のように説明されています。

「ツールチップ」のテキストです。このプロパティが定義されていない場合、ターミナルによって自動生成されたツールチップが表示されます。 ツールチップは、値に「\n」(改行)を割り当てることで無効にできます。

したがって、szDescriptionが受け取るこのデフォルト値は、29行目と38行目でOBJPROP_TEXTプロパティに設定される情報には、実際には影響しません。しかし、30行目と39行目で設定しているOBJPROP_TOOLTIPプロパティについては、MetaTrader 5ターミナルが値を自動生成するのを防ぐことができます。

「では、OBJPROP_TOOLTIPプロパティにはどうやってアクセスするのでしょうか。OBJPROP_TEXTと同じものではないのですか。」いいえ、同じではありません。OBJPROP_TEXTプロパティは、オブジェクトにテキストを追加できる場合に、まさに私たちが目にするテキストです。水平線は、このテキストを追加できるオブジェクトの1つです。しかし、このプロパティがすべてのオブジェクトに存在するわけではありません。

一方、OBJPROP_TOOLTIPプロパティは、すべてのチャートオブジェクトに存在します。また、この2つのプロパティに設定する説明文は、必ずしも同じである必要はありません。異なる内容にすることもできます。なぜなら、OBJPROP_TOOLTIPは、カーソルをオブジェクトの上にしばらく置いたときにだけ、そのテキストを表示するからです。そのとき、小さなウィンドウが表示され、OBJPROP_TOOLTIPプロパティの内容が表示されます。

もしこれまで気づいていなかったのであれば、下のアニメーションを見ることで、この小さなウィンドウが何のためにあるのか、またOBJPROP_TOOLTIPプロパティの内容をどのように確認できるのかが分かります。

この機能は、その内容、インタラクションの仕組み、そして目的という点で、補助的なツールとして役立ちます。一般的なさまざまな場面で使える、シンプルで直接的、そして実用的な機能です。すべてのオブジェクトは、このプロパティを通して何らかの情報を受け取ることができます。アプリケーションの動作をユーザーに理解してもらいたいのであれば、特に、多数のボタンがアイコンや画像として表示されることの多いアプリケーションでは、このプロパティを使ってユーザーに必要な情報を提供してください。ユーザーはきっと非常にありがたく感じるでしょう。なぜなら、それによってアプリケーションの使いやすさが向上するからです。このことを覚えておいてください。

さて、この記事も終わりに近づいてきました。しかし、終わる前に、ラインの作成処理を1つの関数にまとめてみるのはどうでしょうか。こうしておけば、今後の時間と手間を節約できます。というのも、これらのラインを移動させるために、別の方法を使用することになるからです。それでは、次回の記事で使用する最終的なコードを、以下に示します。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Indicator for tracking an open position on the server."
04. #property version   "1.00"
05. #property indicator_chart_window
06. #property indicator_plots 0
07. //+------------------------------------------------------------------+
08. #define def_SufixLinePrice   "Price"
09. #define def_SufixLineTake    "Take"
10. #define def_SufixLineStop    "Stop"
11. //+------------------------------------------------------------------+
12. #include <Market Replay\Auxiliar\C_Terminal.mqh>
13. //+------------------------------------------------------------------+
14. input color user00 = clrRoyalBlue;          //Color Line Price
15. input color user01 = clrForestGreen;        //Color Line Take Profit
16. input color user02 = clrFireBrick;          //Color Line Stop Loss
17. //+------------------------------------------------------------------+
18. C_Terminal *Terminal;
19. struct st
20. {
21.     long       id;
22.     string     szPrefixName;
23. }glVariables;
24. //+------------------------------------------------------------------+
25. void CreateLineInfos(const string szObjName, const double price, const color cor, const string szDescription = "\n")
26. {
27.     if (price <= 0) return;
28.     (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, (EnumPriority)(cor == user00 ? ePriorityNull : ePriorityOrders))
29.     ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price);
30.     ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription);
31.     ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription);
32.     ObjectSetInteger(glVariables.id, szObjName, OBJPROP_SELECTABLE, cor != user00);
33. }
34. //+------------------------------------------------------------------+
35. int OnInit()
36. {
37.     ZeroMemory(glVariables);
38.     Terminal = new C_Terminal();
39.     if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED;
40.     glVariables.id = (*Terminal).GetInfoTerminal().ID;
41.     glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET));
42.     CreateLineInfos(glVariables.szPrefixName + def_SufixLinePrice, PositionGetDouble(POSITION_PRICE_OPEN), user00, "Position opening price.");
43.     CreateLineInfos(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point.");
44.     CreateLineInfos(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point.");
45.     
46.     return INIT_SUCCEEDED;
47. }
48. //+------------------------------------------------------------------+
49. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
50. {
51.     return rates_total;
52. }
53. //+------------------------------------------------------------------+
54. void OnDeinit(const int reason)
55. {
56.     delete Terminal;
57.     if (glVariables.id > 0)
58.         ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName);
59. }
60. //+------------------------------------------------------------------+

ポジション表示インジケータのソースコード

これは、次回の記事で変更を加えるコードです。実質的には何も大きく変わっていないので、この記事を読んで少し考えてみるだけで、どのように動作するのかは簡単に理解できると思います。ただし、1つだけ注意があります。ここでは必ず注意しておきたい点です。3本すべてのラインに同じ色を使用しないでください。ストップロスとテイクプロフィットのラインには同じ色を使用しても構いません。しかし、オープン価格のラインには同じ色を使用しないでください。これは、28行目と32行目で、色を物事を区別するための手段として使用しているからです。とはいえ、これは大きな問題ではありません。単に混乱して、なぜ期待したとおりに動作しないのかを理解できなくなる可能性があるだけです。


最後に

本記事では、取引サーバー上の未決済ポジションを監視するために、インジケータをどのように使用できるのかを、できるだけシンプルかつ明確に示そうとしました。このように段階的に進めたのは、これらすべてをEAに組み込む必要はないことを示すためです。皆さんの多くは、何らかの理由で、このような処理をEAでおこなうことに慣れているかもしれません。しかし、実際には、その方法にはあまり意味がありません。なぜなら、この実装を発展させていくにつれて、サーバー上に登録されているポジションや注文、あるいは私たちの場合であればリプレイ/シミュレーションシステム内のポジションや注文を追跡したり、操作したりするために、さまざまな種類のインジケータを作成、実装できることが明らかになるからです。

ただし、ここでの主な目的はリプレイ/シミュレーションシステムでこのインジケータを使用することですが、1つ気づいてほしいことがあります。私たちはまだ、このインジケータをシミュレートされた銘柄や、リプレイを実行している状況には適用していません。現在、私は実際の銘柄と実際のポジションを使って、このインジケータを使用し、テストしています。したがって、このインジケータをリプレイ/シミュレーションシステムだけで使用する理由はありません。ただし、使用する際には、次の点に注意してください。このインジケータが表示できるのは、現時点では1つのポジションだけです。ヘッジ口座の場合、2つ以上の未決済ポジションが存在するからといって、それだけでインジケータが正常に動作すると考えることはできません。現時点では、まだそのような処理には対応していないからです。

近いうちに、このインジケータを正しく使用し、複数の未決済ポジションを表示できるようにする方法を説明します。ネッティング口座の場合は、この問題は発生しません。ネッティング口座では、同じ銘柄を同時に買いと売りの両方で保有することができないからです。一方、ヘッジ口座ではこれが可能です。この具体的なケースでは、このインジケータの使用方法を少し調整する必要があります。しかし、その実装コストは非常に低いので、正直なところ、この処理をEAから切り離してインジケータ側に任せることは、十分に検討する価値があります。なぜなら、私はまだこのインジケータのすべての機能を紹介していないからです。したがって、次回の記事では、このインジケータを少し改善します。というのも、ここには小さな欠点があるからです。その問題が何なのか知りたいのであれば、次回の記事をお読みください。

ファイル説明
Experts\Expert Advisor.mq5
Chart TradeとEA間の相互作用をデモンストレーションする(相互作用にはMouse Studyが必要)
Indicators\Chart Trade.mq5送信する注文を設定するウィンドウを作成する(Mouse Studyが必要)
Indicators\Market Replay.mq5リプレイ/シミュレーションサービスと対話するためのコントロールを作成する(Mouse Studyが必要)
Indicators\Mouse Study.mq5グラフィカルコントロールとユーザーとの相互作用を実現する(リプレイシステムと実市場での運用の両方に必要)
Indicators\Order Indicator.mq5市場における注文の表示、それらとのやり取りや管理を担当する
Indicators\Position View.mq5市場におけるポジションを表示し、それらとのやり取りや管理を担当する
Services\Market Replay.mq5市場リプレイ/シミュレーションサービス(システム全体のメインファイル)を作成および維持する

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

添付されたファイル |
Anexo.zip (779.24 KB)
EAのサンプル EAのサンプル
一般的なMACDを使ったEAを例として、MQL4開発の原則を紹介します。
初級から中級まで:オブジェクトイベント(IV) 初級から中級まで:オブジェクトイベント(IV)
前回の記事で始めた内容を完成させます。つまり、チャート上でオブジェクトを直接操作してサイズを変更できる、完全にインタラクティブな方法を実現します。このようなものを作成するには、MQL5についてもっと深い知識が必要だと、多くの人は考えるかもしれません。しかし実際には、基本的な知識とシンプルな概念を使うことで、チャート上のオブジェクトを直接操作する方法を実装できます。そして、その結果は非常に興味深く、かなり魅力的なものになります。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
Extremal Optimization (EO)アルゴリズム Extremal Optimization (EO)アルゴリズム
極値最適化(EO, Extremal Optimization)アルゴリズムについて説明します。EOは、Bak-Sneppenの自己組織化臨界モデルに着想を得た最適化手法であり、システム内の最も悪い要素を選んで置き換えることで進化を促すという考え方に基づいています。EOの改良版である集団型アルゴリズムでは、理論的な原則から離れ、実用的な効率性を重視することで、強力な計算ツールが開発されています。