English Русский 中文 Español Deutsch Português
preview
自動で動くEAを作る(第10回):自動化(II)

自動で動くEAを作る(第10回):自動化(II)

MetaTrader 5トレーディング | 3 5月 2023, 10:22
763 0
Daniel Jose
Daniel Jose

はじめに

前回の「自動で動くEAを作る(第09回):自動化(I)」では、2つの異なるモードを使用するブレイクイーブンとトレーリングストップシステムを作成する方法について説明しました。1つはOCOポジションのストップラインを使用し、もう1つは指値注文をストップレベルとして使用します。先ほど説明したように、これらの方法にはそれぞれ長所と短所があります。

EAはかなり単純な自動化システムを使用し、計算をおこなうための詳細な指示を提供しますが、完全に自動化されているわけではありません。最初のレベルの自動化はすでにおこなわれていますが、依然として手動、より正確には「半手動」の方法で操作されています。これは、ユーザーが指定した設定に基づいて、ラインの移動またはストップ注文を担当する部分がEA自体によって実行されるためです。

この記事では、EAにもう1つのレベルの自動化を追加して、EAが使用されるチャートに常に留まるようにする方法を見ていきます。しかし、ご心配なく。これは、1日24時間継続的に注文を送信したり、ポジションを開いたりすることを意味するものではありません。EAによる過剰取引を防ぎ、最大許容量を超えて取引させないようにするために、システムの取引量を制御する方法を見ていきます。

ここで紹介する自動化のタイプは、トレーダーが常に取引しないようにする方法として、多くのプラットフォームが提供しているものです。注文の送信またはポジションの開始が許可される時間を定義し、この事前定義された時間の間、プラットフォームを基本的かつ理論的に使用できます。

システムを無効にできるため、その時点でのプラットフォームの使用は理論的なものであり、方法論全体が無駄になります。誰もが休憩を取るべき時を知っていますが、人間のトレーダーとは異なり、EAはこのようには動作しません。

事前定義された方法論内で機能するように、EAに追加または削除するすべてのものを再確認する必要があることを指摘しなければなりません。ただし、EAを監視なしで実行させてはなりません。常に覚えておいてください。監視なしでEAの実行を許可しないでください

スケジュール管理を実装する方法

これにはさまざまな方法があります。このタイプの制御の実装は、どの種類のプログラミングをおこなう必要があるかよりも、プログラマーがそれをEAコードにどのように配置したいかによります。これらの記事の目的は、自動化されたEAを可能な限り簡単に作成する方法を示すことであるため、簡単に削除できるコードを作成します。同時に、独自の取引方法を制御したい場合は手動EAで使用できますが、これはユーザーが決定する必要があります。 


計画

開発システムは、各トレーダーが特定の概念を促進するためのさまざまな方法や手段、さらにはシステム自体がどのように機能するかについて考えるようになるという点で興味深いものです。結果が非常に似ていたり、類似していたりしても、実装方法が異なる場合があります。

しかし、MQL5で私が欲しいのは、いわゆる多重継承という、C++に存在するオブジェクト指向プログラミングの形式です。ただし、この方法論を誤って適用すると、深刻な問題が発生する可能性があるので、多重継承を使用する場合は、プログラミング時に十分に注意する必要があります。このC++機能がなくても、継承システム内に物事を保持するいくつかのタイプのコードを生成できます。

多重継承を使用する場合と使用しない場合の違いを理解するには、下の画像をご覧ください。C_ControlOfTimeは、EAを実行できる期間を制御するために使用するクラスの名前です

図01

図01:多重継承を使用したモデリング

図02

図02:多重継承を使用しないモデリング

図01と図02の違いは、最初の図では、C_ManagerクラスがC_OrdersクラスとC_ControlOfTimeクラスに実装されたメソッドを継承によって派生させていることです。これにより、C_Managerクラスが急速に成長します。MQL5ではそれをおこなうことができないため、図02に示す別の方法を使用します。クラスC_ControlOfTimeは、C_Ordersから継承されています。

