English Русский Español Deutsch Português
preview
リプレイシステムの開発(第74回):新しいChart Trade(I)

リプレイシステムの開発(第74回):新しいChart Trade(I)

MetaTrader 5 |
25 0
Daniel Jose
Daniel Jose

はじめに

前回の「リプレイシステムの開発(第73回):異例のコミュニケーション(II)」では、リプレイ/シミュレーターを生成するアプリケーションの開発第2段階を終了しました。つまり、システム全体が整然とした機能的な形で動作するようになったということです。より具体的には、実際の市場の動きをかなり忠実に再現できるシステムを構築できました。

しかし、ここまでおこなってきたことは全体の作業の一部に過ぎません。これから第3段階の開発に入ります。この段階では、リプレイ/シミュレーターそのものから焦点を外し、ライブ取引サーバーと直接やり取りする作業に移ります。言い換えれば、ポジションを開き、管理し、決済するために必要なツールの開発を開始するということです。

これらのツールの一部は、以前本連載の中で取り上げたことがあります。しかし、リプレイ/シミュレーションアプリケーションは時間の経過とともに多くの変更を受けてきたため、古いツールを作り直すか、少なくとも新しいモデルに適合させる必要があります。最初に取り組むツールはChart Tradeです。このツールは、ポジションのオープンやクローズ、市場注文の実行を支援するために設計されています。MetaTrader 5には標準インストールの中にすでに同様の機能が含まれています。それが、下の画像に示す[ワンクリック取引]ボタンです。

これらのボタンは非常に便利で、完璧に動作しますが、私たちには役に立ちません。なぜなら、これらはライブ取引サーバーと直接通信するように設計されているからです。一方で、私たちのリプレイ/シミュレーションシステムは実際のサーバーではありません。それはサービス、正確に言えば、実際のサーバーを模擬するために設計された一連のアプリケーションです。

このため、私たちは独自のツールを作成する必要があります。そうすることで、シミュレーション環境で作業しているために使用できないMetaTrader 5の標準機能を再現できるようになります。

ここで重要な点があります。私がこれまでに費やしてきた努力は、このシステム全体をMQL5のみで構築するという決断に基づいています。この制約によって、開発プロセスは技術的な観点から特に興味深いものとなりました。MQL5だけで実際の取引サーバーのように振る舞うものを作ることは、非常に魅力的な挑戦です。純粋にプログラミングの観点から見ると、ソケットを使用してCやC++でアプリケーションを作成する方がはるかに簡単でしょう。そのようなアプリケーションであれば、必要なすべてのプロトコルを実装し、MetaTrader 5にそれを実際のサーバーだと認識させることが可能です。

このような代替アプローチを採れば、多くの側面が簡略化され、MetaTrader 5を標準構成のまま利用できるようになります。しかし、それではMQL5に関する理解は深まりません。すべてをC/C++で実装してしまえば、そもそもMQL5を使う必要がなくなってしまいます。それは、MQL5で効果的にプログラムを組むための貴重な知識を多く失うことを意味します。私はMQL5だけでシミュレーターのように複雑なものを作るという挑戦を受け入れ、それをここまで貫いてきました。しかし、今からはリプレイ/シミュレーターから少し離れる段階に入ります。しばらくの間、デモ口座で動作している様子を見ることになりますが、実装は常にシミュレーターを念頭に置いておこなわれます。


新たなチャート取引の誕生

Chart Tradeについて最後に議論したのは、「リプレイシステムの開発(第47回):Chart Tradeプロジェクト(VI)」でした。それ以来作業は止まっていましたが、ここで再び取り組む時が来ました。ただし、あの記事から時間が経ちすぎており、当時のコードの大部分はもう役に立ちません。完全に古くなってしまったため、削除する必要があります。それでも、当時の中核となる概念の多くは依然として有効で適用可能です。したがって、開発を再開しますが、重要な違いとして、これまでに新しく発展させてきた概念を取り入れるため、コードは完全に再構築されます。

ここで重要な点を理解しておいてください。インジケーターはポジションや注文を「開く」「変更する」「決済する」ことはできません。その責任は専用のエキスパートアドバイザー(EA)にあります。しかし、今回のChart TradeはEAとして実装しません。代わりに、必要なアクションを実行するためにEAと通信する「インジケーター」として開発します。

