English Русский 中文 Español Deutsch Português
preview
市場シミュレーション(第3回):パフォーマンスの問題

市場シミュレーション(第3回):パフォーマンスの問題

MetaTrader 5 |
75 0
Daniel Jose
Daniel Jose

はじめに

前回の「市場シミュレーション(第2回):両建て注文(II)」では、Chart Tradeでどの資産や契約を監視するかを制御する方法を示しました。これは、取引対象の契約に直接アクセスせずにシステムを使用する場合に適用されます。代わりに別の資産を用いて操作をおこないます。その資産とは、実際には契約の履歴データであり、その理由については前回の記事で説明しました。

とはいえ、システム全体の開発はかなり高度な段階に進んでいるものの、常に安全性、信頼性、パフォーマンスの適切なレベルを維持することが必要です。これは決して簡単な作業ではありません。むしろ、非常に複雑です。遅かれ早かれ、何らかの不具合や問題に直面することは避けられないでしょう。そのような場面では、システム全体がどのように実装されているかを完全に理解していることが極めて重要です。各部分が何をしているのか理解していない場合、事態は急速に悪化し、行き詰まることがあります。

ここ数週間にわたり、実際に必要となるアプリケーションの開発を進めてきました。しかし、新しいアプリケーションを実装し始めると、問題が発生し始めました。これまで観察されていたパフォーマンスに比べ、システムの性能が著しく低下したのです。私はシステムをモノリシックに構築するつもりはないため、問題の原因を分析し特定する必要がありました。

フローチャートを確認していると、いくつかの穴に気付きました。これらの穴は、放置すると必ず問題を引き起こすため、閉じる必要があります。この課題を解決し、再発を防ぐことも最終的なアプリケーション開発の一部です。さらに、後述するように、マウスインジケーター使用時に見られる小さな欠陥もあります。この欠陥はこれまで見過ごされていましたが、詳細に検証した結果、発見し、修正しました。読者の皆さんは、この欠陥が具体的にどのようなものかをご確認いただけます。

こうした詳細は、他の多くの記事では省略されたり説明されなかったりすることが多く、これにより、初心者のプログラマが「プログラマはミスをしない」「コードは完璧に生まれ、エラーなく成長する」と誤解してしまうことがあります。実際には、どれだけ慎重に設計されたコードであっても、エラーは避けられません。本記事は説明を目的とするだけでなく、「コードは完成することがなく、常に改善や修正の余地がある」ということを示すことも意図しています。

それでは、本題に入り、新しいトピックから解説を始めます。


カプセル化の改善

ソフトウェア開発において最も深刻な問題のひとつが、情報の漏洩です。この漏洩はさまざまな形で発生します。しかし、ここで問題となっているのは非常に特定のケースです。本システムのコードは、クラスを多用したオブジェクト指向プログラミングを前提として設計されています。漏洩は、クラス、もしくは特定のクラスを使用するアプリケーションが、本来アクセスすべきでないものにアクセスできてしまう場合に発生します。こうした欠陥は、通常カプセル化の不備や、本来所属すべきでないクラスに関数やプロシージャが存在することに起因します。今回のケースでは、特定のコード以外からアクセスされるべきでないプロシージャが存在していました。しかし、これまでこの問題に注意を払ってこなかったため、欠陥が残ったままとなっていました。

問題となっているのは、C_Mouseクラス内のSetBuffer関数です。しかし、なぜこの手続きが欠陥と見なされるのでしょうか。C_Mouseクラスに存在すること自体に本質的な問題があるのでしょうか。私はそれ自体を間違いだとは考えません。しかし、詳細に検討した結果、完全には適切ではないと判断しました。問題は、他のプログラム(インジケーターかエキスパートアドバイザー(EA)を問わず)がマウスインジケーターのバッファに書き込みできてしまうことではありません。これは、MQL5固有のセキュリティ機能により防がれています。問題は、この手続きがC_Mouseクラスに存在する意味がないことです。実際に使用しているのは、マウスインジケーター自身のみだからです。この手続きをC_Mouseクラスから移動させることで、カプセル化を改善し、マウスインジケーターだけがバッファを変更できるようになります。

クラスから手続きを削除する場合でも、以前と同じように機能することが必要です。これにより、マウスインジケーターのデータを利用する既存または過去に開発されたコードとの互換性が保証されます。次に、更新されたC_Mouseクラス(この変更以外にも修正があります)を示す前に、まずマウスインジケーターのコードを確認してみましょう。完全なコードを以下に示します。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "This is an indicator for graphical studies using the mouse."
04. #property description "This is an integral part of the Replay / Simulator system."
05. #property description "However it can be used in the real market."
06. #property version "1.82"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/pt/articles/12580"
09. #property indicator_chart_window
10. #property indicator_plots 0
11. #property indicator_buffers 1
12. //+------------------------------------------------------------------+
13. double GL_PriceClose;
14. datetime GL_TimeAdjust;
15. //+------------------------------------------------------------------+
16. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
17. //+------------------------------------------------------------------+
18. C_Study *Study       = NULL;
19. //+------------------------------------------------------------------+
20. input color user01   = clrBlack;                   //Price Line
21. input color user02   = clrPaleGreen;               //Positive Study
22. input color user03   = clrLightCoral;              //Negative Study
23. //+------------------------------------------------------------------+
24. C_Study::eStatusMarket m_Status;
25. int m_posBuff = 0;
26. double m_Buff[];
27. //+------------------------------------------------------------------+
28. int OnInit()
29. {
30.    Study = new C_Study(0, "Indicator Mouse Study", user01, user02, user03);
31.    if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
32.    MarketBookAdd((*Study).GetInfoTerminal().szSymbol);
33.    OnBookEvent((*Study).GetInfoTerminal().szSymbol);
34.    m_Status = C_Study::eCloseMarket;
35.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
36.    ArrayInitialize(m_Buff, EMPTY_VALUE);
37.    
38.    return INIT_SUCCEEDED;
39. }
40. //+------------------------------------------------------------------+
41. int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], 
42.                 const double& high[], const double& low[], const double& close[], const long& tick_volume[], 
43.                 const long& volume[], const int& spread[]) 
44. {
45.    GL_PriceClose = close[rates_total - 1];
46.    if (_Symbol == def_SymbolReplay)
47.       GL_TimeAdjust = spread[rates_total - 1] & (~def_MaskTimeService);
48.    m_posBuff = rates_total;
49.    (*Study).Update(m_Status);   
50.    
51.    return rates_total;
52. }
53. //+------------------------------------------------------------------+
54. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
55. {
56.     uCast_Double info;
57.     C_Mouse::st_Mouse sMouse;
58.     
59.    (*Study).DispatchMessage(id, lparam, dparam, sparam);
60.    sMouse = (*Study).GetInfoMouse();            
61.    info._8b[0] = (uchar)(sMouse.ExecStudy == C_Mouse::eStudyNull ? sMouse.ButtonStatus : 0);
62.    info._16b[1] = (ushort)sMouse.Position.X_Graphics;
63.    info._16b[2] = (ushort)sMouse.Position.Y_Graphics;
64.    if (m_posBuff > 0) m_Buff[m_posBuff - 1] = info.dValue;
65. 
66.    ChartRedraw((*Study).GetInfoTerminal().ID);
67. }
68. //+------------------------------------------------------------------+
69. void OnBookEvent(const string &symbol)
70. {
71.    MqlBookInfo book[];
72.    C_Study::eStatusMarket loc = m_Status;
73.    
74.    if (symbol != (*Study).GetInfoTerminal().szSymbol) return;
75.    MarketBookGet((*Study).GetInfoTerminal().szSymbol, book);
76.    m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : (symbol == def_SymbolReplay ? C_Study::eInReplay : C_Study::eInTrading));
77.    for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
78.       if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
79.    if (loc != m_Status) (*Study).Update(m_Status);
80. }
81. //+------------------------------------------------------------------+
82. void OnDeinit(const int reason)
83. {
84.    MarketBookRelease((*Study).GetInfoTerminal().szSymbol);
85. 
86.    delete Study;
87. }
88. //+------------------------------------------------------------------+

マウスポインタのソースコード

元のコードと比較すると、ほとんど変更はありません。元のコードについては、前回の記事をご参照ください。しかし、コードを注意深く追ってきた方は、OnChartEvent手続きに変更があることに気づくでしょう。以前は、同じ処理がC_Mouseクラス内のSetBuffer関数でおこなわれていました。今回この手続きを削除したことで、OnChartEvent内でバッファが正しく書き込まれることを保証しています。

では、なぜこの処理を一見自然に思えるOnCalculate関数内でおこなわないのでしょうか。その理由は、マウス関連のイベントを扱っていることです。OnCalculateは価格に関する計算処理を目的として設計されています。一方で、マウスインジケーターの目的はマウス関連のデータを提供することなので、より適切な関数を使用する必要があります。

