错误146 ("交易作业忙") 和如何处理

Andrey Khatimlianskii | 26 二月, 2008


1.MetaTrader 4客户端中 "交易运作" 的概念

来自MetaEditor参考文档的一段话:

来自智能交易和脚本的交易,只能够提供一个并在交易作业中开启 (来自智能交易和脚本的自动交易作业)。这就是为什么如果智能交易业务作业忙,其他的智能交易或脚本在此刻不能调用函数生成错误146 (ERR_TRADE_CONTEXT_BUSY)。

换句话说,只有一个智能交易(脚本)可以准时交易。所以其他智能交易尝试开始交易将被错误 146停止。文章将会找到问题的解决方案。



2. 函数 IsTradeAllowed()

这是最简单的方法是使用名称为IsTradeAllowed()的函数找出交易作业忙。

来自 MetaEditor参考文档的一段话:

"bool IsTradeAllowed()

如果智能交易允许交易,返回TRUE 。否则,返回FALSE。

这就意味着如果函数 IsTradeAllowed()返回TRUE只有一个可以尝试交易。

在交易业务之前必须检测完成。

函数错误使用范例:

int start()
  {
    // 检测交易作业
    if(!IsTradeAllowed())
      {
        // 如果函数IsTradeAllowed() 返回FALSE, 通知用户
        Print("交易作业忙。智能交易不能开仓!");
        // 并且中止交易业务。当下一个替克进入将重新开始 
        // 进入
        return(-1);
      }
    else
      {
        // 如果函数IsTradeAllowed()返回TRUE,通知用户 
        // 并继续运行
        Print("交易作业空闲!开始运作...");
      }
    // 现在检测市场
    ...
    // 计算止损和赢利水平和标准手
    ...
    // 开仓
    if(OrderSend(...) < 0) 
        Alert("开仓错误 # ", GetLastError());
    return(0);
  }
在这个范例中,在start() 函数开始时检测交易运作状态。这是个错误想法: 在我们的智能交易计算的时间内交易作业会被其他智能交易占据(需要进入市场,止损和赢利,标准手等等)。这些情况,接受开仓将失败。


函数适当应用范例:

int start()
  {
    // 现在检测市场
    ...
    // 计算止损和赢利水平,标准手数
 ...
    // 现在检测交易作业
    if(!IsTradeAllowed())
      {
        Print("交易作业忙! 智能交易不能开仓!");
        return(-1);
      }
    else
        Print("交易作业正常! 尝试开仓...");
    //如果检测正常,开仓
    if(OrderSend(...) < 0) 
        Alert("错误开仓 # ", GetLastError());
    return(0);
  }
在开仓之前立即检测交易作业状态,并且可能在两种动作行为之间插入其他的智能交易 .

这种方法存在两点不足:

解决第二种不足很简单:等待交易作业空闲即可.随后,在其他智能交易结束后,智能交易将立即开始交易.

范例如下:

int start()
  {
    // 现在检测是否进入市场
    ...
    // 计算赢利/止损水平和标准手数
    ...
    // 检测交易作业是否空闲
    if(!IsTradeAllowed())
      {
        Print("交易作业忙!等待空闲...");
        // 无限循环
        while(true)
          {
            // 如果智能交易被用户停止,停止业务
            if(IsStopped()) 
              { 
                Print("智能交易被用停止!"); 
                return(-1); 
              }
            // 如果交易作业空闲,开始交易
            if(IsTradeAllowed())
              {
                Print("交易作业空闲!");
                break;
              }
            // 如果没有条件设置循环, "等待" 0.1秒
            // 并且检测重新开始
            Sleep(100);
          }
      }
    else
        Print("交易作业空闲!尝试开仓...");
    // 尝试开仓
    if(OrderSend(...) < 0) 
        Alert("错误开仓 # ", GetLastError());
    return(0);
  }
这种情况出现,我们可以指出以下错误:

纠正的错误代码将会是以下:

// 智能交易等待交易的时间time (in seconds) whithin which the expert will wait until the trade 
// 交易作业空闲(如果忙)
int MaxWaiting_sec = 30;
int start()
  {
    // 现在检验是否进入市场
    ...
    // 计算止损,赢利和标准手数
    ...
    // 检测交易作业是否空闲
    if(!IsTradeAllowed())
      {
        int StartWaitingTime = GetTickCount();
        Print("交易作业忙!等待空闲...");
        // 无限循环
        while(true)
          {
            // 如过用户中止智能交易,停止业务
            if(IsStopped()) 
              { 
                Print("智能交易被用户中止!"); 
                return(-1); 
               }
            // 如果等待时间超过命名变量 
            // MaxWaiting_sec, 停止业务
            if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000) 
              {
                Print("标准限定(" + MaxWaiting_sec + " sec)超过!");
                return(-2);
              }
            // 如果交易作业空闲,
            if(IsTradeAllowed())
              {
                Print("交易作业空闲!");
                // 刷新市场信息
                RefreshRates();
                // 重新计算止损和赢利水平
                ...
                // 离开循环状态并开始交易             
                break;
              }
            // 如过没有条件离开,请 "等待"  0.1秒 
            // 并且重新开始检测
            Sleep(100);
          }
      }
    else
        Print("交易作业空闲!尝试开仓...");

    // 尝试开仓
    if(OrderSend(...) < 0) 
        Alert("错误开仓 # ", GetLastError());
 
    return(0);
  }
对于上面的范例,我们还可以添加:

以上的这些代码你已经可以使用到你的智能交易中。

现在让我们来谈谈在单独函数中的检测。这将简化在智能交易中的简化和用法。

/////////////////////////////////////////////////////////////////////////////////
// int _IsTradeAllowed( int MaxWaiting_sec = 30 )
//
// 函数检测交易作业状态. R返回代码:
//  1 - 交易作业空闲, 允许交易
//  0 - 交易作业忙,但是将空闲。刷新市场信息后允许交易。
// -1 - 交易作业忙,用户中止等待(智能交易从图表中删除,终端删除,图表周期/货币对改变等等)
// -2 - 交易作业忙,达到最大等待限度 (MaxWaiting_sec). 
//      智能交易禁止交易(检测 "Allow live trading" ).
//
// MaxWaiting_sec - 函数将等待的时间 
// 直到交易作业控点(如果交易作业忙).默认值,30.
/////////////////////////////////////////////////////////////////////////////////
int _IsTradeAllowed(int MaxWaiting_sec = 30)
  {
    // 检测交易作业是否空闲
    if(!IsTradeAllowed())
      {
        int StartWaitingTime = GetTickCount();
        Print("交易作业忙!等待空闲...");
        // 无限循环
        while(true)
          {
            // 如果智能交易被用户中止,停止业务
            if(IsStopped()) 
              { 
                Print("智能交易被用户中止!"); 
                return(-1); 
              }
            // 如果等待时间超出指定
            // MaxWaiting_sec 变量 ,停止业务
            if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000)
              {
                Print("等待限度超过 (" + MaxWaiting_sec + " сек.)!");
                return(-2);
              }
            // 如果交易作业空闲
            if(IsTradeAllowed())
              {
                Print("交易作业空闲!");
                return(0);
              }
            // 如果没有条件离开循环状态,请"等待" 0.1秒 
            // 并且重新开始检测           Sleep(100);
          }
      }
    else
      {
        Print("交易作业空闲!");
        return(1);
      }
  }


对于智能交易使用函数的一个模板:

int start()
  {
    // 现在检测是否进入市场
    ...
    // 计算止损,赢利水平和标准手数
    ...
    // 检测交易作业是否空闲
    int TradeAllow = _IsTradeAllowed();
    if(TradeAllow < 0) 
      { 
        return(-1); 
      }
    if(TradeAllow == 0)
      {
        RefreshRates();
        // 重新计算赢利水平和止损水平
        ...
      }
    // 开仓
    if(OrderSend(...) < 0) 
        Alert("错误开仓 # ", GetLastError());
    return(0);
  }

由此我们得出以下结论:



函数 IsTradeAllowed()很方便使用,并且对于两到三个智能交易同时运行同样适用。虽然存在一些不足,当很多智能交易同时运行时会出现错误 146。如果"Allow live trading" 禁止,同样可能出现智能交易"hanging" 。

这就是为什么我们考虑解决方案-整体变量作为一个 "信号旗"的原因。


3.客户终端的整体变量

首先是概念:

客户终端的整体变量是所有智能交易,脚本和指标的变量通道。这就意味着整体变量可以由一个智能交易创建,其他的智能交易同样可以使用 。

在 MQL4中提供了以下函数的整体变量:

为什么使用 GlobalVariableSetOnCondition(),而不是联合函数 GlobalVariableGet()和 GlobalVariableSet()呢? 同样的原因:两个函数之前可能重合。这样其他的智能交易则不能插入。这就不使用的原因。


4. 信号旗的基本理念

智能交易准备交易应该检测信号旗的状态。如果信号旗显示 "红色" (整体变量 = 1),意味着其他智能交易在运行中,这样需要等待。如果显示“绿色” (整体变量 = 0),交易可以立即开始 (但不要忘记对其他智能交易设定"红色")。

