
リプレイシステムの開発(第34回):発注システム (III)
はじめに
前回の「リプレイシステムの開発(第33回):発注システム(II)」稿では、発注システムをどのように構築するかを説明しました。その記事では、コードの大部分を見て、その複雑さと私たちが解決しなければならない問題に触れました。コードはシンプルで使いやすいように見えるかもしれませんが、それは真実とはほど遠いです。そこでこの記事では、私たちが実際に実装していること、そしてまだ実装する必要があることについて、もう少し深く掘り下げてみることにします。C_Managerクラスの最後のメソッドについては、まだ議論と説明が必要だし、EAのコードについてもコメントしなければなりません。EAコードの場合は、主に修正部分に焦点を当てます。こうすることで、MetaTrader 5プラットフォームでのテストに頼ることなく、どのように動作するかを知ることができます。この記事の付録にコードが含まれているので、いくらでもテストできます。
多くの方は、実際に実験する前により互換性のあるシステムがあればと思い、今示されているシステムをテストする必要はないと思うかもしれません。これは良い考えではありません。今システムがどのように機能しているかを理解していないと、将来それがどのように機能するかを理解するのに大きな問題が生じるからです。さらに悪いことに、読者が開発に興味がないのであれば、もしこれが何らかの理由でここに示されていない場合、見たいものを生成できるような形でそれを適応させることができません。システムが徐々に進化するにつれて、細部にもっと注意を払ったりテストしたりする、あるいはシステムが成熟して、実際にプラットフォームに載せて挙動を分析する時が来たと感じるまで待つこともできます。
多くの方がより洗練されたシステムを手に取って使用することを好む一方で、それが成長し発展するのを見ることを好む方もいらっしゃることは承知しています。まずはこれまで欠けていたメソッドを考えてみましょう。これは非常に大きなテーマなので、別のセクションを設けることにします。
C_Managerクラスの主要関数:DispatchMessage
この関数が、今回作成するクラス全体の中核であることは間違いません。これは、MetaTrader 5プラットフォームから生成されて私たちのプログラムに送信されるイベントを処理します。これらは、プラットフォームが私たちに送ってほしいイベントです(例:CHARTEVENT_MOUSE_MOVE)。その他にも、送信されるが、私たちが作成しようとしているプロジェクトにはあまり役に立たないのでプログラムで無視することができるイベントがあります(例:CHARTEVENT_OBJECT_CLICK)。
すべてのイベント処理がクラスに集約されているため、プロジェクトをモジュール単位で実行するのが非常に簡単になっています。これは大変な作業のように思えるかもしれませんが、プロジェクト間のコードの移行が非常に簡単になり、それによって新しいプロジェクトの開発がスピードアップすることがすぐにわかるでしょう。
ここには2つの機会があります。
- まず、イベント処理を一箇所に集中させ、プロジェクト間でのコードの移植や移動を容易にすることで、メインコード(この場合はEA)に配置する必要のあるコードの量を減らすことができます。クラスを再利用するときにも、イベントを処理するときにも、発生する可能性のあるエラーの数を減らすことができるので、デバッグが非常に簡単になります。
- 2つ目の機会はもう少し複雑です。各プログラムがどのように機能するかということです。ある種のコードは、一定の順序で実行されるべきです。特定の順序で実行されなければならないコードを書くことはよくあります。
この2点を考慮すれば、そのメソッドのコードを見て、なぜそのメソッドが実行されるのかを調べ、理解することができます。
void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) { static double price = 0; bool bBuy, bSell; def_AcessTerminal.DispatchMessage(id, lparam, dparam, sparam); def_AcessMouse.DispatchMessage(id, lparam, dparam, sparam); switch (id) { case CHARTEVENT_KEYDOWN: if (TerminalInfoInteger(TERMINAL_KEYSTATE_CONTROL)) { if (TerminalInfoInteger(TERMINAL_KEYSTATE_UP)) ToMarket(ORDER_TYPE_BUY); if (TerminalInfoInteger(TERMINAL_KEYSTATE_DOWN))ToMarket(ORDER_TYPE_SELL); } break; case CHARTEVENT_MOUSE_MOVE: bBuy = def_AcessMouse.CheckClick(C_Mouse::eSHIFT_Press); bSell = def_AcessMouse.CheckClick(C_Mouse::eCTRL_Press); if (bBuy != bSell) { if (!m_Objects.bCreate) { def_AcessTerminal.CreateObjectGraphics(def_LINE_PRICE, OBJ_HLINE, m_Objects.corPrice, 0); def_AcessTerminal.CreateObjectGraphics(def_LINE_STOP, OBJ_HLINE, m_Objects.corStop, 0); def_AcessTerminal.CreateObjectGraphics(def_LINE_TAKE, OBJ_HLINE, m_Objects.corTake, 0); EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_HideMouse, 0, 0, ""); m_Objects.bCreate = true; } ObjectMove(def_InfoTerminal.ID, def_LINE_PRICE, 0, 0, def_InfoMouse.Position.Price); ObjectMove(def_InfoTerminal.ID, def_LINE_TAKE, 0, 0, def_InfoMouse.Position.Price + (Terminal.FinanceToPoints(m_Infos.FinanceTake, m_Infos.Leverage) * (bBuy ? 1 : -1))); ObjectMove(def_InfoTerminal.ID, def_LINE_STOP, 0, 0, def_InfoMouse.Position.Price + (Terminal.FinanceToPoints(m_Infos.FinanceStop, m_Infos.Leverage) * (bSell ? 1 : -1))); if ((def_AcessMouse.CheckClick(C_Mouse::eClickLeft)) && (price == 0)) CreateOrder((bBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL), price = def_InfoMouse.Position.Price); }else if (m_Objects.bCreate) { EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_ShowMouse, 0, 0, ""); ObjectsDeleteAll(def_InfoTerminal.ID, def_Prefix); m_Objects.bCreate = false; price = 0; } break; } }
このコードを恐れないでください。一見複雑に見えるかもしれませんが、恐れる必要はありません。やっていることはすべて非常にシンプルで、比較的一般的なことです。コードの見た目は、非常に複雑で理解しにくいという第一印象を与えます。では、順を追って説明します。まず、最初の呼び出しを見てみましょう。よりよく説明するために、部分に分けて説明した方が理解しやすいと思います。いくつかのポイントに絞って説明するので、ページをスクロールして内容を探す必要はありません。
def_AcessTerminal.DispatchMessage(id, lparam, dparam, sparam); def_AcessMouse.DispatchMessage(id, lparam, dparam, sparam);
ある時点で、すべてが一定の順序で起こる必要があると話したのを覚えていらっしゃるでしょうか。上の2行がまさにそれです。EAコードの中で、イベント処理を忘れたり、(さらに悪いことに)間違った順序で配置したりすることを防いでくれます。コードを書いている現段階では、このことが今すぐ影響を及ぼすというわけではありませんが、EAに配置するコードが少なければ少ないほど、将来的にはより良いものになるでしょう。あるイベントを見逃すことで、プロジェクト全体がまったく予期せぬ形で動いてしまったり、思い通りにいかなくなってしまったりします。
これはすべて明確です。次に、CHARTEVENT_KEYDOWNイベントから見ていきましょう。キーを押したときに発生するトリガーを処理してくれます。
case CHARTEVENT_KEYDOWN: if (TerminalInfoInteger(TERMINAL_KEYSTATE_CONTROL)) { if (TerminalInfoInteger(TERMINAL_KEYSTATE_UP)) ToMarket(ORDER_TYPE_BUY); if (TerminalInfoInteger(TERMINAL_KEYSTATE_DOWN))ToMarket(ORDER_TYPE_SELL); } break;
ここでは、少し分かりにくいかもしれない状況があります。ドキュメントによれば、lparam変数には押されたキーのコードが格納されます。それはそうなのですが、問題なのは、私たちは少し違った方法で物事を進める必要があるということです。キーが押されると、オペレーティングシステムは特定のイベントを発生させます。MetaTrader 5がオペレーティングシステムからフォーカスを受け取ると、生成されたイベントを渡します。このコードでは、キー押下行動のハンドラがあるので、キー押下処理を他タイプのイベントから分離することができます。
注:上記のコードは、実際には CHARTEVENT_KEYDOWNイベントに配置する必要はありません。このイベントの外に配置する可能性もあります。しかし、CHARTEVENT_KEYDOWNに置くことで、恥ずかしい事態を防ぐことができます。このような状況は、キーコンディション、つまり CHARTEVENT_KEYDOWNイベントを分析する際に、プラットフォームが何らかの理由で発生した他タイプのイベントに対して警告を発している場合に発生します。
CHARTEVENT_CHART_CHANGEのようなイベントが発生したときのキーボード状態の処理について考えてみましょう。これは、チャート上で何らかの変化が発生したときに実際にアクティブになります。そして同時に、私たちのプログラムはキーボードの状態を確認します。そのようなことは現実的な意味を持たないし、実行するには多くの時間を必要とします。これが、CHARTEVENT_KEYDOWNイベントでキーボードの状態の解析を分離している理由です。
コードに戻りましょう。TerminalInfoInteger関数を使用して、特定のキーボードコードを認識し、分離していることがおわかりでしょう。もしそうしなければ、CTRLキーが他のキー(この場合、↑矢印キーまたは↓矢印キー)と同時に押されたかどうかを確認するために、余計な努力をしなければなりません。これこそが私たちの仕事です。私たちのプログラム(この場合はEA)がプログラミングに関して何をすべきかを知るために、キーボードショートカットが必要なのです。CTRL+上矢印の組み合わせを押すと、EAは市場価格で買いたいことを理解するはずです。CTRL + DOWN ARROWの組み合わせが押された場合、EAは市場価格で売りに進むはずです。lparam変数はキーの値を個別に指定しますが、これはキーボードショートカットの操作には役立たないことにご注意ください。しかし、今のようにキーの組み合わせの1つだけを押す方法では、EAは市場価格で取引する指示を受け取りません。
この組み合わせが使用しているものと何らかの形で競合する可能性があると思われる場合は変更できますが、MetaTrader 5がキーイベントを発生させた時のみ実行されるという事実を利用し、不必要なコードの実行を防ぐため、注意深くCHARTEVENT_KEYDOWNイベント内にコードを保持してください。もう1つは、lparam変数に表示されるキーコードが、地域によって異なるテーブルに従っていることです。それは物事をさらに複雑にします。ここで紹介する方法は、実際にはそのようなテーブルは使用しません。
次のイベントハンドラ、CHARTEVENT_MOUSE_MOVEを見てみましょう。説明を簡単にするために、クラスコードに登場する順番に、小さな部分に分けて説明します。
case CHARTEVENT_MOUSE_MOVE: bBuy = def_AcessMouse.CheckClick(C_Mouse::eSHIFT_Press); bSell = def_AcessMouse.CheckClick(C_Mouse::eCTRL_Press);
1つのことに注意を払います。ここでは、C_Studyクラスを使用してC_Mouseクラスにアクセスしています。また、前述のCHARTEVENT_KEYDOWNイベントハンドラとは異なり、ここではボタンの状態をキャプチャしていることに注意してください。これはマウスを指しています。気になりませんか。実際、これらのボタンは英数字キーボードではなくマウスに属しています。なぜでしょうか。読者を混乱させようとしているわけではありません。英数字キーボードでSHIFTとCTRLを押しても、C_Mouseクラスの中で何とかできるというのは、そういうわけにはいかないからです。これらのSHIFTキーとCTRLキーは、実際にはマウスに属していますが、すべてのマウスではありません。私が言っているのは、非常に特殊なタイプのマウスのことで、多かれ少なかれ図01に示したものに似ています。
図01:
こタイプのマウスでは、本体にボタンが追加されています。オペレーティングシステムにとって、したがってプラットフォームにとっても私たちのプログラムにとっても、SHIFTキーとCTRLキーは、実はマウスの一部なのです。しかし、マウスにはそのような追加ボタンがないため、オペレーティングシステムはキーボードの使用を許可し、そのおかげでプラットフォームとプログラムはコードが正しい方法で解釈されることを保証します。したがって、CHARTEVENT_KEYDOWNイベントのSHIFTキーと CTRLキーは、CHARTEVENT_MOUSE_MOVEイベントで使用されているものと混同しないでください。
SHIFTキーとCTRLキーの状態がわかったので、残りのイベントコードを見てみましょう。このことは、以下の断片から判断できます。
if (bBuy != bSell) { if (!m_Objects.bCreate) { def_AcessTerminal.CreateObjectGraphics(def_LINE_PRICE, OBJ_HLINE, m_Objects.corPrice, 0); def_AcessTerminal.CreateObjectGraphics(def_LINE_STOP, OBJ_HLINE, m_Objects.corStop, 0); def_AcessTerminal.CreateObjectGraphics(def_LINE_TAKE, OBJ_HLINE, m_Objects.corTake, 0); EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_HideMouse, 0, 0, ""); m_Objects.bCreate = true; } ObjectMove(def_InfoTerminal.ID, def_LINE_PRICE, 0, 0, def_InfoMouse.Position.Price); ObjectMove(def_InfoTerminal.ID, def_LINE_TAKE, 0, 0, def_InfoMouse.Position.Price + (Terminal.FinanceToPoints(m_Infos.FinanceTake, m_Infos.Leverage) * (bBuy ? 1 : -1))); ObjectMove(def_InfoTerminal.ID, def_LINE_STOP, 0, 0, def_InfoMouse.Position.Price + (Terminal.FinanceToPoints(m_Infos.FinanceStop, m_Infos.Leverage) * (bSell ? 1 : -1))); if ((def_AcessMouse.CheckClick(C_Mouse::eClickLeft)) && (price == 0)) CreateOrder((bBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL), price = def_InfoMouse.Position.Price); }else if (m_Objects.bCreate) { EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_ShowMouse, 0, 0, ""); ObjectsDeleteAll(def_InfoTerminal.ID, def_Prefix); m_Objects.bCreate = false; price = 0; }
このコードは複雑に見えますが、実際には3つの単純なことをおこなっています。これらは、クラス内の別々の関数やメソッドに分離することができますが、今のところは、少なくとも呼び出しに関して少しでも楽にするために、ここに残るでしょう。まず最初におこなうべきことは、EAを通じてプラットフォームに未決注文を出したい旨を伝えることです。しかしその前に、指値レベルと注文がどこに置かれるかを確認する必要があります。そのために、コードのこの時点で作成される3つのオブジェクトを使用します。また、C_Mouseクラスのシステムにもイベントを送り、C_Mouseクラスに、これからマウスを隠すべきことを伝えます。これが第一段階です。
必要なオブジェクトをチャート上に配置したら、それらを特定の方法で移動させます。そのために、この一連の関数を使用します。しかし、もしユーザーが、作成したオブジェクトを通じて、注文を出したい瞬間がチャートに表示されている瞬間であることを教えてくれれば、未決注文を出すというリクエストに応えることになります。マウスとチャート上で何が起こっているかを知るために、どのように確認がおこなわれているかにご注目ください。
最後の3点目については、次のように展開します。まず、C_Mouseクラスシステムにイベントが送られ、マウスがグラフ上に再び表示されるようになります。この後すぐに、作成したオブジェクトを削除します。
さて、このコードには重要なことがあります。自分でコードをプログラミングする際には、常にこの点に注意する必要があります。もし読者がプログラミングを始めたばかりなら、上のコードにある非常に興味深く、同時に危険な点に気づかないかもしれません。正しいやり方をしないと危険です。私が言っているのは再帰についてです。上のコードでは再帰を使用しています。これが正しく計画されていなければ、無限ループに陥ってしまいます。一度再帰を使用するコード部分に入ると、二度とそこから離れることはないでしょう。
この再帰がどのように起こるかを理解するには、すぐ下にある図02をご覧ください。
図02:内部メッセージの流れ
図02の緑の矢印は、まさに再帰がおこなわれる場所です。しかし、コード上ではどうなっているのでしょうか。C_ManagerクラスにあるDispatchMessageメソッドを見てみましょう。
void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) { // ... def_AcessMouse.DispatchMessage(id, lparam, dparam, sparam); switch (id) { // ... case CHARTEVENT_MOUSE_MOVE: // ... if (bBuy != bSell) { if (!m_Objects.bCreate) { // ... EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_HideMouse, 0, 0, ""); } // ... }else if (m_Objects.bCreate) { EventChartCustom(def_InfoTerminal.ID, C_Mouse::ev_ShowMouse, 0, 0, ""); // ... } break; } }
図02を見ても、再帰がよくわからないかもしれません。上のコードだけを見ても、よくわからないかもしれません。しかし、図02と上のコードを組み合わせれば、再帰がどのように機能するかがわかるでしょう。これが正しく計画されていなければ、深刻な問題を抱えることになります。経験の浅いプログラマーの方が再帰の威力と危険性の両方を理解できるように、説明に入楼と思います。非常に複雑なことをする必要があるときは、それが何であれ、プロセスをより小さく、より単純なタスクに分解する必要があります。これらの作業を一定回数繰り返すことで、単純な概念に従いながら、より複雑なものを完成させることができます。これが再帰の力です。しかし、このシナリオだけで使用されるわけではありません。このようなケースでよく使用されますが、再帰を使用して他のケースを解決することもできます。
上の断片もその1つです。説明を理解するために、もう一度図02をご覧ください。ユーザーがイベントを生成し、MetaTrader 5プラットフォームがOnChartEvent関数を呼び出すと開始します。この場合、この関数はC_ManagerクラスのDispatchMessageメソッドを呼び出します。この時点で、C_Mouseクラスに継承されているイベントハンドラ、特にこのクラスに存在するDispatchMessageメソッドを呼び出します。メソッドが戻ると、プログラムは中断したところから続行します。ユーザーがヘルパー行を作成したいのか削除したいのかを確認するために、イベントハンドラを入力します。そこで、ある時点でEventChartCustomを呼び出します。この時点で再帰が有効になります。
実際に起こることは、MetaTrader 5がOnChartEvent関数に新しい呼び出しをおこない、その関数が再度実行され、C_ManagerクラスのDispatchMessageメソッドが呼び出されるということです。これによって、C_Mouseクラスが継承によって呼び出され、状況に応じてマウスカーソルを表示したり消したりするカスタムメソッドが実行されます。しかし、再帰のため、このコードは多くの方が考えるようには戻りません。実際戻りますが、C_ManagerクラスのDispatchMessageメソッドで再びコードが実行されることになります。ここに危険があります。C_Mouseクラスで処理されたカスタムイベントがC_Managerクラスで表示されるように呼び出しを配置すると、C_Managerクラスでも処理されます。また、誤ってEventChartCustom関数を使用してC_Mouseクラスでこのイベントを再び処理すると、無限ループに陥ることになります。
しかし、C_Managerクラスはこのようなイベントを持たず、C_Mouseクラスによって処理されるため、OnChartEvent関数にフォールバックし、完全に実行されることになります。OnChartEvent関数が実行されると、EventChartCustom呼び出しがおこなわれたポイントに戻ります。これは上のコード部分に示されています。これにより、C_ManagerクラスのDispatchMessageメソッドに残っているすべてのコードが実行されます。それが完了すると、OnChartEvent関数に戻り、そこで完全に実行され、プラットフォームは他タイプのイベントを実行するために解放されます。
したがって、EventChartCustomを呼び出すと、再帰のためにOnChartEvent関数の少なくとも2倍のコードが実行されることになります。これは非効率的に見えますが、ポイントはコードがシンプルで、プラットフォーム全体のパフォーマンスに大きな影響を与えないということです。しかし、何が本当に起こっているのかを常に意識することはいいことです。この場合の再帰のコストは、よりモジュール化されたコードに比べてかなり低くなりますが、状況によっては、これらのコストがカバーできず、コードが遅くなりすぎる可能性があります。そのような場合は何か別の行動を取らなければならないでしょうが、今のところ私たちの場合はそうではありません。
C_Managerクラスで使用されているDispatchMessageメソッドについて詳しく説明したと思います。これは非常に複雑に見えるかもしれませんが、実際には、システムがまだクロスオーダーモデルをどのように扱うかを知らないため、真に複雑なものには程遠いです。そのためには、DispatchMessageメソッドに大幅な修正を加える必要がありました。しかし、これは将来のために残しておきましょう。
次に、EAコードのさらなる変更点を見てみましょう。
エキスパートアドバイザーで更新を分析する
EAは設定された取引時間内に注文を出し、取引することができるようになりましたが、そのコードに大きな変更はありません。コードの中に、特別な注意を払うべき部分があります。そこで何が起こっているのかを説明しましょう。この重要な点は、OnInitイベントにおけるユーザーとの対話とコードに関係しています。まずはユーザーとの対話から始めましょう。以下のコードに示します。
input group "Mouse"; input color user00 = clrBlack; //Price Line input color user01 = clrPaleGreen; //Positive Study input color user02 = clrLightCoral; //Negative Study input group "Trade"; input uint user10 = 1; //Leverage input double user11 = 100; //Take Profit ( Finance ) input double user12 = 75; //Stop Loss ( Finance ) input bool user13 = true; //Is Day Trade //+------------------------------------------------------------------+ input group "Control of Time" input string user20 = "00:00 - 00:00"; //Sunday input string user21 = "09:05 - 17:35"; //Monday input string user22 = "10:05 - 16:50"; //Tuesday input string user23 = "09:45 - 13:38"; //Wednesday input string user24 = "11:07 - 15:00"; //Thursday input string user25 = "12:55 - 18:25"; //Friday input string user26 = "00:00 - 00:00"; //Saturday
コードはユーザーとの対話を担当します。ここでは、ユーザーがアクセスし設定できる2つの新しい情報グループがあります。最初のグループでは、ユーザーは取引操作をどのように実行するかを選択します。設定はいたって簡単です。レバレッジを表す値(user10)は、その資産を取引するために最小取引量を使用する回数を表す必要があります。FXの場合、ほとんどの場合、100または類似の値を使用して、作業するのに適した証拠金を見つけるでしょう。そうでなければ、セント単位で作業することになり、限界線が期待した位置から遠く離れてしまいます。取引所で取引する場合は、使用株式数を報告しなければなりません。それ以外の場合は、ロット数を指定します。先物取引の場合は、契約数を指定します。とても簡単なことです。テイクプロフィット(user11)とストップロス(user12)については、ポイント数ではなく、使用する金融価値を示す必要があります。この値は、資産価格の正しい位置を反映するために、コードによって適宜調整されなければなりません。最後の変数(user13)は、ロングポジションかショートポジションかを示すだけです。
重要な注意事項:証券会社が非常に特殊な取引条件を持っている可能性があるため、この仕組みは注意してテストする必要があります。事前に証券会社にご確認ください。
さて、2つ目のグループでは、適切に設定する前に確認すべき点がいくつかあります。これは、これらの変数が複雑で理解しにくいからではなく、これらの変数によってEAがいつ注文を出したり、未決注文を出したりできるかが決まることを理解しておく必要があるからです。注文の管理、終了、あるいは変更さえも、もはやEAに依存することはないでしょう。MetaTrader 5プラットフォームが、少なくとも当面はこれを担当します。
そして、EAが持っているリソースを使用して動作することを許可する1時間のウィンドウを設定することができます。この設定は、特定の日や特別な日ではなく、1週間分おこなわれます。
これを理解するために、以下のOnInitコードをご覧ください。
int OnInit() { string szInfo; terminal = new C_Terminal(); study = new C_Study(terminal, user00, user01, user02); manager = new C_Manager(terminal, study, user00, user02, user01, def_MagicNumber, user12, user11, user10, user13); if (_LastError != ERR_SUCCESS) return INIT_FAILED; for (ENUM_DAY_OF_WEEK c0 = SUNDAY; c0 <= SATURDAY; c0++) { switch (c0) { case SUNDAY : szInfo = user20; break; case MONDAY : szInfo = user21; break; case TUESDAY : szInfo = user22; break; case WEDNESDAY : szInfo = user23; break; case THURSDAY : szInfo = user24; break; case FRIDAY : szInfo = user25; break; case SATURDAY : szInfo = user26; break; } (*manager).SetInfoCtrl(c0, szInfo); } MarketBookAdd(def_InfoTerminal.szSymbol); OnBookEvent(def_InfoTerminal.szSymbol); EventSetMillisecondTimer(500); return INIT_SUCCEEDED; }
上のOnInitコードにご注目ください。これは、特定の日だけでなく、その週にEAがどのように動作すべきかの全体像を表しています。1週間全体でです。資産や市場、この場合は外国為替市場があり、そこでは取引がほぼ継続的におこなわれ、1日中いつでも停止することはありません。24時間稼動するEAに個別の取引スケジュールを設定する必要がある場合、日替わり時間帯に問題が発生します。つまり、23:59:59になったらすぐにEAを停止し、次の秒に戻して新しい取引間隔を見つける必要があります。しかし、上記の方法を使用すれば、EAは1日24時間、週7日、1年52週稼働させることができ、迷ったり、どのタイムスケジュールを使えばいいのかわからなくなったりすることはありません。このコードを見ただけでは、これが実際にどのように起こるのか理解できない方も多いでしょう。したがって、このシステムがどのように機能するかを理解するために、EAをテストする必要があります。ただし、このシステムは新しいものではありません。これについては、以前の「自動で動くEAを作る(第10回):自動化(II)」稿ですでに説明しました。
結論
このシステムは非常に安定しており、多機能であるように見えますが、あるエラーに悩まされ始めました。このエラーの出現は意味をなさないため、非常に奇妙で不可解です。その理由は、何ら変更されていない場所でエラーが発生したからです。いずれにせよ、このエラーについてはまた次回に。添付ファイルには、現在の開発段階におけるコード全体が含まれており、詳細に調査分析することができます。前回の記事では、コードを添付しませんでした。
前回の記事では、プラットフォーム標準のダブルクリックモードとは対照的に、ワンクリックでオブジェクトを選択するシステムについてお話ししました。最良の選択肢は、読者のプラットフォームでシステムをテストし、実際に動作するのを見てから読者自身の結論を出すことです。ダウンロードして実行し、システムがどのように機能するかをご覧ください。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/11484





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