そのため、SetBuffer内にあったほぼ同じコードは、現在では61行目から64行目に配置されています。ただし、64行目では、以前は実行されていなかった追加のテストを行う必要があります。このテストは、リプレイ/シミュレーター環境でのマウスインジケーターに関する実験中に観察された特異な不具合に対応するものです。このテストを64行目で実行することで、これらの不具合を回避できます。このテストの実装により、タイミングよく別の問題も解決されました。

さらに、マウスインジケーターコードの改善に加えて、あと2つの変更点も紹介したいと思います。これらはまだ実運用では使用されていませんが、リプレイ/シミュレーターシステムに他のアプリケーションを統合するため、すでに実装されています。今後の記事でこれらの変更点について触れることもありますが、触れない場合でも、事前に実装しておくことで繰り返し修正する必要がなくなります。特定の状況で役立つ可能性があり、同様の機能を実装したい場合には、すでに利用できる状態になっています。

1つ目は、macros.mqhを含むヘッダーファイルです。完全なファイルを以下に示します。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define macroRemoveSec(A)            (A - (A % 60))
05. #define macroGetDate(A)              (A - (A % 86400))
06. #define macroGetSec(A)               (A - (A - (A % 60)))
07. #define macroGetTime(A)              (A % 86400)
08. #define macroGetMin(A)               (int)((A - (A - ((A % 3600) - (A % 60)))) / 60)
09. #define macroGetHour(A)              (A - (A - ((A % 86400) - (A % 3600))))
10. #define macroHourBiggerOrEqual(A, B) ((A * 3600) < (B - (B - ((B % 86400) - (B % 3600)))))
11. #define macroMinusMinutes(A, B)      (B - ((A * 60) + (B % 60)))
12. #define macroMinusHours(A, B)        (B - (A * 3600))
13. #define macroAddHours(A, B)          (B + (A * 3600))
14. #define macroAddMin(A, B)            (B + (A * 60))
15. #define macroSetHours(A, B)          ((A * 3600) + (B - ((B % 86400))))
16. #define macroSetMin(A, B)            ((A * 60) + (B - (B % 3600)))
17. #define macroSetTime(A, B, C)        ((A * 3600) + (B * 60) + (C - (C % 86400)))
18. //+------------------------------------------------------------------+
19. #define macroColorRGBA(A, B) ((uint)((B << 24) | (A & 0x00FF00) | ((A & 0xFF0000) >> 16) | ((A & 0x0000FF) << 16)))
20. #define macroTransparency(A) (((A > 100 ? 100 : (100 - A)) * 2.55) / 255.0)
21. //+------------------------------------------------------------------+

ヘッダーファイル:Macros.mqh

このファイルの多くは、datetime型やulong型を用いた日時の操作に重点を置いていますが、これらの関数は、そうした操作に関連する幅広いタスクに非常に有用です。なぜなら、計算を直接行う方が、同じ結果を得るためにMQL5ライブラリ関数を呼び出すよりもプロセッサにとって高速だからです。これらの計算の有用性はすぐには実感できないかもしれませんが、とにかく、macros.mqhファイルには今後、この内容が含まれることになります。

もう一つ変更があったヘッダーファイルはdefines.mqhです。ただし、変更は最小限で、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,                     //Event of tic-tac
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 sales event 
42.          evChartTradeCloseAll,         //Event to close positions
43.          evChartTrade_At_EA,           //Event to communication
44.          evEA_At_ChartTrade            //Event to communication
45.                   };
46. //+------------------------------------------------------------------+

ヘッダーファイル:Defines.mqh

問題となる行は30行目です。この行を列挙型の先頭に追加することで、すべてが変わります。このイベント、いわゆる「Tic-Tac」は同期イベントです。現在のところ、リプレイ/シミュレーターシステムのコードでは実際には使用されていません。しかし、複数のコンポーネント間で同期が必要になった際には、このイベントが非常に重要になります。この追加は、前述したように、私が個人的に開発しているアプリケーションに関連しています。すべてのプロジェクトをリプレイ/シミュレーターシステムに統合する際に必要なものです。

したがって、このTic-Tacイベントは、現時点では読者の皆様にとって、リプレイ/シミュレーターコードの一部として完全には理解されないかもしれません。しかし心配はいりません。将来的には、このイベントを利用したアプリケーションも紹介できるかもしれません。

以上で本セクションは終了です。次のセクションでは、システムのパフォーマンス低下を防ぐことを主な焦点として解説します。この問題はすでに兆候が現れ始めています。話題を明確に分けるために、新しいセクションに進みましょう。


パフォーマンス低下の軽減

長い間、システムは十分なパフォーマンスを維持していました。しかし、新しい段階に入り、特定の機能をより広範に利用するようになると、リプレイ/シミュレーターシステム全体でパフォーマンスの低下が観察されました。この現象は、開発したプログラムをライブ口座やデモ口座で使用した場合でも発生します。このパフォーマンス低下の原因はごく限定的な要因であり、以下に原因を説明し、少なくとも初期段階で有効な解決策を提案します。

全体的なパフォーマンス低下の主な原因は、マウスインジケーターです。なぜでしょうか。マウスインジケーターが予想以上にリソースを消費しているわけではありません。問題は、インジケーターバッファをより集中的に読み込むと、システム全体が遅くなり始めることにあります。

この原因は、C_Mouseクラス内の手続きにあります。具体的には、インジケーターバッファを読み込む際に、小さな遅延が蓄積し始めます。この遅延は、システムがグラフィカル座標を価格・時間座標に変換する際に発生します。この変換が頻繁におこなわれると、マウスインジケーターに関連する価格ラインの位置に目に見える遅れが生じます。

解決策は、この影響を分離することです。方法としては、バッファの読み取り頻度を減らすか、別の手法を採用することも可能です。システム全体がグラフィカルであるため、マウスの利用を完全に排除することはできません。しかし、極端な手段に頼らずに解決する方法があります。

重要なのは、マウスインジケーターがスタディ(分析)の期間とそうでない期間を区別することです。これがマウスインジケーター作成の根本的な理由です。スタディが進行中の場合、チャート上のアプリケーションはクリックを無視する必要があります。

マウスが現在どの状態にあるか(アクティブかスタディモードか)を判断するために、CheckClick関数を使用します。この関数がtrueを返す場合は、マウス位置を利用できます。falseを返す場合は、マウスはスタディモードにあり、この場合、マウスの状態を認識しているアプリケーションは位置情報を無視する必要があります。

この仕組みを実現するためには、マウスインジケーターがチャート上に存在している必要があります。これらの課題をすべて解決するには、いくつかの変更が必要でした。ただし、ほとんどの変更は、機能をあるクラスから別のクラスに移動するという内容であるため、細かい部分の説明は省略します。

理解しておくべきことは、以前はすべての処理がC_Mouseクラス内でおこなわれ、マウスインジケーターを使用する任意のアプリケーションが必要なデータにアクセスできるようになっていた点です。現在は、この処理がC_MouseとC_Terminalの間で分割されています。しかし、チャート上にマウスインジケーターが存在しなければ、これに依存するアプリケーションは完全には機能しません。これまでに開発されたアプリケーションを反映した新しいクラス階層は、以下の図の通りです。

画像

この図は、更新されたクラス構造を示しています。これが現在の基本構造です。各長方形は異なるアプリケーションを表しています。C_ChartFloatingRADはC_Mouseを継承していますが、チャート上にマウスインジケーターが存在しない場合、Chart Tradeインジケーターは機能しません。

一見すると直感に反するかもしれません。C_ChartFloatingRADがC_Mouseを継承しているため、Chart Tradeは独立して動作するように思えます。しかし、これらのインジケーターの設計上、Chart Tradeはマウスインジケーターに依存しています。マウスインジケーターが存在しなければ、ユーザーはChart Tradeを利用できません。

この挙動は意図的なものであり、妥当と判断されています。キーボードおよびマウスの操作は、C_TerminalおよびC_Mouseに関連付けられています。これらのクラスは、全体的なパフォーマンス低下を最小限に抑えるよう設計されています。それでも必要に応じて、マウスインジケーターのバッファにはChart Tradeインジケーターや他のアプリケーションがアクセスできるデータが格納されています。

基本的な考え方は、グラフィカルターミナル内のどのコンポーネントも、C_Mouseのデータにできるだけ迅速にアクセスできるようにすることです。このデータを別のアプリケーションやサービスで利用する場合、バッファを外部から読み取ることで計算負荷を分散させ、システム全体のパフォーマンスを維持できます。

さらに、関数呼び出しの回数を減らすためのいくつかの変更もおこなわれました。多くの場合、値がアプリケーションのライフサイクル中に変化しない場合は、privateクラス変数に保持できます。同じ値を取得するために関数を繰り返し実行するよりも、変数を使用する方が望ましいです。

その結果、チャートIDを取得する多くの呼び出しは、クラスコンストラクタで初期化された変数への参照に置き換えられました。この値はチャートが開いている間に変わりません。わずか数プロセッササイクルの節約に過ぎませんが、関数呼び出しが多い場合、このような最適化は大きな違いを生みます。

