English Русский 中文 Español Deutsch Português
preview
リプレイシステムの開発(第40回):第2段階の開始(I)

リプレイシステムの開発(第40回):第2段階の開始(I)

MetaTrader 5 | 2 8月 2024, 10:59
33 0
Daniel Jose
Daniel Jose

はじめに

前回の「リプレイシステムの開発(第39回):道を切り開く(III)」稿では、特定のアクションを可能にするために、プロセス間のコミュニケーションをどのように組織化できるかを検討しました。現時点ではEAと指標を使用していますが、必要に応じてこれらのツールを拡張していくことができます。

このタイプの通信の主な利点は、システムをモジュールで構築できることです。私たちに何ができるのか、まだ理解されていないかもしれません。最終的には、グローバルターミナル変数を使用する場合よりも、より「安全な」チャンネルでプロセス間の情報交換ができるようになるでしょう。

これを実装し、リプレイ/シミュレーターシステムをモジュラーシステムに統合する方法を示すために、一歩下がって一歩進んでみましょう。今回は、EAマウスを使った学習システムを削除します。同じシステムを指標に変換し、次に実施するステップと互換性を持たせます。

成功すれば、この後、もっと多くのことができることが明らかになるでしょう。後で他の作業が必要になるため、指標がここで終了しない可能性は高いですが、最終的には完了します。大きな利点は、メインシステムの残りの部分に影響を与えることなく、モジュールの更新や修正ができることです。


コーディングの開始

私たちのコードは、現時点で私たちがすでに持っているものから作られます。EAコードが指標になるように、最小限の変更を加えます。

最初にすべきことは、InterProcess.mqhヘッダーファイルのコードを確認することです。全コードは以下で見ることができます。

InterProcess.mqhファイルのコード

01. #property copyright "Daniel Jose"
02. //+------------------------------------------------------------------+
03. #define def_SymbolReplay                    "RePlay"
04. #define def_GlobalVariableReplay            def_SymbolReplay + "_Infos"
05. #define def_GlobalVariableIdGraphics        def_SymbolReplay + "_ID"
06. #define def_GlobalVariableServerTime        def_SymbolReplay + "_Time"
07. #define def_MaxPosSlider                    400
08. //+------------------------------------------------------------------+
09. union u_Interprocess
10. {
11.     union u_0
12.     {
13.             double  df_Value;  // Value of the terminal global variable...
14.             ulong   IdGraphic; // Contains the Graph ID of the asset...
15.     }u_Value;
16.     struct st_0
17.     {
18.             bool    isPlay;     // Indicates whether we are in Play or Pause mode...
19.             bool    isWait;     // Tells the user to wait...
20.             bool    isHedging;  // If true we are in a Hedging account, if false the account is Netting...
21.             bool    isSync;     // If true indicates that the service is synchronized...
22.             ushort  iPosShift;  // Value between 0 and 400...
23.     }s_Infos;
24.     datetime        ServerTime;
25. };
26. //+------------------------------------------------------------------+
27. union uCast_Double
28. {
29.     double   dValue;
30.     long     _long;                    // 1 Information
31.     datetime _datetime;                // 1 Information
32.     int      _int[sizeof(double)];     // 2 Informations
33.     char     _char[sizeof(double)];    // 8 Informations
34. };
35. //+------------------------------------------------------------------+

このコードで本当に興味があるのは、27行目と34行目の間です。ここでは、プロセス間の情報伝達を可能にするリファレンスがあります。このコネクションには、私たちが最初から必要とし、使用することになるいくつかの種類のデータがあります。このアイデアは、前回の記事ですでに説明したデータ転送を、実用的で的を絞った方法で促進することです。

これらの行がInterProcess.mqhファイルに追加されました。続けて、今度はコードに別の変更を加えてみましょう。これらはクラスファイルC_Study.mqhに実装しなければなりません。

まず、クラスの最初にいくつかの点を変更します。以下でご覧ください。

