价格行为分析工具包开发(第五部分):波动率导航智能交易系统(Volatility Navigator EA)·进阶篇
🧭

价格行为分析工具包开发(第五部分):波动率导航智能交易系统(Volatility Navigator EA)·进阶篇

(2/3)·不少交易者方向看对却败在点位粗糙,本篇拆解EA如何靠布林带RSI与ATR自动算入场与风控

偏理论进阶 第 2/3 篇
把止损拍在整数位、止盈凭感觉挂,是多数手动交易者的日常。方向读对了,却被糟糕的执行吃掉利润。波动率导航的思路,是先让算法把点位算准,再谈决策。

三指标叠合才放单的入场判定

波动率导航类 EA 的核心不在指标本身,而在把 RSI、布林带、ATR 三套数据放在同一根 tick 上做交叉验证。OnInit 里先把三个句柄建好,之后每次 OnTick 直接拉当前值,避免重复创建带来的开销。 RSI 用 70 / 30 两个阈值切超买超卖,布林带看收盘价是否碰上轨或下轨,ATR 则设了一道 atrThreshold 波动性门槛。单看任何一个都容易假信号,EA 的逻辑是三者同满足才动手:RSI<30 且价格<下轨且 ATR>阈值,倾向触发买入;反向条件齐备才倾向卖出。 图表上用 ObjectCreate 画 OBJ_ARROW 做可视化,买卖箭头带时间戳命名,不会互相覆盖。外汇与贵金属杠杆高、滑点随机,这套组合只提高信号概率,不消除踏空或止损风险,实盘前建议在 MT5 策略测试器用 2023 年 XAUUSD 的 M15 跑一遍看信号密度。 下面这段是 EA 里指标句柄初始化与信号判定的原样代码,逐行拆完你能直接抄进自己的 ea 模板:先声明 rsiHandle、bbHandle、atrHandle 三个 int 句柄;OnInit 中用 iRSI(NULL,0,rsiPeriod,PRICE_CLOSE) 建 RSI 句柄,参数为当前品种、当前周期、rsiPeriod 周期、收盘价;iBands 建布林带句柄,传入 bbPeriod 与上下偏离 bbDevUp/bbDevDown;iATR 建平均真实波幅句柄;返回 INIT_SUCCEEDED 表示初始化通过。 后续用 iCustom 取 RSI 值,>70 注释为超买可能卖,<30 为超卖可能买;iBands 分别取上轨下轨,Close[0] 碰下轨倾向买、碰上轨倾向卖;iATR 取波动值,超阈值才考虑进场;最后 if 把三个条件用 && 锁死,全满足才 Generate buy/sell signal;DrawTradeSignals 里用 ObjectCreate 画箭头,买信号名前缀 BuySignal 加时间,卖信号前缀 SellSignal 加时间。

MQL5 / C++
class=class="str">"cmt">// Declare indicator handles
class="type">int rsiHandle;
class="type">int bbHandle;
class="type">int atrHandle;
class=class="str">"cmt">// OnInit function
class="type">int OnInit() {
    class=class="str">"cmt">// Create indicator handles
    rsiHandle = iRSI(NULL, class="num">0, rsiPeriod, PRICE_CLOSE);
    bbHandle = iBands(NULL, class="num">0, bbPeriod, bbDevUp, bbDevDown, PRICE_CLOSE);
    atrHandle = iATR(NULL, class="num">0, atrPeriod);
    