個々の変更は劇的なパフォーマンス向上をもたらさないかもしれませんが、積み重なることで大きな効果が得られます。

最後に、新しいコードがどのように構成されているかを見てみましょう。本連載の目的は単にソースコードを提供することではなく、知識を伝えることにあります。これまで通り、各変更点について丁寧に説明していきます。アプリケーションは図に示されたように複数のファイルで構成されているため、コードを記事内にそのまま掲載すると、読者が理解せずにコピー&ペーストしてしまう恐れがあります。

このアプローチについて初心者の方にはお詫びします。私の目的は、CTRL+C/CTRL+Vによるプログラミングを推奨することではありません。この方法はしばしばコードを壊す原因となります。代わりに、根本的なロジックを理解してほしいのです。ある程度プログラミング知識がある方は、ソースに容易にアクセスできます。まず、C_Terminalクラスから始めます。完全な新しいクラスを以下に示します。

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "Macros.mqh"
005. #include "..\Defines.mqh"
006. //+------------------------------------------------------------------+
007. class C_Terminal
008. {
009. //+------------------------------------------------------------------+      
010.    public   :
011. //+------------------------------------------------------------------+      
012.       struct st_Mouse
013.       {
014.          struct st00
015.          {
016.             short    X_Adjusted,
017.                      Y_Adjusted,
018.                      X_Graphics,
019.                      Y_Graphics;
020.             double   Price;
021.             datetime dt;
022.          }Position;
023.          uchar      ButtonStatus;
024.          bool       ExecStudy;
025.       };
026. //+------------------------------------------------------------------+
027.    protected:
028.       enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance};
029. //+------------------------------------------------------------------+
030.       struct st_Terminal
031.       {
032.          ENUM_SYMBOL_CHART_MODE   ChartMode;
033.          ENUM_ACCOUNT_MARGIN_MODE TypeAccount;
034.          long           ID;
035.          string         szSymbol;
036.          int            Width,
037.                         Height,
038.                         nDigits,
039.                         SubWin,
040.                         HeightBar;
041.          double         PointPerTick,
042.                         ValuePerPoint,
043.                         VolumeMinimal,
044.                         AdjustToTrade;
045.       };
046. //+------------------------------------------------------------------+
047.       void CurrentSymbol(bool bUsingFull)
048.          {
049.             MqlDateTime mdt1;
050.             string sz0, sz1;
051.             datetime dt = macroGetDate(TimeCurrent(mdt1));
052.             enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER;
053.       
054.             sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
055.             for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS);
056.             switch (eTS)
057.             {
058.                case DOL   :
059.                case WDO   : sz1 = "FGHJKMNQUVXZ"; break;
060.                case IND   :
061.                case WIN   : sz1 = "GJMQVZ";       break;
062.                default    : return;
063.             }
064.             sz0 = EnumToString((eTypeSymbol)(((eTS & 1) == 1) ? (bUsingFull ? eTS : eTS - 1) : (bUsingFull ? eTS + 1: eTS)));
065.             for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0))
066.                if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break;
067.          }
068. //+------------------------------------------------------------------+
069. inline void DecodeMousePosition(int xi, int yi)
070.          {
071.             int w = 0;
072.             
073.             xi = (xi > 0 ? xi : 0);
074.             yi = (yi > 0 ? yi : 0);
075.             ChartXYToTimePrice(m_Infos.ID, m_Mouse.Position.X_Graphics = (short)xi, m_Mouse.Position.Y_Graphics = (short)yi, w, m_Mouse.Position.dt, m_Mouse.Position.Price);
076.             m_Mouse.Position.dt = AdjustTime(m_Mouse.Position.dt);
077.             m_Mouse.Position.Price = AdjustPrice(m_Mouse.Position.Price);
078.             ChartTimePriceToXY(m_Infos.ID, w, m_Mouse.Position.dt, m_Mouse.Position.Price, xi, yi);
079.             yi -= (int)ChartGetInteger(m_Infos.ID, CHART_WINDOW_YDISTANCE, m_Infos.SubWin);
080.             m_Mouse.Position.X_Adjusted = (short) xi;
081.             m_Mouse.Position.Y_Adjusted = (short) yi;
082.          }
083. //+------------------------------------------------------------------+
084.    private   :
085.       st_Terminal m_Infos;
086.       st_Mouse    m_Mouse;
087.       struct mem
088.       {
089.          long    Show_Descr,
090.                  Show_Date;
091.          bool    AccountLock;
092.       }m_Mem;
093. //+------------------------------------------------------------------+
094. inline void ChartChange(void)
095.          {
096.             int x, y, t;
097.             
098.             m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
099.             m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
100.             ChartTimePriceToXY(m_Infos.ID, 0, 0, 0, x, t);
101.             ChartTimePriceToXY(m_Infos.ID, 0, 0, m_Infos.PointPerTick * 100, x, y);
102.             m_Infos.HeightBar = (int)(t - y) / 100;
103.          }
104. //+------------------------------------------------------------------+
105.    public   :
106. //+------------------------------------------------------------------+      
107.       C_Terminal(const long id = 0, const uchar sub = 0)
108.          {
109.             m_Infos.ID = (id == 0 ? ChartID() : id);
110.             m_Mem.AccountLock = false;
111.             m_Infos.SubWin = (int) sub;
112.             CurrentSymbol(false);
113.             ZeroMemory(m_Mouse);
114.             m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
115.             m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
116.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
117.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, true);
118.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, true);
119.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
120.             m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
121.             m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
122.             m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
123.             m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
124.             m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
125.             m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
126.             m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
127.             m_Infos.ChartMode   = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE);
128.             if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE));
129.             ChartChange();
130.          }
131. //+------------------------------------------------------------------+
132.       ~C_Terminal()
133.          {
134.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, m_Mem.Show_Date);
135.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, m_Mem.Show_Descr);
136.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, false);
137.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, false);
138.          }
139. //+------------------------------------------------------------------+
140. inline void SetTypeAccount(const ENUM_ACCOUNT_MARGIN_MODE arg)
141.          {
142.             if (m_Mem.AccountLock) return; else m_Mem.AccountLock = true;
143.             m_Infos.TypeAccount = (arg == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? arg : ACCOUNT_MARGIN_MODE_RETAIL_NETTING);
144.          }
145. //+------------------------------------------------------------------+
146. inline const st_Terminal GetInfoTerminal(void) const
147.          {
148.             return m_Infos;
149.          }
150. //+------------------------------------------------------------------+
151. inline const st_Mouse GetPositionsMouse(void) const
152.          {
153.             return m_Mouse;
154.          }
155. //+------------------------------------------------------------------+
156. const double AdjustPrice(const double arg) const
157.          {
158.             return NormalizeDouble(round(arg / m_Infos.PointPerTick) * m_Infos.PointPerTick, m_Infos.nDigits);
159.          }
160. //+------------------------------------------------------------------+
161. inline datetime AdjustTime(const datetime arg)
162.          {
163.             int nSeconds= PeriodSeconds();
164.             datetime   dt = iTime(m_Infos.szSymbol, PERIOD_CURRENT, 0);
165.             
166.             return (dt < arg ? ((datetime)(arg / nSeconds) * nSeconds) : iTime(m_Infos.szSymbol, PERIOD_CURRENT, Bars(m_Infos.szSymbol, PERIOD_CURRENT, arg, dt)));
167.          }
168. //+------------------------------------------------------------------+
169. inline double FinanceToPoints(const double Finance, const uint Leverage)
170.          {
171.             double volume = m_Infos.VolumeMinimal + (m_Infos.VolumeMinimal * (Leverage - 1));
172.             
173.             return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade)));
174.          };
175. //+------------------------------------------------------------------+
176.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
177.          {
178.             static string st_str = "";
179.             
180.             switch (id)
181.             {
182.                case CHARTEVENT_CHART_CHANGE:
183.                   m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
184.                   m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
185.                   ChartChange();
186.                   break;
187.                case CHARTEVENT_MOUSE_MOVE:
188.                   DecodeMousePosition((int)lparam, (int)dparam);
189.                   break;
190.                case CHARTEVENT_OBJECT_CLICK:
191.                   if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
192.                   if (ObjectGetInteger(m_Infos.ID, sparam, OBJPROP_SELECTABLE) == true)
193.                      ObjectSetInteger(m_Infos.ID, st_str = sparam, OBJPROP_SELECTED, true);
194.                   break;
195.                case CHARTEVENT_OBJECT_CREATE:
196.                   if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
197.                   st_str = sparam;
198.                   break;
199.             }
200.          }
201. //+------------------------------------------------------------------+
202. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const int zOrder = -1) 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.             ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n");
207.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false);
208.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor);
209.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false);
210.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false);
211.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder);
212.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true);
213.          }
214. //+------------------------------------------------------------------+
215.       bool IndicatorCheckPass(const string szShortName)
216.          {
217.             string szTmp = szShortName + "_TMP";
218.             
219.             IndicatorSetString(INDICATOR_SHORTNAME, szTmp);
220.             m_Infos.SubWin = ((m_Infos.SubWin = ChartWindowFind(m_Infos.ID, szTmp)) < 0 ? 0 : m_Infos.SubWin);
221.             if (ChartIndicatorGet(m_Infos.ID, m_Infos.SubWin, szShortName) != INVALID_HANDLE)
222.             {
223.                ChartIndicatorDelete(m_Infos.ID, 0, szTmp);
224.                Print("Only one instance is allowed...");
225.                SetUserError(C_Terminal::ERR_NoMoreInstance);
226.                
227.                return false;
228.             }
229.             IndicatorSetString(INDICATOR_SHORTNAME, szShortName);
230.    
231.             return true;
232.          }
233. //+------------------------------------------------------------------+
234. };