class C_Study : public C_Mouse
{
        protected:
                enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
        private :
                enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};

消された線は、コードのprivate部分から削除され、現在はprotected部分にあります。private部分は、その内容がクラスの外部からアクセスできないことを示しますが、列挙の場合はそうではありません。定義部分に関わらず、常にpublicとして扱われます。

以下は、実際に変更がおこなわれた部分です。

//+------------------------------------------------------------------+
void Update(const eStatusMarket arg)
void Update(void)
{
   datetime dt;
                                
   switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status))
   switch (m_Info.Status)
   {
      case eCloseMarket : m_Info.szInfo = "Closed Market";
         break;
      case eInReplay    :
      case eInTrading   :
         if ((dt = GetBarTime()) < ULONG_MAX)
         {
            m_Info.szInfo = TimeToString(dt, TIME_SECONDS);
            break;
         }
      case eAuction     : m_Info.szInfo = "Auction";
         break;
      default           : m_Info.szInfo = "ERROR";
   }
   Draw();
}
//+------------------------------------------------------------------+
void Update(const MqlBookInfo &book[])
{
   m_Info.Status = (ArraySize(book) == 0 ? eCloseMarket : (def_InfoTerminal.szSymbol == def_SymbolReplay ? eInReplay : eInTrading));
   for (int c0 = 0; (c0 < ArraySize(book)) && (m_Info.Status != eAuction); c0++)
      if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Info.Status = eAuction;
   this.Update();
}
//+------------------------------------------------------------------+

消された線はすべてクラスから取り除かれ、新しい強調された線に置き換えられました。

なぜこうなるのでしょうか。このことを理解するためには、EAのコードを指標に変えることで、アクションの方法が変わることを理解する必要があります。

問題のコードは、勉強道具としてのマウスの使用に関するものです。

EAがすべてのアクションを実行するようにコードを書くと、私たちはある特定の方法で行動することができます。しかし、同じことを指標でおこなう場合、私たちは異なる方法に適応しなければなりません。これは、指標がスレッド内に存在し、何らかの影響を受けると、チャートに存在する他のすべての指標に影響を及ぼすからです。

しかし、ある時点で指標がブロックされるのは避けたいです。そのため、少し違ったアプローチをしなければなりません。

下の指標のコードを見てみましょう。これがフルコードです。

