市場シミュレーション(第3回):パフォーマンスの問題
はじめに
前回の「市場シミュレーション(第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
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
取引所価格のバイナリコードの分析(第1回):テクニカル分析の新たな視点
取引におけるニューラルネットワーク:層状メモリを持つエージェント
量子コンピューティングと取引:価格予測への新たなアプローチ
取引におけるニューラルネットワーク:ウェーブレット変換とマルチタスクアテンションを用いたモデル(最終回)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索