反対にしないのは、EAがC_Ordersクラスに直接アクセスできないようにするためです。ただし、C_ControlOfTimeクラスの実装にアクセスする必要があります。プログラミングの最も良い点は、多くの場合、さまざまなアプローチを取ることができますが、最終的には他のプログラマーが作成できるのとまったく同じ機能性になることです。

ここで紹介するのは、同じ結果を得るために考えられる多くの方法の1つにすぎません。本当に大事なのは結果です。コードの整合性が保たれている限り、これをどのように達成するかは問題ではありません。プログラミングによりこれが可能になるため、特定のものを実装する手法と方法は、独自に作成できます。


実装前に確認するべき詳細

図02に示すクラスモデリングを使用するアイデアを定義した後、タイムスロット制御クラスを作成する第2の計画段階に進みました。

次に、作業時間範囲を決定する方法、つまりトレーダーがこのスケジュールを簡単に設定する方法を定義する必要があります。1つの方法は、EAが固執する時間範囲データを含むファイルを使用することです。

ただし、ファイルの使用は、この種のことではかなり物議を醸しています。ファイルを使用することで、トレーダーが同じ日に複数の期間を定義する自由度を高めることができます。これは場合によっては合理的かもしれませんが、単純なタスクをより困難にする可能性があります。EAは特定の期間でパフォーマンスが低下する可能性があり、非常にストレスになります。

一方、スケジュールがファイルで定義される方法は、ほとんどのトレーダーにとってこのような単純なタスクを複雑にする可能性があります.これは、EAが1つの期間内でのみ機能する場合があるためです。

ほとんどの場合、これは見かけよりもはるかに一般的な事実です。少し改善して、中間にとどまるようにします。MetaTrader 5プラットフォームでは、必要な設定をファイルから保存および読み込みできます。したがって、特定の期間の構成を作成するだけで済みます。たとえば、ある構成を午前中に使用し、別の構成を午後に使用することができます。時間制御システムによってEAをブロックするには、たとえば、トレーダーが少し休むとき、+トレーダーは設定ファイルをMetaTrader 5プラットフォームに直接アップロードでき、構成はプラットフォーム自体によって維持されます。この目的のためだけに追加の構成ファイルを作成する手間が省けるので、非常に役立ちます。

図03

図03:EAの設定

図03にEA設定システムを示します。EAを設定したら、<SAVE>ボタンを使用して設定を保存できます。保存した構成をアップロードする必要がある場合は、<OPEN>ボタンを使用します。したがって、コード全体を非常に信頼性の高いものにしながら、必要なコードがはるかに少ないシステムが得られます。一部の作業はMetaTrader 5プラットフォーム自体によっておこなわれるため、すべてが正しく機能することを確認するためのテストを軽減できます。

決定するべき最後の詳細は、期間外の注文またはポジションに関するものです。それらに対して何ができるでしょうか。EAは、ポジションを開く注文を送信したり、許可された時間間隔外に注文を出すことができません。ただし、作業範囲外では、EAは注文を管理したり、サーバー上に既にあるポジションを決済したりできます。必要に応じて、私が使用したポリシーを無効にするか変更してください。読者自身の取引方針に従って設定してください。


C_ControlOfTimeクラスの誕生

C_ControlOfTime.mqhヘッダーファイル内で最初におこなうことは、次のコードを作成することです。

#include "C_Orders.mqh"
//+------------------------------------------------------------------+
class C_ControlOfTime : protected C_Orders
{
        private :
                struct st_00
                {
                        datetime Init,
                                 End;
                }m_InfoCtrl[SATURDAY + 1];
//+------------------------------------------------------------------+
        public  :

//... Class functions ...

};

C_Orders.mqhヘッダーファイルを追加して、C_Ordersクラスにアクセスできるようにします。したがって、C_ControlOfTimeは、保護されたメソッドを使用してC_Ordersクラスから継承されます。 このタイプの継承を使用した結果については、本連載の別の記事「自動で動くEAを作る(第05回):手動トリガー(II)」で既に説明しました。

ここで、制御クラスコードのprivate部分内に、7つの要素を持つ配列として使用される構造体を追加します。しかし、そのクレイジーなステートメントを使用する代わりに、なぜ7を定義しないのでしょうか。これは、SATURDAYの値がMQL5言語でENUM_DAY_OF_WEEK列挙の値として内部的に定義されているためです。配列へのアクセスに曜日を使用する理由が明確です。

