English Русский 中文 Español Deutsch Português
preview
自動で動くEAを作る(第02回):コードを始める

自動で動くEAを作る(第02回):コードを始める

MetaTrader 5トレーディング | 28 2月 2023, 13:48
2 213 0
Daniel Jose
Daniel Jose

はじめに

前回の「自動で動くEAを作る(第01回):概念と構造」では、自動売買をおこなうEAの作成に進む前に、誰もが理解する必要がある最初のステップについて説明しました。その中で、どのような概念を考慮し、どのような構造を作るべきかを示しました。

しかし、コードをどう書くかは説明しませんでした。私の目的は、読者、特にプログラミングの分野で実践的な知識を持たない初心者の知識を同じレベルにすることでした。そこで、そのような方にもMQL5の世界を身近に感じていただけるよう、完全自動で動作するEAの作り方を紹介しました。前回の記事をお読みでない場合、自動EAを作成する際の文脈を理解することが重要なので、ぜひ読んでおくことをお勧めします。

では、コードの記述に進みましょう。まずは基本システムである「オーダーシステム」から始めます。これが、これから作るEAの原点になります。


計画

MetaTrader 5標準ライブラリには、オーダーシステムを操作するための便利で適切な方法があります。しかし、この記事では、さらに踏み込んで、MetaTrader 5のTRADEライブラリの裏側を紹介したいと思います。ただし、今日の私の目的は、ライブラリの利用を思いとどまらせることではありません。逆に、このブラックボックスの中に何があるのかに、光を当てることです。そのために、注文を送るためのライブラリを独自に開発します。MetaTrader 5標準ライブラリに存在するすべてのリソースを持つわけではなく、機能的で堅牢かつ信頼性の高いオーダーシステムを作成および維持するために必要な機能のみが含まれています。

開発には、特定のタイプの構造を採用することになります。プログラミング初心者の方には申し訳ないのですが、説明についていくのには努力が必要です。皆さんが理想と概念を理解できるよう、できるだけシンプルに説明するよう努めます。努力なしでは、これから作るものに感心するだけで、独自でMQL5で開発することができなくなる恐れがあります。


C_Ordersクラスの作成

クラスを作成するには、まず、クラスコードを格納するファイルを作成します。MetaEditorのブラウザウィンドウで、「Include」フォルダに移動し、右クリックします。[新規ファイル]を選択し、以下の画像に表示されている手順に従ってください。

図1

図01:インクルードファイルの追加

図02

図02:必要なファイルが作成される


図01と図02の手順が完了すると、ファイルが作成され、MetaEditorで開くことができるようになります。その内容は、以下の通りです。その前に、さっそく説明したいことがあります。図01をご覧ください。クラスを直接作成することができることがわかります。なぜそうしないのかと思われるかもしれません。これはいい質問です。理由は、図01の手順でクラスを作ると、クラスがゼロから作られるのではなく、すでに何らかの定義された情報やフォーマットが含まれているということです。しかし、この連載の中では、クラスを一から作ることが重要です。コードに戻りましょう。

#property copyright "Daniel Jose"
#property link      ""
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
// #define MacrosHello   "Hello, world!"
// #define MacrosYear    2010
//+------------------------------------------------------------------+
//| DLL imports                                                      |
//+------------------------------------------------------------------+
// #import "user32.dll"
//   int      SendMessageA(int hWnd,int Msg,int wParam,int lParam);
// #import "my_expert.dll"
//   int      ExpertRecalculate(int wParam,int lParam);
// #import
//+------------------------------------------------------------------+
//| EX5 imports                                                      |
//+------------------------------------------------------------------+
// #import "stdlib.ex5"
//   string ErrorDescription(int error_code);
// #import
//+------------------------------------------------------------------+

薄いグレーのテキストがある行はすべてコメント(サンプルコードとして)であり、必要に応じて削除することができます。興味深いことはここから始まります。ファイルを開いたら、コードの追加を開始します。ここでは、オーダーシステムと連携するためのコードを追加します。

