English Русский 中文 Español Deutsch Português
preview
自動で動くEAを作る(第04回):手動トリガー(I)

自動で動くEAを作る(第04回):手動トリガー(I)

MetaTrader 5トレーディング | 6 3月 2023, 09:08
1 346 0
Daniel Jose
Daniel Jose

はじめに

前回の「自動で動くEAを作る(第03回):新機能」稿では、オーダーシステムの説明を終わりました。まだ読んでいない、または内容を完全に理解していない場合は、その記事に戻ることをお勧めします。今回はオーダーシステムについては触れずに、他のことに進みます。特にトリガーです。

トリガーシステムはおそらく最も難しい部分です。100%エラーのない手法はないため、混乱、疑問、問題が生じます。すべての手法には、独自の欠点と問題があります。エラーの確率が高いものもあれば低いものもあります。ただし、次のルールを常に覚えておいてください。トリガーが何であれ、依存してはいけません。予期しないときに失敗する可能性があるためです。トリガーシステムが失敗すると、良い機会を逃したり、大きな損失を被ったりする可能性があるため、トリガーには常に細心の注意を払う必要があります。

このシステムの詳細を見てみましょう。まず、システムの目的は何でしょうか。トリガーシステムは、ブレークイーブンのタイミングやトレーリングストップの動きをトリガーするタイミングを示すなど、多くの目的を果たします。これらは最も単純な例です。トリガーシステムは、EAがいつポジションを開くかや閉じるかをを示すこともできます。

これらのイベントはすべて完全に自動的に発生します。トレーダーは何もする必要はありません。どのトリガーがイベントをアクティブにするかをEAに指示するだけです。EAが連続ループに入った場合を除いて、人間の参加は必要ありません。

これらのアイデアと概念をよりよく理解するには、手動EAをプログラムする必要がありますが、ここでは手動EAとして通常おこなうのとはまったく異なることをおこないます。この連載で例として使用しているEAでは、指値注文を出すか、成行のポジションを開くために注文を送信する可能性を追加します。EAはデモンストレーションと学習を目的としているため、EAの使用を検討しているすべての方には、デモ口座でこれをおこなうようお勧めします。EAをリアル口座で使用しないでください。EAが動かなくなったり、おかしくなったりする危険性があります。


注文の送信方法と成行ポジションを開く方法

私の意見では、チャートで取引する最良の方法は、マウスとキーボードを組み合わせて使用することです。これは指値注文の発注に適していますが、成行注文はキーボードのみを使用して発注できます。唯一の問題は、説明するのが難しいほど複雑ではないコードを実装する方法です。マウスとキーボードを使用して注文を送信するシステムの開発は非常に困難になる可能性があります。

しばらく考えた後、この記事で使用する理想的なシステムを考え出すことにしました。これは非常に単純なものです。したがって、このシステムをリアル口座で使用しないでください。このシステムは、トリガーの実装方法と処理方法を理解できるように、トリガーに関するいくつかのことを説明するのに役立つだけです。

このコードは作成できる最も単純なものですが、それでもこの連載の残りの部分には十分です。それでは、コードがどのように実装されているか見てみましょう。

まず、C_Mouse.mqhという名前のヘッダーファイルを追加しました。ファイル内のコードは次のように始まります。

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_MouseName "MOUSE_H"
//+------------------------------------------------------------------+
#define def_BtnLeftClick(A)     ((A & 0x01) == 0x01)
#define def_SHIFT_Press(A)      ((A & 0x04) == 0x04)
#define def_CTRL_Press(A)       ((A & 0x08) == 0x08)
//+------------------------------------------------------------------+

作成しているグラフィカルオブジェクトの名前を設定します。これは水平線で、マウスポインタがチャート上にある価格位置を示します。また、マウスイベントによって生成される情報に簡単にアクセスできるように、3つの定義を作成しました。 これは、クリックおよびキー押下イベントをテストする方法です。これらのアクセス定義は後でWAで使用しますが、オブジェクト名の定義は次の行を使用してファイルの最後で完了します。

//+------------------------------------------------------------------+
#undef def_MouseName
//+------------------------------------------------------------------+

この行をヘッダーファイルの最後に追加すると、この定義が漏れたり、コード内の他の場所に表示されたりすることがなくなります。このヘッダーファイル以外の場所で再宣言せずにこの定義を使用しようとすると、コンパイラは関連する警告を返します。

それでは、マウス操作を担当するクラスのコードを書き始めましょう。次のように開始します。

