English Русский 中文 Español Deutsch Português
preview
市場シミュレーション(第1回):両建て注文(I)

市場シミュレーション(第1回):両建て注文(I)

MetaTrader 5テスター |
453 0
Daniel Jose
Daniel Jose

はじめに

前回の「リプレイシステムの開発(第78回):新しいChart Trade (V)」では、エキスパートアドバイザー(EA)がChart Tradeから送信された指示をどのように解釈するかを説明しました。Chart Tradeが実際にEAに送信する情報は、ユーザーの操作に依存します。つまり、ユーザーが[買い]、[売り]、[ポジションクローズ]ボタンをクリックすると、チャートにメッセージが送信されます。EAの役割のひとつは、このチャートに接続された状態で、そのメッセージを受信、解析、実行することです。

この仕組みは単純で信頼性も高いのですが、ひとつ小さな問題(正確には不便さ)があります。この不便さを解決しなければ、実際に注文を取引サーバーへ送信することができません。

もしここで言っている意味がピンとこない場合は、特定の資産、特に先物契約を取引していないためかもしれません。先物契約には有効期限(満期日)があります。多くの場合、「フル契約(標準契約)」と「ミニ契約」の2種類が同時に取引されています。ミニ契約はフル契約の一部(分数的なサイズ)であり、少ない枚数や小さいボリュームで戦略を構築したい場合に利用できます。

ここで戦略の詳細については深入りしませんが、要するに少ない契約数で戦略を構築する必要があることがあります。こうしたケースでは「ヘッジ戦略」を調べてみると良いでしょう。私たちプログラマにとって重要なのは、「フル契約のチャートを表示している状態で、どのようにミニ契約の取引を実行するか」という点です。

もうひとつの課題は長期戦略です。契約が期限切れになるたび(これは決まった日付で起こります)に、新しいシリーズ(銘柄コード)が始まります。長期的な時間軸で取引をおこなうトレーダーにとって、これは大きな問題となります。なぜなら、インジケーターや移動平均線の計算が、新しいシリーズが始まるたびに最初からリセットされてしまうからです。

これをより具体的に理解するために、ブラジル証券取引所(B3)のドル先物契約を見てみましょう。この契約は毎月満期を迎えます。つまり、毎月1つのシリーズが終了し、新しいシリーズが始まります。1か月には約20営業日(週5日×4週間)があるため、たとえば20期間移動平均線を使用する場合、平均値がやっと計算されてプロットされる頃には契約が期限切れとなり、再び新しいシリーズに切り替わってしまいます。これが20期間の例ですが、より長期のインジケーターではさらに問題が深刻です。つまり、これは大きな課題なのです。

この問題を解決するために、先物契約の過去データを使用する方法があります。しかし、過去データを利用してもすべてが解決するわけではありません。実際には、プログラマにとって新たな課題が生まれます。トレーダーは、サーバーがどのようにデータを受け取り、チャートを描画しているかには関心がありません。彼らが求めるのは、正確な情報と確実な注文実行です。したがって、チャート上の表示処理と注文を取引サーバーへ適切に転送する処理を実装するのは、私たちプログラマの責任です。

一からの取引エキスパートアドバイザーの開発(第11回):両建て注文システム」という記事で、この問題の一部を説明しました。しかし今回は、リプレイ/シミュレーションシステムも扱うため、問題はさらに複雑になります。とはいえ、ナポレオンの有名な戦略「分割して征服せよ」を適用できます。問題を小さな部分に分けて段階的に開発すれば、シミュレーション用の注文システムを構築することができます。その第一歩として、今回は先物契約におけるインジケーターの取り扱いに焦点を当てます。特に、ドル先物契約は私が知る限り最も複雑なケースであるため、これを題材に進めます。ただし、ここで説明する原理は、同様の性質を持つ他の契約にも適用できることを覚えておいてください。


実装の開始

前回の記事(両建て注文システムを開発した回)では、他の種類の契約に対応させるための改良が比較的複雑でした。今回は、より実用的な観点から、そうした調整をより簡単におこなえるようにするため、少し異なるアプローチを取ります。これはトレーダーのためではなく、私たちプログラマーのための設計です。トレーダー側には私たちの実装に適応してもらう必要がありますが、その代わりに、「フル契約」か「ミニ契約」かを選択できるシンプルなオプションを提供します。

