量化交易软件:跨平台智能交易系统停止位
概述
如前一篇文章中所讨论的那样, 对于跨平台智能交易系统, 赫兹量化已创建了一个订单管理器 (COrderManager), 它负责处理 MQL4 和 MQL5 之间交易入场和离场的大部分差异。在两个版本中, 智能交易系统通过创建 COrder 的实例将交易信息保存在内存中。这些类对象实例的动态指针容器也可作为 COrderManager 的类成员 (当前和历史交易均可)。

编辑切换为居中
订单管理器能够直接处理每笔交易的停止价位。但是, 这样做会有一些限制:
COrderManager 有很大可能需要扩展, 以便能够定制如何处理每笔交易的停止价位。
COrderManager 的现有方法只能处理主要交易的入场。
可以扩展 COrderManager, 以便它可以自行处理每笔交易的停止价位。不过, 这不仅限于实际价位的设置, 还涉及有关停止位监控的其它任务, 诸如修改和检查行情是否已触及预定价位。这些功能将致 COrderManager 更复杂, 而目前并未提及如何拆分实现它。
订单管理器仅处理主要交易的入场, 而 EA 执行其它交易操作时会需要一些停止价位, 例如放置挂单, 以及真实仓位的平仓。虽然订单管理器可以自行处理它, 但最好将其重点放在主要交易的入场上, 并让另一个类对象处理停止价位。
在本文中, 赫兹量化将研讨具体实现, 其中 COrderManager 将专门处理主要交易 (目前为止) 的入场和离场, 然后由另一个单独的类对象 (CStop) 处理停止价位的实施。
COrder
正如赫兹量化从前一篇文章 (参见 跨平台智能交易系统: 订单) 中了解到的那样, 在交易操作 (入场) 成功之后创建了一个 COrder 实例。关于止损和止盈的信息可以从经纪商的后台获取并保存。然而, 如果要对经纪商隐藏停止位或涉及多个停止位的情况下, 关于这些停止位的更多信息就应该被保存在本地。因此, 在后一种情况下, 在仓位成功入场后, 应首先创建 COrder 实例, 随后是代表其止损和止盈价位的对象实例。在早前的文章中, 赫兹量化已演示了如何在创建时将 COrder 实例添加到订单管理器中, 正如下图所示:

编辑切换为居中
停止价位以相同的方式添加。为此, 赫兹量化只需要在创建新的 COrder 实例之后, 并在将其添加到进行中交易列表 (COrders) 之前插入该方法。因此, 我们只需稍微修改以上示意图, 如下图所示:

编辑切换为居中
创建停止价位的一般操作如下图所示:

编辑切换为居中
如前面两个流程图所示, 一旦交易成功入场, 将会创建一个新的 COrder 实例来表示它。此后, 将为每个已定义的止损和止盈价位创建 COrderStop 的实例。如果在智能交易系统的初始化中没有声明 CStop 实例, 则会跳过这一特定过程。另一方面, 如果创建了 COrderStop 的实例, 那么这些实例的指针将被存储在早前创建的 COrder 实例中。赫兹量化可以在 COrder 的 Init 方法中找到这个操作:
bool COrderBase::Init(COrders *orders,CStops *stops) { if(CheckPointer(orders)) SetContainer(GetPointer(orders)); if(CheckPointer(stops)) CreateStops(GetPointer(stops)); m_order_stops.SetContainer(GetPointer(this)); return true; }
为了给 COrder 实例创建停止价位, 赫兹量化可以看到它调用了自身的 CreateStops 方法, 如下所示:
void COrderBase::CreateStops(CStops *stops) { if(!CheckPointer(stops)) return; if(stops.Total()>0) { for(int i=0;i<stops.Total();i++) { CStop *stop=stops.At(i); if(CheckPointer(stop)==POINTER_INVALID) continue; m_order_stops.NewOrderStop(GetPointer(this),stop); } } }
该方法遍历 CStop 的所有可用实例, 该实例代表每对止损和止盈价位。这些 CStop 实例中的每一个都传递给 COrderStops 的实例, 其中 COrderStops 只是所有给定顺序的停止价位的容器。然后, 赫兹量化可以如下构造类对象的层次结构:

编辑切换为居中
对于 COrder 的每个实例, 都有一个类型为 COrderStops 的成员。这个 COrderStops 实例是一个容器 (CArrayObj 的扩展), 它包含指向 COrderStop 实例的指针。每个 COrderStop 实例代表该特定交易 (COrder 实例) 的停止价位 (CStop)。
CStop
如前所述, 赫兹量化希望有一定的自由度来定制每笔交易的停止价位如何处理, 而无需修改订单管理器的源代码。这主要是由 CStop 类完成的。当中这个类的职责是:
定义止损和止盈价位
执行计算停止价位所需的计算
主要交易的止损和止盈价位实现
检查是否已触发停止价位
调整 MQL4 和 MQL5 之间实现止损和止盈价位的差异
定义如何随时间处理停止位 (例如, 盈亏平衡, 尾随等)
指针 #1-#5 将在本文中讨论, 而 #6 将在单独一篇文章中讨论。
主要停止位
主要停止位是触发整体仓位退出的停止价位。通常, 这是经纪商端的停止位。当经纪商端的止损或止盈触发时, 整体持仓离场, 无论 EA 是否打算为此进一步操作。然而, 如果有多个停止价位, 且无经纪商端的停止位, 那么让 EA 决定哪个是主要停止位可能是一个坏主意。在这种情况下, 程序员必须选择哪个 CStop 实例是交易的主要停止位。这对于某些依赖仓位的主要停止位的功能和特性尤为有用, 例如资金管理。由于主要停止位的止损会导致整体持仓的离场, 它代表入场交易的最大风险。已知主要停止位的实例, EA 将能够相应地计算手数。
值得注意的是, 当一个停止位被指定为主要停止位时, 其交易量将始终等于主要交易的初始交易量。对于经纪商端和虚拟停止位这将工作良好, 但不适用在基于挂单的停止位。有两个关联的问题会引出这种方法。
第一个原因是在 赫兹量化中, 在挂单尚未触发前可以修改入场价格。在赫兹量化中, 这是不可能的, 因为必须维护交易操作的清晰足迹。必须放出新的挂单, 如果成功, 应删除旧订单, 然后智能交易系统应使用新挂单作为新的停止价位。
第二个问题是给经纪商发送替换挂单时, 旧的挂单可能被同时触发。由于 EA 对触发挂单没有控制权 (只有经纪商可以), 这会导致诸如孤立交易或一个停止价位超预期将更多持仓平仓等问题。
为了避免这些问题, 更简单的方法是在交易创建时分配挂单的交易量, 而非在主要交易的整个生存期内动态调整挂单的交易量。不过, 这就要求如果主停止位为挂单类型, 则不应有其它挂单停止位。