
リプレイシステムの開発(第39回):道を切り開く(III)
はじめに
前回の「リプレイシステムの開発(第38回):道を切り開く(II)」では 、エキスパートアドバイザー(EA)から指標にデータを送信する方法を説明し、実演しました。これで指標を設定し、もっともらしい情報を返すようにすることができました。
そこで、今度は逆方向に進み、指標が呼び出し元(この場合はEA)に何らかの意味を持つものを伝えるようにします。どのように進めるべきか知る必要があります。
ここにはいくつかのことがあります。説明しやすいものもあれば、そうでないものもあります。従って、簡単に実演できるものもあれば、時間を要するものもあります。しかし、いずれにせよ、この記事ではChart Traderを構築するために必要な土台を作ります。リプレイ/シミュレーションシステムで開発されるChatTraderは、前回の記事で紹介したものと非常によく似ています。
しかし、「一からの取引エキスパートアドバイザーの開発(第30部):指標としてのCHART TRADE?」稿で見たものとは違うものになるでしょう。ここでやることは、Chart Traderの作成方法を選択したおかげで、子供の遊びに見えるかもしれません。しかし、その驚きを台無しにしてはなりません。そうでなければ面白くありません。これから説明することを簡単にするために、まずいくつかのことを理解する必要があります。
モデルの初期構築
この2つの記事で説明したことはある程度公平であり、うまく機能しており、それが基本的に私が示したかったことですが、ここで別の問題が発生します。EAから指標にデータを送信するだけです。つまり、コミュニケーションがあるということです。しかし、指標データはまだ読めていません。この場合、指標からデータを読み取る機能を実装する必要があります。
作業を楽にするためには、コードを準備する必要があります。
最初にすることは、以下に示すヘッダーファイルの作成です。
ヘッダーファイル:
1. #property copyright "Daniel Jose" 2. #property link "" 3. //+------------------------------------------------------------------+ 4. #define def_ShortName "SWAP MSG" 5. //+------------------------------------------------------------------+
このヘッダーファイルはDefines.mqhと呼ばれます。IncludesディレクトリのMode Swapサブフォルダに保存します。気になるのは4行目だけです。ここでは、使いやすいようにEAと指標の両方で使用される名前を設定します。なぜなら、指標で名前を指定しても、EAで同じ名前を指定するのを忘れてしまうことがあるからです。ファイル名のことを言っているのではありません。MetaTrader 5で指標が認識される名前のことです。
分かりやすくするために、下の指標のコードを見てみましょう。
指標のソースコード:
01. #property copyright "Daniel Jose" 02. #property link "" 03. #property version "1.00" 04. #property indicator_chart_window 05. #property indicator_plots 0 06. #property indicator_buffers 0 07. //+------------------------------------------------------------------+ 08. #include <Mode Swap\Defines.mqh> 09. //+------------------------------------------------------------------+ 10. #define def_ShortNameTmp def_ShortName + "_Tmp" 11. //+------------------------------------------------------------------+ 12. input double user00 = 0.0; 13. //+------------------------------------------------------------------+ 14. long m_id; 15. //+------------------------------------------------------------------+ 16. int OnInit() 17. { 18. m_id = ChartID(); 19. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortNameTmp); 20. if (ChartWindowFind(m_id, def_ShortName) != -1) 21. { 22. ChartIndicatorDelete(m_id, 0, def_ShortNameTmp); 23. Print("Only one instance is allowed..."); 24. return INIT_FAILED; 25. } 26. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName); 27. Print("Indicator configured with the following value:", user00); 28. 29. return INIT_SUCCEEDED; 30. } 31. //+------------------------------------------------------------------+ 32. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 33. { 34. return rates_total; 35. } 36. //+------------------------------------------------------------------+
08行目のコードに注目してください。この行で、上記のヘッダーファイルのコードをインクルードするようコンパイラに要求します。指標のコードにこの定義がなくなったことを除けば、コードは同じままです。
従って、これまでの説明はすべて有効です。では、下のEAコードを見てみましょう。
EAのソースコード:
01. #property copyright "Daniel Jose" 02. #property link "" 03. #property version "1.00" 04. //+------------------------------------------------------------------+ 05. #include <Mode Swap\Defines.mqh> 06. //+------------------------------------------------------------------+ 07. #define def_SWAP "Mode Swap\\Swap MSG.ex5" 08. #resource "\\Indicators\\" + def_SWAP 09. //+------------------------------------------------------------------+ 10. input double user00 = 2.2; 11. //+------------------------------------------------------------------+ 12. int m_handle; 13. long m_id; 14. //+------------------------------------------------------------------+ 15. int OnInit() 16. { 17. m_id = ChartID(); 18. 19. EraseIndicator(); 20. m_handle = iCustom(NULL, PERIOD_CURRENT, "::" + def_SWAP, user00); 21. ChartIndicatorAdd(m_id, 0, m_handle); 22. 23. Print("Indicator loading result:", m_handle != INVALID_HANDLE ? "Success" : "Failed"); 24. 25. return INIT_SUCCEEDED; 26. } 27. //+------------------------------------------------------------------+ 28. void OnDeinit(const int reason) 29. { 30. EraseIndicator(); 31. } 32. //+------------------------------------------------------------------+ 33. void OnTick() 34. { 35. } 36. //+------------------------------------------------------------------+ 37. void EraseIndicator(void) 38. { 39. if ((m_handle = ChartIndicatorGet(m_id, 0, def_ShortName)) == INVALID_HANDLE) return; 40. ChartIndicatorDelete(m_id, 0, def_ShortName); 41. IndicatorRelease(m_handle); 42. } 43. //+------------------------------------------------------------------+
ここには、非常に異なっているものがあります。しかしよく見ると、その違いはそれほど大きくありません。EraseIndicatorという関数があるだけのようです。37行目にあります。この関数は2箇所で呼び出されます。1つ目は19行目、2つ目は30行目です。なぜ30行目で呼び出されるのかを疑問に思っている方はいらっしゃらないと思います。しかし、19行目はどうでしょうか。この呼び出しの理由をご存知ですか。
19行目からの呼び出しの理由は、まさにEAで報告される値と指標に送信される値の同期を確保するためです。また、注意深く考慮しなければならないことがあります。EAコードの39行目を見てください。19行目と30行目の両方で、関数を呼び出す際に指標がチャート上にあるかどうかを確認していることに注意してください。指標がなければ、コードは終了します。この場合、指標をチャートから削除する40行目も、ハンドルを解放する41行目も実行されません。
しかし、なぜ40行目を使ってチャートから指標を削除する必要があるのでしょうか。この原因はEAのコードではなく、指標のコードにあります。
指標のコードを見ると、チャート上にある場合、EAがその値を変更しようとした瞬間に、20行目が指標をブロックすることに気づくでしょう。これにより、チャートに新しい指標を配置しようとしていることが検知され、更新されなくなります。実際には新しい指標は追加されていないが、20行目はそのように認識します。このため、指標をチャートから削除する必要があります。
EAのコードに戻りましょう。07行目と08行目は何を表しているのでしょうか。そして、なぜ20行目が前回の記事で取り上げたものと違うのでしょうか。
まさにここから、メインから別のエリアへの移行が始まります。07行目は定義を作成しますが、その定義は指標コードの場所を指定します。したがって、コンパイラがEA実行ファイルを作成し、08行目を見つけると、07行目で指定された実行ファイルを探すことになります。実行ファイルが存在しない場合、コンパイラはそのような実行ファイルを作成するコードのコンパイルを開始します。つまり、08行目では、指標をEAの内部リソースとします。
これはなかなか興味深いです。しかし、長所と短所があります。特筆すべき利点の1つは、EAをコンパイルした後、指標の実行ファイルを削除できることです。しかし、まだその必要はありません。これを可能にするには、考慮しなければならない細部があります。特筆すべきデメリットは、EAをコンパイルした後、指標に問題が発生した場合、EAコード全体を再コンパイルしなければならないことです。たとえ問題が指標だけにあったとしてもです。落ち着いてください。ひとつ、考慮しなければならないことがあります。
これはどんな詳細なのでしょうか。これは、ほとんど気づかれない、かなり微妙なポイントです。だからこそ、優れたプログラマーは優れた観察者でもあるのです。では、詳細を説明しましょう。EAコードの20行目を見てください。第3パラメータの宣言に小さな鎖(::)があることに注目してください。定義の前に来るこの小さな連鎖が、スコープ解決演算子です。
演算子がここにあるという事実そのものが、私たちが想像しているのとは違う方法で何かを使うことを示しています。この演算子が登場するたびに、何らかの形で明示的にコンパイラに何をすべきかを指示していることになります。通常、コンパイラが下すべき判断がありますが、それが何であるかを明示的に知る必要があります。
コードに示した具体的なケースでは、EAの実行ファイルのリソースとして見つかった指標を使用するようコンパイラに指示します。つまり、EAのコンパイルに成功したら、指標の実行ファイルを削除すればよいのです。そして、EAは依然として正しい指標を使うことができます。
以下の例に示すように、このスコープ解決演算子を追加しないと、コンパイラが指標をEAリソースとして含めても、EAは実際には定義で指定されたディレクトリにある実行ファイルを使用します。この場合、指標をEAと一緒に別のファイルに移植する必要があります。
20. m_handle = iCustom(NULL, PERIOD_CURRENT, def_SWAP, user00);
このシンプルな詳細が重要なのです。フルコードの20行目の場合、EAをコンパイルした後、指標の実行ファイルを削除することができます。指標が追加され、EAリソースとして使用されている場合、後者をコンパイルする前に、指標の実行ファイルを削除することをお勧めします。これにより、指標に存在する最新のコードが実際にコンパイルされることが保証されます。通常、複数のコンパイルが使われるシステムでは、MAKEと呼ばれるファイルというヘルパーがあります。
これを使用すると、コードが再度コンパイルされるたびに、MAKEは実行可能ファイルを適切に削除します。もちろん自動的にです。これは、実行ファイルのソースコードで使われているいくつかのファイルの最終コンパイル時刻と最終更新時刻を比較します。何らかの変更が検出されると、MAKEは実行ファイルを削除し、コンパイラに新しい実行ファイルを作らせます。今のところ、MQL5にはそのような機会はないが、もしかしたら将来、開発者が追加することになるかもしれません。
以上の説明はすべて、私たちに何ができるか、どう行動すべきか、そして将来どう行動するかを示しています。しかし、指標からデータを読み取る機能はまだありません。
コミュニケーションの非常にシンプルなバージョンを1つ実装してみましょう。このことを一貫して詳しく説明するために、新しいトピックに移りましょう。
指標からデータを読み込む
実際、指標に表示された情報を読み取る方法はただ1つ、そのバッファを読み取ることです。CopyBuffer関数は、バッファに存在するあらゆるタイプのデータを扱うことができないからです。自然な形ではありません。この関数の宣言を見れば、私が何を言いたいかわかるでしょう。
CopyBuffer関数を宣言します。
int CopyBuffer( int indicator_handle, // indicator handle int buffer_num, // number of indicator buffer int start_pos, // starting position int count, // amount to copy double buffer[] // destination array to copy );
より明確にするために、CopyBuffer関数のバリエーションの1つがどのように宣言されているかを上で見てみましょう。バリエーションと言ったのは、3つあるからですが、本当に気になるのは、宣言の最後の変数です。型に注目してください。doubleです。つまり、double型の値しか返せません。これは理論上の話であって、実際には何でも返すことができるからです。前回の「一からの取引エキスパートアドバイザーの開発(第17回):Web上のデータにアクセスする(III)」ではこの制限を回避する方法を紹介しました。
このシークエンスでも、似たようなものを使いますが、レベルは違います。おそらく、初心者にはもう少し大きくて複雑なものになるでしょう。ただし、考え方は前述の記事と同じでしょう。より詳細が必要な場合は、私たちが何をするか正確に理解するために、言及された記事を参照してください。ここでは単にそうするだけで、詳細には触れません。
これは私たちにとってすでに馴染みのあることです。指標のソースコードを以下のように変更してみましょう。注:何がおこなわれるのかを理解できるよう、徐々に進めていきます。
指標のソースコード:
01. #property copyright "Daniel Jose" 02. #property link "" 03. #property version "1.00" 04. #property indicator_chart_window 05. #property indicator_plots 0 06. #property indicator_buffers 1 07. //+------------------------------------------------------------------+ 08. #include <Mode Swap\Defines.mqh> 09. //+------------------------------------------------------------------+ 10. #define def_ShortNameTmp def_ShortName + "_Tmp" 11. //+------------------------------------------------------------------+ 12. input double user00 = 0.0; 13. //+------------------------------------------------------------------+ 14. long m_id; 15. double m_Buff[]; 16. //+------------------------------------------------------------------+ 17. int OnInit() 18. { 19. m_id = ChartID(); 20. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortNameTmp); 21. if (ChartWindowFind(m_id, def_ShortName) != -1) 22. { 23. ChartIndicatorDelete(m_id, 0, def_ShortNameTmp); 24. Print("Only one instance is allowed..."); 25. return INIT_FAILED; 26. } 27. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName); 28. Print("Indicator configured with the following value:", user00); 29. 30. SetIndexBuffer(0, m_Buff, INDICATOR_CALCULATIONS); 31. ArrayInitialize(m_Buff, EMPTY_VALUE); 32. 33. return INIT_SUCCEEDED; 34. } 35. //+------------------------------------------------------------------+ 36. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 37. { 38. m_Buff[rates_total - 1] = user00 * 2.0; 39. 40. return rates_total; 41. } 42. //+------------------------------------------------------------------+
上に示したソースコードと記事の冒頭で紹介したソースコードを比較すると、わずかな違いに気づくでしょう。さっきも言ったように、ゆっくり進みます。実は、06行目で指標がバッファを持つように変更されています。このバッファは指標の外側に表示されます。それについては後にお話しします。まず、指標の中で何が起こっているのかを把握しましょう。
次に、15行目にグローバル変数の宣言があります。ただし、この変数は、指標コードまたはそれに関連する部分からしか見えません。この変数はバッファであり、初期化が必要です。これは30行目と31行目でおこなわれます。これでバッファが初期化され、何らかの情報を持って利用できるようになりました。
しかし、それを読んでも役に立たないデータしか得られません。データをバッファにロードする効率的な方法が必要です。これにはさまざまな方法がありますが、技術的には、すでに使われている方法が最も安全で良いです。このため、38行目としました。
この行で、私はバッファのどのエリアも指していないことにお気づきでしょうか。なぜでしょうか。
どうにかしてプロセスを標準化する必要があるからです。この場合、EAから報告された値に2.0を掛けた結果ですが、このような情報を並べると、本当に有益な情報はどこにあるのかわからなくなります。物事を冷静に理解しようとしますが、将来的にはすべてがより複雑になるでしょう。
そのため、常に同じ位置に情報が配置されるよう、独自の指標計算システムを採用しています。
それを明確にするために、以下のEAのソースコードを見てみましょう。
EAのソースコード:
01. #property copyright "Daniel Jose" 02. #property link "" 03. #property version "1.00" 04. //+------------------------------------------------------------------+ 05. #include <Mode Swap\Defines.mqh> 06. //+------------------------------------------------------------------+ 07. #define def_SWAP "Indicators\\Mode Swap\\Swap MSG.ex5" 08. #resource "\\" + def_SWAP 09. //+------------------------------------------------------------------+ 10. input double user00 = 2.2; 11. //+------------------------------------------------------------------+ 12. int m_handle; 13. long m_id; 14. //+------------------------------------------------------------------+ 15. int OnInit() 16. { 17. double Buff[]; 18. 19. m_id = ChartID(); 20. 21. if ((m_handle = ChartIndicatorGet(m_id, 0, def_ShortName)) == INVALID_HANDLE) 22. { 23. m_handle = iCustom(NULL, PERIOD_CURRENT, "::" + def_SWAP, user00); 24. ChartIndicatorAdd(m_id, 0, m_handle); 25. } 26. 27. Print ("Buffer reading:", (m_handle == INVALID_HANDLE ? "Error..." : CopyBuffer(m_handle, 0, 0, 1, Buff) > 0 ? (string)Buff[0] : " Failed.")); 28. 29. return INIT_SUCCEEDED; 30. } 31. //+------------------------------------------------------------------+ 32. void OnDeinit(const int reason) 33. { 34. ChartIndicatorDelete(m_id, 0, def_ShortName); 35. IndicatorRelease(m_handle); 36. } 37. //+------------------------------------------------------------------+ 38. void OnTick() 39. { 40. } 41. //+------------------------------------------------------------------+
コードは以前に紹介したものとあまり変わっていないことに注意してください。指標はリソースとして使用されるため、EAをコンパイルした後、その実行ファイルを削除することができます。コードに追加されたものを見てみましょう。
まず、17行目に変数宣言があります。これは、リターンバッファまたは指標のリーディングバッファとなります。この17行目に加えて、27行目も追加しました。
多くの人はこの27行目を本当のカオスと感じるかもしれませんが、これは2つの入れ子になった3項演算子に過ぎません。まず、指標ハンドルが有効かどうかを確認します。もし有効でなければ、端末のメッセージウィンドウに警告が表示されます。ハンドルが有効であれば、指標バッファからポジションを1つ読み取ります。どのポジションを読むのでしょうか。1番目のものです。わかりにくいでしょうか。落ち着いて、リラックスしてください。後で全部説明します。バッファの読み込みに成功したら、その値を表示します。そうでない場合は、別のエラーメッセージが表示されます。それは最初のものとは異なっており、失敗の理由が異なることを示しています。
では、なぜ27行目に示されているようにバッファを読み込むと、最初の位置が読み込まれ、最後の位置が読み込まれないのかを見てみましょう。
バッファの書き込みと読み出しプロセスについて
以下はMQL5のドキュメントからのイメージです。これから説明することを明確に示しています。ご覧ください。
図01:指標バッファの書き込みと読み出し
この画像は、何が起こっているかを明確に説明しています。とにかく、データがどのように指標バッファに書き込まれ、どのようにそこから読み出されるかを詳しく見てみましょう。
指標バッファに書き込む場合、常に特定の位置に書き込むことが重要です。新しいバーが現れると、新しいポジションが自動的にバッファに現れます。問題は、各時間枠でバッファサイズが異なることです。
どこに問題があるのかと思うかもしれません。問題は1つではなく、いくつもあります。まずはじめに、指標で、次のコードを使ってゼロの位置に書き込むことにしたとします。
コードスニペット - モデル 01:
35. //+------------------------------------------------------------------+ 36. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 37. { 38. m_Buff[0] = user00 * 3.0; 39. 40. return rates_total; 41. } 42. //+------------------------------------------------------------------+
前のトピックで説明した指標コードの行をこのコードに変更すると、38行目にあるように、文字通りバッファのゼロ位置に書き込むことになります。しかし、この方法には問題があります。
問題は書き込み時ではなく、例えばEAで指標コードの外にあるバッファを読み込もうとした時に現れます。
バッファを読み込む際、MetaTrader 5は通常最大2000ポジションを返します。図01のようになります。万が一、指標に存在するデータ量がこの2000ポジションを超えると、問題が生じます。ゼロ位置に書き込みましたが、そのゼロ位置はCopyBufferが参照するゼロ位置と同じではありません。CopyBufferの場合、指標コードにあるこのゼロの位置は、実際には2001の位置である可能性があり、その場合、指標バッファからゼロの位置を読み取ることはできません。その値は指標で固定されます。
このため、ゼロ位置への書き込みはおこなわず、バッファの終端位置から書き込みを開始します。まだはっきりしないでしょうか。
指標バッファのゼロ位置は、常に「rates_total - 1」の位置とみなされるべきです。これは、前のトピックで説明した指標のコードで記述する位置です。このため、CopyBufferを使って指標バッファを読み込んだ場合、その値を出力する際には、実際にはゼロインデックスを使うことになります。
おそらく、これはまだ完全には明らかではありません。わかりやすくするために、別のコード例を見てみましょう。このコード例では、指標にデータを渡しますが、以前のように1つの値だけではなく、複数の値を返します。そのうちのひとつは単純な文字列です。
注目すべきは、バッファへの書き込みは、逆順でも、通常の方法でも、どちらかといえば、第一の情報-第一の値、第二の情報-第二の値という順番で書き込めることです。逆の場合は、1つ目の情報-最後の値、2つ目の情報-最後の値から1つポジションを引いた値、というようになります。書くときではなく、読むときに解釈しやすくするために、通常の方法で物事を進めましょう。
まずヘッダーファイルを変更してみましょう。
.mqhファイルのソースコードを定義します。
01. #property copyright "Daniel Jose" 02. //+------------------------------------------------------------------+ 03. #define def_ShortName "SWAP MSG" 04. //+------------------------------------------------------------------+ 05. union uCharDouble 06. { 07. double dValue; 08. char cInfo[sizeof(double)]; 09. }; 10. //+------------------------------------------------------------------+
ご覧のように、05行目と09行目の間にユニオンがあります。このユニオンは、double型の値を使ってテキストタイプのデータを渡すことを可能にします。初めてこれをご覧になる方は、奇妙に思われるかもしれません。でも、これは前にもやったことです。その一例が「一からの取引エキスパートアドバイザーの開発(第17回):Web上のデータにアクセスする(III)」稿にあります。しかし、質問に戻りましょう。これで、指標からEAに小さな文字列を送信する方法ができました。double値を使う理由は、CopyBufferを通して別の型の値を送ることができないからです。double型を使わなければなりません。
Defines.mqhファイルにこの変更を加えた後、指標のソースコードに移ります。
指標のソースコードを更新し、複数のポジションへの書き込みを可能にします。
01. #property copyright "Daniel Jose" 02. #property version "1.00" 03. #property indicator_chart_window 04. #property indicator_plots 0 05. #property indicator_buffers 1 06. //+------------------------------------------------------------------+ 07. #include <Mode Swap\Defines.mqh> 08. //+------------------------------------------------------------------+ 09. #define def_ShortNameTmp def_ShortName + "_Tmp" 10. //+------------------------------------------------------------------+ 11. input double user00 = 0.0; 12. //+------------------------------------------------------------------+ 13. long m_id; 14. double m_Buff[]; 15. //+------------------------------------------------------------------+ 16. int OnInit() 17. { 18. m_id = ChartID(); 19. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortNameTmp); 20. if (ChartWindowFind(m_id, def_ShortName) != -1) 21. { 22. ChartIndicatorDelete(m_id, 0, def_ShortNameTmp); 23. Print("Only one instance is allowed..."); 24. return INIT_FAILED; 25. } 26. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName); 27. Print("Indicator configured with the following value:", user00); 28. 29. SetIndexBuffer(0, m_Buff, INDICATOR_CALCULATIONS); 30. ArrayInitialize(m_Buff, EMPTY_VALUE); 31. 32. return INIT_SUCCEEDED; 33. } 34. //+------------------------------------------------------------------+ 35. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 36. { 37. uCharDouble info; 38. int pos = rates_total - 3; 39. 40. StringToCharArray("Config", info.cInfo); 41. m_Buff[pos + 0] = info.dValue; 42. m_Buff[pos + 1] = user00 * 2.0; 43. m_Buff[pos + 2] = user00 * 2.5; 44. 45. return rates_total; 46. } 47. //+------------------------------------------------------------------+
上のコードを詳しく見てみましょう。37行目からだけ、冒頭で紹介したコードに関連していくつかの変更があります。なぜそのようなことが起こるのでしょうか。これからCopyBufferを通して返されるより広範な情報を組み立てるからです。コードの他の部分を修正する必要はなく、37行目から始まるコードだけを修正すれば大丈夫です。
さて、何が起こっているのか考えてみましょう。37行目では、文字列をdouble値に変換するための変数を宣言しています。文字列の長さは8文字までであることに注意してください。情報にさらに文字が含まれている場合は、常に8ブロックの情報を考慮して、配列を提供する必要があります。
38行目では、通常の方法で、つまりテキストを左から右に書くように情報を書き込むために使う変数を宣言しています。アラビア語の場合、書くのは右から左へです。考え方は明確だと思います。同じ行38に、掲載するdouble値の数を示します。私たちの場合、3つの値があります。
40行目では、文字列を文字の配列に変換しています。これで、最初のポジションで使われる値が得られます。そして41行目で、この値をバッファに入れます。
42行目と43行目では、読み込むデータを得るために簡単な計算をおこないます。こうして、バッファ内のより多くのポジションにアクセスする必要があるときに、読み出しがどのようにおこなわれるかを示すことができます。
基本的には指標の問題に尽きます。この瞬間、コミュニケーションについてこれ以上議論することはありません。それでは、指標に作成されたバッファを認識し、読み取る方法を説明します。そのために、すでに更新されたEAのコードを以下に見てみましょう。
EAのソースコードが更新され、複数のポジションを読み込めるようになりました。
01. #property copyright "Daniel Jose" 02. #property version "1.00" 03. //+------------------------------------------------------------------+ 04. #include <Mode Swap\Defines.mqh> 05. //+------------------------------------------------------------------+ 06. #define def_SWAP "Indicators\\Mode Swap\\Swap MSG.ex5" 07. #resource "\\" + def_SWAP 08. //+------------------------------------------------------------------+ 09. input double user00 = 2.2; 10. //+------------------------------------------------------------------+ 11. int m_handle; 12. long m_id; 13. //+------------------------------------------------------------------+ 14. int OnInit() 15. { 16. double Buff[]; 17. uCharDouble Info; 18. int iRet; 19. string szInfo; 20. 21. m_id = ChartID(); 22. if ((m_handle = ChartIndicatorGet(m_id, 0, def_ShortName)) == INVALID_HANDLE) 23. { 24. m_handle = iCustom(NULL, PERIOD_CURRENT, "::" + def_SWAP, user00); 25. ChartIndicatorAdd(m_id, 0, m_handle); 26. } 27. ArraySetAsSeries(Buff, false); 28. if (m_handle == INVALID_HANDLE) szInfo = "Invalid handler to read the buffer."; 29. else 30. { 31. if ((iRet = CopyBuffer(m_handle, 0, 0, 3, Buff)) < 3) szInfo = "Buffer reading failed."; 32. else 33. { 34. Info.dValue = Buff[0]; 35. szInfo = CharArrayToString(Info.cInfo) + " [ " + (string)Buff[1] + " ] [ " + (string)Buff[2] + " ]"; 36. } 37. } 38. Print("Return => ", szInfo); 39. 40. return INIT_SUCCEEDED; 41. } 42. //+------------------------------------------------------------------+ 43. void OnDeinit(const int reason) 44. { 45. ChartIndicatorDelete(m_id, 0, def_ShortName); 46. IndicatorRelease(m_handle); 47. } 48. //+------------------------------------------------------------------+ 49. void OnTick() 50. { 51. } 52. //+------------------------------------------------------------------+
上記の更新されたEAコードは、最初に見たものと大きな違いはありません。しかし、ここには説明に値する新しい要素がまだいくつかあります。
17行目と19行目の間には、バッファに含まれる情報を解読するために使用される新しい変数の宣言があります。これは奇妙に思えるかもしれませんが、実際にはバッファに存在する情報はエンコードされています。いずれかの位置に文字列を渡すためです。
本当に興味深いのは27行目と38行目の間です。ここでバッファが読み込まれます。では、この部分から始めましょう。
27行目には、複数のバッファポジションを読み込んでいるときでも、読み込み時に時々見られるコードが含まれています。これは、デフォルトでは読み込みが直接おこなわれるからです。図01を見れば、配列が直列になっていることがわかります。しかし、読む順序が逆になる瞬間があります。次に、配列へのアクセスに逆インデックスを使う代わりに、27行目で定義した関数を使い、逆順に読み込むように指定します。この場合、見た目に反して、関数に渡される値はfalseになります。こうすることで、アクセスインデックスをダイレクトアクセスのように使うことができます。
直接書いているので、27行目は現在のコードではあまり意味をなさないかもしれませんが、先に述べたことを説明するために追加しました。
その他の行については、その多くが自明であるため、詳しく説明する必要はありません。しかし、31行目には少し考えなければならないことがあります。
指標内部のバッファにデータを書き込むとき、送信する情報のポジション数がわかります。31行目はまさに、予想されるポジション数を読み取ります。ヘッダーファイルを使ってより良い組み合わせを作る方が適しているでしょう。そのため、情報を含むポジションの数が増えれば、EAも指標も常にそれを意識することになります。しかし、これらは後に使われる概念を説明するための例に過ぎないので、今は無視して構いません。
読み取られたポジションの数が予想より少ない場合はエラーとなり、端末に関連情報が表示されます。期待通りの数字であれば、34行目に進み、doubleとして提供された情報を文字列に変換します。そこで、指標がバッファに入れた情報を取り戻します。
この場合のインデックスは、指標で指定されている通り、ゼロであることに注意してください。もし逆順の書き出しが指標でおこなわれたとしても、EAで同じインデックスを使用することができます。27行目の値を変更するだけで、EAは同じインデックス方法で情報を理解します。
物事が実際にどのように起こるかを理解するために、これを試してみてください。これらの詳細を理解することは、本連載の次の記事を理解する上で重要です。
変換が完了したら、35行目を使用してターミナルに表示されるメッセージを生成します。それだけです。
結論
今回はリプレイ/シミュレーションシステムプロジェクトで少し脱線して、今後の記事で取り上げる内容の基本を示しました。
これは、私たちを待ち受けているすべてのことのほんの一部に過ぎません。リプレイ/シミュレーターシステムの一部として、まだ追加していないもの、実装していないものがあります。ここで紹介した内容はすべて、今後の記事にとって非常に重要なものです。指標と他のプロセス間のデータ転送の基本を理解していないと、迷子になりかねません。ユーザーが指標を削除できないように、指標ウィンドウから指標を非表示にする方法はまだ紹介していません。また、さまざまな組み合わせで理解を著しく複雑にするような要素も追加していません。
この3つの記事で提示されたコンセプトと本当の意味を理解してください。その後、すべてがもっと複雑になります。
一種の機械的記憶を身につけてほしいので、この記事に添付ファイルはありません。ここで紹介されている基本的なことを実験して学びたいのであれば、コードを書かなければなりません。コードは非常に短いので、これには何の問題もないはずです。
次回は、リプレイ/シミュレーターシステムで使用するためのChart Traderの統合を開始します。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/11599





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索