轻松快捷开发 MetaTrader 程序的函数库(第 二十三部分):基准交易类 - 基准类,有效参数验证·进阶篇
🔧

轻松快捷开发 MetaTrader 程序的函数库(第 二十三部分):基准交易类 - 基准类,有效参数验证·进阶篇

(2/3)· 止损小于 StopLevel、价格触碰 FreezeLevel 还在发单?本篇讲清基准类如何提前拦截

案例拆解 第 2/3 篇
不少 EA 把停止单直接丢给服务器,结果因小于 StopLevel 被拒,白白消耗请求额度。冻结级别 FreezeLevel 内的改单尝试也一样,终端根本不会告诉你早该拦下。在发送前做参数有效性验证,是降低无效负载的最直接一步。

「挂单改价与报错的声音分支」

在 MT5 的 EA 里给不同挂单类型配提示音,常见做法是按订单动作走 switch 分支。下面这段把止盈修改和价格修改拆成两个独立函数,分别覆盖 BuyStop / BuyLimit / BuyStopLimit / SellStop / SellLimit / SellStopLimit 六种类型。 以 PlaySoundModifyPrice 为例,每个 case 先调 UseSoundModifyPrice(action) 判断该类型是否启用声音,再取对应结构体的 SoundModifyPrice() 返回文件名交给 CMessage::PlaySound 播放。若未启用则直接 break,不占资源。 这种写法在回测里几乎零开销,但在实盘高频改价时可能在 1 秒内触发多次 PlaySound,建议把 UseSoundModifyPrice 内部做成带时间戳的节流,否则黄金跳空时段容易被提示音刷屏。外汇与贵金属杠杆高,声音提醒只是辅助,不能替代风控。

MQL5 / C++
class="type">void CTradeObj::PlaySoundModifyPrice(class="kw">const class="type">int action)
  {
   class="kw">switch(action)
     {
      case ORDER_TYPE_BUY_STOP         :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.BuyStop.SoundModifyPrice());          class="kw">break;
      case ORDER_TYPE_BUY_LIMIT        :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.BuyLimit.SoundModifyPrice());        class="kw">break;
      case ORDER_TYPE_BUY_STOP_LIMIT   :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundModifyPrice());   class="kw">break;
      case ORDER_TYPE_SELL_STOP        :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.SellStop.SoundModifyPrice());        class="kw">break;
      case ORDER_TYPE_SELL_LIMIT       :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.SellLimit.SoundModifyPrice());       class="kw">break;
      case ORDER_TYPE_SELL_STOP_LIMIT  :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.SellStopLimit.SoundModifyPrice());  class="kw">break;
      class="kw">default: class="kw">break;
     }
  }

开仓报错音效按订单类型分流

交易对象里处理下单失败提示时,会把市价买、买停、买限、买停限、市价卖、卖停、卖限、卖停限八类订单分开走音效分支。每段 case 都先判断 UseSoundOpen(action) 开关,再调用 CMessage::PlaySound 播放在 m_datas 里对应类型预存的 SoundErrorOpen 音频,避免所有报错共用一个声音导致分不清是哪类单子没挂上。 下面这段是开仓报错音效分发的核心写法,挂在 CTradeObj 的方法内: case ORDER_TYPE_BUY : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.Buy.SoundErrorOpen()); break; case ORDER_TYPE_BUY_STOP : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.BuyStop.SoundErrorOpen()); break; case ORDER_TYPE_BUY_LIMIT : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.BuyLimit.SoundErrorOpen()); break; case ORDER_TYPE_BUY_STOP_LIMIT : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundErrorOpen()); break; case ORDER_TYPE_SELL : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.Sell.SoundErrorOpen()); break; case ORDER_TYPE_SELL_STOP : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.SellStop.SoundErrorOpen()); break; case ORDER_TYPE_SELL_LIMIT : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.SellLimit.SoundErrorOpen()); break; case ORDER_TYPE_SELL_STOP_LIMIT : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.SellStopLimit.SoundErrorOpen()); break; default: break; 平仓或删单失败的提示音走另一个方法 PlaySoundErrorClose,结构完全对称,只是把 UseSoundOpen 换成 UseSoundClose、SoundErrorOpen 换成 SoundErrorClose。外汇和贵金属市场跳空频繁,挂单被拒的概率不低,给不同订单类型配独立报错音,能让你在盯盘时靠耳朵就识别出是买限还是卖停没成。 开 MT5 在自定义 CTradeObj 里补一段上面代码,把 SoundErrorOpen 各字段填上 wav 路径,跑一晚模拟盘看能不能听音辨单。

