MQL5 Cookbook: 处理 TradeTransaction 事件

Denis Kirichenko | 16 十月, 2014

介绍

在本文中,我打算介绍一种使用 MQL5 的手段来控制交易事件的方法。我要指出的是,有些文章已经专门讨论过这个话题。"EA 交易中采用 OnTrade() 函数处理交易事件" 即是其一。我不想重复其他作者,并将使用另一个处理器 - OnTradeTransaction()。

我想提请读者注意以下几点。在 MQL5 语言的当前版本,客户终端里包含 14 个正式的事件处理器。此外,程序员有可能利用 EventChartCustom() 创建自定义事件,并利用 OnChartEvent() 来处理它们。然而,术语“事件驱动编程”(EDP)在文档中并未提及。这很奇怪,事实表明,MQL5 的任何程序,都基于 EDP 原则创建。例如,在所有 EA 模板的“EA 事件处理器”步骤,可让用户进行选择。

很明显,不管怎样,事件驱动编程机制在 MQL5 中都被使用。该语言包含的程序块由两部分组成:事件选择和处理。此外,如果我们谈论的是客户端的事件,程序员只控制第二部分,即事件处理器。为了公平起见,也有一些事件异常。定时器和自定义事件也在其中。这些事件的控制权,则完全留给了程序员。

1. TradeTransaction 事件

在深入讨论我们的话题之前,让我们参考官方资料。

根据文档, 该 TradeTransaction 事件是一个交易账户的确定操作结果。操作本身由数个确定的事务阶段构成。例如,按市价开仓,通过交易账户进行的最常见操作有以下几个阶段:

  1. 制定交易请求;
  2. 验证交易请求;
  3. 发送交易请求至服务器;
  4. 接收服务器端的有关交易请求执行情况的响应。

这些序列,仅显示出终端-服务器对之间的工作逻辑,它们反映在 EA 代码的字符串中。从 TradeTransaction 交易事件的角度来看,在市场中开仓,会按以下途径发生:

  1. MQL5-程序接收来自服务器端的有关请求完成的结果;
  2. 请求在订单表格中具有唯一单号,包括已开订单列表;
  3. 在订单执行之后从已开订单列表中删除;
  4. 之后,订单转移到帐户历史;
  5. 账户历史同样包含成交数据,它是订单的执行结果。

所以, 开仓调用五次 OnTradeTransaction() 处理器。

我们稍后将详细讨论程序代码,现在我们打算近距离观察函数头。它有三个输入参数。

void  OnTradeTransaction(
   const MqlTradeTransaction&    trans,        // structure of the trade transaction
   const MqlTradeRequest&        request,      // structure of the request
   const MqlTradeResult&         result        // structure of the response
   );

这些参数在文档中有详细描述。我想提请注意,一个交易事务结构的参数,是一类抛出信息,由处理器在当前调用过程中接收。

我也必须用几句话来说一下有关交易事务,因为我们将会碰到了很多。

在 MQL5 中, ENUM_TRADE_TRANSACTION_TYPE 是一个特殊枚举,它负责提供交易事务的类型。要找出交易事务所属类型, 我们需要参考参数- MqlTradeTransaction 类型常量。

struct MqlTradeTransaction
  {
   ulong                         deal;             // Ticket of the deal
   ulong                         order;            // Ticket of the order
   string                        symbol;           // Trade symbol
   ENUM_TRADE_TRANSACTION_TYPE   type;             // Type of the trade transaction
   ENUM_ORDER_TYPE               order_type;       // Type of the order
   ENUM_ORDER_STATE              order_state;      // Status of the order
   ENUM_DEAL_TYPE                deal_type;        // Type of the deal
   ENUM_ORDER_TYPE_TIME          time_type;        // Order expiration type
   datetime                      time_expiration;  // Order expiration time
   double                        price;            // Price 
   double                        price_trigger;    // Price that triggers the Stop Limit order
   double                        price_sl;         // Level of Stop Loss
   double                        price_tp;         // Level of Take Profit
   double                        volume;           // Volume in lots
  };

结构的第四个字段即是我们寻找的枚举变量。

2. 仓位处理

实质上,所有交易操作的仓位处理,都会导致调用五次 OnTradeTransaction() 处理器。其中有:

修改仓位是仅有的调用两次 TradeTransaction 处理器的交易操作。

由于没有关于事务类型的信息来对应明确的交易操作,我们打算通过试验和排错来发现它。

在此之前, 我们要创建包含 TradeTransaction 事件处理器的 EA 模板。我为我的模板命名为 TradeProcessor.mq5。我添加了一个功能,可以在日志中显示结构字段值的信息。这些值是事件处理器的参数。分析这些记录将非常耗时,但最后它会通过呈现事件全貌来补偿。

我们需要在 MetaTrader 5 终端里,在任意图表中以调试模式启动 EA。

手工开仓并查看代码。第一次处理器调用将会如此 (图例. 1).

