从头开始开发智能交易系统(第 26 部分):面向未来(I)

概述

尽管在“从头开始开发交易系统”系列的第 24 部分和第 25 部分的篇幅中展示了代码修复和改进,其中我们已经看到了如何提高系统健壮性,但仍有一些细节留待改进。 但并非因为它们的重要性较低,事实上它们真的很重要。

我们现有的一些问题,是与在交易日里我们想要如何工作,以及我们做什么事情相关。 许多交易者简单地在某个价位处下单,且不会再从该点位挪动。 无论发生什么,他们都会认定这是一个完美的入场点,故不会挪动订单。 他们也许会平移破位级别,甚至删除破位级别,但他们不会改变入场点。

因此,代码中的剩余缺陷不会影响交易者的实际操作方式。 他们甚至可能意识到订单系统含有缺陷(例如,我们将在本文中修复的那些)。 但那些喜欢追逐价格,无论如何都要试图完成一笔交易,但又不想入场的人,会见证系统中的许多错误。 他们中的一些人可能会干预,并令业务不安全(委婉地说),而另一些人却在赚钱;徒留这些交易者在市场面前茫然无助。

2.0. 实现

为了开始本文的旅程,我们先修复 EA 中一个令真金白银爆仓的缺陷。 再者,如果您不会随时更改入场点,则此问题不会影响您。 不过,我建议考虑更新代码,以防万一。 即使修复已经在文后附带的代码中实现,您也许想到这会损害 EA,因为它会损失一些性能,这是事实。 然而,哪个更好:是损失一些性能,亦或冒着风险盲目入场而赔钱?

2.0.1. 入场点错误

该错误是我们第一时间要修复的,尽管它们都需要以某种方式进行修复。 然而,这一个是迄今为止最灾难性的。 当我们放置一笔挂单入场时,它就会发生,比如说破位买入(BUY STOP),并移动入场点,如此,该订单现在应该是限价买入(BUY LIMIT)类型。 似乎这里没问题,但若失败则是相当灾难性的,因为当前开发阶段的 EA 将无法以正确的方式进行更改。 事实上,许多 EA 都希望执行这种修改,且如果发生了,您会在图表上看到某些信息,但服务器上是不同的信息。 只在开仓时,系统才会正确更新,在此之前,EA 在图表上显示的内容与服务器上的数据或将是不一致的。

在某些情况下,我们能容忍这种不一致,而在其它情况下,问题将是一场彻底的灾难。 为了理解这一点,请仔细阅读本文。

为了消除此错误,我们有一个解决方案,即在应用之前也许要遍历不同的路径。 但操作原理始终如一:从订单簿中删除订单,将其移动到新位置,更改订单类型,并将其放回到订单簿。 应该就是这样做的,但如何做到就要取决于具体的实现。

因此,我们就要实现最基本的解决方案,但由于它不太理想,我们不得不处置一些问题。

解决方案是修改以下函数,添加高亮显示的代码行。

void SetPriceSelection(double price)  {          char Pending;                            if (m_Selection.ticket == 0) return;          Mouse.Show();          if (m_Selection.ticket == def_IndicatorTicket0)          {                  CreateOrderPendent(m_Selection.vol, m_Selection.bIsBuy, price,  price + m_Selection.tp - m_Selection.pr, price + m_Selection.sl - m_Selection.pr, m_Selection.bIsDayTrade);                  RemoveIndicator(def_IndicatorTicket0);                  return;          }          if ((Pending = GetInfosTradeServer(m_Selection.ticket)) == 0) return;          m_TradeLine.SpotLight();          switch (m_Selection.it)          {                  case IT_TAKE:                          if (Pending < 0) ModifyOrderPendent(m_Selection.ticket, m_Selection.pr, price, m_Selection.sl);                          else ModifyPosition(m_Selection.ticket, price, m_Selection.sl);                          break;                  case IT_STOP:                          if (Pending < 0) ModifyOrderPendent(m_Selection.ticket, m_Selection.pr, m_Selection.tp, price);                          else ModifyPosition(m_Selection.ticket, m_Selection.tp, price);                          break;                  case IT_PENDING:                          if (!ModifyOrderPendent(m_Selection.ticket, price, (m_Selection.tp == 0 ? 0 : price + m_Selection.tp - m_Selection.pr), (m_Selection.sl == 0 ? 0 : price + m_Selection.sl - m_Selection.pr)))                          {                                  MoveSelection(macroGetLinePrice(def_IndicatorGhost, IT_PENDING));                                  m_TradeLine.SpotLight();                          }                          break;          }          RemoveIndicator(def_IndicatorGhost);  }    