MQL5 / C++
case ORDER_TYPE_BUY               :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.Buy.SoundErrorOpen());             class="kw">break;
case ORDER_TYPE_BUY_STOP           :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.BuyStop.SoundErrorOpen());         class="kw">break;
case ORDER_TYPE_BUY_LIMIT          :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.BuyLimit.SoundErrorOpen());        class="kw">break;
case ORDER_TYPE_BUY_STOP_LIMIT    :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundErrorOpen());    class="kw">break;
case ORDER_TYPE_SELL              :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.Sell.SoundErrorOpen());            class="kw">break;
case ORDER_TYPE_SELL_STOP          :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.SellStop.SoundErrorOpen());        class="kw">break;
case ORDER_TYPE_SELL_LIMIT         :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.SellLimit.SoundErrorOpen());       class="kw">break;
case ORDER_TYPE_SELL_STOP_LIMIT   :   if(this.UseSoundOpen(action))   CMessage::PlaySound(this.m_datas.SellStopLimit.SoundErrorOpen());   class="kw">break;
class="kw">default: class="kw">break;

◍ 平仓与改SL报错音的分类型触发

在 MT5 的 EA 架构里,把「平仓失败」和「止损修改失败」的报错提示做成声音反馈,最干净的做法是按订单类型走 switch 分支,而不是写一堆 if-else 堆在 main 里。下面这段逻辑覆盖了买、卖、止损单、限价单以及止盈止损限价单共 6 种平仓类型,每种都先判断 UseSoundClose(action) 开关再播对应的 SoundErrorClose 音频。 实际跑起来你会注意到:若某一类型在 m_datas 里没配置音频路径,SoundErrorClose() 返回空串时 PlaySound 不报错但也没声,这点在回测里不容易发现,得在实盘模拟环境用 EURUSD 的 0.01 手去故意触发一次拒绝平仓才能验证。外汇与贵金属杠杆高,这类报错音只是辅助,不能替代对成交回执的硬判断。 紧接着的 PlaySoundErrorModifySL 函数结构完全一致,只是把动作切到改 SL 失败,并且调用 UseSoundModifySL 与各自的 SoundErrorModifySL。把它俩放同一个 CTradeObj 类里,意味着你改一处音频目录逻辑,六类订单的两种报错音会同时生效。

MQL5 / C++
case ORDER_TYPE_BUY_LIMIT          :  if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.BuyLimit.SoundErrorClose());          class="kw">break;
case ORDER_TYPE_BUY_STOP_LIMIT   :  if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundErrorClose());    class="kw">break;
case ORDER_TYPE_SELL             :  if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.Sell.SoundErrorClose());                class="kw">break;
case ORDER_TYPE_SELL_STOP        :  if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.SellStop.SoundErrorClose());            class="kw">break;
case ORDER_TYPE_SELL_LIMIT       :  if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.SellLimit.SoundErrorClose());           class="kw">break;
case ORDER_TYPE_SELL_STOP_LIMIT  :  if(this.UseSoundClose(action))  CMessage::PlaySound(this.m_datas.SellStopLimit.SoundErrorClose());      class="kw">break;
class="kw">default: class="kw">break;
}
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Play StopLoss modification error sound of                          |
class=class="str">"cmt">//| a specified position/order type                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CTradeObj::PlaySoundErrorModifySL(class="kw">const class="type">int action)
  {
   class="kw">switch(action)
     {
      case ORDER_TYPE_BUY             :  if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.Buy.SoundErrorModifySL());             class="kw">break;
      case ORDER_TYPE_BUY_STOP        :  if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.BuyStop.SoundErrorModifySL());         class="kw">break;
      case ORDER_TYPE_BUY_LIMIT       :  if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.BuyLimit.SoundErrorModifySL());        class="kw">break;
      case ORDER_TYPE_BUY_STOP_LIMIT  :  if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundErrorModifySL());    class="kw">break;

「改单失败时的分类型声音告警」

