EA开发系列--交易以及交易相关的回调函数

411 0 2023-11-01

# 概要 EA在绝大多数场景下被用作自动化交易,因此,交易也就是EA的核心内容,这一节,介绍如何交易以及与交易相关的回调函数。这里包括两部分内容: 1、交易订单处理:挂单,现价单,止盈,止损等设定 2、OnTradeTransaction函数 3、历史订单处理

交易订单处理

在EA中,所有的订单处理都统一到OrderSend函数中完成,包括:现价,限价,止盈/止损设置等动作。它的原型如下:

bool  OrderSend( 
   MqlTradeRequest&  request,      // 订单请求   MqlTradeResult&   result        // 处理结果);

与MQL4不同,OrderSend在MQL5中所有的订单参数都需要你构造一个MqlTradeRequest结构,具体处理结果可以在MqlTradeResult结构中查询(由系统填写),是否成功则通过返回的bool(布尔)类型来表示。 注意:每一次交易都涉及金额,因此下单后,我建议大家严格判断下单动作是否成功。

下面来解释一下MqlTradeRequest这个结构体,他的原型如下:

struct MqlTradeRequest
  {
   ENUM_TRADE_REQUEST_ACTIONS    action;           // 交易操作类型   ulong                         magic;            // EA交易魔数字,多用于标识订单   ulong                         order;            // 订单号   string                        symbol;           // 交易的交易品种   double                        volume;           // 交易数量   double                        price;            // 价格   double                        stoplimit;        // 订单止损限价   double                        sl;               // 订单止损价位   double                        tp;               // 订单盈利价位   ulong                         deviation;        // 需求价格最可能的偏差,也就是滑点   ENUM_ORDER_TYPE               type;             // 订单类型   ENUM_ORDER_TYPE_FILLING       type_filling;     // 订单执行类型   ENUM_ORDER_TYPE_TIME          type_time;        // 订单执行时间   datetime                      expiration;       // 订单终止期   string                        comment;          // 订单注释   ulong                         position;         // 持仓编号   ulong                         position_by;      // 反向持仓编号  };

这个下单请求的结构体中包含了很多的内容,在我的工作范围内,有很多字段都是非必要不必填写的。 以下分享两个例子,它展示如何挂一个做多的单子和如何现价开仓:

//挂单做多bool openBuyLimitOrder(string input_symbol, ulong input_magic, double input_shares, 
                        double order_price, double sl_price, double tp_price) {
        MqlTradeRequest request = {};//初始化下单请求结构体,由代码填写        MqlTradeResult  result  = {};//初始化请求返回结构体,由系统填写后返回
        request.action    = TRADE_ACTION_PENDING;//挂单        request.type      = ORDER_TYPE_BUY_LIMIT;//挂单做多, 限价做空用ORDER_TYPE_SELL_LIMIT        request.expiration = ORDER_TIME_GTC;
        request.symbol    = input_symbol;//品种        request.volume    = input_shares;//数量        request.magic     = input_magic;//标识,魔数        request.deviation = 5;//支持的滑点        request.price     = order_price;//挂单价格        if(sl_price > 0) {
            request.sl = sl_price;//设置止损价格        }

        if(tp_price > 0) {
            request.tp = tp_price;//设置止盈价格        }

        //发送请求        bool is_success = OrderSend(request, result);
        if(is_success == false) {
            PrintFormat("openBuyLimitOrder::OrderSend error %d", GetLastError());
            PrintFormat("openBuyLimitOrder::retcode=%u  deal=%I64u  order=%I64u", 
                    result.retcode, result.deal, result.order);
        }
        return is_success;
    }//现价做多    bool openBuyOrder(string input_symbol, ulong input_magic, 
                    double input_shares, double sl_price, double tp_price) {
        MqlTradeRequest request = {};
        MqlTradeResult  result  = {};

        request.action    = TRADE_ACTION_DEAL;//实时成交        request.type      = ORDER_TYPE_BUY;//做多,做空用ORDER_TYPE_SELL        request.symbol    = input_symbol;
        request.volume    = input_shares;
        request.magic     = input_magic;
        request.deviation = 5;
        //做多用ASK(卖出价)入场,做空用BID(买入价)入场        request.price     = SymbolInfoDouble(input_symbol, SYMBOL_ASK);
        if(sl_price > 0) {
            request.sl = sl_price;
        }

        if(tp_price > 0) {
            request.tp = tp_price;
        }

        bool is_success = OrderSend(request, result);
        if(is_success == false) {
            PrintFormat("openBuyOrder::OrderSend error %d", GetLastError());
            PrintFormat("openBuyOrder::retcode=%u  deal=%I64u  order=%I64u", result.retcode, result.deal, result.order);
        }
        return is_success;
    }

上面两个函数展示了4种比较常见的交易场景,其中魔数等字段是可以不填写的。它们的区别也就体现在“action”和“type”两个字段的不同。

OrderSend执行以后,你就会通过其布尔返回值得到下单是否成功,但是无论是否成功MqlTradeResult结构体中都会被填充数据,它展示了执行的细节。那么MqlTradeResult结构体包含的内容有:

struct MqlTradeResult
  {
   uint     retcode;          // 操作返回代码   ulong    deal;             // 交易订单号,如果完成的话   ulong    order;            // 订单号,如果下订单的话   double   volume;           // 交易交易量,经纪人确认的   double   price;            // 交易的真实价格   double   bid;              // 当前买入价   double   ask;              // 当前卖出价   string   comment;          // 注释 (默认填充操作描述)   uint     request_id;       // 分派期间通过程序端设置Request ID 
   uint     retcode_external; // 返回外部交易系统代码  };

在这个结构体里你可以找到本次交易中涉及的返回结果细节信息。其中“request_id”是由MT5交易系统分配的。“retcode”标识当前操作的结果码,这个结果码由MT5内部定义。

交易订单处理流程回调函数(OnTradeTransaction)

当你执行了“OrderSend”函数以后,你的请求就会交由MT5处理,MT5出了的过程会通过这个函数通知EA来处理。如果你关心这个中间过程,你就需要在EA中实现这个函数。它的原型如下:

void  OnTradeTransaction( 
   const MqlTradeTransaction&    trans,        // 交易动作结构 
   const MqlTradeRequest&        request,      // 交易请求结构 
   const MqlTradeResult&         result        // 请求结果结构 
   );

以下是一个交易流程的日志:

OnTradeTransaction::Begin=======================Transaction:[TRADE_TRANSACTION_ORDER_ADD, Symbol: EURCHF, Deal ticket: 0, Deal type: DEAL_TYPE_BUY, Order ticket: 1100221132, Order type: ORDER_TYPE_BUY_LIMIT, Order state: ORDER_STATE_STARTED, Order time type: ORDER_TIME_GTC, Order expiration: 1970.01.01 00:00, Price: 1.07145, Price trigger: 0, Stop Loss: 1.06994, Take Profit: 1.07557, Volume: 0.3]Request:[ENUM_TRADE_REQUEST_ACTIONS::0, Symbol: , Magic Number: 0, Order ticket: 0, Order type: ORDER_TYPE_BUY, Order filling: ORDER_FILLING_FOK, Order time type: ORDER_TIME_GTC, Order expiration: 1970.01.01 00:00, Price: 0, Deviation points: 0, Stop Loss: 0, Take Profit: 0, Stop Limit: 0, Volume: 0, Comment: ]Result:[Retcode 0, Request ID: 0, Order ticket: 0, Deal ticket: 0, Volume: 0, Price: 0, Ask: 0, Bid: 0, Comment: ]OnTradeTransaction::End=========================OnTradeTransaction::Begin=======================Transaction:[TRADE_TRANSACTION_ORDER_UPDATE, Symbol: EURCHF, Deal ticket: 0, Deal type: DEAL_TYPE_BUY, Order ticket: 1100221132, Order type: ORDER_TYPE_BUY_LIMIT, Order state: ORDER_STATE_PLACED, Order time type: ORDER_TIME_GTC, Order expiration: 1970.01.01 00:00, Price: 1.07145, Price trigger: 0, Stop Loss: 1.06994, Take Profit: 1.07557, Volume: 0.3]Request:[ENUM_TRADE_REQUEST_ACTIONS::0, Symbol: , Magic Number: 0, Order ticket: 0, Order type: ORDER_TYPE_BUY, Order filling: ORDER_FILLING_FOK, Order time type: ORDER_TIME_GTC, Order expiration: 1970.01.01 00:00, Price: 0, Deviation points: 0, Stop Loss: 0, Take Profit: 0, Stop Limit: 0, Volume: 0, Comment: ]Result:[Retcode 0, Request ID: 0, Order ticket: 0, Deal ticket: 0, Volume: 0, Price: 0, Ask: 0, Bid: 0, Comment: ]OnTradeTransaction::End=========================Transaction:[TRADE_TRANSACTION_REQUEST, Symbol: , Deal ticket: 0, Deal type: DEAL_TYPE_BUY, Order ticket: 0, Order type: ORDER_TYPE_BUY, Order state: ORDER_STATE_STARTED, Order time type: ORDER_TIME_GTC, Order expiration: 1970.01.01 00:00, Price: 0, Price trigger: 0, Stop Loss: 0, Take Profit: 0, Volume: 0]Request:[TRADE_ACTION_PENDING, Symbol: EURCHF, Magic Number: 20210801, Order ticket: 1100221132, Order type: ORDER_TYPE_BUY_LIMIT, Order filling: ORDER_FILLING_RETURN, Order time type: ORDER_TIME_GTC, Order expiration: 1970.01.01 00:00, Price: 1.07145, Deviation points: 5, Stop Loss: 1.06994, Take Profit: 1.07557, Stop Limit: 0, Volume: 0.3, Comment: ]Result:[Retcode 10009, Request ID: 1, Order ticket: 1100221132, Deal ticket: 0, Volume: 0.3, Price: 0, Ask: 0, Bid: 0, Comment: ]OnTradeTransaction::End=========================

由此可见,“OrderSend”以后,发生了三个步骤,每一个步骤包含了三个内容,分别是:交易信息,交易请求,请求结果。这三个步骤分别是: 1、新增新的持仓订单(TRADE_TRANSACTION_ORDER_ADD) 2、更新订单状态(TRADE_TRANSACTION_ORDER_ADD) 3、收到交易服务器的结果(TRADE_TRANSACTION_REQUEST)