この仕組みを動作させるために(少なくとも初期段階では、まだ実際の取引サーバーと通信している段階のため)、既存のコードにいくつかの限定的な修正を加える必要があります。まず重要な点を押さえておきましょう。冒頭でも述べたように、最も適したチャートは過去データに基づくものです。しかし、この過去チャート上では直接取引を行うことはできません。

したがって、必要なのは、過去のチャート上で発行された注文を、トレーダーが実際に取引したい契約(フルまたはミニ)へルーティングする仕組みです。トレーダーがどちらを選択するかは後で対応します。まず理解すべきなのはシンプルな事実です。「チャートに表示されている内容は、契約の過去データに基づいている」という点です。これが基本となります。

B3では、先物契約には6種類の命名規則があります。これは各契約に適用されるもので、つまりフル契約に6種類、ミニ契約にも6種類あります。一見すると非常にややこしく感じられますが、よく見てみるとそれほど複雑ではありません。実際には、これら6種類は3つの主要タイプに分類でき、それぞれが2つのサブバリエーションを持っています。

この整理によって、作業がかなり楽になります。それでも、これら3種類の違いをきちんと調べておくことを強くおすすめします。タイプごとにチャートデータの構成が大きく異なり、多くのトレーダーがその違いをまったく認識していないからです。もしあなたがソリューション開発のみを担当するプログラマであれば、必ずトレーダーにこの点を説明してください。また、開発と取引の両方を行う方であれば、なおさら注意が必要です。この違いを理解していないと、深刻な問題に直面するおそれがあります。

さて、3つの命名タイプがあり、それぞれ2つのバリエーションを持つことが分かりました。よいでしょう。しかし、私たちプログラマにとって重要なのは、名前そのものではありません。それはトレーダーにとって重要な情報です。私たちにとっての核心は「この命名規則には何らかの法則性があるのか?」「もしあるなら、それをどのように利用して両建て注文システムを構築できるのか?」という問いです。

幸いにも、そのような法則は確かに存在します。実際、私たちはすでにそれをある程度活用しています。次のコードスニペットでは、その仕組みがどのように機能するのかを見ていきましょう。

38. //+------------------------------------------------------------------+
39.       void CurrentSymbol(void)
40.          {
41.             MqlDateTime mdt1;
42.             string sz0, sz1;
43.             datetime dt = macroGetDate(TimeCurrent(mdt1));
44.             enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER;
45.       
46.             sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
47.             for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS);
48.             switch (eTS)
49.             {
50.                case DOL   :
51.                case WDO   : sz1 = "FGHJKMNQUVXZ"; break;
52.                case IND   :
53.                case WIN   : sz1 = "GJMQVZ";       break;
54.                default    : return;
55.             }
56.             for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0))
57.                if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break;
58.          }
59. //+------------------------------------------------------------------+

C_Terminal.mqhファイルのコード

このコードスニペットは、ヘッダーファイル「C_Terminal.mqh」に含まれています。まず、44行目では、サポートする先物契約(銘柄)の名前を定義しています。もしトウモロコシ、牛、S&P、ユーロなど他の資産を扱いたい場合は、このリストに追加することも可能です。ただし、それぞれの契約を正しく識別するために、各資産の命名規則に従う必要があります。ここで説明する手続きは、過去の契約や、2回以上先の満期契約を返すことはありません。常に現在アクティブな契約を特定するようになっています。

これを実現するために、46行目では、資産名の最初の3文字を抽出しています。どの資産であっても、常にこの最初の3文字を取得します。これは、B3が先頭3文字を資産識別子として使用する命名規則を採用しているためです。抽出された文字列は変数に保存され、その後のコード全体で使用されます。この点を覚えておいてください。

続いて、47行目では、先ほど定義した契約名の列挙型をループ処理します。ここでの目的は、正しい契約名を照合して特定することです。そのため、44行目の列挙型の名前は、実際の契約名と一致する形式でなければなりません。B3ではすべての契約名が大文字表記であるため、列挙型でも大文字を使用する必要があります。一致が見つかるか、リストの走査が終了すると、47行目のループは終了します。

48行目では、取得した値をテストします。一致が見つからなかった場合、実行は54行目のコードへジャンプします。一致が見つかった場合は、完全な契約名を構築します。命名の最終確認は57行目でおこなわれ、生成された契約名が現在アクティブな契約に対応しているかを検証します。つまり、この処理は可能性のある先物契約を順に走査し、最終的に現在有効な契約を特定する仕組みになっています。

