市場シミュレーション(第24回):ポジション表示(II)
はじめに
皆さん、こんにちは。そして、リプレイ/シミュレーションシステムの作成方法に関する連載の新しい記事へようこそ。
前回の「市場シミュレーション:ポジション表示(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
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
初級から中級まで:オブジェクトイベント(IV)
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
Extremal Optimization (EO)アルゴリズム
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索