ヘッダーファイル:C_Terminal.mqh

このコードを初めて見る方は、それが大きくて複雑だと思うかもしれません。しかし、実際には多くの部分が以前のまま維持されています。ただし、現在ではC_Mouseクラスがその処理を実行する際に支援するよう変更が加えられています。そのため、DispatchMessage手続き内でCHARTEVENT_MOUSE_MOVEイベントが追加されているのが確認できます。主要な処理はここでおこなわれます。

12行目には、マウスデータを提供するための構造体が定義されています。ただし、69行目にあるDecodeMousePosition手続きでは、マウスボタンに関するデータは設定されません。この処理の責任はC_Mouseクラスに移されています。したがって、次に確認すべき項目は、以下に示すC_Mouseクラスとなります。

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_Terminal.mqh"
005. //+------------------------------------------------------------------+
006. #define def_MousePrefixName "MouseBase" + (string)GetInfoTerminal().SubWin + "_"
007. #define macro_NameObjectStudy (def_MousePrefixName + "T" + (string)ObjectsTotal(0))
008. //+------------------------------------------------------------------+
009. class C_Mouse : public C_Terminal
010. {
011.    public   :
012.       enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
013.       enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10};
014. //+------------------------------------------------------------------+
015.    protected:
016. //+------------------------------------------------------------------+
017.       void CreateObjToStudy(int x, int w, string szName, color backColor = clrNONE) const
018.          {
019.             if (!m_Mem.IsInitOk) return;
020.             CreateObjectGraphics(szName, OBJ_BUTTON);
021.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_STATE, true);
022.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_BORDER_COLOR, clrBlack);
023.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_COLOR, clrBlack);
024.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_BGCOLOR, backColor);
025.             ObjectSetString(m_Mem.id, szName, OBJPROP_FONT, "Lucida Console");
026.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_FONTSIZE, 10);
027.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
028.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_XDISTANCE, x);
029.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1);
030.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_XSIZE, w); 
031.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_YSIZE, 18);
032.          }
033. //+------------------------------------------------------------------+
034.    private   :
035.       enum eStudy {eStudyNull, eStudyCreate, eStudyExecute};
036.       struct st01
037.       {
038.          st_Mouse Data;
039.          color    corLineH,
040.                   corTrendP,
041.                   corTrendN;
042.          eStudy   Study;
043.       }m_Info;
044.       struct st_Mem
045.       {
046.          bool     CrossHair,
047.                   IsFull,
048.                   IsInitOk;
049.          datetime dt;
050.          string   szShortName,
051.                   szLineH,
052.                   szLineV,
053.                   szLineT,
054.                   szBtnS;
055.          long     id;
056.       }m_Mem;
057. //+------------------------------------------------------------------+
058.       void GetDimensionText(const string szArg, int &w, int &h)
059.          {
060.             TextSetFont("Lucida Console", -100, FW_NORMAL);
061.             TextGetSize(szArg, w, h);
062.             h += 5;
063.             w += 5;
064.          }
065. //+------------------------------------------------------------------+
066.       void CreateStudy(void)
067.          {
068.             if (m_Mem.IsFull)
069.             {
070.                CreateObjectGraphics(m_Mem.szLineV = macro_NameObjectStudy, OBJ_VLINE, m_Info.corLineH);
071.                CreateObjectGraphics(m_Mem.szLineT = macro_NameObjectStudy, OBJ_TREND, m_Info.corLineH);
072.                ObjectSetInteger(m_Mem.id, m_Mem.szLineT, OBJPROP_WIDTH, 2);
073.                CreateObjToStudy(0, 0, m_Mem.szBtnS = macro_NameObjectStudy);
074.             }
075.             m_Info.Study = eStudyCreate;
076.          }
077. //+------------------------------------------------------------------+
078.       void ExecuteStudy(const double memPrice)
079.          {
080.             double v1 = GetPositionsMouse().Position.Price - memPrice;
081.             int w, h;
082.             
083.             if (!CheckClick(eClickLeft))
084.             {
085.                m_Info.Study = eStudyNull;
086.                ChartSetInteger(m_Mem.id, CHART_MOUSE_SCROLL, true);
087.                if (m_Mem.IsFull)   ObjectsDeleteAll(m_Mem.id, def_MousePrefixName + "T");
088.             }else if (m_Mem.IsFull)
089.             {
090.                string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ",
091.                                          MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetPositionsMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0));
092.                GetDimensionText(sz1, w, h);
093.                ObjectSetString(m_Mem.id, m_Mem.szBtnS, OBJPROP_TEXT, sz1);                                                
094.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP));
095.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_XSIZE, w);
096.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_YSIZE, h);
097.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_XDISTANCE, GetPositionsMouse().Position.X_Adjusted - w);
098.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - (v1 < 0 ? 1 : h));            
099.                ObjectMove(m_Mem.id, m_Mem.szLineT, 1, GetPositionsMouse().Position.dt, GetPositionsMouse().Position.Price);
100.                ObjectSetInteger(m_Mem.id, m_Mem.szLineT, OBJPROP_COLOR, (memPrice > GetPositionsMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
101.             }
102.             m_Info.Data.ButtonStatus = eKeyNull;
103.          }
104. //+------------------------------------------------------------------+
105.    public   :
106. //+------------------------------------------------------------------+
107.       C_Mouse(const long id, const string szShortName)
108.          :C_Terminal(id)
109.          {
110.             m_Mem.IsInitOk = false;
111.             m_Mem.id = GetInfoTerminal().ID;
112.             m_Mem.szShortName = szShortName;
113.          }
114. //+------------------------------------------------------------------+
115.       C_Mouse(const long id, const string szShortName, color corH, color corP, color corN)
116.          :C_Terminal(id)
117.          {
118.             m_Mem.id = GetInfoTerminal().ID;
119.             if (!(m_Mem.IsInitOk = IndicatorCheckPass(m_Mem.szShortName = szShortName))) return;
120.             m_Mem.CrossHair = (bool)ChartGetInteger(m_Mem.id, CHART_CROSSHAIR_TOOL);
121.             ChartSetInteger(m_Mem.id, CHART_EVENT_MOUSE_MOVE, true);
122.             ChartSetInteger(m_Mem.id, CHART_CROSSHAIR_TOOL, false);
123.             ZeroMemory(m_Info);
124.             m_Info.corLineH  = corH;
125.             m_Info.corTrendP = corP;
126.             m_Info.corTrendN = corN;
127.             m_Info.Study = eStudyNull;
128.             if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE))
129.                CreateObjectGraphics(m_Mem.szLineH = (def_MousePrefixName + (string)ObjectsTotal(0)), OBJ_HLINE, m_Info.corLineH);
130.             ChartRedraw(m_Mem.id);
131.          }
132. //+------------------------------------------------------------------+
133.       ~C_Mouse()
134.          {
135.             if (!m_Mem.IsInitOk) return;
136.             ChartSetInteger(m_Mem.id, CHART_EVENT_OBJECT_DELETE, false);
137.             ChartSetInteger(m_Mem.id, CHART_EVENT_MOUSE_MOVE, ChartWindowFind(m_Mem.id, m_Mem.szShortName) != -1);
138.             ChartSetInteger(m_Mem.id, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair);
139.             ObjectsDeleteAll(m_Mem.id, def_MousePrefixName);
140.          }
141. //+------------------------------------------------------------------+
142. inline bool CheckClick(const eBtnMouse value) 
143.          {
144.             return (m_Info.Data.ButtonStatus & value) == value;
145.          }
146. //+------------------------------------------------------------------+
147. inline const st_Mouse GetInfoMouse(void)
148.          {
149.             if (!m_Mem.IsInitOk)
150.             {
151.                double Buff[];
152.                uCast_Double loc;
153.                int handle = ChartIndicatorGet(m_Mem.id, 0, m_Mem.szShortName);
154. 
155.                ZeroMemory(m_Info.Data);
156.                if (CopyBuffer(handle, 0, 0, 1, Buff) == 1)
157.                {
158.                   loc.dValue = Buff[0];
159.                   DecodeMousePosition((int)loc._16b[1], (int)loc._16b[2]);
160.                   m_Info.Data = GetPositionsMouse();
161.                   m_Info.Data.ButtonStatus = loc._8b[0];
162.                }
163.                IndicatorRelease(handle);
164.             }
165.             return m_Info.Data;
166.          }
167. //+------------------------------------------------------------------+*/
168.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
169.          {
170.             int w = 0;
171.             static double memPrice = 0;
172.       
173.             C_Terminal::DispatchMessage(id, lparam, dparam, sparam);
174.             switch (id)
175.             {
176.                case (CHARTEVENT_CUSTOM + evHideMouse):
177.                   if (m_Mem.IsFull)   ObjectSetInteger(m_Mem.id, m_Mem.szLineH, OBJPROP_COLOR, clrNONE);
178.                   break;
179.                case (CHARTEVENT_CUSTOM + evShowMouse):
180.                   if (m_Mem.IsFull) ObjectSetInteger(m_Mem.id, m_Mem.szLineH, OBJPROP_COLOR, m_Info.corLineH);
181.                   break;
182.                case CHARTEVENT_MOUSE_MOVE:
183.                   m_Info.Data = GetPositionsMouse();
184.                   if (m_Mem.IsFull) ObjectMove(m_Mem.id, m_Mem.szLineH, 0, 0, m_Info.Data.Position.Price);
185.                   if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(m_Mem.id, m_Mem.szLineV, 0, m_Info.Data.Position.dt, 0);
186.                   m_Info.Data.ButtonStatus = (uchar) sparam;
187.                   if (CheckClick(eClickMiddle) && (m_Info.Study != eStudyCreate))
188.                      if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(m_Mem.id, m_Mem.szLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
189.                   if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
190.                   {
191.                      ChartSetInteger(m_Mem.id, CHART_MOUSE_SCROLL, false);
192.                      if (m_Mem.IsFull)   ObjectMove(m_Mem.id, m_Mem.szLineT, 0, m_Mem.dt = m_Info.Data.Position.dt, memPrice = m_Info.Data.Position.Price);
193.                      m_Info.Study = eStudyExecute;
194.                   }
195.                   if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
196.                   m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute;
197.                   break;
198.                case CHARTEVENT_OBJECT_DELETE:
199.                   if ((m_Mem.IsFull) && (sparam == m_Mem.szLineH))
200.                      CreateObjectGraphics(m_Mem.szLineH, OBJ_HLINE, m_Info.corLineH);
201.                   break;
202.             }
203.          }
204. //+------------------------------------------------------------------+
205. };
206. //+------------------------------------------------------------------+
207. #undef macro_NameObjectStudy
208. //+------------------------------------------------------------------+

