市場シミュレーション(第5回):C_Ordersクラスの作成(II)
はじめに
前回の「市場シミュレーション(第4回):C_Ordersクラスの作成(I)」では、主に成行注文を送信するためのコードの構造について説明しました。全体の説明は、Chart Tradeインジケーターから受け取った情報をどのようにデコードし、それを基にクラスのコードを構成できるかを示すことを目的としていました。
しかし、EAのソースコードを実際に見ていなくても、ある程度の経験を持つ方であれば、すでに実運用として実装できる内容だったと思います。また、前回の記事で示したコードは、実取引サーバー上では動作しないように見えるかもしれませんが、必ずしもそうではありません。ただし、ヘッジ口座で使用するには不十分である、という点は事実です。
ネッティング口座においては、そのコードだけでもポジションの新規建てや決済は可能です。しかし、ヘッジタイプの口座では、動作の仕組みが少し異なります。
たとえば、買いポジションを保有している状態で売りボタンを使って決済しようとした場合、ヘッジ口座では実際には新たな売りポジションが開かれてしまいます。一方、ネッティング口座で同じ操作をおこなった場合は、買いポジションが決済されるか、少なくとも以下のような何らかの効果が発生します。
- ポジションの反転(フリップ):既存の買いポジションよりも大きな数量で売った場合に発生します。この場合、ロングからショートへと転換され、新しいショートポジションのサイズは、元の買いポジションの量と売りの量との差分となります。
- 部分決済:売る量が既存の買いポジションより小さい場合に発生します。この場合、買いポジションの一部が決済され、残りの数量は引き続き市場に残ります。
これらの挙動は非常に興味深く、実は以前に私が執筆した別の連載記事ですでに詳しく解説しています。その連載では、自動売買をおこなうエキスパートアドバイザー(EA)をどのように構築するかを徹底的に説明しました。全15本の記事で構成されており、手動操作モデルから完全自動化システムに至るまで、EA開発に必要な各要素や注意点を網羅しています。興味のある方はぜひそちらをご参照ください。最初の記事(「自動で動くEAを作る(第01回):概念と構造」)はこちらです。
本記事で紹介するコードの多くは、その連載から流用されています。ただし、今回の焦点はやや異なります。主目的が違うからです。つまり、注文送信や実取引サーバーとの通信システムを構築することが主目的ではありません。私たちが本当にやりたいこと、そして実際におこなうことは、取引サーバーをシミュレートすることです。しかし、その前段階として、いくつかの機能を実装する必要があります。理想的には、リプレイ/シミュレーションシステムと実際の取引サーバー(デモ口座またはリアル口座)で共通のコンポーネントを使用できる構成にしたいと考えています。そのため、まずは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. } 158. } 159. //+------------------------------------------------------------------+ 160. }; 161. //+------------------------------------------------------------------+
C_Orders.mqhヘッダファイルのコード
さて、ここに示すコードは、Chart TradeインジケーターがEAに送信するすべてのメッセージに適切に対応できるようになっています。これにより、Chart Tradeインジケーター内のすべてのボタンや操作が機能するようになります。このコードは、いくつかの調整を加えれば、どのEAでも使用可能です。ここでの目的は、まさにそれを可能にすることです。つまり、Chart Tradeとマウスインジケーターを構成するモジュールを活用できるようにすることです。上記のコードを使用することで、ご自分のEAを、すでにコンパイル済みのChart Tradeとマウスインジケーターのモジュールと互換性を持たせることができます。言い換えれば、一度プログラムを作成して動作を確認したら、それ以降は触る必要はなく、次のステップに進むことができる、ということです。
ここでコードをまだ完全に理解できない初心者の方のために、今回の変更点を説明します。まず、構造上の変更は34行目にあります。この行で、クラスのprivate変数を1つの構造体にまとめました。しかし、前回の記事で説明した内容は、この変更によって無効になるわけではありません。
最初に疑問に思うのは、「Chart Tradeインジケーターでポジションを決済するボタンを押したとき、どのポジションが決済されるのか?」という点です。答えは、このクラス自体はどのポジションを決済すべきか知らない、ということです。ボタンには「CLOSE ALL POSITIONS」とラベルが付いているため、論理的にはすべてのポジションが決済されると考えてしまうでしょう。ですよね。違います。実際、このクラスはそのような動作はしていません。少なくとも、上で示したコードにいくつかの修正を加えない限りはそうです。
この時点で、混乱しているかもしれません。「CLOSE ALL POSITIONS」を押してもすべてのポジションが決済されないのに、なぜボタンにはそう書かれているのか。これを理解するためには、クラスのコードが実際にどのように動作するかを見ていく前に、少し時をさかのぼる必要があります。
以下が両建て注文システムに関する記事です。
以前の記事で、なぜ場合によっては特殊な種類の銘柄、いわゆる「履歴用銘柄」を使う必要があるのかを説明しました。その際、Chart TradeインジケーターがEAに対して、どの実際の銘柄を取引すべきかを確実に伝えられるようにする必要があることも説明しました。なぜなら、「履歴用銘柄」は取引可能な銘柄ではないため、それを使って取引リクエストを送信することはできないからです。こうした履歴用銘柄の多くは、ロット構造に起因して存在しています。この点についても、前述の2つの記事で説明しています。しかし、ここでの問題はさらに複雑です。たとえば、履歴用銘柄のチャートを使用しており、EAにフルロットからミニロットへいつでも切り替えるように指示した場合、EAと、さらに重要なことに、Chart Tradeインジケーターの両方が、取引銘柄を変更することになります。
ここで本当の問題が出てきます。仮に、ドルの履歴用チャートを使用していて、EAにドルをフルロットで操作させていたとします。その後、気が変わり、EAにミニロットに切り替えるよう指示します。EAは、ロット種別の変更をChart Tradeインジケーターに通知するメッセージを送ります。ここまでは問題ありません。しかし、フルロットでポジションを持っていた場合、ここで問題が発生します。フルロットのポジションがある状態でEAにミニロットへ切り替えるよう指示すると、EAは単純にその指示に従います。そしてChart Tradeインジケーターもそれに応じて更新されます。ここで問題が生じます。これは、あなたがコードを自分のニーズに合わせて適応させるために理解しておくべき重要なポイントです。
もし、この時点で Chart Tradeインジケーターが取引対象の銘柄をミニロットとして表示している場合、「すべてのポジションを決済」要求を送ったらどうなるでしょうか?。ポジションはフルロットです。Chart Tradeインジケーターは確かにEAにメッセージを送信します。このメッセージには、EAが何をすべきかを判断するために必要なすべての情報が含まれています(前回の記事で説明した通りです)。しかし、EAは実際にはどう処理すべきかを知りません。これは、取引不可の履歴用チャート上で動作しているためです。この問題に対応するために、メッセージはコードの153行目でインターセプトされます。この時点で、メッセージはすでに正しく変換されています。その後、コードの実行は120行目に進み、ここで本格的な処理が開始されます。
122行目にはループがあります。このループでは、すべてのポジションを読み取ります。例外はありません。では、なぜドルの例とは関係ないポジションまで読み取るのでしょうか?それは、各ポジションを建てたときにサーバーから提供されたチケット番号を保持していないからです。そのため、必要に応じて再度チケットを検索する必要があります。
では、なぜこれらのチケットを保持しないのでしょうか。当然ながら保持すれば処理は簡単になります。確かにそうです。しかし、このチケット情報をEA自身の中で保持することはできません。チャート上でEAに関わる変更があるたびに、そのチケット情報は失われてしまうからです。したがって、必要に応じて毎回再取得する必要があります。これは、チャートの時間軸やロットタイプを変更するたびに発生します。
現時点では、この方法で進めることにします。しかし、このやり方では単純にポジションを決済する処理を呼ぶことはできません。なぜなら、Chart Tradeメッセージに関係のないポジションも含めて、すべてのポジションをループで読み取っているからです。
したがって、フィルター処理を適用する必要があります。最初のフィルターは124行目にあります。ここでは、ポジションの銘柄名を確認し、Chart Tradeインジケーターから受け取った名前と比較します。注意してください。例として、フルロットのポジションがあるとします。しかし、Chart Tradeからのメッセージはミニロットを示しています。この場合、銘柄名が一致しないため、このテストは失敗します。逆の場合、つまりミニロットのポジションがあり、Chart Tradeでフルロットのポジションを決済しようとした場合も同様です。このテストにより、こうした混乱を防ぐことができます。
重要なポイントです。このテストは処理を複雑にしているように見えるかもしれませんが、絶対に必要です。たとえ両建て注文システムを通じて履歴用銘柄を使ってミニロットとフルロットの両方を取引している場合でも、124行目のテストを削除してはいけません。ドルに関連する任意のポジションを決済したい場合でも、このテストは残す必要があります。なぜなら、これを削除すると、Chart Tradeを使用したときに、全ポジションをスキャンする過程で、まったく別の銘柄のポジションまで決済されてしまう可能性があるからです。
124行目のテストが成功した場合、新しいチケット番号を取得します。その後、別のフィルター処理に進み、別の条件を隔離・判定します。前回の記事で説明した通り、このクラスはマジックナンバーで識別される独自のエンティティを表しています。これにより、1つのEAの中に、複数の独立した取引戦略を共存させることができ、それぞれが異なるマジックナンバーで区別されます。
ただし、ネッティング口座の場合、この仕組みは意味をなしません。なぜなら、ネッティング口座では取引サーバーがポジション価格を平均化するため、銘柄ごとに建てることができるポジションは1つだけだからです。
一方、ヘッジ口座では状況がまったく異なります。同じ銘柄に対して、買いと売りのポジションを同時に複数持つことが可能です。このような場合、このクラスは同じEA内の他のクラスと区別されることが必須です。詳細な実装方法についてはここでは触れませんが、重要なのは、このクラスでは可能でありサポートされているということです。
さらに、EAが自動でポジションを建てる場合(このとき、どのクラスが作成したかを示すマジックナンバーで識別されます)以外に、もう1つの可能性があります。それは、トレーダーが手動でポジションを建てた場合です。この場合、注意が必要です。なぜなら、トレーダーはChart Tradeを使っても、自分で開いた手動ポジションを決済されたくないかもしれないからです。この場合、EAはポジションのマジックナンバーとクラスのマジックナンバーを比較します。もし一致しなければ、そのポジションは無視され、建てられたままになります。つまり、トレーダーが手動で開いたポジションは、EAによって触られず、手動で決済する必要があります。
これらの動作がどのように展開されるかがわかります。これらの挙動は変更可能ですが、システム全体は主にヘッジ口座向けに設計されており、スコープ外の動作に干渉しないようになっています。したがって、両方のテストが通過した場合、126行目が実行され、ポジションのチケットを取得し、指定されたポジションを決済する手続きを呼び出します。この手続きは12行目に定義されています。注意すべき点は、これはprotected節内にあるため、継承を通じてアクセスは可能ですが、継承システムの外部から直接呼び出すことはできません。要するに、EAのコードや他の外部コードから直接呼び出すことはできません。
さて、このコードにはいくつか特異な点があり、将来的には削除するかもしれません。状況は次の通りです。12行目のコードが呼び出された時点では、すでにいくつかの情報を把握しています。たとえば、銘柄名やチケット番号です。しかし、17行目で再度、そのチケットがポジションに属しているかを確認しています。なぜこの確認を繰り返すのでしょうか。理由は、この時点では、将来的に開発予定の別のツールがこの手続きとどのように通信するかがまだ確定していないということです。それでも、サーバーから必要な情報を取得するために、PositionSelectByTicketライブラリ関数を使ってデータを更新する必要があります。ただし、この更新はすでに124行目のPositionGetSymbol呼び出しでおこなわれています。言い換えれば、ここには若干の重複があります。しかし、現時点では問題ありません。
いずれにしても、情報を更新する必要があります。データが更新され、チケットが有効なポジションとして確認されたら、18行目でそのポジションが買いか売りかを確認します。これは重要です。次に、19行目でポジションの銘柄名を取得します。これは、将来的に新しいツールが実装された場合、変更される可能性があります。なぜなら、このクラスだけを見れば、これらのステップは冗長であり、120行目の手続きで直接情報を渡すことも可能だからです。繰り返しになりますが、ClosePositionが将来的にどのように動作するかはまだわかりません。そのため、次に進むのは、サーバーに送信する構造体の内容を設定するステップです。
前回の記事で説明したToMarket手続きでは、「各フィールドの説明は後でおこなう」と述べましたが、ここでは主要なフィールドについて説明できます。これらを理解することは後々重要になります。21行目では、サーバーにどの種類のアクションを実行するかを伝えます。ここで、「ポジションを決済しようとしているのだから、なぜTRADE_ACTION_CLOSE_BYを使わないのか?」と疑問に思う人もいるでしょう。これはもっともな質問です。その主な理由はネッティング口座にあります。理解するために、26行目を見てください。ここでは取引量(ボリューム)を指定しています。TRADE_ACTION_CLOSE_BYを使った場合、サーバーは単純にポジションを完全に決済してしまいます。
しかし、それではネッティング口座で可能な部分決済のための追加ロジックやコードを作る必要が出てきます。部分決済では、ポジションの一部だけを決済します。現時点では、部分決済や反転のロジックは実装していません。これは設計上の選択であり、どの機能を実装するかによって異なります。このシステムはネッティング口座でもヘッジ口座でも使えるため、優先すべき機能を決める必要があります。ネッティング口座とヘッジ口座では、それぞれ独自の機能があり、一部は相互に排他的です。私は両方に対応できるようにしています。これが、この説明が重要な理由です。
その他のフィールドはすべて、サーバーに関する詳細を指定しています。例えば、売買の方向(22行目)、取引をおこなう価格(23行目)、ポジションのチケット番号(24行目)、銘柄名(25行目)。ここで注意すべきは、チケット上の名前と一致していなければエラーになることです。取引量(26行目)、そして最後に許容価格スリッページを示す偏差(27行目)です。これらすべての情報によって、サーバーはマーケットリクエストを実行します。つまり、最良の利用可能価格で取引をおこなったり、取引量や方向を変更したり、必要に応じてポジションを決済したりするわけです。
注目すべき点は、TRADE_ACTION_CLOSE_BYのようにポジションを単に決済するだけの操作とは異なり、この方法でははるかに柔軟性が高いということです。私がTRADE_ACTION_DEALを使用しているのはこのためです。ただし、これはすでにポジションが建てられている場合にのみ適用されます。また、ストップロスやテイクプロフィットのレベルを変更することはできません。これらは後ほど説明する別のアクションで扱います。現時点では、MetaTrader 5が提供するシステムを引き続き使用します。つまり、技術的には履歴用の銘柄を使って取引をおこなうことも可能ですが、そのチャート上には価格ラインは表示されません。価格ラインは、実際に取引がおこなわれた銘柄のチャート上に表示されます。これも将来的には変更される予定です。
では、最後に考慮すべきものはEAのコードです。以下がその様子です。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property icon "/Images/Market Replay/Icons/Replay - EA.ico" 04. #property description "Demo version between interaction" 05. #property description "of Chart Trade and Expert Advisor" 06. #property version "1.84" 07. #property link "https://www.mql5.com/pt/articles/12598" 08. //+------------------------------------------------------------------+ 09. #include <Market Replay\Order System\C_Orders.mqh> 10. //+------------------------------------------------------------------+ 11. enum eTypeContract {MINI, FULL}; 12. //+------------------------------------------------------------------+ 13. input eTypeContract user00 = MINI; //Cross order in contract 14. //+------------------------------------------------------------------+ 15. C_Orders *Orders; 16. long GL_ID; 17. //+------------------------------------------------------------------+ 18. int OnInit() 19. { 20. GL_ID = 0; 21. Orders = new C_Orders(0xC0DEDAFE78514269); 22. 23. return INIT_SUCCEEDED; 24. } 25. //+------------------------------------------------------------------+ 26. void OnTick() {} 27. //+------------------------------------------------------------------+ 28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 29. { 30. (*Orders).DispatchMessage(id, lparam, dparam, sparam); 31. switch (id) 32. { 33. case CHARTEVENT_CHART_CHANGE: 34. if (GL_ID > 0) break; else GL_ID = ChartID(); 35. case CHARTEVENT_CUSTOM + evChartTrade_At_EA: 36. EventChartCustom(GL_ID, evEA_At_ChartTrade, user00, 0, ""); 37. break; 38. } 39. } 40. //+------------------------------------------------------------------+ 41. void OnDeinit(const int reason) 42. { 43. switch (reason) 44. { 45. case REASON_REMOVE: 46. case REASON_INITFAILED: 47. EventChartCustom(GL_ID, evEA_At_ChartTrade, -1, 0, ""); 48. break; 49. } 50. 51. delete Orders; 52. } 53. //+------------------------------------------------------------------+
EAのソースコード
このコードは、「市場シミュレーション(第02回):両建て注文(II) 」ですでにほぼ全て提示されており、今回の変更はC_Ordersクラスを利用するためのほんの少しの変更だけです。これらの変更は非常に単純です。しかし、C言語でのコーディング経験があまりない場合、いくつかの点が少し奇妙に見えるかもしれません。しかし、ここに神秘的なことは何もなく、単に少し異なるだけです。最初の変更は9行目に現れます。ここで、この記事で紹介したヘッダファイルをインクルードしています。もう一つの変更は15行目で、クラスをポインタとして宣言している点です。
21行目では、NEW演算子を使ってクラスを初期化しています。ここで少し珍しい点に気づくかもしれません。このクラスのコンストラクタは64ビットのlong整数を受け取るように宣言されていますが、渡されている値が少し奇妙に見えます。 0xC0DEDAFE78514269には文字と数字の両方が含まれています。これは一体何でしょうか。実は、全く奇妙なことではありません。これは16進数の値です。先頭の0xは、続く値が16進数であることをコンパイラに伝えています。この表記は多くの場面で非常に便利です。しかし、後でこの値をポジションのマジックナンバーとして見ると、まったく異なるものに見えるでしょう。この16進数値を10進数で表したものです。その10進数の値が何かは、ここでは明かさず、ぜひ自分で確かめてみてください。
最後の変更は30行目にあります。この行では、クラスがその機能を果たすために必要なデータを渡しています。これにより、EAはChart Tradeインジケーターから送信されるメッセージを処理できるようになります。ごく少しのコードでありながら、すでに非常に注目すべきことが実現されています。それは、システム全体がモジュール化されているためです。このモジュール設計により、あるモジュールの改善が他のモジュールに直接影響を与えることはなく、安全かつ効率的にコードを拡張できます。これは、すべてが一つの巨大なファイルに含まれるモノリシックなシステムとは対照的であり、開発や保守がはるかに大変で、エラーも発生しやすくなります。
最後に
これまでの2つの記事では、成り行き注文を送信するクラスコードと共にEAを紹介しました。このシステムは、ネッティングでもヘッジでも問題なく動作でき、同じEAをForex、株式、OTC市場で利用することが可能です。これにより、全体のプロセスがはるかに柔軟で扱いやすくなります。
しかし、このEAはまだ完成していません。本来の目的を果たすには、追加すべき機能がまだ多く残っています。その目的とは、リプレイ/シミュレーションで使用するための取引サーバーのシミュレーションです。
ただし、それをおこなう前に、いくつかの他のコンポーネントを構築する必要があります。ですので、次の記事をお見逃しなく。そこでは、このEAがまだ完成していなくても、すでに実行可能なことについて説明を始めます。今から話し始めることで、説明を簡単にしようという意図です。後回しにすると、説明が非常に複雑になり、読者の皆さんが関連する詳細を理解するのが難しくなるかもしれません。
| ファイル | 説明 |
|---|---|
| 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 | グラフィカルコントロールとユーザー間のインタラクションを実現する(リプレイ/シミュレーターおよび実取引の両方で必須) |
| Servicios\Market Replay.mq5 | マーケットリプレイおよびシミュレーションサービスを生成し、維持する(システム全体のメインファイル) |
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/12598
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
初級から中級まで:テンプレートとtypename(V)
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
取引におけるニューラルネットワーク:暗号通貨市場向けメモリ拡張コンテキスト認識学習(最終回)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索