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

MQL4(49):错误处理 - 订单重试机制

我们始终强调在尝试发出交易指令前,预先检验订单的各项参数。这样做是为了尽可能避免因参数设置错误或价格不当而引发的常见错误提示。然而,即便如此,实际交易中仍可能遭遇重新报价、交易环境繁忙或服务器通讯故障等问题。这些问题并非总能完全规避,但当它们发生时,我们可以设计程序尝试重新提交订单。

要在出错时自动重试订单,核心思路是将 OrderSend() 函数置于一个 while 循环结构之内。如果 OrderSend() 函数未能成功返回一个有效的订单编号,程序将自动再次尝试发送该订单:

int Ticket = 0; // 初始化订单编号变量
while(Ticket <= 0) // 当订单尚未成功发送时 (Ticket 小于等于0)
{
    Ticket = OrderSend(Symbol(), OP_BUY, LotSize, OpenPrice, UseSlippage,
                       BuyStopLoss, BuyTakeProfit); // 尝试发送买入订单
}

首先,我们声明一个用于存储订单编号的变量,此处命名为 Ticket。只要 Ticket 的值没有大于0(即订单未成功创建),包含 OrderSend() 函数的 while 循环便会周而复始地执行。然而,这种简单的循环存在一个隐患:一旦代码逻辑有误,或者发生了某些无法自动纠正的交易错误,循环可能会无限进行下去,导致您的EA陷入瘫痪状态(俗称“死循环”)。为了规避此风险,我们可以引入一个最大重试次数的限制:

int Retries = 0;        // 初始化重试次数计数器
int MaxRetries = 5;     // 设定最大允许重试次数
int Ticket = 0;         // 初始化订单编号变量

while(Ticket <= 0)      // 当订单尚未成功发送时
{
    Ticket = OrderSend(Symbol(), OP_BUY, LotSize, OpenPrice, UseSlippage, BuyStopLoss,
                       BuyTakeProfit); // 尝试发送订单
    if(Retries < MaxRetries) // 如果未达到最大重试次数
    {
        Retries++;          // 重试次数加1
    }
    else
    {
        break;              // 若已达最大次数,则跳出循环
    }
}

在这里,我们额外声明了两个变量:Retries 作为当前重试次数的计数器,以及 MaxRetries 作为允许的最大重试上限。只要尚未超出 MaxRetries 设定的次数,Retries 变量就会在每次循环后递增,然后程序继续尝试下单。一旦 Retries 达到了 MaxRetries 的值,break 语句便会强制终止循环。循环结束后,您可以根据实际需求,向用户发出错误警报。

如果您希望重试机制能根据特定的错误类型来决定是否执行,我们可以检查返回的错误代码。通过将错误代码与一个预设的列表进行比对,若发现匹配,则返回 true 值表示可以重试。下面的函数 ErrorCheck() 就包含了若干常见的、通常意味着可以尝试重新交易的错误代码:

bool ErrorCheck(int ErrorCode) // 检查特定错误代码是否允许重试
{
    switch(ErrorCode) // 根据传入的错误代码进行判断
    {
        case 128: // 交易超时 (Trade timeout)
            return(true);
        case 136: // 无效报价/离线报价 (Off quotes)
            return(true);
        /* 129 EXPERT ADVISOR PROGRAMMING (专家顾问系统编程) */
        case 138: // 重新报价 (Requotes)
            return(true);
        case 146: // 交易繁忙 (Trade context busy)
            return(true);
        default:  // 其他所有未列出的错误代码
            return(false);
    }
}

这个 ErrorCheck() 函数运用了 switch 语句。switch 语句会查找与其括号内表达式(本例中为 ErrorCode)值相匹配的 case 标签。一旦找到匹配的 case,其后的代码块便会得到执行。如果没有任何 case 标签与表达式的值相符,那么 default 标签后的代码块将被执行。

值得注意的是,当一个 case 条件满足后,必须通过 break 语句或 return 语句来显式退出 switch 代码块。在本示例中,我们使用了 return 语句,它不仅会退出 switch,还会将一个布尔值(truefalse)返回给调用 ErrorCheck() 函数的地方。switch 语句在需要根据一个整数常量的值进行多路分支判断时非常有用,但其适用场景相对较为局限。

以下演示了如何在下单逻辑中结合 ErrorCheck() 函数,实现有条件的订单重试:

int Retries = 0;        // 重试次数计数器,应在循环外部初始化,此处原文代码有误,已修正
int MaxRetries = 5;     // 最大重试次数
int Ticket = 0;         // 订单编号

while(Ticket <= 0)      // 当订单尚未成功发送时
{
    Ticket = OrderSend(Symbol(), OP_BUY, LotSize, OpenPrice, UseSlippage, BuyStopLoss,
                       BuyTakeProfit); // 尝试发送订单

    if(Ticket == -1) // 如果OrderSend返回-1,表示发生错误
    {
        int ErrCode = GetLastError(); // 获取具体的错误代码
        // 只有当错误允许重试且未达最大次数时,才增加重试计数并继续循环
        if(Retries < MaxRetries && ErrorCheck(ErrCode) == true)
        {
            Retries++;
        }
        else
        {
            break; // 否则,跳出循环
        }
    }
    // 如果Ticket > 0,表示订单成功,循环会自动结束
    // 如果Ticket == 0 (或其他非-1的错误情况,虽然不常见于OrderSend的典型失败模式)
    // 且不满足重试条件,也应跳出或有其他处理,原逻辑在此处可能形成死循环,
    // 此处根据上下文意图,主要处理 Ticket == -1 的情况。
    // 为确保安全,如果不是可重试错误,也应跳出:
    else if (Ticket == 0) { // 假设Ticket为0也是一种失败且不可直接重试的状态
        break;
    }
}

如果 OrderSend() 函数返回 -1,这标志着下单过程中出现了错误。此时,我们调用 GetLastError() 函数来获取具体的错误代码。随后,我们将这个错误代码传递给先前定义的 ErrorCheck() 函数。倘若该错误代码与 ErrorCheck() 函数内所列举的任一可重试错误相匹配,ErrorCheck() 便会返回 true,进而触发 OrderSend() 函数再次尝试下单,最多尝试5次。

赞(0)
未经允许不得转载:图道交易 » MQL4(49):错误处理 - 订单重试机制
分享到