指標のソースコード

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.40"
007. #property icon "/Images/Market Replay/Icons/Indicators.ico"
008. #property link "https://www.mql5.com/ja/articles/11624"
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. #include <Market Replay\Auxiliar\InterProcess.mqh>
015. //+------------------------------------------------------------------+
016. C_Terminal *Terminal  = NULL;
017. C_Study    *Study     = NULL;
018. //+------------------------------------------------------------------+
019. input C_Study::eStatusMarket user00 = C_Study::eAuction;   //Market Status
020. input color user01 = clrBlack;                             //Price Line
021. input color user02 = clrPaleGreen;                         //Positive Study
022. input color user03 = clrLightCoral;                        //Negative Study
023. //+------------------------------------------------------------------+
024. C_Study::eStatusMarket m_Status;
025. int m_posBuff = 0;
026. double m_Buff[];
027. //+------------------------------------------------------------------+
028. int OnInit()
029. {
030.    if (!CheckPass("Indicator Mouse Study")) return INIT_FAILED;
031.            
032.    Terminal = new C_Terminal();
033.    Study = new C_Study(Terminal, user01, user02, user03);
034.    if ((*Terminal).GetInfoTerminal().szSymbol != def_SymbolReplay)
035.    {
036.            MarketBookAdd((*Terminal).GetInfoTerminal().szSymbol);
037.            OnBookEvent((*Terminal).GetInfoTerminal().szSymbol);
038.            m_Status = C_Study::eCloseMarket;
039.    }else
040.            m_Status = user00;
041.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
042.    ArrayInitialize(m_Buff, EMPTY_VALUE);
043.    
044.    return INIT_SUCCEEDED;
045. }
046. //+------------------------------------------------------------------+
047. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
048. {
049.    m_posBuff = rates_total - 4;
050.    (*Study).Update(m_Status);      
051.    
052.    return rates_total;
053. }
054. //+------------------------------------------------------------------+
055. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
056. {
057.    (*Study).DispatchMessage(id, lparam, dparam, sparam);
058.    SetBuffer();
059.    
060.    ChartRedraw();
061. }
062. //+------------------------------------------------------------------+
063. void OnBookEvent(const string &symbol)
064. {
065.    MqlBookInfo book[];
066.    C_Study::eStatusMarket loc = m_Status;
067.    
068.    if (symbol != (*Terminal).GetInfoTerminal().szSymbol) return;
069.    MarketBookGet((*Terminal).GetInfoTerminal().szSymbol, book);
070.    m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : C_Study::eInTrading);
071.    for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
072.            if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
073.    if (loc != m_Status) (*Study).Update(m_Status);
074. }
075. //+------------------------------------------------------------------+
076. void OnDeinit(const int reason)
077. {
078.    if (reason != REASON_INITFAILED)
079.    {
080.            if ((*Terminal).GetInfoTerminal().szSymbol != def_SymbolReplay)
081.                    MarketBookRelease((*Terminal).GetInfoTerminal().szSymbol);
082.            delete Study;
083.            delete Terminal;
084.    }
085. }
086. //+------------------------------------------------------------------+
087. bool CheckPass(const string szShortName)
088. {
089.    IndicatorSetString(INDICATOR_SHORTNAME, szShortName + "_TMP");
090.    if (ChartWindowFind(ChartID(), szShortName) != -1)
091.    {
092.            ChartIndicatorDelete(ChartID(), 0, szShortName + "_TMP");
093.            Print("Only one instance is allowed...");
094.            
095.            return false;
096.    }
097.    IndicatorSetString(INDICATOR_SHORTNAME, szShortName);
098.    
099.    return true;
100. }
101. //+------------------------------------------------------------------+
102. inline void SetBuffer(void)
103. {
104.    uCast_Double Info;
105.    
106.    m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff);
107.    m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
108.    Info._datetime = (*Study).GetInfoMouse().Position.dt;
109.    m_Buff[m_posBuff + 1] = Info.dValue;
110.    Info._int[0] = (*Study).GetInfoMouse().Position.X;
111.    Info._int[1] = (*Study).GetInfoMouse().Position.Y;
112.    m_Buff[m_posBuff + 2] = Info.dValue;
113.    Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
114.    m_Buff[m_posBuff + 3] = Info.dValue;
115. }
116. //+------------------------------------------------------------------+

指標のソースコードを見て、「リプレイシステムの開発(第39回):道を切り開く(III)」稿で提供されているコードと比較してみてください。似たような部分がたくさん見られます。

このリプレイ/シミュレーターシステムに関する連載を見てきたのであれば、言及されているコードのほとんどを理解することに問題はないはずです。しかし、コードを見ただけでは何が起こっているのか分からない人も多いと思うので、もう少し詳しく説明する価値のあるものもいくつかあります。というのも、これらのソースコードやその他のソースコードを正しく理解することは、システムがどのように実装されているかを本当に理解したい人にとって非常に重要だからです。

2行目と8行目の間には、時間とともに変化する情報があります。この情報は、コードが作成、修正、投稿された記事にリンクしています。この情報は、経験の浅いユーザーのために提供されています。

9行目と11行目では、指標がどのように投影されるかをコンパイラに正確に伝えています。すべての指標コードには、少なくとも9行目と10行目が必要です。すべてです。11行目では、バッファを使用すること、そしてCopyBuffer関数を通して何かを読み取りたいプロセスは、指標の内部バッファにアクセスすることでそれが可能であることを伝えています。