class C_Mouse
{
        private :
                struct st00
                {
                        long    Id;
                        color   Cor;
                        double  PointPerTick,
                                Price;
                        uint    BtnStatus;
                }m_Infos;
//+------------------------------------------------------------------+
                void CreateLineH(void)
                        {
                                ObjectCreate(m_Infos.Id, def_MouseName, OBJ_HLINE, 0, 0, 0);
                                ObjectSetString(m_Infos.Id, def_MouseName, OBJPROP_TOOLTIP, "\n");
                                ObjectSetInteger(m_Infos.Id, def_MouseName, OBJPROP_BACK, false);
                                ObjectSetInteger(m_Infos.Id, def_MouseName, OBJPROP_COLOR, m_Infos.Cor);
                        }
//+------------------------------------------------------------------+
inline double AdjustPrice(const double value)
                        {
                                return MathRound(value / m_Infos.PointPerTick) * m_Infos.PointPerTick;
                        }
//+------------------------------------------------------------------+

このコードを怖がらないでください。この部分はほんの一例です。 クラス内のいくつかのグローバル変数を含むデータ構造を宣言するだけですが、それらはクラスに対してprivateになります。その後、Metatrader 5プラットフォームでチャートオブジェクトを作成する手順を使用します。このオブジェクトは水平線になり、そのプロパティのいくつかを設定して次に進みます

この関数は前回の記事で既に登場しました。まだの場合は、連載の以前の記事を最初にお読みください。これらの記事もこれからおこなうことにとって重要です。

次に、C_Mouseクラスでpublicになるプロシージャを見てみましょう。クラスコンストラクタから始めます。

                C_Mouse(const color cor)
                        {
                                m_Infos.Id = ChartID();
                                m_Infos.Cor = cor;
                                m_Infos.PointPerTick = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
                                ChartSetInteger(m_Infos.Id, CHART_EVENT_MOUSE_MOVE, true);
                                ChartSetInteger(m_Infos.Id, CHART_EVENT_OBJECT_DELETE, true);
                                CreateLineH();
                        }

MQL5にあまり慣れていない場合、奇妙に思えるかもしれない2つの行があるので、簡単に説明します。この行はMetatrader 5プラットフォームに、私たちのコードであるEAがマウスイベントを受け取りたいことを伝えます。スクロールを使用するときに発生する、マウスによって生成される別のタイプのイベントもありますが、このイベントタイプで十分なので、使用しません。

この行は、オブジェクトがチャートから削除されたことを知りたいことをMetatrader 5に通知します。 これが発生すると、どの要素がチャートから削除されたかを通知するイベントが生成されます。このタイプのイベントは、タスクを完全に実行するために特定の重要な要素を保存したい場合に役立ちます。次に、ユーザーが重要な要素を誤って削除した場合、Metatrader 5は要素が削除されたことをコード(この場合はEA)に通知し、再作成できるようにします。次に、クラスデストラクタコードを見てみましょう。

