市場シミュレーション(第2回):両建て注文(II)
はじめに
前回の「市場シミュレーション(第1回):両建て注文(I)」では、特に先物契約を取引される方にとって比較的よくある問題に対する、代替的な解決策をデモンストレーションしながら解説しました。最終的な解決策は提示していませんが、記事内ではChart Tradeインジケーターに焦点を当て、重要な内容をカバーしています。これにより、C_Terminalクラスが先物契約の取引に適した名称を提供するための土台が整います。
しかし、問題は依然として残っています。Chart TradeやEA自体に問題があるわけではなく、未決注文のシステムに起因しています。これまでの記事では未決注文の仕組みについて詳しく触れていませんが、何らかの形で準備しておく必要があります。このシステムで使用される情報の一部はChart Tradeから取得され、サーバーとの通信はEAを介しておこなわれます。つまり、三角形の各頂点が開発対象のアプリケーションを表し、サーバーと通信するエッジはEAの頂点から伸びている構造です。
したがって、EAで先物契約の選択を管理することは理にかなっています。これにより、取引するのがミニ契約かフル契約かをその時点で判断することが可能です。ただし、この選択を複数のアプリケーションに分散させることはできません。契約タイプの判断は1か所でおこなう必要があり、そうしないと選択や情報に曖昧さが生じ、ユーザーにとって非常に混乱を招くシステムとなります。
前回の記事のようにChart Tradeで選択をおこなえば、プロセスを制御することができます。しかし、EAはサーバーへ直接オーダーを送信する唯一のコンポーネントであるため、EAに選択機能を置くことも論理的に考えられます。この場合、別の種類の問題が発生します。本記事では、その解決方法の一例を示すことに焦点を当てます。ただし、ここで提示する内容は提案の段階であり、最終的な解決策ではないことを明記しておきます。最終的なアプローチについては、後ほど改めて検討する予定です。
問題を理解する
今回提案する解決策では、契約タイプの選択にEAを使用します。そのため、Chart Tradeの一部を修正する必要があります。というのも、契約タイプの選択をEAに任せた場合、Chart Tradeに表示される情報に問題が生じるためです。以下の図をご覧ください。