由此,我们创建了2个函数:一个设定"红色",另一个设定“绿色”。事实上,他们类似。我们尝试地制定了函数的次序(函数TradeIsBusy() 和函数TradeIsNotBusy()) 并且赋予实践。


5. 函数TradeIsBusy()

像我们前面所讲的,这个函数的主要功能是使智能交易等待直至“绿色”显现,并且将其切换成“红色”。另外,我们需要检验是否存在整体变量,并且进行创建。如果不存在。这个检测会从智能交易的函数init() 中进行逻辑性地执行。 但是随后可能被用户删除并且不会有智能交易进行运行。这就是我们将它放置到创建函数中的原因。

所有的这些整体变量的运行都需要伴随信息的展示和错误的生成。 应该记住"hanging":函数的业务时间需要限定。

这就是我们最终得到的:

/////////////////////////////////////////////////////////////////////////////////
// int TradeIsBusy( int MaxWaiting_sec = 30 )
//
// 函数还原TradeIsBusy 值0 - 1.
// 在开启时如果TradeIsBusy = 1 ,函数等待直至 TradeIsBusy 为 0, 
// 随后还原
// 如果TradeIsBusy没有任何整体变量,函数将会自己创建。
// 返回代码:
//  1 - 成功编译。TradeIsBusy整体变量值指定为 1
// -1 - TradeIsBusy = 1 函数在此刻开启,等待用户中止
//      (智能交易从图表中移除,终端被停止,图表周期/货币对被改变等等)
// -2 - TradeIsBusy = 1 函数在此刻开启,等待限定超时
//      (MaxWaiting_sec)
/////////////////////////////////////////////////////////////////////////////////
int TradeIsBusy( int MaxWaiting_sec = 30 )
  {
    // 测试时,没有理由划分交易作业 - 只是终止 
    // 此函数
    if(IsTesting()) 
        return(1);
    int _GetLastError = 0, StartWaitingTime = GetTickCount();
    //+------------------------------------------------------------------+
    //| 检测整体变量是否存在,如果没有,创建整体变量                           |
    //+------------------------------------------------------------------+
    while(true)
      {
        // 如果智能交易被用户中止,停止业务
        if(IsStopped()) 
          { 
            Print("智能交易被用户中止!"); 
            return(-1); 
          }
        // 如果等待时间超过指定限定时间 
        // MaxWaiting_sec, 停止业务
        if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000)
          {
            Print("等待时间(" + MaxWaiting_sec + " sec)超出!");
            return(-2);
          }
        // 检测整体变量是否存在
        // 如果不存在离开循环模式,进行改变
        // TradeIsBusy值
        if(GlobalVariableCheck( "TradeIsBusy" )) 
            break;
        else
        // 如果GlobalVariableCheck 返回FALSE, 意味着整体变量不存在或者  
        // 检测中生成错误
          {
            _GetLastError = GetLastError();
            // 如果仍然有错误信息显示,等待0.1 秒, 
            //重新开始检测
            if(_GetLastError != 0)
             {
              Print("TradeIsBusy()-GlobalVariableCheck(\"TradeIsBusy\")-Error #",
                    _GetLastError );
              Sleep(100);
              continue;
             }
          }
        // 如果没有错误,意味着没有整体变量,尝试创建
        // 整体变量
        //如果 the GlobalVariableSet > 0, 说明整体变量成功创建。 
        // 离开函数
        if(GlobalVariableSet( "TradeIsBusy", 1.0 ) > 0 ) 
            return(1);
        else
        // 如果GlobalVariableSet返回值<= 0, 说明有错误
        // 在变量创建时生成
         {
          _GetLastError = GetLastError();
          //显示信息,等待0.1秒,再次尝试
          if(_GetLastError != 0)
            {
              Print("TradeIsBusy()-GlobalVariableSet(\"TradeIsBusy\",0.0 )-Error #",
                    _GetLastError );
              Sleep(100);
              continue;
            }
         }
      }
    //+----------------------------------------------------------------------------------+
    //| 如果函数达到执行点,说明整体变量                                                      | 
    //| 变量退出.                                                                         |
    //| 等待TradeIsBusy 值成为0 并且改变 TradeIsBusy 值为 1                                 |
    //+----------------------------------------------------------------------------------+
    while(true)
     {
     // 如果智能交易被用户中止,停止业务
     if(IsStopped()) 
       { 
         Print("智能交易被用户中止!"); 
         return(-1); 
       }
     // 如果等待超过限定时间
     // MaxWaiting_sec, 停止业务
     if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000)
       {
         Print("等待时间 (" + MaxWaiting_sec + " sec) 超出!");
         return(-2);
       }
     // 尝试改变 TradeIsBusy的值从 0 - 1
     // 如果成功,离开函数返回1 ("成功编译")
     if(GlobalVariableSetOnCondition( "TradeIsBusy", 1.0, 0.0 )) 
         return(1);
     else
     // 如果失败,可能导致失败的2个原因: TradeIsBusy = 1 (随后等待), 

     // 错误生成  (需要我们检测)
      {
      _GetLastError = GetLastError();
      // 如果仍然存在错误,显示信息并且重新尝试
      if(_GetLastError != 0)
      {
   Print("TradeIsBusy()-GlobalVariableSetOnCondition(\"TradeIsBusy\",1.0,0.0 )-Error #",
         _GetLastError );
       continue;
      }
     }
     //如果没有错误,说明TradeIsBusy = 1 (其他智能交易在运行), 
     // 随后显示信息并且等待...
     Comment("等待其他智能交易交易完成...");
     Sleep(1000);
     Comment("");
    }
  }