ヘッダーファイル:C_Mouse.mqh

このクラスは一見すると変更されていないように見えますが、実際にはいくつかの修正が加えられています。これらの変更は、マウスインジケーターの使用をより明確にすることを目的としています。コードをよく見ると、いくつかの違いが確認できるでしょう。前のバージョンのC_Mouseクラスと比較してみることを、熱心な開発者やプロフェッショナルを目指すプログラマの皆様への課題として残しておきます。比較することで多くの興味深い点や、クラス継承に関する理解が深まり、非常に有意義で楽しい学習になるはずです。少しだけヒントを差し上げます。187行目を見て、スタディを作成する際にすでにスタディが進行中かどうかどうかを確認している理由を考えてみてください。

予想通り、C_Studyクラスのコードも修正されています。これらの変更は、定数値(この場合はチャートID)を関数呼び出しによって繰り返し取得することを防ぐためだけにおこなわれたものです。更新後のC_Studyクラスの完全なコードは、以下で確認することができます。

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\C_Mouse.mqh"
005. //+------------------------------------------------------------------+
006. #define def_ExpansionPrefix def_MousePrefixName + "Expansion_"
007. //+------------------------------------------------------------------+
008. class C_Study : public C_Mouse
009. {
010.    private   :
011. //+------------------------------------------------------------------+
012.       struct st00
013.       {
014.          eStatusMarket  Status;
015.          MqlRates       Rate;
016.          string         szInfo,
017.                         szBtn1,
018.                         szBtn2,
019.                         szBtn3;
020.          color          corP,
021.                         corN;
022.          int            HeightText;
023.          bool           bvT, bvD, bvP;
024.          long           id;
025.       }m_Info;
026. //+------------------------------------------------------------------+
027.       void Draw(void)
028.          {
029.             double v1;
030.             
031.             if (m_Info.bvT)
032.             {
033.                ObjectSetInteger(m_Info.id, m_Info.szBtn1, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 18);
034.                ObjectSetString(m_Info.id, m_Info.szBtn1, OBJPROP_TEXT, m_Info.szInfo);
035.             }
036.             if (m_Info.bvD)
037.             {
038.                v1 = NormalizeDouble((((GetPositionsMouse().Position.Price - m_Info.Rate.close) / m_Info.Rate.close) * 100.0), 2);
039.                ObjectSetInteger(m_Info.id, m_Info.szBtn2, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 1);
040.                ObjectSetInteger(m_Info.id, m_Info.szBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
041.                ObjectSetString(m_Info.id, m_Info.szBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
042.             }
043.             if (m_Info.bvP)
044.             {
045.                v1 = NormalizeDouble((((GL_PriceClose - m_Info.Rate.close) / m_Info.Rate.close) * 100.0), 2);
046.                ObjectSetInteger(m_Info.id, m_Info.szBtn3, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 1);
047.                ObjectSetInteger(m_Info.id, m_Info.szBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
048.                ObjectSetString(m_Info.id, m_Info.szBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
049.             }
050.          }
051. //+------------------------------------------------------------------+
052. inline void CreateObjInfo(EnumEvents arg)
053.          {
054.             switch (arg)
055.             {
056.                case evShowBarTime:
057.                   C_Mouse::CreateObjToStudy(2, 110, m_Info.szBtn1 = (def_ExpansionPrefix + (string)ObjectsTotal(0)), clrPaleTurquoise);
058.                   m_Info.bvT = true;
059.                   break;
060.                case evShowDailyVar:
061.                   C_Mouse::CreateObjToStudy(2, 53, m_Info.szBtn2 = (def_ExpansionPrefix + (string)ObjectsTotal(0)));
062.                   m_Info.bvD = true;
063.                   break;
064.                case evShowPriceVar:
065.                   C_Mouse::CreateObjToStudy(58, 53, m_Info.szBtn3 = (def_ExpansionPrefix + (string)ObjectsTotal(0)));
066.                   m_Info.bvP = true;
067.                   break;
068.             }
069.          }
070. //+------------------------------------------------------------------+
071. inline void RemoveObjInfo(EnumEvents arg)
072.          {
073.             string sz;
074.             
075.             switch (arg)
076.             {
077.                case evHideBarTime:
078.                   sz = m_Info.szBtn1;
079.                   m_Info.bvT = false;
080.                   break;
081.                case evHideDailyVar:
082.                   sz = m_Info.szBtn2;
083.                   m_Info.bvD   = false;
084.                   break;
085.                case evHidePriceVar:
086.                   sz = m_Info.szBtn3;
087.                   m_Info.bvP = false;
088.                   break;
089.             }
090.             ChartSetInteger(m_Info.id, CHART_EVENT_OBJECT_DELETE, false);
091.             ObjectDelete(m_Info.id, sz);
092.             ChartSetInteger(m_Info.id, CHART_EVENT_OBJECT_DELETE, true);
093.          }
094. //+------------------------------------------------------------------+
095.    public   :
096. //+------------------------------------------------------------------+
097.       C_Study(long IdParam, string szShortName, color corH, color corP, color corN)
098.          :C_Mouse(IdParam, szShortName, corH, corP, corN)
099.          {
100.             ZeroMemory(m_Info);
101.             m_Info.id = GetInfoTerminal().ID;
102.             if (_LastError >= ERR_USER_ERROR_FIRST) return;
103.             m_Info.corP = corP;
104.             m_Info.corN = corN;
105.             CreateObjInfo(evShowBarTime);
106.             CreateObjInfo(evShowDailyVar);
107.             CreateObjInfo(evShowPriceVar);
108.             ResetLastError();
109.          }
110. //+------------------------------------------------------------------+
111.       void Update(const eStatusMarket arg)
112.          {
113.             int i0;
114.             datetime dt;
115.                
116.             if (m_Info.Rate.close == 0)
117.                m_Info.Rate.close = iClose(NULL, PERIOD_D1, ((_Symbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(NULL, PERIOD_D1, 0))) ? 0 : 1));
118.             switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status))
119.             {
120.                case eCloseMarket   :
121.                   m_Info.szInfo = "Closed Market";
122.                   break;
123.                case eInReplay      :
124.                case eInTrading     :
125.                   i0 = PeriodSeconds();
126.                   dt = (m_Info.Status == eInReplay ? (datetime) GL_TimeAdjust : TimeCurrent());
127.                   m_Info.Rate.time = (m_Info.Rate.time <= dt ? (datetime)(((ulong) dt / i0) * i0) + i0 : m_Info.Rate.time);
128.                   if (dt > 0) m_Info.szInfo = TimeToString((datetime)m_Info.Rate.time - dt, TIME_SECONDS);
129.                   break;
130.                case eAuction      :
131.                   m_Info.szInfo = "Auction";
132.                   break;
133.                default            :
134.                   m_Info.szInfo = "ERROR";
135.             }
136.             Draw();
137.          }
138. //+------------------------------------------------------------------+
139. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
140.          {
141.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
142.             switch (id)
143.             {
144.                case CHARTEVENT_CUSTOM + evHideBarTime:
145.                   RemoveObjInfo(evHideBarTime);
146.                   break;
147.                case CHARTEVENT_CUSTOM + evShowBarTime:
148.                   CreateObjInfo(evShowBarTime);
149.                   break;
150.                case CHARTEVENT_CUSTOM + evHideDailyVar:
151.                   RemoveObjInfo(evHideDailyVar);
152.                   break;
153.                case CHARTEVENT_CUSTOM + evShowDailyVar:
154.                   CreateObjInfo(evShowDailyVar);
155.                   break;
156.                case CHARTEVENT_CUSTOM + evHidePriceVar:
157.                   RemoveObjInfo(evHidePriceVar);
158.                   break;
159.                case CHARTEVENT_CUSTOM + evShowPriceVar:
160.                   CreateObjInfo(evShowPriceVar);
161.                   break;
162.                case CHARTEVENT_MOUSE_MOVE:
163.                   Draw();
164.                   break;
165.             }
166.             ChartRedraw(m_Info.id);
167.          }
168. //+------------------------------------------------------------------+
169. };
170. //+------------------------------------------------------------------+
171. #undef def_ExpansionPrefix
172. #undef def_MousePrefixName
173. //+------------------------------------------------------------------+

ヘッダーファイル:C_Study.mqh

これまでに確認してきたすべてのソースコードを使用すれば、マウスインジケーターを作成できるようになります。ただし、この記事を完成させるためには、まだ2つのファイルが残っています。次回の記事では別のテーマを扱う予定のため、これらを後回しにせず、今回のうちに取り上げたいと思います。こうしておけば、すべての準備が整い、しばらくこの話題に戻る必要もなくなるでしょう。それでは、最後の2つのソースファイルを順に確認していきましょう。まずはC_ChartFloatingRADクラスからです。以下に、その完全なコードを示します。

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "../Auxiliar/C_Mouse.mqh"
005. #include "C_AdjustTemplate.mqh"
006. //+------------------------------------------------------------------+
007. #define macro_NameGlobalVariable(A) StringFormat("ChartTrade_%u%s", m_Init.id, A)
008. #define macro_CloseIndicator(A)   {           \
009.                OnDeinit(REASON_INITFAILED);   \
010.                SetUserError(A);               \
011.                return;                        \
012.                                  }
013. //+------------------------------------------------------------------+
014. class C_ChartFloatingRAD : private C_Mouse
015. {
016.    private   :
017.       enum eObjectsIDE {MSG_LEVERAGE_VALUE, MSG_TAKE_VALUE, MSG_STOP_VALUE, MSG_MAX_MIN, MSG_TITLE_IDE, MSG_DAY_TRADE, MSG_BUY_MARKET, MSG_SELL_MARKET, MSG_CLOSE_POSITION, MSG_NULL};
018.       struct st00
019.       {
020.          short    x, y, minx, miny,
021.                   Leverage;
022.          string   szObj_Chart,
023.                   szObj_Editable,
024.                   szFileNameTemplate;
025.          long     WinHandle;
026.          double   FinanceTake,
027.                   FinanceStop;
028.          bool     IsMaximized,
029.                   IsDayTrade,
030.                   IsSaveState;
031.          struct st01
032.          {
033.             short  x, y, w, h;
034.             color  bgcolor;
035.             int    FontSize;
036.             string FontName;
037.          }Regions[MSG_NULL];
038.       }m_Info;
039.       struct st01
040.       {
041.          short     y[2];
042.          bool      bOk;
043.          long      id;
044.       }m_Init;
045. //+------------------------------------------------------------------+
046.       void CreateWindowRAD(int w, int h)
047.          {
048.             m_Info.szObj_Chart = "Chart Trade IDE";
049.             m_Info.szObj_Editable = m_Info.szObj_Chart + " > Edit";
050.             ObjectCreate(m_Init.id, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0);
051.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x);
052.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y);
053.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XSIZE, w);
054.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YSIZE, h);
055.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false);
056.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false);
057.             m_Info.WinHandle = ObjectGetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_CHART_ID);
058.          };
059. //+------------------------------------------------------------------+
060.       void AdjustEditabled(C_AdjustTemplate &Template, bool bArg)
061.          {
062.             for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_STOP_VALUE; c0++)
063.                if (bArg)
064.                {
065.                   Template.Add(EnumToString(c0), "bgcolor", NULL);
066.                   Template.Add(EnumToString(c0), "fontsz", NULL);
067.                   Template.Add(EnumToString(c0), "fontnm", NULL);
068.                }
069.                else
070.                {
071.                   m_Info.Regions[c0].bgcolor = (color) StringToInteger(Template.Get(EnumToString(c0), "bgcolor"));
072.                   m_Info.Regions[c0].FontSize = (int) StringToInteger(Template.Get(EnumToString(c0), "fontsz"));
073.                   m_Info.Regions[c0].FontName = Template.Get(EnumToString(c0), "fontnm");
074.                }
075.          }
076. //+------------------------------------------------------------------+
077. inline void AdjustTemplate(const bool bFirst = false)
078.          {
079. #define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade
080.             
081.             C_AdjustTemplate    *Template;
082.             
083.             if (bFirst)
084.             {
085.                Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(m_Init.id) + ".tpl", true);
086.                for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++)
087.                {
088.                   (*Template).Add(EnumToString(c0), "size_x", NULL);
089.                   (*Template).Add(EnumToString(c0), "size_y", NULL);
090.                   (*Template).Add(EnumToString(c0), "pos_x", NULL);
091.                   (*Template).Add(EnumToString(c0), "pos_y", NULL);
092.                }
093.                AdjustEditabled(Template, true);
094.             }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate);
095.             if (_LastError >= ERR_USER_ERROR_FIRST)
096.             {
097.                delete Template;
098.                
099.                return;
100.             }
101.             m_Info.Leverage = (m_Info.Leverage <= 0 ? 1 : m_Info.Leverage);
102.             m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.Leverage));
103.             m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.Leverage));
104.             (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol);
105.             (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.Leverage));
106.             (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2));
107.             (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2));
108.             (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0"));
109.             (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0"));
110.             if (!(*Template).Execute())
111.             {
112.                delete Template;
113. 
114.                macro_CloseIndicator(C_Terminal::ERR_FileAcess);
115.             };
116.             if (bFirst)
117.             {
118.                for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++)
119.                {
120.                   m_Info.Regions[c0].x = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_x"));
121.                   m_Info.Regions[c0].y = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_y"));
122.                   m_Info.Regions[c0].w = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_x"));
123.                   m_Info.Regions[c0].h = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_y"));
124.                }
125.                m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x;
126.                AdjustEditabled(Template, false);
127.             };
128.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? m_Init.y[m_Init.bOk] : m_Info.Regions[MSG_TITLE_IDE].h + 6));
129.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx));
130.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny));
131. 
132.             delete Template;
133.             
134.             ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate);
135.             ChartRedraw(m_Info.WinHandle);
136. 
137. #undef macro_PointsToFinance
138.          }
139. //+------------------------------------------------------------------+
140.       eObjectsIDE CheckMousePosition(short &x, short &y)
141.          {
142.             int xi, yi, xf, yf;
143.             st_Mouse loc;
144.             
145.             loc = GetPositionsMouse();
146.             x = loc.Position.X_Graphics;
147.             y = loc.Position.Y_Graphics;
148.             for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++)
149.             {
150.                xi = (m_Info.IsMaximized ? m_Info.x : m_Info.minx) + m_Info.Regions[c0].x;
151.                yi = (m_Info.IsMaximized ? m_Info.y : m_Info.miny) + m_Info.Regions[c0].y;
152.                xf = xi + m_Info.Regions[c0].w;
153.                yf = yi + m_Info.Regions[c0].h;
154.                if ((x > xi) && (y > yi) && (x < xf) && (y < yf)) return c0;
155.             }
156.             return MSG_NULL;
157.          }
158. //+------------------------------------------------------------------+
159. inline void DeleteObjectEdit(void)
160.          {
161.             ChartRedraw();
162.             ObjectsDeleteAll(m_Init.id, m_Info.szObj_Editable);
163.          }
164. //+------------------------------------------------------------------+
165.       template <typename T >
166.       void CreateObjectEditable(eObjectsIDE arg, T value)
167.          {
168.             DeleteObjectEdit();
169.             CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, 0);
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);
172.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_XSIZE, m_Info.Regions[arg].w);
173.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_YSIZE, m_Info.Regions[arg].h);
174.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_BGCOLOR, m_Info.Regions[arg].bgcolor);
175.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_ALIGN, ALIGN_CENTER);
176.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_FONTSIZE, m_Info.Regions[arg].FontSize - 1);
177.             ObjectSetString(m_Init.id, m_Info.szObj_Editable, OBJPROP_FONT, m_Info.Regions[arg].FontName);
178.             ObjectSetString(m_Init.id, m_Info.szObj_Editable, OBJPROP_TEXT, (typename(T) == "double" ? DoubleToString(value, 2) : (string) value));
179.             ChartRedraw();
180.          }
181. //+------------------------------------------------------------------+
182.       bool RestoreState(void)
183.          {
184.             uCast_Double info;
185.             bool bRet;
186.             C_AdjustTemplate *Template;
187.             
188.             if (bRet = GlobalVariableGet(macro_NameGlobalVariable("POST"), info.dValue))
189.             {
190.                m_Info.x = (short) info._16b[0];
191.                m_Info.y = (short) info._16b[1];
192.                m_Info.minx = (short) info._16b[2];
193.                m_Info.miny = (short) info._16b[3];
194.                Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(m_Init.id) + ".tpl");
195.                if (_LastError >= ERR_USER_ERROR_FIRST) bRet = false; else
196.                {
197.                   (*Template).Add("MSG_LEVERAGE_VALUE", "descr", NULL);
198.                   (*Template).Add("MSG_TAKE_VALUE", "descr", NULL);
199.                   (*Template).Add("MSG_STOP_VALUE", "descr", NULL);
200.                   (*Template).Add("MSG_DAY_TRADE", "state", NULL);
201.                   (*Template).Add("MSG_MAX_MIN", "state", NULL);
202.                   if (!(*Template).Execute()) bRet = false; else
203.                   {
204.                      m_Info.IsDayTrade = (bool) StringToInteger((*Template).Get("MSG_DAY_TRADE", "state")) == 1;
205.                      m_Info.IsMaximized = (bool) StringToInteger((*Template).Get("MSG_MAX_MIN", "state")) == 1;
206.                      m_Info.Leverage = (short)StringToInteger((*Template).Get("MSG_LEVERAGE_VALUE", "descr"));
207.                      m_Info.FinanceTake = (double) StringToDouble((*Template).Get("MSG_TAKE_VALUE", "descr"));
208.                      m_Info.FinanceStop = (double) StringToDouble((*Template).Get("MSG_STOP_VALUE", "descr"));
209.                   }
210.                };               
211.                delete Template;
212.             };
213.             
214.             GlobalVariablesDeleteAll(macro_NameGlobalVariable(""));
215.             
216.             return bRet;
217.          }
218. //+------------------------------------------------------------------+
219.    public   :
220. //+------------------------------------------------------------------+
221.       C_ChartFloatingRAD(string szShortName, const short Leverage, const double FinanceTake, const double FinanceStop)
222.          :C_Mouse(0, "")
223.          {
224.             m_Init.id = GetInfoTerminal().ID;
225.             m_Info.IsSaveState = false;
226.             if (!IndicatorCheckPass(szShortName)) return;
227.             if (!RestoreState())
228.             {
229.                m_Info.Leverage = Leverage;
230.                m_Info.IsDayTrade = true;
231.                m_Info.FinanceTake = FinanceTake;
232.                m_Info.FinanceStop = FinanceStop;
233.                m_Info.IsMaximized = true;
234.                m_Info.minx = m_Info.x = 115;
235.                m_Info.miny = m_Info.y = 64;
236.             }
237.             m_Init.y[false] = 150;
238.             m_Init.y[true] = 210;
239.             CreateWindowRAD(170, m_Init.y[m_Init.bOk = false]);
240.             AdjustTemplate(true);
241.          }
242. //+------------------------------------------------------------------+
243.       ~C_ChartFloatingRAD()
244.          {
245.             ChartRedraw();
246.             ObjectsDeleteAll(m_Init.id, m_Info.szObj_Chart);
247.             if (!m_Info.IsSaveState)
248.                FileDelete(m_Info.szFileNameTemplate);
249.          }
250. //+------------------------------------------------------------------+
251.       void SaveState(void)
252.          {
253. #define macro_GlobalVariable(A, B) if (GlobalVariableTemp(A)) GlobalVariableSet(A, B);
254.             
255.             uCast_Double info;
256.             
257.             info._16b[0] = m_Info.x;
258.             info._16b[1] = m_Info.y;
259.             info._16b[2] = m_Info.minx;
260.             info._16b[3] = m_Info.miny;
261.             macro_GlobalVariable(macro_NameGlobalVariable("POST"), info.dValue);
262.             m_Info.IsSaveState = true;
263.             
264. #undef macro_GlobalVariable
265.          }
266. //+------------------------------------------------------------------+
267.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
268.          {
269. #define macro_AdjustMinX(A, B)    {                          \
270.             B = (A + m_Info.Regions[MSG_TITLE_IDE].w) > x;   \
271.             mx = x - m_Info.Regions[MSG_TITLE_IDE].w;        \
272.             A = (B ? (mx > 0 ? mx : 0) : A);                 \
273.                                  }
274. #define macro_AdjustMinY(A, B)   {                           \
275.             B = (A + m_Info.Regions[MSG_TITLE_IDE].h) > y;   \
276.             my = y - m_Info.Regions[MSG_TITLE_IDE].h;        \
277.             A = (B ? (my > 0 ? my : 0) : A);                 \
278.                                  }
279.                               
280.             static short sx = -1, sy = -1, sz = -1;
281.             static eObjectsIDE obj = MSG_NULL;
282.             short x, y, mx, my;
283.             double dvalue;
284.             bool b1, b2, b3, b4;
285.             ushort ev = evChartTradeCloseAll;
286. 
287.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
288.             switch (id)
289.             {
290.                case CHARTEVENT_CUSTOM + evEA_At_ChartTrade:
291.                   if (m_Init.bOk = ((lparam >= 0) && (lparam < 2)))
292.                      CurrentSymbol((bool)lparam);
293.                   AdjustTemplate(true);
294.                   break;
295.                case CHARTEVENT_CHART_CHANGE:
296.                   if (!m_Init.bOk)
297.                      EventChartCustom(m_Init.id, evChartTrade_At_EA, 0, 0, "");
298.                   x = (short)ChartGetInteger(m_Init.id, CHART_WIDTH_IN_PIXELS);
299.                   y = (short)ChartGetInteger(m_Init.id, CHART_HEIGHT_IN_PIXELS);
300.                   macro_AdjustMinX(m_Info.x, b1);
301.                   macro_AdjustMinY(m_Info.y, b2);
302.                   macro_AdjustMinX(m_Info.minx, b3);
303.                   macro_AdjustMinY(m_Info.miny, b4);
304.                   if (b1 || b2 || b3 || b4) AdjustTemplate();
305.                   break;
306.                case CHARTEVENT_MOUSE_MOVE:
307.                   if (CheckClick(C_Mouse::eClickLeft))
308.                   {                  
309.                      switch (CheckMousePosition(x, y))
310.                      {
311.                         case MSG_MAX_MIN:
312.                            if (sz < 0) m_Info.IsMaximized = (m_Info.IsMaximized ? false : true);
313.                            break;
314.                         case MSG_DAY_TRADE:
315.                            if ((m_Info.IsMaximized) && (sz < 0)) m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true);
316.                            break;
317.                         case MSG_LEVERAGE_VALUE:
318.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_LEVERAGE_VALUE, m_Info.Leverage);
319.                            break;
320.                         case MSG_TAKE_VALUE:
321.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_TAKE_VALUE, m_Info.FinanceTake);
322.                            break;
323.                         case MSG_STOP_VALUE:
324.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_STOP_VALUE, m_Info.FinanceStop);
325.                            break;
326.                         case MSG_TITLE_IDE:
327.                            if (sx < 0)
328.                            {
329.                               ChartSetInteger(m_Init.id, CHART_MOUSE_SCROLL, false);
330.                               sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx);
331.                               sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny);
332.                            }
333.                            if ((mx = x - sx) > 0) ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx);
334.                            if ((my = y - sy) > 0) ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my);
335.                            if (m_Info.IsMaximized)
336.                            {
337.                               m_Info.x = (mx > 0 ? mx : m_Info.x);
338.                               m_Info.y = (my > 0 ? my : m_Info.y);
339.                            }else
340.                            {
341.                               m_Info.minx = (mx > 0 ? mx : m_Info.minx);
342.                               m_Info.miny = (my > 0 ? my : m_Info.miny);
343.                            }
344.                            break;
345.                         case MSG_BUY_MARKET:
346.                            ev = evChartTradeBuy;
347.                         case MSG_SELL_MARKET:
348.                            ev = (ev != evChartTradeBuy ? evChartTradeSell : ev);
349.                         case MSG_CLOSE_POSITION:
350.                            if ((m_Info.IsMaximized) && (sz < 0) && (m_Init.bOk))
351.                            {
352.                               string szTmp = StringFormat("%d?%s?%s?%c?%d?%.2f?%.2f", ev, _Symbol, GetInfoTerminal().szSymbol, (m_Info.IsDayTrade ? 'D' : 'S'),
353.                                                           m_Info.Leverage, FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage));                           
354.                               PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp);
355.                               EventChartCustom(m_Init.id, ev, 0, 0, szTmp);
356.                            }
357.                            break;
358.                      }
359.                      if (sz < 0)
360.                      {
361.                         sz = x;
362.                         AdjustTemplate();
363.                         if (obj == MSG_NULL) DeleteObjectEdit();
364.                      }
365.                   }else
366.                   {
367.                      sz = -1;
368.                      if (sx > 0)
369.                      {
370.                         ChartSetInteger(m_Init.id, CHART_MOUSE_SCROLL, true);                  
371.                         sx = sy = -1;
372.                      }
373.                   }
374.                   break;
375.                case CHARTEVENT_OBJECT_ENDEDIT:
376.                   switch (obj)
377.                   {
378.                      case MSG_LEVERAGE_VALUE:
379.                      case MSG_TAKE_VALUE:
380.                      case MSG_STOP_VALUE:
381.                         dvalue = StringToDouble(ObjectGetString(m_Init.id, m_Info.szObj_Editable, OBJPROP_TEXT));
382.                         if (obj == MSG_TAKE_VALUE)
383.                            m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue);
384.                         else if (obj == MSG_STOP_VALUE)
385.                            m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue);
386.                         else
387.                            m_Info.Leverage = (dvalue <= 0 ? m_Info.Leverage : (short)MathFloor(dvalue));
388.                         AdjustTemplate();
389.                         obj = MSG_NULL;
390.                         ObjectDelete(m_Init.id, m_Info.szObj_Editable);
391.                         break;
392.                   }
393.                   break;
394.                case CHARTEVENT_OBJECT_DELETE:
395.                   if (sparam == m_Info.szObj_Chart) macro_CloseIndicator(C_Terminal::ERR_Unknown);
396.                   break;
397.             }
398.             ChartRedraw();
399.          }
400. //+------------------------------------------------------------------+
401. };
402. //+------------------------------------------------------------------+
403. #undef macro_NameGlobalVariable
404. #undef macro_CloseIndicator
405. //+------------------------------------------------------------------+