图例.1. 类型字段等于 TRADE_TRANSACTION_REQUEST

图例.1. 类型字段等于 TRADE_TRANSACTION_REQUEST

以下条目出现在日志中:

IO      0       17:37:53.233    TradeProcessor (EURUSD,H1)      ---===Transaction===---
NK      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Ticket of the deal: 0
RR      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Type of the deal: DEAL_TYPE_BUY
DE      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Ticket of the order: 0
JS      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Status of the order: ORDER_STATE_STARTED
JN      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Type of the order: ORDER_TYPE_BUY
FD      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Price: 0.0000
FN      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Level of Stop Loss: 0.0000
HF      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Level of Take Profit: 0.0000
FQ      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Price that triggers the Stop Limit order: 0.0000
RR      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Trade symbol: 
HD      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Pending order expiration time: 1970.01.01 00:00
GS      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Order expiration type: ORDER_TIME_GTC
DN      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Type of the trade transaction TRADE_TRANSACTION_REQUEST
FK      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Volume in lots: 0.00

在这程序块中,只有关注事务类型的记录,才是我们感兴趣的。正如我们所见, 该类型属于 (TRADE_TRANSACTION_REQUEST) 请求。

有关请求的详情,可以在 “Request”程序块中获得。

QG      0       17:37:53.233    TradeProcessor (EURUSD,H1)      ---===Request===--- HL      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Type of the trade operation: TRADE_ACTION_DEAL EE      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Comment to the order: JP      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Deviation from the requested price: 0 GS      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Order expiration time: 1970.01.01 00:00 LF      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Magic number of the EA: 0 FM      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Ticket of the order: 22535869 EJ      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Price: 1.3137 QR      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Stop Loss level of the order: 0.0000 IJ      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Take Profit level of the order: 0.0000 KK      0       17:37:53.233    TradeProcessor (EURUSD,H1)      StopLimit level of the order: 0.0000 FS      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Trade symbol: EURUSD RD      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Type of the order: ORDER_TYPE_BUY

请求执行的结果数据,可以从 "Response" 程序块中获得。

KG      0       17:37:53.233    TradeProcessor (EURUSD,H1)      ---===Response===---
JR      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Code of the operation result: 10009
GD      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Ticket of the deal: 15258202
NR      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Ticket of the order: 22535869
EF      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Volume of the deal: 0.11
MN      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Price of the deal: 1.3137
HJ      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Bid: 1.3135
PM      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Ask: 1.3137
OG      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Comment to the operation: 
RQ      0       17:37:53.233    TradeProcessor (EURUSD,H1)      Request ID: 1

分析处理器的其它参数,如请求和响应的结构,您可以得到有关在第一次请求调用时的附加信息。

注意第二次调用是将订单加入到已开订单列表中 (图例. 2)。

图例.2. 类型字段等于 TRADE_TRANSACTION_ORDER_ADD

图例.2. 类型字段等于 TRADE_TRANSACTION_ORDER_ADD

程序块 "Transaction" 是日志中我们唯一需要的东西。

MJ      0       17:41:12.280    TradeProcessor (EURUSD,H1)      ---===Transaction===---
JN      0       17:41:12.280    TradeProcessor (EURUSD,H1)      Ticket of the deal: 0
FG      0       17:41:12.280    TradeProcessor (EURUSD,H1)      Type of the deal: DEAL_TYPE_BUY
LM      0       17:41:12.280    TradeProcessor (EURUSD,H1)      Ticket of the order: 22535869
LI      0       17:41:12.280    TradeProcessor (EURUSD,H1)      Status of the order: ORDER_STATE_STARTED
LP      0       17:41:12.280    TradeProcessor (EURUSD,H1)      Type of the order: ORDER_TYPE_BUY
QN      0       17:41:12.280    TradeProcessor (EURUSD,H1)      Price: 1.3137
PD      0       17:41:12.280    TradeProcessor (EURUSD,H1)      Level of Stop Loss: 0.0000
NL      0       17:41:12.280    TradeProcessor (EURUSD,H1)      Level of Take Profit: 0.0000
PG      0       17:41:12.280    TradeProcessor (EURUSD,H1)      Price that triggers the Stop Limit order: 0.0000
DL      0       17:41:12.280    TradeProcessor (EURUSD,H1)      Trade symbol: EURUSD
JK      0       17:41:12.280    TradeProcessor (EURUSD,H1)      Pending order expiration time: 1970.01.01 00:00
QD      0       17:41:12.280    TradeProcessor (EURUSD,H1)      Order expiration type: ORDER_TIME_GTC
IQ      0       17:41:12.280    TradeProcessor (EURUSD,H1)      Type of the trade transaction: TRADE_TRANSACTION_ORDER_ADD
PL      0       17:41:12.280    TradeProcessor (EURUSD,H1)      Volume in lots: 0.11

在订单中,我们可以看到,已经收到了单号和其它参数(品名,价格和交易量),并包含在已完成订单的列表中。