在这里可以清楚地看到:

函数可以运行 MaxWaiting_sec,并且不从图表中删除任何商品。
错误信息生成你可以在日志中找到。


6. 函数TradeIsNotBusy()

函数TradeIsNotBusy 解决返回的问题:开启"绿色"。

这项功能没有限定并且不能由用户中止。方式非常简单:如果 "绿色" 关闭,智能交易将不会进行交易。

不会返回任何代码:结果只能被成功编译。

参见下面示例:

/////////////////////////////////////////////////////////////////////////////////
// void TradeIsNotBusy()
//
// 函数设置整体变量 TradeIsBusy 值等于0.
// 如果TradeIsBusy不存在,函数创建。
/////////////////////////////////////////////////////////////////////////////////
void TradeIsNotBusy()
  {
    int _GetLastError;
    // 测试时,交易作业不被划分 - 只是终止 
    // 此函数
    if(IsTesting()) 
      { 
        return(0); 
      }
    while(true)
      {
        // 如果智能交易被用户中止,停止业务
        if(IsStopped()) 
          { 
            Print("智能交易被用户中止!"); 
            return(-1); 
          }
        // 尝试设置整变量值= 0 (创建整体变量)
        // 如果 GlobalVariableSet 返回值 > 0, 说明成功
        // 离开函数
        if(GlobalVariableSet( "TradeIsBusy", 0.0 ) > 0) 
            return(1);
        else
        // 如果GlobalVariableSet 返回值 <= 0, 说明错误生成
        // 显示信息,等待并且尝试重新开始
         {
         _GetLastError = GetLastError();
         if(_GetLastError != 0 )
           Print("TradeIsNotBusy()-GlobalVariableSet(\"TradeIsBusy\",0.0)-Error #", 
                 _GetLastError );
         }
        Sleep(100);
      }
  }


7. 在智能交易中的结合使用

现在我们有 3 个函数可以通向交易作业。使他们简单地结合到智能交易中,我们可以创建一个 TradeContext.mq4 文件并且使用 #include (获取文件)。

这是一个使用函数 TradeIsBusy()和函数TradeIsNotBusy()的模板:

#include <TradeContext.mq4>
 
int start()
  {
    // 现在检测是否进入市场
    ...
    // 计算止损水平,赢利水平和标准手数
    ...
    // 等待交易作业空闲并且进入(如果生成错误, 
    // 离开)
    if(TradeIsBusy() < 0) 
        return(-1); 
    // 刷新市场信息
    RefreshRates();
    // 重新计算止损和赢利水平
    ...
    // 开仓
    if(OrderSend(...) < 0) 
      { 
        Alert("错误开仓位置 # ", GetLastError()); 
      }

    //设置交易作业空闲
    TradeIsNotBusy();

    return(0);
  }

在使用函数 TradeIsBusy()和函数 TradeIsNotBusy()时,只有一个问题能够产生: 如果在交易作业变成忙后,智能交易从图表中移除,变量 TradeIsBusy将会等于 1。其他的智能交易将不可能运行。

这个问题可以很轻松地解决: 在智能交易在图表中交易时,不从图表中移除;)

在终端停歇时, TradeIsBusy值也有可能不等于0。这种情况,函数 TradeIsNotBusy()从智能交易的函数 init() 被使用。

当然,在任何时间内可以手动改变变量值: 终端内的F3键 。不建议进行使用。


komposter (komposterius@mail.ru), 2006.04.11