リプレイシステムの開発(第49回):物事は複雑になる(I)
はじめに
この記事では、「リプレイシステムの開発(第48回)::理解し、考えるべき概念」稿で議論された内容を使用します。この記事の内容は、ここでおこなう内容を理解する上で非常に重要ですので、まだ読んでいない場合はぜひお読みください。
以前の記事を書いているときに最も気になったことの1つは、リプレイ/シミュレーターシステムに、指標がリストされている領域でMetaTrader5ユーザーに表示される指標が含まれており、そこからチャート上に配置できることでした。
記事には、ユーザーが間違ったChart Trade、つまりリプレイ/シミュレーターサービスで使用される銘柄とは異なるChart Tradeに指標を配置するのを防ぐロック機能がありましたが、他とともにそのリストにこの指標が存在すること自体が、私にとって非常に不安でした。
これまで数か月間、私はすべてを最も適切な方法で整理する方法を試行錯誤してきました。幸いなことに、最近、状況を改善する解決策を見つけることができました。この場合、コントロール指標は他の指標の中に存在しなくなり、リプレイ/シミュレーターサービスの不可欠な部分になります。
こうすることで、いくつかの要素に関して自由度が高まります。ただし、MetaTrader 5の負荷を軽減するために指標を改良していく予定なので、変更は徐々におこなっていく予定です。つまり、一部のリソースの使用をやめ、他のプラットフォーム機能の使用を開始します。これにより、リプレイ/シミュレーターシステムの安定性、セキュリティ、信頼性が向上します。
変更の実装は非常に興味深いものとなり、MQL5をより専門的に操作する方法に関する優れた知識が得られるため、これがどのように実装されるかを見てみましょう。いずれにしても、これはリプレイ/シミュレーターシステムの開発が遅れることを意味しません。実際、このシステムには、将来何か他のものをプログラムするために、この記事で説明するものを含め、いくつかのものが必要です。
それでは、より高度なリプレイ/シミュレーターサービスを実装するための壮大な旅を続けましょう。
変化を始める
これから何がおこなわれるかを明確に説明し、読者の皆さんが変更を追いやすいよう、段階的に進めていきます。今回のデモンストレーションで選んだ方法は手順が多く、すぐに最終コードに進んだほうが良いと感じる方もいるかもしれません。しかし、これこそがまさに必要なプロセスであり、これまでの記事が存在する理由でもあります。そうでなければ、意味がわかりませんね。この資料の目的は、皆さんが独自のプログラムやソリューションを作成できるように動機付け、支援することにあります。そのため、既存のコードを整理し、より複雑なモデルに適用できる形へ改良する方法を示すことが重要です。また、コードが奇妙で非効率的にならないようにも心がける必要があります。
そのため、焦らず、初心者の方の理解も考慮しながら進めていきましょう。
まず、Chart IDバインディングシステムを削除します。このシステムは、コントロール指標が複数のChart Tradeに配置されるのを防ぐとともに、指標が誤ったChart Tradeに配置されるのも防いでいました。このシステムは現時点では削除しますが、後に再度導入する予定です。その際、指標は正しいChart Tradeにのみ配置され、指標自体ではなくリプレイ/シミュレーターサービスが操作をおこなうようにします。
この削除が正しく、安全におこなわれるよう、サービスとコントロール指標間のインタラクションポイントは常に統一されます。以下に、この一般的なコードを示します。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_SymbolReplay "RePlay" 05. #define def_GlobalVariableReplay def_SymbolReplay + "_Infos" 06. #define def_GlobalVariableIdGraphics def_SymbolReplay + "_ID" 07. #define def_GlobalVariableServerTime def_SymbolReplay + "_Time" 08. #define def_MaxPosSlider 400 09. //+------------------------------------------------------------------+ 10. union u_Interprocess 11. { 12. union u_0 13. { 14. double df_Value; // Value of the terminal global variable... 15. ulong IdGraphic; // Contains the Graph ID of the asset... 16. }u_Value; 17. struct st_0 18. { 19. bool isPlay; // Indicates whether we are in Play or Pause mode... 20. bool isWait; // Tells the user to wait... 21. bool isHedging; // If true we are in a Hedging account, if false the account is Netting... 22. bool isSync; // If true indicates that the service is synchronized... 23. ushort iPosShift; // Value between 0 and 400... 24. }s_Infos; 25. datetime ServerTime; 26. }; 27. //+------------------------------------------------------------------+ 28. union uCast_Double 29. { 30. double dValue; 31. long _long; // 1 Information 32. datetime _datetime; // 1 Information 33. int _int[sizeof(double)]; // 2 Informations 34. char _char[sizeof(double)]; // 8 Informations 35. }; 36. //+------------------------------------------------------------------+
Interprocess.mqhのソースコード
行06には、チャートIDを渡すために使用するグローバル端末変数名を、サービスと指標の両方に伝える定義が含まれています。この行を削除すると、指標やサービスをコンパイルしようとした際にエラーが発生しますが、実はこれこそが必要な反応です。コンパイル中に生成されるこれらのエラーは、グローバル端末変数を介したサービスと指標間の接続を取り除くために対応が求められる箇所を指し示してくれます。
ここでは、単調な繰り返しを避けながら、コードフラグメントを用いてこれを説明していきます。システムが正常に機能し続けるようにするため、ソースコードのどこでどのように修正を加えるべきか、正確に示していきます。
それでは、まず次のポイントから考えていきましょう。
01. //+------------------------------------------------------------------+ 02. #property service 03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico" 04. #property copyright "Daniel Jose" 05. #property version "1.49" 06. #property description "Replay-Simulator service for MT5 platform." 07. #property description "This is dependent on the Market Replay indicator." 08. #property description "For more details on this version see the article." 09. #property link "https://www.mql5.com/ru/articles/11820" 10. //+------------------------------------------------------------------+ 11. #define def_Dependence_01 "Indicators\\Replay\\Market Replay.ex5" 12. #resource "\\" + def_Dependence_01 13. //+------------------------------------------------------------------+ 14. #include <Market Replay\Service Graphics\C_Replay.mqh> 15. //+------------------------------------------------------------------+ 16. input string user00 = "Forex - EURUSD.txt"; //Replay configuration file. 17. input ENUM_TIMEFRAMES user01 = PERIOD_M5; //Initial graphic time. 18. //+------------------------------------------------------------------+ 19. void OnStart() 20. { 21. C_Replay *pReplay; 22. 23. pReplay = new C_Replay(user00); 24. if ((*pReplay).ViewReplay(user01)) 25. { 26. Print("Permission granted. Replay service can now be used..."); 27. while ((*pReplay).LoopEventOnTime(false)); 28. } 29. delete pReplay; 30. } 31. //+------------------------------------------------------------------+
ソースコード:replay.mq5サービス
上記のコードはサービスコードそのものですが、11行目と12行目に、以前のコードといくつか異なる点が見られます。この変更点は小さなものに見えるかもしれませんが、目的の達成には重要な役割を果たします。この詳細については後ほど触れます。ここでの課題は、グローバル端末変数名の定義を削除し、ヘッダーコードを変更したあと、コントロール指標が最近コンパイルされたかどうかを確認することです。
この記事執筆時点では、MetaEditorにはまだMAKEコンパイルシステムが実装されていませんが、将来的にはMQLプラットフォームおよび言語の開発者がこの機能を追加する可能性があります。しかし現時点では、特定の予防策を講じる必要があります。
ここで少し時間をとり、MAKEコンパイルシステムについて簡単に説明させてください。このシステムは、一部の読者には新しい概念かもしれませんが、長年プログラミングに従事している方には馴染み深いものです。仕組みとしては、通常通りソースコードをビルドするのですが、複数の実行可能ファイルを一度にビルドする必要がある場合などに役立ちます。また、単一の実行可能ファイルを作成する際にも使えますが、すべてがMAKE経由で行われるわけではありません。
一般的に、何かをコンパイルする前に、MakeFileと呼ばれるファイルを作成します。このMakeFileには、コンパイラやLinkEditorがすべての実行可能ファイルを一度に生成できるように必要な手順や設定が含まれています。このシステムの大きなメリットは、あるヘッダーファイルやライブラリファイルが変更されると、MAKEがそれを検知し、必要なファイルのみをコンパイルする点です。これにより、関連するすべての実行可能ファイルが更新されるため、
開発者は特定のファイルの更新を心配する必要がなく、MAKEがその作業を自動的に行ってくれます。このように、異なる実行可能ファイルの間で変更の適用漏れが起こるリスクを回避できるのです。つまり、MAKEが私たちに代わってすべての作業を実行してくれるのです。
ただし、現時点ではMetaEditorにはこのようなツールがありません。コマンドラインからバッチファイルを使用して類似の操作を実現することも可能ですが、これは一時的な解決策にすぎず、理想的とはいえません。そのため、この記事ではその方法について詳しくは説明しません。ただし、上記のコードの12行目で、MQL5コンパイラに対し、コントロール指標の実行可能ファイルをサービスに含めるよう指示しているため、サービスコードをコンパイルすると指標コードも同時にコンパイルされる仕組みです。この点については以前にも触れましたが、ここで改めて強調しておきます。指標が正しく再コンパイルされるためには、サービスコードをコンパイルするたびに、指標の実行可能ファイルを削除する必要があります。これを怠ると、サービスコードのコンパイルだけでは指標コードは再コンパイルされません。この点に注意してください。
次に、サービスコードをコンパイルした際の結果が以下のように得られます。
図01:新しいコードのコンパイルの試み結果
2つのエラーと1つの警告が発生していることに気付いたかもしれません。警告は現時点では考慮の優先度が低く、まずはエラーの修正に取り組むべきです。画像01を確認すると、エラーの発生場所がわかります。エラーをクリックすると、エラーの発生箇所へ移動できるため、これを活用します。
画像01に示された行12のエラーは、コンパイラがサービスリソースの実行可能ファイルを見つけられなかったことが原因です。
次に、156行目で報告されたエラーはサービスコードの一部で発生しており、コンパイルを続行するにはここを修正する必要があります。
以下に、そのエラーに関連するコードを示します。
141. ~C_Replay() 142. { 143. ArrayFree(m_Ticks.Info); 144. ArrayFree(m_Ticks.Rate); 145. m_IdReplay = ChartFirst(); 146. do 147. { 148. if (ChartSymbol(m_IdReplay) == def_SymbolReplay) 149. ChartClose(m_IdReplay); 150. }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0); 151. for (int c0 = 0; (c0 < 2) && (!SymbolSelect(def_SymbolReplay, false)); c0++); 152. CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX); 153. CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX); 154. CustomSymbolDelete(def_SymbolReplay); 155. GlobalVariableDel(def_GlobalVariableReplay); 156. GlobalVariableDel(def_GlobalVariableIdGraphics); 157. GlobalVariableDel(def_GlobalVariableServerTime); 158. Print("Finished replay service..."); 159. }
C_Replay.mqhからのコード
156行目のコードはコメントアウト、もしくは削除可能です。この変更は永続的であるため、今の段階でこの行を削除しましょう。これによって、サービスはグローバル端末変数を認識しなくなります。この修正をサービスコードに適用した後で、コードを再度コンパイルしてみてください。すると、画像02に示されているような結果が得られます。
画像02:新しいコンパイルの試み
表示されているのはエラーが1つだけで、警告はなくなりました。この時点で、経験の浅い読者の中には、表示されたエラーをどのように修正すればよいのか疑問に思う方もいるかもしれません。しかし、そういった方々はコンパイラのメッセージを読み取ることを忘れてしまっています。
図02を見てください。エラーメッセージのすぐ上には、コンパイラからの他の情報が表示されています。この情報を決して無視してはいけません。すべてのメッセージに注意を払う必要があり、特にエラー表示の直前に含まれているメッセージには、問題解決に役立つ重要な情報が含まれています。
compiling '\Indicators\Replay\Market Replay.mq5' failed
このメッセージは、何らかの理由でコンパイラが実行可能な指標ファイルを生成できないことを知らせる重要な情報です。サービスコードが部分的にコンパイルされているため、指標コードに焦点を当てる必要があります。指定されたコードをMetaEditorで開き、コンパイラに実行可能ファイルの生成を試みるように要求しましょう。「コードにエラーがあることはわかっているのに、なぜそうするのか?」と疑問に思う方もいるかもしれません。はい、エラーが存在することは理解していますが、コンパイラに正確なエラーの位置を教えてもらいたいのです。手動でエラーを探すのは非生産的ですので、コンパイラにエラーを表示させる方が効率的です。
したがって、指標コードを開き、コンパイラに実行可能ファイルの生成を要求すると、図03に示されるような結果が得られます。
画像03:コントロール指標のコンパイルの試み
今回は、5つのエラーと5つの警告が表示されています。まずはエラーを処理し、その後で警告に対処します。最初のエラーメッセージをクリックすると、そのエラーの正確な位置にあるコードにリダイレクトされます。重要なポイントは、多くの人がエラーをランダムにクリックしてしまうことです。しかし、ルールに従い、次のように行動する必要があります。まずリストの最初のエラーを見つけ、それ以外は無視して、常に最初のエラーから修正を始めます。その後、コードを再コンパイルし、エラーや警告がなくなるまでこのプロセスを繰り返します。特に初心者の場合、100以上のコンパイルエラーが表示されると混乱してしまうことがよくあります。
とはいえ、多くの場合、最初のエラーを修正するだけで、コード全体が正常にコンパイルされることがあります。したがって、次のアドバイスに従ってください。コンパイラが示す最初のエラーを探し、その修正方法を確認してから、コードを再コンパイルします。新しいエラーが表示された場合は、リストの最初のエラーに戻って修正を行い、エラーが解消されるまでこの作業を続けます。警告についても同様に対処する必要があります。常にリストの最初の警告から取り組むのです。
コンパイラによって報告されるすべてのエラー(図03に示す)は同じコード内にあるため、全体を表示し、変更が行われる場所を把握しやすくなります。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico" 04. #property description "Control indicator for the Replay-Simulator service." 05. #property description "This one doesn't work without the service loaded." 06. #property version "1.49" 07. #property link "https://www.mql5.com/ru/articles/11820" 08. #property indicator_chart_window 09. #property indicator_plots 0 10. //+------------------------------------------------------------------+ 11. #include <Market Replay\Service Graphics\C_Controls.mqh> 12. //+------------------------------------------------------------------+ 13. #define def_BitShift ((sizeof(ulong) * 8) - 1) 14. //+------------------------------------------------------------------+ 15. C_Terminal *terminal = NULL; 16. C_Controls *control = NULL; 17. //+------------------------------------------------------------------+ 18. #define def_InfoTerminal (*terminal).GetInfoTerminal() 19. #define def_ShortName "Market_" + def_SymbolReplay 20. //+------------------------------------------------------------------+ 21. int OnInit() 22. { 23. #define macro_INIT_FAILED { ChartIndicatorDelete(def_InfoTerminal.ID, 0, def_ShortName); return INIT_FAILED; } 24. u_Interprocess Info; 25. ulong ul = 1; 26. 27. ResetLastError(); 28. if (CheckPointer(control = new C_Controls(terminal = new C_Terminal())) == POINTER_INVALID) return INIT_FAILED; 29. if (_LastError != ERR_SUCCESS) return INIT_FAILED; 30. ul <<= def_BitShift; 31. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName); 32. if ((def_InfoTerminal.szSymbol != def_SymbolReplay) || (!GlobalVariableCheck(def_GlobalVariableIdGraphics))) macro_INIT_FAILED; 33. Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics); 34. if (Info.u_Value.IdGraphic != def_InfoTerminal.ID) macro_INIT_FAILED; 35. if ((Info.u_Value.IdGraphic >> def_BitShift) == 1) macro_INIT_FAILED; 36. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName + "Device"); 37. Info.u_Value.IdGraphic |= ul; 38. GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value); 39. if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.u_Value.df_Value = 0; 40. EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.u_Value.df_Value, ""); 41. (*control).Init(Info.s_Infos.isPlay); 42. 43. return INIT_SUCCEEDED; 44. 45. #undef macro_INIT_FAILED 46. } 47. //+------------------------------------------------------------------+ 48. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 49. { 50. static bool bWait = false; 51. u_Interprocess Info; 52. 53. Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); 54. if (!bWait) 55. { 56. if (Info.s_Infos.isWait) 57. { 58. EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOn, 1, 0, ""); 59. bWait = true; 60. } 61. }else if (!Info.s_Infos.isWait) 62. { 63. EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.u_Value.df_Value, ""); 64. bWait = false; 65. } 66. 67. return rates_total; 68. } 69. //+------------------------------------------------------------------+ 70. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 71. { 72. (*control).DispatchMessage(id, lparam, dparam, sparam); 73. } 74. //+------------------------------------------------------------------+ 75. void OnDeinit(const int reason) 76. { 77. u_Interprocess Info; 78. ulong ul = 1; 79. 80. switch (reason) 81. { 82. case REASON_CHARTCHANGE: 83. ul <<= def_BitShift; 84. Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics); 85. Info.u_Value.IdGraphic ^= ul; 86. GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value); 87. break; 88. case REASON_REMOVE: 89. case REASON_CHARTCLOSE: 90. if (def_InfoTerminal.szSymbol != def_SymbolReplay) break; 91. GlobalVariableDel(def_GlobalVariableReplay); 92. ChartClose(def_InfoTerminal.ID); 93. break; 94. } 95. delete control; 96. delete terminal; 97. } 98. //+------------------------------------------------------------------+
指標のソースコード:Market replay.mq5
コード内で取り消し線が引かれた行はすべて削除されます。そのため、再度コンパイルしようとすると、図04に示されているようなメッセージが表示されます。
図04
この画像04には警告が表示されています。これは実際にはコードに干渉しませんが、煩わしいことがあります。したがって、77行目に移動してその警告を削除し、コードを再度コンパイルしてみてください。画像05に示されるメッセージが表示されますが、これが必要なメッセージです。ただし、77行目を削除できるのは、コンパイラが変数が使用されていないと通知したためです。
図05:コンパイルが正常に完了した
非常に良いです。コードは部分的にクリーンアップされました。ただし、このトピックの冒頭で示した Interprocess.mqh ヘッダーファイルのコードを再度確認すると、もはや必要のないいくつかの部分が残っていることに気づくでしょう。これは、チャートIDを指標に渡すためにグローバル端末変数を使用しなくなったためです。そのため、Interprocess.mqhファイルの15行目を削除する必要があります。ただ、なぜこの行をもっと早く削除しなかったのかという疑問が浮かぶかもしれません。その理由は、サービスファイルには特別な注意が必要だからです。しかし、それだけではありません。もう1つの理由があるため、次のトピックに進みましょう。
決定を過激化する
リプレイ/シミュレーターサービスを設計する際、私が目指したのは、チャートに何を表示するか、または表示しないかについてユーザーが介入できないようにすることでした。
この問題を解決するために、グローバル端末変数を追加し、指標がどのチャートに配置されるべきかを指定しました。これにより、ユーザーは指標を別のチャートに配置することができなくなります。実際、この解決策は適切であり、非常に興味深いものでした。指標、スクリプト、またはEAがチャートに配置されないようにしたい場合には、この方法を利用できます。しかし、それは今私たちが興味を持っていることではありません。
チャートに何を表示するか、または表示しないかを制御するサービスを使用すると、従来のシステムは完全に不要になります。より正確に言えば、指標はサービスリソースとして存在し、ユーザーはそれにアクセスできないため、そのコードをサポートし続ける意味がなくなります。これにより、セキュリティレベルが大幅に向上し、アクセス制御も完全に異なります。
そのため、システムからID変数を削除する際には注意が必要です。前のトピックでは削除しませんでしたが、その理由はすぐにわかるでしょう。もし削除が事前におこなわれていた場合、適切な変更を加えることが非常に難しくなり、多くの間違いを犯すことになっていたでしょう。
偉大なプログラマーの最大の強みは、急がずに慎重に取り組むことです。問題を1つずつ解決し、徐々に修正や変更を加えながら、プログラムの既存の機能をすべて維持し、必要に応じてそれらを拡張していきます。したがって、モットーは次のとおりです。
分割して征服する
コードがどのようにクリーンアップされるか見てみましょう。Interprocess.mqhの新しいコード全体を以下に示します。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_SymbolReplay "RePlay" 05. #define def_GlobalVariableReplay def_SymbolReplay + "_Infos" 06. #define def_GlobalVariableServerTime def_SymbolReplay + "_Time" 07. #define def_MaxPosSlider 400 08. //+------------------------------------------------------------------+ 09. union u_Interprocess 10. { 11. double df_Value; // Value of the terminal global variable... 12. struct st_0 13. { 14. bool isPlay; // Indicates whether we are in Play or Pause mode... 15. bool isWait; // Tells the user to wait... 16. bool isHedging; // If true we are in a Hedging account, if false the account is Netting... 17. bool isSync; // If true indicates that the service is synchronized... 18. ushort iPosShift; // Value between 0 and 400... 19. }s_Infos; 20. datetime ServerTime; 21. }; 22. //+------------------------------------------------------------------+ 23. union uCast_Double 24. { 25. double dValue; 26. long _long; // 1 Information 27. datetime _datetime; // 1 Information 28. int _int[sizeof(double)]; // 2 Informations 29. char _char[sizeof(double)]; // 8 Informations 30. }; 31. //+------------------------------------------------------------------+
ソースコード:Interprocess.mqh
このコードは見た目には大きな変化がないように見えますが、前のセクションで変更したシステムには重要な違いをもたらします。そのため、システムを再度コンパイルしようとする際に、表示されるエラーの数に驚いたり、不安になったりしないでください。必要なのは、以前と同じパフォーマンスを実現するためにすべてを適切に構成することだけです。しかし、これによりコントロール指標はユーザーに利用できなくなります。サービスが正しいチャートでの配置とサポートを担当するからです。
サービスコードをコンパイルしようとすると、コンパイラから次の出力が得られます。これを図06に示します。
図06:複数のエラー。しかし、本当にそんなにたくさんあるのでしょうか
エラーの数は非常に多く、警告も同様に多数存在します。上で述べたように、このリストの最初のエラーから始めます。したがって、変更はC_Replay.mqhヘッダーファイルの28行目から始まります。退屈にならないように、以下のコードを確認してみましょう。必要な作業のほとんどは、u_Valueを削除することです。この参照のないコードは次のとおりです。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "C_ConfigService.mqh" 005. //+------------------------------------------------------------------+ 006. class C_Replay : private C_ConfigService 007. { 008. private : 009. long m_IdReplay; 010. struct st01 011. { 012. MqlRates Rate[1]; 013. datetime memDT; 014. }m_MountBar; 015. struct st02 016. { 017. bool bInit; 018. double PointsPerTick; 019. MqlTick tick[1]; 020. }m_Infos; 021. //+------------------------------------------------------------------+ 022. void AdjustPositionToReplay(const bool bViewBuider) 023. { 024. u_Interprocess Info; 025. MqlRates Rate[def_BarsDiary]; 026. int iPos, nCount; 027. 028. Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); 029. if (Info.s_Infos.iPosShift == (int)((m_ReplayCount * def_MaxPosSlider * 1.0) / m_Ticks.nTicks)) return; 030. iPos = (int)(m_Ticks.nTicks * ((Info.s_Infos.iPosShift * 1.0) / (def_MaxPosSlider + 1))); 031. Rate[0].time = macroRemoveSec(m_Ticks.Info[iPos].time); 032. CreateBarInReplay(true); 033. if (bViewBuider) 034. { 035. Info.s_Infos.isWait = true; 036. GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value); 037. }else 038. { 039. for(; Rate[0].time > (m_Ticks.Info[m_ReplayCount].time); m_ReplayCount++); 040. for (nCount = 0; m_Ticks.Rate[nCount].time < macroRemoveSec(m_Ticks.Info[iPos].time); nCount++); 041. nCount = CustomRatesUpdate(def_SymbolReplay, m_Ticks.Rate, nCount); 042. } 043. for (iPos = (iPos > 0 ? iPos - 1 : 0); (m_ReplayCount < iPos) && (!_StopFlag);) CreateBarInReplay(false); 044. CustomTicksAdd(def_SymbolReplay, m_Ticks.Info, m_ReplayCount); 045. Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); 046. Info.s_Infos.isWait = false; 047. GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value); 048. } 049. //+------------------------------------------------------------------+ 050. inline void CreateBarInReplay(const bool bViewTicks) 051. { 052. #define def_Rate m_MountBar.Rate[0] 053. 054. bool bNew; 055. double dSpread; 056. int iRand = rand(); 057. 058. if (BuildBar1Min(m_ReplayCount, def_Rate, bNew)) 059. { 060. m_Infos.tick[0] = m_Ticks.Info[m_ReplayCount]; 061. if ((!m_Ticks.bTickReal) && (m_Ticks.ModePlot == PRICE_EXCHANGE)) 062. { 063. dSpread = m_Infos.PointsPerTick + ((iRand > 29080) && (iRand < 32767) ? ((iRand & 1) == 1 ? m_Infos.PointsPerTick : 0 ) : 0 ); 064. if (m_Infos.tick[0].last > m_Infos.tick[0].ask) 065. { 066. m_Infos.tick[0].ask = m_Infos.tick[0].last; 067. m_Infos.tick[0].bid = m_Infos.tick[0].last - dSpread; 068. }else if (m_Infos.tick[0].last < m_Infos.tick[0].bid) 069. { 070. m_Infos.tick[0].ask = m_Infos.tick[0].last + dSpread; 071. m_Infos.tick[0].bid = m_Infos.tick[0].last; 072. } 073. } 074. if (bViewTicks) CustomTicksAdd(def_SymbolReplay, m_Infos.tick); 075. CustomRatesUpdate(def_SymbolReplay, m_MountBar.Rate); 076. } 077. m_ReplayCount++; 078. #undef def_Rate 079. } 080. //+------------------------------------------------------------------+ 081. void ViewInfos(void) 082. { 083. MqlRates Rate[1]; 084. 085. ChartSetInteger(m_IdReplay, CHART_SHOW_ASK_LINE, m_Ticks.ModePlot == PRICE_FOREX); 086. ChartSetInteger(m_IdReplay, CHART_SHOW_BID_LINE, m_Ticks.ModePlot == PRICE_FOREX); 087. ChartSetInteger(m_IdReplay, CHART_SHOW_LAST_LINE, m_Ticks.ModePlot == PRICE_EXCHANGE); 088. m_Infos.PointsPerTick = SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE); 089. m_MountBar.Rate[0].time = 0; 090. m_Infos.bInit = true; 091. CopyRates(def_SymbolReplay, PERIOD_M1, 0, 1, Rate); 092. if ((m_ReplayCount == 0) && (m_Ticks.ModePlot == PRICE_EXCHANGE)) 093. for (; m_Ticks.Info[m_ReplayCount].volume_real == 0; m_ReplayCount++); 094. if (Rate[0].close > 0) 095. { 096. if (m_Ticks.ModePlot == PRICE_EXCHANGE) m_Infos.tick[0].last = Rate[0].close; else 097. { 098. m_Infos.tick[0].bid = Rate[0].close; 099. m_Infos.tick[0].ask = Rate[0].close + (Rate[0].spread * m_Infos.PointsPerTick); 100. } 101. m_Infos.tick[0].time = Rate[0].time; 102. m_Infos.tick[0].time_msc = Rate[0].time * 1000; 103. }else 104. m_Infos.tick[0] = m_Ticks.Info[m_ReplayCount]; 105. CustomTicksAdd(def_SymbolReplay, m_Infos.tick); 106. ChartRedraw(m_IdReplay); 107. } 108. //+------------------------------------------------------------------+ 109. void CreateGlobalVariable(const string szName, const double value) 110. { 111. GlobalVariableDel(szName); 112. GlobalVariableTemp(szName); 113. GlobalVariableSet(szName, value); 114. } 115. //+------------------------------------------------------------------+ 116. public : 117. //+------------------------------------------------------------------+ 118. C_Replay(const string szFileConfig) 119. { 120. m_ReplayCount = 0; 121. m_dtPrevLoading = 0; 122. m_Ticks.nTicks = 0; 123. m_Infos.bInit = false; 124. Print("************** Market Replay Service **************"); 125. srand(GetTickCount()); 126. GlobalVariableDel(def_GlobalVariableReplay); 127. SymbolSelect(def_SymbolReplay, false); 128. CustomSymbolDelete(def_SymbolReplay); 129. CustomSymbolCreate(def_SymbolReplay, StringFormat("Custom\\%s", def_SymbolReplay), _Symbol); 130. CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX); 131. CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX); 132. CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE, 0); 133. CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE, 0); 134. CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP, 0); 135. CustomSymbolSetString(def_SymbolReplay, SYMBOL_DESCRIPTION, "Symbol for replay / simulation"); 136. CustomSymbolSetInteger(def_SymbolReplay, SYMBOL_DIGITS, 8); 137. m_IdReplay = (SetSymbolReplay(szFileConfig) ? 0 : -1); 138. SymbolSelect(def_SymbolReplay, true); 139. } 140. //+------------------------------------------------------------------+ 141. ~C_Replay() 142. { 143. ArrayFree(m_Ticks.Info); 144. ArrayFree(m_Ticks.Rate); 145. m_IdReplay = ChartFirst(); 146. do 147. { 148. if (ChartSymbol(m_IdReplay) == def_SymbolReplay) 149. ChartClose(m_IdReplay); 150. }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0); 151. for (int c0 = 0; (c0 < 2) && (!SymbolSelect(def_SymbolReplay, false)); c0++); 152. CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX); 153. CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX); 154. CustomSymbolDelete(def_SymbolReplay); 155. GlobalVariableDel(def_GlobalVariableReplay); 156. GlobalVariableDel(def_GlobalVariableServerTime); 157. Print("Finished replay service..."); 158. } 159. //+------------------------------------------------------------------+ 160. bool ViewReplay(ENUM_TIMEFRAMES arg1) 161. { 162. #define macroError(A) { Print(A); return false; } 163. u_Interprocess info; 164. 165. if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE) == 0) 166. macroError("Asset configuration is not complete, it remains to declare the size of the ticket."); 167. if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE) == 0) 168. macroError("Asset configuration is not complete, need to declare the ticket value."); 169. if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP) == 0) 170. macroError("Asset configuration not complete, need to declare the minimum volume."); 171. if (m_IdReplay == -1) return false; 172. if ((m_IdReplay = ChartFirst()) > 0) do 173. { 174. if (ChartSymbol(m_IdReplay) == def_SymbolReplay) 175. { 176. ChartClose(m_IdReplay); 177. ChartRedraw(); 178. } 179. }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0); 180. Print("Waiting for [Market Replay] indicator permission to start replay ..."); 181. info.ServerTime = ULONG_MAX; 182. CreateGlobalVariable(def_GlobalVariableServerTime, info.df_Value); 183. m_IdReplay = ChartOpen(def_SymbolReplay, arg1); 184. ChartApplyTemplate(m_IdReplay, "Market Replay.tpl"); 185. while ((!GlobalVariableGet(def_GlobalVariableReplay, info.df_Value)) && (!_StopFlag) && (ChartSymbol(m_IdReplay) != "")) Sleep(750); 186. info.s_Infos.isHedging = TypeAccountIsHedging(); 187. info.s_Infos.isSync = true; 188. GlobalVariableSet(def_GlobalVariableReplay, info.df_Value); 189. 190. return ((!_StopFlag) && (ChartSymbol(m_IdReplay) != "")); 191. #undef macroError 192. } 193. //+------------------------------------------------------------------+ 194. bool LoopEventOnTime(const bool bViewBuider) 195. { 196. u_Interprocess Info; 197. int iPos, iTest, iCount; 198. 199. if (!m_Infos.bInit) ViewInfos(); 200. iTest = 0; 201. while ((iTest == 0) && (!_StopFlag)) 202. { 203. iTest = (ChartSymbol(m_IdReplay) != "" ? iTest : -1); 204. iTest = (GlobalVariableGet(def_GlobalVariableReplay, Info.df_Value) ? iTest : -1); 205. iTest = (iTest == 0 ? (Info.s_Infos.isPlay ? 1 : iTest) : iTest); 206. if (iTest == 0) Sleep(100); 207. } 208. if ((iTest < 0) || (_StopFlag)) return false; 209. AdjustPositionToReplay(bViewBuider); 210. Info.ServerTime = m_Ticks.Info[m_ReplayCount].time; 211. GlobalVariableSet(def_GlobalVariableServerTime, Info.df_Value); 212. iPos = iCount = 0; 213. while ((m_ReplayCount < m_Ticks.nTicks) && (!_StopFlag)) 214. { 215. iPos += (int)(m_ReplayCount < (m_Ticks.nTicks - 1) ? m_Ticks.Info[m_ReplayCount + 1].time_msc - m_Ticks.Info[m_ReplayCount].time_msc : 0); 216. CreateBarInReplay(true); 217. while ((iPos > 200) && (!_StopFlag)) 218. { 219. if (ChartSymbol(m_IdReplay) == "") return false; 220. GlobalVariableGet(def_GlobalVariableReplay, Info.df_Value); 221. if (!Info.s_Infos.isPlay) return true; 222. Info.s_Infos.iPosShift = (ushort)((m_ReplayCount * def_MaxPosSlider) / m_Ticks.nTicks); 223. GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value); 224. Sleep(195); 225. iPos -= 200; 226. iCount++; 227. if (iCount > 4) 228. { 229. iCount = 0; 230. GlobalVariableGet(def_GlobalVariableServerTime, Info.df_Value); 231. if ((m_Ticks.Info[m_ReplayCount].time - m_Ticks.Info[m_ReplayCount - 1].time) > 60) Info.ServerTime = ULONG_MAX; else 232. { 233. Info.ServerTime += 1; 234. Info.ServerTime = ((Info.ServerTime + 1) < m_Ticks.Info[m_ReplayCount].time ? Info.ServerTime : m_Ticks.Info[m_ReplayCount].time); 235. }; 236. GlobalVariableSet(def_GlobalVariableServerTime, Info.df_Value); 237. } 238. } 239. } 240. return (m_ReplayCount == m_Ticks.nTicks); 241. } 242. //+------------------------------------------------------------------+ 243. }; 244. //+------------------------------------------------------------------+ 245. #undef macroRemoveSec 246. #undef def_SymbolReplay 247. //+------------------------------------------------------------------+
ファイルC_Replay.mqhのソースコード
上記のコードは、この記事で説明する内容に合わせて修正されています。ただし、指標コードはまだ不足しており、C_Replay.mqhヘッダーファイルに変更を加えてサービスコードを再度コンパイルすると、結果は図07のようになります。
図07:指標にまだエラーが残っている
したがって、指標 コードを修正する必要があります。これを試みると、図08のような結果が得られます。
図08:同じ理由で発生するエラー
もう一度、ステップごとに進めていきます。常に最初のエラーから始めます。
次に、指標ファイルが正しく変更されると、次のコードが表示されます。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico" 04. #property description "Control indicator for the Replay-Simulator service." 05. #property description "This one doesn't work without the service loaded." 06. #property version "1.49" 07. #property link "https://www.mql5.com/ru/articles/11820" 08. #property indicator_chart_window 09. #property indicator_plots 0 10. //+------------------------------------------------------------------+ 11. #include <Market Replay\Service Graphics\C_Controls.mqh> 12. //+------------------------------------------------------------------+ 13. C_Terminal *terminal = NULL; 14. C_Controls *control = NULL; 15. //+------------------------------------------------------------------+ 16. #define def_InfoTerminal (*terminal).GetInfoTerminal() 17. #define def_ShortName "Market_" + def_SymbolReplay 18. //+------------------------------------------------------------------+ 19. int OnInit() 20. { 21. u_Interprocess Info; 22. 23. ResetLastError(); 24. if (CheckPointer(control = new C_Controls(terminal = new C_Terminal())) == POINTER_INVALID) return INIT_FAILED; 25. if (_LastError != ERR_SUCCESS) return INIT_FAILED; 26. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName); 27. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName + "Device"); 28. if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0; 29. EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.df_Value, ""); 30. (*control).Init(Info.s_Infos.isPlay); 31. 32. return INIT_SUCCEEDED; 33. } 34. //+------------------------------------------------------------------+ 35. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 36. { 37. static bool bWait = false; 38. u_Interprocess Info; 39. 40. Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); 41. if (!bWait) 42. { 43. if (Info.s_Infos.isWait) 44. { 45. EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOn, 1, 0, ""); 46. bWait = true; 47. } 48. }else if (!Info.s_Infos.isWait) 49. { 50. EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.df_Value, ""); 51. bWait = false; 52. } 53. 54. return rates_total; 55. } 56. //+------------------------------------------------------------------+ 57. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 58. { 59. (*control).DispatchMessage(id, lparam, dparam, sparam); 60. } 61. //+------------------------------------------------------------------+ 62. void OnDeinit(const int reason) 63. { 64. switch (reason) 65. { 66. case REASON_REMOVE: 67. case REASON_CHARTCLOSE: 68. if (def_InfoTerminal.szSymbol != def_SymbolReplay) break; 69. GlobalVariableDel(def_GlobalVariableReplay); 70. ChartClose(def_InfoTerminal.ID); 71. break; 72. } 73. delete control; 74. delete terminal; 75. } 76. //+------------------------------------------------------------------+
リプレイ指標のソースコード
このコードは完全に修正されましたが、コンパイルしようとすると、図09に示すように、コンパイラからいくつかのエラーが発生します。
図09:コントロール指標のコンパイルの試み
ここで、C_Controls.mqhヘッダーファイルに移動して、いくつかの修正をおこなう必要があります。ただし、これらの修正は非常に簡単です。u_Valueへの参照をすべて削除するだけです。その結果、次のコードになります。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\Auxiliar\Interprocess.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_PathBMP "Images\\Market Replay\\Control\\" 007. #define def_ButtonPlay def_PathBMP + "Play.bmp" 008. #define def_ButtonPause def_PathBMP + "Pause.bmp" 009. #define def_ButtonLeft def_PathBMP + "Left.bmp" 010. #define def_ButtonLeftBlock def_PathBMP + "Left_Block.bmp" 011. #define def_ButtonRight def_PathBMP + "Right.bmp" 012. #define def_ButtonRightBlock def_PathBMP + "Right_Block.bmp" 013. #define def_ButtonPin def_PathBMP + "Pin.bmp" 014. #define def_ButtonWait def_PathBMP + "Wait.bmp" 015. #resource "\\" + def_ButtonPlay 016. #resource "\\" + def_ButtonPause 017. #resource "\\" + def_ButtonLeft 018. #resource "\\" + def_ButtonLeftBlock 019. #resource "\\" + def_ButtonRight 020. #resource "\\" + def_ButtonRightBlock 021. #resource "\\" + def_ButtonPin 022. #resource "\\" + def_ButtonWait 023. //+------------------------------------------------------------------+ 024. #define def_PrefixObjectName "Market Replay _ " 025. #define def_NameObjectsSlider def_PrefixObjectName + "Slider" 026. #define def_PosXObjects 120 027. //+------------------------------------------------------------------+ 028. #include "..\Auxiliar\C_Terminal.mqh" 029. #include "..\Auxiliar\C_Mouse.mqh" 030. //+------------------------------------------------------------------+ 031. #define def_AcessTerminal (*Terminal) 032. #define def_InfoTerminal def_AcessTerminal.GetInfoTerminal() 033. //+------------------------------------------------------------------+ 034. class C_Controls : protected C_Mouse 035. { 036. protected: 037. enum EventCustom {ev_WaitOn, ev_WaitOff}; 038. private : 039. //+------------------------------------------------------------------+ 040. string m_szBtnPlay; 041. bool m_bWait; 042. struct st_00 043. { 044. string szBtnLeft, 045. szBtnRight, 046. szBtnPin, 047. szBarSlider, 048. szBarSliderBlock; 049. int posPinSlider, 050. posY, 051. Minimal; 052. }m_Slider; 053. C_Terminal *Terminal; 054. //+------------------------------------------------------------------+ 055. inline void CreateObjectBitMap(int x, int y, string szName, string Resource1, string Resource2 = NULL) 056. { 057. ObjectCreate(def_InfoTerminal.ID, szName, OBJ_BITMAP_LABEL, 0, 0, 0); 058. ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_XDISTANCE, def_PosXObjects + x); 059. ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_YDISTANCE, y); 060. ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_BMPFILE, 0, "::" + Resource1); 061. ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_BMPFILE, 1, "::" + (Resource2 == NULL ? Resource1 : Resource2)); 062. ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_ZORDER, 1); 063. } 064. //+------------------------------------------------------------------+ 065. inline void CreteBarSlider(int x, int size) 066. { 067. ObjectCreate(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJ_RECTANGLE_LABEL, 0, 0, 0); 068. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x); 069. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Slider.posY - 4); 070. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size); 071. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9); 072. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue); 073. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack); 074. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3); 075. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT); 076. //--- 077. ObjectCreate(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJ_RECTANGLE_LABEL, 0, 0, 0); 078. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x); 079. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Slider.posY - 9); 080. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19); 081. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown); 082. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED); 083. } 084. //+------------------------------------------------------------------+ 085. void CreateBtnPlayPause(bool state) 086. { 087. m_szBtnPlay = def_PrefixObjectName + "Play"; 088. CreateObjectBitMap(0, 25, m_szBtnPlay, (m_bWait ? def_ButtonWait : def_ButtonPause), (m_bWait ? def_ButtonWait : def_ButtonPlay)); 089. ObjectSetInteger(def_InfoTerminal.ID, m_szBtnPlay, OBJPROP_STATE, state); 090. } 091. //+------------------------------------------------------------------+ 092. void CreteCtrlSlider(void) 093. { 094. u_Interprocess Info; 095. 096. m_Slider.szBarSlider = def_NameObjectsSlider + " Bar"; 097. m_Slider.szBarSliderBlock = def_NameObjectsSlider + " Bar Block"; 098. m_Slider.szBtnLeft = def_NameObjectsSlider + " BtnL"; 099. m_Slider.szBtnRight = def_NameObjectsSlider + " BtnR"; 100. m_Slider.szBtnPin = def_NameObjectsSlider + " BtnP"; 101. m_Slider.posY = 40; 102. CreteBarSlider(77, 436); 103. CreateObjectBitMap(47, 25, m_Slider.szBtnLeft, def_ButtonLeft, def_ButtonLeftBlock); 104. CreateObjectBitMap(511, 25, m_Slider.szBtnRight, def_ButtonRight, def_ButtonRightBlock); 105. CreateObjectBitMap(0, m_Slider.posY, m_Slider.szBtnPin, def_ButtonPin); 106. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnPin, OBJPROP_ANCHOR, ANCHOR_CENTER); 107. if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0; 108. m_Slider.Minimal = Info.s_Infos.iPosShift; 109. PositionPinSlider(Info.s_Infos.iPosShift); 110. } 111. //+------------------------------------------------------------------+ 112. inline void RemoveCtrlSlider(void) 113. { 114. ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, false); 115. ObjectsDeleteAll(def_InfoTerminal.ID, def_NameObjectsSlider); 116. ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, true); 117. } 118. //+------------------------------------------------------------------+ 119. inline void PositionPinSlider(int p, const int minimal = 0) 120. { 121. m_Slider.posPinSlider = (p < minimal ? minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p)); 122. m_Slider.posPinSlider = (p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p)); 123. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnPin, OBJPROP_XDISTANCE, m_Slider.posPinSlider + def_PosXObjects + 95); 124. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != minimal); 125. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != m_Slider.Minimal); 126. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnRight, OBJPROP_STATE, m_Slider.posPinSlider < def_MaxPosSlider); 127. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, minimal + 2); 128. ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2); 129. ChartRedraw(); 130. } 131. //+------------------------------------------------------------------+ 132. public : 133. //+------------------------------------------------------------------+ 134. C_Controls(C_Terminal *arg) 135. :C_Mouse(arg), 136. m_bWait(false) 137. { 138. if (CheckPointer(Terminal = arg) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid); 139. m_szBtnPlay = NULL; 140. m_Slider.szBarSlider = NULL; 141. m_Slider.szBtnPin = NULL; 142. m_Slider.szBtnLeft = NULL; 143. m_Slider.szBtnRight = NULL; 144. } 145. //+------------------------------------------------------------------+ 146. ~C_Controls() 147. { 148. if (CheckPointer(Terminal) == POINTER_INVALID) return; 149. ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, false); 150. ObjectsDeleteAll(def_InfoTerminal.ID, def_PrefixObjectName); 151. } 152. //+------------------------------------------------------------------+ 153. void Init(const bool state) 154. { 155. CreateBtnPlayPause(state); 156. GlobalVariableTemp(def_GlobalVariableReplay); 157. if (!state) CreteCtrlSlider(); 158. ChartRedraw(); 159. } 160. //+------------------------------------------------------------------+ 161. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 162. { 163. u_Interprocess Info; 164. static int six = -1, sps; 165. int x, y, px1, px2; 166. 167. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 168. switch (id) 169. { 170. case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOn): 171. if (lparam == 0) break; 172. m_bWait = true; 173. CreateBtnPlayPause(true); 174. break; 175. case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOff): 176. if (lparam == 0) break; 177. m_bWait = false; 178. Info.df_Value = dparam; 179. CreateBtnPlayPause(Info.s_Infos.isPlay); 180. break; 181. case CHARTEVENT_OBJECT_DELETE: 182. if (StringSubstr(sparam, 0, StringLen(def_PrefixObjectName)) == def_PrefixObjectName) 183. { 184. if (StringSubstr(sparam, 0, StringLen(def_NameObjectsSlider)) == def_NameObjectsSlider) 185. { 186. RemoveCtrlSlider(); 187. CreteCtrlSlider(); 188. }else 189. { 190. Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); 191. CreateBtnPlayPause(Info.s_Infos.isPlay); 192. } 193. ChartRedraw(); 194. } 195. break; 196. case CHARTEVENT_OBJECT_CLICK: 197. if (m_bWait) break; 198. if (sparam == m_szBtnPlay) 199. { 200. Info.s_Infos.isPlay = (bool) ObjectGetInteger(def_InfoTerminal.ID, m_szBtnPlay, OBJPROP_STATE); 201. if (!Info.s_Infos.isPlay) CreteCtrlSlider(); else 202. { 203. RemoveCtrlSlider(); 204. m_Slider.szBtnPin = NULL; 205. } 206. Info.s_Infos.iPosShift = (ushort) m_Slider.posPinSlider; 207. GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value); 208. ChartRedraw(); 209. }else if (sparam == m_Slider.szBtnLeft) PositionPinSlider(m_Slider.posPinSlider - 1); 210. else if (sparam == m_Slider.szBtnRight) PositionPinSlider(m_Slider.posPinSlider + 1); 211. break; 212. case CHARTEVENT_MOUSE_MOVE: 213. if (GetInfoMouse().ExecStudy) return; 214. if ((CheckClick(C_Mouse::eClickLeft)) && (m_Slider.szBtnPin != NULL)) 215. { 216. x = GetInfoMouse().Position.X; 217. y = GetInfoMouse().Position.Y; 218. px1 = m_Slider.posPinSlider + def_PosXObjects + 86; 219. px2 = m_Slider.posPinSlider + def_PosXObjects + 114; 220. if ((y >= (m_Slider.posY - 14)) && (y <= (m_Slider.posY + 14)) && (x >= px1) && (x <= px2) && (six == -1)) 221. { 222. six = x; 223. sps = m_Slider.posPinSlider; 224. ChartSetInteger(def_InfoTerminal.ID, CHART_MOUSE_SCROLL, false); 225. } 226. if (six > 0) PositionPinSlider(sps + x - six); 227. }else if (six > 0) 228. { 229. six = -1; 230. ChartSetInteger(def_InfoTerminal.ID, CHART_MOUSE_SCROLL, true); 231. } 232. break; 233. } 234. } 235. //+------------------------------------------------------------------+ 236. }; 237. //+------------------------------------------------------------------+ 238. #undef def_InfoTerminal 239. #undef def_AcessTerminal 240. #undef def_PosXObjects 241. #undef def_ButtonPlay 242. #undef def_ButtonPause 243. #undef def_ButtonLeft 244. #undef def_ButtonRight 245. #undef def_ButtonPin 246. #undef def_NameObjectsSlider 247. #undef def_PrefixObjectName 248. #undef def_PathBMP 249. //+------------------------------------------------------------------+
C_Controls.mqhファイルのソースコード
これらの修正、変更、調整をおこなった後、リプレイ/シミュレーターサービスを再度コンパイルしてみます。その結果、次のようになります。
図10:最終コンパイル
これは、サービスが正常にコンパイルされたことを示しており、指標も無事にコンパイルされたことに注意してください。指標の実行可能ファイルはリプレイ/シミュレーターサービスの一部となるため、ファイルエクスプローラーを使って指標の実行可能ファイルを削除することができます。ただし、指標の実行可能ファイルを削除し、MetaTrader 5でリプレイ/シミュレーターシステムを実行すると、チャートは開くもののコントロール指標が表示されません。その理由は何でしょうか。
コントロール指標は、実際にはサービスによってではなくテンプレートによってトリガーされるためです。コントロール指標がサービスの実行可能ファイルの一部であることを示す情報は、テンプレートには含まれていません。テンプレートファイルの内容を確認すれば、これに気づくでしょう。しかし、私たちの目的はテンプレートを使用するのではなく、サービスを介してチャート上でコントロール指標を実行することなので、この問題の解決策については詳細には触れません。本当に注目すべき点に焦点を当てていきましょう。
必要な作業はそれほど単純ではなく、この記事では詳細を述べたくないため、変更については次回に持ち越します。コントロール指標を機能させながら、リプレイ/シミュレーターサービスを無料で軽量かつオープンに保ち、指標や戦略、個人モデルを使用できるようにするためには、多くの変更が必要だからです。これらの変更の背後にある考え方は、まさにこれを促進することにあります。独自の概念やアイデアでシステムを活用できるようにするためです。
しかし、サービスリソースとして、テンプレートを使用せずにコントロール指標をチャートに表示するために必要なすべての手順のデモンストレーションは、次回の記事に残しておきます。その理由は、現在重複している一部を削除する必要があるからです。このような重複が存在すると、サービスを介して直接使用する際にコントロール指標が非常に不安定になる恐れがあります。
結論
この記事では、コントロール指標とサービスに変更を加え、指標の実行可能ファイルをユーザーがアクセスできるファイルのリストから削除することができましたが、システム自体は未解決の問題のために不安定であることが判明しました。これらの問題は、コントロール指標とユーザーのやり取りに関連しています。この不安定さは、チャート上に存在する必要がある他の要素の必要性によって引き起こされます。これらの要素は、リプレイ/シミュレーターサービスを利用する際には使用したくないテンプレートファイルの一部です。ユーザーが独自の設定やテンプレートを使用できるようにするため、サービスを自立させる方法について説明します。
同時に、分析戦略やモデルを実践できるように、リプレイ/シミュレーターサービスを促進するための適切な方法を構築します。
このシステムが、リプレイおよびシミュレーター モードで注文やポジションを操作するという待望の機能を実現するためには、まだ多くの課題があります。親愛なる読者の皆さんには、私たちが到達した複雑さのレベルと程度を理解していただければ幸いです。そして、これらすべては外部プログラミングなしでMQL5の助けを借りてのみ行われています。確かに、このシステムは非常に複雑であることがわかりましたが、私は挑戦が好きであり、それが私にインスピレーションを与え続けています。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/11820
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索