一からの取引エキスパートアドバイザーの開発(第27部):未来に向かって(II)
はじめに
前回の「一からの取引エキスパートアドバイザーの開発(第26部)」稿では、発注システムに存在する壊滅的なエラーを修正しました。また、新しい発注システムの運用を可能にするための変更も実装し始めました。この連載で最初に実装されたシステムは非常に興味深いものですが、欠陥があるため操作できません。この欠陥は、前回の記事の最後で示しました。その理由は取引方法を知らなかったということです。より具体的には、他の小さな問題に加えて、注文またはポジションの有効期限の選択方法を知らなかったことです。システムは、取引セッションまたは当日の終わりに決済する必要がある注文またはポジションとして修正されました。ただし、長期的な取引をしたい場合もあるため、すべてをそのままにしておいてもあまり役に立ちません。
したがって、この記事では、修正を提示します。各注文が何であるか、どのように処理され、どのような動きが予想されるかを即座に正確に判断できるように、発注システムをより直感的にする方法を見ていきます。
このシステムは非常に興味深く、シンプルかつ直感的であるため、実際に動作しているのを見ると、これなしでは作業したくなくなります。この記事で紹介するのは、発注システムで実装できる多くの可能性の1つにすぎません。おそらく、後で他のことを紹介しますが、この記事で説明することは、特定のケースに対して他の有用で興味深い変更を作成するための優れた基礎を与えることができます。とにかく、これらの記事のすべてを可能な限り一般的に保つようにしています。
2.0.直感的なモデル
これまでのところ、次のように注文を処理してきました。
テイクプロフィットとストップロスの表示には非常に直感的な認識可能な形式があります。緑は口座に追加されるもの、赤は口座から引かれるものを示します。すべて明らかです。以下に示すようにストップ表示がある場合でも、ストップロスを有効にすると、この合計が口座に追加されることを示しています。言い換えれば、ストップオーダーレベルは、少なくとも今のところ、私たちの努力を必要としません。おそらく将来、それらの何かを変更したいかもしれませんが、現時点では使用に非常に適しています。
ストップレベルを使用するこの方法は、トレーダーが分析するのに非常にシンプルで直感的です。ただし、あまり明確でないことがあります。1つ目は未決注文エントリポイントの表示です。
この未決注文が買い注文か売り注文かを知ることはできるでしょうか。2つ目は、この未決注文が1日の終わりに決済されるか、それとも長期ポジションが開かれるかを知ることは可能かということです。簡単です。すでにポジションがある場合、指標は以下のようになります。
繰り返しになりますが、未決注文の表示と同じ問題があります。チャートを見てこれらの指標を見ると、ポジションが1日の終わりに決済されるか長く続くかはわかりません。1日の終わりに取引が終了した場合、その費用を支払う必要があるため、証券会社に強制的に取引を停止してほしくないでしょう。また、MetaTraderツールボックスでさえこの情報を表示しないため、レベルなしで注文を決済することはあまり合理的ではありません。そのため、指標を介してチャートに表示されることは素晴らしいことです。
したがって、何が起こっているのかをよりよく理解するために、ここで、特にポジションエントリポイントを示す指標に変更を加える必要があります。
2.0.1.指標に新しい情報を追加する方法
チャートのスペースをあまり占有せずに新しい情報を追加する最も簡単な方法は、ビットマップを使用することです。ビットマップは理解しやすく、かなり代表的なものだからです。したがって、追加のコードを挿入することなく、C_IndicatorTradeViewクラスで確認できる4つの新しいビットマップをEAに追加します。
#define def_BtnClose "Images\\NanoEA-SIMD\\Btn_Close.bmp" #define def_BtnCheckEnabled "Images\\NanoEA-SIMD\\CheckBoxEnabled.bmp" #define def_BtnCheckDisabled "Images\\NanoEA-SIMD\\CheckBoxDisabled.bmp" #define def_BtnDayTrade "Images\\NanoEA-SIMD\\Inf_DayTrade.bmp" #define def_BtnSwing "Images\\NanoEA-SIMD\\Inf_Swing.bmp" #define def_BtnInfoBuy "Images\\NanoEA-SIMD\\Inf_Buy.bmp" #define def_BtnInfoSell "Images\\NanoEA-SIMD\\Inf_Sell.bmp" //+------------------------------------------------------------------+ #resource "\\" + def_BtnClose #resource "\\" + def_BtnCheckEnabled #resource "\\" + def_BtnCheckDisabled #resource "\\" + def_BtnDayTrade #resource "\\" + def_BtnSwing #resource "\\" + def_BtnInfoBuy #resource "\\" + def_BtnInfoSell
さらに、発注システムに2つの新しいオブジェクトを実装するだけで済みます。
//+------------------------------------------------------------------+ enum eIndicatorTrade {IT_NULL, IT_STOP= 65, IT_TAKE, IT_PENDING, IT_RESULT}; enum eEventType {EV_NULL, EV_GROUND = 65, EV_LINE, EV_CLOSE, EV_EDIT, EV_PROFIT, EV_MOVE, EV_CHECK, EV_TYPE, EV_DS}; //+------------------------------------------------------------------+ C_Object_BackGround m_BackGround; C_Object_TradeLine m_TradeLine; C_Object_BtnBitMap m_BtnClose, m_BtnCheck, m_BtnInfoType, m_BtnInfo_DS; C_Object_Edit m_EditInfo1, m_EditInfo2; C_Object_Label m_BtnMove;
新しいオブジェクトを追加するときはいつでも、オブジェクトにリンクされたEVENTも追加する必要があります。これにより、オブジェクトが一意の名前を持つことが保証されます。
ここからがプログラミングの最も興味深い部分です。まずやらなければならないことは、ゴーストの世話をすることです。それらがそこにある情報を保持するように、更新する必要があります。もちろん削除することもできますが、基本データは残しておいたほうがいいと思います。次のコードをご覧ください。
#define macroSwapName(A, B) ObjectSetString(Terminal.Get_ID(), macroMountName(ticket, A, B), OBJPROP_NAME, macroMountName(def_IndicatorGhost, A, B)); void CreateGhostIndicator(ulong ticket, eIndicatorTrade it) { if (GetInfosTradeServer(m_Selection.ticket = ticket) != 0) { ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false); macroSwapName(it, EV_LINE); macroSwapName(it, EV_GROUND); macroSwapName(it, EV_MOVE); macroSwapName(it, EV_EDIT); macroSwapName(it, EV_CLOSE); if (it == IT_PENDING) { macroSwapName(it, EV_CHECK); macroSwapName(it, EV_TYPE); macroSwapName(it, EV_DS); } m_TradeLine.SetColor(macroMountName(def_IndicatorGhost, it, EV_LINE), def_IndicatorGhostColor); m_BackGround.SetColor(macroMountName(def_IndicatorGhost, it, EV_GROUND), def_IndicatorGhostColor); m_BtnMove.SetColor(macroMountName(def_IndicatorGhost, it, EV_MOVE), def_IndicatorGhostColor); ObjectDelete(Terminal.Get_ID(), macroMountName(def_IndicatorGhost, it, EV_CLOSE)); m_TradeLine.SpotLight(); ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true); m_Selection.it = it; }else m_Selection.ticket = 0; } #undef macroSwapName
強調表示された線はオブジェクトをゴーストに渡します。これは非常にシンプルで明確です。別の単純なコードは、指標を未決からフローティングに変換します。
#define macroSwapAtFloat(A, B) ObjectSetString(Terminal.Get_ID(), macroMountName(ticket, A, B), OBJPROP_NAME, macroMountName(def_IndicatorFloat, A, B)); bool PendingAtFloat(ulong ticket) { eIndicatorTrade it; if (macroGetLinePrice(def_IndicatorFloat, IT_PENDING) > 0) return false; macroSwapAtFloat(IT_PENDING, EV_CHECK); macroSwapAtFloat(IT_PENDING, EV_TYPE); macroSwapAtFloat(IT_PENDING, EV_DS); for (char c0 = 0; c0 < 3; c0++) { switch(c0) { case 0: it = IT_PENDING; break; case 1: it = IT_STOP; break; case 2: it = IT_TAKE; break; default: return false; } macroSwapAtFloat(it, EV_CLOSE); macroSwapAtFloat(it, EV_MOVE); macroSwapAtFloat(it, EV_EDIT); macroSwapAtFloat(it, EV_GROUND); macroSwapAtFloat(it, EV_LINE); m_EditInfo1.SetOnlyRead(macroMountName(def_IndicatorFloat, IT_PENDING, EV_EDIT), false); } return true; } #undef macroSwapAtFloat
強調表示された行はオブジェクトをフローティング指標に変換し、後で必要なアクションを実行できるようにします。ここで、指標を作成するコードにいくつかの変更を実装する必要があります。気に入るまでテストして調整するようなものです。基本的に、変更は以下のコードの強調表示されたところで行われました。
#define macroCreateIndicator(A, B, C, D) { \ m_TradeLine.Create(ticket, sz0 = macroMountName(ticket, A, EV_LINE), C); \ m_BackGround.Create(ticket, sz0 = macroMountName(ticket, A, EV_GROUND), B); \ m_BackGround.Size(sz0, (A == IT_RESULT ? 100 : (A == IT_PENDING ? 144 : 92)), (A == IT_RESULT ? 34 : 22)); \ m_EditInfo1.Create(ticket, sz0 = macroMountName(ticket, A, EV_EDIT), D, 0.0); \ m_EditInfo1.Size(sz0, 60, 14); \ if (A != IT_RESULT) { \ m_BtnMove.Create(ticket, sz0 = macroMountName(ticket, A, EV_MOVE), "Wingdings", "u", 17, C); \ m_BtnMove.Size(sz0, 21, 23); \ }else { \ m_EditInfo2.Create(ticket, sz0 = macroMountName(ticket, A, EV_PROFIT), clrNONE, 0.0); \ m_EditInfo2.Size(sz0, 60, 14); } \ } #define macroInfoBase(A) { \ m_BtnInfoType.Create(ticket, sz0 = macroMountName(ticket, A, EV_TYPE), def_BtnInfoBuy, def_BtnInfoSell); \ m_BtnInfoType.SetStateButton(sz0, m_Selection.bIsBuy); \ m_BtnInfo_DS.Create(ticket, sz0 = macroMountName(ticket, A, EV_DS), def_BtnDayTrade, def_BtnSwing); \ m_BtnInfo_DS.SetStateButton(sz0, m_Selection.bIsDayTrade); \ } void CreateIndicator(ulong ticket, eIndicatorTrade it) { string sz0; switch (it) { case IT_TAKE : macroCreateIndicator(it, clrForestGreen, clrDarkGreen, clrNONE); break; case IT_STOP : macroCreateIndicator(it, clrFireBrick, clrMaroon, clrNONE); break; case IT_PENDING: macroCreateIndicator(it, clrCornflowerBlue, clrDarkGoldenrod, def_ColorVolumeEdit); m_BtnCheck.Create(ticket, sz0 = macroMountName(ticket, it, EV_CHECK), def_BtnCheckEnabled, def_BtnCheckDisabled); m_BtnCheck.SetStateButton(sz0, true); macroInfoBase(IT_PENDING); break; case IT_RESULT : macroCreateIndicator(it, clrSlateBlue, clrSlateBlue, def_ColorVolumeResult); macroInfoBase(IT_RESULT); break; } m_BtnClose.Create(ticket, macroMountName(ticket, it, EV_CLOSE), def_BtnClose); } #undef macroInfoBase #undef macroCreateIndicator
macroInfoBaseは指標で使用されるオブジェクトを作成しますが、これらのオブジェクトはポジションを建てるのととポジション結果指標でのみ作成され、他の指標でこれらのオブジェクトを作成する必要はありません。ただし、オブジェクトを作成した場所に配置していません。これは、次に示す別の場所でおこなわれます。
#define macroSetAxleY(A) { \ m_BackGround.PositionAxleY(macroMountName(ticket, A, EV_GROUND), y); \ m_TradeLine.PositionAxleY(macroMountName(ticket, A, EV_LINE), y); \ m_BtnClose.PositionAxleY(macroMountName(ticket, A, EV_CLOSE), y); \ if (A != IT_RESULT)m_BtnMove.PositionAxleY(macroMountName(ticket, A, EV_MOVE), y, 1); \ else m_EditInfo2.PositionAxleY(macroMountName(ticket, A, EV_PROFIT), y, 1); \ m_EditInfo1.PositionAxleY(macroMountName(ticket, A, EV_EDIT), y, (A == IT_RESULT ? -1 : 0)); \ if (A == IT_PENDING) m_BtnCheck.PositionAxleY(macroMountName(ticket, A, EV_CHECK), y); \ if ((A == IT_PENDING) || (A == IT_RESULT)) { \ m_BtnInfoType.PositionAxleY(macroMountName(ticket, A, EV_TYPE), y + (A == IT_PENDING ? 0 : 8)); \ m_BtnInfo_DS.PositionAxleY(macroMountName(ticket, A, EV_DS), y - (A == IT_PENDING ? 0: 8)); \ } \ } #define macroSetAxleX(A, B) { \ m_BackGround.PositionAxleX(macroMountName(ticket, A, EV_GROUND), B); \ m_TradeLine.PositionAxleX(macroMountName(ticket, A, EV_LINE), B); \ m_BtnClose.PositionAxleX(macroMountName(ticket, A, EV_CLOSE), B + 3); \ m_EditInfo1.PositionAxleX(macroMountName(ticket, A, EV_EDIT), B + 21); \ if (A != IT_RESULT) m_BtnMove.PositionAxleX(macroMountName(ticket, A, EV_MOVE), B + 80 + (A == IT_PENDING ? 52 : 0)); \ else m_EditInfo2.PositionAxleX(macroMountName(ticket, A, EV_PROFIT), B + 21); \ if (A == IT_PENDING) m_BtnCheck.PositionAxleX(macroMountName(ticket, A, EV_CHECK), B + 82); \ if ((A == IT_PENDING) || (A == IT_RESULT)) { \ m_BtnInfoType.PositionAxleX(macroMountName(ticket, A, EV_TYPE), B + (A == IT_PENDING ? 100 : 82)); \ m_BtnInfo_DS.PositionAxleX(macroMountName(ticket, A, EV_DS), B + (A == IT_PENDING ? 118 : 82)); \ } \ } //--- void ReDrawAllsIndicator(void) { C_IndicatorTradeView::st00 Local; int max = ObjectsTotal(Terminal.Get_ID(), -1, OBJ_EDIT); ulong ticket; eIndicatorTrade it; eEventType ev; Local = m_Selection; m_Selection.ticket = 0; for (int c0 = 0; c0 <= max; c0++) if (GetIndicatorInfos(ObjectName(Terminal.Get_ID(), c0, -1, OBJ_EDIT), ticket, it, ev)) if ((it == IT_PENDING) || (it == IT_RESULT)) { PositionAxlePrice(ticket, IT_STOP, macroGetLinePrice(ticket, IT_STOP)); PositionAxlePrice(ticket, IT_TAKE, macroGetLinePrice(ticket, IT_TAKE)); PositionAxlePrice(ticket, it, macroGetLinePrice(ticket, it)); } m_Selection = Local; ChartRedraw(); } //--- inline void PositionAxlePrice(ulong ticket, eIndicatorTrade it, double price) { int x, y, desl; ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y); macroSetLinePrice(ticket, it, price); macroSetAxleY(it); switch (it) { case IT_TAKE: desl = 160; break; case IT_STOP: desl = 270; break; default: desl = 0; } macroSetAxleX(it, desl + (int)(ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS) * 0.2)); } #undef macroSetAxleX #undef macroSetAxleY
また、コードに抜本的かつ根本的な変更を加えるのは好きではないことも強調したいと思います。基本的に、上記のコードでは変更のみが強調表示されています。
2.0.2.目の前の問題
すべてが問題なく動作しますが、問題があります。すべてのMQL5ドキュメントを検索しましたが、単純に問題を解決する方法が見つかりませんでした。問題は、最近開いたポジションがデイトレード(同じ日のショートトレード)かスイングトレード(ロングトレード)かを知る方法です。前日に開かれた古いポジションの場合、このタイプの分析をおこなうのは非常に簡単です。現在の日とポジションの開始日を比較するだけで十分だからです。それらが異なる場合、ポジションはスイングトレードです。しかし、EAが閉じられていて、ポジションが開かれた同じ日にそれを開始した場合はどうなるでしょうか。この場合、ポジションがデイトレードかスイングトレードかを知る方法はありません。
未決注文では、これを確認する方法があるためこの問題は存在しません。ORDER_TYPE_TIMEパラメータを使用してOrderGetIntegerを呼び出す場合、ENUM_ORDER_TYPE_TIME列挙の値が返され、注文がデイトレードかスイングトレードかを示しますが、同じことはポジションには起こりません。
このため、この場合の私の解決策は、注文またはポジションに何かを追加して、他の情報に関係なく、操作の期間をEAに知らせることです。ただし、これは完全な解決策ではありません。いくつかのケースでは問題を解決できますが、すべてではありません。トレーダーは、EAが使用するシステムを変更して、分析に必要な期間の前に取引がスイングかデイトレードかを識別することができるためです。
理解を深めるために、解決法の実装方法を見てみましょう。
inline char GetInfosTradeServer(ulong ticket) { long info; if (ticket == 0) return 0; if (OrderSelect(ticket)) { if (OrderGetString(ORDER_SYMBOL) != Terminal.GetSymbol()) return 0; info = OrderGetInteger(ORDER_TYPE); m_Selection.bIsBuy = ((info == ORDER_TYPE_BUY_LIMIT) || (info == ORDER_TYPE_BUY_STOP) || (info == ORDER_TYPE_BUY_STOP_LIMIT) || (info == ORDER_TYPE_BUY)); m_Selection.pr = OrderGetDouble(ORDER_PRICE_OPEN); m_Selection.tp = OrderGetDouble(ORDER_TP); m_Selection.sl = OrderGetDouble(ORDER_SL); m_Selection.vol = OrderGetDouble(ORDER_VOLUME_CURRENT); m_Selection.bIsDayTrade = ((ENUM_ORDER_TYPE_TIME)OrderGetInteger(ORDER_TYPE_TIME) == ORDER_TIME_DAY); return -1; } if (PositionSelectByTicket(ticket)) { if (PositionGetString(POSITION_SYMBOL) != Terminal.GetSymbol()) return 0; m_Selection.bIsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY; m_Selection.pr = PositionGetDouble(POSITION_PRICE_OPEN); m_Selection.tp = PositionGetDouble(POSITION_TP); m_Selection.sl = PositionGetDouble(POSITION_SL); m_Selection.vol = PositionGetDouble(POSITION_VOLUME); if (macroGetDate(PositionGetInteger(POSITION_TIME)) == macroGetDate(TimeTradeServer())) m_Selection.bIsDayTrade = PositionGetString(POSITION_COMMENT) == def_COMMENT_TO_DAYTRADE; else m_Selection.bIsDayTrade = false; return 1; } return 0; }
上記のように、指値注文の場合、OrderGetIntegerを呼び出して必要な値を取得するだけで十分です。ポジションに関してはもう少し複雑です。それは次のように機能します。ポジションの開始日と取引サーバーの現在の日付を確認します。両方が同じ場合は、注文のコメントを確認します。コメントがC_Routerクラスで使用される文字列を示し、ポジションが開かれた場合にデイトレードになることを示す場合、EAはそれを解釈し、ポジション指標に表示します。ただし、コメントはその日の終わりまで変更してはなりません。なぜなら、変更された場合、EAはデイトレードポジションが実際にはスイングトレードであると報告する可能性があるからです。その場合、これはEAのせいではなく、トレーダーによるコメントの変更が早すぎたことになります。
これはこの解決策の欠点です。ポジションデータを見るだけでポジションがデイトレードであるかどうかを判断する方法についてどなたかアイデアをお持ちでしたら、コメントで共有してください。
未決注文の場合の様子は、以下の動画でご覧になれます。
2.0.3.プラットフォームメッセージへの応答
ここでの発注システム全体はMetaTrader 5によって送信されたメッセージに基づいているため、EAは何をすべきか、何をすべきでないかを知ることができます。そのため、メッセージシステムの実装方法を知ることが非常に重要です。
メッセージ関連の完全なコードを以下に示します。
#define macroGetDataIndicatorFloat { \ m_Selection.vol = m_EditInfo1.GetTextValue(macroMountName(def_IndicatorFloat, IT_PENDING, EV_EDIT)) * Terminal.GetVolumeMinimal(); \ m_Selection.bIsBuy = m_BtnInfoType.GetStateButton(macroMountName(def_IndicatorFloat, IT_PENDING, EV_TYPE)); \ m_Selection.pr = macroGetLinePrice(def_IndicatorFloat, IT_PENDING); \ m_Selection.sl = macroGetLinePrice(def_IndicatorFloat, IT_STOP); \ m_Selection.tp = macroGetLinePrice(def_IndicatorFloat, IT_TAKE); \ m_Selection.bIsDayTrade = m_BtnInfo_DS.GetStateButton(macroMountName(def_IndicatorFloat, IT_PENDING, EV_DS)); \ } void DispatchMessage(int id, long lparam, double dparam, string sparam) { ulong ticket; double price; bool bKeyBuy, bKeySell, bEClick; datetime dt; uint mKeys; char cRet; eIndicatorTrade it; eEventType ev; static bool bMounting = false; static double valueTp = 0, valueSl = 0, memLocal = 0; 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 pressed bKeySell = (mKeys & 0x08) == 0x08; //CTRL pressed if (bKeyBuy != bKeySell) { if (!bMounting) { m_Selection.bIsDayTrade = Chart.GetBaseFinance(m_Selection.vol, valueTp, valueSl); valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / m_Selection.vol); valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / m_Selection.vol); m_Selection.it = IT_PENDING; m_Selection.pr = price; } m_Selection.tp = m_Selection.pr + (bKeyBuy ? valueTp : (-valueTp)); m_Selection.sl = m_Selection.pr + (bKeyBuy ? (-valueSl) : valueSl); m_Selection.bIsBuy = bKeyBuy; m_BtnInfoType.SetStateButton(macroMountName(def_IndicatorTicket0, IT_PENDING, EV_TYPE), bKeyBuy); if (!bMounting) { IndicatorAdd(m_Selection.ticket = def_IndicatorTicket0); bMounting = true; } MoveSelection(price); if ((bEClick) && (memLocal == 0)) SetPriceSelection(memLocal = price); }else if (bMounting) { RemoveIndicator(def_IndicatorTicket0); memLocal = 0; bMounting = false; }else if ((!bMounting) && (bKeyBuy == bKeySell) && (m_Selection.ticket > def_IndicatorGhost)) { if (bEClick) SetPriceSelection(price); else MoveSelection(price); } break; case CHARTEVENT_OBJECT_DELETE: if (GetIndicatorInfos(sparam, ticket, it, ev)) { if (GetInfosTradeServer(ticket) == 0) break; CreateIndicator(ticket, it); if ((it == IT_PENDING) || (it == IT_RESULT)) PositionAxlePrice(ticket, it, m_Selection.pr); ChartRedraw(); m_TradeLine.SpotLight(); m_Selection.ticket = 0; UpdateIndicators(ticket, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy); } break; case CHARTEVENT_OBJECT_ENDEDIT: macroGetDataIndicatorFloat; m_Selection.ticket = 0; UpdateIndicators(def_IndicatorFloat, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy); break; case CHARTEVENT_CHART_CHANGE: ReDrawAllsIndicator(); break; case CHARTEVENT_OBJECT_CLICK: if (GetIndicatorInfos(sparam, ticket, it, ev)) switch (ev) { case EV_TYPE: if (ticket == def_IndicatorFloat) { macroGetDataIndicatorFloat; m_Selection.tp = (m_Selection.tp == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.tp - m_Selection.pr) * (m_Selection.bIsBuy ? 1 : -1))); m_Selection.sl = (m_Selection.sl == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.sl - m_Selection.pr) * (m_Selection.bIsBuy ? -1 : 1))); m_Selection.ticket = 0; UpdateIndicators(def_IndicatorFloat, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy); } else m_BtnInfoType.SetStateButton(sparam, !m_BtnInfoType.GetStateButton(sparam)); break; case EV_DS: if (ticket != def_IndicatorFloat) m_BtnInfo_DS.SetStateButton(sparam, !m_BtnInfo_DS.GetStateButton(sparam)); break; case EV_CLOSE: if (ticket == def_IndicatorFloat) RemoveIndicator(def_IndicatorFloat, it); else if ((cRet = GetInfosTradeServer(ticket)) != 0) switch (it) { case IT_PENDING: case IT_RESULT: if (cRet < 0) RemoveOrderPendent(ticket); else ClosePosition(ticket); break; case IT_TAKE: case IT_STOP: m_Selection.ticket = ticket; m_Selection.it = it; SetPriceSelection(0); break; } break; case EV_MOVE: if (ticket == def_IndicatorFloat) { macroGetDataIndicatorFloat; m_Selection.ticket = ticket; m_Selection.it = it; }else CreateGhostIndicator(ticket, it); break; case EV_CHECK: if (ticket != def_IndicatorFloat) { if (PendingAtFloat(ticket)) RemoveOrderPendent(ticket); else m_BtnCheck.SetStateButton(macroMountName(ticket, IT_PENDING, EV_CHECK), true); } else { macroGetDataIndicatorFloat; m_Selection.ticket = def_IndicatorTicket0; m_Selection.it = IT_PENDING; SetPriceSelection(m_Selection.pr); RemoveIndicator(def_IndicatorFloat); } break; } break; } } #undef macroGetDataIndicatorFloat
コードは怖くありません。長くて複雑に見えますが、実はとてもシンプルです。強調表示された部分に焦点を当てて、メッセージ処理コードの新機能を説明します。
最初の新しいことは、CHARTEVENT_OBJECT_ENDEDITイベント処理コードにあります。これは、EDITオブジェクトにあるコンテンツの編集を終了するたびに、MetaTrader 5によってトリガーされます。それはどういうことでしょうか。これは非常に重要です。このイベントを処理せず、レバレッジ値を編集した後にストップレベル指標のデータを操作しようとすると、値に不一致が生じるためです。EAは強制的に値を元の値に戻しますが、コードに示されているようにこのイベントを処理すると、この問題は存在せず、レバレッジデータでスムーズに取引できます。EAにこれらの調整を許可するように依頼するときは、多かれ少なかれレバレッジをかけて操作を開始するべきかどうかを実際に確認する必要があります。このすれば、リスクを負うことなく確認できます。EAは、注文をサーバーに送信するように依頼した場合にのみ注文を送信し、これが発生した瞬間はチェックボックスがアクティブになったときです。
次に、CHARTEVENT_OBJECT_CLICKイベントを詳しく見てみましょう。このために、前のコードで強調表示されたフラグメントをみてみます.
case CHARTEVENT_OBJECT_CLICK: if (GetIndicatorInfos(sparam, ticket, it, ev)) switch (ev) { case EV_TYPE: if (ticket == def_IndicatorFloat) { macroGetDataIndicatorFloat; m_Selection.tp = (m_Selection.tp == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.tp - m_Selection.pr) * (m_Selection.bIsBuy ? 1 : -1))); m_Selection.sl = (m_Selection.sl == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.sl - m_Selection.pr) * (m_Selection.bIsBuy ? -1 : 1))); m_Selection.ticket = 0; UpdateIndicators(def_IndicatorFloat, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy); } else m_BtnInfoType.SetStateButton(sparam, !m_BtnInfoType.GetStateButton(sparam)); break; case EV_DS: if (ticket != def_IndicatorFloat) m_BtnInfo_DS.SetStateButton(sparam, !m_BtnInfo_DS.GetStateButton(sparam)); break; // ... Rest of the code...
このコードは実際に何をしているのでしょうか。何か考えはありますか。この記事の動画に示されてはいますが、そのようなことがどのようにおこなわれるかお判りでしょうか。非常に複雑なコードだと思われる方も多いと思いますが、実際は上記のとおりです。
ここでやらなければならないことが2つあります。1つ目は、BitMapオブジェクトがクリックされると、そのステータスが変化し、そのチケットがサーバー上に既に存在するものからのものなのかチャート上にのみあるものなのかを確認することです。これは、緑色で強調表示されたところでおこなわれます。チケットがサーバーに存在する場合、ステータスの変更を元に戻す必要があり、EAは必要な変更をおこなうことでこれを修正します。
次に、黄色で強調表示されたセクションをご覧ください。この考え方は、次のことに基づいています。チャートにすでに注文があってその方向を逆にしたいだけなのに、なぜチャートに別の注文を出す必要があるのでしょうか。言い換えれば、BuyをSellしたいだけで、逆もまた然りです。黄色のフラグメントはまさにそれをおこないます。売/買を担当するBitMapをクリックすると、方向が自動的に変わります。1つの詳細を言えば、これは変動注文に対してのみ実行できます。すでにサーバー上にある注文は禁止されています。
これらすべての変更により、指標は次のようになります。
未決注文の種類
ポジション指標
予想される動きやポジションの存続期間を正確に知ることができるため、未決注文やポジションが何をしているかを判断することがはるかに簡単になりました。緑の上向き矢印は買いポジションを示します。下向きの赤い矢印は売りポジションです。文字Dは、1日の終わりに決済されるデイトレードを示します。Sの場合はスイングトレードであり、その日の終わりに操作が終了するとは限りません。
次の動画は、新しい発注システムがどのように機能するかを示しています。指値注文はさらに変更される可能性があり、ポジション指標は変更できないため、未決注文に焦点を当てました。ポジションに関してサーバーから提供されたデータのみが表示されます。システムは実用的ですが、その機能を最大限に活用するには、システムに慣れる必要があります。実際の口座で試す前に、すべてがどのように機能するかを詳しく調べてください。
結論
私たちの発注システムの用途が非常に広がってきました。いくつかのことをおこなって大いに助けになりますが、重要な詳細がまだ1つ欠落しており、次の記事で実装されることになります。またすぐお会いしましょう。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/10630
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索