コードを読む人にとって、単語は数値よりも表現力があるため、これは言語レベルのエンリッチメントです。この構造には2つの要素しかありません。1つはEA操作の開始点を示し、もう1つはEAが機能しなくなる時点を示し、どちらもdatetime型です。

配列が定義されたら、クラスの最初のコードに進むことができます。これは、以下に示すクラスコンストラクタです。

                C_ControlOfTime(const ulong magic)
                        :C_Orders(magic)
                        {
                                ResetLastError();
                                for (ENUM_DAY_OF_WEEK c0 = SUNDAY; c0 <= SATURDAY; c0++) ZeroMemory(m_InfoCtrl[c0]);
                        }

多くの人にとって、次のコードは奇妙に見えるかもしれませんが、これは私たちが慣れ親しんでいるものよりも少し高度なプログラミング形式にすぎません。高水準プログラミングであると私が言うことは、私のプログラミング経験とは何の関係もありません。前に言ったように:

「数値を使用するコードよりも、自然言語を使用するコードの方が理解しやすい」

このコンストラクタのループは自明です。そのようなものは、コードが高レベルか低レベルかを実際に定義します。

このコンストラクタを理解するのは非常に簡単だと思います。以前、コンストラクタ内のコードがどのように機能するかについて説明しました。よくわからない場合は、本連載の以前の記事をお読みください。ここでの唯一の違いは、実際には変数を示すループで、値SUNDAYで始まり、SATURDAYで終わります。ここでは他に複雑なことは何もありません。

列挙で日曜日が週の最初の日として定義され、土曜日が最後の日として定義されているため、ループは正しく機能します。ただし、MONDAYが週の最初の曜日として設定されている場合、MetaTrader 5プラットフォームでコードを実行するとループが失敗し、エラーがスローされます。したがって、高レベルコードを使用する場合は注意が必要です。構成が正しくないと、コードによって複数の実行時エラーが発生する可能性があるためです。

これが完了したら、クラスの次の関数に進むことができます。

virtual void SetInfoCtrl(const ENUM_DAY_OF_WEEK index, const string szArg) final
                        {
                                string szRes[];
                                bool bLocal;
                                
                                if (_LastError != ERR_SUCCESS) return;
                                if ((index > SATURDAY) || (index < SUNDAY)) return;
                                if (bLocal = (StringSplit(szArg, '-', szRes) == 2))
                                {
                                        m_InfoCtrl[index].Init = (StringToTime(szRes[0]) % 86400);
                                        m_InfoCtrl[index].End = (StringToTime(szRes[1]) % 86400);
                                        bLocal = (m_InfoCtrl[index].Init <= m_InfoCtrl[index].End);
                                        if (_LastError == ERR_WRONG_STRING_DATE) ResetLastError();
                                }
                                if ((_LastError != ERR_SUCCESS) || (!bLocal))
                                {
                                        Print("Error in the declaration of the time of day: ", EnumToString(index));
                                        ExpertRemove();
                                }
                        }

この部分は、経験の浅い方にとってはそれほど単純ではないため、より詳細な分析が必要です。私もその立場だったので分かります。上記のコードをより詳細に説明するために、コードを小さな部分に分解してみましょう。

if (_LastError != ERR_SUCCESS) return;
if ((index > SATURDAY) || (index < SUNDAY)) return;

このコードは非常に機密性が高く、扱っているものの性質上エラーが発生しやすいため、可能な限りシステムのエラーを確認する必要があります。この特定の関数が呼び出される前に、障害がないかどうかを確認することから始めます。障害が発生した場合、関数はすぐに閉じられます。

ランダムな障害を回避するために、2番目の確認も必要です。なんらかの理由で、ある数字を示す曜日に応じて呼び出しをおこない、プロセッサがSATURDAYSUNDAYの範囲内にないために数字としてのみ扱う場合、その関数内でそれ以上の行を実行しません。

この最初のステップが実行され、受け入れられ、コードが最初のテストに合格したら、呼び出し元から渡されたコンテンツの翻訳をおこないます。