13行目と14行目はインクルードで、コンパイラに使用するヘッダーファイルを指示します。これらのファイルにはすでにいくつかの変更を加えているが、他にも変更が必要です。今のところ、提供されたヘッダーファイルは問題なく使用できます。

16行目と17行目では、2つの内部グローバル指標変数を宣言しています。これらのポインタは、クラスへのアクセスをコントロールするのに役立ちます。通常、グローバル変数はデフォルトでは初期化されません。しかし、ここではっきりさせておきたいのは、これらの変数(ポインタ)は特定のメモリロケーションを指すわけではないが、もし明示的に宣言した場合、コンパイラはデフォルトで暗黙的にそうすることを覚えておかなければならないということです。

19行目と22行目の間が外部設定変数です。これからは、このような変数を、単にユーザーが指標にアクセスしたり、指標を設定したりするための方法と見なすのはやめることをお勧めします。このような考え方では、視野が非常に狭くなり、その助けを借りて成し遂げられるすべてのことを想像することはできないでしょう。

どんなプログラムも、純粋で単純なものだと考えるべきではありません。プログラムは情報を受け取り、それをある方法で処理し、何らかの出力を生成するものなので、常に関数として考えるべきです。プログラミングで同じ動作をする要素は何でしょうか。関数です。では、もっと広い視点から物事を見てみましょう。可能性を想像したり夢見たりするのはやめて、物事の本質を見極めましょう。プログラムは関数です。

19行目には、ユーザーが指標をチャート上に配置する際に意味を持たないパラメータがあります。このパラメータは、ユーザーが設定することを意図したものではなく、他のプログラムが、私たちが作成したこの指標を使用できるようにするために存在します。これはどのように可能なのでしょうか。わかりにくいでしょうか🤔。今は心配しなくて大丈夫です。この記事は、私たちのリプレイ/シミュレーターシステムを構築する第2段階の最初の記事です。以下の記事では、なぜ19行目が存在するのかを理解するのに役立ついくつかの事柄を紹介します。慌てないでください。

一方、20~22行目は、チャート上でマウスが使用する色を調整するために使用される、実際に意味のあるパラメータです。

なぜ24行目が必要なのでしょうか。なぜ19行目と同じ型のグローバル変数を持っているのでしょうか。というのも、19行目のパラメータは実際には変数ではなく、定数とみなされているからです。したがって、指標の実行中に使用され、設定される変数を宣言する必要があります。これが24行目の宣言の理由です。

25行目で宣言された変数は、一種のメモリとして機能しますが、通常のメモリよりも崇高な目的を持ちます。この部分についてはまた後ほど。26行目では、指標の出力となるバッファを宣言しています。注意:バッファを宣言しましたが、まだ機能していません。この中で何かをしようとするとランタイムエラーの警告が表示され、読もうとすると読めなくなります。

ここからは、MetaTrader 5と指標の相互作用について説明します。指標は、コードがどのように構成され、どのような作業をおこなうことになっているかに関係なく、MetaTrader 5が報告するイベントに反応することを覚えておいてください。常に与えられた出来事に反応します。これらの中にはインタラクションイベントもあれば、アクティビティイベントもあります。

Initイベントが発生すると、OnInit関数が呼び出されます。この関数は28行目から始まり、パラメータを取りませんが、19行目から22行目の間に宣言された相互作用パラメータをOnInit関数のパラメータとして使用することができます。

OnInit関数がどのように機能するか見てみましょう。まずはチェックインです。これは、関数を呼び出している30行目で起こります。その後、実行スレッドは87行目に転送されます。このチェックインが何なのか、なぜOnInit関数が機能しないと指標の初期化エラーが返されるのかを理解するために解析してみましょう。

