轻松快捷开发 MetaTrader 程序的函数库(第 二十三部分):基准交易类 - 基准类,有效参数验证(基础篇)
基准交易类里先卡死参数合法性
在 MetaTrader 5 里写基准交易类,第一步不是急着发单,而是把外部传进来的参数先验证一遍。很多策略回测看着漂亮,实盘一跑就因为手数、魔术码或交易品种名不合规直接报错退出,这类问题在 2020 年 1 月那版函数库示例里就被单独拎出来处理过。 有效参数验证的核心,是给基准类一个统一的入口方法,在对象初始化阶段就拒绝明显非法的输入。比如品种字符串为空、手数小于等于零、偏离点数写成负数,都该在构造时返回错误而不是留到 OrderSend 才崩。 外汇和贵金属波动大、杠杆高,参数错一格可能让实盘单子开在完全意料外的仓位上,所以验证层不能省。打开 MT5 用 MetaEditor 建个基准类,把校验逻辑写进构造函数,比事后查日志省事得多。
◍ 给交易事件挂上声音与参数闸门
在把订单真正丢给服务器前,先卡一道参数有效性检查。最典型的是停止单距离不能小于品种规定的 StopLevel,否则服务器必然拒单,白白占用请求通道。 同样要盯住 FreezeLevel 冻结级别:它规定了挂单价、止损价与当前价之间的最小点数距离,若改动或删除操作落在这个冻结区内,服务器会直接返回错误。检测到违规则在方法内返回 false,不发请求。 顺手把声音接上。我们给基准交易对象加一个播放能力,测试 EA 里不必逐个事件、逐个品种去配声,先统一挂一套“成功/出错”的标准音;实际工程里你也能按事件或品种单独覆盖。 外汇与贵金属杠杆高、滑点突变频繁,这类前置拦截能降低无效发单概率,但无法消除服务器拒单风险,仍需以回测和实盘日志验证。
「把交易事件声音收进私密层」
之前我们把播放各类交易事件声音的方法挂在类的公开部分,现在只保留成功/出错两个对外入口,把具体播放逻辑下沉到私密部分。新入口接收交易事件、订单类型、止损止盈修改标志和订单价格,先判断全局声音开关——没开就直接退出,所有声音都被禁用。 根据交易操作类型,入口方法会针对对应订单调用私密层的播放函数。若是修改类事件,还要额外检查具体改了哪个标志;一次改多个参数时,只播第一个被改项的声音,避免刷屏。 交易方法参数顺序也调了:注释紧跟魔幻数字,偏距换到滑点之后。原因是注释通常按订单个性设置,而偏距和滑点性质不同,这样排更顺手。 下面这段是私密层声音方法的声明与骨架,五个正常音 + 五个错误音,按开仓/平仓/改SL/改TP/改挂单价切分。入参都是 const int action,用 switch 分流到具体音效。 别把声音开关当摆设 全局标记没置位时方法直接 return,实盘里若发现事件没响,先查这个标志位而不是怀疑 wav 路径。外汇和贵金属波动快,声音提示只能辅助,不能替代风控。
class=class="str">"cmt">//--- Play the sound of(class="num">1) opening/placing a specified position/order type, class=class="str">"cmt">//--- (class="num">2) closing/removal of a specified position/order type, (class="num">3) StopLoss modification for a specified position/order type, class=class="str">"cmt">//--- (class="num">4) TakeProfit modification for a specified position/order type, (class="num">5) placement price modification for a specified order type class="type">void PlaySoundOpen(class="kw">const class="type">int action); class="type">void PlaySoundClose(class="kw">const class="type">int action); class="type">void PlaySoundModifySL(class="kw">const class="type">int action); class="type">void PlaySoundModifyTP(class="kw">const class="type">int action); class="type">void PlaySoundModifyPrice(class="kw">const class="type">int action); class=class="str">"cmt">//--- Play the error sound of(class="num">1) opening/placing a specified position/order type, class=class="str">"cmt">//--- (class="num">2) closing/removal of a specified position/order type, (class="num">3) StopLoss modification for a specified position/order type, class=class="str">"cmt">//--- (class="num">4) TakeProfit modification for a specified position/order type, (class="num">5) placement price modification for a specified order type class="type">void PlaySoundErrorOpen(class="kw">const class="type">int action); class="type">void PlaySoundErrorClose(class="kw">const class="type">int action); class="type">void PlaySoundErrorModifySL(class="kw">const class="type">int action); class="type">void PlaySoundErrorModifyTP(class="kw">const class="type">int action); class="type">void PlaySoundErrorModifyPrice(class="kw">const class="type">int action); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Play the sound of opening/placing | class=class="str">"cmt">//| a specified position/order type | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTradeObj::PlaySoundOpen(class="kw">const class="type">int action) { class="kw">switch(action) {
开仓与平仓的声音分支怎么写
在 MT5 的 EA 或指标里,给不同订单类型绑定提示音,最直白的写法就是用一个 switch 按 ORDER_TYPE_* 宏分流。下面这段只处理市价买、买停、买限、买停限、卖及各类挂单的开仓音,default 直接 break 不发声,避免无效调用。 核心逻辑是:先判断 UseSoundOpen(action) 是否允许发声,再调 CMessage::PlaySound() 取对应类型的 SoundOpen() 字符串。外汇与贵金属波动快,声音提醒只能作辅助,实际成交仍以终端日志与成交回执为准,这类品种杠杆高、滑点可能突然扩大。 平仓侧对称地提供了 PlaySoundClose(),同样用 switch(action) 分流,只是把 SoundOpen 换成 SoundClose,并用 UseSoundClose(action) 做开关判断。这样开平仓各有一套音源,交易者能靠耳朵区分是黄金新单还是欧美止损单被平。 把这两段直接塞进 CTradeObj 类,编译后到 MT5 策略测试器用 2023 年 XAUUSD 的 M1 数据跑一遍,若每次模拟成交都听到对应 wav,说明分支没漏类型。
case ORDER_TYPE_BUY : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.Buy.SoundOpen()); class="kw">break; case ORDER_TYPE_BUY_STOP : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.BuyStop.SoundOpen()); class="kw">break; case ORDER_TYPE_BUY_LIMIT : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.BuyLimit.SoundOpen()); class="kw">break; case ORDER_TYPE_BUY_STOP_LIMIT : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundOpen()); class="kw">break; case ORDER_TYPE_SELL : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.Sell.SoundOpen()); class="kw">break; case ORDER_TYPE_SELL_STOP : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.SellStop.SoundOpen()); class="kw">break; case ORDER_TYPE_SELL_LIMIT : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.SellLimit.SoundOpen()); class="kw">break; case ORDER_TYPE_SELL_STOP_LIMIT : if(this.UseSoundOpen(action)) CMessage::PlaySound(this.m_datas.SellStopLimit.SoundOpen()); class="kw">break; class="kw">default: class="kw">break; } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Play the sound of closing/removal of | class=class="str">"cmt">//| a specified position/order type | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTradeObj::PlaySoundClose(class="kw">const class="type">int action) { class="kw">switch(action) { case ORDER_TYPE_BUY : if(this.UseSoundClose(action)) CMessage::PlaySound(this.m_datas.Buy.SoundClose()); class="kw">break; case ORDER_TYPE_BUY_STOP : if(this.UseSoundClose(action)) CMessage::PlaySound(this.m_datas.BuyStop.SoundClose()); class="kw">break;
◍ 平仓与改止损的逐类型声音触发
在 EA 的事件音效模块里,平仓动作按订单类型分流播报:BUY_LIMIT、BUY_STOP_LIMIT、SELL、SELL_STOP、SELL_LIMIT、SELL_STOP_LIMIT 各自走独立 case,先由 UseSoundClose(action) 判断是否启用,再调用 CMessage::PlaySound 播放对应 SoundClose 文件,default 直接 break 不发声。 改止损音效由 PlaySoundModifySL 承接,结构同平仓:BUY、BUY_STOP、BUY_LIMIT、BUY_STOP_LIMIT 分别用 UseSoundModifySL 把关后播放各自的 SoundModifySL。 开 MT5 把这两段贴进 CTradeObj 类,把 m_datas 里各类型的 SoundClose / SoundModifySL 指向不同 wav,就能在回测或实盘里靠声音区分六类平仓与四类买侧改止损事件;外汇与贵金属杠杆高,音效仅作辅助提醒,不等于风险可控。
case ORDER_TYPE_BUY_LIMIT : if(this.UseSoundClose(action)) CMessage::PlaySound(this.m_datas.BuyLimit.SoundClose()); class="kw">break; case ORDER_TYPE_BUY_STOP_LIMIT : if(this.UseSoundClose(action)) CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundClose()); class="kw">break; case ORDER_TYPE_SELL : if(this.UseSoundClose(action)) CMessage::PlaySound(this.m_datas.Sell.SoundClose()); class="kw">break; case ORDER_TYPE_SELL_STOP : if(this.UseSoundClose(action)) CMessage::PlaySound(this.m_datas.SellStop.SoundClose()); class="kw">break; case ORDER_TYPE_SELL_LIMIT : if(this.UseSoundClose(action)) CMessage::PlaySound(this.m_datas.SellLimit.SoundClose()); class="kw">break; case ORDER_TYPE_SELL_STOP_LIMIT : if(this.UseSoundClose(action)) CMessage::PlaySound(this.m_datas.SellStopLimit.SoundClose()); class="kw">break; class="kw">default: class="kw">break; } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Play StopLoss modification sound of | class=class="str">"cmt">//| a specified position/order type | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTradeObj::PlaySoundModifySL(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.SoundModifySL()); class="kw">break; case ORDER_TYPE_BUY_STOP : if(this.UseSoundModifySL(action)) CMessage::PlaySound(this.m_datas.BuyStop.SoundModifySL()); class="kw">break; case ORDER_TYPE_BUY_LIMIT : if(this.UseSoundModifySL(action)) CMessage::PlaySound(this.m_datas.BuyLimit.SoundModifySL()); class="kw">break; case ORDER_TYPE_BUY_STOP_LIMIT : if(this.UseSoundModifySL(action)) CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundModifySL()); class="kw">break;
「止盈改仓时的分类型声音触发」
在 CTradeObj 类里,PlaySoundModifyTP 方法承接了止盈(TakeProfit)修改事件的声音反馈逻辑,和前面处理止损修改的 switch 结构一一对应。 它同样按 action 参数做分支:市价买、买停、买限、买停限、市价卖、卖停等类型各自走自己的 SoundModifyTP() 取值,再用 CMessage::PlaySound 播放。 代码里每种订单类型都先过一道 UseSoundModifyTP(action) 的开关判断,只有该类型允许发声才真正调用播放,避免无意义的音频占用。 你在 MT5 里接这套逻辑时,可以直接把各类型的 SoundModifyTP 字符串换成自定义 wav 路径,改一个参数就能区分不同订单的止盈调整提示音。
class="type">void CTradeObj::PlaySoundModifyTP(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.SoundModifyTP()); class="kw">break; case ORDER_TYPE_BUY_STOP : if(this.UseSoundModifyTP(action)) CMessage::PlaySound(this.m_datas.BuyStop.SoundModifyTP()); class="kw">break; case ORDER_TYPE_BUY_LIMIT : if(this.UseSoundModifyTP(action)) CMessage::PlaySound(this.m_datas.BuyLimit.SoundModifyTP()); class="kw">break; case ORDER_TYPE_BUY_STOP_LIMIT : if(this.UseSoundModifyTP(action)) CMessage::PlaySound(this.m_datas.BuyStopLimit.SoundModifyTP()); class="kw">break; case ORDER_TYPE_SELL : if(this.UseSoundModifyTP(action)) CMessage::PlaySound(this.m_datas.Sell.SoundModifyTP()); class="kw">break; case ORDER_TYPE_SELL_STOP : if(this.UseSoundModifyTP(action)) CMessage::PlaySound(this.m_datas.SellStop.SoundModifyTP()); class="kw">break;