
一からの取引エキスパートアドバイザーの開発(第31部):未来に向かって(IV)
はじめに
「一からの取引エキスパートアドバイザーの開発(第29部)」でEAからChart Tradeを削除した後、Chart Tradeパネルを指標化しました。その方法と、指標を動作させるために必要な機能の調整・メンテナンス方法については、第30回に記載しています。これは可能なアプローチの1つで、実際には他にもメリット・デメリットのある方法がありますが、それはまた別の機会に考えたいと思います。
EAから削除するものがまだ残っているので、今回削除し、この記事が本連載の最終回となる予定です。削除するのは、サウンドシステムです。過去の記事を追っていない方は混乱されるかもしれません。
全体の流れがどうなるかを理解するために(説明が必要なものが多く含まれているので)、前回の記事を基にほぼ同じモデルで説明します。そうすることで、プロのプログラマーでない方でも、説明がわかりやすなります。また、ちょっとした刺激として、システムを少し複雑にする予定です。
今回の新しい部分はサウンドシステムです。これはEAの一部でなくなります。これは将来的に多くの利益をもたらすでしょう。ただし、大事なのはここで何が起こるかを理解することなので、焦らずにいきましょう。比較的短いですが、興味深い記事になると思います。
サウンドサービスの導入
このような変更ばかりでは、気が狂いそうになりますが、信じてください。このアイデアは、気を狂わせないとしても少し困惑させるかもしれませんが、時には小さな変化が大きな違いを生み、MetaTrader 5プラットフォームの使用をより楽しくすることを示すためのものです。同時に、これらの動作がいかに物事の変調を可能にするかということもおわかりいただけると思います。
こうすることで、必要なものとそうでないものを選ぶことができるのです。本当に使われているものであれば、後から改良を加えて、さらに便利で楽しい機能にすることができるはずです。少し前に作ったものを大きく変更したり、プログラムを作り直したりする必要はありません。アイデアは、常に再使用するということです。
その1つが、サウンドシステムです。このシステムをEA内に残しておくのは良いアイデアだと思われるかもしれません。ある意味、EA全体の機能に支障をきたさないシステムです。しかし、これをEAから外し、その間に何らかの通信を実装すれば、あたかもサウンドライブラリのように、非常にシンプルにサウンドアラートを利用できることがおわかりいただけると思います。この解決策は非常に有効でしょう。
EAの中だけにアラートシステムを設置しても意味がありません。指標や、特定の時間に実行されるスクリプトでも、サウンドシステムがあると便利です。これは分析に非常に役立つと思います。このように、MetaTrader 5プラットフォームは、ポジションのエントリや決済など、非常に特定の瞬間に市場をよりよく分析するために膨大な計算をおこなうことができ、分析の面で真のモンスターとなることができます。これらはすべて、最小限の労力でおこなうことができます。
「でも、すべてのサウンドをMQHファイル(ヘッダーファイル)に追加して実行ファイルに埋め込めば、必要な動作を得ることができます。」と言う人もいるかもしれません。 確かにできますが、次のようなシナリオを考えてみてください。時間の経過とともに、このMQHファイルは大きくなり、それに伴い、一部の古いプログラムはこのヘッダーファイル(MQH)と互換性がなくなる可能性があります。このような古いファイルを再コンパイルする場合、問題が発生します。また、プロセス間に通信プロトコルが存在するモジュール式のシステムを作れば、古いプログラムとの互換性を保ちながらプラットフォームの機能を拡張することができます。
これが、今回の変更の理由です。可能な限りのパスを作成し、それを使用する方法を示すということです。そして、できるだけ元の動作に近づけながら、EAからいろいろなものを取り出してこれを示しています。
前回は、Chart Tradeを再作成して、EAに組み込んだときと同じ動作をさせる方法を紹介しました。しかし、EAから外したことで、同じモードで動作し続けるための工夫が必要になりました。今回ご紹介した方法は、数ある方法のうちの1つで、ベストな方法とは言えませんが、効果はあります。あらゆる解決策には、一般的な物事の仕組みを正しく理解することが必要です。1つのアイデアモデルに縛られることが、具体的な状況解決につながらないこともありますし、それとは全く逆です。知識がないために、「できない」と思ったり、「仕組みに限界がある」と言ったりしますが、実はその限界は、解決策を企画・実行する責任者の知識不足にあるのです。
これは、データを格納する構造体を使わずに受注システムを実装したときに見られました。多くの人が、そんなことできるわけがないと思っていましたが、私はそれが可能であることを示したのです。大切なのは、自分が何をやっているのかを知り、理解することです。まず、それぞれの解決策の限界を知ることが大切です。
そこで、システムがさらに成長するにつれて機能を拡張していくことを念頭に、サウンドシステムを可能な限りモジュール化する方法を学びましょう。
まず、C_Soundクラスは、機能拡張が必要な場合を除き、触らないことにするので、このクラスで大きな変化はないでしょう。実は、この段階では、このクラスはそのままなのですが、システムにちょっとした追加をする必要があります。その第一弾が、以下に示すヘッダーファイルです。
#property copyright "Daniel Jose" //+------------------------------------------------------------------+ #define def_GlobalVariableAlert "Sound Alert" //+------------------------------------------------------------------+
このファイルをEAで使うのかと思われるかもしれませんが、違います...EAでは少なくともまだこのファイルを使わず、後で見る別のファイルを使用します。
その後、サウンドサービスとなるファイルを作成します。以下のコードに示します。
#property service #property copyright "Daniel Jose" //+------------------------------------------------------------------+ #include <NanoEA-SIMD\Auxiliar\C_Sounds.mqh> #include <NanoEA-SIMD\Interprocess\Sound.mqh> //+------------------------------------------------------------------+ void OnStart() { union u00 { double value; struct s00 { uint i0, i1; }info; }u_local; C_Sounds Sound; while (!IsStopped()) { Sleep(500); if (GlobalVariableGet(def_GlobalVariableAlert, u_local.value)) { GlobalVariableDel(def_GlobalVariableAlert); if (u_local.info.i1 == 0) Sound.PlayAlert((C_Sounds::eTypeSound)u_local.info.i0); else Sound.PlayAlert(u_local.info.i1); } } } //+------------------------------------------------------------------+
MetaTrader 5のグローバル変数を監視するサービスです。ヘッダーファイルで宣言されている名前の変数が、スクリプト、EA、指標のいずれによって起動されると、それが何であっても、いつであっても、指定したサウンドを再生します。
再生するファイルのインデックスを指定するだけです。上記の構造体から、外部ファイルのみの数である合計4,294,967,295種類のサウンドを再生できることになります。内部サウンドの数が同じだから、いろいろなことができるのです。
システムは再生するサウンドの種類を知るために、変数 u_local.info.i1を確認します。もし値が0であれば、再生するサウンドはサービスファイルに埋め込まれ、サウンドのインデックスがu_local.info.i0変数で示されますが、この値はC_Soundクラス内の列挙子を表しています。
これで、サービスをコンパイルして実行することができます。上記の条件が満たされた時点で、サービスはその作業を実行しますが、サービスによってグローバル変数がキャプチャされると、別の機会に使用できるように削除されることを忘れません。
進む前に、少し考えてみましょう。EAとしか通信しないChart Trade指標とは異なり、サウンドシステムはMetaTrader 5プラットフォームのあらゆる種類のプログラムと通信することができます。希望のサウンドを再生するには、変数の値を設定する必要がありますが、その値は常に2倍となります。
簡単だと思うかもしれませんが、やってみるとそうでもないことがわかります。さらに、毎回正しい名前でグローバル変数を作成し続けなければならなくなるため、以前に保存したサウンドを再生するたびに、多くの作業をおこなう必要があります。
ただし、このような手間を省く現実的な解決策があります。かなりいい感じなので、この初期段階では、この解決策を最も基本的なレベルで使用することにします。その方法を見るために、次のトピックに移りましょう。
サウンドサービスにアクセスするためのライブラリを作成する
ライブラリを作るのは、それが何らかの形で便利だからです。方法は問いませんが、便利になります。前回のトピックでは、プログラムがサウンドサービスにアクセスするとき、サービスへのアクセスを与えるグローバル変数の名前を知る必要はないと述べました。奇妙に聞こえるかもしれませんが、プロセス間で情報を受け渡すには、システムに層を追加するのが一番です。この層がライブラリです。
これらのライブラリは、データモデリングの複雑さをプロセス間で「隠す」ので、どのような形でモデリングをおこなうべきかを心配する必要がなくなります。呼び出しそのものと期待される結果についてだけ気をつければいいのです。
ライブラリを作成する際の注意点は2つだけです。
- エクスポートされる関数を明確に宣言する。
- 内部モデリングの複雑さをできるだけ隠し、ライブラリの利用者が何が起こっているかを知る必要がないようにする。ユーザーは、入ってくるデータと出てくる結果だけを見ればいいため、
ライブラリ内のプロシージャや関数は、ユーザーから見て非常にシンプルな動作になるように設計されています。ただし、内部では、最終的な結果に至るまで、非常に複雑なレベルの操作がおこなわれていることがありますが、そのライブラリを利用するプログラマーは、その内部で何が起こっているかを知る必要はありません。結果が正しく提供されているかどうかが重要です。
それでは、サウンドサービスで使用するデータモデリングを隠蔽するライブラリを見てみましょう。1つ目は内部音声か外部サウンドか、2つ目はサウンドのインデックスです。複雑そうでしょうか。これらの呼び出しをライブラリ内部で行っているコードを見てみましょう。
void Sound_WAV(uint index) export { Sound(0, index); } void Sound_Alert(uint index) export { Sound(index, 0); }
この2つの機能は、データモデリングにおけるあらゆる複雑性を隠します。キーワードexportは、これらの関数へのシンボリックリンクを作成するようにコンパイラに指示するものであることに注意してください。これらは値を返さないので、実際にはプロシージャです。このようにすると、このファイルがあたかもDLLであるかのように、ファイルの外側から見えるようになりますが、
コードを見てみると、Sound関数は見当たりません。どこにあるでしょうか。ライブラリ自体にありますが、その外には表示されません。ライブラリの全コードは以下をご覧ください。
//+------------------------------------------------------------------+ #property library #property copyright "Daniel Jose" //+------------------------------------------------------------------+ #include <NanoEA-SIMD\Interprocess\Sound.mqh> //+------------------------------------------------------------------+ void Sound_WAV(uint index) export { Sound(0, index); } //+------------------------------------------------------------------+ void Sound_Alert(uint index) export { Sound(index, 0); } //+------------------------------------------------------------------+ void Sound(uint value00, uint value01) { union u00 { double value; struct s00 { uint i0, i1; }info; }u_local; u_local.info.i0 = value00; u_local.info.i1 = value01; GlobalVariableTemp(def_GlobalVariableAlert); GlobalVariableSet(def_GlobalVariableAlert, u_local.value); } //+------------------------------------------------------------------+
サウンドプロシージャは、スクリプト、指標、またはEAが要求するタスクを、サービスが実行できるように、適切な値を組み立てるために必要なすべての複雑さを含んでいることに注意してください。しかし、このコードをサービスにアクセスするプログラム内に配置するのではなく、簡略化した呼び出しのみを使用することで、プログラムのデバッグをより快適に、より疲れにくくすることができます。
この仕組みを理解するために、スクリプトの例を見てみましょう。
#property copyright "Daniel Jose" #property script_show_inputs #import "Service_Sound.ex5" void Sound_WAV(uint); void Sound_Alert(uint); #import //+------------------------------------------------------------------+ input uint value00 = 1; //Internal sound service... input uint value01 = 10016; //Sound in WAV file... //+------------------------------------------------------------------+ void OnStart() { Sound_WAV(value01); Sound_Alert(value00); } //+------------------------------------------------------------------+
上のコードを見てください。どのような通信がおこなわれ、いつどこで音のイベントが発生するのかを知る必要はなく、プラットフォーム内、OS内、あるいは遠隔地など、どこで発生してもかまいません。知らせる必要があるのは、音がシステムの内部か外部か、そしてそのインデックスだけです。
さて、話を続ける前に、1つ実験して、関数を入れ替えてください。この場合、Sound_WAV、Sound_Alertの順に実行します。実行し、その結果をご覧ください。次に、Sound_Alert、Sound_WAVという順序に変更して実行し、結果を確認します。わからない人のために説明すると、OnStartイベント内のコードは、最初の状況では次のようになります。
void OnStart() { Sound_WAV(value01); Sound_Alert(value00); }
そして、2つ目のケースでこのようになります。
void OnStart() { Sound_Alert(value00); Sound_WAV(value01); }
バカバカしいと思うかもしれませんが、この実験はいくつかのことを理解するために必要なのです。無視しないでください、面白い結果になります。
さて、サウンドを再生できるようにするために、プログラムに何を追加すればよいかを見てきましたが、次のコードを追加するだけです。
#import "Service_Sound.ex5" void Sound_WAV(uint); void Sound_Alert(uint); #import
音を鳴らす必要があるときは、仕組みを気にせず、適切な機能を適切な値で使えばいいのです。システムそのものが、すべてを完璧に動作させるのです。今回のEAでは、以下のようなコードになります。
// ... #import "Service_Sound.ex5" void Sound_WAV(uint); void Sound_Alert(uint); #import //+------------------------------------------------------------------+ #include <NanoEA-SIMD\Trade\Control\C_IndicatorTradeView.mqh> #include <NanoEA-SIMD\Interprocess\Sound.mqh> // ...
ここで、強調表示されたコードがそこで何をするのかという、疑問が生じます。単にライブラリを利用することはできないのでしょうか。できますが、以前のようにサウンドの場合は列挙型を使って数値コードを特定することができますし、よほど少ない数のサウンドやアラートを使っていない限り、コードを見ただけではそれぞれが何を表しているのかが非常にわかりにくくなることがあります。このため、ヘッダーファイルSound.mqhには、以下のコードで強調されている部分が追加されています。
#property copyright "Daniel Jose" //+------------------------------------------------------------------+ #define def_GlobalVariableAlert "Sound Alert" //+------------------------------------------------------------------+ enum eTypeSound {TRADE_ALLOWED, OPERATION_BEGIN, OPERATION_END}; //+------------------------------------------------------------------+
このようなコードになります。
int OnInit() { if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { Sound_Alert(TRADE_ALLOWED); return INIT_FAILED; } // ... The rest of the function
列挙の代わりにインデックスを使用した同じコードよりも、はるかに代表的なものです。
int OnInit() { if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { Sound_Alert(0); return INIT_FAILED; } // ... Rest of the code
どちらがわかりやすいですか。
このような作業を経て、下図のようなプラットフォームの情報の流れができあがります。
このように、誰がシグナルを供給しても、行き先は同じになるのです。
結論
大したことではないと思われるかもしれませんが、この記事で紹介したことは、コードやプログラム、情報の使い勝手を向上させるために大いに役立ちます。プログラミングの量が減り、生産性が上がると同時に、再利用とテストがさまざまな場面で繰り返されるため、コードの安全性と安定性が高まります。
ここでは、前回の記事とは異なる別のパスを見てきましたが、それでもこのパスはかなり改良することができ、新しい可能性をたくさん与えてくれますが、これは別の連載で見ることにします。そこでは、MetaTrader 5のプログラムやプロジェクトを、ここで紹介したどの方法よりもはるかに高いレベルのセキュリティ、使いやすさ、安定性を備えたモジュールにする方法を学びます。
ただし、最も重要なことは、ある解決策が他の解決策よりも優れている場合があるため、異なる解決策を設計し使用する方法を知ることです。
すべてのコードは、添付ファイルでご確認いただけます。このようなライブラリを使ったプログラミングにあまり慣れていない人は、EA開発のこの段階をよく勉強しておくことをお勧めします。今日できることを明日に延ばしてはいけません。明日は期待通りに来ないかもしれません。
今回の記事で、このEA開発段階は終わりです。近々、別のタイプの状況に焦点を当てた、より複雑であるがより興味深い資料を紹介する予定です。またお会いしましょう。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/10678





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