
从头开始开发智能交易系统(第 23 部分):新订单系统 (VI)
概述
在上一篇文章从头开始开发交易系统(第 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
但是,伴随函数内部的修改,我们还需要实现一些与鼠标事件相关的修改。 这就是我们首先要关注的。
高亮显示的片段需要替换为其它内容,从而表示将会用到新仓位。 但是,对于用户来说,所有修改都应该能够理解。
我已找到了一种有效的方式,可令所有内容易于理解,同时又可以运作,而且无需对整个代码结构进行重大更改。 这就是创建一个幻影指示标签,其在需要之前将不可见。 幸运的是,MetaTrader 5 提供了一种非常简单的途径来做到这一点。 如此,对于那些已熟悉本系列早期文章中的素材的人来说,很容易就能理解本文内容。
2.0. 实现
为了实现一个幻影标签,我们简单地按照真实标签来创建它。 这它是真实标签的精确阴影,直到我们继续按照上一篇文章中展示的方式操控价格。 在此刻,幻影标签就会随着真实标签的移动而出现在图表上。 这将令您能够轻松比较正在发生的事情,并了解是否要进行更改。
2.0.1. 创建一个幻影指示标签
所有修改都在 C_IndicatorTradeView 类中实现。 我们从定义三条新指令开始:
#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 将为我们完成剩下的工作。 我们不需要为此创建一系列函数。 只需添加高亮显示的部分即可。
要更改的另一个函数在此如下所示:
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 变量的状态,同时更改标签,从而示意它是否被选中。
那么,如果我们令该变量在整个类中可见,我们就可把幻影与真实标签分开,并且有可能只操纵真实标签或幻影 — 这取决于您觉得哪个更有趣。 但在这此,我们将更改真实标签,而幻影将显示交易服务器之所见。
以此方式,我们就不必过多地搅乱代码,只需调整一些细节即可。 一旦左键点击完成,且对象已被移动,则发送更改挂单或止损位的订单。
我们看看这在实践中是如何做到的。 首先,我们创建一个私密变量。
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. 只移动所要的材料
在之前两个主题中,我们创建并修复了系统,从而能用一个幻影指代标签。 现在我们需要移动组件,为此,我们将不得不做一些工作。 第一步是创建一个结构来存储我们所需的各种信息,从而避免函数之间的过多调用。 该结构如下图所示:
struct st00 { eIndicatorTrade it; bool bIsMovingSelect, bIsBuy; ulong ticket; double vol, pr, tp, sl; }m_InfoSelection;
高亮显示的片段以前就存在于代码当中,但现在它作为结构的一部分。 不用担心。 随着我们走得更远,结构为什么拥这些元素将变得更加清晰。
在此,我们研究已修改并需要解释的函数。 第一处是设置文本值。
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 函数还有另一处修复。 如果我们现在不这样做,我们以后会遇到问题。 其它函数非常简单,无需解释。
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; } }
关于该函数,有些事情需要注意,即要关注与以前版本的区别:我们在此重用了更多的代码。 故此,若在其它地方,我们的代码中有错误,我们就可以快速察觉它,并予以修复。 以前,该函数代码有点不稳定,其它部分存在一些修复和缺陷。 其中一个要点就是,当我们下挂单时,有一个单独的系统用于移动指标;现在我们有一个单一的系统,与在图表上下订单的系统相同。 它也能用于移动对象,即现在相同的 CHARTEVENT_MOUSE_MOVE 事件既用于下挂单,又可用于移动对象。 它也许看起来像小事一桩,但它令代码的任何更改均可见,如果我们遇到问题,它就会尽可能长地显示出来,如同我们所用的鼠标事件。
结束语
请观看下面的视频,以便理清思路,了解所做的更改会发生什么。 您将看到,现在我们只需稍多细节即可令 EA 在订单系统方面全部完成。
本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/10563