図では、資産名と契約名を強調しています。ここでの重要なポイントは、この名称がChart Tradeインターフェースに表示される金融値の計算に使用されていることです。問題がさらに複雑になるのは、Chart Tradeが既にティック単位に変換した値をEAに送信し、EAは成行注文の送信のみを担当している点です。
次に、この課題を考えてみましょう。EAが契約タイプをミニ契約やフル契約に変更した場合、Chart Tradeはこの情報を正しく反映させ、ユーザーやオペレーターによる誤操作を防ぐ必要があります。この情報の反映自体は難しい作業ではありません。Chart TradeとEAを連携させることで対応可能です。現状では、ユーザーがChart Tradeをチャートに追加する必要がありますが、EAがChart Tradeを自動的に追加し、契約情報を適切に調整する方法が考えられます。ユーザーはチャートにEAを設置するだけで、EAがChart Tradeを追加し、契約情報を自動更新するという仕組みです。
これは最も簡単で直感的な方法ですが、EAごとにChart Tradeを配置する必要があるため、実務上は現実的ではありません。理由は単純で、Chart Tradeに改善を加えた場合、全てのEAを再コンパイルする必要があるからです。このため、Chart TradeはEAから独立して設計されています。
それでは、コンポーネントを分離したまま問題をどのように解決するかを見ていきましょう。解決策の1つはすでに示唆されています。EAとChart Trade間でメッセージ交換をおこなうことです。一見すると簡単に思えますが、実際にはそれほど単純ではありません。本記事では、その実装方法について詳しく説明します。
最初の問題は、アプリケーションがチャートに追加される順序に関係しています。どのような順序かというと、ユーザーにEAをChart Tradeより先に追加させるように強制することが考えられます。その場合、Chart Tradeが読み込まれた際にEAに契約タイプを問い合わせることが可能です。一見すると良い方法のように思えますが、ここで別の問題が発生します。ユーザーに正しい順序を指示することはできますが、MetaTrader 5自体に順序を強制することはできません。ここで、見落とされがちな2つ目の問題が生じます。MetaTrader 5では、チャートの時間足を変更すると、チャート上のインジケーターやEAが破棄され、再読み込みされる仕様になっています。この場合、Chart TradeとEAのどちらが先に読み込まれるかが問題になります。EAが先に読み込まれれば、1番目の問題が2番目の問題を解決する形になります。しかし、Chart Tradeが先に読み込まれた場合はどうでしょうか。
ご覧の通り、状況は見た目よりも複雑です。さらに3番目の問題があります。ユーザーがEA内の契約パラメーターやその他の設定を変更した場合、Chart Tradeは正しく更新されません。これまでの計画では、最初の2つの問題のみを想定していました。この3番目の問題が発生する理由は、MetaTrader 5がEAをチャートから削除して再追加する可能性があるためです。加えて、Chart Tradeは何らかの形でEAの存在を認識する必要があります。認識できなければ、さらなる問題が発生します。
幸い、この「EA認識」の問題は現時点では一時的に保留できます。本システムは、ユーザーにステータスをフィードバックする設計になっているため、Chart TradeとEAの相互作用において現時点で致命的な問題ではありません。大規模なコード変更を回避するため、ここでは一定の妥協をおこない、コンポーネントの分離については次章で改めて取り扱います。
一部妥協をおこなう
ここで実施する変更は恒久的なものであり、コードは後退するのではなく、今後も進化し続けます。この妥協により、以前は使用できなかった特定の機能を利用できるようになります。そのため、これらの新しい可能性は慎重に運用する必要があります。
最初の変更は、C_Terminal.mqhファイルに反映されます。以下に、使用するための完全な更新コードを示します。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "Macros.mqh" 005. #include "..\Defines.mqh" 006. //+------------------------------------------------------------------+ 007. class C_Terminal 008. { 009. //+------------------------------------------------------------------+ 010. protected: 011. enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance}; 012. //+------------------------------------------------------------------+ 013. struct st_Terminal 014. { 015. ENUM_SYMBOL_CHART_MODE ChartMode; 016. ENUM_ACCOUNT_MARGIN_MODE TypeAccount; 017. long ID; 018. string szSymbol; 019. int Width, 020. Height, 021. nDigits, 022. SubWin, 023. HeightBar; 024. double PointPerTick, 025. ValuePerPoint, 026. VolumeMinimal, 027. AdjustToTrade; 028. }; 029. //+------------------------------------------------------------------+ 030. void CurrentSymbol(bool bUsingFull) 031. { 032. MqlDateTime mdt1; 033. string sz0, sz1; 034. datetime dt = macroGetDate(TimeCurrent(mdt1)); 035. enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER; 036. 037. sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3); 038. for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS); 039. switch (eTS) 040. { 041. case DOL : 042. case WDO : sz1 = "FGHJKMNQUVXZ"; break; 043. case IND : 044. case WIN : sz1 = "GJMQVZ"; break; 045. default : return; 046. } 047. sz0 = EnumToString((eTypeSymbol)(((eTS & 1) == 1) ? (bUsingFull ? eTS : eTS - 1) : (bUsingFull ? eTS + 1: eTS))); 048. for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0)) 049. if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break; 050. } 051. //+------------------------------------------------------------------+ 052. private : 053. st_Terminal m_Infos; 054. struct mem 055. { 056. long Show_Descr, 057. Show_Date; 058. bool AccountLock; 059. }m_Mem; 060. //+------------------------------------------------------------------+ 061. inline void ChartChange(void) 062. { 063. int x, y, t; 064. m_Infos.Width = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS); 065. m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS); 066. ChartTimePriceToXY(m_Infos.ID, 0, 0, 0, x, t); 067. ChartTimePriceToXY(m_Infos.ID, 0, 0, m_Infos.PointPerTick * 100, x, y); 068. m_Infos.HeightBar = (int)((t - y) / 100); 069. } 070. //+------------------------------------------------------------------+ 071. public : 072. //+------------------------------------------------------------------+ 073. C_Terminal(const long id = 0, const uchar sub = 0) 074. { 075. m_Infos.ID = (id == 0 ? ChartID() : id); 076. m_Mem.AccountLock = false; 077. m_Infos.SubWin = (int) sub; 078. CurrentSymbol(false); 079. m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR); 080. m_Mem.Show_Date = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE); 081. ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false); 082. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, true); 083. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, true); 084. ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false); 085. m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS); 086. m_Infos.Width = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS); 087. m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS); 088. m_Infos.PointPerTick = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE); 089. m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE); 090. m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP); 091. m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick; 092. m_Infos.ChartMode = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE); 093. if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)); 094. ChartChange(); 095. } 096. //+------------------------------------------------------------------+ 097. ~C_Terminal() 098. { 099. ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, m_Mem.Show_Date); 100. ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, m_Mem.Show_Descr); 101. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, false); 102. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, false); 103. } 104. //+------------------------------------------------------------------+ 105. inline void SetTypeAccount(const ENUM_ACCOUNT_MARGIN_MODE arg) 106. { 107. if (m_Mem.AccountLock) return; else m_Mem.AccountLock = true; 108. m_Infos.TypeAccount = (arg == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? arg : ACCOUNT_MARGIN_MODE_RETAIL_NETTING); 109. } 110. //+------------------------------------------------------------------+ 111. inline const st_Terminal GetInfoTerminal(void) const 112. { 113. return m_Infos; 114. } 115. //+------------------------------------------------------------------+ 116. const double AdjustPrice(const double arg) const 117. { 118. return NormalizeDouble(round(arg / m_Infos.PointPerTick) * m_Infos.PointPerTick, m_Infos.nDigits); 119. } 120. //+------------------------------------------------------------------+ 121. inline datetime AdjustTime(const datetime arg) 122. { 123. int nSeconds= PeriodSeconds(); 124. datetime dt = iTime(m_Infos.szSymbol, PERIOD_CURRENT, 0); 125. 126. return (dt < arg ? ((datetime)(arg / nSeconds) * nSeconds) : iTime(m_Infos.szSymbol, PERIOD_CURRENT, Bars(m_Infos.szSymbol, PERIOD_CURRENT, arg, dt))); 127. } 128. //+------------------------------------------------------------------+ 129. inline double FinanceToPoints(const double Finance, const uint Leverage) 130. { 131. double volume = m_Infos.VolumeMinimal + (m_Infos.VolumeMinimal * (Leverage - 1)); 132. 133. return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade))); 134. }; 135. //+------------------------------------------------------------------+ 136. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 137. { 138. static string st_str = ""; 139. 140. switch (id) 141. { 142. case CHARTEVENT_CHART_CHANGE: 143. m_Infos.Width = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS); 144. m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS); 145. ChartChange(); 146. break; 147. case CHARTEVENT_OBJECT_CLICK: 148. if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false); 149. if (ObjectGetInteger(m_Infos.ID, sparam, OBJPROP_SELECTABLE) == true) 150. ObjectSetInteger(m_Infos.ID, st_str = sparam, OBJPROP_SELECTED, true); 151. break; 152. case CHARTEVENT_OBJECT_CREATE: 153. if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false); 154. st_str = sparam; 155. break; 156. } 157. } 158. //+------------------------------------------------------------------+ 159. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const int zOrder = -1) const 160. { 161. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false); 162. ObjectCreate(m_Infos.ID, szName, obj, m_Infos.SubWin, 0, 0); 163. ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n"); 164. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false); 165. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor); 166. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false); 167. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false); 168. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder); 169. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true); 170. } 171. //+------------------------------------------------------------------+ 172. bool IndicatorCheckPass(const string szShortName) 173. { 174. string szTmp = szShortName + "_TMP"; 175. 176. IndicatorSetString(INDICATOR_SHORTNAME, szTmp); 177. m_Infos.SubWin = ((m_Infos.SubWin = ChartWindowFind(m_Infos.ID, szTmp)) < 0 ? 0 : m_Infos.SubWin); 178. if (ChartIndicatorGet(m_Infos.ID, m_Infos.SubWin, szShortName) != INVALID_HANDLE) 179. { 180. ChartIndicatorDelete(m_Infos.ID, 0, szTmp); 181. Print("Only one instance is allowed..."); 182. SetUserError(C_Terminal::ERR_NoMoreInstance); 183. 184. return false; 185. } 186. IndicatorSetString(INDICATOR_SHORTNAME, szShortName); 187. 188. return true; 189. } 190. //+------------------------------------------------------------------+ 191. };
C_Terminal.mqhのソースコード
変更は最小限であることに注意してください。実際、前回の記事のコードと比べると、C_Terminalクラスを継承する他のクラスからCurrentSymbol手続きを呼び出せる可能性を開いただけです。以前はこの手続きはクラス内でのみ使用可能なprivateでしたが、今回はprotectedに変更しました。多くの方は思い切ってpublicにするかもしれませんが、私は極端な変更は好みません。プログラムが実際により広いアクセス権を必要とすることが明確になるまでは、最小限の権限を付与する方針です。protectedの手続きとしておけば、無秩序にアクセスされることはありません。
この小さな変更に加えて、もう1つ修正をおこないました。コンストラクタ内では、73行目に前回の記事で追加したパラメータを削除しました。一方、78行目では、初期状態でミニ契約を使用するよう強制しています。このような調整は、今後のさらなる変更への前例となります。この方法を採用する理由は、従来の構造を維持していた場合、Chart Tradeを閉じなければインジケーターに表示される契約を更新できなかったからです。しかし、今回の変更により、その必要はなくなりました。メッセージを介して契約を切り替えることが可能になったのです。ただし、この変更に伴い、前回の記事でおこなったその他の修正は、Chart Tradeコードにおいて依然として有効ですが、現時点ではそれらに重点を置きません。まず別の課題に取り組む必要があります。
ここでメッセージ処理についての話に移りますが、これらの問題を解決するためには、新しいメッセージを実装する必要があります。しかしその前に、一度立ち止まって状況を整理しましょう。以下の図をご覧ください。

