轻松快捷开发 MetaTrader 程序的函数库(第 二十三部分):基准交易类 - 基准类,有效参数验证·进阶篇
(2/3)· 止损小于 StopLevel、价格触碰 FreezeLevel 还在发单?本篇讲清基准类如何提前拦截
「挂单改价与报错的声音分支」
在 MT5 的 EA 里给不同挂单类型配提示音,常见做法是按订单动作走 switch 分支。下面这段把止盈修改和价格修改拆成两个独立函数,分别覆盖 BuyStop / BuyLimit / BuyStopLimit / SellStop / SellLimit / SellStopLimit 六种类型。 以 PlaySoundModifyPrice 为例,每个 case 先调 UseSoundModifyPrice(action) 判断该类型是否启用声音,再取对应结构体的 SoundModifyPrice() 返回文件名交给 CMessage::PlaySound 播放。若未启用则直接 break,不占资源。 这种写法在回测里几乎零开销,但在实盘高频改价时可能在 1 秒内触发多次 PlaySound,建议把 UseSoundModifyPrice 内部做成带时间戳的节流,否则黄金跳空时段容易被提示音刷屏。外汇与贵金属杠杆高,声音提醒只是辅助,不能替代风控。
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 路径,跑一晚模拟盘看能不能听音辨单。
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 类里,意味着你改一处音频目录逻辑,六类订单的两种报错音会同时生效。
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 双响,这样不用看屏幕也能分辨是哪类改单被拒。外汇与贵金属杠杆高,改单失败可能让浮亏扩大,声音冗余提醒只是辅助,不能替代风控。
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 即可静音。 外汇与贵金属本身波动剧烈、滑点频发,这类改单失败提示只能帮你快速察觉异常,不表示任何后续价格走向。
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 自动交易伴随滑点和高波动风险,音效仅作提醒,不构成任何方向判断。
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; } }