保持敬畏之心
交易是一场持久战

​MQL4(25):封装平仓/删除挂单函数

封装平仓函数

最后,我们将订单平仓的逻辑也封装成一个可重用的函数。这个函数将专注于关闭单个指定的市价订单。当您需要精确地关闭某一个特定的订单时(例如,基于订单号)这时下面这个函数就很有用,同时加入了必要的检查和错误处理。

关闭指定买单 (CloseBuyOrder)

/**
 * @brief 关闭一个指定的市价买单 (Buy Market Order)。
 * @param argSymbol         订单的交易品种名称 (用于获取当前价格)
 * @param argCloseTicket    要关闭的买单的订单号 (Ticket)
 * @param argSlippagePoints 允许的滑点 (单位: points)
 * @return bool             true: 平仓指令成功发送; false: 操作失败或订单无需/无法关闭
 */
bool CloseBuyOrder(string argSymbol, int argCloseTicket, int argSlippagePoints)
{
    bool closedSuccess = false; // 初始化函数返回值,默认为失败

    // 1. 尝试选中指定的订单
    if (OrderSelect(argCloseTicket, SELECT_BY_TICKET))
    {
        // 2. 确认订单是【未平仓】的【市价买单】
        if (OrderCloseTime() == 0 && OrderType() == OP_BUY)
        {
            // 3. 等待交易环境空闲 (假设 WaitForTradeContext 是封装好的函数)
            if (!WaitForTradeContext()) {
                Print("等待交易环境超时,无法关闭订单 #", argCloseTicket);
                return(false); // 等待失败,直接返回 false
            }

            // 4. 准备平仓所需参数
            double lotsToClose = OrderLots(); // 获取订单的全部手数进行平仓
            RefreshRates(); // 获取最新的市场报价
            // !!! 关键修正:平掉买单必须使用当前的【买价 Bid】 !!!
            double closePrice = MarketInfo(argSymbol, MODE_BID); // 使用 MarketInfo 获取指定品种的 Bid 价

            // 5. 发送平仓指令
            closedSuccess = OrderClose(argCloseTicket, lotsToClose, closePrice, argSlippagePoints, Red); // 使用红色箭头标记

            // 6. 处理平仓指令的发送结果
            if (!closedSuccess)
            {
                // 调用统一的错误处理函数记录错误详情
                HandleTradeError(StringFormat("关闭买单 #%d", argCloseTicket), GetLastError());
                /*
                // 或者直接在此处理:
                int errorCode = GetLastError();
                string errorDesc = ErrorDescription(errorCode); // 需要 #include <stdlib.mqh>
                Alert("关闭买单失败: Error ", errorCode, ": ", errorDesc);
                Print("OrderClose(Buy) failed for #", argCloseTicket, ". Error: ", errorCode, " ", errorDesc,
                      ". Bid=", closePrice); // 记录使用的 Bid 价
                */
            }
            else
            {
                Print("成功发送平仓指令 for 买单 #", argCloseTicket);
                // 注意:这里仅表示指令发送成功,不代表已最终成交。
            }
        }
        // else Print("订单 #", argCloseTicket, " 不是一个未平仓的买单."); // 可选的调试信息
    }
    // else Print("无法选中订单 #", argCloseTicket, ", Error: ", GetLastError()); // 可选的调试信息

    // 返回平仓指令是否成功发送
    return(closedSuccess);
}
  • 函数逻辑:

    • 函数接收品种名称 argSymbol、要关闭的订单号 argCloseTicket 和滑点 argSlippagePoints (points 单位) 作为参数。
    • 首先调用 OrderSelect() 选中订单。
    • 然后检查 OrderCloseTime() == 0 确认订单未关闭,并检查 OrderType() == OP_BUY 确认是买单。
    • 关键修正: 在获取平仓价格时,必须使用当前的 Bid 价(通过 MarketInfo(argSymbol, MODE_BID) 获取)来平掉买单。
    • 调用 OrderClose() 发送平仓指令。
    • OrderClose() 的返回值进行判断,如果发送失败 (false),则执行错误处理逻辑(建议调用封装的 HandleTradeError 函数),记录错误信息,并返回 false
    • 如果指令发送成功 (true),则函数返回 true
    • 如果订单一开始就无法选中,或者不是未平仓的买单,函数也会返回初始化的 false 值。
  • 关闭卖单 (CloseSellOrder): 关闭卖单的函数 (CloseSellOrder) 逻辑与 CloseBuyOrder 完全相同,唯一的区别在于:

    • 需要检查订单类型为 OrderType() == OP_SELL
    • 在获取平仓价格时必须使用当前的 Ask 价 (MarketInfo(argSymbol, MODE_ASK))。