第三次调用事件处理器,是将订单从已开订单列表中删除 (图例. 3)。

图例.3. 类型字段等于 TRADE_TRANSACTION_ORDER_DELETE

图例.3. 类型字段等于 TRADE_TRANSACTION_ORDER_DELETE

程序块 "Transaction" 是日志中我们唯一需要的东西。

PF      0       17:52:36.722    TradeProcessor (EURUSD,H1)      ---===Transaction===---
OE      0       17:52:36.722    TradeProcessor (EURUSD,H1)      Ticket of the deal: 0
KL      0       17:52:36.722    TradeProcessor (EURUSD,H1)      Type of the deal: DEAL_TYPE_BUY
EH      0       17:52:36.722    TradeProcessor (EURUSD,H1)      Ticket of the order: 22535869
QM      0       17:52:36.722    TradeProcessor (EURUSD,H1)      Status of the order: ORDER_STATE_STARTED
QK      0       17:52:36.722    TradeProcessor (EURUSD,H1)      Type of the order: ORDER_TYPE_BUY
HS      0       17:52:36.722    TradeProcessor (EURUSD,H1)      Price: 1.3137
MH      0       17:52:36.722    TradeProcessor (EURUSD,H1)      Level of Stop Loss: 0.0000
OP      0       17:52:36.722    TradeProcessor (EURUSD,H1)      Level of Take Profit: 0.0000
EJ      0       17:52:36.722    TradeProcessor (EURUSD,H1)      Price that triggers the Stop Limit order: 0.0000
IH      0       17:52:36.722    TradeProcessor (EURUSD,H1)      Trade symbol: EURUSD
KP      0       17:52:36.722    TradeProcessor (EURUSD,H1)      Pending order expiration time: 1970.01.01 00:00
LO      0       17:52:36.722    TradeProcessor (EURUSD,H1)      Order expiration type: ORDER_TIME_GTC
HG      0       17:52:36.722    TradeProcessor (EURUSD,H1)      Type of the trade transaction: TRADE_TRANSACTION_ORDER_DELETE
CG      0       17:52:36.722    TradeProcessor (EURUSD,H1)      Volume in lots: 0.11

这里除了事务类型,没有新信息。

第四次调用处理器,是当新的历史订单出现在历史数据里。 (图例. 4)。

图例.4. 类型字段等于 TRADE_TRANSACTION_HISTORY_ADD

图例.4. 类型字段等于 TRADE_TRANSACTION_HISTORY_ADD

我们可以从程序块 "Transaction" 得到相关信息。

QO      0       17:57:32.234    TradeProcessor (EURUSD,H1)      ---===Transaction==---
RJ      0       17:57:32.234    TradeProcessor (EURUSD,H1)      Ticket of the deal: 0
NS      0       17:57:32.234    TradeProcessor (EURUSD,H1)      Type of the deal: DEAL_TYPE_BUY
DQ      0       17:57:32.234    TradeProcessor (EURUSD,H1)      Ticket of the order: 22535869
EH      0       17:57:32.234    TradeProcessor (EURUSD,H1)      Status of the order: ORDER_STATE_FILLED
RL      0       17:57:32.234    TradeProcessor (EURUSD,H1)      Type of the order: ORDER_TYPE_BUY
KJ      0       17:57:32.234    TradeProcessor (EURUSD,H1)      Price: 1.3137
NO      0       17:57:32.234    TradeProcessor (EURUSD,H1)      Level of Stop Loss: 0.0000
PI      0       17:57:32.234    TradeProcessor (EURUSD,H1)      Level of Take Profit: 0.0000
FS      0       17:57:32.234    TradeProcessor (EURUSD,H1)      Price that triggers the Stop Limit order: 0.0000
JS      0       17:57:32.234    TradeProcessor (EURUSD,H1)      Trade symbol: EURUSD
LG      0       17:57:32.234    TradeProcessor (EURUSD,H1)      Pending order expiration time: 1970.01.01 00:00
KP      0       17:57:32.234    TradeProcessor (EURUSD,H1)      Order expiration type: ORDER_TIME_GTC
OL      0       17:57:32.234    TradeProcessor (EURUSD,H1)      Type of the trade transaction: TRADE_TRANSACTION_HISTORY_ADD
JH      0       17:57:32.234    TradeProcessor (EURUSD,H1)      Volume in lots: 0.00

在此阶段, 我们可以看到订单已经被执行。

最后,成交订单被添加到历史记录时,第五次调用发生 (图例. 5)。

图例.5. 类型字段等于 TRADE_TRANSACTION_DEAL_ADD

图例.5. 类型字段等于 TRADE_TRANSACTION_DEAL_ADD

在日志中, 我们同样只对程序块中的 "Transaction" 感兴趣。