在 MT5 的 EA 或指标里,止损(SL)、止盈(TP)修改失败如果只靠日志,盯盘时极容易漏看。把错误事件按订单类型拆开播报,是降低漏单概率的直接办法。 下面这段逻辑用 switch 按 action 区分市价单、stop、limit、stop_limit 的买/卖方向,先由 UseSoundModifySL / UseSoundModifyTP 判断是否开启声音,再调用 CMessage::PlaySound 播放各自预设的错误音效文件。 实战中建议把 SoundErrorModifySL 与 SoundErrorModifyTP 设成不同频率的 wav,例如 SL 失败用 440Hz 短鸣、TP 失败用 880Hz 双响,这样不用看屏幕也能分辨是哪类改单被拒。外汇与贵金属杠杆高,改单失败可能让浮亏扩大,声音冗余提醒只是辅助,不能替代风控。

MQL5 / C++
case ORDER_TYPE_SELL               :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.Sell.SoundErrorModifySL());                class="kw">break;
case ORDER_TYPE_SELL_STOP            :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.SellStop.SoundErrorModifySL());            class="kw">break;
case ORDER_TYPE_SELL_LIMIT           :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.SellLimit.SoundErrorModifySL());           class="kw">break;
case ORDER_TYPE_SELL_STOP_LIMIT      :   if(this.UseSoundModifySL(action))  CMessage::PlaySound(this.m_datas.SellStopLimit.SoundErrorModifySL());       class="kw">break;
class="kw">default: class="kw">break;
   }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Play TakeProfit modification error sound of                        |
class=class="str">"cmt">//| a specified position/order type                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CTradeObj::PlaySoundErrorModifyTP(class="kw">const class="type">int action)
  {
  class="kw">switch(action)
   {
   case ORDER_TYPE_BUY                :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.Buy.SoundErrorModifyTP());                 class="kw">break;
   case ORDER_TYPE_BUY_STOP           :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.BuyStop.SoundErrorModifyTP());             class="kw">break;
   case ORDER_TYPE_BUY_LIMIT          :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.BuyLimit.SoundErrorModifyTP());            class="kw">break;
   case ORDER_TYPE_BUY_STOP_LIMIT     :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundErrorModifyTP());        class="kw">break;
   case ORDER_TYPE_SELL               :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.Sell.SoundErrorModifyTP());                class="kw">break;
   case ORDER_TYPE_SELL_STOP          :   if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.SellStop.SoundErrorModifyTP());            class="kw">break;

挂单改价失败的声音分支

在 EA 里给挂单改价或改 TP 被拒时,靠 switch 按订单类型播不同提示音,比全局一个 beep 更利于分辨哪类单子出问题。下面这段是改 TP 失败的声音分发,只处理六种挂单类型,市价单不在此列。 改价失败的函数结构完全一致,只是调用 SoundErrorModifyPrice() 而非 SoundErrorModifyTP(),且每个 case 都先过 UseSoundModifyPrice(action) 这个开关判断。若你不想某类挂单发声,在对应 UseSound* 方法里返回 false 即可静音。 外汇与贵金属本身波动剧烈、滑点频发,这类改单失败提示只能帮你快速察觉异常,不表示任何后续价格走向。

MQL5 / C++
case ORDER_TYPE_SELL_LIMIT        :  if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.SellLimit.SoundErrorModifyTP());        class="kw">break;
case ORDER_TYPE_SELL_STOP_LIMIT  :  if(this.UseSoundModifyTP(action))  CMessage::PlaySound(this.m_datas.SellStopLimit.SoundErrorModifyTP());  class="kw">break;
case class="kw">default: class="kw">break;
}
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Play price modification error sound                               |
class=class="str">"cmt">//| for a specified order type                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CTradeObj::PlaySoundErrorModifyPrice(class="kw">const class="type">int action)
  {
   class="kw">switch(action)
     {
      case ORDER_TYPE_BUY_STOP          :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.BuyStop.SoundErrorModifyPrice());           class="kw">break;
      case ORDER_TYPE_BUY_LIMIT         :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.BuyLimit.SoundErrorModifyPrice());          class="kw">break;
      case ORDER_TYPE_BUY_STOP_LIMIT    :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundErrorModifyPrice());     class="kw">break;
      case ORDER_TYPE_SELL_STOP         :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.SellStop.SoundErrorModifyPrice());          class="kw">break;
      case ORDER_TYPE_SELL_LIMIT        :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.SellLimit.SoundErrorModifyPrice());        class="kw">break;
      case ORDER_TYPE_SELL_STOP_LIMIT   :   if(this.UseSoundModifyPrice(action))  CMessage::PlaySound(this.m_datas.SellStopLimit.SoundErrorModifyPrice());  class="kw">break;
      class="kw">default: class="kw">break;
     }
  }