これまでの記事をきちんと理解しているなら、インジケーターとサービス間の通信はカスタムイベントを使っておこなうことを覚えているでしょう。これらのカスタムイベントを使うことが、リプレイ/シミュレーション環境内でプログラム間のデータをやり取りする最も実用的な方法です。したがって、今回は一時的にリプレイ/シミュレーターから焦点を移しますが、開発は引き続きその文脈に沿っておこなわれます。そのため、最初のステップは新しいカスタムイベント値をいくつか定義することになります。以下に、新しいDefines.mqhファイルを示します。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_VERSION_DEBUG
05. //+------------------------------------------------------------------+
06. #ifdef def_VERSION_DEBUG
07.    #define macro_DEBUG_MODE(A) \
08.                Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A));
09. #else
10.    #define macro_DEBUG_MODE(A)
11. #endif
12. //+------------------------------------------------------------------+
13. #define def_SymbolReplay         "RePlay"
14. #define def_MaxPosSlider          400
15. #define def_MaskTimeService       0xFED00000
16. #define def_IndicatorTimeFrame    (_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96))))
17. #define def_IndexTimeFrame        4
18. //+------------------------------------------------------------------+
19. union uCast_Double
20. {
21.    double   dValue;
22.    long     _long;                                  // 1 Information
23.    datetime _datetime;                              // 1 Information
24.    uint     _32b[sizeof(double) / sizeof(uint)];    // 2 Informations
25.    ushort   _16b[sizeof(double) / sizeof(ushort)];  // 4 Informations
26.    uchar    _8b [sizeof(double) / sizeof(uchar)];   // 8 Informations
27. };
28. //+------------------------------------------------------------------+
29. enum EnumEvents    {
30.          evHideMouse,               //Hide mouse price line
31.          evShowMouse,               //Show mouse price line
32.          evHideBarTime,             //Hide bar time
33.          evShowBarTime,             //Show bar time
34.          evHideDailyVar,            //Hide daily variation
35.          evShowDailyVar,            //Show daily variation
36.          evHidePriceVar,            //Hide instantaneous variation
37.          evShowPriceVar,            //Show instantaneous variation
38.          evCtrlReplayInit,          //Initialize replay control
39.          evChartTradeBuy,           //Market buy event
40.          evChartTradeSell,          //Market sales event 
41.          evChartTradeCloseAll       //Event to close positions
42.                   };
43. //+------------------------------------------------------------------+

Defines.mqhファイルのソースコード

変更が加えられたのは、39行目、40行目、41行目のみです。これらの行では、Chart TradeインジケーターがEAに、何らかのアクションが必要であることを通知するためにトリガーするカスタムイベントを定義しています。実際に実行を担当するのはEAであり、ポジションのオープンやクローズをおこないます。ここで言っているのは「注文」そのものの話ではないことに注意してください。Chart Tradeインジケーターの真の目的は、MetaTrader 5のデフォルトのポジションオープン/クローズ機能を置き換えることにあります。これにはポジションサイズの増加や部分決済も含まれます。Chart Tradeインジケーターはこれらと同等の機能を実行しますが、後にリプレイ/シミュレーター環境でも同じロジックを適用できるという利点が加わります。このコンテキストでは、取引は常に成行注文で実行されます。今後は、指値・逆指値注文を使えるようにする別システムの開発にも着手しますが、それはもう少し先の話です。まずは最も単純な概念である「成行執行」から始めます。

さて、この準備ができたところで、古いインジケーターを再び動作させるようにする必要があります。まさか「インジケーター全体をゼロから書き直す」と思ってはいないでしょう。そのつもりは最初からありません。それは完全に非合理的です。

