市場シミュレーション:ポジション表示(VII)
はじめに
皆さん、こんにちは。リプレイ/シミュレーションシステムの構築をテーマとする本連載の新しい記事へようこそ。
前回の「市場シミュレーション:ポジション表示(VI)」では、エキスパートアドバイザー(EA)からポジション表示インジケータに対して、更新が必要かどうか、あるいは多くの場合、そのままチャート上に表示しておくべきか、それとも削除すべきかを指示する方法を紹介しました。 前回の記事で取り上げた内容は、MetaTrader 5向けのさまざまなコンポーネントの開発を始めるうえで興味深いものですが、実用性という点では、まだ十分とはいえません。 なぜなら、ポジション表示インジケータを直接操作して、そのポジションのストップロスやテイクプロフィットの価格ラインを移動したり、少なくとも削除したりすることができないからです。
しかし、もう一つ解決すべき課題があります。それは、ポジション表示インジケータを何らかの方法で操作して、ポジションを決済できるようにすることです。むしろ、今はこちらのほうがさらに興味深いかもしれません。 これが実現すれば、ターミナルウィンドウの[取引]タブを常に開いておく必要がなくなります。このタブはテスト中には便利ですが、ポジション表示インジケータを直接操作できるようになれば、開いておく必要はなくなります。
この記事では、ポジション表示インジケータを操作して、価格ラインを変更したり、ポジションを直接決済したりできるように、改良を加えていきます。 実装に進む前に、特にこの仕組みに詳しくない方のために、いくつか明確にしておきたい点があります。 まず、このインジケータを使って、取引サーバー上の情報を変更することは、いかなる場合もできません。 MetaTrader 5には、注文やポジションの変更をエキスパートアドバイザー(EA)のみに許可するセキュリティシステムが設けられているためです。
MetaTrader 5自体には、標準設定のままでも注文やポジションを操作する機能が備わっています。しかし、EA以外のアプリケーションから、注文やポジションを操作することはできません。 そこで、ここからはポジション表示インジケータとEAの間に通信の仕組みを構築し、あたかもポジション表示インジケータがサーバー上の注文やポジションを直接操作しているかのように見せていきます。 ただし、先ほども述べたように、これはあくまでも見かけ上の仕組みに過ぎません。
これから説明する内容を実際に活用したいと考えている方は、MetaTrader 5上で複数のアプリケーションが連携して動作するように設定する必要があることを理解しておいてください。 ここで紹介する機能を、一つのアプリケーションだけですべて実現できると考えてはいけません。それは現実的には不可能であり、複数のアプリケーションを連携させることで、あたかも一つのアプリケーションで実現しているかのように見せるのです。 この仕組みを正しく実装すれば、MetaTrader 5での操作や取引を、より快適で使いやすいものにできるでしょう。 それでは、本題に入りましょう。
ポジション表示インジケータを操作してポジションを決済する
いくつかの仕組みの実装に入る前に、まずは簡単な安全対策を用意しておきましょう。 デモ口座であっても、ポジション表示インジケータ以外の仕組みに頼ることなく、最初からポジションを決済できる方法を実装しておきたいからです。
その仕組みは比較的シンプルで、理解も難しくありません。 実際には、チャート上にオブジェクトを作成し、所定の位置に配置するだけです。 このオブジェクトがクリックされるなど、何らかのイベントが発生すると、ポジション表示インジケータがイベントを生成します。 EAはそのイベントを捕捉します。 EAの内部には、このイベントを利用してサーバーにリクエストを送信するための仕組みを実装します。 このリクエストによって、ポジション表示インジケータがチャート上に表示しているポジションを決済します。
一見すると複雑そうに思えるかもしれませんが、実際には比較的簡単に実装できます。ただし、リプレイ/シミュレーションシステムの開発に関する連載の記事を順番に読んでいる場合に限ります。 なぜなら、以前にChart Tradeインジケータを作成した際、これと非常によく似た仕組みをすでに実装しているからです。 今回作成するのは、Chart Tradeインジケータの仕組みとよく似たものですが、いくつか細かな違いがあるため、ポジション表示インジケータではChart Tradeインジケータとは異なる実装方法を採用します。 具体的にどういうことなのか、実際に作りながら見ていきましょう。
まず、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. evTicTac, //Event of tic-tac 31. evHideMouse, //Hide mouse price line 32. evShowMouse, //Show mouse price line 33. evHideBarTime, //Hide bar time 34. evShowBarTime, //Show bar time 35. evHideDailyVar, //Hide daily variation 36. evShowDailyVar, //Show daily variation 37. evHidePriceVar, //Hide instantaneous variation 38. evShowPriceVar, //Show instantaneous variation 39. evCtrlReplayInit, //Initialize replay control 40. evChartTradeBuy, //Market buy event 41. evChartTradeSell, //Market sales event 42. evChartTradeCloseAll, //Event to close positions 43. evChartTrade_At_EA, //Event to communication 44. evEA_At_ChartTrade, //Event to communication 45. evChatWriteSocket, //Event to Mini Chat 46. evChatReadSocket, //Event To Mini Chat 47. evUpdate_Position, //Event to communication 48. evMsgClosePositionEA //Event to communication 49. }; 50. //+------------------------------------------------------------------+ 51. enum EnumPriority { //Priority list on objects 52. ePriorityNull = -1, 53. ePriorityDefault = 0, 54. ePriorityChartTrade = 5, 55. ePriorityOrders = 10 56. }; 57. //+------------------------------------------------------------------+
Defines.mqh
新しいイベントは48行目に追加されています。 これによって何を実現しようとしているのかは、明らかだと思います。 しかし、なぜ42行目で宣言されているChart Tradeインジケータのイベントを使用しないのでしょうか。 理由は簡単です。ポジション表示インジケータやEAを、特定の口座タイプだけに限定したくないからです。 アプリケーションがネッティング口座でのみ動作すると確信できるのであれば、42行目のイベントをそのまま使用しても何の問題もありません。 しかし、同じアプリケーション、つまりポジション表示インジケータとEAをヘッジ口座で使用した場合、1つのポジションを決済しようとすると、すべてのポジションが同時に決済されてしまいます。 これが、この2つのインジケータの実装方法における違いの一つです。
さて、これから何をおこなうのか理解できたところで、次はポジション表示インジケータとEAの両方のコードを変更し、イベントが正しく解釈・処理されるようにする必要があります。 ここで、「これは大変そうだ。 コードにかなり多くの変更を加えなければならないのではないか」と心配になるかもしれません。しかし、もしそう思ったのであれば、コードの仕組みを十分に理解できていないということです。 実際には、この作業に必要な変更はほんのわずかです。 まずは、EAのコードに加える変更から見ていきましょう。 以下のコードに示します。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\Defines.mqh" 005. //+------------------------------------------------------------------+ 006. class C_Orders 007. { 008. protected: 009. //+------------------------------------------------------------------+ 010. inline const ulong GetMagicNumber(void) const { return m_Base.MagicNumber; } 011. //+------------------------------------------------------------------+ 012. bool ClosePosition(const ulong ticket) 013. { 014. bool IsBuy; 015. string szContract; 016. 017. if (!PositionSelectByTicket(ticket)) return false; 018. IsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY; 019. szContract = PositionGetString(POSITION_SYMBOL); 020. ZeroMemory(m_Base.TradeRequest); 021. m_Base.TradeRequest.action = TRADE_ACTION_DEAL; 022. m_Base.TradeRequest.type = (IsBuy ? ORDER_TYPE_SELL : ORDER_TYPE_BUY); 023. m_Base.TradeRequest.price = NormalizeDouble(SymbolInfoDouble(szContract, (IsBuy ? SYMBOL_BID : SYMBOL_ASK)), (int)SymbolInfoInteger(szContract, SYMBOL_DIGITS)); 024. m_Base.TradeRequest.position = ticket; 025. m_Base.TradeRequest.symbol = szContract; 026. m_Base.TradeRequest.volume = PositionGetDouble(POSITION_VOLUME); 027. m_Base.TradeRequest.deviation = 1000; 028. 029. return SendToPhysicalServer() != 0; 030. }; 031. //+------------------------------------------------------------------+ 032. private : 033. //+------------------------------------------------------------------+ 034. struct stBase 035. { 036. MqlTradeRequest TradeRequest; 037. ulong MagicNumber; 038. bool bTrash; 039. }m_Base; 040. //+------------------------------------------------------------------+ 041. struct stChartTrade 042. { 043. struct stEvent 044. { 045. EnumEvents ev; 046. string szSymbol, 047. szContract; 048. bool IsDayTrade; 049. ushort Leverange; 050. double PointsTake, 051. PointsStop; 052. }Data; 053. //--- 054. bool Decode(const EnumEvents ev, const string sparam) 055. { 056. string Res[]; 057. 058. if (StringSplit(sparam, '?', Res) != 7) return false; 059. stEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])}; 060. if ((ev == loc.ev) && (loc.szSymbol == _Symbol)) Data = loc; 061. else return false; 062. 063. return true; 064. } 065. //--- 066. }m_ChartTrade; 067. //+------------------------------------------------------------------+ 068. ulong SendToPhysicalServer(void) 069. { 070. MqlTradeCheckResult TradeCheck; 071. MqlTradeResult TradeResult; 072. 073. ZeroMemory(TradeCheck); 074. ZeroMemory(TradeResult); 075. if (!OrderCheck(m_Base.TradeRequest, TradeCheck)) 076. { 077. PrintFormat("Order System - Check Error: %d", GetLastError()); 078. return 0; 079. } 080. m_Base.bTrash = OrderSend(m_Base.TradeRequest, TradeResult); 081. if (TradeResult.retcode != TRADE_RETCODE_DONE) 082. { 083. PrintFormat("Order System - Send Error: %d", TradeResult.retcode); 084. return 0; 085. }; 086. 087. return TradeResult.order; 088. } 089. //+------------------------------------------------------------------+ 090. ulong ToMarket(const ENUM_ORDER_TYPE type) 091. { 092. double price = SymbolInfoDouble(m_ChartTrade.Data.szContract, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID)); 093. double vol = SymbolInfoDouble(m_ChartTrade.Data.szContract, SYMBOL_VOLUME_STEP); 094. uchar nDigit = (uchar)SymbolInfoInteger(m_ChartTrade.Data.szContract, SYMBOL_DIGITS); 095. 096. ZeroMemory(m_Base.TradeRequest); 097. m_Base.TradeRequest.magic = m_Base.MagicNumber; 098. m_Base.TradeRequest.symbol = m_ChartTrade.Data.szContract; 099. m_Base.TradeRequest.price = NormalizeDouble(price, nDigit); 100. m_Base.TradeRequest.action = TRADE_ACTION_DEAL; 101. m_Base.TradeRequest.sl = NormalizeDouble(m_ChartTrade.Data.PointsStop == 0 ? 0 : price + (m_ChartTrade.Data.PointsStop * (type == ORDER_TYPE_BUY ? -1 : 1)), nDigit); 102. m_Base.TradeRequest.tp = NormalizeDouble(m_ChartTrade.Data.PointsTake == 0 ? 0 : price + (m_ChartTrade.Data.PointsTake * (type == ORDER_TYPE_BUY ? 1 : -1)), nDigit); 103. m_Base.TradeRequest.volume = NormalizeDouble(vol + (vol * (m_ChartTrade.Data.Leverange - 1)), nDigit); 104. m_Base.TradeRequest.type = type; 105. m_Base.TradeRequest.type_time = (m_ChartTrade.Data.IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC); 106. m_Base.TradeRequest.stoplimit = 0; 107. m_Base.TradeRequest.expiration = 0; 108. m_Base.TradeRequest.type_filling = ORDER_FILLING_RETURN; 109. m_Base.TradeRequest.deviation = 1000; 110. m_Base.TradeRequest.comment = "Order Generated by Experts Advisor."; 111. 112. MqlTradeRequest TradeRequest[1]; 113. 114. TradeRequest[0] = m_Base.TradeRequest; 115. ArrayPrint(TradeRequest); 116. 117. return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? SendToPhysicalServer() : 0); 118. }; 119. //+------------------------------------------------------------------+ 120. void CloseAllsPosition(void) 121. { 122. for (int count = PositionsTotal() - 1; count >= 0; count--) 123. { 124. if (PositionGetSymbol(count) != m_ChartTrade.Data.szContract) continue; 125. if (PositionGetInteger(POSITION_MAGIC) != m_Base.MagicNumber) continue; 126. ClosePosition(PositionGetInteger(POSITION_TICKET)); 127. } 128. }; 129. //+------------------------------------------------------------------+ 130. public : 131. //+------------------------------------------------------------------+ 132. C_Orders(const ulong magic) 133. { 134. m_Base.MagicNumber = magic; 135. } 136. //+------------------------------------------------------------------+ 137. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 138. { 139. switch (id) 140. { 141. case CHARTEVENT_CUSTOM + evChartTradeBuy : 142. case CHARTEVENT_CUSTOM + evChartTradeSell : 143. case CHARTEVENT_CUSTOM + evChartTradeCloseAll: 144. if (m_ChartTrade.Decode((EnumEvents)(id - CHARTEVENT_CUSTOM), sparam)) switch (m_ChartTrade.Data.ev) 145. { 146. case evChartTradeBuy: 147. ToMarket(ORDER_TYPE_BUY); 148. break; 149. case evChartTradeSell: 150. ToMarket(ORDER_TYPE_SELL); 151. break; 152. case evChartTradeCloseAll: 153. CloseAllsPosition(); 154. break; 155. } 156. break; 157. case CHARTEVENT_CUSTOM + evMsgClosePositionEA: 158. ClosePosition((ulong)StringToInteger(sparam)); 159. break; 160. } 161. } 162. //+------------------------------------------------------------------+ 163. }; 164. //+------------------------------------------------------------------+
C_Orders.mqh
ここでは、本連載を読み進めていない方にも処理の流れを理解していただけるよう、C_Ordersクラスのコード全体を掲載していますが、実際にクラスのコードに追加したのは、 157、158、159行目のわずか3行だけです。 EAがポジション決済のリクエストを処理できるようにするために必要な変更は、これだけです。 ここで注意していただきたいのは、この部分ではデータのフィルタリングを一切おこなっていないということです。 したがって、たとえばサービスやスクリプトからでも、同じイベントを発生させることができます。 つまり、ポジションの決済という処理を実行するようEAに指示するのが、具体的にどのアプリケーションなのかについては、何の制限も設けていません。 実際に注意すべきなのは、決済対象となるポジションの識別子を、sparamフィールドに指定することだけです。 該当するチケットが存在すれば、そのポジションは決済されます。
ここで、先ほど説明した点を思い出してください。evChartTradeCloseAllイベントを使用できなかったのは、このイベントで呼び出す処理と、今回必要となる呼び出し処理が異なるためです。 今回の実装は、ネッティング口座でもヘッジ口座でも、口座タイプを問わず動作するように設計されています。 では、 EA側の実装は完了です。C_Ordersクラスで使用しているこのコードは、すでにかなり長い期間にわたってテストを重ねてきたものなので、問題なく動作することも確認できています。
次は、皆さんが最も難しいと感じるかもしれない部分に取り組みましょう。 しかし、実際にはとても簡単だということがお分かりいただけるはずです。 ポジション表示インジケータ側の実装もそれほど複雑ではないことを示すため、今回はインジケータのコードに大幅な変更を加えることはしません。 インジケータを操作したときに、指定したポジションを決済するリクエストがEAに送信されるよう、必要な処理を追加するだけです。 これは非常に簡単に実現できます。以下をご覧ください。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #define def_SufixTake "Take" 005. #define def_SufixStop "Stop" 006. //+------------------------------------------------------------------+ 007. #define def_NameBtnClose m_Infos.szShortName + "Close" 008. //+------------------------------------------------------------------+ 009. #define def_PathBtns "Images\\Market Replay\\Orders\\" 010. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp" 011. #resource "\\" + def_Btn_Close; 012. //+------------------------------------------------------------------+ 013. #include "..\Auxiliar\C_Terminal.mqh" 014. //+------------------------------------------------------------------+ 015. class C_IndicatorPosition : private C_Terminal 016. { 017. private : 018. struct st00 019. { 020. ulong ticket; 021. color corPrice, corTake, corStop; 022. string szShortName; 023. }m_Infos; 024. //+------------------------------------------------------------------+ 025. inline void MoveLineInfos(const string szDefine, const double price) 026. { 027. string szName = m_Infos.szShortName + szDefine; 028. int x, y; 029. 030. if (price <= 0) ObjectDelete(0, szName); 031. else 032. { 033. if (ObjectFind(0, szName) < 0) 034. { 035. if (szDefine == def_SufixTake) CreateLineInfos(def_SufixTake, m_Infos.corTake, "Take Profit point."); else 036. if (szDefine == def_SufixStop) CreateLineInfos(def_SufixStop, m_Infos.corStop, "Stop Loss point."); else 037. { 038. CreateLineInfos(NULL, m_Infos.corPrice, "Position opening price."); 039. CreateObjectGraphics(def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityOrders + 1)); 040. ObjectSetString(0, def_NameBtnClose, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close); 041. } 042. } 043. ObjectSetDouble(0, szName, OBJPROP_PRICE, price); 044. if (szDefine == NULL) 045. { 046. ChartTimePriceToXY(0, 0, 0, price, x, y); 047. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, 130); 048. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y - 8); 049. } 050. } 051. } 052. //+------------------------------------------------------------------+ 053. void CreateLineInfos(string szObjName, const color cor, const string szDescription = "\n") 054. { 055. szObjName = m_Infos.szShortName + szObjName; 056. CreateObjectGraphics(szObjName, OBJ_HLINE, cor, (EnumPriority)(cor == m_Infos.corPrice ? ePriorityNull : (ePriorityOrders + (cor == m_Infos.corStop)))); 057. ObjectSetString(0, szObjName, OBJPROP_TEXT, szDescription); 058. ObjectSetString(0, szObjName, OBJPROP_TOOLTIP, szDescription); 059. ObjectSetInteger(0, szObjName, OBJPROP_SELECTABLE, cor != m_Infos.corPrice); 060. } 061. //+------------------------------------------------------------------+ 062. public : 063. //+------------------------------------------------------------------+ 064. C_IndicatorPosition(color corPrice, color corTake, color corStop) 065. :C_Terminal() 066. { 067. ZeroMemory(m_Infos); 068. m_Infos.corPrice = corPrice; 069. m_Infos.corTake = corTake; 070. m_Infos.corStop = corStop; 071. } 072. //+------------------------------------------------------------------+ 073. ~C_IndicatorPosition() 074. { 075. if (m_Infos.ticket != 0) 076. ObjectsDeleteAll(0, IntegerToString(m_Infos.ticket)); 077. } 078. //+------------------------------------------------------------------+ 079. bool CheckCatch(ulong ticket) 080. { 081. m_Infos.szShortName = IntegerToString(m_Infos.ticket = ticket); 082. if (!PositionSelectByTicket(m_Infos.ticket)) return false; 083. if (ObjectFind(0, m_Infos.szShortName) >= 0) 084. { 085. m_Infos.ticket = 0; 086. return false; 087. } 088. IndicatorSetString(INDICATOR_SHORTNAME, m_Infos.szShortName); 089. EventChartCustom(0, evUpdate_Position, ticket, 0, ""); 090. 091. return true; 092. } 093. //+------------------------------------------------------------------+ 094. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 095. { 096. static double price = 0; 097. 098. switch (id) 099. { 100. case CHARTEVENT_CUSTOM + evUpdate_Position: 101. if (lparam != m_Infos.ticket) return; 102. if (!PositionSelectByTicket(m_Infos.ticket)) 103. { 104. ChartIndicatorDelete(0, 0, m_Infos.szShortName); 105. return; 106. }; 107. MoveLineInfos(NULL, price = PositionGetDouble(POSITION_PRICE_OPEN)); 108. MoveLineInfos(def_SufixTake, PositionGetDouble(POSITION_TP)); 109. MoveLineInfos(def_SufixStop, PositionGetDouble(POSITION_SL)); 110. break; 111. case CHARTEVENT_CHART_CHANGE: 112. MoveLineInfos(NULL, price); 113. break; 114. case CHARTEVENT_OBJECT_CLICK: 115. if (sparam == def_NameBtnClose) 116. EventChartCustom(0, evMsgClosePositionEA, 0, 0, m_Infos.szShortName); 117. break; 118. } 119. ChartRedraw(); 120. } 121. //+------------------------------------------------------------------+ 122. }; 123. //+------------------------------------------------------------------+ 124. #undef def_Btn_Close 125. #undef def_PathBtns 126. //+------------------------------------------------------------------+ 127. #undef def_NameBtnClose 128. //+------------------------------------------------------------------+ 129. #undef def_SufixTake 130. #undef def_SufixStop 131. //+------------------------------------------------------------------+
C_IndicatorPosition.mqh
それでは、C_IndicatorPositionクラスのコードを見ていきましょう。 実装上の変更はごくわずかであることに注目してください。 しかし、このコードによって、チャート上にオブジェクトを配置し、それを操作してEAにポジション決済のリクエストを送ることができるようになります。 では、これはどのような仕組みで実現されているのでしょうか。 今後、いくつかの改良を加える前に、まずはこのコードを詳しく見ていきましょう。 現時点では、このコードはインジケータのさらなる開発にそのまま使用できる状態ではありません。 しかし、読者の皆さんには、より複雑なコードに進む前に、この仕組みを理解しておいていただきたいと思います。
まず、07行目に定義があることに注目してください。 これは、ポジションを決済するために操作するオブジェクトの名前を定義するものです。 次に、09行目から11行目には、ローカル環境で準備しておく必要があるものが記述されています。 これは、独自のテンプレートを作成したい場合に必要となります。 自分で用意する予定がない方は、心配する必要はありません。前回の記事と同様に、この記事の公開に合わせて、必要なアプリケーションも間もなく提供する予定です。
それでは、続きを見ていきましょう。 09行目では、ビットマップ画像の保存場所を指定しています。 10行目では、そのビットマップ画像が何を表すのかを指定しています。 この場合、16×16ピクセルの画像を用意する必要があります。 ただし、コードに適切な変更を加えれば、別のサイズの画像を使用することもできます。 どこを変更すればよいのかは、後ほど説明します。 ここでは、まず11行目に注目してください。 この行では、ビットマップ画像を実行ファイル内のリソースとして組み込むよう、MQL5コンパイラに指示しています。 これにより、コンパイル後にビットマップ画像をアプリケーションのほかのファイルと一緒に配布する必要がなくなり、開発者にとってもエンドユーザーにとっても、作業が大幅に簡単になります。
ここまでは、大きな変更を加えることなくコードを見てきましたが、例外が2か所あります。 まず、25行目の処理については、今はまだ気にする必要はありません。 ここに変更が加えられているからです。先に94行目へ進みましょう。ここには、MetaTrader 5から送られてくるメッセージを処理するコードが実装されています。
ここからは、特によく注意して見てください。94行目で定義されているプロシージャ内で何がおこなわれているのかを理解することが、次に説明する内容を理解するうえで非常に重要になります。 まず、96行目でstatic変数が追加されていることに注目してください。 この変数は最終的には削除する予定ですが、 現時点では、ポジションのオープン価格をローカルに保存し、メモリ上に保持するために使用します。 また、この同じ変数が107行目で初期化されていることにも注目してください。 では、 ポジションがオープンされた価格を取得できました。 重要なのは、この値が処理の間ずっと変わらないということです。 しかし、この値が必要になるのは、特定のタイミングです。 それが112行目で発生します。 この行は、CHARTEVENT_CHART_CHANGEイベントのハンドラ内にあります。 理由は簡単です。 チャートの価格スケールを変更すると、それに合わせて価格ラインの表示位置も変わるからです。
しかし、後ほど25行目に戻ったときに説明しますが、ここには一つ問題があります。チャート上に配置したボタンは、価格ラインと一緒には移動しないのです。 この問題を解決するために、112行目に示されているように、チャート上のボタンの位置を更新するリクエストを送信します。 そのために、ポジションがオープンされた価格をあらかじめ把握しておく必要があるのです。 価格スケールが変更されるたびに、ポジションのオープン価格を取得し直さなければならないとしたら、チャート上で動作しているアプリケーションの実行に大きな遅延が生じてしまいます。 ポジションの価格は変わらないのですから、できるだけ処理を簡略化しておくことができます。
さて、DispatchMessageプロシージャで実際に注目したいのは、その次のブロック、つまり114行目に実装されているコードです。 ここでは、オブジェクトがクリックされたかどうかを確認しています。 MetaTrader 5からクリックが発生したという通知を受け取ると、どのオブジェクトがクリックされたのかも正確に通知されます。 そのオブジェクト名は、sparam変数に渡されます。 そこで115行目では、その名前が最初に定義したオブジェクト、つまり07行目で作成したオブジェクトの名前と一致するかどうかを確認しています。 115行目の判定結果がtrueであれば、116行目でMetaTrader 5にカスタムイベントの送信を要求します。これにより、EAが後からそのイベントを受け取れるようになります。
ここで、このイベントの値を確認してみてください。 これらは、ポジション決済のリクエストを認識するためにEAが期待している値と、まったく同じです。 興味深い仕組みだと思いませんか。 それでは次に、ポジション決済用のボタンを表すオブジェクト(ビットマップ画像)が、どのように作成され、チャート上に配置されるのかを見ていきましょう。 この部分については、ひとまず25行目に注目します。ここで重要な処理が実行されています。
前のバージョンと比べて、ここにはいくつか変更が加えられています。 まず、28行目で変数xとyを追加しています。 続いて38行目では、先ほどと同じように、価格を文字列に変換しています。 しかし今回は、もう一つオブジェクトが必要です。それがポジション決済用のボタンです。このオブジェクトを39行目で作成しています。 ここで、どのような方法でオブジェクトを作成しているのか、また、どの種類のオブジェクトを使用しているのかに注目してください。 ここではOBJ_BITMAP_LABELオブジェクトを作成しています。 このオブジェクトを正しく配置するには、X座標とY座標の値が必要です。 価格座標を使用することはできません。 オブジェクトを作成した直後の40行目では、実行ファイル内のリソースとして組み込まれている画像を使用するよう、MetaTrader 5に指示しています。 つまり、実行ファイルに組み込まれた画像を実際にオブジェクトに割り当て、外部ディレクトリにある画像の代わりに使用するのは、この段階です。11行目ではありません。 ただし、この時点では、まだオブジェクトを作成しただけで、画面上には表示されていません。
正しい位置に配置するには、価格と時間による座標を、デカルト座標に変換する必要があります。 幸い、MQL5にはそのための便利な関数が用意されており、46行目で使用しています。 ここでは、価格をY軸上の適切な座標に変換しています。 これは最も簡単な部分ですが、いくつか注意すべき点があります。 それを理解するために、48行目で何をおこなっているかを見てください。 ここでは、46行目の関数が返した値から8を引いています。 なぜこの値を引くのでしょうか。また、なぜ8なのでしょうか。 この計算をおこなうのは、OBJ_BITMAP_LABELオブジェクトの基準点が中央ではなく、左上隅に設定されているためです。
オブジェクトの基準点を中央に設定していないため、位置を調整する必要があります。 そのために8を引いているのです。 今回使用しているビットマップ画像は16×16ピクセルなので、画像の中心を合わせるには8ピクセル分を差し引く必要があります。 それだけです。 これで、作成したオブジェクトをクリックするだけで、ポジション表示インジケータを操作してポジションを決済できるようになります。 しかし、実はこれ以外にも、さらに多くのことができます。 ただし、コードの保守が困難になるのを避けるため、ここでコードを改良しておく必要があります。これからおこなうのが、まさにその作業です。 この改良によって、コードを最小限に抑えながら、より多くの機能を実現できるようになります。 なお、話題を適切に分けるため、この内容については新しいセクションで説明することにします。
インジケータのさらなる改善
少し立ち止まって考えてみると、ポジションを決済するためのオブジェクトが、すでに用意されていることに気づくはずです。 チャートには価格ラインがすでに表示されています。 また、ポジション表示インジケータからEAへリクエストを送信する機能も備わっています。 しかも、これらはすべて必要最小限の変更だけで実現されています。 では、ポジションの決済に使用したものと同じ仕組みを、ストップロスラインやテイクプロフィットラインにも適用し、損失の上限や利益の目標値を解除できるようにするには、どうすればよいでしょうか。 しかも、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. #define def_MaskTimeService 0xFED00000 16. #define def_IndicatorTimeFrame (_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96)))) 17. #define def_IndexTimeFrame 4 18. //+------------------------------------------------------------------+ 19. union uCast_Double 20. { 21. double dValue; 22. long _long; // 1 Information 23. datetime _datetime; // 1 Information 24. uint _32b[sizeof(double) / sizeof(uint)]; // 2 Informations 25. ushort _16b[sizeof(double) / sizeof(ushort)]; // 4 Informations 26. uchar _8b [sizeof(double) / sizeof(uchar)]; // 8 Informations 27. }; 28. //+------------------------------------------------------------------+ 29. enum EnumEvents { 30. evTicTac, //Event of tic-tac 31. evHideMouse, //Hide mouse price line 32. evShowMouse, //Show mouse price line 33. evHideBarTime, //Hide bar time 34. evShowBarTime, //Show bar time 35. evHideDailyVar, //Hide daily variation 36. evShowDailyVar, //Show daily variation 37. evHidePriceVar, //Hide instantaneous variation 38. evShowPriceVar, //Show instantaneous variation 39. evCtrlReplayInit, //Initialize replay control 40. evChartTradeBuy, //Market buy event 41. evChartTradeSell, //Market sales event 42. evChartTradeCloseAll, //Event to close positions 43. evChartTrade_At_EA, //Event to communication 44. evEA_At_ChartTrade, //Event to communication 45. evChatWriteSocket, //Event to Mini Chat 46. evChatReadSocket, //Event To Mini Chat 47. evUpdate_Position, //Event to communication 48. evMsgClosePositionEA, //Event to communication 49. evMsgCloseTakeProfit, //Event to communication 50. evMsgCloseStopLoss //Event to communication 51. }; 52. //+------------------------------------------------------------------+ 53. enum EnumPriority { //Priority list on objects 54. ePriorityNull = -1, 55. ePriorityDefault = 0, 56. ePriorityChartTrade = 5, 57. ePriorityOrders = 10 58. }; 59. //+------------------------------------------------------------------+
Defines.mqh
ここでおこなったことは非常にシンプルで実用的です。 何を変更したのかも、すぐにお分かりいただけると思います。 それでは、次のステップに進みましょう。 最初の段階では、作業に少し時間がかかります。 何がおこなわれているのかをしっかり理解していただくために、焦らず進めていきます。 さて、今回はストップロスラインとテイクプロフィットラインを解除できる仕組みを追加したいと考えています。 しかし、単にメッセージブロックにその機能を追加するだけでは不十分です。実際にリクエストを実行することができないからです。 そのため、EAのコードも変更する必要があります。 この変更をおこなうため、重要な部分から直接取りかかりましょう。C_Orders.mqhヘッダーファイルです。 以下に示すように、このファイルに新しいコードを追加します。
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\Defines.mqh" 005. //+------------------------------------------------------------------+ 006. class C_Orders 007. { 008. protected: 009. //+------------------------------------------------------------------+ 010. inline const ulong GetMagicNumber(void) const { return m_Base.MagicNumber; } 011. //+------------------------------------------------------------------+ 012. bool ClosePosition(const ulong ticket) 013. { 014. bool IsBuy; 015. string szContract; 016. 017. if (!PositionSelectByTicket(ticket)) return false; 018. IsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY; 019. szContract = PositionGetString(POSITION_SYMBOL); 020. ZeroMemory(m_Base.TradeRequest); 021. m_Base.TradeRequest.action = TRADE_ACTION_DEAL; 022. m_Base.TradeRequest.type = (IsBuy ? ORDER_TYPE_SELL : ORDER_TYPE_BUY); 023. m_Base.TradeRequest.price = NormalizeDouble(SymbolInfoDouble(szContract, (IsBuy ? SYMBOL_BID : SYMBOL_ASK)), (int)SymbolInfoInteger(szContract, SYMBOL_DIGITS)); 024. m_Base.TradeRequest.position = ticket; 025. m_Base.TradeRequest.symbol = szContract; 026. m_Base.TradeRequest.volume = PositionGetDouble(POSITION_VOLUME); 027. m_Base.TradeRequest.deviation = 1000; 028. 029. return SendToPhysicalServer() != 0; 030. }; 031. //+------------------------------------------------------------------+ 032. private : 033. //+------------------------------------------------------------------+ 034. struct stBase 035. { 036. MqlTradeRequest TradeRequest; 037. ulong MagicNumber; 038. bool bTrash; 039. }m_Base; 040. //+------------------------------------------------------------------+ 041. struct stChartTrade 042. { 043. struct stEvent 044. { 045. EnumEvents ev; 046. string szSymbol, 047. szContract; 048. bool IsDayTrade; 049. ushort Leverange; 050. double PointsTake, 051. PointsStop; 052. }Data; 053. //--- 054. bool Decode(const EnumEvents ev, const string sparam) 055. { 056. string Res[]; 057. 058. if (StringSplit(sparam, '?', Res) != 7) return false; 059. stEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])}; 060. if ((ev == loc.ev) && (loc.szSymbol == _Symbol)) Data = loc; 061. else return false; 062. 063. return true; 064. } 065. //--- 066. }m_ChartTrade; 067. //+------------------------------------------------------------------+ 068. ulong SendToPhysicalServer(void) 069. { 070. MqlTradeCheckResult TradeCheck; 071. MqlTradeResult TradeResult; 072. 073. ZeroMemory(TradeCheck); 074. ZeroMemory(TradeResult); 075. if (!OrderCheck(m_Base.TradeRequest, TradeCheck)) 076. { 077. PrintFormat("Order System - Check Error: %d", GetLastError()); 078. return 0; 079. } 080. m_Base.bTrash = OrderSend(m_Base.TradeRequest, TradeResult); 081. if (TradeResult.retcode != TRADE_RETCODE_DONE) 082. { 083. PrintFormat("Order System - Send Error: %d", TradeResult.retcode); 084. return 0; 085. }; 086. 087. return TradeResult.order; 088. } 089. //+------------------------------------------------------------------+ 090. ulong ToMarket(const ENUM_ORDER_TYPE type) 091. { 092. double price = SymbolInfoDouble(m_ChartTrade.Data.szContract, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID)); 093. double vol = SymbolInfoDouble(m_ChartTrade.Data.szContract, SYMBOL_VOLUME_STEP); 094. uchar nDigit = (uchar)SymbolInfoInteger(m_ChartTrade.Data.szContract, SYMBOL_DIGITS); 095. 096. ZeroMemory(m_Base.TradeRequest); 097. m_Base.TradeRequest.magic = m_Base.MagicNumber; 098. m_Base.TradeRequest.symbol = m_ChartTrade.Data.szContract; 099. m_Base.TradeRequest.price = NormalizeDouble(price, nDigit); 100. m_Base.TradeRequest.action = TRADE_ACTION_DEAL; 101. m_Base.TradeRequest.sl = NormalizeDouble(m_ChartTrade.Data.PointsStop == 0 ? 0 : price + (m_ChartTrade.Data.PointsStop * (type == ORDER_TYPE_BUY ? -1 : 1)), nDigit); 102. m_Base.TradeRequest.tp = NormalizeDouble(m_ChartTrade.Data.PointsTake == 0 ? 0 : price + (m_ChartTrade.Data.PointsTake * (type == ORDER_TYPE_BUY ? 1 : -1)), nDigit); 103. m_Base.TradeRequest.volume = NormalizeDouble(vol + (vol * (m_ChartTrade.Data.Leverange - 1)), nDigit); 104. m_Base.TradeRequest.type = type; 105. m_Base.TradeRequest.type_time = (m_ChartTrade.Data.IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC); 106. m_Base.TradeRequest.stoplimit = 0; 107. m_Base.TradeRequest.expiration = 0; 108. m_Base.TradeRequest.type_filling = ORDER_FILLING_RETURN; 109. m_Base.TradeRequest.deviation = 1000; 110. m_Base.TradeRequest.comment = "Order Generated by Experts Advisor."; 111. 112. MqlTradeRequest TradeRequest[1]; 113. 114. TradeRequest[0] = m_Base.TradeRequest; 115. ArrayPrint(TradeRequest); 116. 117. return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? SendToPhysicalServer() : 0); 118. }; 119. //+------------------------------------------------------------------+ 120. void CloseAllsPosition(void) 121. { 122. for (int count = PositionsTotal() - 1; count >= 0; count--) 123. { 124. if (PositionGetSymbol(count) != m_ChartTrade.Data.szContract) continue; 125. if (PositionGetInteger(POSITION_MAGIC) != m_Base.MagicNumber) continue; 126. ClosePosition(PositionGetInteger(POSITION_TICKET)); 127. } 128. }; 129. //+------------------------------------------------------------------+ 130. void ModifyValueSLTP(const ulong ticket, const string symbol, const double sl, const double tp) 131. { 132. ZeroMemory(m_Base.TradeRequest); 133. MqlTradeRequest TradeRequest[1]; 134. 135. m_Base.TradeRequest.magic = m_Base.MagicNumber; 136. m_Base.TradeRequest.action = TRADE_ACTION_SLTP; 137. m_Base.TradeRequest.symbol = symbol; 138. m_Base.TradeRequest.position = ticket; 139. m_Base.TradeRequest.sl = sl; 140. m_Base.TradeRequest.tp = tp; 141. 142. TradeRequest[0] = m_Base.TradeRequest; 143. ArrayPrint(TradeRequest); 144. 145. SendToPhysicalServer(); 146. } 147. //+------------------------------------------------------------------+ 148. public : 149. //+------------------------------------------------------------------+ 150. C_Orders(const ulong magic) 151. { 152. m_Base.MagicNumber = magic; 153. } 154. //+------------------------------------------------------------------+ 155. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 156. { 157. switch (id) 158. { 159. case CHARTEVENT_CUSTOM + evChartTradeBuy : 160. case CHARTEVENT_CUSTOM + evChartTradeSell : 161. case CHARTEVENT_CUSTOM + evChartTradeCloseAll: 162. if (m_ChartTrade.Decode((EnumEvents)(id - CHARTEVENT_CUSTOM), sparam)) switch (m_ChartTrade.Data.ev) 163. { 164. case evChartTradeBuy: 165. ToMarket(ORDER_TYPE_BUY); 166. break; 167. case evChartTradeSell: 168. ToMarket(ORDER_TYPE_SELL); 169. break; 170. case evChartTradeCloseAll: 171. CloseAllsPosition(); 172. break; 173. } 174. break; 175. case CHARTEVENT_CUSTOM + evMsgClosePositionEA: 176. ClosePosition((ulong)(lparam)); 177. break; 178. case CHARTEVENT_CUSTOM + evMsgCloseTakeProfit: 179. ModifyValueSLTP((ulong)(lparam), sparam, dparam, 0); 180. break; 181. case CHARTEVENT_CUSTOM + evMsgCloseStopLoss: 182. ModifyValueSLTP((ulong)(lparam), sparam, 0, dparam); 183. break; 184. } 185. } 186. //+------------------------------------------------------------------+ 187. }; 188. //+------------------------------------------------------------------+
C_Orders.mqh
上記のコードは、これまでと同様、すべてを含んだ完全なものです。 これを見ると、前のバージョンと比べてそれほど大きな違いはないと思われるかもしれません。実際、常にコードに大幅な変更を加えることを避けるようにしています。 しかし、今回はいくつかの変更をおこなっています。 これらの変更は、現在および次の開発段階で必要となる機能をサポートするためのものです。 まずは、ストップロスとテイクプロフィットのラインを解除する今回のステップに注目しましょう。 上記のコードの130行目に、新しいプロシージャが追加されていることに注目してください。 このプロシージャは、すでにオープンしているポジションのストップロスとテイクプロフィットの値を変更するために使用します。 この点を覚えておいてください。 現時点では、注文板にある注文を扱っているのではなく、すでにオープンしているポジションを扱っています。 この2つの状況には違いがあります。
基本的には、いくつかのパラメータを受け取り、テイクプロフィットとストップロスのレベルを変更するリクエストを取引サーバーに送信します。 143行目では、サーバーに送信したリクエストをターミナルに出力しています。 これにより、リクエストの内容を確認し、システムの動作を分析できます。 さて、このプロシージャは非常にシンプルで、引数を渡すだけで済みますので、コードが変更された次の部分に進みましょう。 変更箇所は155行目にあるプロシージャです。
ここでは、EAが受け取ったリクエストを実際に処理します。 覚えておいていただきたいのは、リクエストはどのような送信元から届く可能性もあるということです。必ずしも、ここで紹介しているポジション表示インジケータから送信されるとは限りません。 ぜひ、ここで紹介する内容をよく理解し、ご自身のソリューションを作成する際に役立ててください。 DispatchMessageプロシージャのコードは、以前のものとほとんど変わっていません。 よくご覧ください。 いくつか変更されていることに気づくはずです。 その理由については、次の記事で解説します。
175行目では、受信したメッセージがポジション決済のリクエストかどうかを確認しています。ここで、ポジションを決済する処理の実装が変更されていることに注目してください。 具体的には、176行目を少し変更しています。 以前は識別子をsparam変数で渡していましたが、今回はlparam変数を使用しています。 これには理由がありますが、その理由は次の記事でより詳しく理解できるでしょう。 次に、受信したメッセージの種類を判別するためにおこなう、他の2つのチェックを見ていきましょう。 最初のチェックは178行目です。ここでは、受信したメッセージがテイクプロフィットレベルの解除要求に該当するかどうかを確認しています。 2つ目のチェックは181行目です。ここでは、メッセージがストップロスレベルの解除要求かどうかを確認しています。
どちらの場合も、呼び出すのは同じModifyValueSLTPプロシージャです。このプロシージャは、ポジションのストップロスとテイクプロフィットのレベルを変更するために使用します。 それぞれのパラメータに何が渡されているのか、よく確認してください。 ここで唯一異なるのは、dparamパラメータの使い方です。 一方のケースではテイクプロフィットに適用され、もう一方ではストップロスに適用されます。 このように、どちらか一方のレベルにゼロを設定することで、そのレベルを解除できます。 これは、取引サーバーがゼロという値を、テイクプロフィットまたはストップロスのいずれかの制限が存在しないことを意味するものとして解釈するためです。 これで、EAのコードに必要な変更はすべて完了です。 少なくとも、現時点では、これ以上の変更は必要ありません。
まとめ
この記事では、Mouse Studyインジケータとポジション表示インジケータを組み合わせ、適切にプログラミングされたエキスパートアドバイザーと連携させることで、ポジションを決済するための比較的シンプルで効果的な方法を紹介しました。 実装全体は非常に洗練された方法でおこなわれており、コードに大幅な変更を加える必要はありませんでした。 また、テイクプロフィットとストップロスのラインを解除する仕組みを実現するために、EAのコードに実装する必要があった部分もすべて紹介しました。
ただし、ポジション表示インジケータのコードには、さらに多くの変更を加える必要があります。そのため、変更後のコードはこの記事には掲載しません。 次の記事で詳しく説明し、解説する予定です。 そうすることで、このコードがどのように、そしてなぜ機能するのかを、皆さんに本当に理解していただきたいと考えています。しかし、それ以上に重要なのは、MetaTrader 5が取引中の情報をなぜ正しく解釈し、表示できるのかという点です。 MetaTrader 5を使えば、細かな部分について心配する必要がないことがお分かりいただけるでしょう。 実際に私たちがおこなう作業の大部分は、MetaTrader 5にメッセージを送信するよう指示することです。あとは、特別な手間や苦労をかけることなく、自然に処理されます。
MQL5の経験が豊富な方も、学び始めたばかりの方も、今回の目的が、このようなソリューションをどのように実装できるかを皆さんに伝えることにあると理解していただければ幸いです。 もし興味を持たれたのであれば、ぜひこの記事とコードを注意深く確認してください。次の記事では、ポジション表示インジケータに関連する部分を取り上げます。
| ファイル | 説明 |
|---|---|
| Experts\Expert Advisor.mq5 | Chart TradeとEAの連携を示します(操作にはMouse Studyが必要)。 |
| Indicators\Chart Trade.mq5 | 送信する注文を設定するためのウィンドウを作成します(操作にはMouse Studyが必要)。 |
| Indicators\Market Replay.mq5 | リプレイ/シミュレーションサービスを操作するためのコントロールを作成します(操作にはMouse Studyが必要)。 |
| Indicators\Mouse Study.mq5 | グラフィカルなコントロールとユーザーとの対話を実現します。市場のリプレイ/シミュレーションシステムと実際の市場の両方で必要です。 |
| Indicators\Order Indicator.mq5 | 成行注文の生成と、その操作および管理を担当します。 |
| Indicators\Position View.mq5 | 保有中のポジションの表示と、その操作および管理を担当します。 |
| Services\Market Replay.mq5 | 市場のリプレイ/シミュレーションサービスを作成し、管理する、システム全体の中核となるファイルです。 |
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/13206
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。