◍ 成交音效按动作类型分流的逻辑

CTradeObj 里把交易事件的声音反馈拆成了成功与错误两套接口:PlaySoundSuccess 与 PlaySoundError,参数完全一致,都接收动作类型、订单号以及 sl/tp/pr 三个布尔开关,用来标记这次事件是否涉及止损、止盈或价格字段的变动。 成功音效函数开头先判 m_use_sound 标志,若未开启直接 return,这意味着你如果在面板里关了声音,后续 switch 再怎么匹配也不会出声。 动作分流靠 switch((int)action):开仓类(市价买、买限、买停、买停限及对应的卖系列共 8 个枚举)统一调 PlaySoundOpen(order);平仓类(ACTION_TYPE_CLOSE、ACTION_TYPE_CLOSE_BY)走 PlaySoundClose;修改类里则按 sl/tp/pr 优先级短路返回——先判 sl 播修改止损音,再判 tp 播修改止盈音,最后 pr 播改价音,三者都不命中就不发声。 这种结构让你在 EA 里加音效时,只需维护 m_use_sound 和那几个 PlaySoundXxx 子函数,主流程靠传参就能覆盖外汇与贵金属订单的全部状态变更。注意 MT5 自动交易伴随滑点和高波动风险,音效仅作提醒,不构成任何方向判断。

MQL5 / C++
class="type">void CTradeObj::PlaySoundSuccess(class="kw">const ENUM_ACTION_TYPE action,class="kw">const class="type">int order,class="type">bool sl=class="kw">false,class="type">bool tp=class="kw">false,class="type">bool pr=class="kw">false)
  {
   if(!this.m_use_sound)
      class="kw">return;
   class="kw">switch((class="type">int)action)
     {
      class=class="str">"cmt">//--- Open/set
      case ACTION_TYPE_BUY           :
      case ACTION_TYPE_BUY_LIMIT     :
      case ACTION_TYPE_BUY_STOP      :
      case ACTION_TYPE_BUY_STOP_LIMIT:
      case ACTION_TYPE_SELL          :
      case ACTION_TYPE_SELL_LIMIT    :
      case ACTION_TYPE_SELL_STOP     :
      case ACTION_TYPE_SELL_STOP_LIMIT:
         this.PlaySoundOpen(order);  
         class="kw">break;
      class=class="str">"cmt">//--- Close/remove
      case ACTION_TYPE_CLOSE         :
      case ACTION_TYPE_CLOSE_BY      :
         this.PlaySoundClose(order);
         class="kw">break;
      class=class="str">"cmt">//--- Modification
      case ACTION_TYPE_MODIFY        :
         if(sl) { this.PlaySoundModifySL(order);     class="kw">return; }
         if(tp) { this.PlaySoundModifyTP(order);     class="kw">return; }
         if(pr) { this.PlaySoundModifyPrice(order); class="kw">return; }
         class="kw">break;
      class="kw">default:
         class="kw">break;
     }
  }
把参数预检交给小布盯盘
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到 StopLevel 与 FreezeLevel 实时边界,你只需专注下单逻辑而非记点数。

常见问题

若止损小于品种最小停止水平,服务器会直接拒绝订单。提前在基准类返回 false 可避免无效请求加重服务器负担。
价格处于冻结级别点数范围内时,终端不允许修改或删除对应挂单与止损止盈,基准类应在发送前检测并返回错误。
可在基准交易对象中声明播放成功/出错声音的公开方法,由通用标记控制开关,并按交易事件与订单类型调用私密播放实现。
能。小布盯盘对应品种页已集成 StopLevel、FreezeLevel 等边界展示,省去你手动查询符号属性的麻烦。
按文中实现,一次修改多个参数时仅播放其中第一个被检测到的修改事件声音,避免重复提示。