if (bLocal = (StringSplit(szArg, '-', szRes) == 2))
{
        m_InfoCtrl[index].Init = (StringToTime(szRes[0]) % 86400);
        m_InfoCtrl[index].End = (StringToTime(szRes[1]) % 86400);
        bLocal = (m_InfoCtrl[index].Init <= m_InfoCtrl[index].End);
        if (_LastError == ERR_WRONG_STRING_DATE) ResetLastError();
}

ここには、特にプログラミングを学び始めた人にとって、さらに興味深いコードがあります。ここでは、StringSplit関数を使用して、呼び出し元から受け取った情報を2つの部分に「分割」します。情報が壊れる場所を示す文字は、マイナス記号(-)です。私たちが見たいと思っているのは、2つの情報です。以下の例に示すように、異なる番号を取得した場合、これはエラーと見なされます。

12:34 - - 18:34 <-- This is an error
12:32  -  18:34 <-- Information is correct
12:32     18:34 <-- This is an error
       -  18:34 <-- This is an error
12:34  -        <-- This is an error

StringSplit関数では、情報の内部コンテンツは重要ではありません。指定されたセパレータに基づいてデータを「分割」します。この関数は特定の場合に非常に役立つため、適切に学習することが重要です。文字列内の情報を分離するのに非常に役立つからです。

これら2つの情報を取得したら、StringToTime関数を使用して日付形式コードに変換します。

後1つ重要なのは、提供する情報には、時間と分のみが含まれるということです。特定の日付には関心がありません。特定の日付を指定することを妨げるものは何もありませんが、実装によれば、日付は無視されます。名前を入力するだけです。StringToTime関数は、現在の日付を自動的に追加します。これは実際には問題ではありませんが、後で検討することが1つあります。

StringToTime関数によって追加された現在の日付値を削除するには、因数分解を使用します。この結果は、呼び出し元によって指定された時間値だけになります。StringToTime関数によって追加された日付(常に現在の日付)を本当に削除したくない場合は、この指定された因数分解を削除するだけです。

値が変換された後、最初のテストがあります。後でトラブルを避けるためには非常に重要です。開始時間が指定された終了時間以前前かどうかを確認します。この確認が成功すれば、後でこの問題について心配する必要はありません。確認が失敗した場合、値が適切でないことをユーザーに通知します。

日付を指定していないため、プラットフォームが実行時エラーを生成するため、別の確認もおこないます。この種のエラーは非常に嫌なものですが、非常に適切な方法で対処します。このようなエラーが検出された場合は、エラーが発生したという表示を削除するだけです。日付が通知されないのでこのエラーが発生することは事前にわかっています。

重要な注意点:EAのテスト中に、EAの整合性に違反せず、不安定でも安全でもない実行時エラーがトリガーされることに気付いた場合は常に、エラーを生成するコードの後にこのタイプのテストを追加して、将来のエラー生成を最小限に抑えます。一部のランタイムエラーは、それほど重要ではなく、EAの操作にまったく影響しないため、無視できます。ただし、一部のエラーはEAがチャート上で移動を停止する原因となるため、異なる方法で処理する必要があります。これは、強制的な堅牢性と呼ばれます。なぜなら、障害が発生する可能性があることはわかっていて、それによってシステムが危険にさらされることはないこともわかっているからです。

値変換を実装すると、次のコードが得られます。

if ((_LastError != ERR_SUCCESS) || (!bLocal))
{
        Print("Error in the declaration of the time of day: ", EnumToString(index));
        ExpertRemove();
}

ここで重要なことがあります。値の変換中にシステムが重大なエラーを生成した場合、この定数変数はERR_SUCCESSとは異なる値になります。これは、ユーザーが使用または入力したデータを信頼できないことを示しています。ここの変数が「false」の値を持っている場合も同様で、これはある点が原因で失敗したことを示します。とにかく、ターミナルにメッセージを出力してトレーダーに通知します。また、EAを閉じる要求がMetaTrader 5プラットフォームで生成されます。

詳細に説明されているので、関数の仕組みの説明が非常に明確であることを願っています。ただし、これがすべてではありません。説明する別の関数があります。