尽管此方案部分解决了问题,但并未彻底解决它。 例如,对于破位买入(BUY STOP)破位卖出(SELL STOP)订单,可以添加这些简单的线条来解决问题。 但对于限价买入(BUY LIMIT)破位限价(STOP LIMIT),一旦我们点击更改入场点,服务器就会立即填单。 更糟糕的则是,我们这笔持仓入场即亏。 如果订单配置为空订单(带有止盈或止损价位),且止损点超出限价,那么除了服务器将立即执行订单之外,它还会立即平仓,而这意味着我们的交易账户彻底遭殃。 这就是为什么交易系统如此难以开发的原因。 我们在模拟账户上进行了若干测试,如果一切看着还正常,我们就会转到实盘账户,而从此我们就开始亏损,且不晓得到底发生了什么。

我再重复一遍:若入场点只放置一次,且永不更改时,该错误不会有任何影响。 只当交易者移动点位时,才会出现问题。

实际上,破位(STOP)订单执行良好。 现在我们需要解决限价(LIMIT)挂单的问题。 尽管这个问题看起来很容易解决,但有一件事需要理解:没有完美的解决方案,对于系统开发人员来说工作优异的解决方案,也许并不适合您

我将在此展示解决此问题的可能解决方案之一。 该解决方案将在如上所示的相同函数中实现。 这是它的新代码:

void SetPriceSelection(double price)  {          char Pending;          double last;          long orderType;                                            if (m_Selection.ticket == 0) return;          Mouse.Show();          if (m_Selection.ticket == def_IndicatorTicket0)          {                  CreateOrderPendent(m_Selection.vol, m_Selection.bIsBuy, price,  price + m_Selection.tp - m_Selection.pr, price + m_Selection.sl - m_Selection.pr, m_Selection.bIsDayTrade);                  RemoveIndicator(def_IndicatorTicket0);                  return;          }          if ((Pending = GetInfosTradeServer(m_Selection.ticket)) == 0) return;          m_TradeLine.SpotLight();          switch (m_Selection.it)          {                  case IT_TAKE:                          if (Pending < 0) ModifyOrderPendent(m_Selection.ticket, m_Selection.pr, price, m_Selection.sl);                          else ModifyPosition(m_Selection.ticket, price, m_Selection.sl);                          break;                  case IT_STOP:                          if (Pending < 0) ModifyOrderPendent(m_Selection.ticket, m_Selection.pr, m_Selection.tp, price);                          else ModifyPosition(m_Selection.ticket, m_Selection.tp, price);                          break;                  case IT_PENDING:                          orderType = OrderGetInteger(ORDER_TYPE);                          if ((orderType == ORDER_TYPE_BUY_LIMIT) || (orderType == ORDER_TYPE_SELL_LIMIT))                          {                                  last = SymbolInfoDouble(Terminal.GetSymbol(), (m_Selection.bIsBuy ? SYMBOL_ASK : SYMBOL_BID));                                  if (((m_Selection.bIsBuy) && (price > last)) || ((!m_Selection.bIsBuy) && (price < last)))                                  {                                          RemoveOrderPendent(m_Selection.ticket);                                          RemoveIndicator(m_Selection.ticket);                                          CreateOrderPendent(m_Selection.vol, m_Selection.bIsBuy, price, (m_Selection.tp == 0 ? 0 : price + m_Selection.tp - m_Selection.pr), (m_Selection.sl == 0 ? 0 : price + m_Selection.sl - m_Selection.pr), m_Selection.bIsDayTrade);                                          break;                                  }                          }                          if (!ModifyOrderPendent(m_Selection.ticket, price, (m_Selection.tp == 0 ? 0 : price + m_Selection.tp - m_Selection.pr), (m_Selection.sl == 0 ? 0 : price + m_Selection.sl - m_Selection.pr)))                          {                                  MoveSelection(macroGetLinePrice(def_IndicatorGhost, IT_PENDING));                                  m_TradeLine.SpotLight();                          }                          break;          }          RemoveIndicator(def_IndicatorGhost);  }  

