リプレイシステムの開発(第54回):最初のモジュールの誕生
はじめに
前回の「リプレイシステムの開発(第53回):物事は複雑になる(V)」稿では、これからプログラミングの一部となり、MQL5とMetaTrader 5プラットフォームで使用されるいくつかの概念について説明しました。これらの概念の多くは、ほとんどの読者にとって新しいものである一方、Windowsシステムなどのシステムプログラミングの経験がある方には馴染みのあるものだと認識しています。
したがって、これからお見せする内容が「なぜ」「どのように」機能するのかを本当に理解したい方は、プログラム間でメッセージがどのようにやり取りされるのかを知るために、少しWindowsプログラミングを学ぶことをお勧めします。ただし、この記事で取り上げる情報は、私が本当にお伝えしたいこと(つまり、MQL5やMetaTrader 5をより高度なレベルで開発・活用する方法)から離れてしまう内容になります。
Windows環境でこのメッセージ交換を使用して通信をおこなうプログラムを見つけること自体は難しくありませんが、それらを適切に理解するには、事前に一定の知識とC言語プログラミングのしっかりとした基礎が必要です。この基礎知識がまだない場合は、まずC言語プログラミングを学び、次にWindows環境でプログラム間のメッセージ交換がどのようにおこなわれるのかを学ぶことをお勧めします。そうすることで、今後の取り組みを理解するために必要な幅広く堅固な土台を築くことができるでしょう。
物事を可能にする
前回の記事を注意深く読んでいただけたなら、私が長い間何かを試みていたことにお気づきになったかもしれません。しかし、すべての要素が部分的に機能していたとしても、それらがより大きなシステム内で共存することはできませんでした。少なくとも、現在の進捗状況ではそれが不可能だったのです。
おそらく、私が犯した最大のミスは、数週間にわたり、アプリケーションがMetaTrader 5プラットフォームのイベントに応答する仕組みを無視していたことです。しかし、実際の問題はそれだけではありませんでした。私はMetaTrader 5をプラットフォームとしてではなく、他のプロセスが実行される単なるプログラムとして捉えていたのです。
このような認識の欠陥が生じたのは、他のプラットフォームがMetaTrader 5ほどの柔軟性を提供していないことに原因があります。このため、より高度な方法でアプリケーションを開発する際、時間を浪費し、開発速度が落ちてしまいました。ただし、このリプレイ/シミュレーターシステムは、一般的におこなわれる方法とは異なるモジュール方式で開発するほうが適していることが分かりました。そのため、いくつかの課題に直面しましたが、それは単なる課題ではなく、私が見落としていた点に起因するものでした。
前回の記事で紹介したビデオをご覧になった方は、MetaTrader 5が私たちがこれまでに探求してきた以上の多くの機能を提供していることに気づいたかもしれません。しかし、今回は単なる夢のように思えたことを、実現可能なものへと変えることに挑戦します。プログラミング作業を減らし、構築に注力していきます。この記事を皮切りに、メッセージ交換の仕組みを探り、MetaTrader 5をより効率的に動作させる方法に焦点を当てます。目指すのは、アプリケーションがチャート上の他の要素と調和して動作する環境を構築することです。
まず最初におこなうのは、マウス指標を調整し、MetaTrader 5アプリケーション開発の新たなフェーズを始めることです。
コードには大幅な変更が加えられるため、多くの箇所を書き換える必要があります。ただし、これまでの手順をきちんと追ってきた方であれば、これらの変更に苦労することはないでしょう。
そこで、今後作成するすべてのアプリケーションに共通するファイルをまず用意します。このファイルによって、私たちが作成するすべてのコードがメッセージを処理できるようになります。また、メッセージハンドラを持つ複数のアプリケーションがチャート上に存在する場合に、それぞれが正しくメッセージを処理できるよう制御することも可能になります。
この共通ファイルの初期内容は以下の通りです。このファイルはDefines.mqhという名前で保存する必要があります。その保存場所については、後ほど詳しく説明します。もし、プログラミングについて全くの初心者であれば、申し訳ありませんが、これから私が実装する内容を理解することは難しいでしょう。この瞬間から、基本的なプログラミング知識がない方には越えられない壁が立ちはだかります。本日取り上げる内容を実際に活用したい場合は、最低限のプログラミング基礎を習得する必要があります。
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. //+------------------------------------------------------------------+ 16. union uCast_Double 17. { 18. double dValue; 19. long _long; // 1 Information 20. datetime _datetime; // 1 Information 21. int _int[sizeof(double) / sizeof(int)]; // 2 Informations 22. char _char[sizeof(double) / sizeof(char)]; // 8 Informations 23. }; 24. //+------------------------------------------------------------------+ 25. enum EnumEvents { 26. evHideMouse, //Hide mouse price line 27. evShowMouse, //Show mouse price line 28. evHideBarTime, //Hide bar time 29. evShowBarTime, //Show bar time 30. evHideDailyVar, //Hide daily variation 31. evShowDailyVar, //Show daily variation 32. evHidePriceVar, //Hide instantaneous variation 33. evShowPriceVar, //Show instantaneous variation 34. evSetServerTime, //Replay/simulation system timer 35. evCtrlReplayInit //Initialize replay control 36. }; 37. //+------------------------------------------------------------------+
Defines.mqhファイルのソースコード
次に13行目では、カスタム銘柄の名前を定義します。この名前はリプレイ/シミュレーターサービスで使用されます。これは、本連載の最初の記事から継続して使用しているものです。続く14行目では、コントロール指標でのみ使用される要素を宣言しています。
これら2行、および16行目から23行目の間にある部分は、以前に作成されたコードに含まれていました。しかし、今回からInterProcess.mqhヘッダファイルは廃止されるため、その内容を定義ファイル内に移動する必要が生じました。
このファイルで特に注目すべき部分は25行目以降です。ここでは、将来的に新しいイベントが追加されるたびに拡張されるリストを定義しています。ここで注意すべき重要な点があります。それは、列挙をリストの最後に必ず追加するということです。これにより、古いコードを再コンパイルする必要がなくなります。しかし、リストの途中に新しい列挙を追加すると、過去のすべてのコードを再コンパイルする必要が出てきます。したがって、このルールを守ることで潜在的な問題を回避できます。
各行でイベントごとに値を定義し、対応するイベントの簡単なコメントを記入していることに注意してください。
この後のコード例を見れば、それぞれのイベントがどの場所でどのように発生するかが具体的にわかるようになります。ただし、イベント処理の重要な部分はコード内だけで確認できるものになります。そのため、これらの仕組みを使用する予定がある場合は、メッセージハンドラに注意を払うことが必要です。
こうして、システム全体を完全にモジュール化するための準備が整います。これからは、コードの内容やチャート上に表示される情報に注意を払い、MetaTrader 5を本格的に使いこなす時が来ました。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #include "Macros.mqh" 05. #include "..\Defines.mqh" 06. #include "Interprocess.mqh" 07. //+------------------------------------------------------------------+ 08. class C_Terminal 09. { 10. //+------------------------------------------------------------------+ 11. protected: 12. enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance}; 13. //+------------------------------------------------------------------+ 14. struct st_Terminal
C_Terminal.mqhファイルのコード
5行目をよく見てください。ここでは、C_Terminal.mqhヘッダファイルを基準とした、上記のDefines.mqhヘッダファイルの場所が指定されています。しかし、私が皆さんに注目していただきたいのはそれだけではありません。6行目がコードから削除されていることに注意してください。つまり、InterProcess.mqhヘッダファイルは使用されなくなるため、プロジェクトから削除できます。
この変更は非常に単純であり、C_Terminal.mqhファイルコードには他の変更が加えられていないため、ファイル全体を複製する必要はないと思います。大きな変更はありませんが、1つだけ言及する必要があります。そうしないと、表示されるコードをコンパイルするときに問題が発生します。
12行目には、新しい値が割り当てられた列挙が含まれています。テスト中に、同じ指標がチャート上にすでに存在しているかどうかを確認するために使用する必要があります。したがって、同じヘッダファイルC_Terminal.mqhにもう1つ小さな変更を加える必要があります。この変更は次のコードに示されています。
157. //+------------------------------------------------------------------+ 158. bool IndicatorCheckPass(const string szShortName) 159. { 160. string szTmp = szShortName + "_TMP"; 161. 162. if (_LastError != ERR_SUCCESS) return false; 163. IndicatorSetString(INDICATOR_SHORTNAME, szTmp); 164. if (ChartWindowFind(m_Infos.ID, szShortName) != -1) 165. { 166. ChartIndicatorDelete(m_Infos.ID, 0, szTmp); 167. Print("Only one instance is allowed..."); 168. SetUserError(C_Terminal::ERR_NoMoreInstance); 169. 170. return false; 171. } 172. IndicatorSetString(INDICATOR_SHORTNAME, szShortName); 173. ResetLastError(); 174. 175. return true; 176. } 177. //+------------------------------------------------------------------+
C_Terminal.mqhファイルで変更する必要がある部分
C_Terminal.mqhファイルにある元の関数をフラグメント2に示すコードに置き換える必要があります。したがって、テスト中に、システムはエラーが発生したことを示す_LastError定数を正しく設定します。このエラーは、チャート上に同じ指標の別のインスタンスが存在するために発生しました。これら 2つの簡単な変更以外には変更はおこなわれていないため、先に進んでマウス指標 クラスの開発を開始できます。
以下は、C_Mouse.mqhヘッダファイルの完全なコードです。コードを正しく理解するのは難しい場合もあるため、何が起こっているのか簡単に説明します。そうすれば、すべてがモジュール化され始める様子がよくわかるでしょう。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "C_Terminal.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_MousePrefixName "MouseBase_" 007. #define def_NameObjectLineH def_MousePrefixName + "H" 008. #define def_NameObjectLineV def_MousePrefixName + "TV" 009. #define def_NameObjectLineT def_MousePrefixName + "TT" 010. #define def_NameObjectStudy def_MousePrefixName + "TB" 011. //+------------------------------------------------------------------+ 012. class C_Mouse : public C_Terminal 013. { 014. public : 015. enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay}; 016. enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10}; 017. struct st_Mouse 018. { 019. struct st00 020. { 021. int X_Adjusted, 022. Y_Adjusted, 023. X_Graphics, 024. Y_Graphics; 025. double Price; 026. datetime dt; 027. }Position; 028. uint ButtonStatus; 029. bool ExecStudy; 030. datetime TimeDevice; 031. }; 032. //+------------------------------------------------------------------+ 033. protected: 034. //+------------------------------------------------------------------+ 035. void CreateObjToStudy(int x, int w, string szName, color backColor = clrNONE) const 036. { 037. if (m_Mem.szShortName != NULL) return; 038. CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE); 039. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_STATE, true); 040. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BORDER_COLOR, clrBlack); 041. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_COLOR, clrBlack); 042. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BGCOLOR, backColor); 043. ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_FONT, "Lucida Console"); 044. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_FONTSIZE, 10); 045. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 046. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XDISTANCE, x); 047. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1); 048. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XSIZE, w); 049. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YSIZE, 18); 050. } 051. //+------------------------------------------------------------------+ 052. private : 053. enum eStudy {eStudyNull, eStudyCreate, eStudyExecute}; 054. struct st01 055. { 056. st_Mouse Data; 057. color corLineH, 058. corTrendP, 059. corTrendN; 060. eStudy Study; 061. }m_Info; 062. struct st_Mem 063. { 064. bool CrossHair, 065. IsFull; 066. datetime dt; 067. string szShortName; 068. }m_Mem; 069. bool m_OK; 070. //+------------------------------------------------------------------+ 071. void GetDimensionText(const string szArg, int &w, int &h) 072. { 073. TextSetFont("Lucida Console", -100, FW_NORMAL); 074. TextGetSize(szArg, w, h); 075. h += 5; 076. w += 5; 077. } 078. //+------------------------------------------------------------------+ 079. void CreateStudy(void) 080. { 081. if (m_Mem.IsFull) 082. { 083. CreateObjectGraphics(def_NameObjectLineV, OBJ_VLINE, m_Info.corLineH); 084. CreateObjectGraphics(def_NameObjectLineT, OBJ_TREND, m_Info.corLineH); 085. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_WIDTH, 2); 086. CreateObjToStudy(0, 0, def_NameObjectStudy); 087. } 088. m_Info.Study = eStudyCreate; 089. } 090. //+------------------------------------------------------------------+ 091. void ExecuteStudy(const double memPrice) 092. { 093. double v1 = GetInfoMouse().Position.Price - memPrice; 094. int w, h; 095. 096. if (!CheckClick(eClickLeft)) 097. { 098. m_Info.Study = eStudyNull; 099. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 100. if (m_Mem.IsFull) ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName + "T"); 101. }else if (m_Mem.IsFull) 102. { 103. string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ", 104. MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetInfoMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0))); 105. GetDimensionText(sz1, w, h); 106. ObjectSetString(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_TEXT, sz1); 107. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP)); 108. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XSIZE, w); 109. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YSIZE, h); 110. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XDISTANCE, GetInfoMouse().Position.X_Adjusted - w); 111. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - (v1 < 0 ? 1 : h)); 112. ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 1, GetInfoMouse().Position.dt, GetInfoMouse().Position.Price); 113. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_COLOR, (memPrice > GetInfoMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP)); 114. } 115. m_Info.Data.ButtonStatus = eKeyNull; 116. } 117. //+------------------------------------------------------------------+ 118. public : 119. //+------------------------------------------------------------------+ 120. C_Mouse(const long id, const string szShortName) 121. :C_Terminal(id), 122. m_OK(false) 123. { 124. m_Mem.szShortName = szShortName; 125. } 126. //+------------------------------------------------------------------+ 127. C_Mouse(const long id, const string szShortName, color corH, color corP, color corN) 128. :C_Terminal(id) 129. { 130. if (!(m_OK = IndicatorCheckPass(szShortName))) SetUserError(C_Terminal::ERR_Unknown); 131. if (_LastError != ERR_SUCCESS) return; 132. m_Mem.szShortName = NULL; 133. m_Mem.CrossHair = (bool)ChartGetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL); 134. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, true); 135. ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, false); 136. ZeroMemory(m_Info); 137. m_Info.corLineH = corH; 138. m_Info.corTrendP = corP; 139. m_Info.corTrendN = corN; 140. m_Info.Study = eStudyNull; 141. if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE)) 142. CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH); 143. } 144. //+------------------------------------------------------------------+ 145. ~C_Mouse() 146. { 147. if (!m_OK) return; 148. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false); 149. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, false); 150. ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair); 151. ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName); 152. } 153. //+------------------------------------------------------------------+ 154. inline bool CheckClick(const eBtnMouse value) 155. { 156. return (GetInfoMouse().ButtonStatus & value) == value; 157. } 158. //+------------------------------------------------------------------+ 159. inline const st_Mouse GetInfoMouse(void) 160. { 161. if (m_Mem.szShortName != NULL) 162. { 163. double Buff[]; 164. uCast_Double loc; 165. int handle = ChartIndicatorGet(GetInfoTerminal().ID, 0, m_Mem.szShortName); 166. 167. ZeroMemory(m_Info.Data); 168. if (CopyBuffer(handle, 0, 0, 6, Buff) == 6) 169. { 170. m_Info.Data.Position.Price = Buff[0]; 171. loc.dValue = Buff[1]; 172. m_Info.Data.Position.dt = loc._datetime; 173. loc.dValue = Buff[2]; 174. m_Info.Data.Position.X_Adjusted = loc._int[0]; 175. m_Info.Data.Position.Y_Adjusted = loc._int[1]; 176. loc.dValue = Buff[3]; 177. m_Info.Data.Position.X_Graphics = loc._int[0]; 178. m_Info.Data.Position.Y_Graphics = loc._int[1]; 179. loc.dValue = Buff[4]; 180. m_Info.Data.ButtonStatus = loc._char[0]; 181. m_Info.Data.TimeDevice = (datetime)Buff[5]; 182. IndicatorRelease(handle); 183. } 184. } 185. 186. return m_Info.Data; 187. } 188. //+------------------------------------------------------------------+ 189. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 190. { 191. int w = 0; 192. static double memPrice = 0; 193. 194. if (m_Mem.szShortName == NULL) 195. { 196. C_Terminal::DispatchMessage(id, lparam, dparam, sparam); 197. switch (id) 198. { 199. case (CHARTEVENT_CUSTOM + evHideMouse): 200. if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE); 201. break; 202. case (CHARTEVENT_CUSTOM + evShowMouse): 203. if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH); 204. break; 205. case (CHARTEVENT_CUSTOM + evSetServerTime): 206. m_Info.Data.TimeDevice = (datetime)dparam; 207. break; 208. case CHARTEVENT_MOUSE_MOVE: 209. ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X_Graphics = (int)lparam, m_Info.Data.Position.Y_Graphics = (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price); 210. if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price)); 211. m_Info.Data.Position.dt = AdjustTime(m_Info.Data.Position.dt); 212. ChartTimePriceToXY(GetInfoTerminal().ID, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price, m_Info.Data.Position.X_Adjusted, m_Info.Data.Position.Y_Adjusted); 213. if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0); 214. m_Info.Data.ButtonStatus = (uint) sparam; 215. if (CheckClick(eClickMiddle)) 216. if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy(); 217. if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate)) 218. { 219. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 220. if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Mem.dt = GetInfoMouse().Position.dt, memPrice = GetInfoMouse().Position.Price); 221. m_Info.Study = eStudyExecute; 222. } 223. if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice); 224. m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute; 225. break; 226. case CHARTEVENT_OBJECT_DELETE: 227. if ((m_Mem.IsFull) && (sparam == def_NameObjectLineH)) CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH); 228. break; 229. } 230. } 231. } 232. //+------------------------------------------------------------------+ 233. }; 234. //+------------------------------------------------------------------+ 235. #undef def_NameObjectLineV 236. #undef def_NameObjectLineH 237. #undef def_NameObjectLineT 238. #undef def_NameObjectStudy 239. //+------------------------------------------------------------------+
C_Mouse.mqhファイルのソースコード
以前のバージョンとは異なり、現在では、必要なファイルはC_Terminal.mqhのみと見なされていますただし、上記に示すように、このクラスで変更されたその他の詳細にも注意する必要があります。
ご覧のとおり、30行目には新しい変数が追加されています。これは、リプレイ/シミュレータサービスを使用する場合にのみ有効です。この変数により、以前にグローバル端末変数を通じて取得された、リプレイ/シミュレーターサービスによって提供される値にアクセスできるようになります。この変数は30行目で宣言されていますが、他の場所でも使用されます。しかし、後で説明する別の問題のため、ここで C_Mouse クラスに追加する必要がありました。
ご覧のとおり、コードの大部分は168行目まで前のコードと同一ですが、168行目には異なる点が見つかります。ここで、6つのバッファ位置を使用します。6番目の位置は、30行目で宣言された変数によって占められる必要があります。したがって、181行目では、値を読み取ってその変数の値を確認する必要があるときに値を入力します。ここで少し立ち止まって説明しなければなりません。30行目で宣言された変数の内容を実際に使用する唯一の手順は、リプレイ/シミュレーターサービスとマウス指標です。後者は、この値を使用して、次のバーが開くまでの残り時間をユーザーに通知します。ただし、使用しない場合は、マウス指標またはリプレイ/シミュレーターサービスから削除できます。こうすれば問題は起きません。この情報を使用する場合、グローバル端末変数またはその他の手段を使用してリプレイ/シミュレーターサービスに時間値を保存しない限り、そのサービスはこのデータをいつ再度更新する必要があるかを把握できないことに注意してください。
このため、マウス指標バッファに値を配置しますが、ユーザーがチャートの時間枠を変更した後、MetaTrader 5がチャート上の指標を復元する際にこの値が削除されてリセットされるのではないかという疑問が生じます。その場合、指標バッファに保存しても意味がありません。実際、これこそが私たちが望んでいることなのです。サービスは、バッファ内の値が有効でなくなったことを検出すると、マウス指標にイベントを送信して値を再度更新します。こうすることで、すべてが期待どおりに保たれます。
つまり、MetaTrader 5が指標を強制的にバッファをゼロにすることで、リプレイ/シミュレーターサービスは、ユーザーがリプレイ/シミュレーターサービスによって再評価される必要がある変更を行ったことを認識します。こうすることで、アプリケーションは何かが起こったことを簡単に検出し、MetaTrader 5がすべての処理を実行します。
しかし、C_Mouseクラスのメッセージハンドラが配置されている189行目から始まるコードを見ると、疑問はさらに興味深いものになります。
このメッセージ処理手順には、モジュラーシステムが処理を開始する最初の3つのメッセージがあります。だから、何が起こっているかに注意してください。まず、メッセージ処理がクラスを使用する他のコードではなく指標によって行われていることを確認するために、指標を正確に呼び出しているかどうかを確認する必要があります。このチェックは194行目で実行されます。チェックが成功した場合、指標を扱っているとみなされるため、潜在的な利益相反を回避するために、チャート上にマウス指標を1つだけ配置する必要があります。これについては以前に他の記事で取り上げたことがあるのですが、さらに詳しく見ていきましょう。
199行目では、マウス指標が価格ラインとして使用されるラインを非表示にするイベントを処理します。
202行目では、価格線をチャートに再度表示するようにマウス指標に伝えるイベントを処理します。この方法により、どのアプリケーションでも、使用している価格ラインをいつ表示または非表示にするかをマウス指標に指示できるようになります。
どちらのイベントでも追加のパラメータを指定する必要はありません。そのため、マウスラインを表示または非表示にするアプリケーションでは、指定された値を使用してカスタムイベントを生成し、このイベントをマウス指標が配置されているチャートに送信するだけです。これについては後で詳しく説明します。現時点では、次のように理解できます。ある時点でマウス指標がチャート上に表示され、マウスラインを非表示にしたい場合、マウスラインを非表示または表示するには、目的の値を持つカスタムイベントを生成する必要があります。このイベントは、たとえばエキスパートアドバイザーによってトリガーされる可能性があります。
ここでも実装する3番目のイベントは、205行目に示されています。この場合、サービス実行時間として設定する値を指定します。つまり、実際にこれを実行するのはリプレイ/シミュレーターサービスです。なぜなら、そのようなイベントを生成する際に何らかの利点を得られるのはサービスだけだからです。しかし、ここで重要な点が1つあります。このイベントが発生するときは、時間値を指定する必要があるということです。この値は、double値であるdparamパラメータ内にある必要があります。
もう一度言いますが、物事をより広い視点から見る必要があります。double値は、datetime値と同様に8バイトで構成されます。datetime値も8バイトで構成されます。したがって、コンパイラが何を実行しているかを理解できるように、型変換を実行します。しかし、プロセッサにとっては、8バイトを変数に格納するだけです。これらのバイトの内容は重要ではありません。
これを理解することは重要です。なぜなら、値の文字列全体、場合によっては構造体全体を渡す必要がある状況があり、MetaTrader 5、より正確にはMQL5ではC/C++のようにポインターを使用できないため、MetaTrader 5で実行されているアプリケーション間でこのデータを渡すには何らかのトリックが必要になるからです。
はい、仕事の最初の部分は完了しました。しかし、私たちはまだ物事をより良くしていくつもりです。注意深く見ていれば、マウス指標に関連する他のイベントがあることに気付いたかもしれません。ただし、これらのイベントはヘッダファイルC_Mouse.mqhではなく、ファイルC_Study.mqhにあります。これらのイベントを確認し、それらがどのように機能するかを理解するため、以下のコードを参照してください。完全なコードを以下に示します。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\C_Mouse.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_ExpansionPrefix def_MousePrefixName + "Expansion_" 007. #define def_ExpansionBtn1 def_ExpansionPrefix + "B1" 008. #define def_ExpansionBtn2 def_ExpansionPrefix + "B2" 009. #define def_ExpansionBtn3 def_ExpansionPrefix + "B3" 010. //+------------------------------------------------------------------+ 011. class C_Study : public C_Mouse 012. { 013. private : 014. //+------------------------------------------------------------------+ 015. struct st00 016. { 017. eStatusMarket Status; 018. MqlRates Rate; 019. string szInfo; 020. color corP, 021. corN; 022. int HeightText; 023. bool bvT, bvD, bvP; 024. }m_Info; 025. //+------------------------------------------------------------------+ 026. const datetime GetBarTime(void) 027. { 028. datetime dt; 029. int i0 = PeriodSeconds(); 030. 031. if (m_Info.Status == eInReplay) 032. { 033. if ((dt = GetInfoMouse().TimeDevice) == ULONG_MAX) return ULONG_MAX; 034. }else dt = TimeCurrent(); 035. if (m_Info.Rate.time <= dt) 036. m_Info.Rate.time = (datetime)(((ulong) dt / i0) * i0) + i0; 037. 038. return m_Info.Rate.time - dt; 039. } 040. //+------------------------------------------------------------------+ 041. void Draw(void) 042. { 043. double v1; 044. 045. if (m_Info.bvT) 046. { 047. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 18); 048. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_TEXT, m_Info.szInfo); 049. } 050. if (m_Info.bvD) 051. { 052. v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / GetInfoMouse().Position.Price) * 100.0), 2); 053. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1); 054. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP)); 055. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1))); 056. } 057. if (m_Info.bvP) 058. { 059. v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / iClose(GetInfoTerminal().szSymbol, PERIOD_D1, 0)) * 100.0), 2); 060. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1); 061. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP)); 062. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1))); 063. } 064. } 065. //+------------------------------------------------------------------+ 066. inline void CreateObjInfo(EnumEvents arg) 067. { 068. switch (arg) 069. { 070. case evShowBarTime: 071. C_Mouse::CreateObjToStudy(2, 110, def_ExpansionBtn1, clrPaleTurquoise); 072. m_Info.bvT = true; 073. break; 074. case evShowDailyVar: 075. C_Mouse::CreateObjToStudy(2, 53, def_ExpansionBtn2); 076. m_Info.bvD = true; 077. break; 078. case evShowPriceVar: 079. C_Mouse::CreateObjToStudy(58, 53, def_ExpansionBtn3); 080. m_Info.bvP = true; 081. break; 082. } 083. } 084. //+------------------------------------------------------------------+ 085. inline void RemoveObjInfo(EnumEvents arg) 086. { 087. string sz; 088. 089. switch (arg) 090. { 091. case evHideBarTime: 092. sz = def_ExpansionBtn1; 093. m_Info.bvT = false; 094. break; 095. case evHideDailyVar: 096. sz = def_ExpansionBtn2; 097. m_Info.bvD = false; 098. break; 099. case evHidePriceVar: 100. sz = def_ExpansionBtn3; 101. m_Info.bvP = false; 102. break; 103. } 104. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false); 105. ObjectDelete(GetInfoTerminal().ID, sz); 106. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true); 107. } 108. //+------------------------------------------------------------------+ 109. public : 110. //+------------------------------------------------------------------+ 111. C_Study(long IdParam, string szShortName, color corH, color corP, color corN) 112. :C_Mouse(IdParam, szShortName, corH, corP, corN) 113. { 114. if (_LastError != ERR_SUCCESS) return; 115. ZeroMemory(m_Info); 116. m_Info.Status = eCloseMarket; 117. m_Info.Rate.close = iClose(GetInfoTerminal().szSymbol, PERIOD_D1, ((GetInfoTerminal().szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(GetInfoTerminal().szSymbol, PERIOD_D1, 0))) ? 0 : 1)); 118. m_Info.corP = corP; 119. m_Info.corN = corN; 120. CreateObjInfo(evShowBarTime); 121. CreateObjInfo(evShowDailyVar); 122. CreateObjInfo(evShowPriceVar); 123. } 124. //+------------------------------------------------------------------+ 125. void Update(const eStatusMarket arg) 126. { 127. datetime dt; 128. 129. switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status)) 130. { 131. case eCloseMarket : 132. m_Info.szInfo = "Closed Market"; 133. break; 134. case eInReplay : 135. case eInTrading : 136. if ((dt = GetBarTime()) < ULONG_MAX) 137. { 138. m_Info.szInfo = TimeToString(dt, TIME_SECONDS); 139. break; 140. } 141. case eAuction : 142. m_Info.szInfo = "Auction"; 143. break; 144. default : 145. m_Info.szInfo = "ERROR"; 146. } 147. Draw(); 148. } 149. //+------------------------------------------------------------------+ 150. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 151. { 152. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 153. switch (id) 154. { 155. case CHARTEVENT_CUSTOM + evHideBarTime: 156. RemoveObjInfo(evHideBarTime); 157. break; 158. case CHARTEVENT_CUSTOM + evShowBarTime: 159. CreateObjInfo(evShowBarTime); 160. break; 161. case CHARTEVENT_CUSTOM + evHideDailyVar: 162. RemoveObjInfo(evHideDailyVar); 163. break; 164. case CHARTEVENT_CUSTOM + evShowDailyVar: 165. CreateObjInfo(evShowDailyVar); 166. break; 167. case CHARTEVENT_CUSTOM + evHidePriceVar: 168. RemoveObjInfo(evHidePriceVar); 169. break; 170. case CHARTEVENT_CUSTOM + evShowPriceVar: 171. CreateObjInfo(evShowPriceVar); 172. break; 173. case CHARTEVENT_MOUSE_MOVE: 174. Draw(); 175. break; 176. } 177. ChartRedraw(GetInfoTerminal().ID); 178. } 179. //+------------------------------------------------------------------+ 180. }; 181. //+------------------------------------------------------------------+ 182. #undef def_ExpansionBtn3 183. #undef def_ExpansionBtn2 184. #undef def_ExpansionBtn1 185. #undef def_ExpansionPrefix 186. #undef def_MousePrefixName 187. //+------------------------------------------------------------------+
C_Study.mqhファイルのソースコード
C_Mouse.mqhとは異なり、ここには多くの違いが見られます。まず、新しい変数、チェック、その他のものが現在実装されているという事実から始めましょう。しかし、そのほとんどは単純なものなので、ここでは最も「珍しい」もののいくつかについてのみ詳しく説明します。その中には33行目があり、ここでは端末上のグローバル変数にアクセスする関数が使用されなくなっていることがわかります。ここで、ターミナルのグローバル変数で以前に検索されたものをマウス指標に要求します。これこそが、私たちが目指す形です。つまり、ユーザーが制御できるものを使用せず、外部プログラミングに頼らずに物事をやりたいのです。すべてを純粋なMQL5で実装するという考え方です。
コードを見るだけでは、以前よりも多くの設定が実装されていることが明確には伝わらないかもしれません。これは、マウス指標をプラットフォーム上の他のアプリケーションで使用できる一種の標準にするためです。ここで重要な点が1つあります。プログラマーは、マウス指標にメッセージを送信して、すでに定義されているものをオンまたはオフにすることができます。
これを実現するには、コードの特定の部分を分離して、何かをオンまたはオフにする必要があるイベントが発生したときに、アクセスしているオブジェクトに必要な変更が加えられるようにする必要があります。例えば、チャート上に何かを表示または非表示にする、といったシンプルな操作が対象です。まあ、もっと複雑なこともあったかもしれません。徐々に導入される自由度によって、多くのことが可能になります。これらを実装するには、すでに準備されているものに若干の変更を加えるだけで済みます。これは、アプリケーションのセキュリティと信頼性のレベルが常に最高レベルに保たれることも意味します。
C_Study.mqhファイルに表示されるコードのほとんどは、まさにこれを実行します。しかし、今私たちが本当に興味を持っているのは、このヘッダファイルに記述されている内容を実装する150行目以降で何が起こるかです。ここで処理する必要のない未処理の部分は、C_Mouseクラスに渡され、そこでイベントを処理できることに注意してください。これは152行目からわかります。
さて、1つのことに注意を払ってください。このハンドラで処理される各ユーザーイベントは、実行時にマウス指標のオン/オフを切り替え、その外観を少し変化させます。これにより、ユーザーがコードを再コンパイルしたり、多くのオプションを設定したりする必要がなくなります。この事実に留意してください。マウス指標にカスタムイベントを送信して、使用中に外観が変化するようにする小さなスクリプトファイルを簡単に作成できます。
もし創造力があれば、このコードとその動作を見るだけで、さまざまな工夫を考えたり計画したりしていることでしょう。しかし、この段階ではリプレイ/シミュレーターシステムがどの方向に発展するかはまだ完全には明らかではないため、急ぐことはお勧めしません。さまざまな要素をモジュールにすることで、幅広い可能性が開かれるからです。そして、システムが成長していく過程を考えると、新たな可能性について考えるときには注意を払う必要があります。しかし、今のところは、モジュールが最初に作成されたときと同じ品質を維持しながら、端末のグローバル変数を使用せずにシステムを動作させることに重点を置いています。
よし。これらすべてが示されたので、実際にマウス指標を作成するコードに進むことができます。以下でご覧いただけます。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. #property description "This is an indicator for graphical studies using the mouse." 004. #property description "This is an integral part of the Replay / Simulator system." 005. #property description "However it can be used in the real market." 006. #property version "1.54" 007. #property icon "/Images/Market Replay/Icons/Indicators.ico" 008. #property link "https://www.mql5.com/pt/articles/11971" 009. #property indicator_chart_window 010. #property indicator_plots 0 011. #property indicator_buffers 1 012. //+------------------------------------------------------------------+ 013. #include <Market Replay\Auxiliar\Study\C_Study.mqh> 014. //+------------------------------------------------------------------+ 015. C_Study *Study = NULL; 016. //+------------------------------------------------------------------+ 017. input long user00 = 0; //ID 018. input C_Study::eStatusMarket user01 = C_Study::eAuction; //Market Status 019. input color user02 = clrBlack; //Price Line 020. input color user03 = clrPaleGreen; //Positive Study 021. input color user04 = clrLightCoral; //Negative Study 022. //+------------------------------------------------------------------+ 023. C_Study::eStatusMarket m_Status; 024. int m_posBuff = 0; 025. double m_Buff[]; 026. //+------------------------------------------------------------------+ 027. int OnInit() 028. { 029. ResetLastError(); 030. Study = new C_Study(user00, "Indicator Mouse Study", user02, user03, user04); 031. if (_LastError != ERR_SUCCESS) return INIT_FAILED; 032. if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay) 033. { 034. MarketBookAdd((*Study).GetInfoTerminal().szSymbol); 035. OnBookEvent((*Study).GetInfoTerminal().szSymbol); 036. m_Status = C_Study::eCloseMarket; 037. }else 038. m_Status = user01; 039. SetIndexBuffer(0, m_Buff, INDICATOR_DATA); 040. ArrayInitialize(m_Buff, EMPTY_VALUE); 041. 042. return INIT_SUCCEEDED; 043. } 044. //+------------------------------------------------------------------+ 045. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 046. { 047. m_posBuff = rates_total - 6; 048. (*Study).Update(m_Status); 049. 050. return rates_total; 051. } 052. //+------------------------------------------------------------------+ 053. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 054. { 055. (*Study).DispatchMessage(id, lparam, dparam, sparam); 056. SetBuffer(); 057. 058. ChartRedraw((*Study).GetInfoTerminal().ID); 059. } 060. //+------------------------------------------------------------------+ 061. void OnBookEvent(const string &symbol) 062. { 063. MqlBookInfo book[]; 064. C_Study::eStatusMarket loc = m_Status; 065. 066. if (symbol != (*Study).GetInfoTerminal().szSymbol) return; 067. MarketBookGet((*Study).GetInfoTerminal().szSymbol, book); 068. m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : C_Study::eInTrading); 069. for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++) 070. if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction; 071. if (loc != m_Status) (*Study).Update(m_Status); 072. } 073. //+------------------------------------------------------------------+ 074. void OnDeinit(const int reason) 075. { 076. if (reason != REASON_INITFAILED) 077. { 078. if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay) 079. MarketBookRelease((*Study).GetInfoTerminal().szSymbol); 080. } 081. delete Study; 082. } 083. //+------------------------------------------------------------------+ 084. inline void SetBuffer(void) 085. { 086. uCast_Double Info; 087. 088. m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff); 089. m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price; 090. Info._datetime = (*Study).GetInfoMouse().Position.dt; 091. m_Buff[m_posBuff + 1] = Info.dValue; 092. Info._int[0] = (*Study).GetInfoMouse().Position.X_Adjusted; 093. Info._int[1] = (*Study).GetInfoMouse().Position.Y_Adjusted; 094. m_Buff[m_posBuff + 2] = Info.dValue; 095. Info._int[0] = (*Study).GetInfoMouse().Position.X_Graphics; 096. Info._int[1] = (*Study).GetInfoMouse().Position.Y_Graphics; 097. m_Buff[m_posBuff + 3] = Info.dValue; 098. Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0); 099. m_Buff[m_posBuff + 4] = Info.dValue; 100. m_Buff[m_posBuff + 5] = (double)(*Study).GetInfoMouse().TimeDevice; 101. } 102. //+------------------------------------------------------------------+
マウス指標のソースコード
コードには大きな変更はありません。新しく追加されたのは1つだけです。これは100行目で、データが指標バッファに配置される部分です。繰り返しになりますが、このデータはリプレイ/シミュレーターサービスの初期段階でのみ役立ちます。他に用途はありません。少なくとも私は他には気づきませんでした。
結論
この記事の内容を理解しやすくするために、指標の実行ファイルを添付しました。また、チャート上で指標を変更する際に使用するいくつかのスクリプトも含めています。ただし、すでにコンパイルされたプログラムを自分のプラットフォームで実行することに抵抗がある方もいるでしょう。その理由は理解できます。この記事の最後にあるビデオでは、スクリプトを実行した際に何が起こるかを確認することができます。
ここで示しているのは、私たちが実現できることのほんの一部に過ぎません。視野の狭い人たちは、これを無駄だとか、別の方法でできるはずだと言うかもしれませんが、私はそのような意見には耳を貸しません。むしろ、私が示している可能性について考え、このモジュール式システムがどのように発展していくかに目を輝かせる人々を見ることのほうが重要です。私はそのような人々のためにこれらの記事を書いています。本連載では、多くの人がMQL5で実際に可能なことを表面的にしか理解していないことを示しています。また、MetaTrader 5が本当に優れたプラットフォームであり、適切に活用すれば驚くべき成果を生み出せることも伝えています。
とはいえ、プログラミングスキルを持たない方々には、改めてお詫びします。申し訳ありませんが、この記事で示されている内容について十分な知識がなければ、このリプレイ/シミュレーターサービスのソースコードにアクセスすることは危険を伴います。しかし、私はこの問題を放置しません。モジュールのバージョンが完成し安定した段階で、実行可能ファイルを公開する予定です。その際には、この記事で提供する形にします。一方で、もしソースコードをコンパイルしたい場合は、記事内で常に利用可能にします。ただし、適切な知識がないと、システムをコンパイルするために必要なディレクトリ構造を作成することは難しいでしょう。
この取り組みこそが私の目的です。なぜなら、過去の記事で、内容を十分に理解せずに何かをコンパイルしようとする人々を目にしてきたからです。そうした行為は単にリスクがあるだけでなく、非常に危険でもあります。物事の目的を理解しないまま使用するべきではありません。
ビデオ 01 - モジュールの操作のデモンストレーション
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/11971
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
独自のLLMをEAに統合する(第5部):LLMs(II)-LoRA-チューニングによる取引戦略の開発とテスト
知っておくべきMQL5ウィザードのテクニック(第42回):ADXオシレーター
ニューラルネットワークの実践:擬似逆行列(I)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索