价格行为分析工具包开发(第11部分):基于Heikin Ashi(平均K线)信号的智能交易系统(EA)
概述
数学公式是价格行为分析的核心基石,它为解读市场数据提供了客观方法,并指导交易决策。例如,在南非外汇市场中,交易南非兰特兑美元(ZAR/USD)货币对的交易者可能采用50日均线(SMA)来识别趋势反转,同时运用标准差衡量市场波动性,从而确认潜在的突破机会。通过整合头寸规模计算与风险管理公式(如基于设定风险百分比和止损条件确定交易量),交易者能有效控制风险,并借助如Fibonacci回调等工具设定清晰的盈利目标。
本文将重点介绍Heikin Ashi技术在趋势反转计算中的应用,为我们的量化分析与经典价格行为方法相结合的综合策略增添新维度。Heikin Ashi的核心优势在于其强大的市场噪音过滤能力,使交易者能聚焦核心趋势,避免被短期价格波动干扰。该方法通过平滑市场波动,确保决策基于数据驱动,为全球各类金融市场提供了一套严谨的交易框架。
首先,我们将介绍 Heikin Ashi的定义及其原理。随后,不做冗余铺垫,我们直接切入策略核心,让您立即明白其运作方式。我们还会讲解其他关键功能,提供完整代码,并在文章结束前回顾结果。现在,让我们先浏览目录。
Heikin Ashi
Heikin Ashi技术由18世纪日本米市交易员Munehisa Homma所创,他也是K线图的先驱,该工具至今仍是技术分析的基石。Heikin Ashi技术起源于日本,其设计初衷是通过聚焦平均价格波动而非单一K线形态,帮助交易者更清晰地洞察市场趋势。在日语中,Heikin 意为“平均”或“平衡”,Ashi 则指“柱”或“K线”。这一命名直接体现了该图表的核心原理——通过平滑价格数据,呈现更均衡、波动性更低的市场走势视图。

图例1. 平滑图表
下表对比了传统K线与Heikin Ashi形态,突出了它们的主要区别。
| 特征 | 传统K线 | Heikin Ashi形态 |
|---|---|---|
| 计算基础 | 使用实际的开盘价、最高价、最低价和收盘价。 | 使用经过修改的公式取平均价格。 |
| 波动性展示 | 显示真实的价格波动和缺口。 | 平滑价格走势,减少噪音。 |
| 趋势清晰度 | 趋势中可能会出现反复的红绿K线交替。 | 提供趋于平滑的趋势,颜色变化更少。 |
| 可视化缺口 | 展示K线之间的价格缺口。 | 由于使用平均公式,很少出现缺口。 |
| 反转信号 | 显示快速反转和影线。 | 反转较慢,但一经确认更为可靠。 |
| 价格表示 | 反映真实的市场价格。 | 对价格取平均值,最终价格可能与真实市场价不匹配。 |
策略
本策略围绕 MQL5 EA 的四大功能构建:Heikin Ashi计算、趋势确认、反转信号识别,以及使用RSI的信号确认。下面逐一详解。
Heikin Ashi计算
Heikin Ashi图表由四个关键数据节点构成:开盘价、最高价、最低价和收盘价。这些值经处理后生成Heikin Ashi形态,与传统K线不同。它并非反映原始价格行为,而是使用平均价格计算——混合开收盘及高低价——以呈现更流畅的市场趋势。
这种平滑效果削弱突发价格波动的影响,减少市场噪音,使趋势更易识别。因此,Heikin Ashi形态通常实体更小、影线更长,突出市场动量并滤除短期波动。为实现Heikin Ashi策略,以下流程图展示如何将传统K线数据转化为平滑、聚焦趋势的Heikin Ashi图表。此过程过滤市场噪音,提供更清晰的主流趋势视角。以下是其工作原理的逐步分解:

图例2. 流程图
该流程图展示了如何将传统开盘价-最高价-最低价-收盘价(OHLC)数据转换为Heikin Ashi形态。流程始于原始市场数据,通过计算收盘价的平均值(haClose)进行平滑处理。开盘价(haOpen)则使用上一根K线的数值来确定,以保持连续性;而haHigh与haLow确保K线反映价格波动的完整区间。最终得到的K线减少了短期波动,使整体趋势更加明显。以下为计算Heikin Ashi形态的MQL5代码段:
void CalculateHeikinAshi() { MqlRates rates[]; int copied = CopyRates(_Symbol, _Period, 0, Bars(_Symbol, _Period), rates); if(copied < TrendCandles + 2) { Print("Failed to copy rates. Copied: ", copied); return; } ArraySetAsSeries(rates, true); // Resize arrays to match the number of copied bars ArrayResize(haClose, copied); ArrayResize(haOpen, copied); ArrayResize(haHigh, copied); ArrayResize(haLow, copied); // Calculate Heikin-Ashi values for each bar for(int i = copied - 1; i >= 0; i--) { haClose[i] = (rates[i].open + rates[i].high + rates[i].low + rates[i].close) / 4.0; haOpen[i] = (i == copied - 1) ? (rates[i].open + rates[i].close) / 2.0 : (haOpen[i + 1] + haClose[i + 1]) / 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"); }
最终得到的一连串Heikin Ashi形态过滤掉了大量市场噪音,使潜在趋势变得更加清晰。
趋势确认
在尝试识别任何反转信号之前,EA会先进行趋势确认,以确保当前存在强劲的定向走势。这一步通过分析连续指定数量的Heikin Ashi来完成。对于看涨趋势,EA检查每根K线的haClose是否高于前一根K线的haClose;对于看跌趋势,则验证haClose是否连续低于前一根。所需连续K线的数量由输入参数控制,确保仅当趋势已充分确立时才会被接受。这一严格校验通过确认市场处于明确趋势之中,最大限度降低虚假信号风险,随后才进入反转检测阶段。
int consecutive = 0; for(int i = 2; i <= TrendCandles + 1; i++) { if((haClose[i] > haClose[i + 1] && isBullish) || (haClose[i] < haClose[i + 1] && !isBullish)) consecutive++; else break; } if(consecutive < ConsecutiveCandles) return false;
这一步确保只在有强劲证据表明趋势正在持续时才会接受信号,从而降低因随机市场波动而产生虚假信号的可能性。
反转信号识别
在确认趋势已确立后,EA通过仔细观察下一根Heikin Ashi的结构来识别潜在的反转信号。在此阶段,EA计算K线实体为haClose与haOpen的绝对差值,并根据关注方向测量影线——看涨反转时重点考察下影线,而看跌反转时则关注上影线。一个核心条件是影线与实体的比值必须超过预设阈值。高影线-实体比表明市场强烈拒绝当前趋势,其特征为相对于小实体而言的长影线。此类形态是可能正在发生反转的强力信号。
// Check for a strong reversal candlestick double body = MathAbs(haClose[1] - haOpen[1]); double shadow = (direction > 0) ? MathAbs(haLow[1] - haOpen[1]) : MathAbs(haHigh[1] - haOpen[1]); // Avoid division by zero and confirm shadow-to-body ratio if(body == 0.0 || (shadow / body) < ShadowToBodyRatio) return false;
这一步通过要求K线结构出现强烈的反转特征,过滤掉疲弱或模糊的信号,然后才继续下一步。
RSI信号确认
策略的最后一步是使用相对强弱指数(RSI)对反转信号进行确认,从而增加一层验证。一旦通过Heikin Ashi标准识别出潜在反转,EA会获取最新的RSI值来评估市场动量。对于看涨反转信号,RSI必须低于指定的买入阈值,表明资产处于超卖状态;反之,对于看跌反转,RSI必须高于指定的卖出阈值,表明资产处于超买状态。只有当Heikin Ashi形态与RSI条件同时满足时,EA才会生成交易信号——例如在图表上绘制买入或卖出箭头。这种双重确认方法有助于减少虚假信号,确保只有在多个指标共同证实市场反转时才执行交易。
// Get RSI Value double rsiValue; if(!GetRSIValue(rsiValue)) { Print("Failed to retrieve RSI value."); return; } // Detect potential reversals with RSI confirmation if(DetectReversal(true) && rsiValue < RSI_Buy_Threshold) // Bullish reversal with RSI confirmation { DrawArrow("BuyArrow", iTime(NULL, 0, 0), SymbolInfoDouble(_Symbol, SYMBOL_BID), 233, BuyArrowColor); Print("Bullish Reversal Detected - RSI:", rsiValue); } else if(DetectReversal(false) && rsiValue > RSI_Sell_Threshold) // Bearish reversal with RSI confirmation { DrawArrow("SellArrow", iTime(NULL, 0, 0), SymbolInfoDouble(_Symbol, SYMBOL_ASK), 234, SellArrowColor); Print("Bearish Reversal Detected - RSI:", rsiValue); }
RSI的确认为我们增添了动量分析维度。通过把(Heikin Ashi)价格行为与(RSI)动量结合,使得EA提高了信号的可靠性,确保仅在多重指标一致时才入场。
其他函数
输入参数
在进入核心逻辑之前,我们先定义一组输入参数,用于精准调节EA的行为。这些参数可控制:趋势确认规则、反转条件、成交量阈值、RSI设置以及信号可视化,无需改动代码即可随时调整。
input int TrendCandles = 3; // Number of candles for trend detection input double ShadowToBodyRatio = 1.5; // Shadow-to-body ratio for reversal detection input int ConsecutiveCandles = 2; // Consecutive candles to confirm trend input int RSI_Period = 14; // RSI Period input double RSI_Buy_Threshold = 34.0; // RSI level for buy confirmation input double RSI_Sell_Threshold = 65.0; // RSI level for sell confirmation input color BuyArrowColor = clrGreen; // Buy signal color input color SellArrowColor = clrRed; // Sell signal color
这些参数让我们能够完全掌控策略的关键环节。TrendCandles:设定识别趋势时所观察的Heikin Ashi数量。ShadowToBodyRatio:确保只考虑强烈反转信号。ConsecutiveCandles:要求至少连续两根K线同向,过滤掉弱势趋势。RSI_Buy_Threshold与RSI_Sell_Threshold:利用RSI为信号再增加一道确认。BuyArrowColor与SellArrowColor:可自由定制图表上买卖箭头的外观颜色。
全局变量
为了保证计算高效,我们声明全局数组用于存储Heikin Ashi数值,并创建一个RSI指标句柄。
double haClose[], haOpen[], haHigh[], haLow[]; int rsiHandle;
这些变量用来保存计算后的Heikin Ashi值,并在EA中动态获取RSI的读数。
初始化(OnInit)
把EA挂载到图表时,首先运行OnInit()。该函数完成Heikin Ashi数组的设置,并初始化RSI指标。
int OnInit() { ArraySetAsSeries(haClose, true); ArraySetAsSeries(haOpen, true); ArraySetAsSeries(haHigh, true); ArraySetAsSeries(haLow, true); if(Bars(_Symbol, _Period) < TrendCandles + 2) { Print("Not enough bars for initialization."); return INIT_FAILED; } rsiHandle = iRSI(_Symbol, _Period, RSI_Period, PRICE_CLOSE); if(rsiHandle == INVALID_HANDLE) { Print("Failed to create RSI indicator."); return INIT_FAILED; } Print("EA Initialized Successfully"); return INIT_SUCCEEDED; }
这里,我们将数组设为反向序列,使最新K线始终位于索引0处。并在计算前检查可用K线数量是否足够。随后初始化RSI指标,若失败则处理相关错误。如果一切配置正确,EA会打印确认信息并开始运行。
Tick处理(OnTick)
该函数在每次新价格tick时执行,确保持续分析市场并识别潜在交易信号。
void OnTick() { if(Bars(_Symbol, _Period) < TrendCandles + 2) { Print("Not enough bars for tick processing."); return; } CalculateHeikinAshi(); double rsiValue; if(!GetRSIValue(rsiValue)) { Print("Failed to retrieve RSI value."); return; } if(DetectReversal(true) && rsiValue < RSI_Buy_Threshold) { DrawArrow("BuyArrow", iTime(NULL, 0, 0), SymbolInfoDouble(_Symbol, SYMBOL_BID), 233, BuyArrowColor); Print("Bullish Reversal Detected - RSI:", rsiValue); } else if(DetectReversal(false) && rsiValue > RSI_Sell_Threshold) { DrawArrow("SellArrow", iTime(NULL, 0, 0), SymbolInfoDouble(_Symbol, SYMBOL_ASK), 234, SellArrowColor); Print("Bearish Reversal Detected - RSI:", rsiValue); } }
我们为每根箭头生成一个基于信号时间戳的唯一名称。如果发现同名箭头已存在,则先删除旧箭头,避免图表混淆。随后创建新箭头对象,并设置其属性(颜色、大小、类型)。如果创建成功,打印确认信息,表示箭头已放置。
反初始化清理(OnDeinit)
将EA从图表移除时,需释放指标资源,以避免内存问题。
void OnDeinit(const int reason) { if(rsiHandle != INVALID_HANDLE) IndicatorRelease(rsiHandle); }
该函数可确保在EA停止运行时正确移除RSI指标。
MQL5代码
//+------------------------------------------------------------------+
//| Heikin Ashi Signal EA.mq5 |
//| Copyright 2025, Christian Benjamin. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Christian Benjamin."
#property link "https://www.mql5.com/en/users/lynnchris"
#property version "1.00"
#property strict
//--- Input parameters
input int TrendCandles = 3; // Number of candles for trend detection
input double ShadowToBodyRatio = 1.5; // Shadow-to-body ratio for reversal detection
input int ConsecutiveCandles = 2; // Consecutive candles to confirm trend
input int RSI_Period = 14; // RSI Period
input double RSI_Buy_Threshold = 34.0; // RSI level for buy confirmation
input double RSI_Sell_Threshold = 65.0; // RSI level for sell confirmation
input color BuyArrowColor = clrGreen; // Buy signal color
input color SellArrowColor = clrRed; // Sell signal color
//--- Global variables
double haClose[], haOpen[], haHigh[], haLow[];
int rsiHandle;
//+------------------------------------------------------------------+
//| Expert initialization |
//+------------------------------------------------------------------+
int OnInit()
{
ArraySetAsSeries(haClose, true);
ArraySetAsSeries(haOpen, true);
ArraySetAsSeries(haHigh, true);
ArraySetAsSeries(haLow, true);
if(Bars(_Symbol, _Period) < TrendCandles + 2)
{
Print("Not enough bars for initialization.");
return INIT_FAILED;
}
// Initialize RSI indicator
rsiHandle = iRSI(_Symbol, _Period, RSI_Period, PRICE_CLOSE);
if(rsiHandle == INVALID_HANDLE)
{
Print("Failed to create RSI indicator.");
return INIT_FAILED;
}
Print("EA Initialized Successfully");
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(Bars(_Symbol, _Period) < TrendCandles + 2)
{
Print("Not enough bars for tick processing.");
return;
}
// Calculate Heikin-Ashi
CalculateHeikinAshi();
// Get RSI Value
double rsiValue;
if(!GetRSIValue(rsiValue))
{
Print("Failed to retrieve RSI value.");
return;
}
// Detect potential reversals with RSI confirmation
if(DetectReversal(true) && rsiValue < RSI_Buy_Threshold) // Bullish reversal with RSI confirmation
{
DrawArrow("BuyArrow", iTime(NULL, 0, 0), SymbolInfoDouble(_Symbol, SYMBOL_BID), 233, BuyArrowColor);
Print("Bullish Reversal Detected - RSI:", rsiValue);
}
else
if(DetectReversal(false) && rsiValue > RSI_Sell_Threshold) // Bearish reversal with RSI confirmation
{
DrawArrow("SellArrow", iTime(NULL, 0, 0), SymbolInfoDouble(_Symbol, SYMBOL_ASK), 234, SellArrowColor);
Print("Bearish Reversal Detected - RSI:", rsiValue);
}
}
//+------------------------------------------------------------------+
//| Calculate Heikin-Ashi |
//+------------------------------------------------------------------+
void CalculateHeikinAshi()
{
MqlRates rates[];
int copied = CopyRates(_Symbol, _Period, 0, Bars(_Symbol, _Period), rates);
if(copied < TrendCandles + 2)
{
Print("Failed to copy rates. Copied: ", copied);
return;
}
ArraySetAsSeries(rates, true);
// Resize arrays to match the number of copied bars
ArrayResize(haClose, copied);
ArrayResize(haOpen, copied);
ArrayResize(haHigh, copied);
ArrayResize(haLow, copied);
// Calculate Heikin-Ashi
for(int i = copied - 1; i >= 0; i--)
{
haClose[i] = (rates[i].open + rates[i].high + rates[i].low + rates[i].close) / 4.0;
haOpen[i] = (i == copied - 1) ? (rates[i].open + rates[i].close) / 2.0 : (haOpen[i + 1] + haClose[i + 1]) / 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");
}
//+------------------------------------------------------------------+
//| Detect Reversals with Trend Confirmation |
//+------------------------------------------------------------------+
bool DetectReversal(bool isBullish)
{
int direction = isBullish ? 1 : -1;
// Confirm trend location: Check for consecutive candles in the same direction
int consecutive = 0;
for(int i = 2; i <= TrendCandles + 1; i++)
{
if((haClose[i] > haClose[i + 1] && isBullish) || (haClose[i] < haClose[i + 1] && !isBullish))
consecutive++;
else
break;
}
if(consecutive < ConsecutiveCandles)
return false;
// Check for a strong reversal candlestick
double body = MathAbs(haClose[1] - haOpen[1]);
double shadow = (direction > 0) ? MathAbs(haLow[1] - haOpen[1]) : MathAbs(haHigh[1] - haOpen[1]);
// Avoid division by zero and confirm shadow-to-body ratio
if(body == 0.0 || (shadow / body) < ShadowToBodyRatio)
return false;
// Confirm the reversal with the next candlestick (opposite direction)
return ((haClose[0] - haOpen[0]) * direction < 0);
}
//+------------------------------------------------------------------+
//| Get RSI Value |
//+------------------------------------------------------------------+
bool GetRSIValue(double &rsiValue)
{
double rsiBuffer[];
if(CopyBuffer(rsiHandle, 0, 0, 1, rsiBuffer) > 0)
{
rsiValue = rsiBuffer[0];
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Draw Arrow |
//+------------------------------------------------------------------+
void DrawArrow(string name, datetime time, double price, int code, color clr)
{
name += "_" + IntegerToString(time);
if(ObjectFind(0, name) != -1)
ObjectDelete(0, name);
if(ObjectCreate(0, name, OBJ_ARROW, 0, time, price))
{
ObjectSetInteger(0, name, OBJPROP_ARROWCODE, code);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
Print("Arrow Drawn: ", name, " at ", price);
}
else
{
Print("Failed to create arrow: ", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Expert deinitialization |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(rsiHandle != INVALID_HANDLE)
IndicatorRelease(rsiHandle);
}
//+------------------------------------------------------------------+
成果
在测试阶段,我同时采用回测与实盘测试来评估EA的表现。- 回测
我利用历史数据运行EA,以验证其基本可行性并发现优缺点。此阶段可确认策略在已知市场条件下是否有效,尽管无法完全模拟滑点或浮动点差等真实因素。请参见以下GIF动图。

图例3. V100指数回测
下面展示另外三个交易品种、共27天的回测结果,用于评估信号的准确性。如果市场在信号确认后发生转向,则视为成功信号。
- 欧元兑美元
| 信号 | 总计 | 情况相符 | %准确率 |
|---|---|---|---|
| 买入 | 20 | 17 | 85% |
| 卖出 | 10 | 8 | 80% |
- 暴跌900指数
| 信号 | 总计 | 情况相符 | %准确率 |
|---|---|---|---|
| 买入 | 18 | 14 | 77.8% |
| 卖出 | 25 | 15 | 60% |
- 步长指数
| 信号 | 总计 | 情况相符 | %准确率 |
|---|---|---|---|
| 买入 | 18 | 15 | 83.3.% |
| 卖出 | 22 | 14 | 63.6% |
该EA已被证明极为有效:在受测交易品种上,买入信号准确率至少达 77.8%,卖出信号准确率至少达 60%。
- 实盘市场测试

图例4. V25指数实盘测试
核心要点
回测在理论上验证策略,实盘测试则展示其真实表现。两个阶段都至关重要,可用来完善EA,确保其足够稳健以应对真实交易。
结论
Heikin-Ashi技术在过滤市场波动方面尤其有效。我还发现它在识别市场反转点时同样好用。像对待所有工具一样进行测试后,我体会到它与其他策略搭配使用时效率更高。请务必反复调整代码输入,并基于模拟账户或通过回溯测试,直到获得符合您自身偏好的最优表现。欢迎提出宝贵建议!
| 日期 | 工具名 | 描述 | 版本 | 更新 | 备注 |
|---|---|---|---|---|---|
| 01/10/24 | 图表展示器 | 以重影效果覆盖前一日价格走势的脚本 | 1.0 | 初始版本 | Lynnchris工具箱的第一个工具 |
| 18/11/24 | 分析评论 | 以表格形式提供前一日的信息,并预测市场的未来方向 | 1.0 | 初始版本 | Lynnchris工具箱的第二个工具 |
| 27/11/24 | 分析大师 | 每两小时定期更新市场指标 | 1.01 | 第二个版本 | Lynnchris工具箱的第三个工具 |
| 02/12/24 | 分析预测 | 集成Telegram通知功能,每两小时定时更新市场指标 | 1.1 | 第三个版本 | 工具4 |
| 09/12/24 | 波动率导航仪 | 该EA通过布林带、RSI和ATR三大指标综合分析市场状况 | 1.0 | 初始版本 | 工具5 |
| 19/12/24 | 均值回归信号收割器 | 运用均值回归策略分析市场并提供交易信号 | 1.0 | 初始版本 | 工具6 |
| 9/01/25 | 信号脉冲 | 多时间框架分析器 | 1.0 | 初始版本 | 工具7 |
| 17/01/25 | 指标看板 | 带分析按钮的控制面板 | 1.0 | 初始版本 | 工具8 |
| 21/01/25 | 外部数据流 | 通过外部库进行分析 | 1.0 | 初始版本 | 工具9 |
| 27/01/25 | VWAP | 成交量加权平均价 | 1.3 | 初始版本 | 工具10 |
| 02/02/25 | Heikin Ashi信号EA | 平滑趋势和反转信号识别 | 1.0 | 初始版本 | 工具11 |
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/17021
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
MQL5 交易工具包(第 4 部分):开发历史管理 EX5 库
迁移至 MQL5 Algo Forge(第 4 部分):使用版本和发布
交易中的神经网络:搭配预测编码的混合交易框架(终篇)