virtual const bool CtrlTimeIsPassed(void) final
                        {
                                datetime dt;
                                MqlDateTime mdt;
                                
                                TimeToStruct(TimeLocal(), mdt);
                                dt = (mdt.hour * 3600) + (mdt.min * 60);
                                return ((m_InfoCtrl[mdt.day_of_week].Init <= dt) && (m_InfoCtrl[mdt.day_of_week].End >= dt));
                        }

同じ結果が得られる可能性のあるすべてのバリエーションを試すまで、何かを当然のことと見なさないことが重要ですが、はるかに簡単な方法で異なります。がっかりかもしれませんが、問題を解決し、より良い解決策を見つけるための貴重な方法です。

要点に入る前に、上記の機能を理解しましょう。ローカルの時刻と日付を取得し、それらの値を構造体に変換してデータを分割します。次に、時間と分のみを含む値を設定します。これらは前の関数で使用した値です。これが上記した問題です。変換関数で日付値を削除しなかった場合は、ここで日付値を追加する必要があります。これらの値を取得したら、それらが範囲内にあるかどうかを確認します。そうである場合はtrueを返し、その日の定義された範囲外である場合はfalseを返します。

ここで、別の質問があります。ローカル時間が日の値の範囲内にあるかどうかを確認するために、キャプチャ、再構成のすべての作業をおこなう必要があるのはなぜでしょうか。現地時間を取得し、定義された範囲と照合するコードを使用する方が簡単ではないでしょうか。確かに、はるかに簡単ですが、小さな問題があります。曜日です

当日のみを基準にEAの運用範囲を設定すればOKです。現地時間がわかれば十分です。ただし、7日間の値を定義しているので、曜日を知る最も簡単な方法は、上記の関数でおこなわれたすべての作業を正確に使用することです。

本当に、それはすべて、システムをどのように実装しているかに依存します。シンプルに実装する場合、作成および定義する必要がある関数と手順はかなりシンプルになります。より広く利用するためにシステムを構築する場合、機能と手順はかなり複雑になります。

実際にコーディングを開始する前に、考えて分析することが重要です。そうしないと、新しくビルドされたコードによって古いコードが要件を満たすことができなくなるという行き止まりに陥る可能性があります。したがって、古いコードを変更する必要があり、次に気が付くと、すべてを破棄して最初からやり直す必要があるという混乱に巻き込まれています。

話題を変える前に、上記の機能から1つの詳細を強調したいと思います。EAを1日24時間稼働させ、その間ずっとプラットフォームを稼働させたい場合、EAが1日の変わり目に、いつ稼働を再開するかわからないのではないかと心配するかもしれません。関数を呼び出すたびに状況全体を再評価し、現在の曜日を使用してEAが何らかの操作を実行できるかどうかを確認するため、これは実際には起こりません。

たとえば、月曜日の04:15から22:50まで、火曜日の3:15から20:45までの取引が可能であることをEAに伝えるとします。月曜日にオンにして、火曜日まで実行しておくことができます。月曜から火曜に切り替わると同時に、EAは火曜の稼働可能時間帯の確認を自動で開始します。このため、現在の日付に基づく定義ではなく、週モードを使用することにしました。

このクラスで詳細を見逃しているかもしれませんが、関数の動作の説明を過度に複雑にしたくはありません。関数の継承を扱う際の非常に重要な詳細を詳しく見てみましょう。注意深く見ると、SetInfoCtrl関数とCtrlTimeIsPassed関数の両方に非常に奇妙な宣言があることがわかります。なぜそのような宣言をしているのでしょうか。目的は何でしょうか。これらの宣言は、以下で強調表示されています。

virtual void SetInfoCtrl(const ENUM_DAY_OF_WEEK index, const string szArg) final
//+------------------------------------------------------------------+
virtual const bool CtrlTimeIsPassed(void) final
//+------------------------------------------------------------------+

ここで、言葉にはそれぞれ理由があります。コードを装飾するためにここに配置されているものは何もありません。一部の方はこれに慣れていますが、それは別の話です.