                ~C_Mouse()
                        {
                                ChartSetInteger(m_Infos.Id, CHART_EVENT_OBJECT_DELETE, false);
                                ObjectDelete(m_Infos.Id, def_MouseName);
                        }

このコードでは2つのことをおこないます。まず、チャートから要素が削除された場合に通知を受け取りたくないことをMetatrader 5に通知します。 要素を削除しようとすると、Metatrader 5がチャートから何かが削除されたことを通知するイベントを生成するため、続行する前にこれをおこなうことが重要です。これがまさに次にやろうとしていることです。作成した線を削除して、マウスポインタがどの価格帯にあるかを示します

次におこなう必要があるのは、マウスの位置とそのボタンの状態を把握できるように、クラス内のデータを読み取るメソッドを作成することです。この目的のために、次の関数を使用します。

const void GetStatus(double &Price, uint &BtnStatus) const
                        {
                                Price = m_Infos.Price;
                                BtnStatus = m_Infos.BtnStatus;
                        }

オブジェクト指向プログラミングの経験が少ない人にありがちなことを説明したいと思います。クラスを使用する場合、クラス外のコードがクラス変数またはプロシージャに直接アクセスできるようにしてはならず、クラスに何が入るかを常に確認する必要があります。

クラスで変数またはプロシージャを使用して、適切に確認されていない値を取得できるようにすることは、長期的には問題につながるため、重大な誤りです。ある時点で、クラスで使用される重要な変数の値をコードで変更する可能性があります。そして、実際に使用するときに、コード全体が危険にさらされる可能性があります。また、この種の状況に対処し、後で問題を修正することは非常に複雑です。

良いプログラマーになるためには、常に次の方法に従ってください。クラスの内容を読み取る場合は、変更ではなく読み取りを許可します。これは、特にポインタを使用する場合に頻繁に発生します。変数を読み取り、ポインタを返すように要求します。この時点でコードは危険にさらされています。ポインタを使用すると、メモリ内のどこに書き込むかがわかるため、これは非常に危険です。ゲームのチート(電子ゲームでチートをおこなうための小さなプログラム)がどのように作成されるかをご覧ください。ポインタを使用してメモリ位置に書き込むだけです。

ポインタを使用するときは十分に注意してください。よくわからない場合は、常にプロシージャよりも関数を使用することをお勧めします。この2つの違いは、関数は通常は値を返すがポインタを返さないように注意するということです。

ただし、他に選択肢がない場合は、上で示したのと同じことをおこなってください。これは過剰な注意のように見えるかもしれませんが、予約語constを使用してプロシージャまたは関数を宣言し始めると、呼び出し元が変数の値を変更できないことが保証されます。コンパイラが任意の値を変更できない定数として扱うためです。.

予約語constを使用してプロシージャまたは関数の宣言を完了すると、プログラマがプロシージャまたは関数内の値を誤ってまたは無意識のうちに変更することがなくなります。多くの人にとっては奇妙に思えるかもしれませんが、これはおそらく現存する最良のプログラミング手法です。これは、特にOOP(オブジェクト指向プログラミング)でおこなう場合に、いくつかのプログラミングの間違いを回避します。constという単語で宣言を完了するというこの問題を理解するのに役立つより明確な例を使用して、少し後でこれに戻ります。

必要な最後のプロシージャを以下に示します。