ただし、ここには重要な制約があります。この処理では、資産名から派生したベース契約名を使用しています。そのため、現在のコードでは、特定の契約の過去データを同一契約のアクティブ版にしか対応付けできません。言い換えると、フル契約の履歴データをミニ契約のアクティブ版にマッピングすることはできないのです。この不便さを、本記事で解消していきます。

今回の改良により、トレーダーは過去データがどちらの契約に基づいていても、フル契約またはミニ契約のいずれかを自由に選択して取引できるようになります。私たちの目標は、コードの変更を最小限に抑えつつこの柔軟性を実現することです。コードを大きく変更すればするほど、エラーが発生するリスクが高まるためです。

そのため、上記のコードスニペットを次のように修正しました。

38. //+------------------------------------------------------------------+
39.       void CurrentSymbol(bool bUsingFull)
40.          {
41.             MqlDateTime mdt1;
42.             string sz0, sz1;
43.             datetime dt = macroGetDate(TimeCurrent(mdt1));
44.             enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER;
45.       
46.             sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
47.             for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS);
48.             switch (eTS)
49.             {
50.                case DOL   :
51.                case WDO   : sz1 = "FGHJKMNQUVXZ"; break;
52.                case IND   :
53.                case WIN   : sz1 = "GJMQVZ";       break;
54.                default   : return;
55.             }
56.             sz0 = EnumToString((eTypeSymbol)(((eTS & 1) == 1) ? (bUsingFull ? eTS : eTS - 1) : (bUsingFull ? eTS + 1: eTS)));
57.             for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0))
58.                if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break;
59.          }
60. //+------------------------------------------------------------------+

C_Terminal.mqhファイルのコード

ご覧の通り、変更点は最小限です。最初の変更は、39行目の関数にパラメータを追加した点です。この引数は、生成される契約名がフル契約用かミニ契約用かを指定します。どちらを使用するかはトレーダーの判断に委ねられます。私たちプログラマの責任は、トレーダーが希望するチャートを使用できる柔軟性を提供することです。ただし、チャートに表示される資産データは、取引対象の契約と何らかの形で対応している必要があります。もちろん、より複雑な挙動を実装することも可能ですが、必要以上に複雑化するのは避けたいと考えています。

39行目の変更以外では、新しい行を1つ追加しました。この追加は厳密には必須ではなく、既存のコードを修正するだけでも同じ結果が得られます。しかし、この新しい行を追加することで、説明が簡潔になり、理解もしやすくなります。この新しい行は56行目で、変数「sz0」が使用される58行目に直接書くことも可能ですが、そうすると説明がややこしくなり理解が難しくなります。

では、56行目ではsz0に対して何をしているのでしょうか。詳しく見てみましょう。基本的には、資産名そのものは無視しています。代わりに、44行目で定義された列挙型の値を文字列に変換しています。MQL5ではEnumToString関数を使うことで、これを簡単に実現できます。

ここで、注意すべき点があります。フル契約とミニ契約の両方が存在しない先物契約の場合、少し複雑になることがあります。これは特にコモディティでよく見られる現象です。しかし、ここで示す例では、インデックスや通貨、特にドル先物では両方の契約タイプが存在します。

特に指定しない限り、列挙型は0から始まります。今回の場合、ミニ契約は偶数、フル契約は奇数が割り当てられています。この点を理解することは重要です。値は二進数で表されます。どのビットを選択するかを理解しておく必要があります。二進数では、最下位ビット(右端のビット)が偶数か奇数かを決定します。このビットをビット演算で抽出することで、列挙型の値が偶数か奇数かを判定できます。再度整理すると、ミニ契約は偶数、フル契約は奇数です。

したがって、値が奇数であれば三項演算子の最初の部分が実行され、偶数であれば二番目の部分が実行されます。ここまでは理解しやすいと思います。その後、この最初の三項演算子の各分岐内で、さらに2つ目の三項演算子を使用しています。この2つ目の三項演算子により、変数eTSを調整し、正しい契約名を反映させています。