    class="kw">return(INIT_SUCCEEDED);
}
class="type">class="kw">double rsiValue = iCustom(NULL, class="num">0, "RSI", rsiPeriod, class="num">0);
if (rsiValue > class="num">70) {
    class=class="str">"cmt">// Market is overbought - potentially signal to sell
} else if (rsiValue < class="num">30) {
    class=class="str">"cmt">// Market is oversold - potentially signal to buy
}
class="type">class="kw">double upperBand = iBands(NULL, class="num">0, bbPeriod, bbDevUp, class="num">0, PRICE_CLOSE);
class="type">class="kw">double lowerBand = iBands(NULL, class="num">0, bbPeriod, -bbDevDown, class="num">0, PRICE_CLOSE);
class="type">class="kw">double price = Close[class="num">0];
if (price < lowerBand) {
    class=class="str">"cmt">// Price is touching the lower band - potential buy signal
} else if (price > upperBand) {
    class=class="str">"cmt">// Price is touching the upper band - potential sell signal
}
class="type">class="kw">double atrValue = iATR(NULL, class="num">0, atrPeriod);
if (atrValue > atrThreshold) {
    class=class="str">"cmt">// High volatility - consider entering trades
} else {
    class=class="str">"cmt">// Low volatility - potentially stay out of the market
}
if (rsiValue < class="num">30 && price < lowerBand && atrValue > atrThreshold) {
    class=class="str">"cmt">// Generate buy signal
} else if (rsiValue > class="num">70 && price > upperBand && atrValue > atrThreshold) {
    class=class="str">"cmt">// Generate sell signal
}
class="type">void DrawTradeSignals() {
    if (buySignal) {
        class=class="str">"cmt">// Draw buy signal on the chart
        ObjectCreate(class="num">0, "BuySignal" + IntegerToString(TimeCurrent()), OBJ_ARROW, class="num">0, Time[class="num">0], price);
    } else if (sellSignal) {
        class=class="str">"cmt">// Draw sell signal on the chart
        ObjectCreate(class="num">0, "SellSignal" + IntegerToString(TimeCurrent()), OBJ_ARROW, class="num">0, Time[class="num">0], price);
    }
}

「给波动率EA接上声音与图表双重预警」

把 RSI 的极端值变成可感知的提醒,是波动率导航类 EA 落地时最实用的一步。原文方案用音频播报加图表文字对象,覆盖超买(>70)与超卖(<30)两种状态,交易者不必盯死屏幕也能知道阈值被碰。 在 OnTick 里持续取 RSI(14, 收盘价) 即可,不需要额外开定时器。超过 70 就 PlaySound 放 alert.wav,同时在 High[0] 位置写红色「Overbought Signal!」;跌破 30 则在 Low[0] 写绿色「Oversold Signal!」。这种双通道反馈让反应速度倾向更快,但外汇与贵金属波动剧烈,信号只是概率提示,不代表反转必现。 下面这段是核心警报片段,逐行看逻辑:iRSI 取当前品种、当前周期、周期 14、收盘价;if 判断 >70 进音频与红色文本对象分支;else if <30 走绿色文本对象分支。ObjectCreate 的 OBJ_TEXT 挂在 0 号子窗口,时间轴用 Time[0] 对齐最新 K 线。 阈值别照抄 70/30 就完事。EURUSD 在 M15 上 RSI 刺穿 70 后继续冲 20~30 点的情况不少见,真要降噪可把 input 参数暴露出来,自己调到 75/25 回测几周再定。

MQL5 / C++
class=class="str">"cmt">// Example of defining thresholds
class="type">class="kw">double rsiValue = iRSI(NULL, class="num">0, class="num">14, PRICE_CLOSE);
class=class="str">"cmt">// Check for overbought and oversold conditions
if (rsiValue > class="num">70) {
    class=class="str">"cmt">// Audio Alert
    PlaySound("alert.wav");
    class=class="str">"cmt">// Visual Alert on Chart
    ObjectCreate("OverboughtAlert", OBJ_TEXT, class="num">0, Time[class="num">0], High[class="num">0]);
    ObjectSetText("OverboughtAlert", "Overbought Signal!", class="num">12, "Arial", clrRed);
    class=class="str">"cmt">// Additional actions like placing an order can be added here
} else if (rsiValue < class="num">30) {
    class=class="str">"cmt">// Audio Alert
    PlaySound("alert.wav");
    class=class="str">"cmt">// Visual Alert on Chart
    ObjectCreate("OversoldAlert", OBJ_TEXT, class="num">0, Time[class="num">0], Low[class="num">0]);
    ObjectSetText("OversoldAlert", "Oversold Signal!", class="num">12, "Arial", clrGreen);
    class=class="str">"cmt">// Additional actions like placing an order can be added here
}

◍ 布林带与ATR参数及图表清理绘制逻辑

这套信号脚本把布林带周期钉在 20、偏差 2.0,ATR 取 14 周期并用 1.5 倍乘数算止损止盈, alert.wav 作声音提醒。外汇与贵金属波动剧烈,参数直接照搬可能放大滑点风险,建议先在 MT5 策略测试器里跑一轮再上实盘。 ClearPreviousDrawings 在每次重画前清掉旧对象:用 ObjectFind 判是否存在 EntryPoint、StopLoss、TakeProfit、SignalText、BuyArrow、SellArrow 六个命名图元,非 -1 就 ObjectDelete。这样能避免历史箭头和水平线堆叠干扰读图。 DrawTradeLines 先调清理函数,再 ObjectCreate 画 OBJ_HLINE 类型的 EntryPoint,设绿色宽 2;随后同样方式建 StopLoss 线。若创建失败 Print 出 GetLastError 码,方便你定位是句柄问题还是对象名冲突。开 MT5 把这段贴进 EA 的 include 段,改 clrGreen 为 clrDodgerBlue 就能立刻看出自己画的入场线。