これらの宣言で本当に重要なのは、予約語「finalです。これは、宣言に「virtual」という言葉が存在することからの大きな疑問です。実際のところ、クラスを作成するとき、メソッドをオーバーライドしたり、親クラスの関数が子クラスで実行される方法を変更したり、より基本的な作業に基づいて新しいフォームを作成したりするなど、いくつかの方法でクラスに取り組むことができます。上記のように、クラスの関数宣言に「final」を追加すると、子クラスがそれを変更できないことがコンパイラに伝えられます。親クラスで記述された関数をオーバーライドすることさえできません。これは非常に一般的です。

重要なことなので、もう一度繰り返します。宣言に「final」を使用することにより、子クラスが、宣言で「final」を受け取った親クラスを変更したり、オーバーライドしたりすることさえできないことをコンパイラに伝えます

したがって、ここからこのクラスを継承し、そのメソッドと変数を持つクラスがこれらのメソッドを変更しようとすると、この試みはエラーと見なされ、プログラムがコンパイルされないことが保証されます。「virtual」は、変更の可能性を促進するのに正確に役立ちますが、「final」はそのような変更を防ぎます。誰が本当にルールを作るかが「final」です。したがって、子クラス内で関数が過度に変更されないようにしたい場合は、上記のように宣言を追加して、この変更の可能性をロックします。

これにより、多くのクラスと深いレベルの継承を使用している場合に、頭痛の種が大幅に軽減されます。


C_ControlOfTimeをC_Managerとリンクし、EAで使用する

ついに、C_ControlOfTimeクラスをC_Managerクラスにリンクすることができます。EAは新しいタイプの作業パラメーターを持つようになります。これをおこなうには、次の変更をC_Managerクラスコードに追加します。

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Orders.mqh"
#include "C_ControlOfTime.mqh"
//+------------------------------------------------------------------+
#define def_MAX_LEVERAGE                10
#define def_ORDER_FINISH                false
//+------------------------------------------------------------------+
class C_Manager : private C_Orders
class C_Manager : public C_ControlOfTime

クロスアウトした行が元のコードから削除され、新しい行がその場所に表示されました。ただし、C_ManagerクラスがC_ControlOfTimeクラスをpublicに継承することに注意してください。C_ControlOfTimeクラスのすべてをC_Managerクラスに追加するだけです。これにより、コードを増やすことなくC_Managerクラスの機能が拡張されます。C_ControlOfTimeクラスを継承して追加された機能が不要になった場合は、この継承と、C_Managerクラスから参照ポイントを削除するだけです。非常にシンプルです。

したがって、C_Managerクラスの信頼性レベルは変更しません。これは、何も起こらなかったかのように、最大のセキュリティ、安定性、および堅牢性で引き続き機能するためです。C_ControlOfTimeクラスが原因でC_Managerクラスが不安定になった場合は、単にC_ControlOfTimeクラスを削除するだけで、C_Managerクラスは再び安定します。

これが、私が散らばった関数としてではなくクラスの形ですべてを作成することを好む理由を説明していると思います。物事は非常に急速に増加し、改善されており、私たちは言語が提供できる最高レベルの安定性と信頼性を常に持っています。

ここで、コンストラクタを何らかの方法で参照する必要があるため、以下の新しいクラスコンストラクタを見てみましょう。

//+------------------------------------------------------------------+
                C_Manager(const ulong magic, double FinanceStop, double FinanceTake, uint Leverage, bool IsDayTrade, double Trigger)
                        :C_ControlOfTime(magic),
                        :C_Orders(magic),
                        m_bAccountHedging(false),
                        m_TicketPending(0),
                        m_Trigger(Trigger)
                        {
                                string szInfo;
                                
                                ResetLastError();
                                ZeroMemory(m_Position);
                                m_InfosManager.FinanceStop = FinanceStop;

// ... The rest of the constructor code....

                        }
//+------------------------------------------------------------------+

ここでは、宣言と同じです。クロスアウトした行は削除されました。その代わりに、データをC_ControlOfTimeクラスコンストラクタに渡す新しい行が追加されました。このコンストラクタはC_Ordersクラスのコンストラクタを参照し、注文の送信に必要なマジックナンバーを受け取ります。

さて、以下にある時間制御の実際の使用に関するポイントをもって、このトピックを終了します。