87行目の関数は1つの引数、つまり指標の短い名前を取ります。この名前は、特定の動作条件を確保するために必ず定義されなければなりません。最初におこなうのは(これは89行目でおこなわれる)、指標に一時的な名前をつけることです。指標が仮の名前を受け取り次第、このチェックイン段階を続けることができます。次にすることは、チャート上で指標を検索することです。これは90行目でおこなわれます。指標がすでにチャート上に存在する場合、MetaTrader 5はその場所を正確に表示します。そうでない場合は、-1が返されます。

したがって、90行目の確認は、チャート上に指標があるかどうかを教えてくれます。指標が存在する場合、92行目が実行され、一時的な指標、つまりチャート上に配置しようとしている指標が削除されます。次に、このことを警告するメッセージを表示し、95行目で呼び出し元に戻り、登録に失敗したことを通知します。

90行目のテストで指標がないことが報告されたら、97行目を実行し、MetaTrader 5に指標の実名を伝えます。99行目で呼び出し元に戻り、指標をチャートに正常に配置できたことを知らせます。しかし、これはチャート上に実際に配置されたことを意味するものではありません。これは、MetaTrader 5がチャート上で見つけられなかったという情報のみです。これで開始できます。

これで30行目に戻り、実際に指標を立ち上げて動作させ始めます。成功したら32行目に進みます。失敗した場合、MetaTrader 5は76行目の関数を呼び出すイベントをトリガーします。しかし、30行目で失敗を知らせているため、78行目のコードが実行されるとすぐに、80行目から83行目までのコードが実行できなくなります。これは、指標出力での失敗を避けるために重要です。

32行目に戻って、指標を実際に使い始めてみましょう。32行目で、C_Terminalクラスにアクセスするためのポインタを開始します。ここから、C_Terminalクラスの関数を使用することができるようになります。この直後の33行目では、C_Studyクラスにアクセスするためのポインタを実行しています。このクラスでは、マウスを使ってグラフィカルな分析をおこないます。C_Terminalクラスにアクセスする必要があるので、以下の一連の動作を実行する必要があります。

34行目には、リプレイ/シミュレーターの資産を使っているのか、それとも別の資産を使っているのかを判断する新しいテストがあります。これがリプレイ/シミュレータ資産である場合、19行目で宣言した指標入力設定パラメータの内容をグローバル変数に入れる必要があります。これは40行目でおこなわれます。

34行目が別の種類の資産に取り組んでいることを示す場合、36行目から38行目が実行されます。これらの行は、オーダーブックの使用を初期化し、オーダーブックで発生したイベントを受信したいことをMetaTrader 5に伝えます。さらに、38行目は市場がクローズしていることを示しています。この条件は一時的なものであり、今後、オーダーブックから正しい情報を得るためのガイダンスを受けることになります。

さて、指標にとって非常に重要な41行目です。11行目と26行目でバッファを宣言しても、そのバッファにアクセスできる保証はなく、アクセスしようとすると実行時エラーが発生することを覚えておいてください。41行目がなければ、指標はほとんど役に立ちません。この記事では、インデックスと行列、そしてそれらの利点について話しました。通常、インデックスフィールドにはゼロの値から始めます。配列フィールドでは、どの変数をバッファとして使用するかを示します。バッファが何に使われるかを示す3番目のフィールドについては、ENUM_INDEXBUFFER_TYPE列挙値のいずれかを使用することができます。データを保存するため、INDICATOR_DATAを使用しますが、この特定のコードでは、他の型を使用することも可能です。

これが終わったら、42行目に進みます。この時点では不要に思えるかもしれませんが、バッファをきれいにしておきたいのです。まさに今、私たちがやっていることです。ここに何か他のものを加えることもできますが、その必要はないと思います。次回の記事で紹介するように、マウスデータのキャッシュを作りたいと思います。

以上、指標の初期化について説明しました。MetaTrader 5によってトリガーされたイベントに反応する方法を知る必要があります。この部分を説明するために、まずOnBookEvent関数内のオーダーブックイベントコードを見てみましょう。この関数は63行目から始まり、オーダーブックで何かが起こるたびにMetaTrader 5によって呼び出されます。