OE      0       17:59:40.718    TradeProcessor (EURUSD,H1)      ---===Transaction===---
MS      0       17:59:40.718    TradeProcessor (EURUSD,H1)      Ticket of the deal: 15258202
RJ      0       17:59:40.718    TradeProcessor (EURUSD,H1)      Type of the deal: DEAL_TYPE_BUY
HN      0       17:59:40.718    TradeProcessor (EURUSD,H1)      Ticket of the order: 22535869
LK      0       17:59:40.718    TradeProcessor (EURUSD,H1)      Status of the order: ORDER_STATE_STARTED
LE      0       17:59:40.718    TradeProcessor (EURUSD,H1)      Type of the order: ORDER_TYPE_BUY
MM      0       17:59:40.718    TradeProcessor (EURUSD,H1)      Price: 1.3137
PF      0       17:59:40.718    TradeProcessor (EURUSD,H1)      Level of Stop Loss: 0.0000
NN      0       17:59:40.718    TradeProcessor (EURUSD,H1)      Level of Take Profit: 0.0000
PI      0       17:59:40.718    TradeProcessor (EURUSD,H1)      Price that triggers the Stop Limit order: 0.0000
DJ      0       17:59:40.718    TradeProcessor (EURUSD,H1)      Trade symbol: EURUSD
JM      0       17:59:40.718    TradeProcessor (EURUSD,H1)      Pending order expiration time: 1970.01.01 00:00
QI      0       17:59:40.718    TradeProcessor (EURUSD,H1)      Order expiration type: ORDER_TIME_GTC
CK      0       17:59:40.718    TradeProcessor (EURUSD,H1)      Type of the trade transaction: TRADE_TRANSACTION_DEAL_ADD
RQ      0       17:59:40.718    TradeProcessor (EURUSD,H1)      Volume in lots: 0.11

在这个程序块中,最重要的字符串是成交单号。

我将要提出的事务规划。对于仓位,它们只有两部分。第一部分看上去像图例. 6。

图例.6. 第一个事务流程规划

图例.6. 第一个事务流程规划

所有针对仓位处理的交易操作,都按照这个规划执行。此处仅有的例外就是修改仓位操作。最后的操作包括以下事务流程 (图例. 7)。

图例.7. 第二个事务流程规划

图例.7. 第二个事务流程规划

因此,修改仓位不能在成交和订单历史记录中被跟踪。

那里几乎是所有仓位的信息。

3. 限价订单处理

对于限价订单,应当注意的是,它们的操作只需较少事务。同时,订单处理时还有更多事务类型组合。

为修改订单,处理器被调用两次,类似于修改仓位。下单及删除订单则需要调用三次。该 TradeTransaction 事件在删除订单或执行时调用四次。

现在,我们将放置一个限价订单。我们需要在 MetaTrader 5 终端上,在任意图表中以调试模式启动 EA。

处理器的第一次调用将是请求连接 (图例. 8)。

图例.8. 类型字段等于 TRADE_TRANSACTION_REQUEST

图例.8. 类型字段等于 TRADE_TRANSACTION_REQUEST

日志中将包含以下条目:

IO      0       18:13:33.195    TradeProcessor (EURUSD,H1)      ---===Transaction===---
NK      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Ticket of the deal: 0
RR      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Type of the deal: DEAL_TYPE_BUY
DE      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Ticket of the order: 0
JS      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Status of the order: ORDER_STATE_STARTED
JN      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Type of the order: ORDER_TYPE_BUY
FD      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Price: 0.0000
FN      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Level of Stop Loss: 0.0000
HF      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Level of Take Profit: 0.0000
FQ      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Price that triggers the Stop Limit order: 0.0000
RR      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Trade symbol: 
HD      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Pending order expiration time: 1970.01.01 00:00
GS      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Order expiration type: ORDER_TIME_GTC
DN      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Type of the trade transaction: TRADE_TRANSACTION_REQUEST
FK      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Volume in lots: 0.00
NS      0       18:13:33.195    TradeProcessor (EURUSD,H1)      

QG      0       18:13:33.195    TradeProcessor (EURUSD,H1)      ---===Request==---
IQ      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Type of the trade operation: TRADE_ACTION_PENDING
OE      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Order comment: 
PQ      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Deviation from the requested price: 0
QS      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Order expiration time: 1970.01.01 00:00
FI      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Magic number of the EA: 0
CM      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Ticket of the order: 22535983
PK      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Price: 1.6500
KR      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Stop Loss level of the order: 0.0000
OI      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Take Profit level of the order: 0.0000
QK      0       18:13:33.195    TradeProcessor (EURUSD,H1)      StopLimit level of the order: 0.0000
QQ      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Trade symbol: GBPUSD
RD      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Type of the order: ORDER_TYPE_BUY_LIMIT
LS      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Order execution type: ORDER_FILLING_RETURN
MN      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Order expiration type: ORDER_TIME_GTC
IK      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Volume in lots: 0.14
NS      0       18:13:33.195    TradeProcessor (EURUSD,H1)      
CD      0       18:13:33.195    TradeProcessor (EURUSD,H1)      ---===Response===---
RQ      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Code of the operation result: 10009
JI      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Ticket of the deal: 0
GM      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Ticket of the order: 22535983
LF      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Volume of the deal: 0.14
JN      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Price of the deal: 0.0000
MK      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Bid: 0.0000
CM      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Ask: 0.0000
IG      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Comment to the operation: 
DQ      0       18:13:33.195    TradeProcessor (EURUSD,H1)      Request ID: 1