                void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
                        {
                                int w;
                                datetime dt;
                                
                                switch (id)
                                        {
                                                case CHARTEVENT_OBJECT_DELETE:
                                                        if (sparam == def_MouseName) CreateLineH();
                                                        break;
                                                case CHARTEVENT_MOUSE_MOVE:
                                                        ChartXYToTimePrice(m_Infos.Id, (int)lparam, (int)dparam, w, dt, m_Infos.Price);
                                                        ObjectMove(m_Infos.Id, def_MouseName, 0, 0, m_Infos.Price = AdjustPrice(m_Infos.Price));
                                                        m_Infos.BtnStatus = (uint)sparam;
                                                        ChartRedraw();
                                                        break;
                                        }
                        }

多くのプログラマーがコードをOnChartEventメッセージ処理イベント内に配置したいと考えるのは非常に興味深いことです。ただし、私はオブジェクトクラス内でイベントを処理するのが好きです。多くの場合、これは特定の処理や特別な方法での処理を忘れないようにするのに役立ち、多くの場合、コードが機能しているかどうかの確認が必要になります。クラス内でこの処理をおこなうことにより、長期的にはより自信を持てることになります。クラスを使用すると、クラスのメッセージ処理システムが期待どおりに機能していることがわかるからです。これにより、新しいプログラムの作成が大幅に高速化されます。これは再利用への道でもあります。一度プログラムすれば、常に使用できます。

このメッセージングシステムで何が起こるか見てみましょう。ここでは、OnChartEventイベントハンドラによって受信されるデータを正確に複製しています。これは意図的におこなわれます。クラス内でこれらのイベントを処理したいためです。

これが定義されたら、処理するイベントを決定しましょう。1つ目はオブジェクト削除イベントです。これは、オブジェクトがチャートから削除された場合に通知を受けたいと以前にMetatrader 5に伝えたために生成されます。削除されたオブジェクトがマウスラインであった場合、オブジェクトはすぐに再作成されます

クラス内で処理したい次のイベントはマウス移動イベントです。このイベントは、Metatrader 5にマウスで何が起こっているかを知りたいと伝えたために生成されます。ここではMetatrader 5によって提供された値を時間と価格の値に変換するだけです。その後、水平線オブジェクトを価格位置に配置して、正しい点に配置します。画面座標系ではなく、価格表での点であることに注意してください。そこからボタンの値を保存し、ただちにチャートを強制的に再描画します

このチャートの再描画は、クラスイベントハンドラまたはOnChartEventイベントハンドラのいずれかでおこなわれることが重要です。これをおこなわないと、価格ラインが異常に動くため、コードがプラットフォームを遅くしているという印象を受けるかもしれません.

これでC_Mouseクラスが完成し、チャート上で指値注文を直接開始できるようになりました。EAコードに戻り、C_Mouseクラスを追加して、イベントを生成し、指値注文を送信できるようにします。その方法は次のとおりです。

#include <Generic Auto Trader\C_Orders.mqh>
#include <Generic Auto Trader\C_Mouse.mqh>
//+------------------------------------------------------------------+
C_Orders *manager;
C_Mouse *mouse;
//+------------------------------------------------------------------+
input int       user01   = 1;           //Lot increase
input double    user02   = 100;         //Take Profit ( FINANCEIRO )
input double    user03   = 75;          //Stop Loss ( FINANCEIRO )
input bool      user04   = true;        //Day Trade ?
input color     user05  = clrBlack;     //Color Mouse
//+------------------------------------------------------------------+
#define def_MAGIC_NUMBER 987654321
//+------------------------------------------------------------------+
int OnInit()
{
        manager = new C_Orders(def_MAGIC_NUMBER);
        mouse = new C_Mouse(user05);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        delete mouse;
        delete manager;
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+

上記のスニペットは、どのように物を生産するかのステップを説明しています。すべてはC_Mouseクラスを簡単に削除できるようにおこなわれます。自動化されたEAでは役に立たないので、EAをテストする方法が必要です。トリガーを追加せずにこれをおこなう最善の方法は、作成したばかりのC_Mouseクラスを使用することです。したがって、コンパイラがコードを追加できるように、このクラスをインクルードします水平価格線に使用する色を定義します

C_Orderクラスを初期化して、すべての注文がこのEAに対して同じマジックナンバーを持つようにします。EAごとに常に異なるマジックナンバーを設定する必要があることに注意してください。その理由は、同じアセットで同時に複数のEAを使用する場合、各EAが独自の注文を管理できるようにそれらを分離する方法は、このマジックナンバーを使用するためです。2つのEAが同じマジックナンバーを持つ場合、それらを分離することはできず、一方が他方の注文に干渉する可能性があります。したがって、各EAには独自のマジックナンバーが必要です

その後、C_Mouseクラスを初期化すると、EAが動作する準備が整います。EAの最後でデストラクタを呼び出すことを忘れないでください。ただし、自分でおこなうのを忘れた場合は、通常、コンパイラが代わりにおこないます。それでも、コードでこれをおこなうことをお勧めします。これにより、誰もがクラスの参照を停止する場所がわかります。

EAはすでに使用する準備ができていると言いましたが、欠けているものが1つあります。チャートイベント処理システムです。これは次の形式になります。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        uint    BtnStatus;
        double  Price;
        static double mem = 0;
        
        (*mouse).DispatchMessage(id, lparam, dparam, sparam);
        (*mouse).GetStatus(Price, BtnStatus);
        if (def_SHIFT_Press(BtnStatus) != def_CTRL_Press(BtnStatus))
        {
                if (def_BtnLeftClick(BtnStatus) && (mem == 0)) (*manager).CreateOrder(def_SHIFT_Press(BtnStatus) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL, mem = Price, user03, user02, user01, user04);
        }else mem = 0;
}

それだけです。非常に簡単です。自動的に実行するように構築しているEAを手動EAに変えたくはありません。このため、EAはテイクプロフィットとストップロスの制限を定義する価格ラインを作成しません。このコードがどのように機能するか見てみましょう。ここにはまだ何かが欠けています。

Metatrader 5がチャートでイベントが発生したことに気付くと、それが何であれ、上記のコードへの呼び出しが生成されます。このコードがアクティブになり、実行するプロセッサを受け取ると、C_Mouseクラスからメッセージ処理プロシージャが呼び出されます。 このようにして、マウスにリンクされたイベントは適切に処理され、常に同じ方法で処理されます。

C_Mouseクラスのメッセージハンドラが返されると、マウスの状態を取得して、この関数で使用できるようになります。

次に、C_Mouse.mqhヘッダーファイルの定義を使用してShiftキーとCtrlキーの状態を確認してみましょう。これらは、買い注文か売り注文かを示す役割を果たすため、異なる値を持つ必要があります。これが発生した場合、新しいテストをおこないます。

ただし、今回はマウスの左ボタンがクリックされたかどうかを確認します。 これがtrueで、メモリ価格が0の場合、ユーザーが指定したデータを使用して指値注文をサーバーに送信します。売買はShiftキーとCtrlキーによって決定されます。Shiftは買いを示しCtrlは売りを示します。したがって、システムをテストするために必要な数の注文を出すことができますが、前述のように、まだ何かが欠けています。

前回の記事では、市場で取引をおこなうためのコードを生成するという実用的なタスクを提供しました。満たせなかったとしても大丈夫です。しかし、私の解決策を見ずに、自分でタスクを処理できれば素晴らしいことです。解決策は、更新されたOnChartEventハンドラコードのすぐ下にあります。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        uint    BtnStatus;
        double  Price;
        static double mem = 0;
        