よく見ると、70行目と72行目の間にあるその内容は、C_Studyクラスの内容とほとんど変わりません。なぜこの手続きをC_Studyクラスに残さなかったのでしょうか。その理由を説明するのは難しいですが、後で明らかになります。この部分は、もちろん連載を追って勉強した人なら、すでにおなじみでしょう。この関数には追加のコードが含まれています。

66行目でローカルな一時変数を宣言し、70行目や72行目で変更される可能性のある市場の状態を一時的に保存します。この場合、73行目を呼び出して、チャート情報を都合よく更新する必要があります。チャートの更新部分を見る前に、他の行を簡単に見てみましょう。

68行目では、BOOKイベントを、チャート上の資産に実際に含まれるものだけにフィルタリングしています。MetaTrader 5はそのような区別をしないため、気配値表示ウィンドウ内のどの資産がオーダーブックイベントをトリガーしても、MetaTrader 5はオーダーブックイベントをトリガーします。だから、すぐに区別しなければなりません。

この後、69行目でオーダーブックに含まれるデータを記録し、必要に応じて分析できるようにします。次に70行目で、市場がクローズ(またはオープン)であるかどうかを、現物市場、つまり取引サーバーが許可された取引ウィンドウ内にあるかどうかを参照することで、指標に伝えます。これを調べるには、オーダーブックの配列が空かどうかを確認します。

さて、71行目でループに入り、配列の一点一点を確認し、資産が競売中であることを示すポジションがあるかどうかを確認します。72行目の2つの条件のいずれかがtrueの場合、資産はオークション状態にあります。BIDとASKがオーダーブックのどこにあるか正確にはわからないので、71行目でループを使用します。しかし、使用する資産のBIDとASKの位置が固定されている場合は、71行目を削除し、BIDとASKが配置されている配列のポイントを指定するだけで、ループをスキップすることができます。これらのうち、資産が競売中であることを示すものがあれば、状態が更新されます。

アップデートの部分に戻りましょう。この情報は常に送信されるわけではありません。66行目に格納された値が新しい状態と異なる場合にのみ、市場の状態更新を呼び出します。

73行目でこれをおこなう理由は、取引期間中に資産がオークションに参加したり、サスペンド状態になった場合、オーダーブックイベントだけがこの情報を持ち、状態変化を正しく見ることができるからです。他のことが起こるのを待っていたら、何が起こっているのかわからない状況に陥るかもしれません。

状態が更新される2つ目のポイントは、まさにこれから見ていくイベントであるOnCalculate関数です。

価格が更新されたり、チャートに新しいバーが現れるたびに、MetaTrader 5はイベントをトリガーします。このイベントは、指標コードのOnCalculate関数を呼び出します。もちろん、私たちはこのコードをできるだけ早く実行し、興味のあるイベントを見逃さないようにしたいのです。状態の変化が発生するとすぐに報告するOrder Bookイベントとは異なり、OnCalculateイベントは、何かが価格に影響を与える場合にのみ有効になります。

しかし、49行目では、そこで使われていない値を保存し、調整していることがわかります。関数呼び出しがあれば、与えられた値を使用する必要がありますが、なぜこうなるのでしょうか🤔。繰り返しますが、OnCalculate関数はできるだけ早く実行する必要があり、価格が動くたびに、あるいは新しいバーが現れるたびにバッファを更新する意味はありません。チャート上の価格やバーを操作しているのではなく、マウスを操作していることをお忘れなく。

したがって、バッファの更新に実際に関与しているのは、MetaTrader 5によってトリガーされる次のイベント、すなわちOnChartEvent関数です。