図から、メッセージ交換が実際にどの箇所でおこなわれるかがわかります。注目すべき点は、EAの場合、Chart Tradeに送信されるメッセージが2箇所あるのに対し、Chart Tradeは1箇所だけであるということです。
この交換がどのようにおこなわれるかを理解するためには、チャートに何かが追加された際の挙動を知る必要があります。OnInitの後にOnChartEventが実行されることはよく知られていますう。しかし、チャート上にオブジェクトが追加されたときに、どのイベントがOnChartEventをトリガーするかご存じでしょうか。実際に確認すると、インジケーターやEAなど、チャート上に何かオブジェクトが追加されると、MetaTrader 5はCHARTEVENT_CHART_CHANGEイベントを発生させることがわかります。この点を理解することが重要です。今回のシステムは、このイベントを利用して正しく機能させる設計になっています。
さらに考察を進めてみましょう。注文を実行するEAがチャート上に存在しない場合、なぜ注文送信のボタンが必要なのでしょうか。それは意味をなさないでしょう。メッセージ交換が実際におこなわれたこと、そしてChart Tradeの情報がユーザーやオペレーターによって使用され得ることを示すために、Chart Trade内の小さな重要な部分を修正します。これにより、今回の問題に対する解決策を実装するための概念的な基盤が整います。
解決策の実装
最初のステップは、システムに新しいイベントを3つ追加することです。これらの定義は、以下の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. #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. evHideMouse, //Hide mouse price line 31. evShowMouse, //Show mouse price line 32. evHideBarTime, //Hide bar time 33. evShowBarTime, //Show bar time 34. evHideDailyVar, //Hide daily variation 35. evShowDailyVar, //Show daily variation 36. evHidePriceVar, //Hide instantaneous variation 37. evShowPriceVar, //Show instantaneous variation 38. evCtrlReplayInit, //Initialize replay control 39. evChartTradeBuy, //Market buy event 40. evChartTradeSell, //Market sales event 41. evChartTradeCloseAll, //Event to close positions 42. evChartTrade_At_EA, //Event to communication 43. evEA_At_ChartTrade //Event to communication 44. }; 45. //+------------------------------------------------------------------+
Defines.mqhファイルのソースコード
追加されたのは、42行目と43行目です。これらの行はほとんど説明不要ですが、重要なのは通信の方向です。42行目は、Chart TradeからEAへの通信に関するものです。ここで注意すべき点は、これを取引イベントと混同しないことです。このイベントは別種の通信、つまり専用の通信チャンネルとして機能することを目的としています。
43行目は、Chart TradeからEAへのリクエストに対する応答として使用されるイベントの名前を指定しています。現時点ではこれだけで十分です。たった2行の追加ですが、その影響は非常に大きくなります。次に、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.81" 06. #property icon "/Images/Market Replay/Icons/Indicators.ico" 07. #property link "https://www.mql5.com/ja/articles/12537" 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, new C_Mouse(0, "Indicator Mouse Study"), 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インジケーターのソースコード
しかし、新しいイベントはどこにあるのでしょうか。普通であれば、OnChartEvent手続き内に記述されていることが期待されます。実際には存在しますが、処理を簡素化するため、すべてを1か所で扱う形にしています。それがDispatchMessage手続きです。このDispatchMessage手続きは、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", GetInfoTerminal().ID, A) 008. #define macro_CloseIndicator(A) { \ 009. OnDeinit(REASON_INITFAILED); \ 010. SetUserError(A); \ 011. return; \ 012. } 013. //+------------------------------------------------------------------+ 014. class C_ChartFloatingRAD : private C_Terminal 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. }m_Init; 044. C_Mouse *m_Mouse; 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(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0); 051. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x); 052. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y); 053. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XSIZE, w); 054. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, h); 055. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false); 056. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false); 057. m_Info.WinHandle = ObjectGetInteger(GetInfoTerminal().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(GetInfoTerminal().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(GetInfoTerminal().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(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx)); 130. ObjectSetInteger(GetInfoTerminal().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(const short x, const short y) 141. { 142. int xi, yi, xf, yf; 143. 144. for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++) 145. { 146. xi = (m_Info.IsMaximized ? m_Info.x : m_Info.minx) + m_Info.Regions[c0].x; 147. yi = (m_Info.IsMaximized ? m_Info.y : m_Info.miny) + m_Info.Regions[c0].y; 148. xf = xi + m_Info.Regions[c0].w; 149. yf = yi + m_Info.Regions[c0].h; 150. if ((x > xi) && (y > yi) && (x < xf) && (y < yf)) return c0; 151. } 152. return MSG_NULL; 153. } 154. //+------------------------------------------------------------------+ 155. inline void DeleteObjectEdit(void) 156. { 157. ChartRedraw(); 158. ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Editable); 159. } 160. //+------------------------------------------------------------------+ 161. template <typename T > 162. void CreateObjectEditable(eObjectsIDE arg, T value) 163. { 164. long id = GetInfoTerminal().ID; 165. 166. DeleteObjectEdit(); 167. CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, 0); 168. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3); 169. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3); 170. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XSIZE, m_Info.Regions[arg].w); 171. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YSIZE, m_Info.Regions[arg].h); 172. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_BGCOLOR, m_Info.Regions[arg].bgcolor); 173. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_ALIGN, ALIGN_CENTER); 174. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_FONTSIZE, m_Info.Regions[arg].FontSize - 1); 175. ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_FONT, m_Info.Regions[arg].FontName); 176. ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_TEXT, (typename(T) == "double" ? DoubleToString(value, 2) : (string) value)); 177. ChartRedraw(); 178. } 179. //+------------------------------------------------------------------+ 180. bool RestoreState(void) 181. { 182. uCast_Double info; 183. bool bRet; 184. C_AdjustTemplate *Template; 185. 186. if (bRet = GlobalVariableGet(macro_NameGlobalVariable("POST"), info.dValue)) 187. { 188. m_Info.x = (short) info._16b[0]; 189. m_Info.y = (short) info._16b[1]; 190. m_Info.minx = (short) info._16b[2]; 191. m_Info.miny = (short) info._16b[3]; 192. Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl"); 193. if (_LastError >= ERR_USER_ERROR_FIRST) bRet = false; else 194. { 195. (*Template).Add("MSG_LEVERAGE_VALUE", "descr", NULL); 196. (*Template).Add("MSG_TAKE_VALUE", "descr", NULL); 197. (*Template).Add("MSG_STOP_VALUE", "descr", NULL); 198. (*Template).Add("MSG_DAY_TRADE", "state", NULL); 199. (*Template).Add("MSG_MAX_MIN", "state", NULL); 200. if (!(*Template).Execute()) bRet = false; else 201. { 202. m_Info.IsDayTrade = (bool) StringToInteger((*Template).Get("MSG_DAY_TRADE", "state")) == 1; 203. m_Info.IsMaximized = (bool) StringToInteger((*Template).Get("MSG_MAX_MIN", "state")) == 1; 204. m_Info.Leverage = (short)StringToInteger((*Template).Get("MSG_LEVERAGE_VALUE", "descr")); 205. m_Info.FinanceTake = (double) StringToDouble((*Template).Get("MSG_TAKE_VALUE", "descr")); 206. m_Info.FinanceStop = (double) StringToDouble((*Template).Get("MSG_STOP_VALUE", "descr")); 207. } 208. }; 209. delete Template; 210. }; 211. 212. GlobalVariablesDeleteAll(macro_NameGlobalVariable("")); 213. 214. return bRet; 215. } 216. //+------------------------------------------------------------------+ 217. public : 218. //+------------------------------------------------------------------+ 219. C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const short Leverage, const double FinanceTake, const double FinanceStop) 220. :C_Terminal(0) 221. { 222. m_Mouse = MousePtr; 223. m_Info.IsSaveState = false; 224. if (!IndicatorCheckPass(szShortName)) return; 225. if (!RestoreState()) 226. { 227. m_Info.Leverage = Leverage; 228. m_Info.IsDayTrade = true; 229. m_Info.FinanceTake = FinanceTake; 230. m_Info.FinanceStop = FinanceStop; 231. m_Info.IsMaximized = true; 232. m_Info.minx = m_Info.x = 115; 233. m_Info.miny = m_Info.y = 64; 234. } 235. m_Init.y[false] = 150; 236. m_Init.y[true] = 210; 237. CreateWindowRAD(170, m_Init.y[m_Init.bOk = false]); 238. AdjustTemplate(true); 239. } 240. //+------------------------------------------------------------------+ 241. ~C_ChartFloatingRAD() 242. { 243. ChartRedraw(); 244. ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Chart); 245. if (!m_Info.IsSaveState) 246. FileDelete(m_Info.szFileNameTemplate); 247. 248. delete m_Mouse; 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. switch (id) 288. { 289. case CHARTEVENT_CUSTOM + evEA_At_ChartTrade: 290. if (m_Init.bOk = ((lparam >= 0) && (lparam < 2))) 291. CurrentSymbol((bool)lparam); 292. AdjustTemplate(true); 293. break; 294. case CHARTEVENT_CHART_CHANGE: 295. if (!m_Init.bOk) 296. EventChartCustom(GetInfoTerminal().ID, evChartTrade_At_EA, 0, 0, ""); 297. x = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_WIDTH_IN_PIXELS); 298. y = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_HEIGHT_IN_PIXELS); 299. macro_AdjustMinX(m_Info.x, b1); 300. macro_AdjustMinY(m_Info.y, b2); 301. macro_AdjustMinX(m_Info.minx, b3); 302. macro_AdjustMinY(m_Info.miny, b4); 303. if (b1 || b2 || b3 || b4) AdjustTemplate(); 304. break; 305. case CHARTEVENT_MOUSE_MOVE: 306. if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) 307. { 308. switch (CheckMousePosition(x = (short)lparam, y = (short)dparam)) 309. { 310. case MSG_MAX_MIN: 311. if (sz < 0) m_Info.IsMaximized = (m_Info.IsMaximized ? false : true); 312. break; 313. case MSG_DAY_TRADE: 314. if ((m_Info.IsMaximized) && (sz < 0)) m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true); 315. break; 316. case MSG_LEVERAGE_VALUE: 317. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_LEVERAGE_VALUE, m_Info.Leverage); 318. break; 319. case MSG_TAKE_VALUE: 320. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_TAKE_VALUE, m_Info.FinanceTake); 321. break; 322. case MSG_STOP_VALUE: 323. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_STOP_VALUE, m_Info.FinanceStop); 324. break; 325. case MSG_TITLE_IDE: 326. if (sx < 0) 327. { 328. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 329. sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx); 330. sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny); 331. } 332. if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx); 333. if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my); 334. if (m_Info.IsMaximized) 335. { 336. m_Info.x = (mx > 0 ? mx : m_Info.x); 337. m_Info.y = (my > 0 ? my : m_Info.y); 338. }else 339. { 340. m_Info.minx = (mx > 0 ? mx : m_Info.minx); 341. m_Info.miny = (my > 0 ? my : m_Info.miny); 342. } 343. break; 344. case MSG_BUY_MARKET: 345. ev = evChartTradeBuy; 346. case MSG_SELL_MARKET: 347. ev = (ev != evChartTradeBuy ? evChartTradeSell : ev); 348. case MSG_CLOSE_POSITION: 349. if ((m_Info.IsMaximized) && (sz < 0) && (m_Init.bOk)) //<< 350. { 351. string szTmp = StringFormat("%d?%s?%s?%c?%d?%.2f?%.2f", ev, _Symbol, GetInfoTerminal().szSymbol, (m_Info.IsDayTrade ? 'D' : 'S'), 352. m_Info.Leverage, FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage)); 353. PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp); 354. EventChartCustom(GetInfoTerminal().ID, ev, 0, 0, szTmp); 355. } 356. break; 357. } 358. if (sz < 0) 359. { 360. sz = x; 361. AdjustTemplate(); 362. if (obj == MSG_NULL) DeleteObjectEdit(); 363. } 364. }else 365. { 366. sz = -1; 367. if (sx > 0) 368. { 369. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 370. sx = sy = -1; 371. } 372. } 373. break; 374. case CHARTEVENT_OBJECT_ENDEDIT: 375. switch (obj) 376. { 377. case MSG_LEVERAGE_VALUE: 378. case MSG_TAKE_VALUE: 379. case MSG_STOP_VALUE: 380. dvalue = StringToDouble(ObjectGetString(GetInfoTerminal().ID, m_Info.szObj_Editable, OBJPROP_TEXT)); 381. if (obj == MSG_TAKE_VALUE) 382. m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue); 383. else if (obj == MSG_STOP_VALUE) 384. m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue); 385. else 386. m_Info.Leverage = (dvalue <= 0 ? m_Info.Leverage : (short)MathFloor(dvalue)); 387. AdjustTemplate(); 388. obj = MSG_NULL; 389. ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Editable); 390. break; 391. } 392. break; 393. case CHARTEVENT_OBJECT_DELETE: 394. if (sparam == m_Info.szObj_Chart) macro_CloseIndicator(C_Terminal::ERR_Unknown); 395. break; 396. } 397. ChartRedraw(); 398. } 399. //+------------------------------------------------------------------+ 400. }; 401. //+------------------------------------------------------------------+ 402. #undef macro_NameGlobalVariable 403. #undef macro_CloseIndicator 404. //+------------------------------------------------------------------+
ファイルC_ChartFloatingRAD.mqhのソースコード
ヘッダーファイル「C_ChartFloatingRAD.mqh」のコードは、多くの方にとって圧倒される量に見えるかもしれません。また、コード全体を確認することは必須ではありません。しかし、前回の記事との違いを明確にするため、本記事では全体を掲載することにしました。今回の主要な変更は、クラスコンストラクタにあります。行番号が変わっているため、読者の皆様が正しい行を探す際に混乱しないよう、全コードを示しています。
まず、実際に何が変更されたのかに注目しましょう。前回の記事との比較ではなく、この記事の最後に掲載する動画で確認できる動作をサポートするための変更です。最初の変更は、39行目にある小さな構造体です。この説明に注意しないと、動画で示される内容を理解できません。この構造体の中には41行目があり、2つの値が格納されています。これはウィンドウサイズを示すもので、実際にはOBJ_CHARTオブジェクトです。心配する必要はありません、後ほど理解できます。42行目には、この二要素配列と連動する変数があり、動画で確認できる機能を実現しています。
次に、クラスコンストラクタを確認します(219行目から開始)。前回の記事で追加されていた余分なパラメーターは削除されています。220行目ではC_Terminalクラスのコンストラクタを呼び出し、前回記事以前の状態に戻っています。ここで重要な点です:Chart TradeウィンドウはOBJ_CHARTオブジェクトであり、最小化や最大化が可能です。この機能は既に実装済みです。しかし、Chart Tradeが設置されているチャートにEAが存在しない場合、ボタンを非表示にしたいと考えています。
これを実現するため、Y座標の調整が必要です。この座標は235行目と236行目で設定されています。ここで注意してください。m_Init.bOkがfalseの場合、何かが整っていないことを意味します。この場合、操作ボタンは非表示にされ、ユーザーやオペレーターが誤ってサーバーに注文を送信したと誤認することを防ぎます。ボタンを非表示にする際のY座標は150です。m_Init.bOkがtrueの場合、ユーザーやオペレーターはEA経由で注文を送信できます。その場合のY座標は210です。この210は従来のデフォルト値で、237行目で使用されていました。
237行目では、Chart Tradeを格納するOBJ_CHARTオブジェクトの作成方法が変更されています。従来の固定値210ではなく、配列で定義された値を使用しています。同時にm_Init.bOkは初期値「false」に設定されています。つまり、OBJ_CHARTオブジェクトは初期状態でY座標150で作成されます。この値を直接使用しない理由は、コードが意図通りに機能することを確認するためです。150を直接渡すだけでは、モデルが正しく動作しているかを確認できません。この方法により、実装が期待通りに動作することを保証できます。
続いて、直後に呼び出されるAdjustTemplate手続き(77行目開始)を見てみましょう。この手続きの変更は128行目のみです。理由は次の通りです。Chart Tradeが最大化または最小化された場合、Y座標を変更します。コンストラクタで定めたルールと整合させるため、Y座標を適切に調整する必要があります。したがって、ボタンにアクセスするためにChart Tradeを最大化・最小化しても、表示条件が満たされるまではボタンは見えません。
ここまでの変更は簡単で、数も少なく単純です。次に、メッセージ機能を確認します。これにもいくつか小さな変更があります。DispatchMessage手続きは267行目から開始されます。メッセージを確認する前に、349行目に小さな重要な変更が加えられている点に注目してください。マウスチェック機能を簡略化するため、この行に新しいテスト値を追加しました。m_Init.bOkがfalseで、ユーザーがボタンが表示される領域をクリックした場合、チェックによってイベントが発生しないようにします。このような簡潔な設計が、プログラミングの洗練された美しさを生みます。多くの開発者は、このチェックを他の場所で実装しようとして、手続きを不必要に複雑化するでしょう。
さて、メッセージシステムに戻ります。インジケーターがチャートに追加されると、最初に受信するイベントはCHARTEVENT_CHART_CHANGEです。295行目で変数がfalseかどうかを確認し、そうであれば296行目でカスタムイベントがトリガーされます。EAがこのイベントを取得している場合でも、チャートに追加されたばかりの場合でも、別のイベントが発生し、Chart Tradeは289行目でそれを捕捉します。
ここからが面白い部分です。290行目でテストされる値が0以上であれば、291行目が実行されます。注意してください。値が0の場合はfalseを、0以外の場合はtrueを意味します。ただし、1より大きい値は無効です。したがって、EAはミニ契約かフル契約かを示す0か1のみを送信すべきです。最後に292行目でChart Tradeの更新を要求します。これは複雑に思えるかもしれませんが、0や1以外の値が届いた場合、ボタンは非表示となります。これはEAがチャートから削除された、あるいは予期せぬイベントが発生したことを示しています。
この時点で、私が過度に慎重になっていると思われるかもしれません。「Chart TradeがEAの削除や異常をどうして知るのか?」と思われるでしょう。その理解には、次に示すEA自体の動作を確認する必要があります。これは次に紹介します。
現在のEAの動作
デモ用のEAのコードを、以下に全て示します。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Virtual Test..." 04. #property description "Demo version between interaction" 05. #property description "of Chart Trade and Expert Advisor" 06. #property version "1.81" 07. #property link "https://www.mql5.com/ja/articles/12537" 08. //+------------------------------------------------------------------+ 09. #include <Market Replay\Defines.mqh> 10. //+------------------------------------------------------------------+ 11. class C_Decode 12. { 13. private : 14. struct stInfoEvent 15. { 16. EnumEvents ev; 17. string szSymbol, 18. szContract; 19. bool IsDayTrade; 20. ushort Leverange; 21. double PointsTake, 22. PointsStop; 23. }info[1]; 24. public : 25. //+------------------------------------------------------------------+ 26. C_Decode() 27. { 28. info[0].szSymbol = _Symbol; 29. } 30. //+------------------------------------------------------------------+ 31. bool Decode(const int id, const string sparam) 32. { 33. string Res[]; 34. 35. if (StringSplit(sparam, '?', Res) != 7) return false; 36. stInfoEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])}; 37. if ((id == loc.ev) && (loc.szSymbol == info[0].szSymbol)) info[0] = loc; 38. 39. ArrayPrint(info, 2); 40. 41. return true; 42. } 43. }*GL_Decode; 44. //+------------------------------------------------------------------+ 45. enum eTypeContract {MINI, FULL}; 46. //+------------------------------------------------------------------+ 47. input eTypeContract user00 = MINI; //Cross order in contract 48. //+------------------------------------------------------------------+ 49. bool bOk; 50. //+------------------------------------------------------------------+ 51. int OnInit() 52. { 53. bOk = false; 54. GL_Decode = new C_Decode; 55. 56. return INIT_SUCCEEDED; 57. } 58. //+------------------------------------------------------------------+ 59. void OnTick() {} 60. //+------------------------------------------------------------------+ 61. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 62. { 63. switch (id) 64. { 65. case CHARTEVENT_CUSTOM + evChartTradeBuy : 66. case CHARTEVENT_CUSTOM + evChartTradeSell : 67. case CHARTEVENT_CUSTOM + evChartTradeCloseAll: 68. GL_Decode.Decode(id - CHARTEVENT_CUSTOM, sparam); 69. break; 70. case CHARTEVENT_CHART_CHANGE: 71. if (bOk) break; 72. case CHARTEVENT_CUSTOM + evChartTrade_At_EA: 73. bOk = true; 74. EventChartCustom(ChartID(), evEA_At_ChartTrade, user00, 0, ""); 75. break; 76. } 77. } 78. //+------------------------------------------------------------------+ 79. void OnDeinit(const int reason) 80. { 81. switch (reason) 82. { 83. case REASON_REMOVE: 84. case REASON_INITFAILED: 85. EventChartCustom(ChartID(), evEA_At_ChartTrade, -1, 0, ""); 86. break; 87. } 88. delete GL_Decode; 89. } 90. //+------------------------------------------------------------------+
EAソースコード
ここで疑問が生じます。このコードはどのように動作するのでしょうか。その前に、前回の記事で発生した小さな変更に注目しておきます。Chart Tradeが表示している契約を示すようになった際の変更です。Chart Tradeのコード、より具体的にはC_ChartFloatingRADクラスを見ると、351行目に新しい情報が追加されています。この情報は契約名に関するもので、EAコードの36行目でデコードされます。小さな変更ですが、重要な点です。では、EAコードがどのように機能するかを見てみましょう。
まず注目すべきは、以前Chart Trade内にあったユーザーやオペレーターによる契約タイプ選択のコードが、現在はEAの45行目と47行目に移動していることです。ただし、このEA内でC_Terminalクラスにアクセスする箇所はありません。なぜなら、まだサーバーに接続されていないためです。EAはあくまでデモモードの状態です。次に49行目に注目してください。これは、Chart Tradeに不要なイベントが送信されないようにするための変数が宣言されています。53行目で初期値としてfalseに設定され、73行目でtrueに変更されます。また、71行目でこの変数をチェックし、不要なメッセージ送信を回避しています。
重要な点として、インジケーターと同様に、EAの最初のイベントは70行目で宣言されているCHARTEVENT_CHART_CHANGEです。この時点で変数はfalseです。そのため、Chart Tradeがリクエストを送信した場合と同じ処理が実行されます。これが72行目に続き、結果として74行目でイベントがトリガーされ、値は0または1となります。このため、45行目の列挙型の順序は正確に守る必要があります。順序を変更するとChart Tradeで誤った契約が表示されます。変更は、結果を理解した上でのみおこなってください。さもなければ誤った契約が表示されます。
次に、EAがChart Tradeに自身が利用できなくなったことを通知する部分です。この場合、Chart Tradeは注文ボタンを非表示にする必要があります。これは79行目のルーチンが実行されたときに行われます。通常、MetaTrader 5はオブジェクトをチャートから削除する際にDeInitイベントをトリガーします。変数「reason」には、この呼び出しの原因が格納されています。81行目でこの理由を確認し、条件に一致する場合は85行目でイベントがトリガーされます。このイベントタイプに続く値が「-1」である点に注目してください。これにより、Chart TradeはEAが利用できなくなったことを認識し、注文操作が自動的に非表示になります。
その他のコード部分は、Chart Tradeとの通信に関する前回の記事で説明した内容から変更はありません。これにより、このステージは完了と考えられます。
最後に
これまでに、EAとChart Tradeインジケーターの両方を初期化する通信を簡単に実装する方法を示しました。しかし、現時点では、本記事で提示したコードが実際に使用されるかどうかは確信が持てません。理由は、未決注文システムにあります。このシステムが、私たちにとって「難題」となることが予想されます。ここまでの段階では、ユーザーやオペレーターはシステムのパラメーターを1つ変更するだけで、ミニ契約またはフル契約を使ってサーバーに注文を送信できます。この部分は比較的単純で、必要な手順は本連載の前回の記事で概説済みです。しかし、単純なアプリケーション間メッセージのやり取りとは異なり、未決注文情報を管理することはプロセスを大幅に複雑化させます。
いずれにしても、ここまで説明した変更の結果は、記事末尾の動画で確認できます。さらに、動画で使用された実行ファイルも添付していますので、実装内容を実際に確認し、理解することが可能です。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/12537
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
取引におけるニューラルネットワーク:ウェーブレット変換とマルチタスクアテンションを用いたモデル
取引におけるニューラルネットワーク:予測符号化を備えたハイブリッド取引フレームワーク(最終回)
ブラックホールアルゴリズム(BHA)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索