安全で信頼性の高い堅牢なコードにするために、MQL5がC++から持ち込んだツールであるクラスを使用することにします。クラスとは何かをご存じない方は、調べていただくことをお勧めします。クラスに関連するC++ドキュメントに直行する必要はありません。クラスについては、MQL5のドキュメントに記載されているので、そちらをご覧ください。また、MQL5ドキュメントの内容は、クラスというものをご存じでない方にC++があらゆる混乱を与えるのに比べて、理解しやすいと思います。

一般に、コードを他の部分から分離する方法として、クラスは圧倒的に安全で効率的な方法です。これは、クラスがコードとしてではなく、特別なデータ型として扱われるということです。このデータ型を使用すると、 integerdoublebooleanなどのプリミティブ型を使用するだけでなく、はるかに多くのことができます。つまり、プログラムにとってクラスは多機能なツールなのです。クラスが作成され、それが何を含んでいるかについては、本当に何も知る必要はありません。プログラムはその使い方を知っていればいいのです。クラスは電動工具のようなもので、どのように作られたのか、どのような部品があるのかを知る必要はありません。プラグインして使う方法だけ知っていれば、仕組みが変わっても使い方に違いはありません。これは、クラスの簡単な定義です。

次に進みましょう。まず、次のような行を生成します。

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
class C_Orders
{
        private :
        public  :
};
//+------------------------------------------------------------------+

私たちのクラスで最も基本的な部分です。通常、そのクラスが含まれるファイルの名前と同じクラス名を使用します。これは必須ではありませんが、後でクラスを見つけるのに役立ちます。クラス内では、2つの予約語を示していますが、これは両者の情報共有の度合いを示しています。これらの言葉を付け加えなければ、クラスのすべての要素はpublicとみなされます。つまり、誰でも読み書きやアクセスが可能です。これにより、コードのどの部分でもクラス内にあるものにアクセスして変更できるため、クラスを使用する際の特徴であるセキュリティと堅牢性の一部が損なわれます。

要するに:privateという宣言とpublicという宣言の間にあるものはすべて、そのクラス内でのみアクセスすることができるのです。ここでグローバル変数を使用すれば、クラスコードの外からアクセスすることはできません。publicの後に宣言されたものは、クラスの一部であろうとなかろうと、コードのどこからでもアクセスすることができます。そこにあるものなら誰でもアクセスできます。

このファイルを上記のように作成したら、EAに追加します。EAは次のようなコードになります。