该操作如下。 当我们要更改挂单的入场点时,我们会检查订单簿(市场深度)中的订单是破位限价(STOP LIMIT)、还是限价买入(BUY LIMIT)类型。 如果都不是,则执行流程将跳到到代码中的另一个点。 如果是,那么我们会立即捕获当前资产价格,且我们将采用以下准则:对于买入订单,捕获当前要价(ASK)值。 分别地,卖出订单则是出价(BID)值。 这替换了采用 LAST 值的旧方法,但由于在某些市场中未用到该值,故我们不会将其用作参考。 然后检查订单簿中的订单是否已变成无效或仅被修改。

如果订单仍然有效,系统将忽略验证码,并转到订单将被更改的部分。 但如果市场深度中的订单已无效,系统将执行以下代码:

RemoveOrderPendent(m_Selection.ticket);  RemoveIndicator(m_Selection.ticket);  CreateOrderPendent(m_Selection.vol, m_Selection.bIsBuy, price, (m_Selection.tp == 0 ? 0 : price + m_Selection.tp - m_Selection.pr), (m_Selection.sl == 0 ? 0 : price + m_Selection.sl - m_Selection.pr), m_Selection.bIsDayTrade);  break;    

但上面的代码只会将限价卖出(SELL LIMIT)和限价买入(BUY LIMIT)订单分别更改为破位卖出(SELL STOP)和破位买入(BUY STOP)。 如果我们想将这些类型恢复到原本的类型,或只是防止这种更改该怎么办?

如果我们不希望系统更改已执行订单的类型,我们只需要将高亮显示的片段替换为以下代码:

if ((orderType == ORDER_TYPE_BUY_LIMIT) || (orderType == ORDER_TYPE_SELL_LIMIT))  {          last = SymbolInfoDouble(Terminal.GetSymbol(), (m_Selection.bIsBuy ? SYMBOL_ASK : SYMBOL_BID));          if (((m_Selection.bIsBuy) && (price > last)) || ((!m_Selection.bIsBuy) && (price < last)))          {                  RemoveOrderPendent(m_Selection.ticket);                  RemoveIndicator(m_Selection.ticket);                  CreateOrderPendent(m_Selection.vol, m_Selection.bIsBuy, price, (m_Selection.tp == 0 ? 0 : price + m_Selection.tp - m_Selection.pr), (m_Selection.sl == 0 ? 0 : price + m_Selection.sl - m_Selection.pr), m_Selection.bIsDayTrade);                  MoveSelection(macroGetLinePrice(def_IndicatorGhost, IT_PENDING));                  m_TradeLine.SpotLight();                  break;          }  }  

此段代码将防止订单类型被更改。 您可以更改挂单的填单点位,但不能将限价订单更改为破位订单,反之亦然。 现在,如果您想继续追逐价格,并在某个点位强制入场,请用如下显示的代码。 这是将在 EA 中使用的代码。