ヘッダーファイル:C_ChartFloatingRAD.mqh

このコードは一見すると長く複雑に見えるかもしれませんが、これまでのリプレイ/シミュレーションの連載を追ってきた方であれば、ほとんどの部分が変更されていないことに気付くでしょう。実際、コードを学習し、解析する立場から見ても、修正箇所は最小限に留められています。主な違いとして、多くのGetInfoTerminal呼び出しがm_Init.idに置き換えられている点が挙げられます。これは、変数へのアクセスが関数呼び出しよりも圧倒的に高速であるためです。

224行目では、クラス全体で後に使用するためのチャートIDが取得されている箇所を確認できます。しかし、最も重要なポイントは306行目にあるCHARTEVENT_MOUSE_MOVEイベントです。307行目ではC_Mouseクラスが呼び出されていますが、マウスインジケーターが存在しない場合はfalseを返し、存在する場合はtrueを返すようになっています。

309行目に注意してください。ここではCheckMousePosition手続きがlparamおよびdparamのデータを使用していません。通常であればこれらのパラメータにはマウスの座標値が含まれますが、この手続き(140行目)を確認すると、145行目で位置データが取得されていることが分かります。これはC_Terminalクラスによって解釈されており、145行目と146行目で使用する値が指定されています。今回は絶対座標を使用していますが、相対座標を利用することも可能です。