55行目から始まるこのOnChartEvent関数は、非常にシンプルでわかりやすいです。57行目の関数には、学習クラスがイベントを正しく処理するための呼び出しが含まれています。これにより、マウスイベントを完全に処理するために必要な内部作業がおこなわれます。詳しくは、 「リプレイシステムの開発(第31回):エキスパートアドバイザープロジェクト - C_Mouseクラス(V)」稿をご覧ください。これにかかわらず、プロシージャーを呼び出し、実行スレッドは102行目で停止します。

102行目に進む前に、初期化処理の説明の際に前述した76行目のイベントを見てみましょう。では、指標が正しく初期化されたときに、同じコードがどのように動作するかを見てみましょう。こうなると、80行目から83行目が実行されます。80行目では、指標がどの資産に使用されているかを確認します。なぜなら、資産がオーダーブックのイベントを監視できるものである場合、MetaTrader 5にそのようなイベントを受け取りたくないことを伝えなければならないからです。これは81行目でおこないます。82行目と83行目は単にクラスを破棄し、指標がチャートから正しく削除されるようにします。

102行目以降のコードは、本質的にこの記事で理解したいものであり、理解する必要があるものなので、それ自身のトピックとして特別な注意を払う価値があります。そのすぐ下から始まります。


SetBuffer:マジックが起こる場所

本連載の記事をご覧になっている方ならお気づきかもしれませんが、 「リプレイシステムの開発(第39回):道を切り開く(III)」では、別のプロセスに指標情報を送信するための値のバッファリング方法のみを説明するセクションがあります。

このセクションでは、情報が非常に特定のポイントに配置されなければならないことを説明しましたが、何よりも情報が二重のコントロール下になければならないことを強調しました。あの記事では、私たちに何ができるかを理解できなかったかもしれませんが、重要なのは、私たちはこれまで多くの人がおこなってきたことよりもずっと先に進むことができるということです。

102行目から115行目までのコードを見れば、私がすべて自分のやり方でやっていることがわかるでしょう。しかし、なぜ私はこのような方法を取っているのでしょうか。

説明を追うためにページをスクロールする必要がないように、説明の近くの行を記載することにします。そうすることで、アイデアを追いやすくなります。

102. inline void SetBuffer(void)
103. {
104.    uCast_Double Info;
105.    
106.    m_posBuff = (m_posBuff < 0  ? 0 : m_posBuff);
107.    m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
108.    Info._datetime = (*Study).GetInfoMouse().Position.dt;
109.    m_Buff[m_posBuff + 1] = Info.dValue;
110.    Info._int[0] = (*Study).GetInfoMouse().Position.X;
111.    Info._int[1] = (*Study).GetInfoMouse().Position.Y;
112.    m_Buff[m_posBuff + 2] = Info.dValue;
113.    Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
114.    m_Buff[m_posBuff + 3] = Info.dValue;
115. }

49行目では、4つのdouble値をバッファに入れることを示しています。しかし、バッファの具体的な場所はどこでしょうか。これについては前述の記事で述べました。ここからちょうど4つ後ろのrates_totalのポジションを使用します。リプレイ/シミュレーターモードであれば、ゼロの位置から開始できます。なぜなら、そのプロセスを開始するのは私たちだからです。初期化は25行目でおこなわれました。

必要な変換を簡単にするために、104行目でローカル変数を宣言します。106行目の必須確認に注目してください。これは、主にOnCalculate関数に属する49行目に起因する異常事態を防ぐものです。実物資産の場合、m_posBuffにマイナスの値が入ることはまずありません。しかし、リプレイやシミュレーションの資産では、マイナスの値を得ることができます。106行目では、m_posBuffが正しい位置を指すようにすることで、この問題を修正しています。

もしm_posBuffが0より小さいインデックスを指していたら、指標は壊れてしまうでしょう。107行目から指標データの更新を開始することに注意してください。この点は最もシンプルです。109行目、112行目、114行目に提示されているのが、最も難しい部分です。これらの行では、常に最初に計算された位置から始めて、他の値をバッファリングします。