#define def_AdjustValue(A) (A == 0 ? 0 : price + A - m_Selection.pr)  #define macroForceNewType       {                                                                                                                                                                 RemoveOrderPendent(m_Selection.ticket);                                                                                                                                           RemoveIndicator(m_Selection.ticket);                                                                                                                                              CreateOrderPendent(m_Selection.vol, m_Selection.bIsBuy, price, def_AdjustValue(m_Selection.tp), def_AdjustValue(m_Selection.sl), m_Selection.bIsDayTrade);                        break;                                                                                                                                                                                            }                    void SetPriceSelection(double price)                          {                                  char Pending;                                  double last;                                  long orderType;                                                                    if (m_Selection.ticket == 0) return;                                  Mouse.Show();                                  if (m_Selection.ticket == def_IndicatorTicket0)                                  {                                          CreateOrderPendent(m_Selection.vol, m_Selection.bIsBuy, price,  price + m_Selection.tp - m_Selection.pr, price + m_Selection.sl - m_Selection.pr, m_Selection.bIsDayTrade);                                          RemoveIndicator(def_IndicatorTicket0);                                          return;                                  }                                  if (m_Selection.ticket == def_IndicatorFloat)                                  {                                          CreateOrderPendent(m_Selection.vol, m_Selection.bIsBuy, m_Selection.pr,  m_Selection.tp, m_Selection.sl, m_Selection.bIsDayTrade);                                          RemoveIndicator(def_IndicatorFloat);                                          return;                                  }                                  if ((Pending = GetInfosTradeServer(m_Selection.ticket)) == 0) return;                                  m_TradeLine.SpotLight();                                  switch (m_Selection.it)                                  {                                          case IT_TAKE:                                                  if (Pending < 0) ModifyOrderPendent(m_Selection.ticket, m_Selection.pr, price, m_Selection.sl);                                                  else ModifyPosition(m_Selection.ticket, price, m_Selection.sl);                                                  break;                                          case IT_STOP:                                                  if (Pending < 0) ModifyOrderPendent(m_Selection.ticket, m_Selection.pr, m_Selection.tp, price);                                                  else ModifyPosition(m_Selection.ticket, m_Selection.tp, price);                                                  break;                                          case IT_PENDING:                                                  orderType = OrderGetInteger(ORDER_TYPE);                                                  if ((orderType == ORDER_TYPE_BUY_LIMIT) || (orderType == ORDER_TYPE_SELL_LIMIT))                                                  {                                                          last = SymbolInfoDouble(Terminal.GetSymbol(), (m_Selection.bIsBuy ? SYMBOL_ASK : SYMBOL_BID));                                                          if (((m_Selection.bIsBuy) && (price > last)) || ((!m_Selection.bIsBuy) && (price < last))) macroForceNewType;                                                  }                                                  if (!ModifyOrderPendent(m_Selection.ticket, price, def_AdjustValue(m_Selection.tp), def_AdjustValue(m_Selection.sl))) macroForceNewType;                                  }                                  RemoveIndicator(def_IndicatorGhost);                          }  #undef def_AdjustValue  #undef macroForceNewType    

重要说明:由于 ForceNewType 宏替换的原因,使用此代码时要小心。 此宏替换包含一个 “break” 语句,若其执行,则导致代码退出 “case” 模块。 因此,您在修改此模块时应格外小心。

系统移动入场点位时将不再遭遇错误,但我们还有其它问题需要解决。 我已经展示了纠正问题的途径,来修改或保持订单的相同类型 — 您来选择最适合您的那个。 请记住,这些方案均各有其优点和缺点。 但我不会深入细节。 我只展示如何纠正和实现系统。

这些修改的结果可在以下视频中看到:

2.0.2. 为将来做准备

上述修改解决了问题,但还有更多的事情可以做。 在此,我将展示变化的开始。 查看 EA 的订单体系,依然还有很大的提升空间。 这还需要少量的修改,我想解释一下,以便您可以选择最适合自己的路径,因为每名交易者都有自己应对市场的行为方式。 我不想令您觉得有义务去使用我将向您展示的系统。 取而代之,我想打造一个基础,如此任何人都可以开发自定义 EA。

因此,我们迈入下一个事实:从第 18 部分开始,我一直在展示如何开发一个订单系统,易于交易特定资产的人使用。 但在第 20 部分中,订单系统加进了了视觉元素,因为在某些时候 Chart Trade 对于交易来说变得并无必要,因为一切都将由订单系统本身指示,如此您就能够在图表上正确更改和配置所有内容。 为了达到这一点,我们需要从某个地方开始,我们现在就去做。

如何直接在订单内更改交易量,而无需从图表中删除订单,难道在 Chart Trade 中更改交易量,之后要在图表上重新下订单? 很有趣,不是吗? 我们现在就来实现此功能。 它在某些情况下有很大帮助,但您应当学习并理解如何使用该系统,因为您在任何其它平台上找不到它。 老实说,我从未见过拥有此类功能的 EA。 我们来看看您在任何拥有此功能的 EA 中能做哪些操作。

首先,定义一个新的指标索引。

#define def_IndicatorFloat      3    