#property copyright "Daniel Jose"
#property version   "1.00"
#property link      "https://www.mql5.com/pt/articles/11223"
//+------------------------------------------------------------------+
#include <Generic Auto Trader\C_Orders.mqh>
//+------------------------------------------------------------------+
int OnInit()
{
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+

このステップでは、Includeコンパイルディレクティブを使用して、EAコードにクラスを組み込みました。 このディレクティブを使用すると、その瞬間からGenericAutoTraderフォルダのincludeディレクトリにあるヘッダーファイルC_Orders.mqhをシステムに取り込んでコンパイルしなければならないとコンパイラが理解するのです。これについては、いくつかのコツがあるのですが、経験の浅いうちに細かいところを理解しようとすると、全く分からなくなることがあるので、詳細は省略します。ただし、起きていることは、まさに今説明した通りです。


C_Ordersクラスの最初の関数を定義する

多くの方が考えるのとは反対で、プログラマーはコードをひたすら打ち続けるわけではありません。逆に、何かを始める前に、プログラマーは何をすべきかを考え抜き、勉強しなければならないのです。

ですから、これからキャリアをスタートさせる皆さんも同じように、コードを書き加える前に、あらゆる場面を想定しておくことが必要です。MetaTrader 5プラットフォームを最大限に活用するために、C_Ordersクラスに本当に必要なものは何か考えてみましょう。

頭に浮かぶ唯一のことは、注文がどのように送信されるかです。MetaTrader 5プラットフォームでは、注文の移動や削除、ポジションの決済など、あらゆることが可能なため、何らかの手段を必要とするわけではありません。チャート上で注文を出すと、ツールボックスウィンドウの[取引]タブに表示されるので、この点についてはあまり気にする必要はないでしょう。

もう1つ、少なくともこの最初の段階では、注文やポジションをチャートに表示する仕組みは、プラットフォームがやってくれますので、特に必要ありません。実際には、MetaTrader 5のオーダーシステムを経由せずに、EAから直接注文を送信する何らかの方法を実装する必要があるだけです。

この考えに基づいて、この初期段階で本当に必要なもの、つまりEAが注文を送信するための関数やプロシージャのプログラミングを始めることができます。この時点で、多くの人、特にプログラミングの知識や経験があまりない人は、間違いを犯し始めます。

MetaTrader 5を使い、プラットフォームのツールボックスで注文やポジションを管理することは既に決定しているので、最も基本的な部分であるサーバーに注文を送るシステムを開発する必要があるのです。ただし、これをおこなうには、MQL5標準ライブラリ関数呼び出しによって常にこの情報を探す必要がないように、EAが使用する資産に関するいくつかの情報を知っておく必要があります。まず、クラス内部でいくつかのグローバル変数を定義してみましょう。

class C_Orders
{
        private :
//+------------------------------------------------------------------+
                MqlTradeRequest m_TradeRequest;
                struct st00
                {
                        int     nDigits;
                        double  VolMinimal,
                                VolStep,
                                PointPerTick,
                                ValuePerPoint,
                                AdjustToTrade;
                        bool    PlotLast;
                }m_Infos;

これらの変数は必要なデータを格納するもので、クラス全体を通して見ることができます。privateの後に宣言されているので、クラスの外からアクセスすることはできません。つまり、クラス内でのみ閲覧やアクセスが可能です。

次に、これらの変数を何らかの方法で初期化する必要があります。いくつかの方法がありますが、私の経験上、これを忘れてしまうことが多いです。忘れないようにするために、以下のようにクラスのコンストラクタを使用することにします。以下の例を見てみましょう。

                C_Orders()
                        {
                                m_Infos.nDigits         = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
                                m_Infos.VolMinimal      = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
                                m_Infos.VolStep         = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
                                m_Infos.PointPerTick    = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
                                m_Infos.ValuePerPoint   = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
                                m_Infos.AdjustToTrade   = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
                                m_Infos.PlotLast        = (SymbolInfoInteger(_Symbol, SYMBOL_CHART_MODE) == SYMBOL_CHART_MODE_LAST);
                        };

クラスコンストラクタは、クラスを操作する前に初期化する安全な方法です。なお、ここではデフォルトのコンストラクタを使用しています。つまり、何もパラメータを受け取りません。コンストラクタは、クラス内の何かを初期化するためにパラメータを受け取る場合がありますが、ここではその必要はありません。

次に、それぞれの変数がある方法で初期化されていることに注目してください。テストであれ、特定の計算であれ、いずれかの値にアクセスしたときに、適切に初期化されるようになっています。このため、グローバル変数と内部クラス変数の初期化には、コード内で他の処理がおこなわれる前に、常にクラスコンストラクタを優先的に使用する必要があります。

コンストラクタを使用しているので、クラスのデストラクタも宣言する必要があります。以下のような簡単なもので大丈夫です。

                ~C_Orders() { }

構文を見てください。コンストラクタとデストラクタの名前は、クラス名と同じです。ただし、デストラクタの宣言の前にはチルダ(~)がついています。もう1つ注意しなければならないのは、コンストラクタにもデストラクタにも戻り値がないことです。戻り値を持たせようとするとエラーとみなされ、コードをコンパイルできなくなります。

クラスの初期化時や終了時に値を返す必要がある場合は、通常のプロシージャコールを使用する必要があります。コンストラクタやデストラクタは、この目的には使用できません。

では、コンストラクタとデストラクタを使ったこのようなコーディングは、具体的にどのように役立つのでしょうか。前述したように、グローバル変数を初期化せずにアクセスしたり使用しようとすることは、非常によくあることです。そのため、経験豊富なプログラマーであっても、異常な結果が出たり、プログラミングエラーが発生したりすることがあります。コンストラクタでグローバルクラス変数を初期化することで、必要なときにいつでも利用できるようにしています。

実際にコンストラクタやデストラクタを使わずにオブジェクト指向プログラミングを使ってコーディングする悪い習慣を身につけないようにしましょう。そのような習慣を身に着けてしまうと、プログラムが正常に動作しない原因を突き止めるのに多くの頭痛の種を抱えることになります。

続ける前に、クラスのコンストラクタに存在する1つの詳細に注目してください。以下にその内容を紹介します。

        m_Infos.PlotLast = (SymbolInfoInteger(_Symbol, SYMBOL_CHART_MODE) == SYMBOL_CHART_MODE_LAST);

多くの方が考えているのとは逆に、市場間には違いがありますが、それは皆さんが想像しているようなものではありません。その違いは、チャート上の資産のプロットの仕方にあります。前の行は、資産に使用されるヒストグラムの種類を決定します。

なぜそれが重要なのでしょうか。異なる市場や資産で動作するEAを作成したい場合、それらが異なる表示システムを持つ可能性があることを忘れてはいけません。基本的には、大きく分けて2つの方法があります。

  • 外国為替市場で見られるBIDグラフのプロット
  • 株式市場でよく見られるグラフのプロット

一見同じように見えますが、EAと取引サーバーはこれらを異なるプロットタイプとみなします。これは、EAが注文を送信し始めたときに特に明確になり、重要になります。最適な価格で執行される成行注文ではなく、指値注文でです。この場合、市場深度に残るような注文を送信できるクラスを作成することが重要です。

しかし、なぜEAがどのグラフィック表現システムを使用しているかを知る必要があるのでしょうか。理由は簡単です。資産がBIDベースの表現を使用する場合、LAST価格は常にゼロになるからです。この場合、EAはどの注文タイプが正しいのかが分からないため、ある種類の注文をサーバーに送信することができません。仮に注文を送信し、サーバーがそれを受理したとしても、注文の種類が誤って記入されているため、注文は正しく執行されません。

この問題は、株式市場用のEAを作成して、それをFXで使おうとすると発生します。逆に、システムがLAST価格をプロットするがEAがBID価格に基づいてプロットされるFX市場向けに作成されている場合、BIDとASKが固定されたまま、LAST価格が変化しうるので、EAは適切なタイミングで注文を送信できない可能性があります。

株式市場では、BIDとASKの差は常にゼロより大きくなり、両者の間には常にスプレッドが存在します。問題は、サーバーに送信する注文を作成する際に、特にBIDとASKの値を尊重しない場合、EAがそれを誤って作成する可能性があるということです。これは、既存のスプレッド内で取引しようとするEAにとって致命的となりえます。

このため、EAはどの市場(というよりプロットシステム)で作動しているかを確認するので、何の修正も再コンパイルもせずにFXと株式市場の間で移植することができます。

小さなニュアンスがいかに重要であるかは、すでにお分かりいただけたと思います。ほんの些細なことで、すべてが危うくなることがあるのです。次に、指値注文を取引サーバーに送信する方法を説明します。


指値注文をサーバーに送信する

取引システムの仕組みに関連してMQL5言語のドキュメントを勉強していたところ、OrderSend関数があることを知りました。MetaTrader 5の標準ライブラリとそのCTradeクラスを使用することができるため、この関数を直接使用する必要はありません。ただし、ここでは、この関数が裏側でどのように機能しているかを紹介したいと思います。

サーバーに操作リクエストを送るにはOrderSend関数を使えばよいのですが、この関数を直接使う場合は注意が必要です。クラス内でのみ使用されるようにするため、privateの中に入れます。このようにして、リクエストの記入に関連する複雑な部分をすべてクラスのpublic関数に分離し、EAがアクセスできるようにすることができます。

細心の注意を払い、必要なテストをおこない、いつうまくいって、いついかないかを知ることが重要です。すべてのリクエストはOrderSend関数を経由するので、以下のようにそのためのプロシージャを作成するのが便利です。

                ulong ToServer(void)
                        {
                                MqlTradeCheckResult TradeCheck;
                                MqlTradeResult      TradeResult;
                                bool bTmp;
                                
                                ResetLastError();
                                ZeroMemory(TradeCheck);
                                ZeroMemory(TradeResult);
                                bTmp = OrderCheck(m_TradeRequest, TradeCheck);
                                if (_LastError == ERR_SUCCESS) bTmp = OrderSend(m_TradeRequest, TradeResult);
                                if (_LastError != ERR_SUCCESS) MessageBox(StringFormat("Error Number: %d", GetLastError()), "Order System", MB_OK);
                
                                return (_LastError == ERR_SUCCESS ? TradeResult.order : 0);
                        }

上のコードを怖く思わないでください。短く言えば、リクエストが送信されたことを保証し、正しく送信されたかどうかを通知するコードです。送信に失敗した場合は、即座に通知されます。では、その仕組みについて詳しく見ていきましょう。まず、内部エラー変数をリセットし、EA内のどの時点でも生成されたエラーを分析できるようにします。

最も重要なプロシージャの戻り値を常に確認することを忘れないでください。これが終わると、関数の内部構造をリセットし、注文を確認するためのリクエストを送ります注意:どのエラーが発生したかを調べるために、失敗が返されたかどうかを確認するのではなく、エラー変数の値を確認します。予想外の値が示された場合、注文はサーバーに送信されません。確認してエラーがなければ、注文は取引サーバーに送信されます。.

誰がエラーを発生させたかに関わらず、エラーが発生した場合はそのコードがチャートに表示される点が大きな違いです。すべてが完璧であれば、メッセージは表示されません。こうすれば、サーバーに送るリクエストの種類は関係ありません。上に示したプロシージャのみを使用したならば、処理が常に同じタイプになってしまいます。より多くの情報やテストが必要な場合は、上記のプロシージャに追加すれば十分です。そうすることで、システムが可能な限り安定し、信頼できるものであることが常に保証されます。

この関数は、成功した場合にサーバーから返されるオーダーチケットと、エラーを示すゼロの2つの値を返すことができます。エラーの種類を知るには、変数_LastErrorの値を確認します。自動売買EAでは、このメッセージはEAの目的に応じて、別の場所に置かれるか、あるいは単なるログメッセージになるでしょうが、ここではEAの作り方を紹介するのが目的なので、どこからメッセージが来ているのかわかるように、この情報を追加します。

次に、解決すべきもう1つの問題があります。注文が拒否される可能性があるため、任意のランダムな価格をサーバーに送ることはできないということです。正しい価格を入力する必要があります。値を正規化すればよいという人もいますが、ほとんどの場合、これはうまくいきません。実は、正しい価格を使うためには、ちょっとした計算が必要です。ここでは、以下の関数を使用します。

inline double AdjustPrice(const double value)
                        {
                                return MathRound(value / m_Infos.PointPerTick) * m_Infos.PointPerTick;
                        }

この簡単な計算で価格が調整されるため、入力された値にかかわらず、適切な値に修正され、サーバーに受け入れられます。

心配事が1つ減りました。しかし、上記の関数を作っただけでは、注文を作ったり、サーバーに送ったりすることはできません。そのためには、MqlTradeRequest構造体を埋める必要があります。この構造体は、前のコードですでに出てきており、privateグローバル変数m_TradeRequestを通してアクセスできます。この構造体も正しく埋める必要があります。

各リクエストは、特定のフォームで記入される必要がありますが、ここでは、サーバーが指値注文を正しく作成できるように、リクエストを送信したいだけです。これが完了すると、チャート上とツールボックス内に指値注文が表示されます。

この構造体を埋めることは、手動および自動モードの両方でEAに問題を引き起こすことになる主な理由の1つです。そこで、クラスを使って抽象化をおこない、リクエストが取引サーバーに受け入れられるように、正しい記入を促すことにします。

この関数を作る前に、少し考えてみましょう。いくつかの質問に答える必要があります。

  • どのような操作をおこなうか(買いか売りか)
  • どのような価格で取引を実行したいか
  • ボリューム
  • 希望する期間
  • 買い指値、買い逆指値、売り指値、売り逆指値(今は売り逆指値、買い逆指値の話はしていない)などどのような注文を使うか
  • FXと株式市場のどちらで取引するのか
  • どのようなストップロスを設定するか
  • どのようなテイクプロフィットを目指しているのか
  • ご覧のように、いくつかの質問があり、サーバーがリクエストする構造で直接使えないものもあります。そこで、EAが問題なく動作するように、より実用的なモデリングを実現するための抽象化をおこないます。調整や修正はすべてクラスでおこないます。したがって、次のような関数が得られます。

                    ulong CreateOrder(const ENUM_ORDER_TYPE type, double Price, const double FinanceStop, const double FinanceTake, const uint Leverage, const bool IsDayTrade)
                            {
                                            double  bid, ask, Desloc;                      
    					
                                    	Price = AdjustPrice(Price);
                                            bid = SymbolInfoDouble(_Symbol, (m_Infos.PlotLast ? SYMBOL_LAST : SYMBOL_BID));
                                            ask = (m_Infos.PlotLast ? bid : SymbolInfoDouble(_Symbol, SYMBOL_ASK));
                                            ZeroMemory(m_TradeRequest);
                                            m_TradeRequest.action           = TRADE_ACTION_PENDING;
                                            m_TradeRequest.symbol           = _Symbol;
                                            m_TradeRequest.volume           = NormalizeDouble(m_Infos.VolMinimal + (m_Infos.VolStep * (Leverage - 1)), m_Infos.nDigits);
                                            m_TradeRequest.type             = (type == ORDER_TYPE_BUY ? (ask >= Price ? ORDER_TYPE_BUY_LIMIT : ORDER_TYPE_BUY_STOP) : 
                                                                                                        (bid < Price ? ORDER_TYPE_SELL_LIMIT : ORDER_TYPE_SELL_STOP));
                                            m_TradeRequest.price            = NormalizeDouble(Price, m_Infos.nDigits);
                                            Desloc = FinanceToPoints(FinanceStop, Leverage);
                                            m_TradeRequest.sl               = NormalizeDouble(Desloc == 0 ? 0 : Price + (Desloc * (type == ORDER_TYPE_BUY ? -1 : 1)), m_Infos.nDigits);
                                            Desloc = FinanceToPoints(FinanceTake, Leverage);
                                            m_TradeRequest.tp               = NormalizeDouble(Desloc == 0 ? 0 : Price + (Desloc * (type == ORDER_TYPE_BUY ? 1 : -1)), m_Infos.nDigits);
                                            m_TradeRequest.type_time        = (IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC);
                                            m_TradeRequest.type_filling     = ORDER_FILLING_RETURN;
                                            m_TradeRequest.deviation        = 1000;
                                            m_TradeRequest.comment          = "Order Generated by Experts Advisor.";
                                    
                                            return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? ToServer() : 0);
                    };
    
    

    このシンプルな関数が、素晴らしい働きをしてくれるのです。FXでも株でも、取引したいデータを受け取り、サーバーが期待する形に変換してくれます。

    この関数を使うには、売買したいもの、価格、ストップロス、テイクプロフィット、レバレッジ、そして日中取引か長期取引かを指示するだけです。この関数は、注文が正しく作成されるように、残りの部分を計算します。しかし、そのためには、適切な値を持つための計算や調整が必要になります。上記の関数を使用する場合、サーバーが注文を作成する際の問題を回避するために、以下の2点に留意することが重要です。

    このような問題から、注文がサーバーに拒否されることがよくあります。ストップロスとテイクプロフィットです。金融値を1つでもゼロで示すと、この関数はテイクプロフィットやストップロスを生成しません。そのため、ストラテジーがリクエストしている場合にゼロを指定すると、ストップレベルが作成されないので注意が必要です(後で指定することもできますが、この時点では追加されません)。もう1つは、買い取引のストップロス値が負、売り取引のテイクプロフィット値が負では意味がないということです。関数はこれらを無視します。正の値を使用すれば、すべてうまくいきます。

    この関数を見ると、あまりに複雑すぎて混乱してしまうかもしれませんが、その動作を見れば、きっと考えが変わるはずです。その動作を見る前に、上記のプロシージャに出てくるFinanceToPoints関数を解析してみましょう。FinanceToPoints関数のコードを以下に示します。

    inline double FinanceToPoints(const double Finance, const uint Leverage)
                            {
                                    double volume = m_Infos.VolMinimal + (m_Infos.VolStep * (Leverage - 1));
                                    
                                    return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade)));
                            };
    
    

    ちょっと厄介なので、このあたりを考えてみましょう。この関数を理解していないと、クラスがサーバーに注文を出し、サーバーがエントリ価格から一定のずれを持ったテイクプロフィットとストップロスの注文を出すとどうなるのか理解することが難しくなります。したがって、この関数を理解するためには、システムの仕組みを理解することが重要です。

    どんな資産にも、金融値をポイントに変換できる情報があります。それを理解するために、以下の画像をご覧ください。

    図05図06図07


    上記の数値はすべて、金融値をポイントに換算するために使用するポイントを強調したものです。FX銘柄の場合、チケットのサイズと価値は表示されませんが、チケットの価値は1.0、サイズ(ポイント数)は0.00001となります。この値を忘れないでください。

    ここで、金融値とは、取引量をポイントの数で割ったものに、各ポイントの値を乗じたものであると考えてみましょう。例えば、ある資産を最低取引量100と注文サイズ2x(つまり最低取引量の2倍)で取引すると仮定します。この場合、取引量は100×2、すなわち200となります。とりあえずこの値は気にしないで、続けましょう。各ポイントの値を求めるには、次のような計算をします。

    1ポイントの値は、1ポイントあたりの値をポイントの数で割ったものとなります。多くの人がポイント数は1だと思い込んでいますが、これは間違いです。上の図では、ポイント数が異なる場合があることがお分かりいただけると思います。FXの場合は0.00001です。そのため、ポイント値には注意が必要です。プログラムに正しい値を取り込ませ、それを使用します。例えば、ポイント数が0.01で、各ポイントの値が0.01であるとします。この場合、一方の値を他方の値で割ると、値が1になります。ただし、この値は異なる場合があります。例えば、FXでは0.00001ですが、B3 (Bolsa do Brasil) で取引されるドルでは10となります。

    さて、もう1つのことに話を進めましょう。簡単に説明するために、ユーザーが最小取引量100の資産に対して、取引量x3の取引を実行したいと仮定してみましょう。チケットのサイズは0.01で、値は0.01です。それでもユーザーは250という金銭的なリスクを取りたいと考えています。この250という値に合わせるには、エントリの価格から何ポイントオフセットすればよいのでしょうか。上記のプロシージャでおこなうのはこれです。これは値を計算し、金融値が250になるように場合によって正負にシフトするように調整します。この場合、2.5ポイントまたは250ポイントになります。

    この例は簡単そうに見えます。しかし、ボラティリティが高いときに市場で取引しながら、迅速にそれをしようとすると、受け入れられるリスクの制限を超えずに、より多く開けたことを後悔しないように、非常に迅速に使用するサイズを決定する必要があります。この時点で、しっかりとプログラムされたEAを味方につけることがいかに重要であるかがおわかりいただけると思います。

    このクラスを使用すると、EAは適切に設定された注文をサーバーに送信することができます。それがどのようにおこなわれるかを見るために、私たちのオーダーシステムをテストする方法を作ってみましょう。


    オーダーシステムのテスト

    この連載の目的は、手動でコントロールできるEAを作ることではないことを強調しておきたいと思います。その目的は、初心者のプログラマーが、できるだけ少ない労力で自動売買EAを作成する方法を紹介することです。手動取引用のEAの作り方を知りたい方は、別連載の「一からの取引エキスパートアドバイザーの開発」をお読みください。その中で、手動取引用のEAを作る方法を紹介しています。しかし、その連載は少し時代遅れなところがあり、すぐにその理由がわかると思います。とりあえずは、ここでの主要な目標に集中します。

    EAのオーダーシステムをテストし、注文が取引サーバーに送信されるかどうかを確認するために、いくつかのトリックを使用します。その方法をみてみましょう。通常はチャートに水平線を追加するのですが、今回はオーダーシステムが機能しているかどうかを確認したいだけです。そこで、テストを容易にするために、次のような使い方をします。

    #property copyright "Daniel Jose"
    #property description "This one is an automatic Expert Advisor"
    #property description "for demonstration. To understand how to"
    #property description "develop yours in order to use a particular"
    #property description "operational, see the articles where there"
    #property description "is an explanation of how to proceed."
    #property version   "1.03"
    #property link      "https://www.mql5.com/pt/articles/11223"
    //+------------------------------------------------------------------+
    #include <Generic Auto Trader\C_Orders.mqh>
    //+------------------------------------------------------------------+
    C_Orders *orders;
    ulong m_ticket;
    //+------------------------------------------------------------------+
    input int       user01   = 1;           //Lot increase
    input int       user02   = 100;         //Take Profit (financial)
    input int       user03   = 75;          //Stop Loss (financial)
    input bool      user04   = true;        //Day Trade ?
    input double    user05   = 84.00;       //Entry price...
    //+------------------------------------------------------------------+
    int OnInit()
    {
            orders = new C_Orders();
            
            return INIT_SUCCEEDED;
    }
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
            delete orders;
    }
    //+------------------------------------------------------------------+
    void OnTick()
    {
    }
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
    {
    #define KEY_UP          38
    #define KEY_DOWN        40
    
            switch (id)
            {
                    case CHARTEVENT_KEYDOWN:
                            switch ((int)lparam)
                            {
                                    case KEY_UP:
                                            m_ticket = orders.CreateOrder(ORDER_TYPE_BUY, user05, user03, user02, user01, user04);
                                            break;
                                    case KEY_DOWN:
                                            m_ticket = orders.CreateOrder(ORDER_TYPE_SELL, user05, user03, user02, user01, user04);
                                            break;
                            }
                            break;
            }
    #undef KEY_DOWN
    #undef KEY_UP
    }
    //+------------------------------------------------------------------+
    
    

    シンプルなコードですが、このコードでオーダーシステムをテストすることができます。そのためには、以下の手順で希望する価格で注文してください。

    1. 注文をおこなう価格を指定します。現在の価格は使用できないことを忘れないでください。
    2. 価格が上がると思えば上矢印を、価格が下がると思えば下矢印を使用します。
    3. 次に、ツールボックスウィンドウの[取引]タブを開きます。EAが指定した条件で注文が表示されるはずです。

    データは、EAがユーザーと対話できる形式に示されるように入力されます。どのようにコンストラクタでクラスを初期化するかにご注意ください。また、デストラクタのコードにもご注意ください。こうすることで、クラスの内部手続きを呼び出す前に、必ず初期化されていることを確認することができます。


    結論

    このEAは非常にシンプルですが、オーダーシステムをテストし、それを使って注文を送ることができるようになります。ただし、システムが正しい位置に正しい値で注文を出しているかどうかを確認することができないので、現在の価格を使用しないことを覚えておいてください。

    以下の動画では、正しい操作のデモを見ることができます。添付ファイルでは、今回取り上げたコードの完全版を提供しています。実験や勉強に使ってください。

    これはまだ最初の段階で、次回はもう少し面白くなりそうです。この段階では、オーダーブックにどのように注文が出されるかをよく理解しておくことが重要です。


    デモ映像


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

添付されたファイル |
自動で動くEAを作る(第03回):新しい関数 自動で動くEAを作る(第03回):新しい関数
今日は、自動モードでシンプルかつ安全に動作するエキスパートアドバイザー(EA)を作成する方法を紹介します。前回は、自動売買EAで使用するオーダーシステムの開発に着手しましたが、必要な関数のうち1つしか作っていません。
自動で動くEAを作る(第01回):概念と構造 自動で動くEAを作る(第01回):概念と構造
今日は、自動モードでシンプルかつ安全に動作するエキスパートアドバイザー(EA)を作成する方法を紹介します。
母集団最適化アルゴリズム:コウモリアルゴリズム(BA) 母集団最適化アルゴリズム:コウモリアルゴリズム(BA)
今回は、滑らかな関数に対して良好な収束性を示すコウモリアルゴリズム(BA)について考えてみることにします。
DoEasy - コントロール(第31部):ScrollBarコントロールのコンテンツのスクロール DoEasy - コントロール(第31部):ScrollBarコントロールのコンテンツのスクロール
この記事では、水平スクロールバーのボタンを使用してコンテナのコンテンツをスクロールする機能を実装します。