第二次调用处理器将把订单加入到已开订单列表中 (图例. 9)。

图例.9. 类型字段等于 TRADE_TRANSACTION_ORDER_ADDED

图例.9. 类型字段等于 TRADE_TRANSACTION_ORDER_ADDED

在日志中我们只需看 "Transaction" 程序块。

HJ      0       18:17:02.886    TradeProcessor (EURUSD,H1)      ---===Transaction===---
GQ      0       18:17:02.886    TradeProcessor (EURUSD,H1)      Ticket of the deal: 0
CH      0       18:17:02.886    TradeProcessor (EURUSD,H1)      Type of the deal: DEAL_TYPE_BUY
RL      0       18:17:02.886    TradeProcessor (EURUSD,H1)      Ticket of the order: 22535983
II      0       18:17:02.886    TradeProcessor (EURUSD,H1)      Status of the order: ORDER_STATE_STARTED
OG      0       18:17:02.886    TradeProcessor (EURUSD,H1)      Type of the order: ORDER_TYPE_BUY_LIMIT
GL      0       18:17:02.886    TradeProcessor (EURUSD,H1)      Price: 1.6500
IE      0       18:17:02.886    TradeProcessor (EURUSD,H1)      Level of Stop Loss: 0.0000
CO      0       18:17:02.886    TradeProcessor (EURUSD,H1)      Level of Take Profit: 0.0000
IF      0       18:17:02.886    TradeProcessor (EURUSD,H1)      Price that triggers the Stop Limit order: 0.0000
PL      0       18:17:02.886    TradeProcessor (EURUSD,H1)      Trade symbol: GBPUSD
OL      0       18:17:02.886    TradeProcessor (EURUSD,H1)      Pending order expiration time: 1970.01.01 00:00
HJ      0       18:17:02.886    TradeProcessor (EURUSD,H1)      Order expiration type: ORDER_TIME_GTC
LF      0       18:17:02.886    TradeProcessor (EURUSD,H1)      Type of the trade transaction: TRADE_TRANSACTION_ORDER_ADD
FR      0       18:17:02.886    TradeProcessor (EURUSD,H1)      Volume in lots: 0.14

第三次调用处理器将根据放置的订单刷新数据 (图例. 10)。

特别是,订单状态将接收到 ORDER_STATE_PLACED 的值。

图例.10. 类型字段等于 TRADE_TRANSACTION_ORDER_UPDATE

图例.10. 类型字段等于 TRADE_TRANSACTION_ORDER_UPDATE

在日志中, 我们只需看 "Transaction" 程序块中的记录。

HS      0       18:21:27.004    TradeProcessor (EURUSD,H1)      ---===Transaction==---
GF      0       18:21:27.004    TradeProcessor (EURUSD,H1)      Ticket of the deal: 0
CO      0       18:21:27.004    TradeProcessor (EURUSD,H1)      Type of the deal: DEAL_TYPE_BUY
RE      0       18:21:27.004    TradeProcessor (EURUSD,H1)      Ticket of the order: 22535983
KM      0       18:21:27.004    TradeProcessor (EURUSD,H1)      Status of the order: ORDER_STATE_PLACED
QH      0       18:21:27.004    TradeProcessor (EURUSD,H1)      Type of the order: ORDER_TYPE_BUY_LIMIT
EG      0       18:21:27.004    TradeProcessor (EURUSD,H1)      Price: 1.6500
GL      0       18:21:27.004    TradeProcessor (EURUSD,H1)      Level of Stop Loss: 0.0000
ED      0       18:21:27.004    TradeProcessor (EURUSD,H1)      Level of Take Profit: 0.0000
GO      0       18:21:27.004    TradeProcessor (EURUSD,H1)      Price that triggers the Stop Limit order: 0.0000
RE      0       18:21:27.004    TradeProcessor (EURUSD,H1)      Trade symbol: GBPUSD
QS      0       18:21:27.004    TradeProcessor (EURUSD,H1)      Pending order expiration time: 1970.01.01 00:00
JS      0       18:21:27.004    TradeProcessor (EURUSD,H1)      Order expiration type: ORDER_TIME_GTC
RD      0       18:21:27.004    TradeProcessor (EURUSD,H1)      Type of the trade transaction: TRADE_TRANSACTION_ORDER_UPDATE
JK      0       18:21:27.004    TradeProcessor (EURUSD,H1)      Volume in lots: 0.14

这里最重要的字符串是订单的状态。

不像仓位处理,限价订单处理不能由一个规划来实现。每次针对限价订单的操作,从事务类型的角度来看都是唯一的。