通过将这些常用的交易操作封装成函数,可以极大地提高代码的复用性和整洁度,使得主程序能更专注于交易策略的核心逻辑。

关闭挂单函数 (ClosePendingOrder)

除了关闭市价单,我们也需要函数来处理挂单的取消。由于挂单是删除而非平仓,且不涉及价格和滑点,其函数相对简单。这个函数设计为可以关闭(删除)任何类型的未成交挂单(买/卖,止损/限价)。

/**
 * @brief 关闭 (删除) 一个指定的、尚未成交的挂单。
 * @param argSymbol         订单的交易品种名称 (主要用于错误日志记录当前价格)
 * @param argCloseTicket    要删除的挂单的订单号 (Ticket)
 * @return bool             true: 删除指令成功发送; false: 操作失败或订单无需/无法删除
 */
bool ClosePendingOrder(string argSymbol, int argCloseTicket)
{
    bool deletedSuccess = false; // 初始化返回值

    // 1. 尝试选中指定的订单
    if (OrderSelect(argCloseTicket, SELECT_BY_TICKET))
    {
        // 2. 确认订单是【未成交】的【挂单】
        int orderType = OrderType();
        if (OrderCloseTime() == 0 && (orderType >= OP_BUYSTOP && orderType <= OP_SELLLIMIT))
        {
            // 3. 等待交易环境 (假设 WaitForTradeContext 是封装好的函数) if (!WaitForTradeContext()) return(false); // 4. 发送删除指令 deletedSuccess = OrderDelete(argCloseTicket, Red); // 红色箭头标记 // 5. 处理删除指令的发送结果 if (!deletedSuccess) { // 调用统一的错误处理函数 (假设 HandleTradeError 已定义) HandleTradeError(StringFormat("删除挂单 #%d", argCloseTicket), GetLastError()); /* // 或者直接处理: int errorCode = GetLastError(); string errorDesc = ErrorDescription(errorCode); // 需要 #include <stdlib.mqh> Alert("删除挂单失败: Error ", errorCode, ": ", errorDesc); RefreshRates(); Print("OrderDelete failed for #", argCloseTicket, ". Error: ", errorCode, " ", errorDesc, ". Bid=", MarketInfo(argSymbol, MODE_BID), " Ask=", MarketInfo(argSymbol, MODE_ASK)); */ } else { Print("成功发送删除指令 for 挂单 #", argCloseTicket); } } // else { Print("订单 #", argCloseTicket, " 不是未成交挂单."); } // 可选调试 } // else { Print("无法选中订单 #", argCloseTicket, ", Error: ", GetLastError()); } // 可选调试 // 返回删除指令是否成功发送 return(deletedSuccess); } 
  • 参数: 函数接收品种名称 argSymbol(主要用于错误日志)和要删除的挂单号 argCloseTicket。注意,它不需要滑点参数。(译者注:原文此处函数签名误包含 argSlippage,已在翻译中移除)。
  • 逻辑:
    1. 选中订单。
    2. 检查 OrderCloseTime() == 0OrderType() 是挂单类型之一。
    3. 等待交易环境
    4. 调用 OrderDelete() 发送删除指令。
    5. 进行错误处理。
    6. 返回 OrderDelete() 的执行结果 (truefalse)。

最近几章会把封装函数的教学放在一起,最后再以早先的简单EA为基础,把这些自定义函数运用到EA当中,一个完善的EA需要不断的添砖加瓦才能稳定的运行。

赞(0)
未经允许不得转载:图道交易 » ​MQL4(25):封装平仓/删除挂单函数
分享到