概述
在上一篇文章,从头开始开发智能交易系统(第 26 部分):面向未来((I)中,我们修复了订单系统中存在的灾难性错误。 我们还开始实现修改,启动新订单系统的操作。 尽管本系列文章中最初实现的系统非常有趣,但它存在的一个缺陷令其无法操作。 此缺陷在上一篇文章的末尾显示。 原因是不知道如何交易,更具体地说,除了其它次要问题外,如何选择订单或持仓的到期时间。 该系统固定交易一笔订单或持仓,且应在交易时段、或当日收盘时平仓。 但有时我们想进行长线交易,如以让一切都保持原样并没有真正的帮助。
因此,在本文中,我将向您展示如何修复。 我们将看到如何令订单系统更加直观,如此您可立即准确地判定每笔订单是什么,它是如何处理的、以及预期的走势类型是什么。
这个系统非常有趣、简单、和直观,一旦您看到它的实际应用,您就不会再想抛开它来操作。 我在本文中向您展示的,只是您可在订单系统中实现的众多可能性之一。 也许,稍后我将展示其它内容,但我们在本文中将要看到的内容可针对您的特定情况创建其它有用且有趣的修改作为优良的基础。 无论如何,我尽量保持这些文章中的所有内容。
2.0. 直观模型
到目前为止,我们一直在如下操控订单:
止盈和止损指示具有非常直观的、可识别形式:绿色示意我们将在帐户里赚取的金额,红色示意将扣除的金额。 一切都很清楚明了。 如果我们有一个如下所示的破位指示,它仍然表示激活止损将导致结果累计到我们的账户之中。 换言之,破位订单级别不需要我们参与,至少目前是这样。 也许,未来您也许想修改它们中的某些内容,但此时此刻它们非常适合使用。
这种利用破位级别的方式对于任何交易者进行分析都非常直截了当。 但是我们还有一些不太清楚的东西。 第一个是挂单的入场点指示。
我们能知道这笔挂单是买入还是卖出订单吗? 还有一件事:是否有可能知道这笔挂单是否会在一天结束时关闭,或将开仓作为长线持仓? 这很容易。 现在,如果我们已经有持仓,指标将如下所示:
再次,我们遇到了与挂单指标相同的问题。 当您查看图表,并见到这些指标时,您无法判定持仓是否会在一天结束时平仓、或是否会持仓更长时间。 如果它在一天结束时平仓,您不会希望经纪商强制停止它,因为您必须为此付费。 且在没有任何准则的情况下平单也不是很合理,因为即便是 MetaTrader 工具箱也不会显示此信息,那么由指标将其显示在图表上就非常赞。
因此,我们将不得不在此处进行修改,尤其是在显示持仓入场点的指标中,从而能更好地了解正在发生的事情。
2.0.1. 如何往指标里添加新信息
在不占用图表太多空间的情况下,添加新信息的最简单方式是利用位图,因为它们易于理解、且具有相当的代表性。 因此,无需插入任何额外的代码,我们就往 EA 里添加了四个新位图,这可在 C_IndicatorTradeView 类中看到。
#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
此外,我们只需要在订单系统中实现两个新对象。
//+------------------------------------------------------------------+ 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 的过错,而是因为交易者过早更改了注释。
这是此解决方案的缺点,若有人针对如何仅通过查看仓位数据来判定其是否为日内交易有思路,请在评论中分享。
挂单情况的方式如下图所示:
现在我们几乎已经准备就绪,我们只需要在代码里再多加一些,如此令 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...
这段代码实际上在做什么? 您有何想法吗? 那好,本文中的视频演示了这一点,但是您能理解这些事情是如何完成的吗? 许多人想象这是一段极其复杂的代码,但它如上就在那里。
有两件事需要我们去做。 首先是,当单击 BitMap 对象时,其状态会发生变化,我们必须检查其单号是来自服务器上已经存在的内容,还是仅存在于图表上的内容。 这是经由绿色高亮显示的代码行完成的。 如果服务器上存在单号,则必须撤消状态更改,然后 EA 按需进行更改来调整状态。
现在看看以黄色高亮显示的部分。 该思路基于以下原因:如果图表上已经存在一笔订单,而我只想反转方向,我为什么要在图表上另下一笔订单呢? 换句话说,如果是买入,现在我希望它是卖出,反之亦然。 黄色片段就是做这事的:当我们点击负责买入还是卖出的位图时,方向会自动改变。 一个细节:这只能对浮动订单完成;已在服务器上的订单则会被禁止这样做。
经历所有这些更改,指标现在如下所示:
挂单类型
持仓指标:
现在更容易判定挂单或持仓正在做什么,因为您可以准确知道预期的走势或持仓生存期。 向上的绿色箭头表示多头持仓;向下的红色箭头表示空头持仓。 字母 D 表示日内交易,将在一天结束时平仓。 如果是 S,那么它是波段交易,且不一定要在一天结束时平仓。
下一个视频展示了新订单系统的工作原理。 我专注于挂单,因为它们可以进一步修改,而持仓指标无法更改。 它们只会显示服务器提供的有关该笔持仓的数据。 在实盘账户上试用之前,请仔细查看它是如何工作的,因为该系统很实用,但您需要熟悉它才能充分利用其功能。
结束语
好了,我们的订单系统现在是多面手。 它可以做若干件事情,对我们有很大帮助,但它仍然缺少一个重要的细节,将在下一篇文章中实现。 那么,期待与您很快再见…
本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/10630
MyFxtops迈投(www.myfxtops.com)-靠谱的外汇跟单社区,免费跟随高手做交易!
免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。