下限价订单将产生三次事务 (图例. 11)。

图例.11. 下限价订单事务流程

图例.11. 下限价订单事务流程

修改限价订单将产生两次事务 (图例. 12)。

图例.12 修改限价订单事务流程

图例.12. 修改限价订单事务流程

如果限价订单被删除, 则该 OnTradeTransaction() 处理器将被调用四次 (图例. 13)。

图例.13. 限价订单删除事务流程

图例.13. 限价订单删除事务流程

删除限价订单由以下规划定义 (图例. 14)。

图例.14. 限价订单删除事务流程

图例.14. 限价订单删除事务流程

触发限价订单,最后的交易操作将导致四种不同的事务 (图例. 15)。

图例.15. 限价订单激活事务流程

图例.15. 限价订单激活事务流程

我不打算把每一个事务组合的日志条目带到这里。如果读者觉得倾向这样做,他们可以通过执行代码进行研判。


4. 通用处理器

让我们通过最终用户的眼睛,来看看与 TradeTransaction 事件协同工作的程序。最终用户很可能需要一个程序,可以完美地与订单和仓位两者工作。程序员必须为 OnTradeTransaction() 编写代码,让它无论过程如何都可以识别所有事务和它们的组合 - 仓位或订单。理想情况下,该程序能够指出执行什么样的操作可以完成一系列事务流程。

在下面的例子中,一系列事务流程被使用。MQL5 的开发者通过以下声明:

...一个交易请求从终端手工发送或通过 OrderSend()/OrderSendAsync() 函数,可以在交易服务器上生成一些连续的事务。这些事务抵达终端的优先级是没有保证的,因此交易算法不应该基于假设:一组事务一个接一个的到来。除此之外,事务可能会在服务器到终端输送中丢失...

因此,如果需求是写一个接近理想工作的程序,您可以改善建议样本,制作事务过程使之独立于事务到达顺序。

在一般情况下,仓位和订单可以有常见事务类型。此处有 11 种事务类型。其中仅有四种需要在来自终端的交易中处理:

我们不打算在本文中讨论它们。这些类型,取决于开发者,可以设计用来扩展交易服务器端的功能。我必须承认,我以前还没有处理过这些类型。

这样留给我们七个功能齐全的类型,从 OnTradeTransaction() 中得到最常用的流程。

在处理器主体的这一段,定义了当前事务类型,具有极其重要的角色。

//--- ========== Types of transaction [START]
   switch(trans_type)
     {
      //--- 1) if it is a request
      case TRADE_TRANSACTION_REQUEST:
        {

         //---
         break;
        }
      //--- 2) if it is an addition of a new open order
      case TRADE_TRANSACTION_ORDER_ADD:
        {

         //---
         break;
        }
      //--- 3) if it is a deletion of an order from the list of open ones
      case TRADE_TRANSACTION_ORDER_DELETE:
        {

         //---     
         break;
        }
      //--- 4) if it is an addition of a new order to the history
      case TRADE_TRANSACTION_HISTORY_ADD:
        {

         //---     
         break;
        }
      //--- 5) if it is an addition of a deal to history
      case TRADE_TRANSACTION_DEAL_ADD:
        {

         //---
         break;
        }
      //--- 6) if it is a modification of a position 
      case TRADE_TRANSACTION_POSITION:
        {

         //---
         break;
        }
      //--- 7) if it is a modification of an open order
      case TRADE_TRANSACTION_ORDER_UPDATE:
        {

         //---
         break;
        }
     }
//--- ========== Types of transactions [END]

我们将尝试定义什么样的交易操作,可由当前的事务类型来处理。要发现我们正在做什么 - 仓位或订单, 我们将交易操作的类型分派给 case 模块来处理请求。

该模块看起来如下所示:

//--- 1) if it is a request
      case TRADE_TRANSACTION_REQUEST:
        {
         //---
         last_action=request.action;
         string action_str;

         //--- what is the request for?
         switch(last_action)
           {
            //--- а) on market
            case TRADE_ACTION_DEAL:
              {
               action_str="place a market order";
               trade_obj=TRADE_OBJ_POSITION;
               break;
              }
            //--- б) place a pending order
            case TRADE_ACTION_PENDING:
              {
               action_str="place a pending order";
               trade_obj=TRADE_OBJ_ORDER;
               break;
              }
            //--- в) modify position
            case TRADE_ACTION_SLTP:
              {
               trade_obj=TRADE_OBJ_POSITION;
               //---
               StringConcatenate(action_str,request.symbol,": modify the levels of Stop Loss",
                                 " and Take Profit");

               //---
               break;
              }
            //--- г) modify order
            case TRADE_ACTION_MODIFY:
              {
               action_str="modify parameters of the pending order";
               trade_obj=TRADE_OBJ_ORDER;
               break;
              }
            //--- д) delete order
            case TRADE_ACTION_REMOVE:
              {
               action_str="delete pending order";
               trade_obj=TRADE_OBJ_ORDER;
               break;
              }
           }
         //---
         if(InpIsLogging)
            Print("Request received: "+action_str);

         //---
         break;
        }