これらの値は、マウスインジケーターバッファではなくC_Terminalクラスの解釈結果から取得されています。この方法により、処理の効率と実行速度が大幅に向上します。ただし、同じ値はマウスインジケーターバッファにも保存されるため、整合性も保たれています。

最後に、C_ChartFloatingRADコンストラクタの変更に伴い修正が行われた最後のソースファイルについて説明します。これはChart Tradeインジケーターのソースコードであり、以下でご覧になれます。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Chart Trade Base Indicator."
04. #property description "See the articles for more details."
05. #property version   "1.82"
06. #property icon "/Images/Market Replay/Icons/Indicators.ico"
07. #property link "https://www.mql5.com/pt/articles/12580"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh>
12. //+------------------------------------------------------------------+
13. #define def_ShortName "Indicator Chart Trade"
14. //+------------------------------------------------------------------+
15. C_ChartFloatingRAD *chart = NULL;
16. //+------------------------------------------------------------------+
17. input ushort user01 = 1;         //Leverage
18. input double user02 = 100.1;     //Finance Take
19. input double user03 = 75.4;      //Finance Stop
20. //+------------------------------------------------------------------+
21. int OnInit()
22. {
23.    chart = new C_ChartFloatingRAD(def_ShortName, user01, user02, user03);
24.    
25.    if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
26. 
27.    return INIT_SUCCEEDED;
28. }
29. //+------------------------------------------------------------------+
30. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
31. {
32.    return rates_total;
33. }
34. //+------------------------------------------------------------------+
35. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
36. {
37.    if (_LastError < ERR_USER_ERROR_FIRST) 
38.       (*chart).DispatchMessage(id, lparam, dparam, sparam);
39. }
40. //+------------------------------------------------------------------+
41. void OnDeinit(const int reason)
42. {
43.    switch (reason)
44.    {
45.       case REASON_INITFAILED:
46.          ChartIndicatorDelete(ChartID(), 0, def_ShortName);
47.          break;
48.       case REASON_CHARTCHANGE:
49.          (*chart).SaveState();
50.          break;
51.    }
52. 
53.    delete chart;
54. }
55. //+------------------------------------------------------------------+