MQL5 / C++
input class="type">int bbPeriod = class="num">20;                    class=class="str">"cmt">// Period for Bollinger Bands
input class="type">class="kw">double bbDeviation = class="num">2.0;                class=class="str">"cmt">// Deviation for Bollinger Bands
input class="type">int atrPeriod = class="num">14;                      class=class="str">"cmt">// ATR period for stop loss and take profit calculations
input class="type">class="kw">double atrMultiplier = class="num">1.5;              class=class="str">"cmt">// Multiplier for ATR in calculating stop loss and take profit
input class="type">class="kw">string signalSound = "alert.wav";        class=class="str">"cmt">// Sound file for alert notifications
class=class="str">"cmt">// Indicator handles for Bollinger Bands and ATR
class="type">int bbHandle = class="num">0;
class="type">int atrHandle = class="num">0;
class=class="str">"cmt">// Function to clear previous drawings from the chart
class="type">void ClearPreviousDrawings()
{
   class=class="str">"cmt">// Delete any previously created trade lines and signal text
   if (ObjectFind(class="num">0, "EntryPoint") != -class="num">1)
      ObjectDelete(class="num">0, "EntryPoint");
   if (ObjectFind(class="num">0, "StopLoss") != -class="num">1)
      ObjectDelete(class="num">0, "StopLoss");
   if (ObjectFind(class="num">0, "TakeProfit") != -class="num">1)
      ObjectDelete(class="num">0, "TakeProfit");
   if (ObjectFind(class="num">0, "SignalText") != -class="num">1)
      ObjectDelete(class="num">0, "SignalText");
   if (ObjectFind(class="num">0, "BuyArrow") != -class="num">1)
      ObjectDelete(class="num">0, "BuyArrow");
   if (ObjectFind(class="num">0, "SellArrow") != -class="num">1)
      ObjectDelete(class="num">0, "SellArrow");
}
class=class="str">"cmt">// Function to draw entry points, stop loss, and take profit on the chart
class="type">void DrawTradeLines(class="type">class="kw">double entryPoint, class="type">class="kw">double stopLoss, class="type">class="kw">double takeProfit, class="type">class="kw">string signalText)
{
   class=class="str">"cmt">// Clear previous drawings before drawing new ones
   ClearPreviousDrawings();
   class=class="str">"cmt">// Draw the entry point line
   if (!ObjectCreate(class="num">0, "EntryPoint", OBJ_HLINE, class="num">0, TimeCurrent(), entryPoint))
      Print("Failed to create EntryPoint line. Error: ", GetLastError());
   ObjectSetInteger(class="num">0, "EntryPoint", OBJPROP_COLOR, clrGreen);
   ObjectSetInteger(class="num">0, "EntryPoint", OBJPROP_WIDTH, class="num">2);
   class=class="str">"cmt">// Draw the stop loss line
   if (!ObjectCreate(class="num">0, "StopLoss", OBJ_HLINE, class="num">0, TimeCurrent(), stopLoss))

把止损止盈和信号直接画进图表

信号触发后若只靠日志输出,盯盘时很容易漏看。下面这段把止损线、止盈线和文字标签直接绘到 MT5 主图,视觉上一眼可辨。 止损线用红色水平线(OBJ_HLINE),宽度设为 2;止盈线用蓝色同类型线。若 ObjectCreate 返回 false,立即用 GetLastError 打印具体错误码,方便排查对象名冲突或图表权限问题。 信号文字标签用 OBJ_LABEL,锚定在 entryPoint+10 的价格位,XDISTANCE 10 像素、YDISTANCE 30 像素偏移,白字 12 号字体。注意 label 的 Y 坐标在 OBJ_LABEL 里是价格而非像素,所以 entryPoint+10 对 XAUUSD 这类点值大的品种可能偏出视野,实盘前先按品种点差调这个数。 DrawEntryArrow 用 OBJ_ARROW 且 arrowCode=233( Wingdings 上箭头),宽 2。循环建仓对象时若失败直接 return,不阻塞后续逻辑。ManageOpenPositions 从 PositionsTotal()-1 倒序遍历,用 PositionGetTicket + PositionSelectByTicket 锁定当前 symbol 的持仓,避免正序删除导致的索引错位。外汇和贵金属杠杆高,图形辅助不等于风控,对象绘制失败也可能让止损线失踪,开 MT5 跑一遍确认对象树里 StopLoss/TakeProfit 都在才算稳。

MQL5 / C++
Print("Failed to create StopLoss line. Error: ", GetLastError());
ObjectSetInteger(class="num">0, "StopLoss", OBJPROP_COLOR, clrRed);
ObjectSetInteger(class="num">0, "StopLoss", OBJPROP_WIDTH, class="num">2);
class=class="str">"cmt">// Draw the take profit line
if (!ObjectCreate(class="num">0, "TakeProfit", OBJ_HLINE, class="num">0, TimeCurrent(), takeProfit))
   Print("Failed to create TakeProfit line. Error: ", GetLastError());
ObjectSetInteger(class="num">0, "TakeProfit", OBJPROP_COLOR, clrBlue);
ObjectSetInteger(class="num">0, "TakeProfit", OBJPROP_WIDTH, class="num">2);
class=class="str">"cmt">// Draw a label with the signal text to provide information at a glance
if (!ObjectCreate(class="num">0, "SignalText", OBJ_LABEL, class="num">0, TimeCurrent(), entryPoint + class="num">10))
   Print("Failed to create SignalText label. Error: ", GetLastError());
ObjectSetInteger(class="num">0, "SignalText", OBJPROP_XDISTANCE, class="num">10);
ObjectSetInteger(class="num">0, "SignalText", OBJPROP_YDISTANCE, class="num">30);
ObjectSetInteger(class="num">0, "SignalText", OBJPROP_COLOR, clrWhite);
ObjectSetInteger(class="num">0, "SignalText", OBJPROP_FONTSIZE, class="num">12);
ObjectSetString(class="num">0, "SignalText", OBJPROP_TEXT, signalText);
}
class=class="str">"cmt">// Function to draw arrows on the chart at entry points
class="type">void DrawEntryArrow(class="type">class="kw">double price, class="type">class="kw">string label, class="type">class="kw">color arrowColor)
{
   if (!ObjectCreate(class="num">0, label, OBJ_ARROW, class="num">0, TimeCurrent(), price))
   {
      Print("Failed to create arrow object. Error: ", GetLastError());
      class="kw">return;
   }
   ObjectSetInteger(class="num">0, label, OBJPROP_ARROWCODE, class="num">233); class=class="str">"cmt">// Arrow code for upward direction
   ObjectSetInteger(class="num">0, label, OBJPROP_COLOR, arrowColor);
   ObjectSetInteger(class="num">0, label, OBJPROP_WIDTH, class="num">2);       class=class="str">"cmt">// Set the width of the arrow
}
class=class="str">"cmt">// Function to manage open positions for efficient trade execution
class="type">void ManageOpenPositions(class="type">class="kw">string symbol)
{
   class=class="str">"cmt">// Loop through all open positions
   for (class="type">int i = PositionsTotal() - class="num">1; i >= class="num">0; i--)
   {
      class="type">ulong ticket = PositionGetTicket(i);
      if (PositionSelectByTicket(ticket))
      {
         class=class="str">"cmt">// Check if the position is for the current symbol
让小布替你跑这套诊断
这些指标叠加与警报逻辑,小布盯盘的AIGC已内置,打开对应品种页即可看到波动率带与RSI背离提示,把重复劳动交给小布,你专注决策。

常见问题

前者不预测方向,只解决入场、止损、止盈的精度问题,可独立于趋势判断运行。
三者分别覆盖波动区间、动能极值和真实振幅,组合起来更适配点位计算,MACD偏趋势确认故未纳入核心逻辑。
不能加载EA本身,但小布的品种页已用AIGC复现了类似的布林带突破与RSI超买超卖提醒,免写代码也能用。
常见为1.5到3倍ATR,具体看品种波动习性,贵金属通常比直盘外汇需要更宽的系数。
文内实现覆盖平台弹窗与推送,具体接入方式看第三节逻辑,外汇贵金属属高风险,信号仅作参考。