        (*mouse).DispatchMessage(id, lparam, dparam, sparam);
        (*mouse).GetStatus(Price, BtnStatus);
        if (TerminalInfoInteger(TERMINAL_KEYSTATE_CONTROL))
        {
                if (TerminalInfoInteger(TERMINAL_KEYSTATE_UP))  (*manager).ToMarket(ORDER_TYPE_BUY, user03, user02, user01, user04);
                if (TerminalInfoInteger(TERMINAL_KEYSTATE_DOWN) (*manager).ToMarket(ORDER_TYPE_SELL, user03, user02, user01, user04);
        }
        if (def_SHIFT_Press(BtnStatus) != def_CTRL_Press(BtnStatus))
        {
                if (def_BtnLeftClick(BtnStatus) && (mem == 0)) (*manager).CreateOrder(def_SHIFT_Press(BtnStatus) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL, mem = Price, user03, user02, user01, user04);
        }else mem = 0;
}

これは、成行注文を取引サーバーに送信する方法です。次の動作があります。CTRLを押すと、ここで条件がtrueになり、2番目の条件を確認できるようになります。この2番目の条件は、市場価格で買うか市場価格で売るかを示します。これには、矢印キーを使用します。CTRL+上矢印が押された場合、ユーザーインタラクションエリアで指定されたパラメータに従って、成行買い操作を実行しますCTRL+下矢印が押された場合はパラメータに基づく成行売り操作です

これらのガイドラインを図01に示します。

図1

図01:EAの設定


結論

ここでイベントシステムとその実装方法について説明したことは、一種のトリガーメカニズムです。ただし、この場合、メカニズムは手動です。つまり、人間のトレーダーの介入が必要です。ポジションを開く注文または指値注文の配置は、EAユーザーとEA自体の相互作用を通じて実行されます。この場合、オーダーブックに注文したりポジションを開いたりするための自動メカニズムはなく、すべてトレーダーに依存します。

しかし、注文がオーダーブックに置かれると、システムはトレーダーに依存しなくなり、価格が注文で指定されたポイントに達するとすぐに注文がポジションになり、ポジションはリミット価格のいずれかに達した場合に決済されます。ただし、ボラティリティの高い瞬間があり、価格が急上昇する可能性があるため、注意が必要です。この場合、証券会社によって強制的に決済されるまで、または、デイトレードの場合は市場が閉まることによって制限時間が満了するまで、ポジションは開いたままになります。したがって、ポジションには常に注意する必要があります。

添付ファイルには、現時点のコードが含まれています。次の記事では、いくつかの変更を加える必要があるため、このコードを修正して、手動モードでEAをより簡単に使用できるようにします。これらの変更は興味深いものになります。


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

添付されたファイル |
ニューラルネットワークの実験(第3回):実用化 ニューラルネットワークの実験(第3回):実用化
この連載では、実験と非標準的なアプローチを使用して、収益性の高い取引システムを開発し、ニューラルネットワークがトレーダーに役立つかどうかを確認します。ニューラルネットワークを取引に活用するための自給自足ツールとしてMetaTrader 5にアプローチします。
母集団最適化アルゴリズム:侵入雑草最適化(IWO) 母集団最適化アルゴリズム:侵入雑草最適化(IWO)
雑草がさまざまな条件で生き残る驚くべき能力は、強力な最適化アルゴリズムのアイデアになっています。IWO(Invasive Weed Optimization)は、以前にレビューされたものの中で最高のアルゴリズムの1つです。
自動で動くEAを作る(第05回):手動トリガー(II) 自動で動くEAを作る(第05回):手動トリガー(II)
今日は、自動モードでシンプルかつ安全に動作するエキスパートアドバイザー(EA)を作成する方法を紹介します。前回の最後に、少なくともしばらくはEAを手動で使えるようにするのが適切ではないかと提案しました。
母集団最適化アルゴリズム:コウモリアルゴリズム(BA) 母集団最適化アルゴリズム:コウモリアルゴリズム(BA)
今回は、滑らかな関数に対して良好な収束性を示すコウモリアルゴリズム(BA)について考えてみることにします。