たとえば、契約がWDOの場合、eTSは2(偶数)です。これにより、最初の三項演算子の第二分岐が実行されます。その内部で、2つ目の三項演算子がさらにチェックをおこないます。ここでは、呼び出し時にフル契約を要求しているかミニ契約を要求しているかを判定します。

トレーダーがフル契約を要求した場合、eTSは1増加し、値が2から3になります。列挙型では、位置3はDOLに対応しています。MQL5がEnumToStringを実行すると、値3が文字列「DOL」に変換され、ミニドルの履歴チャートを基にしていてもフル契約名が出力されます。

逆も同様です。フルドルの履歴チャートを使用していて、トレーダーがミニ契約を要求した場合、最初の三項演算子の第一分岐が実行されます。その内部で2つ目の三項演算子によりeTSが1減少し、値が3から2に戻ります。これによりWDOに対応するミニ契約名が取得されます。

要するに、47行目で取得した値を56行目で調整することで、契約名がトレーダーの選択(ミニまたはフル)に一致しつつ、どちらかの履歴チャートデータに依存できるようになっています。

ここまでは順調です。しかし、もしミニ契約が存在せず、フル契約しかない場合はどうでしょうか。この場合、どのように対応すればよいでしょうか。2つの解決策があるように思えますが、実際には1つしかありません。列挙型の値を複製して偶数・奇数のエントリを人工的に作ろうとすると、コンパイラがエラーを返します。そのため、解決策は列挙型を論理的な順序で構造化することです。そして、特定の値をチェックした際にミニ契約が存在しない場合は、変数「sz0」は変更せずそのままにしておきます。実際には、コード内で追加のテストをおこなうだけで対応可能です。複雑な処理は不要です。

これで問題の第一段階は解決されました。しかし、まだ作業は終わっていません。次に、ヘッダーファイル「C_Terminal.mqh」の別の部分、クラスコンストラクタを修正する必要があります。コンストラクタは、先ほど変更した手続き関数を呼び出す役割を担っています。そのため、元の呼び出しは、新しいバージョンに置き換える必要があります。

72. //+------------------------------------------------------------------+      
73.       C_Terminal(const long id = 0, const uchar sub = 0, const bool bUsingFull = false)
74.          {
75.             m_Infos.ID = (id == 0 ? ChartID() : id);
76.             m_Mem.AccountLock = false;
77.             m_Infos.SubWin = (int) sub;
78.             CurrentSymbol(bUsingFull);
79.             m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
80.             m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
81.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
82.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, true);
83.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, true);
84.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
85.             m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
86.             m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
87.             m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
88.             m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
89.             m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
90.             m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
91.             m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
92.             m_Infos.ChartMode   = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE);
93.             if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE));
94.             ChartChange();
95.          }
96. //+------------------------------------------------------------------+

C_Terminal.mqhファイルのコード

ご覧の通り、非常にシンプルな変更が2つだけあります。最初の変更は73行目で、新しいパラメータを追加した点です。このパラメータは、先ほど説明した手続きを呼び出す78行目で使用されています。デフォルトではミニ契約を優先する設定にしていますが、トレーダーは自分の戦略に合ったオプションを自由に選択できます。この柔軟性をサポートするために、コードの特定の箇所でいくつか小さな調整が必要になります。

まだEAのコード自体は変更していないため、必要な変更はChart Trade側でおこなう必要があります。理解を容易にするため、ここでは別セクションとして扱います。


Chart Tradeを両建て注文システムに変える

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.80"
06. #property icon "/Images/Market Replay/Icons/Indicators.ico"
07. #property link "https://www.mql5.com/pt/articles/12536"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh>
12. //+------------------------------------------------------------------+
13. #define def_ShortName "Indicator Chart Trade"
14. //+------------------------------------------------------------------+
15. C_ChartFloatingRAD *chart = NULL;
16. //+------------------------------------------------------------------+
17. enum eTypeContract {MINI, FULL};
18. //+------------------------------------------------------------------+
19. input ushort         user01 = 1;         //Leverage
20. input double         user02 = 100.1;     //Finance Take
21. input double         user03 = 75.4;      //Finance Stop
22. input eTypeContract  user04 = MINI;      //Cross order in contract
23. //+------------------------------------------------------------------+
24. int OnInit()
25. {
26.    chart = new C_ChartFloatingRAD(def_ShortName, new C_Mouse(0, "Indicator Mouse Study"), user01, user02, user03, (user04 == FULL));
27.    
28.    if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
29. 
30.    return INIT_SUCCEEDED;
31. }
32. //+------------------------------------------------------------------+
33. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
34. {
35.    return rates_total;
36. }
37. //+------------------------------------------------------------------+
38. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
39. {
40.    if (_LastError < ERR_USER_ERROR_FIRST) 
41.       (*chart).DispatchMessage(id, lparam, dparam, sparam);
42. }
43. //+------------------------------------------------------------------+
44. void OnDeinit(const int reason)
45. {
46.    switch (reason)
47.    {
48.       case REASON_INITFAILED:
49.          ChartIndicatorDelete(ChartID(), 0, def_ShortName);
50.          break;
51.       case REASON_CHARTCHANGE:
52.          (*chart).SaveState();
53.          break;
54.    }
55. 
56.    delete chart;
57. }
58. //+------------------------------------------------------------------+