//+------------------------------------------------------------------+
                void CreateOrder(const ENUM_ORDER_TYPE type, const double Price)
                        {
                                if (!CtrlTimeIsPassed()) return;
                                if ((m_StaticLeverage >= def_MAX_LEVERAGE) || (m_TicketPending > 0) || (m_bAccountHedging && (m_Position.Ticket > 0))) return;
                                m_TicketPending = C_Orders::CreateOrder(type, Price, (def_ORDER_FINISH ? 0 : m_InfosManager.FinanceStop), (def_ORDER_FINISH ? 0 : m_InfosManager.FinanceTake), m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                        }
//+------------------------------------------------------------------+  
                void ToMarket(const ENUM_ORDER_TYPE type)
                        {
                                ulong tmp;
                                
                                if (!CtrlTimeIsPassed()) return;
                                if ((m_StaticLeverage >= def_MAX_LEVERAGE) || (m_bAccountHedging && (m_Position.Ticket > 0))) return;
                                tmp = C_Orders::ToMarket(type, (def_ORDER_FINISH ? 0 : m_InfosManager.FinanceStop), (def_ORDER_FINISH ? 0 : m_InfosManager.FinanceTake), m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                                m_Position.Ticket = (m_bAccountHedging ? tmp : (m_Position.Ticket > 0 ? m_Position.Ticket : tmp));
                        }
//+------------------------------------------------------------------+

EAまたはC_Managerクラス内の他の関数は時間範囲を使用しません。ただし、必要に応じて、トレーリングストップトリガーなど、制御が必要な関数に強調表示された行を正確に追加することで、この制御を追加できます。これらの関数の一部で制御を使用したくない場合は、追加された行を削除してください。コードの計画方法はいかがだったでしょうか。しかし、これらすべては、EAのコードに別のポイントがないと実際には機能しません。

EAコードで本当に必要な変更のみを以下に示します。

#include <Generic Auto Trader\C_Manager.mqh>
#include <Generic Auto Trader\C_Mouse.mqh>
//+------------------------------------------------------------------+
C_Manager *manager;
C_Mouse  *mouse;
//+------------------------------------------------------------------+
input int       user01   = 1;                   //Leverage Factor
input double    user02   = 100;                 //Take Profit ( FINANCE )
input double    user03   = 75;                  //Stop Loss ( FINANCE )
input bool      user04   = true;                //Day Trade ?
input color     user05  = clrBlack;             //Price Line Color
input color     user06  = clrForestGreen;       //Take Line Color 
input color     user07  = clrFireBrick;         //Stop Line Color
input double    user08  = 35;                   //BreakEven ( FINANCE )
//+------------------------------------------------------------------+
input string    user90  = "00:00 - 00:00";      //Sunday
input string    user91  = "09:05 - 17:35";      //Monday
input string    user92  = "10:05 - 16:50";      //Tuesday
input string    user93  = "09:45 - 13:38";      //Wednesday
input string    user94  = "11:07 - 15:00";      //Thursday
input string    user95  = "12:55 - 16:25";      //Friday
input string    user96  = "00:00 - 00:00";      //Saturday
//+------------------------------------------------------------------+
#define def_MAGIC_NUMBER 987654321
//+------------------------------------------------------------------+
int OnInit()
{
        string szInfo;
        
        manager = new C_Manager(def_MAGIC_NUMBER, user03, user02, user01, user04, user08);
        mouse = new C_Mouse(user05, user06, user07, user03, user02, user01);
        for (ENUM_DAY_OF_WEEK c0 = SUNDAY; c0 <= SATURDAY; c0++)
        {
                switch (c0)
                {
                        case SUNDAY     : szInfo = user90; break;
                        case MONDAY     : szInfo = user91; break;
                        case TUESDAY    : szInfo = user92; break;
                        case WEDNESDAY  : szInfo = user93; break;
                        case THURSDAY   : szInfo = user94; break;
                        case FRIDAY     : szInfo = user95; break;
                        case SATURDAY   : szInfo = user96; break;
                }
                (*manager).SetInfoCtrl(c0, szInfo);
        }
        (*manager).CheckToleranceLevel();
        EventSetMillisecondTimer(100);

        return INIT_SUCCEEDED;
}

ユーザーがコードを操作するポイントを追加するだけで済みました。 このループはデータをキャプチャしてシステムに投入し、C_Managerクラスで使用できるようにして、実際のEA操作時間を制御します。コードのこの部分の動作は理解しやすく、追加の説明は必要ありません。


結論

この記事では、EAが特定の時間範囲内で動作できるようにするために制御を追加する方法を示しました。システムは非常に単純ですが、最後にもう1つ説明が必要です。

対話システムで24時間を超える時間を入力すると、24時間に最も近い時間に修正されます。つまり、EAを22:59まで動作させたい場合(外為で作業している場合)、この値を正確に指定するように注意する必要があります。25:59と入力すると、システムによって23:59に変更されます。この入力エラーは一般的ではありませんが、発生する可能性はあります。

このような状況はめったに起こらないため、この状況を分析するための確認は追加していませんが、これについてコメントし、そのような状態の可能な確認を示したいと思いました。以下でご覧ください。添付のコードには、これらの変更が既に含まれています。

virtual void SetInfoCtrl(const ENUM_DAY_OF_WEEK index, const string szArg) final
                        {
                                string szRes[], sz1[];
                                bool bLocal;
                                
                                if (_LastError != ERR_SUCCESS) return;
                                if ((index > SATURDAY) || (index < SUNDAY)) return;
                                if (bLocal = (StringSplit(szArg, '-', szRes) == 2))
                                {
                                        m_InfoCtrl[index].Init = (StringToTime(szRes[0]) % 86400);
                                        m_InfoCtrl[index].End = (StringToTime(szRes[1]) % 86400);
                                        bLocal = (m_InfoCtrl[index].Init <= m_InfoCtrl[index].End);
                                        for (char c0 = 0; (c0 <= 1) && (bLocal); c0++)
                                                if (bLocal = (StringSplit(szRes[0], ':', sz1) == 2))
                                                        bLocal = (StringToInteger(sz1[0]) <= 23) && (StringToInteger(sz1[1]) <= 59);
                                        if (_LastError == ERR_WRONG_STRING_DATE) ResetLastError();
                                }
                                if ((_LastError != ERR_SUCCESS) || (!bLocal))
                                {
                                        Print("Error in the declaration of the time of day: ", EnumToString(index));
                                        ExpertRemove();
                                }
                        }

入力された時間が24時間の期間内で可能な最大値を下回っているかどうかを確認するために、時間データを分割する新しい変数と強調表示されたコードを追加する必要がありました


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

添付されたファイル |
ニューラルネットワークの実験(第4回):テンプレート ニューラルネットワークの実験(第4回):テンプレート
この記事では、実験と非標準的な方法を使用して収益性の高い取引システムを開発し、ニューラルネットワークがトレーダーに役立つかどうかを確認します。ニューラルネットワークを取引に活用するための自給自足ツールとしてMetaTrader 5を使用します。簡単に説明します。
自動で動くEAを作る(第09回):自動化(I) 自動で動くEAを作る(第09回):自動化(I)
自動EAの作成はそれほど難しい作業ではありませんが、必要な知識がないと多くの間違いを犯す可能性があります。この記事では、ブレイクイーブンとトレーリングストップレベルを作動させるトリガーの作成からなる自動化の最初のレベルを構築する方法について見ていきます。
ビル・ウィリアムズのMFIによる取引システムの設計方法を学ぶ ビル・ウィリアムズのMFIによる取引システムの設計方法を学ぶ
これは、人気のあるテクニカル指標に基づいて取引システムを設計する方法を学ぶための連載の新しい記事です。今回はビル・ウィリアムズの「マーケット・ファシリテーション・インデックス(BW MFI、Bill Williams' Market Facilitation Index)」を取り上げます。
母集団最適化アルゴリズム:モンキーアルゴリズム(MA) 母集団最適化アルゴリズム:モンキーアルゴリズム(MA)
今回は、最適化アルゴリズムであるモンキーアルゴリズム(MA、Monkey Algorithm)について考えてみたいと思います。この動物が難関を乗り越え、最もアクセスしにくい木のてっぺんまで到達する能力が、MAアルゴリズムのアイデアの基礎となりました。