一からの取引エキスパートアドバイザーの開発(第22部):新規受注システム(V)
はじめに
新しいシステムを導入するのはそう簡単なことではありません。プロセスが非常に複雑になるような問題がしばしば発生します。このような問題が発生したら、「このままでよいのか、それとも新しく見直すべきか」と、一度立ち止まって進むべき方向を分析し直さなければなりません。
特に明確な期限や予算がない場合は、このような判断がかなり頻繁におこなわれます。プレッシャーがないときは、テストや調整をして、開発や改善の面で安定性を確保するために最善を尽くすことができるのです。
重要なプログラム、特に予算や納期が決まっている商用プログラムには、後で修正しなければならないようなエラーが多く含まれているのが普通です。システム開発者に、システムにとって非常に有益な解決策を実行したり学んだりする時間がないことはしょっちゅうです。このようにして、多くの場合、プログラマーが作るには不十分なプログラムが出来上がります。企業は小規模の改善やバグ修正をしたバージョンを次々とリリースすることがあります。これは、リリース後にバグが発見されたからではなく、リリース前にプログラマーがプレッシャーにさらされていたためです。
これは、実はプロダクションチェーン全体で起こっていることです。ただし、その解決策を作る過程で、私たちはベストな方法、できれば最も簡単な方法を探します。しかも、可能な限り実現可能な解決策を模索する余裕があります。必要であれば、システム開発のプロセスを修正・改善するために、少し立ち止まって戻ってみることもできます。小さな立ち止まりや方向転換が、目的のシステムの開発に大きく役立つことは非常に多いのです。
「一からの取引エキスパートアドバイザーの開発(第21回)」稿で、システムはほぼ出来上がっていました。欠落していたのはチャート上で直接注文を動かす部分だけです。この部分を実装するために確認してみると、コードに繰り返しの部分が多く、おかしなところがあることに気づきました。無駄な繰り返しはしないように気をつけていたのにです。さらに悪いことに、まったく異なる資産を使用した場合など、思うようにいかないこともありました。EAを設計し始めたとき、そしてここで公開している記事に記録し始めたとき、私は主にB3取引所での先物取引に使うことを考えていましたが、実際は、WDOやWINと呼ばれるドル先物やインデックスを取引するために開発することになりました。ただし、本来使いたいシステムに近づいていくにつれて、他の市場や資産にも拡張できることが分かってきました。ここで問題が発生しました。
1.0.クリップボードに戻る
この問題を理解するために、エキスパートアドバイザー(EA)を開発する際に多くの人が見落としている、ある重要な詳細に注目してみてください。取引システムにおいて、契約はどのように定義されるのでしょうか。MetaTrader 5はこの情報を提供し、MQL5でこの情報にアクセスすることができます。そこで、計算を汎用にし最も完成度の高いシステムを得るための数学を作るために、データを理解する必要があるのです。
ここで開発している注文システムは、他の外部リソースや将来作られるかもしれないものを一切必要とせず、チャートから直接取引できるように設計されています。この開発は、プラットフォームで使われているものに非常に近い取引システムを作ろうとする試みですが、完全にオープンソースなので、必要な情報をカスタマイズして追加することができます。
アイデアは、見るだけで、ポジションがどうなっているのかを知ることができるようにすることです。このアイデアは非常に良いのですが、B3のような外為や株式市場のシステムを作ろうとする人にとって、この取引システムは人生を楽にしてくれるものではありません。情報量はある市場に合わせた注文をカスタマイズするには十分ですが、汎用なものを作るのは、個人的な侮辱に近い問題になっています。しかし、私はこの問題に立ち向かい、普遍的なシステムを作ろうと決心しました。
本当にできるかどうかはわかりませんが、少なくとも必要な情報を見つける方法を示すことはできます。よって、読者がEAをご自分のローカルシステム用にカスタマイズする必要がある場合、その方法についての経験と知識を得ることができます。もともとモデリングに対応できないシステムであっても、ニーズに合わせて対応できるようになります。
考察:データはMQL5コードを使用して取得できますが、作業を簡略化するために、データがある場所を示すためにMetaTrader 5を使用します。
1.0.1.B3で取引されている会社の資産(株式)
次の2つの画像を見てください。
ハイライトされている部分は、ポジションをどのように配置するかを計算するために必要なデータです。
金融価値に基づいてポジションを作成する場合、これらの値はストップロスとテイクプロフィットの値を計算するために必要になります。OCO注文を使用しない場合は、さらに簡単ですが、ここでは、すべての注文がOCO注文またはポジションであると仮定します。
重要:資産が端株として取引されている場合(B3の市場によって違いがあります)、最小ボリュームは指定された値の1%になります。100対100の取引の代わりに、1対1の取引をするのはこのためです。計算が違ってくるので、忘れないようにしましょう。
1.0.2.B3における先物取引
先物取引のルールは株式取引のルールと異なり、ミニ先物かそうでないか、さらには資産によっても出来高が変わってきます。例えば、BOIを取引するには、ここでおこなうことの一部なので、レバレッジ(乗数)がどのように作成されているかを見る必要があります。契約を中心に説明します。先物は25のミニ先物と等しくなることもありますが、異なる場合もあるので、常に取引所のボリュームルールを確認する必要があります。
それでは、次のミニドルの画像をご覧ください。
これらの契約には期限がありますが、今回の記事ではそれは重要ではありません。「一からの取引エキスパートアドバイザーの開発(第11部)」では履歴から直接先物取引をおこなうクロスオーダーシステムの作り方を検討しており、この方法を使えば、現在取引されているのはどの契約かを気にする必要はありません。EAが代わりにおこなってくれるからです。印がついている部分は株式システムとは異なるので注目してください。このため、時々問題が発生することがあります。この部分は直感的ではありませんが、EAで対応しています。とにかく、時間が経つにつれて慣れて、特定の金融量を取引するためにレバレッジで使用すべき正しい値がわかるでしょう。
しかし、私はもっと高い目標を掲げることにしました。ここからが難所です。
1.0.3.外国為替
私の問題はFXのシステムに関するもので、私のEAでは解決できませんでした。なんとかFXのレバレッジに対処しようとしたところ、B3EAと同じコードが使えないことがわかりました。この部分だけが異なる2つのEAを用意する意味が分からないので、困りました。何が起こっているのかを理解するために、次の画像をご覧ください。
B3では4つの値を持っていますが、FXでは2つの値しか持っていません。2つの市場間の計算が一致しなかったのは、2つの値が欠落しているためです。
そのため、計算値が高すぎたり、最小予想ボリュームと比較してボリュームが小さすぎるため、乗数が原因で取引システムが注文を拒否したり、ポジションが間違っているためにOCO指値が受け付けられなかったりと、EAがおこなう計算によって実際におこなわれていることが非常に分かりにくくなっていました。あるのは果てしない混乱でした。
これがわかったので、EAを修正することにしました。これまでの計算方法ではなく、別の方法で値を算出することにします。実際、この値は、株式市場かFX市場かにかかわらず、扱う資産に応じて調整されるようになりました。そのため、EAがデータに適応し、正しく計算をおこなうことができるようになります。ただし、乗数を正しく設定するためには、何を取引しているのかを知る必要があります。また、現在は計算上の理由から、ボリュームに浮動小数点値を使用することを許可していますが、これは避けた方が良いでしょう。実際には、使用されているレバレッジを示す整数値を使用する必要があります。
そのため、EAにはいくらで取引したいかは伝えず、代わりに、最小許容量に適用される乗数を示します。こうして私は問題を解決しました。ご自身でテストして、その効果を実感してください。
理解しやすくして作業を軽減するために、次のトピックでは、結果を簡単に紹介します。
2.0.データの可視化
まず、株式市場、特にブラジル株式市場(B3)の場合について考えてみましょう。5のEAでは、このようにデータが表示されています。
2.0.1.B3資産
ブラジル証券取引所に上場している会社の株式の場合、最低取引高は100です。1を指定して、EAが許容される最小限の量を取引するように設定されます。最小ロットを正確に知る必要はなく、単に乗数を使用するだけで、EAが必要な計算をおこない、正しい注文を作成します。
分数値を使用する場合は、使用する分母を示す。つまり、50と指定すれば50分の1を使用し、15と指定すれば15分の1を使用する、といった具合です。
結果は、MetaTrader 5のツールボックスのウィンドウに以下のように表示されます。最小ロットの注文がどのように作成されたかが示されています。TP値とSL値を分析すると、チャート上に表示されている値と一致しており、ここでEAが動作したことが分かります。
2.0.2.ミニドル
ここではボリュームが1対1ですが、フルコントラクトの場合は値が異なります。Chart Tradeをご覧ください。TPとSLに表示されている値は、上記と同じですが、EAでは正しく値が調整されます。したがって、考慮すべき値は、チャートの指標に見られる値です。Chart Tradeの値は無視すべきですが、チャート上に示された値に近い値になります。
ツールボックスには、次のデータが表示されます。
先ほどの資産と同様に、計算を実行してストップロスやテイクプロフィットのレベルを確認すると、EAで指定したレベルと一致していることがわかります。つまり、コードを再コンパイルすることなく、資産を変更するだけでEAはこの段階を通過することができたのです。
2.0.3.外国為替
この部分は少し複雑です。FXに慣れていない人にとってはかなり不思議なポイントですが、それでもEAはなんとか理解しています。
MetaTrader 5は、この未決取引について、次のように通知します。
FXのレバレッジレベルは上記のものとは異なることを覚えておいてください。しかし、計算を実行すると、EAが正しいポイントを提供することができて、注文が完璧に作成されたことがわかります。
これはすべて、実装部分で示すものを除いて、追加の変更なしでおこなわれるため(これに加えて他の多くの変更はおこなわれましたが)、EA は再コンパイルの必要なく、株式市場と外国為替市場の両方に本当に適しています。ここで過去の記事を完結させます。ストップロスとテイクプロフィットのレベルをチャート上で正しく移動させる方法について見ていきます。
3.0.実装
まず、コードの変更点を見てみましょう。
最初に、3か4バージョン前に実装されたリミットのシステムを削除しました。EAが株式市場とFXのロット計算を誤って調整することがあるためです。
FXと株式市場で同じようにEAが動作するように、新しい計算モデルを追加しました。これは、このバージョン以前は不可能でした。EAは当初は株式市場に特化していましたが、取引手法に大きな違いがないことから、FXにも機能を拡張することにしました。
ロット計算の問題など、以前のバージョンでは対応できなかった内容もありますが、今回の変更により、大きなコード変更なしに、EAをFXと株式市場の両方で使用できるようになりました。ただし、FXや株式市場との互換性を維持するために、何度も変更を余儀なくされました。
その1つが、コードの冒頭にある変更点です。
int OnInit() { static string memSzUser01 = ""; Terminal.Init(); WallPaper.Init(user10, user12, user11); Mouse.Init(user50, user51, user52); if (memSzUser01 != user01) { Chart.ClearTemplateChart(); Chart.AddThese(memSzUser01 = user01); } Chart.InitilizeChartTrade(user20 * Terminal.GetVolumeMinimal(), user21, user22, user23); VolumeAtPrice.Init(user32, user33, user30, user31); TimesAndTrade.Init(user41); TradeView.Initilize(); OnTrade(); EventSetTimer(1); return INIT_SUCCEEDED; }
ハイライトされた部分は以前は存在しませんでした。その他にも、さまざまな変更点があります。ただし、この実装には注目せず、その代わりに、他のトリックを使わずに、チャート上に存在するTPとSLのレベルを直接動かすEAの使い方を見ます。では、この部分に移りましょう。
3.0.1 チャート上で直接注文を移動する
以前のバージョンでは、この部分が非常に難しく、時間の経過とともに現れる未解決の問題をいくつも抱えていたため、作業が難航しました。その理由の1つは、コードが非常にまばらで、チャート上で直接注文を移動するシステムを効果的に実装することが困難だったことです。元々、このシステムではそのようなことは想定していませんでした。
このEAがどの程度の変更を必要としたのか(未決注文やポジションを含む注文の動きを扱えるようにし、チャート上でマウスを使って指値注文の動きをコントロールできるようにするため)、以前のEAがどのようなものだったかを見てみましょう。
問題は、オブジェクトが作成されたときに、C_HLineTradeクラスを置き換えたことです。これは、「一からの取引エキスパートアドバイザーの開発(第20部)」でおこないました。現在、システムの構造がより複雑になっているので、上の全体像を再び見せないために、何が起こったのかだけを見ることにします。
矢印は、C_HLineTradeクラスが削除され、新しいクラスのためのスペースができた接続ポイントを指しています。これにより、以前の記事でおこなったような、より多くの実装が可能になりましたが、C_OrderViewクラスの存在が開発の妨げになり、結局は除外せざるを得ませんでした。ただし、これですべてではありません。C_TradeGraphicsクラスは、古いC_OrderViewクラスと統合され、C_IndicatorTradeViewという新しいクラスが登場しました。そこで、このクラスで2つのクラスを入れ替え、これによって注文移動システムを開発することができました。
今回紹介するのは、このシステムの初回バージョンです。現在、別のバージョンも開発中ですが、それはまた別の記事で紹介します。
3.0.1.1 新しいシステムのためのコードを書く
統合後の新システムの構造は次のようになっています。
緑色の部分は、EAではなくMetaTrader 5で管理される、フリーのクラス群であることを示しています。でも、どうやってやるのでしょうか。その過程を具体的に考えてみましょう。実際には、EAはクラスとそのクラスによって作成されたすべてのオブジェクトのみを作成、配置、削除します。EAコード内部を見ても、緑色の部分で作成したオブジェクトを参照するような構造体や変数は見当たりません。これにより、MetaTrader 5がオペレーティングシステムでメモリを確保できる限り、無制限の数のオブジェクトを作成することができます。EA内部の構造体や変数によって、オブジェクトの数が制限されることはありません。
こんな構造は、頭のおかしい人しか作れないと思われるかもしれませんが、私が作ったのだから私の頭がおかしいことになります。で、これでうまくいくのです。しかも、意外にもシステムにそれほど負荷がかかりません。みんなに頭がおかしいと言われているので、大したことはないのですが...まあ、先に進みましょう。未決注文や指値注文を移動させるときに、若干のもたつきを感じることがあります。これはコードの不具合やパソコンやMetaTrader 5の問題ではなく、システムが未決注文や指値を取引サーバ上で移動させること自体で、移動とサーバの反応に待ち時間が発生することが問題です。これはあるシナリオでは最善かつ安全な操作方法ですが、私がEAにさせたいと思っている他のことをすることはできません。そこで、次回は、この問題を解決するために、EAに新しい機能を追加し、システムをより流動的にする予定ですが、これは上記の構造を変更することなくおこないます。このため、システムの安全性は低くなりますが、コードだけはきちんと書くつもりです。それまでは、誰にもわからないことですが、この問題に対する良い解決策が見つかるかもしれません。
それでは、現在の新しいコードの重要点を見てみましょう。まずは、あまり知られていない、次のような関数から始めます。
void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result) { #define def_IsBuy(A) ((A == ORDER_TYPE_BUY_LIMIT) || (A == ORDER_TYPE_BUY_STOP) || (A == ORDER_TYPE_BUY_STOP_LIMIT) || (A == ORDER_TYPE_BUY)) ulong ticket; if (trans.symbol == Terminal.GetSymbol()) switch (trans.type) { case TRADE_TRANSACTION_DEAL_ADD: case TRADE_TRANSACTION_ORDER_ADD: ticket = trans.order; ticket = (ticket == 0 ? trans.position : ticket); TradeView.IndicatorInfosAdd(ticket); TradeView.UpdateInfosIndicators(0, ticket, trans.price, trans.price_tp, trans.price_sl, trans.volume, (trans.position > 0 ? trans.deal_type == DEAL_TYPE_BUY : def_IsBuy(trans.order_type))); break; case TRADE_TRANSACTION_ORDER_DELETE: if (trans.order != trans.position) TradeView.RemoveIndicator(trans.order); else TradeView.UpdateInfosIndicators(0, trans.position, trans.price, trans.price_tp, trans.price_sl, trans.volume, trans.deal_type == DEAL_TYPE_BUY); if (!PositionSelectByTicket(trans.position)) TradeView.RemoveIndicator(trans.position); break; case TRADE_TRANSACTION_ORDER_UPDATE: TradeView.UpdateInfosIndicators(0, trans.order, trans.price, trans.price_tp, trans.price_sl, trans.volume, def_IsBuy(trans.order_type)); break; case TRADE_TRANSACTION_POSITION: TradeView.UpdateInfosIndicators(0, trans.position, trans.price, trans.price_tp, trans.price_sl, trans.volume, trans.deal_type == DEAL_TYPE_BUY); break; } #undef def_IsBuy }
このコードは非常に興味深いもので、ポジションが新しく現れたり変更されたりするのをいちいち確認する手間が省けます。実際には、何が起こっているかはサーバ自身が知らせてくれるので、EAがイベントに正しく反応することだけを確認すればいいのです。このコーディングとOnTradeTransactionイベントの使い方をよく勉強してください。以前のバージョンで行われている方法でモデルを使って分析すると、確認に多くの時間を費やすことになるからです。この場合、難しい部分はサーバがすべてやってくれます。チャート上の値が本当にその時点でサーバが見ているものを示していることは確信できます。
上のコードのハイライトを見る前に、別のスニペットを見てみましょう。
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { Mouse.DispatchMessage(id, lparam, dparam, sparam); switch (id) { case CHARTEVENT_CHART_CHANGE: Terminal.Resize(); WallPaper.Resize(); TimesAndTrade.Resize(); break; } Chart.DispatchMessage(id, lparam, dparam, sparam); VolumeAtPrice.DispatchMessage(id, sparam); TradeView.DispatchMessage(id, lparam, dparam, sparam); ChartRedraw(); }
すべてが一箇所で完結しています。クラスに入って、中の様子を見ましょう。
3.1.C_IndicatorTradeViewクラス
このクラスは、データの表示と操作に使用されます。基本的には、前述したように古いC_OrderViewクラスとC_TradeGraphicsクラスが含まれていますが、データを操作する方法は全く違います。このクラスのポイントを見てみましょう。
まず、初期化関数のコードは次のようになります。
void Initilize(void) { int orders = OrdersTotal(); ulong ticket; bool isBuy; long info; double tp, sl; ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_OBJECT_DESCR, false); ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_TRADE_LEVELS, false); ChartSetInteger(Terminal.Get_ID(), CHART_DRAG_TRADE_LEVELS, false); for (int c0 = 0; c0 <= orders; c0++) if ((ticket = OrderGetTicket(c0)) > 0) if (OrderGetString(ORDER_SYMBOL) == Terminal.GetSymbol()) { info = OrderGetInteger(ORDER_TYPE); isBuy = ((info == ORDER_TYPE_BUY_LIMIT) || (info == ORDER_TYPE_BUY_STOP) || (info == ORDER_TYPE_BUY_STOP_LIMIT) || (info == ORDER_TYPE_BUY)); IndicatorInfosAdd(ticket); UpdateInfosIndicators(-1, ticket, OrderGetDouble(ORDER_PRICE_OPEN), OrderGetDouble(ORDER_TP), OrderGetDouble(ORDER_SL), OrderGetDouble(ORDER_VOLUME_CURRENT), isBuy); } orders = PositionsTotal(); for (int c0 = 0; c0 <= orders; c0++) if (PositionGetSymbol(c0) == Terminal.GetSymbol()) { tp = PositionGetDouble(POSITION_TP); sl = PositionGetDouble(POSITION_SL); ticket = PositionGetInteger(POSITION_TICKET); IndicatorInfosAdd(ticket); UpdateInfosIndicators(1, ticket, PositionGetDouble(POSITION_PRICE_OPEN), tp, sl, PositionGetDouble(POSITION_VOLUME), PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY); } CreateIndicatorTrade(def_IndicatorTicket0, IT_PENDING); CreateIndicatorTrade(def_IndicatorTicket0, IT_TAKE); CreateIndicatorTrade(def_IndicatorTicket0, IT_STOP); }
基本的に、やっているのは、動作に必要な指標を作成し、ポジションや未決注文など、現在口座に存在するものを提示することです。クロスオーダーシステムを使用していない場合、チャート上に(MetaTrader 5からの)注文ポイントがあり、これらのポイントをクリックしてドラッグすると、EAが指標の変更に合わせて新しいポイントを更新するため、ここでハイライトされた行が重要です。あまりは邪魔にならないのですが、開発しているシステムを実際に使ってみないと、何のために開発しているのかがわかりません。
次に、以下のコードご注目ください。
void UpdateInfosIndicators(char test, ulong ticket, double pr, double tp, double sl, double vol, bool isBuy) { bool isPending; isPending = (test > 0 ? false : (test < 0 ? true : (ticket == def_IndicatorTicket0 ? true : OrderSelect(ticket)))); PositionAxlePrice(ticket, (isPending ? IT_RESULT : IT_PENDING), 0); PositionAxlePrice(ticket, (isPending ? IT_PENDING : IT_RESULT), pr); SetTextValue(ticket, (isPending ? IT_PENDING : IT_RESULT), vol); PositionAxlePrice(ticket, IT_TAKE, tp); PositionAxlePrice(ticket, IT_STOP, sl); SetTextValue(ticket, IT_TAKE, vol, (isBuy ? tp - pr : pr - tp)); SetTextValue(ticket, IT_STOP, vol, (isBuy ? sl - pr : pr - sl)); }
データを受信して更新し、金融価値と注文のある場所について正しい値を提示します。基本的に、未決注文やポジションがあるかどうかを心配する必要はありません-この関数は、チャート上で正しく表示されるようにそれを配置します。
次の関数を紹介します。
inline double SecureChannelPosition(void) { double Res = 0, sl, profit, bid, ask; ulong ticket; bid = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_BID); ask = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_ASK); for (int i0 = PositionsTotal() - 1; i0 >= 0; i0--) if (PositionGetSymbol(i0) == Terminal.GetSymbol()) { ticket = PositionGetInteger(POSITION_TICKET); SetTextValue(ticket, IT_RESULT, PositionGetDouble(POSITION_VOLUME), profit = PositionGetDouble(POSITION_PROFIT), PositionGetDouble(POSITION_PRICE_OPEN)); sl = PositionGetDouble(POSITION_SL); if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { if (ask < sl) ClosePosition(ticket); }else { if ((bid > sl) && (sl > 0)) ClosePosition(ticket); } Res += profit; } return Res; };
OnTickイベントによって呼び出されるため、速度やシステム負荷の面でかなり重要です。確認以外でおこなうのはチャート上の値を更新することだけで、これはハイライトされたコードで実装されています。ここでは、ポジションチケットが非常に重要です。
ここで、上でハイライトされている関数を詳しく見てみましょう。
void SetTextValue(ulong ticket, eIndicatorTrade it, double value0, double value1 = 0.0, double priceOpen = 0.0) { double finance; switch (it) { case IT_RESULT : PositionAxlePrice(ticket, it, priceOpen); PositionAxlePrice(ticket, IT_PENDING, 0); m_EditInfo2.SetTextValue(MountName(ticket, it, EV_PROFIT), value1); case IT_PENDING: m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), value0 / Terminal.GetVolumeMinimal(), def_ColorVolumeEdit); break; case IT_TAKE : case IT_STOP : finance = (value1 / Terminal.GetAdjustToTrade()) * value0; m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), finance); break; } }
このように正しい数値が表示されますが、問題はそれらをどのように動かすかです。これは、他の3つのコードによっておこなわれます。もちろん、それらを避けて、現在のEAシステムよりはるかに高速なMetaTrader 5システムそのものを使うこともできますが、やはりEAを使う方がいいと思います。近々他の改良が入るからです。
移動を担当する最初の関数は以下で見ることができますが、ポイント(限界または注文自体)の移動に必要な断片だけを示しています。コード全体はもっと広範囲で、マウスの動きを使って移動する方法を理解するには必要はないためです。
void DispatchMessage(int id, long lparam, double dparam, string sparam) { ulong ticket; // ... Code .... switch (id) { case CHARTEVENT_MOUSE_MOVE: Mouse.GetPositionDP(dt, price); mKeys = Mouse.GetButtonStatus(); bEClick = (mKeys & 0x01) == 0x01; //Left mouse click bKeyBuy = (mKeys & 0x04) == 0x04; //SHIFT press bKeySell = (mKeys & 0x08) == 0x08; //CTRL press if (bKeyBuy != bKeySell) { if (!bMounting) { Mouse.Hide(); bIsDT = Chart.GetBaseFinance(leverange, valueTp, valueSl); valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / leverange); valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / leverange); m_TradeLine.SpotLight(MountName(def_IndicatorTicket0, IT_PENDING, EV_LINE)); bMounting = true; } tp = price + (bKeyBuy ? valueTp : (-valueTp)); sl = price + (bKeyBuy ? (-valueSl) : valueSl); UpdateInfosIndicators(0, def_IndicatorTicket0, price, tp, sl, leverange, bKeyBuy); if ((bEClick) && (memLocal == 0)) CreateOrderPendent(leverange, bKeyBuy, memLocal = price, tp, sl, bIsDT); }else if (bMounting) { UpdateInfosIndicators(0, def_IndicatorTicket0, 0, 0, 0, 0, false); Mouse.Show(); memLocal = 0; bMounting = false; }else if ((!bMounting) && (bKeyBuy == bKeySell)) { if (bEClick) { bIsMove = false; m_TradeLine.SpotLight(); } MoveSelection(price, mKeys); } break; // ... Code ... case CHARTEVENT_OBJECT_CLICK: if (GetIndicatorInfos(sparam, ticket, price, it, ev)) switch (ev) { // ... Code ... case EV_MOVE: if (bIsMove) { m_TradeLine.SpotLight(); bIsMove = false; }else { m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE)); bIsMove = true; } break; } break; } }
何が起こっているのかを理解しましょう。記事の最後に動画があり、やり方や実際に何が起こるかが紹介されていますが、その前に考えてみましょう。
各表示には、その選択を可能にするオブジェクトがあります(移動できない結果を除く)。このポイントをクリックすると、表示線が太くなります。このとき、選択オブジェクトの外側で新たにクリックしてオブジェクトを移動させるまで、マウスの動きはキャプチャされて、このオブジェクトの新しい位置に変換されます。マウスボタンを押し続ける必要はなく、一度クリックしてドラッグし、もう一度クリックすればよいので、確認してみてください。
ただし、実際には、ここでおこなわれているのは仕事の一部だけです。その他にも、私たちを助けてくれる関数が2つあります。1つは既に見たとおり、計算値を表示する役割を担っており、もう1つは、注文やリミットレベルの移動システムを使用する際にEAを低速にしている「石」です。以下に示します。
void MoveSelection(double price, uint keys) { static string memStr = NULL; static ulong ticket = 0; static eIndicatorTrade it; eEventType ev; double tp, sl, pr; bool isPending; string sz0 = m_TradeLine.GetObjectSelected(); if (sz0 != NULL) { if (memStr != sz0) GetIndicatorInfos(memStr = sz0, ticket, pr, it, ev); isPending = OrderSelect(ticket); switch (it) { case IT_TAKE: if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), price, macroGetPrice(IT_STOP)); else ModifyPosition(ticket, price, macroGetPrice(IT_STOP)); break; case IT_STOP: if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), macroGetPrice(IT_TAKE), price); else ModifyPosition(ticket, macroGetPrice(IT_TAKE), price); break; case IT_PENDING: pr = macroGetPrice(IT_PENDING); tp = macroGetPrice(IT_TAKE); sl = macroGetPrice(IT_STOP); ModifyOrderPendent(ticket, price, (tp == 0 ? 0 : price + tp - pr), (sl == 0 ? 0 : price + sl - pr)); break; } }; }
この関数を「石」と呼んでいるのは、位置決めシステムを遅くしているからです。わからない場合は、ハイライトされた箇所をご覧ください。それぞれC_Routerクラスの中にあって、取引サーバにリクエストを送る関数なので、何らかの理由でサーバが応答するのに時間がかかると(これはレイテンシーのために必ず起こります)、配置システムは多少なりとも遅くなりますが、サーバが素早く応答すれば、システムは流動的になり、物事はよりスムーズに進むことになります。これは後で修正します。このシステムでは他のことができないからです。とにかく、こうすれば少しは安全な方法で操作できるようになります。特に価格が非常に速く動くような揮発性の高い動きを好む人は、それでも限界が跳ね上がるリスクがあることを念頭に置いておかなければならないでしょう。仕方がありません。何かを犠牲にしなければなりません。サーバの中身がまさにこうなることを承知で運用することに同意していただける方は、この時点でEAが完成していることになります。しかし、あまり正確でなくてもいいのでEAの機能に流動性を持たせたいという方にとっては、次回の記事で状況が変わり、より面白くなると思います。
下のビデオは、システムが実際にどのように機能するかを示しているので、チャートとツールボックスの値に注意してください。
結論
非常に興味深いEAができましたが、しばらくはデモ口座で使用して、その動作に慣れることをお勧めします。これ以上の大きな変化はないことをお約束します。改善されるのはこれからですが、次回はこのEAが提供するセキュリティを犠牲にして、足りないものをいくつか追加していきたいと思います。とにかく、これは取引システムの仕組みや、必要なデータモデリングを得るためのプラットフォームの操作方法を学ぶための素晴らしい資料となるでしょう。
忘れてはならないのは、注文や指値の移動が遅いと感じたら、記事で紹介したポイントを外して、MetaTrader 5自体で注文や指値を動かし、EAをサポートとして使って、データの解釈を助けてあげることです。やるかやらないかは、読者しだいでうs。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/10516
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索