当挂单收到此值作为单号时,能够以完全不同的方式进行处理。 以前存在的所有东西都还保留在订单系统之中,而我们只添加一个新索引。

之后,我们将往系统中添加一个新对象:

C_Object_BackGround     m_BackGround;  C_Object_TradeLine      m_TradeLine;  C_Object_BtnBitMap      m_BtnClose,                          m_BtnCheck;  C_Object_Edit           m_EditInfo1,                          m_EditInfo2;  C_Object_Label          m_BtnMove;    

此对象始终在订单挂起时启用一些功能。

现在我们迈入 C_Object_BitMap 类编辑它。 添加一些定义:

#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"  //+------------------------------------------------------------------+  #resource "\" + def_BtnClose  #resource "\" + def_BtnCheckEnabled  #resource "\" + def_BtnCheckDisabled    

我们需要知道该类里发生了什么。 如此,添加以下函数:

bool GetStateButton(string szObjectName) const  {          return (bool) ObjectGetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_STATE);  }  //+------------------------------------------------------------------+  inline void SetStateButton(string szObjectName, bool bState)  {          ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_STATE, bState);  }    

GetStateButton 返回按钮的状态。 MetaTrader 5 会改变状态,因此我们实现额外的步骤,而只需查询按钮值是 True 或是 False。 但也许会发生状态出乎我们所料的情况。 然后调用 SetStateButton 设置状态,来反映出交易服务器和 EA 都看到的实际状态。

另一处简单的修改是在 C_Object_Edit 类之中:

inline void SetOnlyRead(string szObjectName, bool OnlyRead)  {          ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_READONLY, OnlyRead);  }  

它示意该值是否可以编辑。 我们希望能够直接在图表上修改订单交易量,而无需借助 Chart Trade。 创建的任何挂单将始终处于只读模式,但我们将创建一个系统来修改这些。

故此,我们回到 C_IndicatorTradeView,并实现更多修改。 我们将为系统创建一个新函数。 它如下所示:

#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);                                  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    

当调用此函数时,所有指标对象都被重命名,即指向订单的单号将被另一个值替换。 在这种情况下,它是我们在本主题开头研究过的那个指标。 我们还有一个问题。 我不使用任何结构来维护指标对象列表,我会以不同的方式来完成这一点。 这种方式就是,我们让 MetaTrader 5 为我们操心这个列表。 但也正因如此,我无法创建无限的浮动订单,因为我们只能有一笔浮动订单。 这一点可用以下代码行来检查:

if (macroGetLinePrice(def_IndicatorFloat, IT_PENDING) > 0) return false;    

此处的检查很简单:如果指标线位于某处,宏替换将返回一个不同于 0 的值,如此我们就可知道已有某个指标占用了预留单号。 若请求被拒绝,这一点稍后对于 EA 恢复指标数据非常重要。 MetaTrader 5 会自动更改位图对象的状态,故此我们需要通知调用方有关失败信息。

下一个需要修改的地方是在创建指标的函数之中:

#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 ? 84 : (A == IT_PENDING ? 108 : 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);  }                                                                                                                         }                  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);                                                  break;                                          case IT_RESULT  : macroCreateIndicator(it, clrDarkBlue, clrDarkBlue, def_ColorVolumeResult); break;                                  }                                  m_BtnClose.Create(ticket, macroMountName(ticket, it, EV_CLOSE), def_BtnClose);                          }  #undef macroCreateIndicator    

所有高亮显示的部分都已加入,能够支持我们的新系统。 基本上,我们在此创建一个始终设置为 true 的复选框,这意味着订单将被立即放入订单簿之中。 我不打算修改这种交易方式,但将复选框的值从 “true” 改为 “false” 并事实上并不简单,这样会防止直接下订单。 此更改需要进行其它更深层次的修改,问题在于某些时候,您也许来下单,却忘记勾选复选框。 因此,若入场点被错过,您会认为这是 EA 的缺陷,而实际上这一切都是由于健忘。 所以,为避免这种情况,默认情况下,挂单将直接进入订单簿,因此您必须明确更改其状态。

下一个真正重要的函数如下所示:

#define def_AdjustValue(A) (A == 0 ? 0 : price + A - m_Selection.pr)  #define macroForceNewType       {                                                                                                                                                                 RemoveOrderPendent(m_Selection.ticket);                                                                                                                                           RemoveIndicator(m_Selection.ticket);                                                                                                                                              CreateOrderPendent(m_Selection.vol, m_Selection.bIsBuy, price, def_AdjustValue(m_Selection.tp), def_AdjustValue(m_Selection.sl), m_Selection.bIsDayTrade);                        break;                                                                                                                                                                                            }                    void SetPriceSelection(double price)                          {                                  char Pending;                                  double last;                                  long orderType;                                                                    if (m_Selection.ticket == 0) return;                                  Mouse.Show();                                  if (m_Selection.ticket == def_IndicatorTicket0)                                  {                                          CreateOrderPendent(m_Selection.vol, m_Selection.bIsBuy, price, def_AdjustValue(m_Selection.tp), def_AdjustValue(m_Selection.sl), m_Selection.bIsDayTrade);                                          RemoveIndicator(def_IndicatorTicket0);                                          return;                                  }                                  if (m_Selection.ticket == def_IndicatorFloat)                                  {                                          switch(m_Selection.it)                                          {                                                  case IT_STOP   : m_Selection.sl = price; break;                                                  case IT_TAKE   : m_Selection.tp = price; break;                                                  case IT_PENDING:                                                          m_Selection.sl = def_AdjustValue(m_Selection.sl);                                                          m_Selection.tp = def_AdjustValue(m_Selection.tp);                                                          m_Selection.pr = price;                                                          break;                                          }                                          m_Selection.ticket = 0;                                          m_TradeLine.SpotLight();                                          return;                                  }                                  if ((Pending = GetInfosTradeServer(m_Selection.ticket)) == 0) return;                                  m_TradeLine.SpotLight();                                  switch (m_Selection.it)                                  {                                          case IT_TAKE:                                                  if (Pending < 0) ModifyOrderPendent(m_Selection.ticket, m_Selection.pr, price, m_Selection.sl);                                                  else ModifyPosition(m_Selection.ticket, price, m_Selection.sl);                                                  break;                                          case IT_STOP:                                                  if (Pending < 0) ModifyOrderPendent(m_Selection.ticket, m_Selection.pr, m_Selection.tp, price);                                                  else ModifyPosition(m_Selection.ticket, m_Selection.tp, price);                                                  break;                                          case IT_PENDING:                                                  orderType = OrderGetInteger(ORDER_TYPE);                                                  if ((orderType == ORDER_TYPE_BUY_LIMIT) || (orderType == ORDER_TYPE_SELL_LIMIT))                                                  {                                                          last = SymbolInfoDouble(Terminal.GetSymbol(), (m_Selection.bIsBuy ? SYMBOL_ASK : SYMBOL_BID));                                                          if (((m_Selection.bIsBuy) && (price > last)) || ((!m_Selection.bIsBuy) && (price < last))) macroForceNewType;                                                  }                                                  if (!ModifyOrderPendent(m_Selection.ticket, price, def_AdjustValue(m_Selection.tp), def_AdjustValue(m_Selection.sl))) macroForceNewType;                                  }                                  RemoveIndicator(def_IndicatorGhost);                          }  #undef def_AdjustValue  #undef macroForceNewType    

高亮显示的代码部分会完成一件有趣的事情:它们只更新将在选择器中用到的数值,但这些数值实际上存储在指标本身当中。 若我们以更常见的方式移动系统,这也许会发生,因此我们需要在选择器中指定这些数值,如此函数就能够在执行位置计算时指定正确的数值。

该函数中有些内容可能没有意义。 它负责创建和修改挂单的数据,但如果您查看它,您将看不到任何挂单返回到订单簿后的变化。 您可以直接在图表上移动、修改、和调整订单的交易量,但您将无法看到它如何反馈到图表。

这是事实。 更改和创建挂单的整个系统均在上述函数中实现。 特奇怪的是,这个函数不会仅仅迁就我们的希望,就将订单放回订单簿之中,这是因为它实际上只是发出了请求,如下所示。 为免复杂化,我只展示负责在市场深度中请求下订单的部分。

