用于交易事件和信号的语音通知系统·进阶篇
◍ 多周期 RVI 权重与指标句柄初始化
这套指标把 5 组 RVI 周期做了非对称加权:周期 21 权重 0.10、34 权重 0.16、55 权重 0.26、89 权重 0.43,长周期占比明显更高,倾向让慢线主导信号过滤。 OnInit 里先给 BuySignal、SellSignal 两个缓冲绑定 INDICTOR_DATA,箭头分别设为 233 / 234,颜色用 DodgerBlue 和 Crimson,上下偏移 ±20 点避免压住 K 线。 ADX 云和零延迟 RVI 都用 iCustom 挂自定义指标;若 ADX_Handle 返回 INVALID_HANDLE 直接 INIT_FAILED 退出。开 MT5 后把 adxcloud 与 colorzerolagrvi 两个文件放进 Indicators 目录,否则编译能过但运行时拿不到句柄。外汇与贵金属波动剧烈,自定义指标失效可能导致信号真空,属高风险场景。
input class="type">class="kw">double Weight2 = class="num">0.10; input class="type">int RVI_period2 = class="num">21; class=class="str">"cmt">//---- input class="type">class="kw">double Weight3 = class="num">0.16; input class="type">int RVI_period3 = class="num">34; class=class="str">"cmt">//---- input class="type">class="kw">double Weight4 = class="num">0.26; input class="type">int RVI_period4 = class="num">55; class=class="str">"cmt">//---- input class="type">class="kw">double Weight5 = class="num">0.43; input class="type">int RVI_period5 = class="num">89; class=class="str">"cmt">//--- class="type">class="kw">double BuySignal[],SellSignal[],ADXCloud[],FastRVI[],SlowRVI[]; class="type">int ADX_Handle,RVI_Hadnle,min_rates_total; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- SetIndexBuffer(class="num">0,BuySignal,INDICATOR_DATA); SetIndexBuffer(class="num">1,SellSignal,INDICATOR_DATA); class=class="str">"cmt">//--- PlotIndexSetInteger(class="num">0,PLOT_ARROW,class="num">233); PlotIndexSetInteger(class="num">1,PLOT_ARROW,class="num">234); class=class="str">"cmt">//--- PlotIndexSetInteger(class="num">0,PLOT_LINE_COLOR,clrDodgerBlue); PlotIndexSetInteger(class="num">1,PLOT_LINE_COLOR,clrCrimson); class=class="str">"cmt">//--- ArraySetAsSeries(SellSignal,true); ArraySetAsSeries(BuySignal,true); PlotIndexSetDouble(class="num">0,PLOT_EMPTY_VALUE,EMPTY_VALUE); PlotIndexSetDouble(class="num">1,PLOT_EMPTY_VALUE,EMPTY_VALUE); PlotIndexSetInteger(class="num">0,PLOT_ARROW_SHIFT,class="num">20); PlotIndexSetInteger(class="num">1,PLOT_ARROW_SHIFT,-class="num">20); class=class="str">"cmt">//--- ADX_Handle=iCustom(Symbol(),PERIOD_CURRENT,"adxcloud",ADXPeriod,Alpha1,Alpha2); if(ADX_Handle==INVALID_HANDLE) { Print(" Failed to create indicator handle"); class="kw">return(INIT_FAILED); } class=class="str">"cmt">//--- RVI_Hadnle=iCustom(Symbol(),PERIOD_CURRENT,"colorzerolagrvi", Smoothing,
「多周期RVI权重与句柄校验的落地写法」
在自定义指标初始化阶段,用五组 Weight 与 RVI_period 成对传参,是给相对活力指数做多周期加权的基础。上面这段片段里,五个周期分别由 Weight1~Weight5 和 RVI_period1~RVI_period5 控制,句柄创建失败就直接 Print 报错并返回 INIT_FAILED,避免后续 OnCalculate 空跑。 OnCalculate 里先卡 BarsCalculated(ADX_Handle) 和 BarsCalculated(RVI_Hadnle) 是否都小于 rates_total,同时要求 rates_total 不小于 min_rates_total,任一不满足就 return(0) 不画。这个前置判断能挡掉历史数据没加载完时的乱信号。 limit 的计算分两种:首跑或重载时取 rates_total-2,增量更新时取 rates_total-prev_calculated,to_copy 再补 2 根确保 CopyBuffer 不越界。ADXCloud、FastRVI、SlowRVI 以及 high/low 全部 ArraySetAsSeries(true) 按时间倒序排,CopyBuffer 从 0 偏移拉 to_copy 长度,RVI 快慢线分别取 0 和 1 号缓冲区。 开 MT5 把这段嵌进你的指标骨架,重点调 RVI_period1~5 和对应 Weight,看不同周期权重组合下快慢 RVI 交叉频次的变化,外汇和贵金属波动大,参数乱配可能放大假信号,属于高风险验证。
Weight1,RVI_period1,
Weight2,RVI_period2,
Weight3,RVI_period3,
Weight4,RVI_period4,
Weight5,RVI_period5
);
if(RVI_Hadnle==INVALID_HANDLE)
{
Print(" Failed to create indicator handle");
class="kw">return(INIT_FAILED);
}
class=class="str">"cmt">//---
class="kw">return(INIT_SUCCEEDED);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Custom indicator iteration function |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnCalculate(const class="type">int rates_total,
const class="type">int prev_calculated,
const class="type">class="kw">datetime &time[],
const class="type">class="kw">double &open[],
const class="type">class="kw">double &high[],
const class="type">class="kw">double &low[],
const class="type">class="kw">double &close[],
const class="type">long &tick_volume[],
const class="type">long &volume[],
const class="type">int &spread[])
{
class=class="str">"cmt">//--- c
if(BarsCalculated(ADX_Handle)<rates_total || BarsCalculated(RVI_Hadnle)<rates_total || rates_total<min_rates_total)
class="kw">return(class="num">0);
class=class="str">"cmt">//---
class="type">int limit,to_copy,i;
class=class="str">"cmt">//---
ArraySetAsSeries(ADXCloud,true);
ArraySetAsSeries(FastRVI,true);
ArraySetAsSeries(SlowRVI,true);
ArraySetAsSeries(high,true);
ArraySetAsSeries(low,true);
class=class="str">"cmt">//---
if(prev_calculated>rates_total || prev_calculated<=class="num">0)
limit=rates_total-class="num">2;
else
limit=rates_total-prev_calculated;
to_copy=limit+class="num">2;
class=class="str">"cmt">//---
if(CopyBuffer(ADX_Handle,class="num">0,class="num">0,to_copy,ADXCloud)<=class="num">0)
class="kw">return(class="num">0);
class=class="str">"cmt">//---
if(CopyBuffer(RVI_Hadnle,class="num">0,class="num">0,to_copy,FastRVI)<=class="num">0)
class="kw">return(class="num">0);
if(CopyBuffer(RVI_Hadnle,class="num">1,class="num">0,to_copy,SlowRVI)<=class="num">0)给信号加实时弹窗而非只画箭头
上面的循环段把 ADXCloud 与 Fast/Slow RVI 的穿越条件跑完,只是在数组里写 BuySignal/SellSignal,图表上能看到箭头,但人不在屏幕前就漏了。真正能落地的是在 i==0 这一根把信号推出来。 原逻辑用 ADXCloud[i+1]>0 且 FastRVI 刚上穿 SlowRVI(i+1 在上方、i+2 在下方)判多;反之为空。加 Notify.Message(SIGNAL_BUY/SELL) 后,仅在最新柱 i==0 触发,避免历史重绘时狂响。 include 的 SoundsLib 里 CSoundsLib 对象要先 Notify.Language(ENGLISH) 设语言,否则英文模板消息可能乱码。外汇与贵金属波动大,这类提示只作辅助,信号失效概率不低,真要跟单请先在 MT5 策略测试器跑一轮。
class="kw">return(class="num">0); class=class="str">"cmt">//--- for(i=limit-class="num">1; i>=class="num">0 && !IsStopped(); i--) { if(ADXCloud[i+class="num">1]>class="num">0 && FastRVI[i+class="num">1]>SlowRVI[i+class="num">1] && FastRVI[i+class="num">2]<SlowRVI[i+class="num">2]) { BuySignal[i]=low[i]; SellSignal[i]=EMPTY_VALUE; } else if(ADXCloud[i+class="num">1]<class="num">0 && FastRVI[i+class="num">1]<SlowRVI[i+class="num">1] && FastRVI[i+class="num">2]>SlowRVI[i+class="num">2]) { SellSignal[i]=high[i]; BuySignal[i]=EMPTY_VALUE; } else { BuySignal[i]=EMPTY_VALUE; SellSignal[i]=EMPTY_VALUE; } } class=class="str">"cmt">//--- class="kw">return value of prev_calculated for the next call class="kw">return(rates_total); } class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <SoundsLib/SoundsLib.mqh> CSoundsLib Notify; Notify.Language(ENGLISH); SIGNAL_BUY, class=class="str">"cmt">// A Buy signal SIGNAL_SELL, class=class="str">"cmt">// A Sell signal class=class="str">"cmt">//--- for(i=limit-class="num">1; i>=class="num">0 && !IsStopped(); i--) { if(ADXCloud[i+class="num">1]>class="num">0 && FastRVI[i+class="num">1]>SlowRVI[i+class="num">1] && FastRVI[i+class="num">2]<SlowRVI[i+class="num">2]) { BuySignal[i]=low[i]; SellSignal[i]=EMPTY_VALUE; if(i==class="num">0) Notify.Message(SIGNAL_BUY); } else if(ADXCloud[i+class="num">1]<class="num">0 && FastRVI[i+class="num">1]<SlowRVI[i+class="num">1] && FastRVI[i+class="num">2]>SlowRVI[i+class="num">2]) { SellSignal[i]=high[i]; BuySignal[i]=EMPTY_VALUE; if(i==class="num">0) Notify.Message(SIGNAL_SELL); } else { BuySignal[i]=EMPTY_VALUE; SellSignal[i]=EMPTY_VALUE; } }
◍ 用语音警报把信号说出口的 EA 落地法
把指标信号接进智能交易系统,光靠终端“智能系统”选项卡里的文本提示很容易漏看。实测中,交易者在盯多周期图表时漏掉文本警报的概率明显偏高,而语音播报能把“发现买信号”“已开仓”这类动作直接念出来,比单纯蜂鸣声信息密度更高。 这里用 ColorStDev 与三重 Tirone 等级搭一套可语音播报的测试 EA:时间帧任意,买条件是 ColorStdDev 直方图为红色(趋势强烈)且现价高于上层 Tirone 级别;卖条件是直方图为红色且现价低于底层 Tirone 级别;离场用止盈止损。初始化时要先查终端是否允许自动交易,禁用的话立刻播报提醒,避免 EA 静默罢工。 OnTick() 里一旦识别到期望信号,先语音告知“发现所需买/卖信号”,再尝试下单;开仓成功再播一条“已开仓”。这样用户不需要一直盯着日志,也能知道 EA 当前在干什么。外汇与贵金属杠杆高,语音通知只是降低漏单概率,不表示信号必然盈利。 下面这段是 MT5 里的参数与头文件引入部分,逐行看: #include <SoundsLib/SoundsLib.mqh> 引入语音库,负责播放语音警报 #include <DoEasy25/Engine.mqh> 引入 DoEasy 引擎,简化指标与交易对象处理 input uint InpStopLoss=150; 止损点数设为 150 点 input uint InpTakeProfit=250; 止盈点数设为 250 点 input double InpLot=0.1; 每单手数 0.1 input ulong InpDeviation=10; 成交最大允许滑点偏差 10 点 input int InpMagic=2356; 魔幻号 2356,用于区分本 EA 订单 input LANGUAGE NotifyLanguage=ENGLISH; 语音播报语言选英文 input int StDevPeriod=12; ColorStDev 的平滑周期取 12
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| VoiceNotify.mq5 | class=class="str">"cmt">//| Alex2356 | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Alex2356" class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#include <SoundsLib/SoundsLib.mqh> class="macro">#include <DoEasy25/Engine.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ input class="type">uint InpStopLoss = class="num">150; class=class="str">"cmt">// Stop Loss, in pips input class="type">uint InpTakeProfit = class="num">250; class=class="str">"cmt">// Take Profit, in pips input class="type">class="kw">double InpLot = class="num">0.1; class=class="str">"cmt">// Take Profit, in pips input class="type">ulong InpDeviation = class="num">10; class=class="str">"cmt">// Deviation input class="type">int InpMagic = class="num">2356; class=class="str">"cmt">// Magic number input LANGUAGE NotifyLanguage = ENGLISH; class=class="str">"cmt">// Notification Language class=class="str">"cmt">//--- ColorStDev indicator parameters input class="type">int StDevPeriod = class="num">12; class=class="str">"cmt">// Smoothing period StDev
「EA初始化里的指标句柄与交易开关」
这套语音提醒 EA 在 OnInit 里先检查终端是否允许自动交易,若被关闭就调用 notify.Message(AUTO_TRADING_OFF) 弹提示。外汇和贵金属波动剧烈、杠杆风险高,自动交易前务必确认券商端已放行。 随后分别用 iCustom 抓取两个自定义指标句柄:colorstddev 传入 StDevPeriod、MA_Method(默认 MODE_EMA)、applied_price(默认 PRICE_CLOSE)、MaxTrendLevel=90、MiddLeTrendLevel=50、FlatLevel=20;tirone_levels_x3 只传 TironePeriod=13 和偏移 0。任一句柄返回 INVALID_HANDLE 就打印错误并 INIT_FAILED,EA 直接起不来。 在 MT5 里新建 EA 时,把这两个 iCustom 路径写成 'ArticleVoiceNotify\\colorstddev' 和 'ArticleVoiceNotify\\tirone_levels_x3',改 TironePeriod 从 13 调到 21,可能让 Tierone 通道对日线噪声的过滤更钝,但信号延迟也会变大,建议先开策略测试器跑一周验证。
input ENUM_MA_METHOD MA_Method = MODE_EMA; class=class="str">"cmt">// Histogram smoothing method input ENUM_APPLIED_PRICE applied_price = PRICE_CLOSE; class=class="str">"cmt">// Applied price input class="type">int MaxTrendLevel = class="num">90; class=class="str">"cmt">// Maximum trend level input class="type">int MiddLeTrendLevel = class="num">50; class=class="str">"cmt">// Middle trend level input class="type">int FlatLevel = class="num">20; class=class="str">"cmt">// Flat level class=class="str">"cmt">//--- Tirone Levels indicator parameters input class="type">int TironePeriod = class="num">13; class=class="str">"cmt">// Tirone Period class=class="str">"cmt">//--- CEngine trade; CSoundsLib notify; class="type">int Handle1,Handle2; class="type">class="kw">double stdev[],tirone_b[],tirone_s[]; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) notify.Message(AUTO_TRADING_OFF); class=class="str">"cmt">//--- OnInitTrading(); class=class="str">"cmt">//--- Get the handle of the ColorStDev indicator Handle1=iCustom(Symbol(),PERIOD_CURRENT,"ArticleVoiceNotify\\colorstddev", StDevPeriod, MA_Method, applied_price, MaxTrendLevel, MiddLeTrendLevel, FlatLevel ); if(Handle1==INVALID_HANDLE) { Print("Failed to get colorstddev handle"); Print("Handle = ",Handle1," error = ",GetLastError()); class="kw">return(INIT_FAILED); } class=class="str">"cmt">//--- Getting the handle of the Tirone Levels indicator Handle2=iCustom(Symbol(),PERIOD_CURRENT,"ArticleVoiceNotify\\tirone_levels_x3",TironePeriod,class="num">0); if(Handle2==INVALID_HANDLE) { Print("Failed to get Tirone Levels handle");
信号触发与下单的执行链路
EA 在 OnTick 里先判断当前品种是否已有持仓:ExistPositions(Symbol(),-1,InpMagic)<1 才允许新开仓,避免同魔法码重复进场。 无持仓时调用 GetIndValue() 刷新指标缓冲区,失败直接 return。随后并行检查 BuySignal() 与 SellSignal(),任一为真就先推 notify.Message 信号提示,再交由 trade.OpenBuy / OpenSell 按 InpLot、InpStopLoss、InpTakeProfit 发单。 开仓成功后 Sleep(1400) 毫秒再发 BUY_ORDER_SET / SELL_ORDER_SET 回执——这个 1.4 秒停顿是为让券商确认成交、防止同 tick 内重复操作。 买条件看前一根 Tirone 中线是否高于前收:tirone_b[1]>iClose(Symbol(),PERIOD_CURRENT,1) 且实时标准差 stdev[0]>FlatLevel 才返回 true,意味着价格跌破通道中轨且波动脱离平坦区,倾向走出空头反转概率更高。外汇与贵金属杠杆高,信号失效时回撤可能快速扩大,须以实盘点差验证。
class="type">void OnTick() { class=class="str">"cmt">//--- If there are no market positions if(ExistPositions(Symbol(),-class="num">1,InpMagic)<class="num">1) { class=class="str">"cmt">//--- Getting data for calculations if(!GetIndValue()) class="kw">return; class=class="str">"cmt">//--- Open an order if there is a buy signal if(BuySignal()) { notify.Message(SIGNAL_BUY); if(trade.OpenBuy(InpLot,Symbol(),InpMagic,InpStopLoss,InpTakeProfit)) { Sleep(class="num">1400); notify.Message(BUY_ORDER_SET); } } class=class="str">"cmt">//--- Opening an order if there is a sell signal if(SellSignal()) { notify.Message(SIGNAL_SELL); if(trade.OpenSell(InpLot,Symbol(),InpMagic,InpStopLoss,InpTakeProfit)) { Sleep(class="num">1400); notify.Message(SELL_ORDER_SET); } } } } class="type">bool BuySignal() { class="kw">return(tirone_b[class="num">1]>iClose(Symbol(),PERIOD_CURRENT,class="num">1) && stdev[class="num">0]>FlatLevel)?true:class="kw">false; } class="type">bool SellSignal() {