MQL5编程踩坑实录 新评论 Yin Zhou Luo 2026.04.01 17:53 今天说说OrderSend(m_request,m_result)的事。这不是MQL4,但事却多。 当你使用它来处理挂单时,正常的示例代码如下: #define EXPERT_MAGIC 123456 // EA交易的幻数 input ENUM_ORDER_TYPE orderType=ORDER_TYPE_BUY_LIMIT; // 订单类型 //+------------------------------------------------------------------+ //| 下挂单 | //+------------------------------------------------------------------+ void OnStart() { //--- 声明并初始化交易请求和交易请求结果 MqlTradeRequest request={}; MqlTradeResult result={}; //--- 下挂单的参数 request.action =TRADE_ACTION_PENDING; // 交易操作类型 request.symbol =Symbol(); // 交易品种 request.volume =0.1; //0.1手交易量 request.deviation=2; // 允许价格偏差 request.magic =EXPERT_MAGIC; // 订单幻数 int offset = 50; // 以点数从当前价抵消下单 double price; // 订单触动价 double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); // value of point int digits=SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // 小数位数 (精确度) //--- 检查操作类型 if(orderType==ORDER_TYPE_BUY_LIMIT) { request.type =ORDER_TYPE_BUY_LIMIT; // 订单类型 price=SymbolInfoDouble(Symbol(),SYMBOL_ASK)-offset*point; // 持仓价格 request.price =NormalizeDouble(price,digits); //正常开盘价 } else if(orderType==ORDER_TYPE_SELL_LIMIT) { request.type =ORDER_TYPE_SELL_LIMIT; // order type price=SymbolInfoDouble(Symbol(),SYMBOL_ASK)+offset*point; // 持仓价 request.price =NormalizeDouble(price,digits); // 正常开盘价 } else if(orderType==ORDER_TYPE_BUY_STOP) { request.type =ORDER_TYPE_BUY_STOP; // 订单类型 price =SymbolInfoDouble(Symbol(),SYMBOL_ASK)+offset*point; // 开盘价 request.price=NormalizeDouble(price,digits); // 正常开盘价 } else if(orderType==ORDER_TYPE_SELL_STOP) { request.type =ORDER_TYPE_SELL_STOP; // 订单类型 price=SymbolInfoDouble(Symbol(),SYMBOL_ASK)-offset*point; // 开盘价 request.price =NormalizeDouble(price,digits); // 正常开盘价 } else Alert("This example is only for placing pending orders"); // 如果没有挂单被选择 //--- 发送请求 if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // 如果不能发送请求,输出错误代码 //--- 操作信息 PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); } //+------------------------------------------------------------------+ 示例中,挂单间隔是50点。在正常或较慢的行情中,完全正常运行。但当行情波动较快时,会频繁出现#4756 的失败提示,同时retcode=10015,从帮助文档里得知,这是价格无效。 问题来了,明明设置了50点的间隔距离,也标准化了小数位数,为什么会无效呢?其实还是50点小了,执行OrderSend()时,当服务器收到你的发送请求时,行情当时的价格已经跑了一段,这个时候的价格已经不适合接收到的订单类型的订单了。 以BUY_STOP为例,假设XAUUSD现价5000.00,你以5000.50报价,可服务器接收到你的请求时,行情价格已经是5000.60了,自然,你5000.50的突破多单便成了无效价格。 解决办法:酌情放弃小间隔50点,对于活跃的品种,须适当放大些点值;若不愿放大,只想在50点上挂,就得坦然看待 专家/日志中的4756失败提示。 上述代码中还存在一个BUG。按前述举例,价格上到了5000.60,实际当价格行情为5000.40时,STOP_BUY也许就会4756了。为什么?不同的品种都有2个可能“冻结”的阙值常量如下: SYMBOL_TRADE_STOPS_LEVEL 止蚀盘当前收盘价格的最小空间 SYMBOL_TRADE_FREEZE_LEVEL 凝结交易操作的距离 当你的价格位于它以行情实时价为基础的此常量点值的范围时,你的报价也将是4756无效的。所以,上述参考文档中的示例代码可以简单认为属于“伪代码”……哈哈,严格说,是不严谨的。 基于此,我们在计算price时,还得核对一下price不应处于其“冻结”范围内。如下: …… int stoplevel = (int)SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL); int freezelevel = (int)SymbolInfoInteger(Symbol(), SYMBOL_TRADE_FREEZE_LEVEL); int step=10; //--- 检查操作类型 ......//limit 单类似如下处理 ...... if(orderType==ORDER_TYPE_BUY_STOP) { request.type =ORDER_TYPE_BUY_STOP; // 订单类型 double ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK); //取得当前价 if( price>ask+(stoplevel+step)*mpoint && price>ask+(freezelevel+step)*mpoint) //判断报价不在其“冻结”范围内,同时预留10个点空间 price =ask+offset*point; // 开盘价 else { Print("当前价格过于接近现价."); return false; } request.price=NormalizeDouble(price,digits); // 正常开盘价 } else ....//sell stop 单类似 //--- 发送请求 if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // 如果不能发送请求,输出错误代码 //--- 操作信息 PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); } 以上,是正常发送挂单请求的处理。发送时增加检查是否处于“冻结”内,这仅仅是多一道处理环节,以尽量少收到4756通知,不代表就完全避免接“电话”, 因为,MT5的异步机制,它不以你发送时的校对为准,而是以它收到你的请求时,它取当时的实时价(如ask)来判断你的请求是否有效的。 因此我们能做的尽量做到即可,至于会不会收到4756“电话”通知,就只有祈祷了…… 新评论 您错过了交易机会: 免费交易应用程序 8,000+信号可供复制 探索金融市场的经济新闻 注册 登录 拉丁字符(不带空格) 密码将被发送至该邮箱 发生错误 使用 Google 登录 您同意网站政策和使用条款 如果您没有帐号,请注册 可以使用cookies登录MQL5.com网站。 请在您的浏览器中启用必要的设置,否则您将无法登录。 忘记您的登录名/密码? 使用 Google 登录
今天说说OrderSend(m_request,m_result)的事。这不是MQL4,但事却多。
当你使用它来处理挂单时,正常的示例代码如下:
示例中,挂单间隔是50点。在正常或较慢的行情中,完全正常运行。但当行情波动较快时,会频繁出现#4756 的失败提示,同时retcode=10015,从帮助文档里得知,这是价格无效。
问题来了,明明设置了50点的间隔距离,也标准化了小数位数,为什么会无效呢?其实还是50点小了,执行OrderSend()时,当服务器收到你的发送请求时,行情当时的价格已经跑了一段,这个时候的价格已经不适合接收到的订单类型的订单了。
以BUY_STOP为例,假设XAUUSD现价5000.00,你以5000.50报价,可服务器接收到你的请求时,行情价格已经是5000.60了,自然,你5000.50的突破多单便成了无效价格。
解决办法:酌情放弃小间隔50点,对于活跃的品种,须适当放大些点值;若不愿放大,只想在50点上挂,就得坦然看待 专家/日志中的4756失败提示。
上述代码中还存在一个BUG。按前述举例,价格上到了5000.60,实际当价格行情为5000.40时,STOP_BUY也许就会4756了。为什么?不同的品种都有2个可能“冻结”的阙值常量如下:
SYMBOL_TRADE_STOPS_LEVEL
止蚀盘当前收盘价格的最小空间
SYMBOL_TRADE_FREEZE_LEVEL
凝结交易操作的距离
当你的价格位于它以行情实时价为基础的此常量点值的范围时,你的报价也将是4756无效的。所以,上述参考文档中的示例代码可以简单认为属于“伪代码”……哈哈,严格说,是不严谨的。
基于此,我们在计算price时,还得核对一下price不应处于其“冻结”范围内。如下:
以上,是正常发送挂单请求的处理。发送时增加检查是否处于“冻结”内,这仅仅是多一道处理环节,以尽量少收到4756通知,不代表就完全避免接“电话”,
因为,MT5的异步机制,它不以你发送时的校对为准,而是以它收到你的请求时,它取当时的实时价(如ask)来判断你的请求是否有效的。
因此我们能做的尽量做到即可,至于会不会收到4756“电话”通知,就只有祈祷了……