Chart Tradeインジケーターのソースコード

変更点は23行目のみであることにご注目ください。以前のバージョンをご覧になっていない方のために、完全なコードを参考用として掲載しています。


最後に

本記事では、コードの保守性と持続可能性を高めるために加えたすべての変更点を紹介しました。次回の記事では、別のテーマを取り上げます。本記事には、マウスインジケーターおよびChart Tradeインジケーターのコンパイル済みアプリケーションが添付されており、最新バージョンを取引環境でご利用いただけます。次の記事に続きます。

ファイル03 説明
Experts\Expert Advisor.mq5
Chart TradeとEAの連携を示す(Mouse Study が必要)
Indicadores\Chart Trade.mq5 送信する注文を設定するウィンドウを生成する(Mouse Study が必要)
Indicadores\Market Replay.mq5 リプレイ/シミュレーターサービスと連携するためのコントロールを生成する(Mouse Studyが必要
Indicadores\Mouse Study.mq5 グラフィカルコントロールとユーザー間のインタラクションを実現する(リプレイ/シミュレーターおよび実取引の両方で必須)
Servicios\Market Replay.mq5 マーケットリプレイおよびシミュレーションサービスを生成し、維持する(システム全体のメインファイル)


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

添付されたファイル |
Anexo.zip (490.53 KB)
取引所価格のバイナリコードの分析(第1回):テクニカル分析の新たな視点 取引所価格のバイナリコードの分析(第1回):テクニカル分析の新たな視点
本記事では、価格変動をバイナリコードに変換するという新しい視点からテクニカル分析にアプローチします。筆者は、シンプルな値動きから複雑な市場パターンに至るまで、あらゆる市場行動を「0」と「1」のシーケンスとして符号化できることを示します。
取引におけるニューラルネットワーク:層状メモリを持つエージェント 取引におけるニューラルネットワーク:層状メモリを持つエージェント
層状メモリアプローチは、人間の認知プロセスを模倣することで、複雑な金融データの処理や新しいシグナルへの適応を可能にし、動的な市場における投資判断の有効性を向上させます。
量子コンピューティングと取引:価格予測への新たなアプローチ 量子コンピューティングと取引:価格予測への新たなアプローチ
本記事では、量子コンピューティングを用いて金融市場における価格変動を予測するための革新的なアプローチについて説明します。主な焦点は、量子位相推定(QPE: Quantum Phase Estimation)アルゴリズムを適用して価格パターンのプロトタイプを見つけることであり、これによりトレーダーは市場データの分析を大幅に高速化できるようになります。
取引におけるニューラルネットワーク:ウェーブレット変換とマルチタスクアテンションを用いたモデル(最終回) 取引におけるニューラルネットワーク:ウェーブレット変換とマルチタスクアテンションを用いたモデル(最終回)
前回の記事では、Multitask-Stockformerフレームワークを検討しました。このフレームワークは、ウェーブレット変換とマルチタスク自己アテンション(Self-Attention)モデルを組み合わせたものです。本記事では、このフレームワークのアルゴリズムをさらに実装し、実際の過去データを用いてその有効性を評価していきます。