在这种情况下改变一些变量并不困难。

static ENUM_TRADE_REQUEST_ACTIONS last_action; // market operation at the first pass

该 last_action 变量将记忆为什么事件处理器被启动。

static ENUM_TRADE_OBJ trade_obj;               // specifies the trade object at the first pass

而变量 trade_obj 将保持记忆处理了什么 - 仓位或订单。为此,我们应创建 ENUM_TRADE_OBJ 枚举。

此后,我们将继续处理 TRADE_TRANSACTION_ORDER_ADD 事务类型的模块:

//--- 2) if it is an addition of a new open order
      case TRADE_TRANSACTION_ORDER_ADD:
        {
         if(InpIsLogging)
           {
            if(trade_obj==TRADE_OBJ_POSITION)
               Print("Open a new market order: "+
                     EnumToString(trans.order_type));
            //---
            else if(trade_obj==TRADE_OBJ_ORDER)
               Print("Place a new pending order: "+
                     EnumToString(trans.order_type));
           }
         //---
         break;
        }

这个模块是相当简单的。由于仓位在第一步处理,日志条目 "Open a new market order" 将出现在当前地方,否则为 "Place a new pending order"。在这个区块中,没有其它更多动作的信息。

现在轮到第三个模块,处理 TRADE_TRANSACTION_ORDER_DELETE 类型:

//--- 3) if it is a deletion of an order from the list of open ones
      case TRADE_TRANSACTION_ORDER_DELETE:
        {
         if(InpIsLogging)
            PrintFormat("Order deleted from the list of open ones: #%d, "+
                        EnumToString(trans.order_type),trans.order);
         //---     
         break;
        }

该模块也仅具有一个角色。

第四个 case-模块处理 TRADE_TRANSACTION_HISTORY_ADD 类型:

//--- 4) if it is an addition of a new order to the history
      case TRADE_TRANSACTION_HISTORY_ADD:
        {
         if(InpIsLogging)
            PrintFormat("Order added to the history: #%d, "+
                        EnumToString(trans.order_type),trans.order);

         //--- if a pending order is being processed
         if(trade_obj==TRADE_OBJ_ORDER)
           {
            //--- if it is the third pass
            if(gTransCnt==2)
              {
               //--- if the order was canceled, check the deals
               datetime now=TimeCurrent();

               //--- request the history of orders and deals
               HistorySelect(now-PeriodSeconds(PERIOD_H1),now);

               //--- attempt to find a deal for the order
               CDealInfo myDealInfo;
               int all_deals=HistoryDealsTotal();
               //---
               bool is_found=false;
               for(int deal_idx=all_deals;deal_idx>=0;deal_idx--)
                  if(myDealInfo.SelectByIndex(deal_idx))
                     if(myDealInfo.Order()==trans.order)
                        is_found=true;

               //--- if the deal was not found
               if(!is_found)
                 {
                  is_to_reset_cnt=true;
                  //---
                  PrintFormat("Order canceled: #%d",trans.order);
                 }
              }
            //--- if it is the fourth pass
            if(gTransCnt==3)
              {
               is_to_reset_cnt=true;
               PrintFormat("Order deleted: #%d",trans.order);
              }
           }
         //---     
         break;
        }

此外,该订单被添加到历史记录,这个模块将进行检查,是否我们处理的是限价订单。在我们完成的情况下,我们需要找出当前哪些数字要传递到处理器。若限价订单被取消,事务类型将会在第三次传递时出现。当限价订单被删除时,该类型将会在第四次传递时出现。

模块在第三次传递时检查字符串,我们需要再次参考成交历史。如果订单未发现成交,我们认为此订单已被取消。

第五个 case-模块处理 TRADE_TRANSACTION_DEAL_ADD 类型。按照字符串尺寸计算,这是程序的最大模块。

在此块中检查成交。重要的是按照单号选择成交来获取它的属性。如果仓位已开或已平,则可以提供成交类型。限价订单的触发信息也可在此获取。有一种情况,在 TradeTransaction 事件处理器工作时,限价订单成交。