では、更新されたインジケーターコードを見ていきましょう。これには、いくつかの理由で修正が加えられていますが、それは読み進めるうちに理解できるでしょう。まずはヘッダファイルC_AdjustTemplate.mqhを確認します。以下が完全なコードです。

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "../Auxiliar/C_Terminal.mqh"
005. //+------------------------------------------------------------------+
006. #define def_PATH_BTN "Images\\Market Replay\\Chart Trade"
007. #define def_BTN_BUY   def_PATH_BTN + "\\BUY.bmp"
008. #define def_BTN_SELL  def_PATH_BTN + "\\SELL.bmp"
009. #define def_BTN_DT    def_PATH_BTN + "\\DT.bmp"
010. #define def_BTN_SW    def_PATH_BTN + "\\SW.bmp"
011. #define def_BTN_MAX   def_PATH_BTN + "\\MAX.bmp"
012. #define def_BTN_MIN   def_PATH_BTN + "\\MIN.bmp"
013. #define def_IDE_RAD   def_PATH_BTN + "\\IDE_RAD.tpl"
014. #define def_IDE_RAD   "Files\\Chart Trade\\IDE_RAD.tpl"
015. //+------------------------------------------------------------------+
016. #resource "\\" + def_BTN_BUY
017. #resource "\\" + def_BTN_SELL
018. #resource "\\" + def_BTN_DT
019. #resource "\\" + def_BTN_SW
020. #resource "\\" + def_BTN_MAX
021. #resource "\\" + def_BTN_MIN
022. #resource "\\" + def_IDE_RAD as string IdeRad;
023. //+------------------------------------------------------------------+
024. class C_AdjustTemplate
025. {
026.    private   :
027.       string m_szName[],
028.              m_szFind[],
029.              m_szReplace[],
030.              m_szFileName;
031.       int    m_maxIndex,
032.              m_FileIn,
033.              m_FileOut;
034.       bool   m_bFirst;
035. //+------------------------------------------------------------------+
036.    public   :
037. //+------------------------------------------------------------------+
038.       C_AdjustTemplate(const string szFile, const bool bFirst = false)
039.          :m_maxIndex(0),
040.           m_szFileName(szFile),
041.           m_bFirst(bFirst),
042.           m_FileIn(INVALID_HANDLE),
043.           m_FileOut(INVALID_HANDLE)
044.          {
045.             ResetLastError();
046.             if (m_bFirst)
047.             {
048.                int handle = FileOpen(m_szFileName, FILE_TXT | FILE_WRITE);
049.                FileWriteString(handle, IdeRad);
050.                FileClose(handle);
051.             }
052.             if ((m_FileIn = FileOpen(m_szFileName, FILE_TXT | FILE_READ)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess);
053.             if ((m_FileOut = FileOpen(m_szFileName + "_T", FILE_TXT | FILE_WRITE)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess);
054.          }
055. //+------------------------------------------------------------------+
056.       ~C_AdjustTemplate()
057.          {
058.             FileClose(m_FileIn);
059.             FileClose(m_FileOut);
060.             FileMove(m_szFileName + "_T", 0, m_szFileName, FILE_REWRITE);
061.             ArrayResize(m_szName, 0);
062.             ArrayResize(m_szFind, 0);
063.             ArrayResize(m_szReplace, 0);
064.          }
065. //+------------------------------------------------------------------+
066.       void Add(const string szName, const string szFind, const string szReplace)
067.          {
068.             m_maxIndex++;
069.             ArrayResize(m_szName, m_maxIndex);
070.             ArrayResize(m_szFind, m_maxIndex);
071.             ArrayResize(m_szReplace, m_maxIndex);
072.             m_szName[m_maxIndex - 1] = szName;
073.             m_szFind[m_maxIndex - 1] = szFind;
074.             m_szReplace[m_maxIndex - 1] = szReplace;
075.          }
076. //+------------------------------------------------------------------+
077.       string Get(const string szName, const string szFind)
078.          {
079.             for (int c0 = 0; c0 < m_maxIndex; c0++) if ((m_szName[c0] == szName) && (m_szFind[c0] == szFind)) return m_szReplace[c0];
080.             
081.             return NULL;
082.          }
083. //+------------------------------------------------------------------+
084.       void Execute(void)
085.       bool Execute(void)
086.          {
087.             string sz0, tmp, res[];
088.             int count0 = 0, i0;
089.                         
090.             if ((m_FileIn != INVALID_HANDLE) && (m_FileOut != INVALID_HANDLE)) while ((!FileIsEnding(m_FileIn)) && (_LastError == ERR_SUCCESS))
091.             if ((m_FileIn == INVALID_HANDLE) || (m_FileOut == INVALID_HANDLE)) return false;
092.             while (!FileIsEnding(m_FileIn))
093.             {
094.                sz0 = FileReadString(m_FileIn);
095.                if (sz0 == "<object>") count0 = 1;
096.                if (sz0 == "</object>") count0 = 0;
097.                if (count0 > 0) if (StringSplit(sz0, '=', res) > 1)
098.                {
099.                   if ((m_bFirst) && ((res[0] == "bmpfile_on") || (res[0] == "bmpfile_off")))
100.                      sz0 = res[0] + "=\\Indicators\\Replay\\Chart Trade.ex5::" + def_PATH_BTN + res[1];
101.                      sz0 = res[0] + "=\\Indicators\\Chart Trade.ex5::" + def_PATH_BTN + res[1];
102.                   i0 = (count0 == 1 ? 0 : i0);
103.                   for (int c0 = 0; (c0 < m_maxIndex) && (count0 == 1); i0 = c0, c0++) count0 = (res[1] == (tmp = m_szName[c0]) ? 2 : count0);
104.                   for (int c0 = i0; (c0 < m_maxIndex) && (count0 == 2); c0++) if ((res[0] == m_szFind[c0]) && (tmp == m_szName[c0]))
105.                   {
106.                      if (StringLen(m_szReplace[c0])) sz0 =  m_szFind[c0] + "=" + m_szReplace[c0];
107.                      else m_szReplace[c0] = res[1];
108.                   }
109.                }
110.                if (FileWriteString(m_FileOut, sz0 + "\r\n") < 2) return false;
111.             };
112.             
113.             return true;
114.          }
115. //+------------------------------------------------------------------+
116. };
117. //+------------------------------------------------------------------+
118. #undef def_BTN_BUY
119. #undef def_BTN_SELL
120. #undef def_BTN_DT
121. #undef def_BTN_SW
122. #undef def_BTN_MAX
123. #undef def_BTN_MIN
124. #undef def_IDE_RAD
125. #undef def_PATH_BTN
126. //+------------------------------------------------------------------+

C_AdjustTemplate.mqhファイルのソースコード

このコードのいくつかの行には取り消し線が引かれています。では、その理由を見ていきましょう。まず、14行目は修正され、現在は13行目のコードに置き換えられています。この変更は、テンプレートファイルの保存場所が新しいパスに移動されたことを反映しています。心配はいりません。もしまだ持っていなくても、これらのファイルは添付ファイルに含まれています。やるべきことは、添付ファイル内のフォルダ構造をそのまま維持することだけです。そうすれば、ここで宣言されているリソースは問題なく参照できます。この変更以外にも、いくつかの行が削除されています。

45行目は以前の記事で説明した理由により削除されました。つまり、_LastError変数には、実行中にエラーが発生したためではなく、他の内部的な理由で値が格納されている場合があります。そのため、特別なケースで本当に関連がある場合を除き、_LastErrorの値は気にしないことにしています。

次に90行目を見てみましょう。この行が削除された理由は、_LastErrorがERR_SUCCESS以外の値を保持していても、それが必ずしも私たちのアプリケーションによって引き起こされたとは限らないからです。誤解や衝突の可能性を避けるために、ここでは修正を加えました。

また、以前は手続きだった部分が関数に変わっています。84行目に注目すると、取り消し線が引かれ、その下の85行目に置き換えられています。この変更理由はシンプルです。エラーが時々発生することがあり、_LastError変数に直接依存するのは好ましくないからです。

このインジケーターは非常に厳格な環境で動作します。指値注文や逆指値注文のように、(後で「なぜ時々なのか」を説明しますが)軽微なエラーを許容できる場合とは違い、このインジケーターではそうしたことはできません。なぜなら、成行執行を扱うからです。

さらに、この先の記事で触れる、もっと重大な問題も存在します。今はとにかく、テンプレートファイル内のデータが完全に正確であることを保証しなければなりません。

したがって、91行目や110行目で失敗が検出された場合は、必ず呼び出し元に報告します。逆に、すべてが正しければ、テンプレートファイルは更新され、113行目で成功が返されます。それだけの話です。

最後に、ヘッダファイルの説明を終える前に、101行目に注目してください。100行目は削除され、この行に置き換えられています。101行目は文字列で、Chart Tradeインジケーターの想定される場所を指定しています。つまり、保存先が変わったということです。

この文字列で指定された場所に、インジケーターを正確に配置し、その名前も一致させなければなりません。なぜなら、この文字列が使われるとき、実際にはインジケーター内でリソースとして宣言されているビットマップファイルにアクセスしているからです。コードやインジケーター名、フォルダの場所を少しでも変えてしまうと、MetaTrader 5はビットマップファイルにアクセスできなくなります。その結果、Chart Tradeインジケーターは正しく表示されなくなります。

通常はメインのソースコードを見せる前に、すべてのヘッダファイルを確認するのですが、今回は例外とします。インジケーターの中核となるヘッダファイルを飛ばして、直接インジケーターのソースコード本体に進みます。以下が完全なコードです。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Chart Trade Base Indicator."
04. #property description "See the articles for more details."
05. #property version   "1.74"
06. #property icon "/Images/Market Replay/Icons/Indicators.ico"
07. #property link "https://www.mql5.com/pt/articles/12413"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. #property indicator_buffers 1
11. //+------------------------------------------------------------------+
12. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh>
13. //+------------------------------------------------------------------+
14. #define def_ShortName "Indicator Chart Trade"
15. //+------------------------------------------------------------------+
16. C_ChartFloatingRAD *chart = NULL;
17. //+------------------------------------------------------------------+
18. input long     user00 = 0;          //ID
19. input ushort   user01 = 1;          //Leverage
20. input double   user02 = 100.1;      //Finance Take
21. input double   user03 = 75.4;       //Finance Stop
22. //+------------------------------------------------------------------+
23. double m_Buff[];
24. //+------------------------------------------------------------------+
25. int OnInit()
26. {
27.    bool bErr;
28.       
29.    chart = new C_ChartFloatingRAD(user00, "Indicator Chart Trade", new C_Mouse(user00, "Indicator Mouse Study"), user01, user02, user03);
30.    chart = new C_ChartFloatingRAD(def_ShortName, new C_Mouse(0, "Indicator Mouse Study"), user01, user02, user03);
31.    
32.    if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
33. 
34.    if (bErr = (_LastError != ERR_SUCCESS)) Print(__FILE__, " - [Error]: ", _LastError);
35. 
36.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
37.    ArrayInitialize(m_Buff, EMPTY_VALUE);
38.    
39.    return (bErr ? INIT_FAILED : INIT_SUCCEEDED);
40.    return INIT_SUCCEEDED;
41. }
42. //+------------------------------------------------------------------+
43. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
44. {
45.    (*chart).MountBuffer(m_Buff, rates_total);
46.    
47.    return rates_total;
48. }
49. //+------------------------------------------------------------------+
50. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
51. {
52.    if (_LastError < ERR_USER_ERROR_FIRST) 
53.       (*chart).DispatchMessage(id, lparam, dparam, sparam);
54.    (*chart).MountBuffer(m_Buff);
55.    
56.    ChartRedraw();
57. }
58. //+------------------------------------------------------------------+
59. void OnDeinit(const int reason)
60. {
61.    switch (reason)
62.    {
63.       case REASON_INITFAILED:
64.          ChartIndicatorDelete(ChartID(), 0, def_ShortName);
65.          break;
66.       case REASON_CHARTCHANGE:
67.          (*chart).SaveState();
68.          break;
69.    }
70. 
71.    delete chart;
72. }
73. //+------------------------------------------------------------------+

Chart Trade指標のソースコード

一見すると、このコードは多数の取り消し線のせいで複雑に見えるかもしれません。しかし、これだけ多くの行が削除されているのは、元のコードを改変したバージョンだからです。

まだご覧になっていない方は、オリジナル版が記載されたのは「リプレイシステムの開発(第47回):Chart Tradeプロジェクト(VI)」でした。ただし、その記事のコードを使おうと思ってはいけません。このバージョンは動作がまったく異なります。複雑そうに見えますが、実際にはとてもシンプルです。というのも、複雑な処理はすべてヘッダファイルに移されているからです。そのため、このコードの動作を正確に理解することが、後で確認するヘッダファイルの理解にもつながります。

では順を追って見ていきましょう。大部分は変更というより削除です。まず最初に削除したのは10行目です。ご覧のとおり取り消し線が引かれています。

ここから少し難しく感じるかもしれません。インジケーターで使用するバッファ数を指定するプロパティを削除すると、このインジケーターはバッファ数が0であるとコンパイラに伝えることになります。これは、このプロパティのデフォルト値が0だからです。つまり、このインジケーターはCopyBufferを通じてアクセス可能な内部バッファデータを一切出力しません。

バッファがないというのは一見大きな制約のように思えます。もしこの意味がまだ理解できない場合は、MetaTrader 5とMQL5環境の仕組みを十分に理解できていない可能性があります。ここで特に、まだ学習中でMQL5に習熟したいと考えている方のために説明します。

インジケーターは計算やシグナルの発生、あるいは今回のようにユーザーインターフェイスの提供など、私たちを支援するためのものです。こうした用途、とくに後者の場合、スクリプトなど他の種類のプログラムを使うことも技術的には可能です。しかしスクリプトには欠点があります。時間足を切り替えると削除され、自動で再読み込みされません。手動で再適用する必要があります。これは不便です。このため、このようなケースではインジケーターを使用します。

ただし、インジケーターにも問題があります。特にタイマーを使いたい場合です。インジケーターにタイマーを追加すると、同じチャート上の他のインジケーターにも影響します。タイマーを使った定期実行をスクリプトに頼らずにおこないたい場合は、EAやサービスを使うのが一般的です。どちらを選ぶかはタイミングの用途次第です。

もちろん、Chart TradeはサービスやEAとしても実装可能です。インジケーターである必要はありません。サービスにすれば、すべてのチャートでChart Tradeを動かすこともできますし、対象チャートに必要なChart Tradeオブジェクトを正しく表示させることもできます。

しかし、この方法の問題はイベント処理にあります。サービスには内蔵のイベントハンドラがありません。基本的にはチャートに関連付けられないスクリプトのようなものです。したがって、Chart Tradeをサービスとして使うなら、イベント(ボタンクリックなど)を処理するためのインジケーターも別途作らなければなりません。

これはセットアップを必要以上に複雑にします。ではEAの中にChart Tradeを入れるのはどうでしょうか。それは一見もっともらしく、ある程度は合理的です。しかし実用的ではありません。

理由はひと言でいうと「分散性」の欠如です。EAにChart Tradeを組み込むと、そのEAにしか存在しません。後で別のEAを作る場合、再びChart Tradeのコード全体を組み込まなければなりません。確かに#includeを使えばモジュール化は可能です(12行目で見ているように)。しかし、後でChart Tradeのコードを改善した場合、それを使うすべてのEAを再コンパイルする必要があります。これは非効率でエラーのもとです。

DLLのように、EAを再コンパイルせずにアップグレードできる方法も考えられますが、それではかえって複雑さが増してしまいます。最終的に、Chart Tradeをインジケーターとして維持することにしました。ただし以前と違い、このバージョンでは外部アクセス用のバッファは一切使いません。処理方法は変わっており、その詳細は後で説明します。まだ理解すべき点が残っているので、コードに戻りましょう。

14行目を見てください。これは12行目の#includeの後に定義されています。これはとても重要です。#defineが#includeの前にあるか後にあるかで、動作やオプションの可否が変わります。

14行目の定義はC_ChartFloatingRAD.mqhの読み込み後におこなわれるため、そのファイル内では使用できません。逆に、前に置けばそのファイル内からも参照できます。プログラミングでは順番が重要なのです。まだまだ面白いことがたくさんあるので、続けましょう。

18行目は不要になったため削除しました。ユーザーが自分でチャートにChart Tradeインジケーターを追加するため、チャートIDを定義する必要がなくなったからです。23行目も削除されましたが、10行目の削除とは関係ありません。10行目はインジケーターが存在するかを判定し、存在すればCopyBufferを使って外部からアクセスできるバッファ数を示します。外部アクセスを許可しなくても内部バッファを持つことは可能ですが、今回は不要なため23行目も削除しました。同じ理由で36行目と37行目も削除しています。

OnInitでは多くのコードが削除されています。これはC_ChartFloatingRAD.mqhの変更に関連しており、次回の記事で説明します。削除されたコードのうち、機能的に重要なのは3行だけです。30行目から順に見ていきましょう。30行目はC_ChartFloatingRADクラスを初期化します。これはnew演算子を使っていますが、ご存じの通りコンストラクタは戻り値を返しません。では初期化失敗をどうやって検出するのでしょうか。別の仕組みが必要です。

本来、コンストラクタは変数や内部状態の初期化だけをおこなうべきですが、しかし、それを禁止する厳格なルールはないので、現実にはもっと多くの処理をさせることもあります。ここで問題になるのは、失敗を報告する仕組みがないと初期化エラーを解決できない点です。

幸い、MQL5ではこれをおこなう方法があります。最適とは言えませんが機能します。32行目では_LastErrorの値を確認し、事前定義されたエラーコードなら初期化失敗とみなします。この構造を残すかは未定ですが、52行目のおかげで可能になっています。ただし、これについては後で説明します。32行目で失敗を検出した場合、OnInitからエラー定数を返し、MetaTrader 5がOnDeInitを呼び出してインジケーターをチャートから削除します。そうでなければ40行目で成功定数を返します。

次に、45行目と54行目を見てください。以前はバッファの設定に使われていましたが、バッファを使わなくなったため削除しました。52行目の条件式は今は意味がわからないかもしれませんが、これがないと53行目は確実に失敗します。その理由は次回、C_ChartFloatingRADクラスの説明で明らかになります。

最後にOnDeInit手続きを見てみましょう。これはインジケーターがチャートから削除されたときに自動で呼び出されます。何か普通でないことがあります。64行目です。この行はなぜここにあるのでしょうか。一見すると意味が分かりません。しかしこれはC_ChartFloatingRADクラスを知らないと理解できません。このファイルだけを見れば、OnDeInit(REASON_INITFAILED)が呼ばれる理由は32行目だけです。しかしC_ChartFloatingRADクラス内には他の理由もあります。そこでこの呼び出しをここに集約しました。64行目はChart Tradeインジケーターを確実にチャートから削除するためのものです。より深い理由は次回の記事で説明します。


最後に

この記事では、Chart Tradeインジケーターに加えられた変更の一部を紹介しました。次回の記事でC_ChartFloatingRADクラスの説明を読む前に、このコードを完全に理解しようとしたり使用しようとしたりしてはいけません。すれば大きなミスを犯す可能性があります。少しだけ我慢して次の記事をお待ちください。そこでは、Chart Tradeインジケーターがどのようにクラスとして実装されているのかを、まさにそのコードで確認することができます。

このコードをコンパイルしたい場合は、C_AdjustTemplate.mqhヘッダファイルで使われるデータを必ず用意してください。これらは添付ファイルに含まれています。もちろん、自分でChart Trade用の画像やテンプレートモデルを作成することも可能です。ただし、C_AdjustTemplateクラスが期待する仕様に適合させる必要があります。以上が今回の内容です。忘れずに次回の記事もチェックしてください。

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

添付されたファイル |
Anexo.zip (9.92 KB)
初級から中級まで:共用体(II) 初級から中級まで:共用体(II)
今日はとても面白く興味深い記事をご紹介します。今回は共用体(union)を取り上げ、以前に触れた問題の解決を試みます。また、アプリケーションでunionを使用した際に発生しうる、少し変わった状況についても探っていきます。ここで提示される資料は教育目的のみに使用されます。いかなる状況においても、提示された概念を学習し習得する以外の目的でアプリケーションを閲覧することは避けてください。
MQL5での移動平均をゼロから作成する:単純明快 MQL5での移動平均をゼロから作成する:単純明快
簡単な例を使って、移動平均の計算原理を検証するとともに、移動平均を含むインジケーター計算の最適化方法について学びます。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
未来のトレンドを見通す鍵としての取引量ニューラルネットワーク分析 未来のトレンドを見通す鍵としての取引量ニューラルネットワーク分析
この記事では、テクニカル分析の原理とLSTMニューラルネットワークの構造を統合することで、取引量分析に基づく価格予測の改善可能性を探ります。特に、異常な取引量の検出と解釈、クラスタリングの活用、および機械学習の文脈における取引量に基づく特徴量の作成と定義に注目しています。