Chart Tradeインジケーターのソースコード

17行目では列挙型を追加しています。これはトレーダー(あるいはユーザー)が契約タイプを定義するのに役立ちます。この列挙型は22行目で使用されている点に注目してください。ここでトレーダーは、EAがフル契約で動作するか、ミニ契約で動作するかを決定します。ただし、1つ注意点があります。理想的には、この選択はChart TradeではなくEA側でおこなうべきです。しかし、現時点ではChart TradeとEAはまだ別のエンティティとして存在するため、このようにしています。

実際の課題は、Chart TradeやEA自体にはありません。前回の記事で説明した通り、Chart TradeはすでにEAを制御できます。問題は、将来的に開発されるシステムの別の部分にあります。最終的にはすべての処理がEAを通過する必要があるため、ここが本当の難所となります。理想的には、必要な設定や情報はすべてEA内で完結させるべきです。しかし、今回はデモンストレーション目的もあり、選択はChart Trade側で処理しています。

この設定値は26行目で使用されます。ここでは数値ではなく、ブール値をコンストラクタに渡している点に注目してください。なぜブール値なのかというと、エンドユーザーにとっては直感的ではないかもしれませんが、プログラマにとっては非常に明確だからです。結局のところ条件は2つしかありません。トレーダーはフル契約を使用するか、ミニ契約を使用するかのいずれかです。そのため、コーディングの観点からはブール値が最も適切な選択です。このブール値はクラスのコンストラクタに渡されます。以下のコードスニペットで、その実装例を確認できます。

213. //+------------------------------------------------------------------+
214.       C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const short Leverage, const double FinanceTake, const double FinanceStop, const bool bUsingFull)
215.          :C_Terminal(0, 0, bUsingFull)
216.          {
217.             m_Mouse = MousePtr;
218.             m_Info.IsSaveState = false;
219.             if (!IndicatorCheckPass(szShortName)) return;
220.             if (!RestoreState())
221.             {
222.                m_Info.Leverage = Leverage;
223.                m_Info.IsDayTrade = true;
224.                m_Info.FinanceTake = FinanceTake;
225.                m_Info.FinanceStop = FinanceStop;
226.                m_Info.IsMaximized = true;
227.                m_Info.minx = m_Info.x = 115;
228.                m_Info.miny = m_Info.y = 64;
229.             }
230.             CreateWindowRAD(170, 210);
231.             AdjustTemplate(true);
232.          }
233. //+------------------------------------------------------------------+

ファイルC_ChartFloatingRAD.mqhの一部

ここでの変更も、C_Terminalクラスのコンストラクタでおこなった変更と同様に非常にシンプルです。コンストラクタで受け取る新しいパラメータを追加するだけ(214行目)で、その後C_Terminalコンストラクタに渡します(215行目)。これだけで完了です。すべて非常に直接的で、説明するまでもありません。

ただし、もう1か所だけ小さな修正が必要です。今回は、C_ChartFloatingRADクラスへの追加です。この変更により、Chart Tradeがトレーダーの意図する操作内容をEAに伝えられるようになります。修正内容は以下のコードスニペットに示されています。