//--- 5) if it is an addition of a deal to history
      case TRADE_TRANSACTION_DEAL_ADD:
        {
         is_to_reset_cnt=true;
         //---
         ulong deal_ticket=trans.deal;
         ENUM_DEAL_TYPE deal_type=trans.deal_type;
         //---
         if(InpIsLogging)
            PrintFormat("Deal added to history: #%d, "+EnumToString(deal_type),deal_ticket);

         if(deal_ticket>0)
           {
            datetime now=TimeCurrent();

            //--- request the history of orders and deals
            HistorySelect(now-PeriodSeconds(PERIOD_H1),now);

            //--- select a deal by the ticket
            if(HistoryDealSelect(deal_ticket))
              {
               //--- check the deal
               CDealInfo myDealInfo;
               myDealInfo.Ticket(deal_ticket);
               long order=myDealInfo.Order();

               //--- parameters of the deal
               ENUM_DEAL_ENTRY  deal_entry=myDealInfo.Entry();
               double deal_vol=0.;
               //---
               if(myDealInfo.InfoDouble(DEAL_VOLUME,deal_vol))
                  if(myDealInfo.InfoString(DEAL_SYMBOL,deal_symbol))
                    {
                     //--- position
                     CPositionInfo myPos;
                     double pos_vol=WRONG_VALUE;
                     //---
                     if(myPos.Select(deal_symbol))
                        pos_vol=myPos.Volume();

                     //--- if the market was entered
                     if(deal_entry==DEAL_ENTRY_IN)
                       {
                        //--- 1) opening of a position
                        if(deal_vol==pos_vol)
                           PrintFormat("\n%s: new position opened",deal_symbol);

                        //--- 2) addition to the open position        
                        else if(deal_vol<pos_vol)
                           PrintFormat("\n%s: addition to the current position",deal_symbol);
                       }

                     //--- if the market was exited
                     else if(deal_entry==DEAL_ENTRY_OUT)
                       {
                        if(deal_vol>0.0)
                          {
                           //--- 1) closure of a position
                           if(pos_vol==WRONG_VALUE)
                              PrintFormat("\n%s: position closed",deal_symbol);

                           //--- 2) partial closure of the open position        
                           else if(pos_vol>0.0)
                              PrintFormat("\n%s: partial closing of the current position",deal_symbol);
                          }
                       }

                     //--- if position was reversed
                     else if(deal_entry==DEAL_ENTRY_INOUT)
                       {
                        if(deal_vol>0.0)
                           if(pos_vol>0.0)
                              PrintFormat("\n%s: position reversal",deal_symbol);
                       }
                    }

               //--- order activation
               if(trade_obj==TRADE_OBJ_ORDER)
                  PrintFormat("Pending order activation: %d",order);
              }
           }

         //---
         break;
        }

事务类型 TRADE_TRANSACTION_POSITION 是唯一的,并仅在仓位被修改时处理:

//--- 6) if it is a modification of a position
      case TRADE_TRANSACTION_POSITION:
        {
         is_to_reset_cnt=true;
         //---
         PrintFormat("Modification of a position: %s",deal_symbol);
         //---
         if(InpIsLogging)
           {
            PrintFormat("New price of stop loss: %0."+
                        IntegerToString(_Digits)+"f",trans.price_sl);
            PrintFormat("New price of take profit: %0."+
                        IntegerToString(_Digits)+"f",trans.price_tp);
           }

         //---
         break;
        }

最后的 case-模块启用处理 TRADE_TRANSACTION_ORDER_UPDATE 类型。

这个类型只出现在与限价订单工作时。它在任意交易操作触发时启动,关注限价订单,尽管状态可能会变化。

//--- 7) if it is a modification of an open order
      case TRADE_TRANSACTION_ORDER_UPDATE:
        {

         //--- if it was the first pass
         if(gTransCnt==0)
           {
            trade_obj=TRADE_OBJ_ORDER;
            PrintFormat("Canceling the order: #%d",trans.order);
           }
         //--- if it was the second pass
         if(gTransCnt==1)
           {
            //--- if it is an order modification
            if(last_action==TRADE_ACTION_MODIFY)
              {
               PrintFormat("Pending order modified: #%d",trans.order);
               //--- clear counter
               is_to_reset_cnt=true;
              }
            //--- if it is deletion of the order
            if(last_action==TRADE_ACTION_REMOVE)
              {
               PrintFormat("Delete pending order: #%d",trans.order);

              }
           }
         //--- if it was the third pass
         if(gTransCnt==2)
           {
            PrintFormat("A new pending order was placed: #%d, "+
                        EnumToString(trans.order_type),trans.order);
            //--- clear counter
            is_to_reset_cnt=true;
           }

         //---
         break;
        }

总结,如果在第一次触发 OnTradeTransaction() 时此类型出现,则订单既可能被取消,或被执行。

如果在第二次启动事件处理器时此类型出现,则订单既可能被删除,或被修改。为了找出订单的确凿结果,参考包含最后交易操作的静态变量 last_action。

第三次启动事件处理器是此类型出现的最后情况。第三次启动完成下限价订单的过程。

一个布尔变量 is_to_reset_cnt 也在代码中被使用。它作为标志用于清除 OnTradeTransaction() 处理器的传递计数。

这几乎是全部的有关 TradeTransaction 事件流程。我还在调用处理器的开头,添加了一个暂停。在被移至历史数据前,它为成交或订单提供了少许延迟机会。


结论

在本文中,我尝试描绘有哪些不同的交易操作可以使用,以及如何能够检索有关在终端里发生的事件信息。

这种方法的最大好处是,程序可以接收有关交易操作的分阶段实施信息。在我看来,这种方式可用于复制一个终端的交易到另一个终端。