
リプレイシステムの開発(第52回):物事は複雑になる(IV)
はじめに
前回の「リプレイシステムの開発(第51回):物事は複雑になる(III)」では、リプレイ/シミュレーターシステムで正しく動作するよう、古いマウスポインタにいくつかの改良を施しました。また、MetaTrader 5でチャートを開くようプログラムに指示する関数ChartOpenを使用してチャートIDを取得する方法と、既に開いているチャートのIDを取得するためのChartID関数の違いについても実例を交えて説明しました。
これらの違いと今後の展望を踏まえると、MQL5プログラミングをさらに幅広い分野で活用できる可能性があることが明らかです。
一方で、マウスポインタとコントロール指標に多くの改良を加えたものの、それらを統合し、適切に相互作用させる点においていくつかの課題が残っていました。これらの問題は私の見落としによるもので、マウスポインタの開発過程でいくつかの重要な詳細を見逃していたため、相互作用が正しく機能していませんでした。
この記事では、この問題を修正する手順を説明します。修正は非常に簡単ですが、絶対に必要です。この変更により、個人プロジェクトでも同じマウスポインタを活用できるようになり、学習システムを個別にカスタマイズしたり、テーブルとの安全なインタラクションを実現したりすることが可能になります。なお、マウスポインタが学習モードにある場合、チャート上の他のオブジェクトとの干渉は防がれる設計です。ただし、この機能を実現するには、コードをローカル環境に統合する必要があります。
この統合をどのようにおこなうかを説明するために、リプレイ/シミュレーターシステムの他の部分でも同様の手法を使用する予定であることを踏まえ、コントロール指標を使用します。ただし、このコントロール指標をは、以前は基本的なMQL5システムを使用していたものを、より適切なシステムを利用できるように改良したものです。MQL5をさらに深く掘り下げることで、これまで利用できなかったいくつかの新しい機能を活用できるようになります。
この記事で実現するインタラクションは基礎的なものですが、マウスポインタをプログラムに統合し、チャート上のオブジェクトと安全かつ安定的に相互作用するカスタマイズされた学習システムを構築する方法を理解するには十分な内容です。それでは、この記事の最初のテーマである、マウスポインタに必要な変更について説明していきます。
マウスポインタの改善
すべてのコード(現段階ではそのままにしておきます)が記事内に記載される予定です。コードが分割されていると感じるかもしれませんが、このアプローチによりすべての詳細を十分に説明することができます。したがって、コードがここに直接記載されていない場合は、以前の記事を参照してください。
こうすることで、私は、皆さんが実際に説明を理解し、私がどのようにコードを開発しているかをしっかりと把握していることに、完全に自信を持つことができます。この基本的な説明を踏まえ、マウスポインタのコードを確認してみましょう。以下をご覧ください。
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.52" 007. #property icon "/Images/Market Replay/Icons/Indicators.ico" 008. #property link "https://www.mql5.com/ja/articles/11925" 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 - 5; 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. } 101. //+------------------------------------------------------------------+
マウスポインタのソースコード
おそらく、このコードと以前のコードの違いに気付かないでしょう。前述したように、変更は小さいものですが、確かに変更があります。
最初の、そしておそらく唯一の大きな変更点は、指標が書き込まれるバッファのインデックスです。コードの47行目では、以前は4だったオフセット値が5に変更されていることがわかります。この変更の理由は何でしょうか。1単位の増加は、より広い範囲をカバーするために必要です。しかし、この変更の理解を深めるために、84行目にある指標バッファの設定を担当する関数を確認してみましょう。
この機能と以前の機能の違いに気付かないかもしれませんが、変化は微妙です。この指標を個人プロジェクトの特定のモデルに統合する際には、これらの変更点を理解することが重要です。
特に92行目以降では、見た目が少し変わりますが、大きな違いはありません。2つのX変数と2つのY変数を宣言する理由、またそのうちの1つが「Adjusted」、もう1つが「Graphics」と呼ばれている理由は何でしょうか。これが重要なポイントです。これまで、指標は時間と価格で調整されたグラフィック変数のみを返していましたが、これはすべてのケースに適しているわけではありません。実際に必要なのは、指定された値ではなく、グラフィック座標の値である場合もあります。両方の状況に対応できるように、指標はこれら2つの値を返し、それらを最適な方法で使用できるようにします。
バッファのゼロ位置には価格の値が含まれ、最初の位置には時間の値が含まれることに注意してください。これは、個人的に既に使用している他のものとの下位互換性を保つために残しています。しかし、作成されたバッファをそのまま読み取る必要はありません。高レベルでは、Mouseクラスを使用する方が簡単ですが、それでもバッファから直接データを読み取る場合は、バッファの構築方法を理解することが重要です。そのため、84行目に示されているこの関数の理解は非常に重要です。
変更点はこれだけではありません。コードを変更すると、コード全体に多くの影響を及ぼす可能性があることを理解することが重要です。調査の作成と実行を担当するクラスでは、変更はそれほど劇的ではありませんでしたが、それでも説明が必要です。調査コードを以下に示します。
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. }m_Info; 024. //+------------------------------------------------------------------+ 025. const datetime GetBarTime(void) 026. { 027. datetime dt; 028. u_Interprocess info; 029. int i0 = PeriodSeconds(); 030. 031. if (m_Info.Status == eInReplay) 032. { 033. if (!GlobalVariableGet(def_GlobalVariableServerTime, info.df_Value)) return ULONG_MAX; 034. if ((dt = info.ServerTime) == ULONG_MAX) return ULONG_MAX; 035. }else dt = TimeCurrent(); 036. if (m_Info.Rate.time <= dt) 037. m_Info.Rate.time = (datetime)(((ulong) dt / i0) * i0) + i0; 038. 039. return m_Info.Rate.time - dt; 040. } 041. //+------------------------------------------------------------------+ 042. void Draw(void) 043. { 044. double v1; 045. 046. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 18); 047. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1); 048. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1); 049. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_TEXT, m_Info.szInfo); 050. v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / GetInfoMouse().Position.Price) * 100.0), 2); 051. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP)); 052. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1))); 053. v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / iClose(GetInfoTerminal().szSymbol, PERIOD_D1, 0)) * 100.0), 2); 054. ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP)); 055. ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1))); 056. } 057. //+------------------------------------------------------------------+ 058. public : 059. //+------------------------------------------------------------------+ 060. C_Study(long IdParam, string szShortName, color corH, color corP, color corN) 061. :C_Mouse(IdParam, szShortName, corH, corP, corN) 062. { 063. if (_LastError != ERR_SUCCESS) return; 064. ZeroMemory(m_Info); 065. m_Info.Status = eCloseMarket; 066. m_Info.Rate.close = iClose(GetInfoTerminal().szSymbol, PERIOD_D1, ((GetInfoTerminal().szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(GetInfoTerminal().szSymbol, PERIOD_D1, 0))) ? 0 : 1)); 067. m_Info.corP = corP; 068. m_Info.corN = corN; 069. CreateObjectInfo(2, 110, def_ExpansionBtn1, clrPaleTurquoise); 070. CreateObjectInfo(2, 53, def_ExpansionBtn2); 071. CreateObjectInfo(58, 53, def_ExpansionBtn3); 072. } 073. //+------------------------------------------------------------------+ 074. void Update(const eStatusMarket arg) 075. { 076. datetime dt; 077. 078. switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status)) 079. { 080. case eCloseMarket : m_Info.szInfo = "Closed Market"; 081. break; 082. case eInReplay : 083. case eInTrading : 084. if ((dt = GetBarTime()) < ULONG_MAX) 085. { 086. m_Info.szInfo = TimeToString(dt, TIME_SECONDS); 087. break; 088. } 089. case eAuction : m_Info.szInfo = "Auction"; 090. break; 091. default : m_Info.szInfo = "ERROR"; 092. } 093. Draw(); 094. } 095. //+------------------------------------------------------------------+ 096. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 097. { 098. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 099. if (id == CHARTEVENT_MOUSE_MOVE) Draw(); 100. } 101. //+------------------------------------------------------------------+ 102. }; 103. //+------------------------------------------------------------------+ 104. #undef def_ExpansionBtn3 105. #undef def_ExpansionBtn2 106. #undef def_ExpansionBtn1 107. #undef def_ExpansionPrefix 108. #undef def_MousePrefixName 109. //+------------------------------------------------------------------+
C_Studyクラスのソースコード
このクラスで実際に変更されたのは42行目のDraw関数だけです。これらの変更は、時間と価格に基づく調査ロジックを維持することを目的としています。ただし、座標系がグラフィカルなオブジェクトに対してエラーを引き起こします。このタイプのオブジェクトについては、数回前の記事でマウス指標を作成する際に説明しました。価格時間座標とXYタイプのグラフィック座標を使用する理由と方法について解説しています。
下位互換性を維持するために、調整されたグラフィック座標を使用しています。これは46行目から48行目にかけて見られます。ただし、グラフィック座標を必要とする他のオブジェクトを使用し、価格時間座標に従うようにしたい場合は、ここに示すように調整されたグラフィック座標を使用する必要があります。この調整されたシステムを調整されていないグラフィックシステムに置き換えると、調査の外観が多少異なる場合があります。
ご自身でこれを試してみるといいでしょう。
マウス指標部分を終了するには、基本システムのサポートを担当するクラスを確認してみましょう。これにより、調整されたグラフィック座標と調整されていないグラフィック座標の違いをより良く理解できるようになります。次に、コントロール指標コードに進み、これらすべての変更の理由を理解しましょう。以下にマウスクラスの完全なコードを示します。これ以上の修正が不要であることを願っています。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "C_Terminal.mqh" 005. #include "Interprocess.mqh" 006. //+------------------------------------------------------------------+ 007. #define def_MousePrefixName "MouseBase_" 008. #define def_NameObjectLineH def_MousePrefixName + "H" 009. #define def_NameObjectLineV def_MousePrefixName + "TV" 010. #define def_NameObjectLineT def_MousePrefixName + "TT" 011. #define def_NameObjectStudy def_MousePrefixName + "TB" 012. //+------------------------------------------------------------------+ 013. class C_Mouse : public C_Terminal 014. { 015. public : 016. enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay}; 017. enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10}; 018. struct st_Mouse 019. { 020. struct st00 021. { 022. int X_Adjusted, 023. Y_Adjusted, 024. X_Graphics, 025. Y_Graphics; 026. double Price; 027. datetime dt; 028. }Position; 029. uint ButtonStatus; 030. bool ExecStudy; 031. }; 032. //+------------------------------------------------------------------+ 033. protected: 034. enum eEventsMouse {ev_HideMouse, ev_ShowMouse}; 035. //+------------------------------------------------------------------+ 036. void CreateObjectInfo(int x, int w, string szName, color backColor = clrNONE) const 037. { 038. if (m_Mem.szShortName != NULL) return; 039. CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE); 040. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_STATE, true); 041. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BORDER_COLOR, clrBlack); 042. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_COLOR, clrBlack); 043. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BGCOLOR, backColor); 044. ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_FONT, "Lucida Console"); 045. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_FONTSIZE, 10); 046. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 047. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XDISTANCE, x); 048. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1); 049. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XSIZE, w); 050. ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YSIZE, 18); 051. } 052. //+------------------------------------------------------------------+ 053. private : 054. enum eStudy {eStudyNull, eStudyCreate, eStudyExecute}; 055. struct st01 056. { 057. st_Mouse Data; 058. color corLineH, 059. corTrendP, 060. corTrendN; 061. eStudy Study; 062. }m_Info; 063. struct st_Mem 064. { 065. bool CrossHair, 066. IsFull; 067. datetime dt; 068. string szShortName; 069. }m_Mem; 070. bool m_OK; 071. //+------------------------------------------------------------------+ 072. void GetDimensionText(const string szArg, int &w, int &h) 073. { 074. TextSetFont("Lucida Console", -100, FW_NORMAL); 075. TextGetSize(szArg, w, h); 076. h += 5; 077. w += 5; 078. } 079. //+------------------------------------------------------------------+ 080. void CreateStudy(void) 081. { 082. if (m_Mem.IsFull) 083. { 084. CreateObjectGraphics(def_NameObjectLineV, OBJ_VLINE, m_Info.corLineH); 085. CreateObjectGraphics(def_NameObjectLineT, OBJ_TREND, m_Info.corLineH); 086. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_WIDTH, 2); 087. CreateObjectInfo(0, 0, def_NameObjectStudy); 088. } 089. m_Info.Study = eStudyCreate; 090. } 091. //+------------------------------------------------------------------+ 092. void ExecuteStudy(const double memPrice) 093. { 094. double v1 = GetInfoMouse().Position.Price - memPrice; 095. int w, h; 096. 097. if (!CheckClick(eClickLeft)) 098. { 099. m_Info.Study = eStudyNull; 100. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 101. if (m_Mem.IsFull) ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName + "T"); 102. }else if (m_Mem.IsFull) 103. { 104. string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ", 105. MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetInfoMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0))); 106. GetDimensionText(sz1, w, h); 107. ObjectSetString(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_TEXT, sz1); 108. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP)); 109. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XSIZE, w); 110. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YSIZE, h); 111. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XDISTANCE, GetInfoMouse().Position.X_Adjusted - w); 112. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - (v1 < 0 ? 1 : h)); 113. ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 1, GetInfoMouse().Position.dt, GetInfoMouse().Position.Price); 114. ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_COLOR, (memPrice > GetInfoMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP)); 115. } 116. m_Info.Data.ButtonStatus = eKeyNull; 117. } 118. //+------------------------------------------------------------------+ 119. public : 120. //+------------------------------------------------------------------+ 121. C_Mouse(const long id, const string szShortName) 122. :C_Terminal(id), 123. m_OK(false) 124. { 125. m_Mem.szShortName = szShortName; 126. } 127. //+------------------------------------------------------------------+ 128. C_Mouse(const long id, const string szShortName, color corH, color corP, color corN) 129. :C_Terminal(id) 130. { 131. if (!(m_OK = IndicatorCheckPass(szShortName))) SetUserError(C_Terminal::ERR_Unknown); 132. if (_LastError != ERR_SUCCESS) return; 133. m_Mem.szShortName = NULL; 134. m_Mem.CrossHair = (bool)ChartGetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL); 135. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, true); 136. ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, false); 137. ZeroMemory(m_Info); 138. m_Info.corLineH = corH; 139. m_Info.corTrendP = corP; 140. m_Info.corTrendN = corN; 141. m_Info.Study = eStudyNull; 142. if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE)) 143. CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH); 144. } 145. //+------------------------------------------------------------------+ 146. ~C_Mouse() 147. { 148. if (!m_OK) return; 149. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, 0, false); 150. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, false); 151. ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair); 152. ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName); 153. } 154. //+------------------------------------------------------------------+ 155. inline bool CheckClick(const eBtnMouse value) 156. { 157. return (GetInfoMouse().ButtonStatus & value) == value; 158. } 159. //+------------------------------------------------------------------+ 160. inline const st_Mouse GetInfoMouse(void) 161. { 162. if (m_Mem.szShortName != NULL) 163. { 164. double Buff[]; 165. uCast_Double loc; 166. int handle = ChartIndicatorGet(GetInfoTerminal().ID, 0, m_Mem.szShortName); 167. 168. ZeroMemory(m_Info.Data); 169. if (CopyBuffer(handle, 0, 0, 5, Buff) == 5) 170. { 171. m_Info.Data.Position.Price = Buff[0]; 172. loc.dValue = Buff[1]; 173. m_Info.Data.Position.dt = loc._datetime; 174. loc.dValue = Buff[2]; 175. m_Info.Data.Position.X_Adjusted = loc._int[0]; 176. m_Info.Data.Position.Y_Adjusted = loc._int[1]; 177. loc.dValue = Buff[3]; 178. m_Info.Data.Position.X_Graphics = loc._int[0]; 179. m_Info.Data.Position.Y_Graphics = loc._int[1]; 180. loc.dValue = Buff[4]; 181. m_Info.Data.ButtonStatus = loc._char[0]; 182. IndicatorRelease(handle); 183. } 184. } 185. 186. return m_Info.Data; 187. } 188. //+------------------------------------------------------------------+ 189. virtual 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) switch (id) 195. { 196. case (CHARTEVENT_CUSTOM + ev_HideMouse): 197. if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE); 198. break; 199. case (CHARTEVENT_CUSTOM + ev_ShowMouse): 200. if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH); 201. break; 202. case CHARTEVENT_MOUSE_MOVE: 203. 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); 204. if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price)); 205. m_Info.Data.Position.dt = AdjustTime(m_Info.Data.Position.dt); 206. 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); 207. if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0); 208. m_Info.Data.ButtonStatus = (uint) sparam; 209. if (CheckClick(eClickMiddle)) 210. if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy(); 211. if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate)) 212. { 213. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 214. if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Mem.dt = GetInfoMouse().Position.dt, memPrice = GetInfoMouse().Position.Price); 215. m_Info.Study = eStudyExecute; 216. } 217. if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice); 218. m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute; 219. break; 220. case CHARTEVENT_OBJECT_DELETE: 221. if ((m_Mem.IsFull) && (sparam == def_NameObjectLineH)) CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH); 222. break; 223. } 224. } 225. //+------------------------------------------------------------------+ 226. }; 227. //+------------------------------------------------------------------+ 228. #undef def_NameObjectLineV 229. #undef def_NameObjectLineH 230. #undef def_NameObjectLineT 231. #undef def_NameObjectStudy 232. //+------------------------------------------------------------------+
C_Mouseクラスのソースコード
ここでの状況は緊迫しているように見えるかもしれませんが、第一印象に惑わされないでください。まずは詳細を確認していきましょう。4行目と5行目にあるincludeは、以前の記事から取得したものです。これらは変更されていないため、ここで繰り返す必要はありません。
少し下にスクロールすると、22行目から25行目に前のコードで見た変数宣言があります。これらはpublic構造体の一部であり、今後も同じ構造体を使用できます。この仕組みにより、コードが読みやすくなり、全体的な品質が向上します。同じ変数は110行目と111行目でも使用され、一部のオブジェクトが正しく配置されていることを確認するために役立っています。
しかし、特に注目すべき部分は157行目です。以前の記事でも述べたように、マウスクラスを使用して必要な値を取得する際に、バッファがどのようにマウントされたかを知る必要はありません。この157行目の関数がその理由です。この関数はバッファ読み取り要求に適応して正しく応答し、上記の構造を通じて情報を返します。これによりプログラミングがはるかに簡単になります。
ただし、バッファが作成されたプロセスとは逆の操作が必要となります。この操作をおこなうことで、GetInfoMouse関数を通じてマウスの位置やクリックに関連するデータを取得できるようになります。このデータは、開発するプログラムやマウス指標そのもので使用できます。同じインターフェイスを使用しているため、コードの保守や理解が格段に簡単です。
このGetInfoMouse関数の後には、さらに重要な関数があります。これはマウスの操作に応答するためのもので、186行目に記述されているDispatchMessage関数です。基本的には以前のものと同じですが、必要なことだけをおこなうように改良されています。
ここで理解すべき重要なポイントがあります。MetaTrader 5はWindowsに似たメッセージシステムを使用しており、メッセージはWindowsでの動作とほぼ同じ方法でプログラムに渡されます。そのため、Windowsプログラミングの知識があれば役立つ場合がありますが、必要に応じて情報を調べたとしても、その形式が馴染みのないものであったり誤解される可能性があります。
MetaTrader 5がマウスイベントをプログラムに送信すると、そのデータはメッセージ内にコンパイルされます。これは200行目で確認できます。ここでMetaTrader 5は、Windowsが提供するデータをそのまま確認します。ただし、MetaTrader 5は価格と時間に基づいてマウス座標を報告するのではなく、グラフィック形式で報告します。この時点で取得されるのは、調整されていないグラフィック座標です。これらの座標はウィンドウ内のマウス位置を表しており、チャート上の位置ではありません。しかし、ここで明確にしておく必要があることがあります。この「ウィンドウ」とはグラフィックウィンドウを指します。Windowsプログラミングの知識があればこれを容易に理解できますが、この記事ではそこまで深掘りしません。
取得した座標を基に、MQL5を使用して価格と時間のポジションに変換できます。ただし、この段階で取得したオブジェクトはチャート上のバーと完全には一致していないため、位置がわずかにずれる場合があります。この問題を解決するために、行201で価格を調整し、行202で時間を調整しています。これにより、座標とチャートバーの対応関係が確立されます。
さらに、価格と時間に合わせてX値とY値を調整する必要があるポイントでは、行203が使用されます。この方法で調整された値を取得できます。以前は調整済みのグラフィック座標と未調整の座標の区別がありませんでしたが、オブジェクトを操作してマウス指標をコントロール指標と統合しようとすると、この区別が不可欠でした。この対応により、マウス指標を終了させ、コントロール指標に注力する準備が整います。
新しいコントロール指標
記事の冒頭で述べたように、コントロール指標にいくつかの改善を加えるため、MQL5をより集中的に使用していきます。これらの改善により、日常の作業が少し楽しくなります。アプリケーションでは、透明性を持つ新しい形状が利用可能になり、柔軟性が大幅に向上します。これを実現するために、新しいラスター画像を使用します。
これを実現するには新しいクラスを作成する必要があるのかと疑問に思うかもしれませんが、心配無用です。この記事の最後までには、必要な主なやり取りをすべてお見せすることをお約束します。
以下にクラスのソースコードを示します。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #include "C_Terminal.mqh" 05. //+------------------------------------------------------------------+ 06. #define def_MaxImages 2 07. //+------------------------------------------------------------------+ 08. class C_DrawImage : protected C_Terminal 09. { 10. //+------------------------------------------------------------------+ 11. private : 12. struct st_00 13. { 14. int widthMap, 15. heightMap; 16. uint Map[]; 17. }m_InfoImage[def_MaxImages]; 18. uint m_Pixels[]; 19. string m_szObjName, 20. m_szRecName; 21. //+------------------------------------------------------------------+ 22. void ReSizeImage(const int w, const int h, const uchar v, const int what) 23. { 24. #define _Transparency(A) (((A > 100 ? 100 : (100 - A)) * 2.55) / 255.0) 25. double fx = (w * 1.0) / m_InfoImage[what].widthMap; 26. double fy = (h * 1.0) / m_InfoImage[what].heightMap; 27. uint pyi, pyf, pxi, pxf, tmp; 28. uint uc; 29. 30. ArrayResize(m_Pixels, w * h); 31. for (int cy = 0, y = 0; cy < m_InfoImage[what].heightMap; cy++, y += m_InfoImage[what].widthMap) 32. { 33. pyf = (uint)(fy * cy) * w; 34. tmp = pyi = (uint)(fy * (cy - 1)) * w; 35. for (int x = 0; x < m_InfoImage[what].widthMap; x++) 36. { 37. pxf = (uint)(fx * x); 38. pxi = (uint)(fx * (x - 1)); 39. uc = (uchar(double((uc = m_InfoImage[what].Map[x + y]) >> 24) * _Transparency(v)) << 24) | uc & 0x00FFFFFF; 40. m_Pixels[pxf + pyf] = uc; 41. for (pxi++; pxi < pxf; pxi++) m_Pixels[pxi + pyf] = uc; 42. } 43. for (pyi += w; pyi < pyf; pyi += w) 44. for (int x = 0; x < w; x++) 45. m_Pixels[x + pyi] = m_Pixels[x + tmp]; 46. } 47. #undef _Transparency 48. } 49. //+------------------------------------------------------------------+ 50. public : 51. //+------------------------------------------------------------------+ 52. C_DrawImage(long id, int sub, string szObjName, const color cFilter, const string szFile1, const string szFile2 = NULL) 53. :C_Terminal(id), 54. m_szObjName(NULL), 55. m_szRecName(NULL) 56. { 57. if (!ObjectCreate(GetInfoTerminal().ID, m_szObjName = szObjName, OBJ_BITMAP_LABEL, sub, 0, 0)) SetUserError(C_Terminal::ERR_Unknown); 58. m_szRecName = "::" + m_szObjName; 59. for (int c0 = 0; (c0 < def_MaxImages) && (_LastError == ERR_SUCCESS); c0++) 60. { 61. ResourceReadImage((c0 == 0 ? szFile1 : (szFile2 == NULL ? szFile1 : szFile2)), m_InfoImage[c0].Map, m_InfoImage[c0].widthMap, m_InfoImage[c0].heightMap); 62. ArrayResize(m_Pixels, m_InfoImage[c0].heightMap * m_InfoImage[c0].widthMap); 63. ArrayInitialize(m_Pixels, 0); 64. for (int c1 = (m_InfoImage[c0].heightMap * m_InfoImage[c0].widthMap) - 1; c1 >= 0; c1--) 65. if ((m_InfoImage[c0].Map[c1] & 0x00FFFFFF) != cFilter) m_Pixels[c1] = m_InfoImage[c0].Map[c1]; 66. ArraySwap(m_InfoImage[c0].Map, m_Pixels); 67. } 68. ArrayResize(m_Pixels, 1); 69. } 70. //+------------------------------------------------------------------+ 71. ~C_DrawImage() 72. { 73. for (int c0 = 0; c0 < def_MaxImages; c0++) 74. ArrayFree(m_InfoImage[c0].Map); 75. ArrayFree(m_Pixels); 76. ObjectDelete(GetInfoTerminal().ID, m_szObjName); 77. ResourceFree(m_szRecName); 78. } 79. //+------------------------------------------------------------------+ 80. void Paint(const int x, const int y, const int w, const int h, const uchar cView, const int what) 81. { 82. 83. if ((m_szRecName == NULL) || (what < 0) || (what >= def_MaxImages)) return; 84. ReSizeImage(w, h, cView, what); 85. ObjectSetInteger(GetInfoTerminal().ID, m_szObjName, OBJPROP_XDISTANCE, x); 86. ObjectSetInteger(GetInfoTerminal().ID, m_szObjName, OBJPROP_YDISTANCE, y); 87. if (ResourceCreate(m_szRecName, m_Pixels, w, h, 0, 0, 0, COLOR_FORMAT_ARGB_NORMALIZE)) 88. { 89. ObjectSetString(GetInfoTerminal().ID, m_szObjName, OBJPROP_BMPFILE, m_szRecName); 90. ChartRedraw(GetInfoTerminal().ID); 91. } 92. } 93. //+------------------------------------------------------------------+ 94. }; 95. //+------------------------------------------------------------------+ 96. #undef def_MaxImages 97. //+------------------------------------------------------------------+
C_DrawImageクラスのソースコード
コードが非常にコンパクトであることに注目してください。これは、外部リソースを使用せず、MQL5の提供する機能を最大限活用しているためです。本質的に、このクラスは、OBJ_BITMAP_LABELまたはOBJ_BITMAPオブジェクトを使用する代替手段を提供します。これらのオブジェクトはMQL5の標準ライブラリに含まれていますが、画像に透明度や非表示のドットを使用する機能は備わっていません。そのため、これを拡張し、必要な機能を統合する形でクラスを作成しました。
このクラスの基本的な仕組みは非常にシンプルで、4つのプロシージャで構成されています。ここでは、それぞれがどのように機能するかを簡単に説明します。
6行目では、使用する画像の最大数を指定します。この数を増やすことは可能ですが、コードの変更が必要になります。変更が必要な箇所については後ほど説明します。次に、8行目のクラスコードに移ります。このクラスは端末クラスを継承していることに注意してください。これについては、以前の記事で詳しく説明しました。
この直後の11行目では、宣言されたデータがクラスの内部コードに対してprivateであることを示すために、コードのprivate部分を使用しています。実際には、このようなデータが宣言されているこの部分を変更する必要はありません。ただし、22行目では、画像のサイズと透明度レベルを設定する事前レンダリングプロシージャに入ります。このレベルは0%から100%まで変化し、整数ステップ、つまり1ステップずつ変化します。24行目の定義により、画像の透明度変換が正しいことが保証されます。
ここで、画像の解像度を上げたり下げたりします。これらは小さい画像なので、アンチエイリアスを追加する必要はないと思います。そのため、ズームインしすぎると、画像が非常にピクセル化されて見える場合があります。何らかの理由で画像を拡大したい場合は、アンチエイリアスの追加を検討する必要がありますが、現在のシステムは私たちの目的には十分です。
52行目にはクラスコンストラクタが含まれています。この時点で、より多くの画像を扱う場合はパラメーターの数を増やす必要がありますが、ここでも目的はMQL5の機能を拡張することです。したがって、2つの画像で十分です。
このコンストラクタの61行目では、リソースとして参照する画像にアクセスします。システムには外部リソースは必要ないため、実行可能ファイルのみを転送できることに注意してください。63行目では、画像が完全に透明であることを確認します。また、64行目では、読み込まれた画像を反復処理するループを導入し、65行目では、各インタラクションで、指定された色が透明であるかどうかを確認します。trueの場合、色は追加されません。falseの場合、色が追加されます。
66行目では、MQL5関数を使用して元の画像を変更後の画像に変更し、すべてをできるだけ速く実行します。68行目からはシステムをクリアしています。
71行目の先頭にあるデストラクタは、すべてのリソース(この場合はメモリ)をシステムに返し、他のプログラムで使用できるように解放するために使用されます。特別な詳細や説明は必要ありません。
80 行目には描画プロシージャがあります。このプロシージャにより、最初の4つのパラメータをガイドラインとして使用して、指定された位置と寸法でチャート上に画像が再現されます。5番目の呼び出しパラメータは0から100までの値でなければなりません。0は透明度なし、100は完全な透明度です。6番目のパラメータは、実際にレンダリングされる画像を指定します。値は常に0から始まる必要があります。これは最初の画像のインデックスであり、最大値は最高数から1を引いた値です。つまり、画像インデックスを指定するOBJ_BITMAP_LABELおよびOBJ_BITMAPオブジェクトと同じシステムを使用しますが、ここでのみ、指定したポイントを変更すると2を超える値を指定できます。この描画関数では、機能を拡張する場合、説明や追加の変更は必要ありません。
変更する必要があるのは、コンストラクタと6行目の定義だけです。ここで、ファイルから直接画像をロードしたい場合は、それも実装する必要があります。やりたいことに対して、このクラスはすでに完璧です。
次に、指標とコントロールクラスのコードに進みます。ただし、コントロールクラスのコードを見る前に、指標コードを簡単に見てみましょう。以下で全文を確認できます。
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.52" 07. #property link "https://www.mql5.com/ja/articles/11925" 08. #property indicator_chart_window 09. #property indicator_plots 0 10. //+------------------------------------------------------------------+ 11. #include <Market Replay\Service Graphics\C_Controls.mqh> 12. //+------------------------------------------------------------------+ 13. C_Controls *control = NULL; 14. //+------------------------------------------------------------------+ 15. input long user00 = 0; //ID 16. //+------------------------------------------------------------------+ 17. int OnInit() 18. { 19. u_Interprocess Info; 20. 21. ResetLastError(); 22. if (CheckPointer(control = new C_Controls(user00, "Market Replay Control", new C_Mouse(user00, "Indicator Mouse Study"))) == POINTER_INVALID) 23. SetUserError(C_Terminal::ERR_PointerInvalid); 24. if (_LastError != ERR_SUCCESS) 25. { 26. Print("Control indicator failed on initialization."); 27. return INIT_FAILED; 28. } 29. if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0; 30. EventChartCustom(user00, C_Controls::evInit, Info.s_Infos.iPosShift, Info.df_Value, ""); 31. GlobalVariableTemp(def_GlobalVariableReplay); 32. 33. return INIT_SUCCEEDED; 34. } 35. //+------------------------------------------------------------------+ 36. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 37. { 38. return rates_total; 39. } 40. //+------------------------------------------------------------------+ 41. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 42. { 43. (*control).DispatchMessage(id, lparam, dparam, sparam); 44. } 45. //+------------------------------------------------------------------+ 46. void OnDeinit(const int reason) 47. { 48. switch (reason) 49. { 50. case REASON_TEMPLATE: 51. Print("Modified template. Replay/simulation system shutting down."); 52. case REASON_PARAMETERS: 53. case REASON_REMOVE: 54. case REASON_CHARTCLOSE: 55. if (ChartSymbol(user00) != def_SymbolReplay) break; 56. GlobalVariableDel(def_GlobalVariableReplay); 57. ChartClose(user00); 58. break; 59. } 60. delete control; 61. } 62. //+------------------------------------------------------------------+
コントロール指標のソースコード
このコードの重み(量)が少し減ったことがわかります。しかし、適切にテストするにはより軽いコードが必要なので、これは一時的なものです。コントロールクラスへの呼び出しと参照が大幅に減少していることがわかります。なぜでしょうか。その理由は、プロセスの構成方法が変わったということです。アクセスポイントはできる限り少なくする必要があります。OnInitコードが非常に異なることに注意してください。次に、30行目に非常に珍しいものがあります。この手順では、コンストラクタの後にコントロールクラスのデータを初期化します。ただし、22行目のコンストラクタについては、使用するポインタの初期化のみを処理することにしました。イベントハンドラが他のすべてのタスクを処理します。このため、30行目があります。わかりやすくするために、コントロールクラスの基本コードを見てみましょう。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\Auxiliar\Interprocess.mqh" 005. #include "..\Auxiliar\C_DrawImage.mqh" 006. //+------------------------------------------------------------------+ 007. #define def_PathBMP "Images\\Market Replay\\Control\\" 008. #define def_ButtonPlay def_PathBMP + "Play.bmp" 009. #define def_ButtonPause def_PathBMP + "Pause.bmp" 010. #define def_ButtonLeft def_PathBMP + "Left.bmp" 011. #define def_ButtonLeftBlock def_PathBMP + "Left_Block.bmp" 012. #define def_ButtonRight def_PathBMP + "Right.bmp" 013. #define def_ButtonRightBlock def_PathBMP + "Right_Block.bmp" 014. #define def_ButtonPin def_PathBMP + "Pin.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. //+------------------------------------------------------------------+ 023. #define def_PrefixCtrlName "MarketReplayCTRL_" 024. #define def_PosXObjects 120 025. //+------------------------------------------------------------------+ 026. #define def_SizeButtons 32 027. #define def_ColorFilter 0xFF00FF 028. //+------------------------------------------------------------------+ 029. #include "..\Auxiliar\C_Terminal.mqh" 030. #include "..\Auxiliar\C_Mouse.mqh" 031. //+------------------------------------------------------------------+ 032. class C_Controls : private C_Terminal 033. { 034. protected: 035. enum EventCustom {evInit}; 036. private : 037. //+------------------------------------------------------------------+ 038. enum eObjectControl {ePlay, eLeft, eRight, ePin, eNull}; 039. //+------------------------------------------------------------------+ 040. struct st_00 041. { 042. string szBarSlider, 043. szBarSliderBlock; 044. int Minimal; 045. }m_Slider; 046. struct st_01 047. { 048. C_DrawImage *Btn; 049. bool state; 050. int x, y, w, h; 051. }m_Section[eObjectControl::eNull]; 052. C_Mouse *m_MousePtr; 053. //+------------------------------------------------------------------+ 054. inline void CreteBarSlider(int x, int size) 055. { 056. ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSlider = def_PrefixCtrlName + "B1", OBJ_RECTANGLE_LABEL, 0, 0, 0); 057. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x); 058. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Section[ePin].y + 11); 059. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size); 060. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9); 061. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue); 062. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack); 063. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3); 064. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT); 065. ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSliderBlock = def_PrefixCtrlName + "B2", OBJ_RECTANGLE_LABEL, 0, 0, 0); 066. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x); 067. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Section[ePin].y + 6); 068. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19); 069. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown); 070. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED); 071. } 072. //+------------------------------------------------------------------+ 073. void SetPlay(bool state) 074. { 075. if (m_Section[ePlay].Btn == NULL) 076. m_Section[ePlay].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(ePlay), def_ColorFilter, "::" + def_ButtonPlay, "::" + def_ButtonPause); 077. m_Section[ePlay].Btn.Paint(m_Section[ePlay].x, m_Section[ePlay].y, m_Section[ePlay].w, m_Section[ePlay].h, 20, ((m_Section[ePlay].state = state) ? 0 : 1)); 078. ChartRedraw(GetInfoTerminal().ID); 079. } 080. //+------------------------------------------------------------------+ 081. void CreateCtrlSlider(void) 082. { 083. CreteBarSlider(77, 436); 084. m_Section[eLeft].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(eLeft), def_ColorFilter, "::" + def_ButtonLeft, "::" + def_ButtonLeftBlock); 085. m_Section[eRight].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(eRight), def_ColorFilter, "::" + def_ButtonRight, "::" + def_ButtonRightBlock); 086. m_Section[ePin].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(ePin), def_ColorFilter, "::" + def_ButtonPin); 087. PositionPinSlider(m_Slider.Minimal); 088. } 089. //+------------------------------------------------------------------+ 090. inline void RemoveCtrlSlider(void) 091. { 092. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false); 093. for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++) 094. { 095. delete m_Section[c0].Btn; 096. m_Section[c0].Btn = NULL; 097. } 098. ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixCtrlName + "B"); 099. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true); 100. } 101. //+------------------------------------------------------------------+ 102. inline void PositionPinSlider(int p) 103. { 104. int iL, iR; 105. 106. m_Section[ePin].x = (p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p)); 107. iL = (m_Section[ePin].x != m_Slider.Minimal ? 0 : 1); 108. iR = (m_Section[ePin].x < def_MaxPosSlider ? 0 : 1); 109. m_Section[ePin].x += def_PosXObjects; 110. m_Section[ePin].x += 95 - (m_Section[ePin].w / 2); 111. for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++) 112. m_Section[c0].Btn.Paint(m_Section[c0].x, m_Section[c0].y, m_Section[c0].w, m_Section[c0].h, 20, (c0 == eLeft ? iL : (c0 == eRight ? iR : 0))); 113. ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2); 114. } 115. //+------------------------------------------------------------------+ 116. inline eObjectControl CheckPositionMouseClick(void) 117. { 118. C_Mouse::st_Mouse InfoMouse; 119. int x, y; 120. 121. InfoMouse = (*m_MousePtr).GetInfoMouse(); 122. x = InfoMouse.Position.X_Graphics; 123. y = InfoMouse.Position.Y_Graphics; 124. for (eObjectControl c0 = ePlay; c0 < eNull; c0++) 125. { 126. if ((m_Section[c0].x <= x) && (m_Section[c0].y <= y) && ((m_Section[c0].x + m_Section[c0].w) >= x) && ((m_Section[c0].y + m_Section[c0].h) >= y)) 127. return c0; 128. } 129. 130. return eNull; 131. } 132. //+------------------------------------------------------------------+ 133. public : 134. //+------------------------------------------------------------------+ 135. C_Controls(const long Arg0, const string szShortName, C_Mouse *MousePtr) 136. :C_Terminal(Arg0), 137. m_MousePtr(MousePtr) 138. { 139. if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown); 140. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false); 141. ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixCtrlName); 142. ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true); 143. for (eObjectControl c0 = ePlay; c0 < eNull; c0++) 144. { 145. m_Section[c0].h = m_Section[c0].w = def_SizeButtons; 146. m_Section[c0].y = 25; 147. m_Section[c0].Btn = NULL; 148. } 149. m_Section[ePlay].x = def_PosXObjects; 150. m_Section[eLeft].x = m_Section[ePlay].x + 47; 151. m_Section[eRight].x = m_Section[ePlay].x + 511; 152. } 153. //+------------------------------------------------------------------+ 154. ~C_Controls() 155. { 156. delete m_MousePtr; 157. for (eObjectControl c0 = ePlay; c0 < eNull; c0++) delete m_Section[c0].Btn; 158. ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixCtrlName); 159. } 160. //+------------------------------------------------------------------+ 161. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 162. { 163. u_Interprocess Info; 164. 165. switch (id) 166. { 167. case CHARTEVENT_CUSTOM + C_Controls::evInit: 168. Info.df_Value = dparam; 169. m_Slider.Minimal = Info.s_Infos.iPosShift; 170. SetPlay(Info.s_Infos.isPlay); 171. if (!Info.s_Infos.isPlay) CreateCtrlSlider(); 172. break; 173. case CHARTEVENT_OBJECT_DELETE: 174. if (StringSubstr(sparam, 0, StringLen(def_PrefixCtrlName)) == def_PrefixCtrlName) 175. { 176. if (sparam == (def_PrefixCtrlName + EnumToString(ePlay))) 177. { 178. delete m_Section[ePlay].Btn; 179. m_Section[ePlay].Btn = NULL; 180. SetPlay(false); 181. }else 182. { 183. RemoveCtrlSlider(); 184. CreateCtrlSlider(); 185. } 186. } 187. break; 188. case CHARTEVENT_MOUSE_MOVE: 189. if ((*m_MousePtr).CheckClick(C_Mouse::eClickLeft)) switch (CheckPositionMouseClick()) 190. { 191. case ePlay: 192. SetPlay(!m_Section[ePlay].state); 193. if (m_Section[ePlay].state) RemoveCtrlSlider(); 194. else CreateCtrlSlider(); 195. break; 196. case eLeft: 197. break; 198. case eRight: 199. break; 200. case ePin: 201. break; 202. } 203. break; 204. } 205. ChartRedraw(GetInfoTerminal().ID); 206. } 207. //+------------------------------------------------------------------+ 208. }; 209. //+------------------------------------------------------------------+ 210. #undef def_PosXObjects 211. #undef def_ButtonPlay 212. #undef def_ButtonPause 213. #undef def_ButtonLeft 214. #undef def_ButtonRight 215. #undef def_ButtonPin 216. #undef def_PrefixCtrlName 217. #undef def_PathBMP 218. //+------------------------------------------------------------------+
C_Controlsクラスのソースコード
「基本コード」と呼んでいるのは、実際には特定の機能や応用に役立つものではないからです。このコードは、上記の描画クラスの使用方法と、マウスポインタとの非常に単純なインタラクションをデモンストレーションするためのものです。それでは、具体的にどのようなコードなのかを見ていきましょう。26行目では、インタラクションボタンのサイズを定義しています。27行目では、画像領域が完全に透明であることを示すための色を指定します。この色は、実行可能ファイルにリソースとして埋め込まれる添付イメージで確認できます。
38行目では、メインコントロールの種類を指定しています。そして、46行目から51行目の間では、コントロールを保持する配列に配置される構造体を定義しています。このような構造体を理解する能力は、次のステップで重要となるため、ここに注目してください。
コントロールクラスのコードを見ると、以前の呼び出しがもはや使用されていないことがわかります。実際、コードにはかなりの変更が加えられています。ただし、この記事の範囲内ではまだ完成には至っておらず、状況を完全に制御できる段階にあるため、すべての詳細については説明しません。次に、73行目にあるSetPlayプロシージャを確認しましょう。プロシージャの最終的な名前がどうなるかは未定ですが、現時点ではこの名前で扱います。
このプロシージャでは、75行目で再生および一時停止ボタンのポインタが作成されているかどうかを確認します。もしポインタが作成されていない場合、76行目で描画クラスコンストラクタが実行され、使用する画像が指定されます。これは、MQL5ライブラリのObjectCreate関数を使用する場合と同様の操作です。続いて、77行目ではコントロールボタンで指定された位置に画像を再生し、追跡する画像を指定します。そして78行目で、画像を描画するためにチャートを更新するようにシステムに要求します。
他のイベントについては、後日改めて取り上げる予定ですので、ここでは触れません。ただし、188行目に注目してください。この行では、MetaTrader 5が追跡するマウスイベントをインターセプトしています。基本を理解できるように、何が起きているのかを簡単に説明します。後でコードが複雑になった際に、この部分で実際に何がおこなわれているのかをより詳細に解説します。
MetaTrader 5がマウスイベントを送信した際、システムはマウス指標に左クリックが発生したかどうかを確認します。ただし、これはマウス指標が学習モードではない場合にのみおこなわれます。クリックが発生した場合、コントロールクラスはどのボタンがクリックされたかを判定します。なお、再生/一時停止ボタンが押されたことが確認された場合、191行目から195行目のコードが実行され、ユーザーとシステム全体の間で行われているインタラクションを管理します。この際、以前発生していたエラーも修正され、ユーザーとシステム全体の間で行われているインタラクションが正しく管理されるようになります。
この動作の具体例については、下記のビデオで確認できます。まだ機能するものは何もありませんが、マウス指標とコントロール指標間でのこのような相互作用を提供するのが今回の目的です。
結論
この記事では、システムを変更することで、より楽しく便利な体験を実現する方法を探りました。この改良により、長期的にはプログラムを頻繁に書き直す必要がなくなり、システム全体がより安定し、信頼性の高いものとなることを目指しています。ただし、より良い結果を得るためには、MQL5の機能をさらに深く掘り下げる必要があることも明らかになりました。
実行ファイルそのものは動作しないため、今回のアプリケーションには含めていません。しかし、この記事に示したコードを使用し、私が開発しているものと同じシステムを手に入れたい場合は、添付されている画像を活用してください。これらの画像は、既存の古いファイルを置き換えるために使用できます。必要に応じて自由に変更および修正してください。
次回の記事では、このコントロール指標をさらに詳しく掘り下げていきます。そこから、すべてが指数関数的に進化し始めるでしょう。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/11925





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索