330.       case MSG_BUY_MARKET:
331.          ev = evChartTradeBuy;
332.       case MSG_SELL_MARKET:
333.          ev = (ev != evChartTradeBuy ? evChartTradeSell : ev);
334.       case MSG_CLOSE_POSITION:
335.          if ((m_Info.IsMaximized) && (sz < 0))
336.          {
337.             string szTmp = StringFormat("%d?%s?%s?%c?%d?%.2f?%.2f", ev, _Symbol, GetInfoTerminal().szSymbol, (m_Info.IsDayTrade ? 'D' : 'S'),
338.                                         m_Info.Leverage, FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage));                           
339.             PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp);
340.             EventChartCustom(GetInfoTerminal().ID, ev, 0, 0, szTmp);
341.          }
342.       break;

ファイルC_ChartFloatingRAD.mqhの一部

この調整は非常に細かいため、気付かないこともあるかもしれません。337行目でおこなわれており、新しい値をEAに送信するよう追加しています。この値により、EAはChart Tradeに表示されている資産、より正確にはどの契約が表示されているかを認識できます。この変更により、EA側でも別の更新が必要になりますが、それについては後ほど対応します。


最終的な考察

この記事でおこなった変更は、MQL5の柔軟性を示すものです。しかし同時に、後で解決が必要となる新たな課題も生み出しています。このような問題は決して簡単ではなく、完全かつ容易に解決できるとは限りません。Chart Tradeのユーザーが、チャート上に表示されている資産が必ずしも取引対象であるとは限らないことをEAに伝えられる両建て注文システムを実装することは、非常に複雑です。ここで明確にしておきたいのは、これらの問題の多くはChart TradeやEA自体が原因ではないという点です。

本当の課題は、まだ導入されていない要素、つまり今後開発が必要なコード部分に関連しています。Chart Trade内でユーザーに契約タイプを選択させることは、長期的には最適な解決策ではありません。少なくとも、現時点で私の見解ではそうです。

将来的には、Chart Trade内で直接設定できるようにすることが実用的かつ持続可能になる可能性もあります。しかし現状では、説明を明確にするために、シンプルな設計に留めることを優先しています。個人的な解決策を素早く作るのは容易ですが、他者が理解し応用できるものを構築することはより高度な作業です。そのため、このChart Trade–EAシステムについては、今後もアップデートがおこなわれる予定です。必ず提供されるでしょう。

以下の動画では、このプロセスがチャート上でどのように動作するかを直接確認できます。


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

添付されたファイル |
Anexo.zip (490.53 KB)
人工部族アルゴリズム(ATA) 人工部族アルゴリズム(ATA)
本記事では、状況に応じて適応的に動作する独自の二重行動システムを備えた進化的手法、人工部族アルゴリズム(ATA: Artificial Tribe Algorithm)の主要要素と革新点について、詳細に説明します。ATAは、個体学習と社会的学習を組み合わせ、探索には交叉を用い、局所最適に陥った際には移動によって新たな解を探索するためのアルゴリズムです。
取引におけるニューラルネットワーク:予測符号化を備えたハイブリッド取引フレームワーク(StockFormer) 取引におけるニューラルネットワーク:予測符号化を備えたハイブリッド取引フレームワーク(StockFormer)
本記事では、予測符号化と強化学習(RL)アルゴリズムを組み合わせたハイブリッド取引システム「StockFormer」について解説します。本フレームワークは、統合型のDiversified Multi-Head Attention (DMH-Attn)機構を備えた3つのTransformerブランチを使用しています。DMH-Attnは、従来のAttentionモジュールを改良したもので、マルチヘッドのFeed-Forwardブロックを組み込むことにより、異なるサブスペースにわたる多様な時系列パターンを捉えることが可能です。
取引におけるトレンド基準 取引におけるトレンド基準
トレンドは多くの取引戦略において重要な要素です。本記事では、トレンドを識別するために使用されるいくつかのツールとその特性にを見ていきます。トレンドを理解し正しく解釈することは、取引効率を大幅に高め、リスクを最小限に抑えることにつながります。
取引におけるニューラルネットワーク:Attentionメカニズムを備えたエージェントのアンサンブル(最終回) 取引におけるニューラルネットワーク:Attentionメカニズムを備えたエージェントのアンサンブル(最終回)
前回の記事では、複数のエージェントによるアンサンブルを用いて、異なるデータスケールのマルチモーダル時系列をクロス分析するマルチエージェント適応型フレームワーク「MASAAT」を紹介しました。今回は、このフレームワークのアプローチをMQL5で引き続き実装し、この研究を論理的な結論へと導きます。