一からの取引エキスパートアドバイザーの開発(第23部):新規受注システム(IV)
はじめに
前の「一からの取引エキスパートアドバイザーの開発(第22部)」稿では、指値注文とポジションストップレベルを移動するためのシステムを開発しました。この方法は比較的安全ですが(取引サーバの内容が反映されるため)、レベルをすばやく移動するには最適な方法ではありません。
問題は、マウスを使用して何かを変更するたびに、このイベントがサーバに送信され、応答を待たなければならないことです。この問題は、イベントがすべてのティックで送信されるという事実に関連しています。つまり、一度に複数のティックでレベルを移動する必要がある場合、すべての中間値を処理する必要があり、プロセス全体が非常に遅くなります。これに対して、コードをより柔軟にし、レベルをより速く変更するために、コードに変更を実装します。
1.0.計画
変更を実装するには、非常に簡単なことをおこなう必要があります。すべての変更についてサーバに通知するのではなく、必要な変更についてのみ通知します。これをおこなうだけでも、すでにすべてが良好に機能しますが、すべてが私たちがおこなっている方法通りであるとはまったく確信できません.
次に、コードをどこで変更する必要があるか見てみましょう。以下に示す別の関数を使用します。
#define macroGetPrice(A) StringToDouble(ObjectGetString(Terminal.Get_ID(), MountName(ticket, A, EV_LINE), OBJPROP_TOOLTIP)) 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; } }; } #undef macroGetPrice
ただし、関数内の変更に加えて、マウスイベントに関連するいくつかの変更も実装する必要があります。これが1番目の焦点です。
ハイライトされたセグメントは、使用される新しい位置を表すために別のものに置き換える必要があります。ただし、すべての変更はユーザーが理解できるものでなければなりません。
コードの構造全体に大きな変更を加えることなく、すべてを理解しやすくすると同時に機能させる効率的な方法が見つかりました。必要になるまで表示されないゴースト表示ラベルを作成することです。幸いなことに、MetaTrader 5はこれを非常に簡単におこなう方法を備えています。そのため、この記事は、この連載の以前の記事の内容に既に精通している人にとっては非常に理解しやすくなります。
2.0.実装
ゴーストラベルを実装するには、実際のラベルとともに作成するだけです。これは、前回の記事で示した方法で価格を操作するまで、実際のラベルの正確な影になります。この時点で、実際のラベルが移動するにつれて、ゴーストラベルがチャートに表示されます。これにより、何が起こっているかを簡単に比較し、変更を加えるかどうかを理解できます。
2.0.1.ゴースト表示ラベルの作成
すべての変更はC_IndicatorTradeViewクラス内に実装されています。3つの新しいディレクティブを定義することから始めましょう。
#define def_IndicatorGhost "G" #define def_IndicatorReal "R" #define def_IndicatorGhostColor clrDimGray
これにより、MetaTrader 5が機能するようになります。ルールは次のとおりです。最初にゴーストラベルを作成し、次に本物のラベルを作成します。したがって、MetaTrader 5は、ゴーストラベルが本当に表示される必要がある瞬間まで、ゴーストラベルを表示しないように注意します。これはMetaTrader 5自体によっておこなわれるため、プログラミングを大幅に節約できます。
重要な詳細は、ゴーストの色を変更したい場合は、選択した部分で指定された色を変更するだけだということです。
したがって、次の手順は、一意の名前を生成できるように関数を変更することです。
inline string MountName(ulong ticket, eIndicatorTrade it, eEventType ev, bool isGhost = false) { return StringFormat("%s%c%c%c%llu%c%c%c%s", def_NameObjectsTrade, def_SeparatorInfo, (char)it, def_SeparatorInfo, ticket, def_SeparatorInfo, (char)(isGhost ? ev + 32 : ev), def_SeparatorInfo, (isGhost ? def_IndicatorGhost : def_IndicatorReal)); }
ハイライトされている部分は、以前のバージョンから追加または変更されています。MetaTrader 5に一意の名前を生成させることができるので、これについて心配する必要はありません。イベントに値を追加したことにご注意ください。これをおこなったのは、ゴーストがイベントを受け取って制御しようとするのを防ぐためです。
次の手順は明らかです。ラベル自体を作成することです。
inline void CreateIndicatorTrade(ulong ticket, eIndicatorTrade it) { color cor1, cor2, cor3; string sz0, sz1; switch (it) { case IT_TAKE : cor1 = clrForestGreen; cor2 = clrDarkGreen; cor3 = clrNONE; break; case IT_STOP : cor1 = clrFireBrick; cor2 = clrMaroon; cor3 = clrNONE; break; case IT_PENDING: cor1 = clrCornflowerBlue; cor2 = clrDarkGoldenrod; cor3 = def_ColorVolumeEdit; break; case IT_RESULT : default: cor1 = clrDarkBlue; cor2 = clrDarkBlue; cor3 = def_ColorVolumeResult; break; } m_TradeLine.Create(ticket, MountName(ticket, it, EV_LINE, true), def_IndicatorGhostColor); m_TradeLine.Create(ticket, MountName(ticket, it, EV_LINE), cor2); if (ticket == def_IndicatorTicket0) m_TradeLine.SpotLight(MountName(ticket, IT_PENDING, EV_LINE)); if (it != IT_RESULT) m_BackGround.Create(ticket, sz0 = MountName(ticket, it, EV_GROUND, true), def_IndicatorGhostColor); m_BackGround.Create(ticket, sz1 = MountName(ticket, it, EV_GROUND), cor1); switch (it) { case IT_TAKE: case IT_STOP: case IT_PENDING: m_BackGround.Size(sz0, 92, 22); m_BackGround.Size(sz1, 92, 22); break; case IT_RESULT: m_BackGround.Size(sz1, 84, 34); break; } m_BtnClose.Create(ticket, MountName(ticket, it, EV_CLOSE), def_BtnClose); m_EditInfo1.Create(ticket, sz0 = MountName(ticket, it, EV_EDIT, true), def_IndicatorGhostColor, 0.0); m_EditInfo1.Create(ticket, sz1 = MountName(ticket, it, EV_EDIT), cor3, 0.0); m_EditInfo1.Size(sz0, 60, 14); m_EditInfo1.Size(sz1, 60, 14); if (it != IT_RESULT) { m_BtnMove.Create(ticket, sz0 = MountName(ticket, it, EV_MOVE, true), "Wingdings", "u", 17, def_IndicatorGhostColor); m_BtnMove.Create(ticket, sz1 = MountName(ticket, it, EV_MOVE), "Wingdings", "u", 17, cor2); m_BtnMove.Size(sz1, 21, 21); }else { m_EditInfo2.Create(ticket, sz1 = MountName(ticket, it, EV_PROFIT), clrNONE, 0.0); m_EditInfo2.Size(sz1, 60, 14); } }
ハイライトされたすべての行がゴーストを作成します。なぜすべての要素を再現しないのかが奇妙に思えるかもしれません。実際、ゴーストは実際のラベルの正確なコピーではなく、その影に過ぎません。したがって、すべての要素を作成する必要はありません。実際のラベルは何が起こるべきかを定義するものですが、ゴーストは取引サーバが見るものへの参照としてのみ機能します。
ここで、MetaTrader 5が本当に懸命に機能する、より詳細な調査が必要な部分が来ます。オブジェクトを適切な場所に配置するには多くの作業が必要だと思うかもしれませんが、ソースコードで実際に何が変更されたかを確認してください。
#define macroSetAxleY(A, B) { \ m_BackGround.PositionAxleY(MountName(ticket, A, EV_GROUND, B), y); \ m_TradeLine.PositionAxleY(MountName(ticket, A, EV_LINE, B), y); \ m_BtnClose.PositionAxleY(MountName(ticket, A, EV_CLOSE, B), y); \ m_EditInfo1.PositionAxleY(MountName(ticket, A, EV_EDIT, B), y, (A == IT_RESULT ? -1 : 0)); \ m_BtnMove.PositionAxleY(MountName(ticket, A, EV_MOVE, B), (A == IT_RESULT ? 9999 : y)); \ m_EditInfo2.PositionAxleY(MountName(ticket, A, EV_PROFIT, B), (A == IT_RESULT ? y : 9999), 1); \ } #define macroSetAxleX(A, B, C) { \ m_BackGround.PositionAxleX(MountName(ticket, A, EV_GROUND, C), B); \ m_TradeLine.PositionAxleX(MountName(ticket, A, EV_LINE, C), B); \ m_BtnClose.PositionAxleX(MountName(ticket, A, EV_CLOSE, C), B + 3); \ m_EditInfo1.PositionAxleX(MountName(ticket, A, EV_EDIT, C), B + 21); \ m_BtnMove.PositionAxleX(MountName(ticket, A, EV_MOVE, C), B + 80); \ m_EditInfo2.PositionAxleX(MountName(ticket, A, EV_PROFIT, C), B + 21); \ } //+------------------------------------------------------------------+ inline void ReDrawAllsIndicator(void) { int max = ObjectsTotal(Terminal.Get_ID(), -1, -1); ulong ticket; double price; eIndicatorTrade it; eEventType ev; for (int c0 = 0; c0 <= max; c0++) if (GetIndicatorInfos(ObjectName(Terminal.Get_ID(), c0, -1, -1), ticket, price, it, ev)) PositionAxlePrice(ticket, it, price); } //+------------------------------------------------------------------+ inline void PositionAxlePrice(ulong ticket, eIndicatorTrade it, double price) { int x, y, desl; ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y); ObjectSetString(Terminal.Get_ID(), MountName(ticket, it, EV_LINE), OBJPROP_TOOLTIP, DoubleToString(price)); macroSetAxleY(it, true); macroSetAxleY(it, false); switch (it) { case IT_TAKE: desl = 110; break; case IT_STOP: desl = 220; break; default: desl = 0; } macroSetAxleX(it, desl + (int)(ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS) * 0.2), true); macroSetAxleX(it, desl + (int)(ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS) * 0.2), false); } #undef macroSetAxleX #undef macroSetAxleY
「それだけですか。マクロを変更するだけですか。」そうです。コード全体を再作成する必要はなく、単に調整するだけです。本当に必要なのは、MetaTrader 5に操作しているオブジェクトの名前を伝えることだけです。MetaTrader 5が残りの作業をおこないます。このために一連の関数を作成する必要はありません。ハイライトされた部分を追加するだけです。
変更するあと1つの関数を以下に示します。
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: value0 = value0 / Terminal.GetVolumeMinimal(); m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), value0, def_ColorVolumeEdit); m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), value0, def_IndicatorGhostColor); break; case IT_TAKE : case IT_STOP : finance = (value1 / Terminal.GetAdjustToTrade()) * value0; m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), finance); m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), finance, def_IndicatorGhostColor); break; } }
ここには選択したセグメントを追加しました。ゴーストはこのように選択されます。実際のレベルに何が起こっているかを正確に反映しています。実際、よく反映しすぎるので、適切に機能させるには、さらにいくつかのことを実装する必要があります。コードは間違ってはいませんが、ゴーストは実際のラベルと密接に関連しているため、実際には避ける必要があります。
次に、変更する最後の関数を以下に示します。
inline void RemoveIndicator(ulong ticket, eIndicatorTrade it = IT_NULL) { #define macroDestroy(A, B) { \ ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_GROUND, B)); \ ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_LINE, B)); \ ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_CLOSE, B)); \ ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_EDIT, B)); \ if (A != IT_RESULT) ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_MOVE, B)); \ else ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_PROFIT, B)); \ } ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false); if ((it == IT_NULL) || (it == IT_PENDING) || (it == IT_RESULT)) { macroDestroy(IT_RESULT, true); macroDestroy(IT_RESULT, false); macroDestroy(IT_PENDING, true); macroDestroy(IT_PENDING, false); macroDestroy(IT_TAKE, true); macroDestroy(IT_TAKE, false); macroDestroy(IT_STOP, true); macroDestroy(IT_STOP, false); } else { macroDestroy(it, true); macroDestroy(it, false); } ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true); #undef macroDestroy }
ハイライトされたフラグメントが変更されていますが、ここでは特別なことは何もありません。
2.0.2. ゴーストと現実を分ける
前のセクションでおこなった変更によってゴーストは作成されますが、問題があります。実際のオブジェクトに近すぎるということです。実装は一見すると非常に難しいそうに見えますが、コードを見ると、すでに解決策があることがわかります。ただし、この解決策は間違った場所にあるため、場所を変更し、クラス全体でより見やすくしなければなりません。
次のコードに解決策を示します。
void DispatchMessage(int id, long lparam, double dparam, string sparam) { ulong ticket; double price, tp, sl; bool isBuy, bKeyBuy, bKeySell, bEClick; long info; datetime dt; uint mKeys; eIndicatorTrade it; eEventType ev; static bool bMounting = false, bIsDT = false, bIsMove = false; static double leverange = 0, valueTp = 0, valueSl = 0, memLocal = 0; switch (id) { case CHARTEVENT_MOUSE_MOVE: // ... The rest of the code...
ハイライトされたコードが解決策です。でも、それはどのように可能なのでしょうか。指値注文を出すと、システムがデータを作成して操作することを思い出してください。これにより、最終的に、注文がどこに出されるかを示すラベルがチャート上に表示されます。これは、以下のコードでおこなわれます。
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) { 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; //... Rest of the code...
Shiftキーを押して買うかCtrlキーを押して売るかに応じて、システムは未決注文が作成されていることを示します。これはチャートに直接表示されます。テイクプロフィットまたはストップロスとして使用される値は、Chart Trade内でキャプチャされます。次に、注文を出したい点に表示を移動します。その後、マウスの左ボタンをクリックして、そこに注文を出す必要があることをシステムに伝えます。マウスが再び動くとすぐに、注文を表すために使用されたラベルが削除され、注文の表示が残ります。
これまでのところ、特別なことは何もありませんが、コードを調べると、次のことがわかります。
// ... CHARTEVENT_MOUSE_MOVE code.... }else if ((!bMounting) && (bKeyBuy == bKeySell)) { if (bEClick) { bIsMove = false; m_TradeLine.SpotLight(); } MoveSelection(price, mKeys); } break; // ... The rest of the code....
指値注文を作成しておらず、キーが離されている場合、マウスの価格の位置を選択可能なラベルに送信します。これは、ハイライトされた行でおこなわれます。左クリックするとすぐにこの転送が終了し、指標が選択解除され、現在ローカルである変数bIsMoveのステータスが変更されます。これを変えましょう。この変数の状態は、DispatchMessage関数内の別のイベントによってのみ変更されます。このイベントは次のようになります。
// ... Code .... case EV_MOVE: if (bIsMove) { m_TradeLine.SpotLight(); bIsMove = false; }else { m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE)); bIsMove = true; } break;
このコードは、bIsMove変数の状態を変更すると同時に、選択されているかどうかを示すようにラベルを変更します。
したがって、この変数をクラス全体で見えるようにすれば、ゴーストを実際のラベルから分離でき、実際のラベルのみまたはゴーストのみを操作することが可能になります。これは、何が最も興味深いかによって異なります。ここでは実際のラベルを変更しますが、ゴーストはトレードサーバが見ているものを表示します。
このようにすれば、コードをあまりいじる必要はなく、いくつかの詳細を調整するだけになります。左クリックがおこなわれ、オブジェクトが移動されると、指値注文またはストップレベルを変更する注文が送信されます。
これが実際にどのようにおこなわれるか見てみましょう。まず、private変数を作成しましょう。
bool m_bIsMovingSelect;
これは、私が上で説明したことを反映していますが、初期化する必要があります。
C_IndicatorTradeView() : m_bIsMovingSelect(false) {}
次に、DispatchMessageに移動し、bIsMoveの代わりに使用します。
void DispatchMessage(int id, long lparam, double dparam, string sparam) { ulong ticket; // ... Internal code... switch (id) { case CHARTEVENT_MOUSE_MOVE: // ... Internal code... }else if ((!bMounting) && (bKeyBuy == bKeySell)) { if (bEClick) { m_bIsMovingSelect = false; m_TradeLine.SpotLight(); } MoveSelection(price, mKeys); } break; // ... Internal code... case CHARTEVENT_OBJECT_CLICK: if (GetIndicatorInfos(sparam, ticket, price, it, ev)) switch (ev) { // ... Internal code.... case EV_MOVE: if (m_bIsMovingSelect) { m_TradeLine.SpotLight(); m_bIsMovingSelect = false; }else { m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE)); m_bIsMovingSelect = true; } break; } break; } }
変更がハイライトされています。したがって、クラス全体が、ユーザーが選択したラベルで作業するかどうかを知ることができます。このようにして、ゴーストを実際のラベルから分離し、より正確な表現を得ることができます。
2.0.3. 重要なものだけを移動する
前の2つのトピックでは、ゴースト表示ラベルを作成するシステムを作成して修正しました。次に、コンポーネントを移動するために、いくつかの作業をおこなう必要があります。最初の手順は、関数間の過剰な呼び出しを避けるために必要なさまざまな情報を格納する構造体を作成することです。以下に新しい構造体を示します。
struct st00 { eIndicatorTrade it; bool bIsMovingSelect, bIsBuy; ulong ticket; double vol, pr, tp, sl; }m_InfoSelection;
ハイライトされたセグメントは以前はコードに存在していましたが、現在は構造体の一部です。大丈夫です。さらに進むと、構造体にこれらの要素がある理由が明らかになります。
ここでは、変更され、説明が必要な関数について考察します。1つ目はSetTextValueです。
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: value0 = value0 / Terminal.GetVolumeMinimal(); m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), value0, def_ColorVolumeEdit); if (!m_InfoSelection.bIsMovingSelect) m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), value0, def_IndicatorGhostColor); break; case IT_TAKE : case IT_STOP : finance = (value1 / Terminal.GetAdjustToTrade()) * value0; m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), finance); if (!m_InfoSelection.bIsMovingSelect) m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), finance, def_IndicatorGhostColor); break; } }
何かを動かしているとき、ゴーストが新しいデータを追うのではなく、ラベルがどこにあったかを示して静止したままにします。これは、ハイライトされたポイントを追加することで簡単に実行できます。したがって、ラベルが自由に移動している間、ゴーストは静止しています。ここでは、動き自体はありませんが、サーバ上にある値が表示されます。
この後に、以下に示す移動のコードが続きます。
void MoveSelection(double price) { double tp, sl; if (!m_InfoSelection.bIsMovingSelect) return; switch (m_InfoSelection.it) { case IT_TAKE: UpdateInfosIndicators(0, m_InfoSelection.ticket, m_InfoSelection.pr, price, m_InfoSelection.sl, m_InfoSelection.vol, m_InfoSelection.bIsBuy); break; case IT_STOP: UpdateInfosIndicators(0, m_InfoSelection.ticket, m_InfoSelection.pr, m_InfoSelection.tp, price, m_InfoSelection.vol, m_InfoSelection.bIsBuy); break; case IT_PENDING: tp = (m_InfoSelection.tp == 0 ? 0 : price + m_InfoSelection.tp - m_InfoSelection.pr); sl = (m_InfoSelection.sl == 0 ? 0 : price + m_InfoSelection.sl - m_InfoSelection.pr); UpdateInfosIndicators(0, m_InfoSelection.ticket, price, tp, sl, m_InfoSelection.vol, m_InfoSelection.bIsBuy); break; } }
ハイライトされた部分に注目してください。システムがデータを正しく表示するように動きが調整されています。これは奇妙に思えるかもしれませんが、UpdateInfosIndicators関数にはあと1つの修正があります。今やらなければ、後で問題が発生します。その他の関数は非常に単純で、説明は必要ありません。
void SetPriceSelection(double price) { bool isPending; if (!m_InfoSelection.bIsMovingSelect) return; isPending = OrderSelect(m_InfoSelection.ticket); m_InfoSelection.bIsMovingSelect = false; m_TradeLine.SpotLight(); switch (m_InfoSelection.it) { case IT_TAKE: if (isPending) ModifyOrderPendent(m_InfoSelection.ticket, m_InfoSelection.pr, price, m_InfoSelection.sl); else ModifyPosition(m_InfoSelection.ticket, price, m_InfoSelection.sl); break; case IT_STOP: if (isPending) ModifyOrderPendent(m_InfoSelection.ticket, m_InfoSelection.pr, m_InfoSelection.tp, price); else ModifyPosition(m_InfoSelection.ticket, m_InfoSelection.tp, price); break; case IT_PENDING: ModifyOrderPendent(m_InfoSelection.ticket, price, (m_InfoSelection.tp == 0 ? 0 : price + m_InfoSelection.tp - m_InfoSelection.pr), (m_InfoSelection.sl == 0 ? 0 : price + m_InfoSelection.sl - m_InfoSelection.pr)); break; } }
上記の関数は、何が起こっているのか、新しいデータが何であるかをサーバに通知します。ハイライトされた行でいくつかの指標を選択する必要があることに注意してください。そうしないと、サーバへの要求がおこなわれません。
最後に推奨される関数を以下に示します。
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, bIsDT = 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; //Pressed SHIFT bKeySell = (mKeys & 0x08) == 0x08; //Pressed CTRL if (bKeyBuy != bKeySell) { if (!bMounting) { Mouse.Hide(); bIsDT = Chart.GetBaseFinance(m_InfoSelection.vol, valueTp, valueSl); valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / m_InfoSelection.vol); valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / m_InfoSelection.vol); m_TradeLine.SpotLight(MountName(def_IndicatorTicket0, IT_PENDING, EV_LINE)); m_InfoSelection.it = IT_PENDING; m_InfoSelection.ticket = def_IndicatorTicket0; m_InfoSelection.bIsMovingSelect = true; m_InfoSelection.pr = price; bMounting = true; } m_InfoSelection.tp = m_InfoSelection.pr + (bKeyBuy ? valueTp : (-valueTp)); m_InfoSelection.sl = m_InfoSelection.pr + (bKeyBuy ? (-valueSl) : valueSl); m_InfoSelection.bIsBuy = bKeyBuy; MoveSelection(price); if ((bEClick) && (memLocal == 0)) { MoveSelection(0); m_InfoSelection.bIsMovingSelect = false; CreateOrderPendent(m_InfoSelection.vol, bKeyBuy, memLocal = price, price + m_InfoSelection.tp - m_InfoSelection.pr, price + m_InfoSelection.sl - m_InfoSelection.pr, bIsDT); } }else if (bMounting) { MoveSelection(0); m_InfoSelection.bIsMovingSelect = false; Mouse.Show(); memLocal = 0; bMounting = false; }else if ((!bMounting) && (bKeyBuy == bKeySell)) { if (bEClick) SetPriceSelection(price); else MoveSelection(price); } break; case CHARTEVENT_OBJECT_DELETE: if (GetIndicatorInfos(sparam, ticket, price, it, ev)) { CreateIndicatorTrade(ticket, it); GetInfosTradeServer(ticket); m_InfoSelection.bIsMovingSelect = false; UpdateInfosIndicators(0, ticket, m_InfoSelection.pr, m_InfoSelection.tp, m_InfoSelection.sl, m_InfoSelection.vol, m_InfoSelection.bIsBuy); } break; case CHARTEVENT_CHART_CHANGE: ChartSetInteger(ChartID(), CHART_SHOW_OBJECT_DESCR, false); ReDrawAllsIndicator(); break; case CHARTEVENT_OBJECT_CLICK: if (GetIndicatorInfos(sparam, ticket, price, it, ev)) switch (ev) { case EV_CLOSE: 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_InfoSelection.bIsMovingSelect = true; SetPriceSelection(0); break; } break; case EV_MOVE: if (m_InfoSelection.bIsMovingSelect) { m_TradeLine.SpotLight(); m_InfoSelection.bIsMovingSelect = false; }else { m_InfoSelection.ticket = ticket; m_InfoSelection.it = it; if (m_InfoSelection.bIsMovingSelect = (GetInfosTradeServer(ticket) != 0)) m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE)); } break; } break; } }
この関数について注意すべき点がいくつかありますが、以前のバージョンとの違いに注意してください。ここでは、はるかに多くのコードを再利用しています。したがって、コードの他のどこかでエラーが発生したとしても、すぐに気づいて修正できます。以前は、この関数のコードは少し不安定で、他の部分にいくつかの修正と欠陥がありました。主なポイントの1つは、指値注文を出したときに指標を移動するための別のシステムがあったことです。現在は、チャート上で注文を出すために使用されるのと同じシステムで1つになっています。これはオブジェクトの移動にも使用されます。つまり、同じCHARTEVENT_MOUSE_MOVEイベントが未決注文の発注とオブジェクトの移動の両方に使用されます。これは小さなことのように思えるかもしれませんが、コードへの変更が可視化され、問題が発生した場合でも、マウスイベントを使用している限り明らかになります。
結論
変更によって何が起こっているかをより明確に理解するには、以下のビデオをご覧ください。発注システムに関してEAを完全に完成させるために必要なのは、あといくつかの詳細だけであることがわかります。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/10563
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索