void DispatchMessage(int id, long lparam, double dparam, string sparam)  {    // ... Internal code...            case CHARTEVENT_OBJECT_CLICK:                  if (GetIndicatorInfos(sparam, ticket, it, ev)) switch (ev)                  {                          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)                  {                          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                  {                          m_Selection.ticket = def_IndicatorTicket0;                          m_Selection.it = IT_PENDING;                          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.bIsBuy = (m_Selection.pr < m_Selection.tp) || (m_Selection.sl < m_Selection.pr);                          m_Selection.bIsDayTrade = true;                          m_Selection.vol = m_EditInfo1.GetTextValue(macroMountName(def_IndicatorFloat, IT_PENDING, EV_EDIT)) * Terminal.GetVolumeMinimal();                          SetPriceSelection(m_Selection.pr);                          RemoveIndicator(def_IndicatorFloat);                  }    // ... Rest of the code...  

看看系统如何自我构建:随着系统变得越来越大,我们的编程却越来越少。

高亮显示的代码与我们在主题开头创建的指标有关。 虽然一切似乎都工作良好,但有一些东西我们稍后会改变,因为当浮动订单返回订单簿时,日内交易订单还有一个缺点,原因则是它会在一天结束时平单。 这个稍后会加以修改,但您应当留意这一点。 现在您可能会对这一切感到困惑,并且仍然不明白当我们单击复选框时,挂单实际上是如何进入和离开订单簿的。 参见如下示意图:

从头开始开发智能交易系统(第 26 部分):面向未来(I)

能看到所有调用都来自同一个位置。 我们有一笔订单从市场深度中删除,但它还继续出现在图表上。 所有操作都遵照前面文章所示执行。 但是,如果您尝试查找订单返回到市场深度的特定时间时,您可能会在代码中迷失方向。 现在,如果您查看示意图,您可看到调用来自 DispatchMessage 函数,因为这是调用 SetPriceSelect 函数的唯一位置。 但如果我们看一下 SetPriceSelect 函数,并未涉及依据浮动系统中的索引创建订单。 但要注意一件事。 我们曾依据索引 0 创建订单,而这正是我们所用的。 我们更改订单单号,并通报它将作为索引 0 的单号 — 以这种方式创建订单。 请参阅下面的代码以了解其工作原理。

m_Selection.ticket = def_IndicatorTicket0;  m_Selection.it = IT_PENDING;  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.bIsBuy = (m_Selection.pr < m_Selection.tp) || (m_Selection.sl < m_Selection.pr);  m_Selection.bIsDayTrade = true;  m_Selection.vol = m_EditInfo1.GetTextValue(macroMountName(def_IndicatorFloat, IT_PENDING, EV_EDIT)) * Terminal.GetVolumeMinimal();  SetPriceSelection(m_Selection.pr);  RemoveIndicator(def_IndicatorFloat);  

除了高亮显示的代码行之外,代码是完美的。 目前没有办法解决这个问题。 这将在下一篇文章中完成,因为我们必须对类本身进行一些修改。

下面的视频演示修改完毕的结果。 注意交易量是如何修改的,以及如何在指定点位发送新订单。 EA 现在更加易于使用了。

本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/10620

附加的文件 |

下载ZIP
EA_-_Em_direooo_ao_Futuro_b_I_r.zip (12033.63 KB)

 


 

MyFxtops迈投(www.myfxtops.com)-靠谱的外汇跟单社区,免费跟随高手做交易!

 

免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。

風險提示

MyFxtops邁投所列信息僅供參考,不構成投資建議,也不代表任何形式的推薦或者誘導行為。MyFxtops邁投非外匯經紀商,不接觸妳的任何資金。 MYFXTOPS不保證客戶盈利,不承擔任何責任。從事外彙和差價合約等金融產品的槓桿交易具有高風險,損失有可能超過本金,請量力而行,入市前需充分了解潛在的風險。過去的交易成績並不代表以後的交易成績。依據各地區法律法規,MyFxtops邁投不向中國大陸、美國、加拿大、朝鮮居民提供服務。

邁投公眾號

聯繫我們

客服QQ:981617007
Email: service@myfxtop.com

MyFxtops 邁投