同じ値を他の場所でも公表できるのでしょうか。いいえ。別の順番に並べることはできるのでしょうか。はい。しかし、もし順番を変更すれば、正しい情報を得るために、将来のコードを解決、いや修正しなければならなくなります。注文が変更された場合、値は異なる方法で計算されるため、ここか、この指標に基づいて作成された他のコードで調整をおこなう必要があります。これは、私たちが作りたい他のコードにも関係します。

したがって、何らかの方法論を開発し始めるのは良いことです。そうでなければ、すべてが非常にわかりにくくなり、MetaTrader 5をフルに活用することができなくなります。つまり、ここで使われている方法論はこうです。

  • まず、0の位置で、マウスの線がある価格を保存します。
  • 1番目の位置では、マウスポインタが位置する時間値を保存します。
  • 2番目の位置には、画面の位置に関する値、つまりX座標とY座標を格納します。まずXの値、次にYの値です。
  • 3番目の位置には、追加の要素を格納します。マウスボタンの現在の状態です。これは、最下位バイト(LSB)つまり、ゼロバイトに置かなければなりません。

ここで使用されている方法は、何らかの変更や更新がない限り、この指標の寿命が尽きるまで適用されます。


結論

他にも解決しなければならない問題がいくつかあります。添付ファイルのコードと記事で説明されていることが違うことがよくあるので、コードに書かれていることを理解できるように、記事に従ってください。これには単純な理由があります。添付の​​コードは安定しており、完璧に動作しますが、記事で提供されるコードは変更される可能性があるということです。安定したらコードを理解するために、記事の内容を常に最新に保つ必要があります。

しかし、複雑なコードをコンパイルするのは難しいです。でも、ご安心ください。時折、コンパイルしたコードを添付して、誰もがシステムの開発をフォローできるようにするつもりです。さらに、いくつかの記事で紹介されるすでにコンパイルされたコードは、すべてが完璧にコンパイルされた後、アプリケーションがどのように見えるかを理解するのに役立つでしょう。しかし、すでに少し経験があり、プログラミングのスキルを向上させたいのであれば、記事で紹介されていることを追加すればよいのです。こうすることで、将来的に好きなものを使えるようにシステムを変更する方法を学ぶことができます。しかし、もしそうするのであれば、慎重に行動し、コードに追加するものは必ずテストすることを忘れないでください。

いずれにせよ、まだ終わってはいません。次回は、このトピックを続け、いくつかの未解決の問題に取り組む予定です。

MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/11624

添付されたファイル |
Anexo.zip (420.65 KB)
リプレイシステムの開発(第41回):第2段階(II)の開始 リプレイシステムの開発(第41回):第2段階(II)の開始
もし、この時点まですべてが正しく思えたとしたら、それはアプリケーションの開発を始めるときに、長期的なことをあまり考えていないということです。時間が経つにつれて、新しいアプリケーションをプログラムする必要はなくなり、それらを連携させるだけで済むようになります。それでは、マウス指標を組み立てる方法を説明しましょう。
リプレイシステムの開発(第39回):道を切り開く(III) リプレイシステムの開発(第39回):道を切り開く(III)
開発の第2段階に進む前に、いくつかのアイデアを修正する必要があります。MQL5に必要なことをさせる方法をご存知ですか。ドキュメントに書かれている以上のことをしようとしたことはありますか。そうでないなら、準備をしましょう。ここでは、ほとんどの人が普段やらないことをやるからです。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
Candlestick Trend Constraintモデルの構築(第4回):トレンドの波ごとに表示スタイルをカスタマイズ Candlestick Trend Constraintモデルの構築(第4回):トレンドの波ごとに表示スタイルをカスタマイズ
この記事では、Meta Trader 5上で様々な指標のスタイルを描画するための強力なMQL5言語の機能を探ります。また、スクリプトと、スクリプトをモデルでどのように使えるかについても見ていきます。