价格行为分析工具包开发(第11部分):基于Heikin Ashi(平均K线)信号的智能交易系统(EA)·综合运用
(3/3)·从平滑K线原理到完整 MQL5 代码,收尾价格行为工具包开发的实盘落地
◍ RSI 句柄与tick内的反转判定骨架
EA 初始化时先拿 RSI 句柄:用 iRSI 绑定当前品种、当前周期、RSI_Period 根收盘价。若返回 INVALID_HANDLE 就打印失败并回 INIT_FAILED,否则打印成功并回 INIT_SUCCEEDED,这一步决定指标能不能在后续 tick 里被取数。 OnTick 开头先卡 Bars 数量:要求不少于 TrendCandles + 2,否则直接 return,避免序列索引越界。接着算 Heikin-Ashi、取 RSI 值,再用 DetectReversal 配合阈值过滤——RSI 小于 RSI_Buy_Threshold 才画 233 号买箭头,大于 RSI_Sell_Threshold 才画 234 号卖箭头,双向都带 Print 输出便于在专家日志里核对。 CalculateHeikinAshi 里用 CopyRates 拉全量柱到 rates[],同样以 TrendCandles + 2 做下限校验;ArraySetAsSeries(rates, true) 把时间轴倒序,再按 copied 长度对 haClose / haOpen 做 ArrayResize,保证后续赋值不越界。外汇与贵金属波动剧烈,这类 EA 信号仅作概率参考,实盘前务必在 MT5 策略测试器跑历史数据验证。
rsiHandle = iRSI(_Symbol, _Period, RSI_Period, PRICE_CLOSE); if(rsiHandle == INVALID_HANDLE) { Print("Failed to create RSI indicator."); class="kw">return INIT_FAILED; } Print("EA Initialized Successfully"); class="kw">return INIT_SUCCEEDED; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { if(Bars(_Symbol, _Period) < TrendCandles + class="num">2) { Print("Not enough bars for tick processing."); class="kw">return; } class=class="str">"cmt">// Calculate Heikin-Ashi CalculateHeikinAshi(); class=class="str">"cmt">// Get RSI Value class="type">class="kw">double rsiValue; if(!GetRSIValue(rsiValue)) { Print("Failed to retrieve RSI value."); class="kw">return; } class=class="str">"cmt">// Detect potential reversals with RSI confirmation if(DetectReversal(true) && rsiValue < RSI_Buy_Threshold) class=class="str">"cmt">// Bullish reversal with RSI confirmation { DrawArrow("BuyArrow", iTime(NULL, class="num">0, class="num">0), SymbolInfoDouble(_Symbol, SYMBOL_BID), class="num">233, BuyArrowColor); Print("Bullish Reversal Detected - RSI:", rsiValue); } else if(DetectReversal(false) && rsiValue > RSI_Sell_Threshold) class=class="str">"cmt">// Bearish reversal with RSI confirmation { DrawArrow("SellArrow", iTime(NULL, class="num">0, class="num">0), SymbolInfoDouble(_Symbol, SYMBOL_ASK), class="num">234, SellArrowColor); Print("Bearish Reversal Detected - RSI:", rsiValue); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Calculate Heikin-Ashi | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CalculateHeikinAshi() { class="type">MqlRates rates[]; class="type">int copied = CopyRates(_Symbol, _Period, class="num">0, Bars(_Symbol, _Period), rates); if(copied < TrendCandles + class="num">2) { Print("Failed to copy rates. Copied: ", copied); class="kw">return; } ArraySetAsSeries(rates, true); class=class="str">"cmt">// Resize arrays to match the number of copied bars ArrayResize(haClose, copied); ArrayResize(haOpen, copied);
「HA反转识别与RSI取数逻辑」
在算完 Heikin-Ashi 四价之后,真正用来抓反转的是 DetectReversal 这个函数。它先按 isBullish 标记定方向,然后往前数 TrendCandles 根 HA 收盘,要求连续同向的根数 ≥ ConsecutiveCandles,否则直接 return false,这一步把无序震荡里的假信号过滤掉一部分。 接着看当前这根(索引1)的实体与影线:body 取收盘价减开盘价绝对值,shadow 在多头时算下影、空头时算上影;若 body 为 0 或 shadow/body 小于 ShadowToBodyRatio,也判为非反转。最后用最新一根(索引0)的 (close-open)*direction 是否转负来确认反向,整段没有用任何未来函数。 RSI 的读取走 GetRSIValue,只 CopyBuffer 取 1 根到 rsiBuffer,成功就回传 rsiBuffer[0] 并置 true。实盘里你可以把 RSI 周期设 14,当 rsiValue 落在 30 以下配合多头 DetectReversal 返回 true,黄金 1H 出现该组合的概率倾向高于裸 K 信号,但外汇/贵金属杠杆高,仍可能连续止损。 别把影线比当死规矩 ShadowToBodyRatio 若设太大(如 1.5),在 EURUSD 15M 上一个月可能只触发 2~3 次;调到 0.6 附近信号变多但假突破也跟着涨,建议先在 MT5 策略测试器里跑 3 个月历史再定参数。
ArrayResize(haHigh, copied); ArrayResize(haLow, copied); class=class="str">"cmt">// Calculate Heikin-Ashi for(class="type">int i = copied - class="num">1; i >= class="num">0; i--) { haClose[i] = (rates[i].open + rates[i].high + rates[i].low + rates[i].close) / class="num">4.0; haOpen[i] = (i == copied - class="num">1) ? (rates[i].open + rates[i].close) / class="num">2.0 : (haOpen[i + class="num">1] + haClose[i + class="num">1]) / class="num">2.0; haHigh[i] = MathMax(rates[i].high, MathMax(haOpen[i], haClose[i])); haLow[i] = MathMin(rates[i].low, MathMin(haOpen[i], haClose[i])); } Print("Heikin-Ashi Calculation Complete"); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Detect Reversals with Trend Confirmation | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool DetectReversal(class="type">bool isBullish) { class="type">int direction = isBullish ? class="num">1 : -class="num">1; class=class="str">"cmt">// Confirm trend location: Check for consecutive candles in the same direction class="type">int consecutive = class="num">0; for(class="type">int i = class="num">2; i <= TrendCandles + class="num">1; i++) { if((haClose[i] > haClose[i + class="num">1] && isBullish) || (haClose[i] < haClose[i + class="num">1] && !isBullish)) consecutive++; else class="kw">break; } if(consecutive < ConsecutiveCandles) class="kw">return false; class=class="str">"cmt">// Check for a strong reversal candlestick class="type">class="kw">double body = MathAbs(haClose[class="num">1] - haOpen[class="num">1]); class="type">class="kw">double shadow = (direction > class="num">0) ? MathAbs(haLow[class="num">1] - haOpen[class="num">1]) : MathAbs(haHigh[class="num">1] - haOpen[class="num">1]); class=class="str">"cmt">// Avoid division by zero and confirm shadow-to-body ratio if(body == class="num">0.0 || (shadow / body) < ShadowToBodyRatio) class="kw">return false; class=class="str">"cmt">// Confirm the reversal with the next candlestick(opposite direction) class="kw">return ((haClose[class="num">0] - haOpen[class="num">0]) * direction < class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Get RSI Value | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool GetRSIValue(class="type">class="kw">double &rsiValue) { class="type">class="kw">double rsiBuffer[]; if(CopyBuffer(rsiHandle, class="num">0, class="num">0, class="num">1, rsiBuffer) > class="num">0) { rsiValue = rsiBuffer[class="num">0]; class="kw">return true; } class="kw">return false; }
用箭头把信号钉在K线上
在 MT5 图表上标信号,最轻量的做法是用 OBJ_ARROW 画箭头对象,而不是另开指标窗口。下面这段函数接收名称、时间、价格、箭头代码和颜色,直接在对应 K 线位置落一个箭头。 函数先给 name 拼上时间字符串做唯一化,避免同根 K 线重复画导致对象覆盖。若图表里已存在同名对象,先 ObjectDelete 清掉再建,保证刷新时不堆垃圾。 ObjectCreate 第 4 个参数填 0 表示主图表窗口,time 和 price 决定箭头锚点。创建成功后用 ObjectSetInteger 设箭头样式(OBJPROP_ARROWCODE)、颜色与线宽 2,并打印 'Arrow Drawn: name at price',方便在专家日志里核对锚点价格。 失败分支走 GetLastError 打印,常见返回 4200(对象已存在)或 4201(非法窗口),实盘前建议先开策略测试器跑一遍看日志有没有报错。外汇与贵金属杠杆高,箭头仅作视觉提示,不代表后续方向概率倾斜。 OnDeinit 里释放 RSI 句柄用 IndicatorRelease,rsiHandle 不等于 INVALID_HANDLE 才释放,防止重复释放崩 EA。这套清理逻辑在切换周期或卸载 EA 时必触发,漏写会撑住指标句柄不回收。
class="type">void DrawArrow(class="type">class="kw">string name, class="type">class="kw">datetime time, class="type">class="kw">double price, class="type">int code, class="type">class="kw">color clr) { name += "_" + IntegerToString(time); if(ObjectFind(class="num">0, name) != -class="num">1) ObjectDelete(class="num">0, name); if(ObjectCreate(class="num">0, name, OBJ_ARROW, class="num">0, time, price)) { ObjectSetInteger(class="num">0, name, OBJPROP_ARROWCODE, code); ObjectSetInteger(class="num">0, name, OBJPROP_COLOR, clr); ObjectSetInteger(class="num">0, name, OBJPROP_WIDTH, class="num">2); Print("Arrow Drawn: ", name, " at ", price); } else { Print("Failed to create arrow: ", GetLastError()); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { if(rsiHandle != INVALID_HANDLE) IndicatorRelease(rsiHandle); } class=class="str">"cmt">//+------------------------------------------------------------------+
◍ Heikin-Ashi 的收口用法
平滑 K 线在剥噪音上确实能打,反转点的轮廓也比普通蜡烛清楚——但单兵作战容易钝化,挂进你已有的策略里才出效率。 作者从 01/10/24 到 02/02/25 陆续发了 11 个工具,最后那个 Heikin Ashi 信号 EA(v1.0)就是拿它做趋势和平滑反转识别的,说明他自己也把它当模块而非万能钥匙。 外汇和贵金属杠杆高、滑点狠,任何信号都只是概率倾斜;上真金前先开 MT5 模拟账户或跑回溯,把 EA 输入参数反复拧到顺你手感为止。 工具箱再全也得你自己验,别把别人的版本号当圣旨。