English Русский Deutsch 日本語
preview
MQL5 简介(第 13 部分):构建自定义指标的初学者指南(二)

MQL5 简介(第 13 部分):构建自定义指标的初学者指南(二)

MetaTrader 5EA交易 |
44 2
Israel Pelumi Abioye
Israel Pelumi Abioye

概述

欢迎回到我们的 MQL5 系列!本系列的第 12 部分探讨了在 MQL5 中构建自定义指标的基础知识。我们从无到有创建了一个移动平均线指标,手动实现其逻辑,而不是依赖内置函数。然后,我们扩展了这一知识,将其转换为蜡烛图格式的移动平均线,演示如何操作指标内的图形元素。

在此基础上,本文将介绍指标开发中更有趣的概念。我们将一如既往地使用基于项目的方法,通过付诸实践来确保您理解主题。创建 Heikin Ashi 指标并利用其数据计算移动平均线将是主要目标。在这些指标构建完成后,我们将开发一个包含 Heikin Ashi 指标和移动平均线指标的 EA 交易系统。即使是 MQL5 新手也能轻松上手,因为这是一篇适合初学者的文章。为了帮助您不仅了解如何实现函数,还了解为什么需要每个步骤,每一行代码都将被彻底解释。

本文将介绍的是一种仅用于教育目的的策略,它并非旨在提供保证成功的交易策略或财务建议。在将策略应用于实盘交易之前,务必先在无风险环境下进行测试。

Heikin Ashi (HA) 和 HA 移动平均线

图 1. Heikin Ashi 和 MA 指标

在本文中,您将学习:

  • 如何在 MQL5 中从零开始创建自定义 Heikin Ashi 指标。
  • 利用 Heikin Ashi 蜡烛图数据计算 Heikin Ashi 移动平均线。
  • 使用 iCustom() 函数访问非内置指标,并将其数据集成到交易策略中。
  • 使用 Heikin Ashi 和 MA 交叉点定义入场条件。
  • 通过基于 Heikin Ashi 的计算,动态设置止损和止盈水平,从而有效管理风险。
  • 利用 Heikin Ashi 蜡烛图形态应用追踪止损机制,在趋势发展过程中锁定利润。

1.Heikin Ashi 指标

1.1.了解 Heikin Ashi 指标

Heikin Ashi(HA)指标使趋势更容易看到,HA 使用一种独特的技术,基于平均历史价格数据确定新值,与典型的烛形图表形成对比,烛形图表显示每个时期的精确开盘价、最高价、最低价和收盘价。这有助于交易者在混乱中分类,并通过生成更清晰、更易懂的市场走势图,将注意力集中在重要的事情上。

典型的 K 线图中,每一根 K 线都显示了特定时期内的价格走势。红色(看跌)烛形表示相反的情况,绿色(看涨)烛形表示收盘价高于开盘价。通过蜡烛图主体上下方的细影线,交易者可以了解市场波动情况,细影线显示了该时间段内达到的最高价和最低价。

但 Heikin Ashi 烛形的做法却有所不同。它们使用特殊的计算方法来平滑趋势,而不是准确地报告价格变动。较长的绿色蜡形,影线较少,表明价格处于上升趋势,这使得识别和跟踪上涨动能变得更加容易。同样,在下跌趋势中,红色烛形会变得更加明显,充分表明了负面波动。当市场处于区间震荡或缺乏明显动能时,经常会出现两端都有影线的较小烛形,这表明交易者犹豫不决或优柔寡断。

HA 指标的独特之处在于,它通过使用平均技术修改了传统的烛形计算。它产生新的数值,从而更平滑、更一致地描绘价格走势,而不是直接绘制市场的开盘价、最高价、最低价和收盘价。通过消除价格小幅波动的“噪音”,交易者能够更好地发现趋势,做出更明智的选择。

Heikin Ashi 收盘价

Heikin Ashi 收盘价由当前周期的开盘价、最高价、最低价和收盘价的平均值确定。标准蜡烛图只使用收盘价,因此可以提供更平衡的价格变化视角。

公式为:

图 2. H A_Close 公式

Heikin Ashi 收盘价通过对这四个值取平均值来平滑价格波动,从而使价格形态在视觉上更加明显。

Heikin Ashi 开盘价

Heikin Ashi 开盘价是根据前一根 Heikin Ashi 烛形确定的,而不是根据实际的市场开盘价确定的。它是通过对前几个 Heikin Ashi 开盘价和收盘价取平均值来确定的:

图 3. H A_Open 公式

Heikin Ashi 通过将每根新烛形的开盘价与其前一根烛形的开盘价联系起来,建立了价格走势的连续流,从而减少了传统蜡烛图中经常出现的不可预测的跳跃。

Heikin Ashi 最高价

Heikin Ashi 最高价是该时间段内达到的最高价;然而,它考虑了三个值:当前时期的最高价、Heikin Ashi 开盘价和 Heikin Ashi 收盘价,而不仅仅是市场的实际最高价。从这三个选项中,选择最高的:

图 4. H A_High 公式

Heikin Ashi 最低价

同样,Heikin Ashi 最低价的计算方法是:取 Heikin Ashi 收盘价、Heikin Ashi 开盘价和该周期实际最低价中的最低值:

图 5. H A_Low 公式

这种方法与平滑方法保持一致,同时保证 Heikin Ashi 最低价能够捕捉到价格波动的最低点。Heikin Ashi 通过运用这些计算方法消除了微小的波动,并能更准确地反映市场走向。接下来我们将利用这一推理,在 MQL5 中构建我们自己的 Heikin Ashi 指标。

1.2.使用 Heikin Ashi 的好处

因为可以消除小的价格波动,Heikin Ashi 指标在交易者中很受欢迎,使识别趋势变得更加简单。由于传统烛形颜色变化迅速,因此难以解读,经常让交易者无法确定市场是上涨还是下跌。通过使用平均价格数据平滑图表,Heikin Ashi 解决了这个问题,并帮助你避免陷入细节困境。

一连串的红色烛形表明市场下跌,而一连串的绿色烛形通常表明强劲的上涨趋势。由于这种清晰性,区分真实、长期市场变化和短暂下跌就更容易了。  Heikin Ashi 可以帮助您减少错误信号,避免短期价格波动造成的不必要交易。利用历史数据过滤市场噪音,提供更可靠的趋势确认。许多交易者将其与 RSI 或移动平均线等工具结合使用,以改进他们的交易策略。Heikin Ashi 能更清晰地展现价格走势,使人们更容易决定何时进入或退出交易。

1.3.在 MQL5 中实现 Heikin Ashi

在了解了 Heikin Ashi 指标的运行原理之后,下一步就是在 MQL5 中实现 Heikin Ashi 指标。我们将从零开始构建我们自己的 Heikin Ashi 指标,因为 MetaTrader 5 缺少这样的指标。为此,必须对 Heikin Ashi 公式进行编码,将其应用于价格数据,并将结果显示在图表上。

和往常一样,开发一个程序首先要起草一个伪代码,在我们开始编写代码之前概述逻辑。这保证了我们在将计划付诸实现之前理解每个阶段,并帮助我们适当地构建它。

伪代码:

设置指标

  • 将指标设置为在单独的图表窗口中绘制。
  • Heikin Ashi 蜡烛图的绘图设置
  • 定义 4 个缓冲区来存储 Heikin Ashi 值(开盘价、最高价、最低价、收盘价)。
定义缓冲区

创建缓冲区以存储以下计算值:

  • Heikin Ashi 开盘价
  • Heikin Ashi 最高价
  • Heikin Ashi 最低价
  • Heikin Ashi 收盘价

计算 HEIKIN ASHI 指标值 

遍历历史价格数据,使用以下公式计算 Heikin Ashi 值:

  • HA 收盘价= (开盘价 + 最高价 + 最低价 + 收盘价) / 4
  • HA 开盘价= (前一 HA 开盘价 + 前一 HA 收盘价) / 2
  • HA 最高价= (最高价、HA 开盘价、HA 收盘价)中的最大值
  • HA 最低价= (最低价、HA 开盘价、HA 收盘价)中的最小值

1.3.1.创建和定制 Heikin Ashi

在创建伪代码后,我们现在必须进一步进行编程。正如上一篇文章所述,设计和修改指征的第一步是想象它在图表上的样子。在创建任何代码之前,指标的绘制、Heikin Ashi 蜡烛的显示,以及任何其他组成部分的包含,如看涨和看跌趋势的颜色,都应该由决定。

我们必须确保自定义指标能够正确地用其自身计算的值替换普通蜡烛图,因为 Heikin Ashi 会改变蜡烛图的外观。这需要为开盘价、最高价、最低价和收盘价设置缓冲区,并确保颜色动态变化以显示看跌和看涨趋势。在我们有了这种清晰的可视化效果后,我们就可以指定指标结构并开始编写代码了。在应用 Heikin Ashi 指标的逻辑之前,我们必须先定义该指标的属性设置。这些参数决定了绘制元素的样式(例如 Heikin Ashi 烛形)、指标将使用的缓冲区数量以及指标在图表上的显示方式。

示例:

// PROPERTY SETTINGS
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots   1

// PLOT SETTINGS FOR HEIKIN ASHI CANDLES
#property indicator_label1  "Heikin Ashi"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrGreen, clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

解释:

属性设置
#property indicator_separate_window

这指示 MetaTrader 5,该指标不应该叠加在主图表上,而应该显示在单独的窗口中。如果我们希望价格图表上直接显示它,则需要将这一行删除。

类比

把你的交易图表想象成一个工作站,你可以在上面分析市场的变化。基本工具,包括价格烛形和常规指标,被放置在主图表上,其功能类似于桌面。现在,想象一下自己正在做一个需要特定区域的细致的副业项目,比如桌子旁边的一块小白板。通过将这项活动转移到白板上,您可以独立地专注于它,而不会占用主要工作空间。

类似地,#property indicator_separate_window 通过将 Heikin Ashi 指标移至其自身的窗口而不是将其叠加在主价格图表上,从而简化了在不干扰标准蜡烛图数据的情况下检查趋势的操作。

#property indicator_buffers 5

这一行指定了指标将使用的缓冲区数量。本例中使用了五个缓冲区,一个用于颜色表示,另外的用于存储计算出的 Heikin Ashi 值(开盘价、最高价、最低价和收盘价)。

类比

现在,你的工作台旁边有了一块不同的白板,用于你的副业项目,想象一下你需要五个不同的托盘来跟踪你的工作。草图、测量值、笔记等存储在不同的托盘中。这让一切都井然有序,以便在需要时快速获取相关信息。

与这些托盘类似,#property indicator_buffers 5 确保各种 Heikin Ashi 数据点保持分离。这里,我们有五个缓冲区:一个用于颜色表示,四个用于 Heikin Ashi 值(开盘价、最高价、最低价和收盘价)。这些缓冲区使指标的计算保持结构化,这使得在图表上显示正确数据变得更简单,就像托盘保持工作空间有序一样。

#property indicator_plots 1

这表示该指标将显示多少个图表,我们只需要一个图表,因为我们将 Heikin Ashi 蜡烛图作为一个整体进行绘制。

类比

在把你的用品放进不同的托盘并设置好白板工作区后,接下来的步骤是选择如何展示你的作品。与其创建几个不同的图表,不如想象自己制作一个单一、全面的图表,将所有数据统一成一个单一、可理解的视觉描述。

同样,MetaTrader 5 通过 #property indicator_plots 1 获知 Heikin Ashi 指标将显示为单个绘图元素。与白板上的单一图表类似,几个缓冲区保存着不同的数据(开盘价、最高价、最低价、收盘价和颜色),但它们都组合起来创建了一组烛形。我们只需要一个图表来显示 Heikin Ashi 蜡烛图,因为我们只绘制它们。

绘图设置

#property indicator_label1  "Heikin Ashi"
#property indicator_type1   DRAW_COLOR_CANDLES  
#property indicator_color1  clrGreen, clrRed    
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

类比

在整理好材料并设置好白板工作场所后,清晰易懂地传达你的发现是很重要的。为了让趋势更加明显,你选择使用彩色编码符号,而不是写纯文本或创建抽象设计。通过在白板上标注 “Heikin Ashi”,您可以清楚地表明您的图表所代表的含义,以便任何观察白板的人都能看到。同样地,#property indicator_label1 "Heikin Ashi" 为指标命名,并保证它出现在 MetaTrader 5 指标列表中。这样一来,交易者就可以在图表中迅速识别出它以及其他指标。

#property indicator_type1 DRAW_COLOR_CANDLES 告诉 MetaTrader 5 使用彩色蜡烛图而不是线条图或直方图。颜色由 #property indicator_color1 clrGreen, clrRed 定义,其中绿色代表阳线,红色代表阴线。这种视觉清晰度使人们更容易一目了然地发现趋势。 为了保持白板的整洁和可读性,你会决定使用实心标记笔划,而不是虚线或虚线。

同样,#property indicator_style1 STYLE_SOLID 确保 Heikin Ashi 蜡烛图填充为纯色,使其在视觉上变得独特。最后,就像您避免将线条画得太粗以免使图表显得杂乱一样,#property indicator_width1 1 使蜡烛轮廓保持在合理的宽度,以便清晰地显示,而不会使图表显得过于拥挤。 通过以这种方式设置 Heikin Ashi 指标,我们可以创建一个清晰、结构化、直观的市场趋势表示,就像你对组织良好的白板工作区所做的那样。

现在我们已经设置了指标属性和绘图设置,下一步是定义将存储 Heikin Ashi 烛形价格的缓冲区。缓冲区充当指标计算值的存储容器,使 MetaTrader 5 能够在图表上显示这些值。在这种情况下,我们需要缓冲区来存储 Heikin Ashi 的开盘价、最高价、最低价和收盘价,以及一个用于颜色表示的额外缓冲区。我们还将设置各自的缓冲区索引,以确保每个缓冲区与其预期数据正确对应。

示例:

// PROPERTY SETTINGS
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots   1

// PLOT SETTINGS FOR HEIKIN ASHI CANDLES
#property indicator_label1  "Heikin Ashi"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrGreen, clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

// INDICATOR BUFFERS
double HA_Open[];
double HA_High[];
double HA_Low[];
double HA_Close[];
double ColorBuffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
// SET BUFFERS
   SetIndexBuffer(0, HA_Open, INDICATOR_DATA);
   SetIndexBuffer(1, HA_High, INDICATOR_DATA);
   SetIndexBuffer(2, HA_Low, INDICATOR_DATA);
   SetIndexBuffer(3, HA_Close, INDICATOR_DATA);
   SetIndexBuffer(4, ColorBuffer, INDICATOR_COLOR_INDEX);

   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {

   return(rates_total);

  }

解释:

我们构建数组(缓冲区)来保存修改后的烛形的开盘价、最高价、最低价和收盘价,以存储 Heikin Ashi 指标的计算值。我们还使用不同的缓冲区来控制每根蜡烛的颜色。

double HA_Open[];
double HA_High[];
double HA_Low[];
double HA_Close[];
double ColorBuffer[];

HA_Open[] 缓冲区包含每根蜡烛的 Heikin Ashi 开盘价,而 HA_High[] 包含 Heikin Ashi 蜡烛的最高价。同样,Heikin Ashi 蜡烛图的收盘价由 HA_Close[] 保存,而最低价由 HA_Low[] 记录。此外,ColorBuffer[] 用于决定每根烛形的颜色,以区分阳线(绿色)和阴线(红色)。由于这些缓冲区协同工作,图表可以保存并显示更新后的 Heikin Ashi 蜡烛图。

SetIndexBuffer(0, HA_Open, INDICATOR_DATA);
SetIndexBuffer(1, HA_High, INDICATOR_DATA);
SetIndexBuffer(2, HA_Low, INDICATOR_DATA);
SetIndexBuffer(3, HA_Close, INDICATOR_DATA);
SetIndexBuffer(4, ColorBuffer, INDICATOR_COLOR_INDEX);     

MetaTrader 5 中的 SetIndexBuffer 函数将特定的缓冲区与其对应的索引链接起来,确保 Heikin Ashi 数据得到准确处理和显示。根据该平台的烛形结构,开盘价始终分配给索引 0,最高价、最低价和收盘价分别映射到索引 1、2 和 3。如果没有正确的索引,MetaTrader 5 可能无法将数据识别为合法的蜡烛图,这可能会导致图表元素缺失或显示问题。

SetIndexBuffer(4, ColorBuffer, INDICATOR_COLOR_INDEX) 指定每根烛形的颜色,指示看涨(绿色)或看跌(红色)走势,以在视觉上区分趋势。Heikin Ashi 指标通过对这些缓冲进行适当的索引,保证了价格的正确表示和显示风格,使交易者能够快速分析趋势并做出明智的决策。

现在,我们必须在配置指标属性、定义缓冲区并将其连接到适当的索引之后,执行生成 Heikin Ashi 值的计算。

示例:

// PROPERTY SETTINGS
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots   1

// PLOT SETTINGS FOR HEIKIN ASHI CANDLES
#property indicator_label1  "Heikin Ashi"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrGreen, clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

// INDICATOR BUFFERS
double HA_Open[];
double HA_High[];
double HA_Low[];
double HA_Close[];
double ColorBuffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
// SET BUFFERS
   SetIndexBuffer(0, HA_Open, INDICATOR_DATA);
   SetIndexBuffer(1, HA_High, INDICATOR_DATA);
   SetIndexBuffer(2, HA_Low, INDICATOR_DATA);
   SetIndexBuffer(3, HA_Close, INDICATOR_DATA);
   SetIndexBuffer(4, ColorBuffer, INDICATOR_COLOR_INDEX);

   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {

   if(rates_total < 2)
      return 0; // ENSURE ENOUGH DATA

   for(int i = 1; i < rates_total; i++)  // START FROM SECOND BAR
     {
      // HEIKIN ASHI CLOSE FORMULA
      HA_Close[i] = (open[i] + high[i] + low[i] + close[i]) / 4.0;

      // HEIKIN ASHI OPEN FORMULA
      HA_Open[i] = (HA_Open[i - 1] + HA_Close[i - 1]) / 2.0;

      // HEIKIN ASHI HIGH FORMULA
      HA_High[i] = MathMax(high[i], MathMax(HA_Open[i], HA_Close[i]));

      // HEIKIN ASHI LOW FORMULA
      HA_Low[i] = MathMin(low[i], MathMin(HA_Open[i], HA_Close[i]));

      // SET COLOR: GREEN FOR BULLISH, RED FOR BEARISH
      ColorBuffer[i] = (HA_Close[i] >= HA_Open[i]) ? 0 : 1;
     }

   return(rates_total);

  }

由于第一根柱形不包含任何先前的数据,因此该方法从第二根柱形开始计算 Heikin Ashi 值。为了平滑价格波动,收盘价是当前柱形开盘价、最高价、最低价和收盘价的平均值。开盘价通过对前一根柱形的平均开盘价和收盘价取平均值,确保价格平稳过渡。当前柱形的最高价/最低价和 Heikin Ashi 的开盘价/收盘价是计算最高价和最低价的最高值和最低值。最后,如果收盘价小于或等于开盘价,烛形颜色为红色(看跌),否则为绿色(看涨)。这有助于交易者通过减少市场噪音来发现趋势。

图 6. HA 指标


2.利用 Heikin Ashi 数据创建移动平均值

既然我们已经成功生成了 Heikin Ashi 蜡烛图,下一步就是基于 Heikin Ashi 值而不是标准价格数据创建移动平均 (MA)。

伪代码:

修改指标属性

  • 将缓冲区计数从 5 调整为 6,以便为 Heikin Ashi 移动平均腾出空间。 

  • 将绘图数量从 1 改为 2,以便同时看到蜡烛图和 Heikin Ashi 移动平均。

定义 Heikin Ashi 移动平均的缓冲区

  • 创建一个缓冲区来存储 Heikin Ashi 移动平均值。
  • 定义移动平均周期数的输入变量(例如,20)。

设置 Heikin Ashi 移动平均的缓冲区

  • 将缓冲区链接到索引以存储计算的移动平均值。
  • 将绘图索引设置为从移动平均周期开始,以确保正确显示。

计算 Heikin Ashi 移动平均

  • 从(period - 1)开始循环,以确保有足够的数据点。
  • 计算最近 n 个 Heikin Ashi 收盘价的总和。
  • 将总和除以周期数,并将结果存储在缓冲区中。

示例:

// PROPERTY SETTINGS
#property indicator_separate_window
#property indicator_buffers 6
#property indicator_plots   2

// PLOT SETTINGS FOR HEIKIN ASHI CANDLES
#property indicator_label1  "Heikin Ashi"
#property indicator_type1   DRAW_COLOR_CANDLES  
#property indicator_color1  clrGreen, clrRed    
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//PROPERTIES OF THE Heikin MA
#property indicator_label2  "Heikin MA"   
#property indicator_type2   DRAW_LINE  
#property indicator_style2  STYLE_DASH 
#property indicator_width2  1          
#property indicator_color2  clrBrown   

// INDICATOR BUFFERS
double HA_Open[];
double HA_High[];
double HA_Low[];
double HA_Close[];
double ColorBuffer[]; 

double Heikin_MA_Buffer[];
int input  heikin_ma_period = 20;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
    // SET BUFFERS
    SetIndexBuffer(0, HA_Open, INDICATOR_DATA);
    SetIndexBuffer(1, HA_High, INDICATOR_DATA);
    SetIndexBuffer(2, HA_Low, INDICATOR_DATA);
    SetIndexBuffer(3, HA_Close, INDICATOR_DATA);
    SetIndexBuffer(4, ColorBuffer, INDICATOR_COLOR_INDEX);
    
    SetIndexBuffer(5, Heikin_MA_Buffer, INDICATOR_DATA);  
    PlotIndexSetInteger(5, PLOT_DRAW_BEGIN, heikin_ma_period);
     
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
    if (rates_total < 2) return 0; // ENSURE ENOUGH DATA
      
    for (int i = 1; i < rates_total; i++) // START FROM SECOND BAR
    {
        // HEIKIN ASHI CLOSE FORMULA
        HA_Close[i] = (open[i] + high[i] + low[i] + close[i]) / 4.0;

        // HEIKIN ASHI OPEN FORMULA
        HA_Open[i] = (HA_Open[i - 1] + HA_Close[i - 1]) / 2.0;

        // HEIKIN ASHI HIGH FORMULA
        HA_High[i] = MathMax(high[i], MathMax(HA_Open[i], HA_Close[i]));

        // HEIKIN ASHI LOW FORMULA
        HA_Low[i] = MathMin(low[i], MathMin(HA_Open[i], HA_Close[i]));

        // SET COLOR: GREEN FOR BULLISH, RED FOR BEARISH
        ColorBuffer[i] = (HA_Close[i] >= HA_Open[i]) ? 0 : 1;
    }    
    
     for(int i = heikin_ma_period - 1; i < rates_total; i++)
     {

      double sum = 0.0;
      for(int j = 0; j < heikin_ma_period; j++)
        {
         sum += HA_Close[i - j];
        }

      Heikin_MA_Buffer[i] = sum / heikin_ma_period;

     }

    return rates_total;
}

解释:

我们首先必须修改缓冲区和绘图设置,将 Heikin Ashi 移动平均线 (HA MA) 纳入指标中。使用 #property indicator_buffers 6,缓冲区计数从 5 增加到 6,保证有一个备用缓冲区来保存 Heikin MA 数据。通过使用 #property indicator_plots 2 将绘图计数从 1 改为 2,可以在图表上同时看到 Heikin Ashi 蜡烛图和 Heikin MA。这保证了该指标能够有效地处理这两个数据集。

然后配置 Heikin MA 绘图的属性。#property indicator_label2 是标签。通过给移动平均命名,“Heikin MA” 使其在指标列表中更加醒目。要定义移动平均线显示为线条图而不是蜡烛图,可以使用 #property indicator_type2 DRAW_LINE 设置类型。我们设置 #property indicator_style2 STYLE_DASH 使线条为虚线,并使用 #property indicator_width2 1 来确定其宽度以提高可见性。使用 #property indicator_color2 clrBrown,颜色设置为棕色,保证与 Heikin Ashi 烛形形成鲜明对比。

设置属性后,我们定义了一个输入参数 int input heikin_ma_period = 20; 允许用户更改周期数,并声明了一个数组 double Heikin_MA_Buffer[]; 来保存移动平均数据。SetIndexBuffer(5, Heikin_MA_Buffer, INDICATOR_DATA); 用于将缓冲区链接到指标,使 MetaTrader 5 能够正确管理和显示这些值。PlotIndexSetInteger(5, PLOT_DRAW_BEGIN, heikin_ma_period); 也保证只有在有足够的柱形可用时才会绘制移动平均线。

计算 Heikin Ashi 移动平均是最后一步。为了确保我们有足够的数据点进行计算,循环从 heikin_ma_period -1 开始。移动平均值是在循环中通过将最近 n 个 Heikin Ashi 收盘值相加,然后将总和除以周期数来计算的。然后,该指标将移动平均线与 Heikin Ashi 蜡烛图一起绘制,并将结果存储在 Heikin_MA_Buffer[i] 中,从而为交易者提供一个平滑的趋势跟踪工具。

图 7. HA 指标和 HA MA


3.将自定义指标集成到 EA 交易中

你可能会问,既然我们已经创建了一个自定义指标,那么如何才能用它来创建 EA 交易呢?利用上一章创建的 Heikin Ashi 指标,我们将继续在本章中采用基于项目的方法,设计一个 EA。由于该 EA 利用 Heikin Ashi 信号来自动做出交易决策,我们的技术将得以实现,成为一个可运行的交易机器人。

我们将采用基于 Heikin Ashi 移动平均线 (HA MA) 和 Heikin Ashi (HA) 蜡烛图的简单交叉策略作为我们的交易策略。这项技术通过观察 Heikin Ashi 烛形收盘价和 Heikin Ashi 移动平均线之间的关系,有助于识别潜在的趋势反转和延续迹象。Heikin Ashi 蜡烛图收盘价高于 Heikin Ashi 移动平均线,表明存在上升趋势和潜在的买入机会。相反,如果 Heikin Ashi 蜡烛图收盘价低于 Heikin Ashi 移动平均线,则会产生卖出信号,表明看跌趋势反转,并可能出现卖出机会。

图 8. 买卖逻辑

3.1.获取指标数据

将自定义指标集成到 EA 中时,首先要考虑的是如何将指标数据输入到 EA 中。如果无法获取指标值,EA 将无法根据该指标做出交易选择。例如,构成 Heikin Ashi 烛形的四个值必须导入到我们的 Heikin Ashi 移动平均交叉策略中:

  • HA 开盘价
  • HA 最高价
  • HA 最低价
  • HA 收盘价

我们的交易信号基于这些参数,这些参数将用于评估 Heikin Ashi 烛形是否向上或向下穿过 HA MA。

示例:

// Declare arrays to store Heikin Ashi data from the custom indicator
double heikin_open[];   // Stores Heikin Ashi Open prices
double heikin_close[];  // Stores Heikin Ashi Close prices
double heikin_low[];    // Stores Heikin Ashi Low prices
double heikin_high[];   // Stores Heikin Ashi High prices
double heikin_ma[];     // Stores Heikin Ashi Moving Average values

int heikin_handle;      // Handle for the custom Heikin Ashi indicator

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Ensure arrays store data in a time series format (most recent data first)
   ArraySetAsSeries(heikin_open, true);
   ArraySetAsSeries(heikin_close, true);
   ArraySetAsSeries(heikin_low, true);
   ArraySetAsSeries(heikin_high, true);
   ArraySetAsSeries(heikin_ma, true);

// Load the custom Heikin Ashi indicator and get its handle
   heikin_handle = iCustom(_Symbol, PERIOD_CURRENT, "Project7 Heikin Ashi Indicator.ex5");

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
// Nothing to clean up in this case, but can be used for resource management if needed
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Copy the latest 3 values of each buffer from the Heikin Ashi indicator
   CopyBuffer(heikin_handle, 0, 0, 3, heikin_open);  // Get HA Open values
   CopyBuffer(heikin_handle, 1, 0, 3, heikin_high);  // Get HA High values
   CopyBuffer(heikin_handle, 2, 0, 3, heikin_low);   // Get HA Low values
   CopyBuffer(heikin_handle, 3, 0, 3, heikin_close); // Get HA Close values
   CopyBuffer(heikin_handle, 5, 0, 3, heikin_ma);    // Get HA Moving Average values

// Print index 0 values to the terminal
   Print("HA Open: ", heikin_open[0],
         "\nHA High: ", heikin_high[0],
         "\nHA Low: ", heikin_low[0],
         "\nHA Close: ", heikin_close[0],
         "\nHA MA: ", heikin_ma[0]);
  }

解释:

声明用于存储 Heikin Ashi 数据的数组

将自定义指标集成到 EA 交易中的第一步是声明数组来保存从指标收集的数据。本例中声明了五个数组来存储各种 Heikin Ashi 指标的组成部分:

  • heikin_open[]:保存 Heikin Ashi 开盘价。
  • heikin_close[]:用于 Heikin Ashi 收盘价。
  • heikin_low[]:保存 Heikin Ashi 最低价。
  • heikin_high[]:Heikin Ashi 最高价。
  • heikin_ma[]:存储 Heikin Ashi 移动平均值。

因为它们使 EA 能够访问过去和当前的 Heikin Ashi 值,所以这些数组至关重要。EA 可以通过存储这些数据来评估历史价格走势并调整交易决策。这些数组不需要用固定值初始化,因为它们将由自定义指标中的数据填充;并且,它们将随着新数据的出现而动态更新。

声明指标句柄

自定义 Heikin Ashi 指标的句柄存储在变量 heikin_handle 中。MQL5 中的句柄是对后台运行的指标实例的唯一引用。因为它使 EA 能够与指标交互并根据需要请求数据,所以这个句柄至关重要。如果没有句柄,EA 就无法访问 Heikin Ashi 值。稍后,当运行 iCustom() 函数时,将会为句柄设置一个值。如果句柄无效(返回 -1),则指标未正确加载,这将阻止 EA 获取必要的数据。

时间序列格式的数组初始化

数组的构造必须保证在声明时,最新数据始终位于索引 0 处。ArraySetAsSeries() 函数用于此目的,按降序排列数组元素,以便将最新的数据保留在最前面。

该函数应用于五个数组中的每一个,如下所示:

 ArraySetAsSeries(heikin_open, true);
 ArraySetAsSeries(heikin_close, true);
 ArraySetAsSeries(heikin_low, true);
 ArraySetAsSeries(heikin_high, true);
 ArraySetAsSeries(heikin_ma, true);

通过将这些数组转换为时间序列格式,索引 0 可以让 EA 始终访问最新的 Heikin Ashi 数据。在将交易技术付诸实践时,这尤其有用,因为它保证了 EA 对当前市场波动而非历史数据做出响应。

使用 iCustom() 加载自定义 Heikin Ashi 指标

通过建立,我们需要一种访问数据的方法,以便将自定义指标纳入 EA 交易中。我们可以加载一个自定义指标,并接收一个句柄,EA 可以使用该句柄通过 iCustom() 函数请求指标值。如果没有这个句柄,EA 将无法访问指标的数据。

heikin_handle = iCustom(_Symbol, PERIOD_CURRENT, "Project7 Heikin Ashi Indicator.ex5");

  • _Symbol:这告诉该函数将指标应用于当前交易品种(例如,EUR/USD、GBP/JPY 等)。这样可以确保指标处理的数据与 EA 运行所针对的资产的数据一致。
  • PERIOD_CURRENT:这会将该指标应用于与 EA 相同的时间周期。如果 EA 在 H1 图表上运行,则该指标也将应用于 H1 图表。
  • "Project7 Heikin Ashi Indicator.ex5":这指定了 EA 应该使用的自定义指标的文件名。.ex5 扩展名表示这是一个编译后的 MQL5 指标文件。

确保指标文件保存在正确的 MetaTrader 5 目录中至关重要。指标需要位于 MQL5 目录的 Indicators 文件夹中。该目录的完整路径是:

MQL5/Indicators/

总而言之,将自定义指标连接到 EA 的主要函数是 iCustom(),它为 EA 提供了一个可以动态提取指标值的接口。如果指标没有正确放置在 Indicators 目录(或其子文件夹)中,则该函数将无法正常工作。

将指标数据复制到数组中

获取句柄后,EA 可以使用 CopyBuffer() 函数从指标中获取最新的 Heikin Ashi 值。使用 CopyBuffer() 函数将指标内部缓冲区中的数据复制到 EA 的数组中。这个 EA 会调用该方法五次,每次调用对应一部分数据:

CopyBuffer(heikin_handle, 0, 0, 3, heikin_open);  // Get HA Open values
CopyBuffer(heikin_handle, 1, 0, 3, heikin_high);  // Get HA High values
CopyBuffer(heikin_handle, 2, 0, 3, heikin_low);   // Get HA Low values
CopyBuffer(heikin_handle, 3, 0, 3, heikin_close); // Get HA Close values
CopyBuffer(heikin_handle, 5, 0, 3, heikin_ma);    // Get HA Moving Average values

每次调用 CopyBuffer() 时,都会使用相同的结构从自定义指标中获取数据。从 iCustom() 函数获取到的句柄是第一个参数 ,heikin_handle。EA 可以通过使用此句柄作为参考来访问自定义 Heikin Ashi 指标的数据。如果没有这个句柄,EA 就无法请求指标值。 Heikin Ashi 指标各组成部分的缓冲区索引由以下参数集(0、1、2、3、5)表示。在 MQL5 中,指标使用缓冲区来保存其数据,每个缓冲区都有一个唯一的索引。在这种情况下,Heikin Ashi 开盘价由缓冲区 0 表示,最高价由缓冲区 1 表示,最低价由缓冲区 2 表示,收盘价由缓冲区 3 表示,Heikin Ashi 移动平均由缓冲区 5 表示。我们通过提供这些索引来保证从指标中获得正确的数据。

根据第三个选项,数据应该从最近的柱线(当前烛形)开始复制,即 0。这保证了 EA 始终使用最新的市场数据,这对于实时做出交易决策至关重要。根据第四个参数 3,需要复制三个数据点。多值检索使 EA 能够检查历史和当前的 Heikin Ashi 数据,这有助于发现模式或验证趋势。 

最后,关联的数组 heikin_open、heikin_high、heikin_low、heikin_close 和 heikin_ma 用于保存恢复的数据。检索到的值存储在这些数组中,以便 EA 可以在其交易逻辑中处理和利用它们。为了让 EA 能够根据价格趋势做出明智的交易决策,它必须掌握 Heikin Ashi 指标的最新信息。

3.2.在我们的交易策略中使用指标数据

在学习了如何从自定义指标中提取数据之后,我们现在必须了解如何将这些数据应用到我们的交易策略中。这将使我们能够在 EA 中成功使用 Heikin Ashi 指标,并展示如何根据检索到的数据做出交易决策。此外,这个过程提供了一个机会来调查某些重要的 MQL5 思想,这在我们阅读本文时增强了我们的理解。 

然而,我们不能在不考虑 EA 关键要素的情况下简单地应用推理。适当的商务管理需要结合具体特征。这些措施包括密切关注未平仓数量、设置追踪止损点、控制风险参数,以及确保 EA 有条不紊地执行交易。通过包含这些基本要素,我们构建了一个更可靠、更有用的交易系统,能够在现实环境中正常运行。

3.2.1.在 EA 交易中跟踪未平仓的买入和卖出头寸

为了使这种交易技巧有效运作,我们必须确保同时只开立一个买入和一个卖出头寸。这减少了来自同一方向多个头寸的不必要风险,有助于维持对头寸执行的控制。EA 可以通过监控未平仓头寸的数量来决定是建立新头寸还是等待现有头寸平仓后再执行另一个头寸。这种方法保证了策略按计划执行,并增强了仓位管理。

示例:

int totalPositions = 0;
int position_type_buy = 0;
int position_type_sell = 0;

for(int i = 0; i < PositionsTotal(); i++)
  {
   ulong ticket = PositionGetTicket(i);

   if(PositionSelectByTicket(ticket))
     {
      if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(ChartID())
     {
      totalPositions++;
      if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
           {
            position_type_buy++;
           }
         if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
           {
            position_type_sell++;
           }
        }
     }
  }

EA 会统计买入、卖出和总交易次数,以跟踪未平仓头寸。它会遍历所有未平仓位,将它们分类为买入或卖出,并使用幻数和图表交易品种来确认它们是否符合策略。通过一次只允许一笔买入交易和一笔卖出交易,这保证了对仓位管理的可控性。

3.2.2.在 EA 交易中跟踪每日交易限额

本节主要讨论特定交易时段内的交易总额。EA 交易通过设定预定的时间段并统计买卖交易,确保策略不会超过每日允许的交易限额。通过避免在规定时间内过度交易,该策略有助于保持可控的交易行为。

示例:

input int daily_trades = 6; // Total Daily Trades

// Start and end time for
string start = "00:00";
string end = "23:50";

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

   datetime start_time = StringToTime(start);
   datetime end_time = StringToTime(end);
   bool success = HistorySelect(start_time, end_time);

// Getting total trades
   int totalDeal = 0;
   int deal_type_buy = 0;
   int deal_type_sell = 0;
   if(success)
     {
      for(int i = 0; i < HistoryDealsTotal(); i++)
        {
         ulong ticket = HistoryDealGetTicket(i);

         if(HistoryDealGetInteger(ticket, DEAL_MAGIC) == MagicNumber && HistoryDealGetString(ticket,DEAL_SYMBOL) == ChartSymbol(chart_id))
           {
            if(HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_IN)
              {
               totalDeal++;
               if(HistoryDealGetInteger(ticket, DEAL_TYPE) == DEAL_TYPE_BUY)
                 {

                  deal_type_buy++;

                 }
               if(HistoryDealGetInteger(ticket, DEAL_TYPE) == DEAL_TYPE_SELL)
                 {

                  deal_type_sell++;

                 }
              }
           }
        }
     }
  }

解释:

EA 交易系统首先将指定的开始时间和结束时间转换为日期时间格式,以便跟踪给定交易时段内进行的交易总数。然后使用 HistorySelect() 函数选择该时间范围内的交易历史记录。如果选择成功,EA 会设置计数器来记录执行的交易总数以及买入和卖出交易的数量。

然后,EA 通过遍历交易历史记录来提取每笔交易的单号。为了确保只统计相关的交易,它会验证交易是否使用与当前图表相同的幻数和交易品种开仓。如果交易被认定为入场交易,则交易总数增加。EA 在决定是否下单后,会更新相应的计数器。这样可以确保该策略监控已执行的交易,并防止超过每日交易限额。

3.2.3.防止在同一根烛形内重复交易

在某些情况下,如果您希望新交易仅以新烛形的开盘价完成,EA 可能会在平掉现有仓位后立即开立新交易。如果交易条件保持不变,这一点尤其如此。由此可能导致同一根烛形内出现无意的连续交易。在本部分,我们将探讨阻止这种情况的方法,包括确保交易仅以下一根柱形的开盘价执行、改进交易控制以及防止无意义的重新入场。

示例:

// Declare an array to store time data
datetime time[];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Ensure the time array is structured as a time series (most recent data first)
   ArraySetAsSeries(time, true);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Retrieve the latest three time values from the current symbol and timeframe
   CopyTime(_Symbol, PERIOD_CURRENT, 0, 3, time);

// Select trading history from the earliest recorded time to the current time
   bool trade_control = HistorySelect(time[0], TimeCurrent());

// Variable to count the number of closed trades
   int total_deals_out = 0;

// If the trading history selection is successful, process the data
   if(trade_control)
     {
      // Loop through all closed trades in the history
      for(int i = 0; i < HistoryDealsTotal(); i++)
        {
         // Get the ticket number of the historical trade
         ulong ticket = HistoryDealGetTicket(i);

         // Check if the trade matches the current EA's Magic Number and symbol
         if(HistoryDealGetInteger(ticket, DEAL_MAGIC) == MagicNumber && HistoryDealGetString(ticket, DEAL_SYMBOL) == ChartSymbol(chart_id))
           {
            // If the trade was an exit trade, increment the counter
            if(HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_OUT)
              {
               total_deals_out++;
              }
           }
        }
     }
  }

解释:

此功能可防止 EA 在平仓后在同一根烛形内开仓。使用 CopyTime() 函数(该函数会生成最近柱形的时间戳),该过程首先从图表中检索最新的时间值。接下来,EA 从第一个记录的柱形时间(time[0])到当前市场时间(TimeCurrent()),选择该时间范围内的交易历史。

为了找到与 EA 的幻数和交易品种相匹配的已平仓交易,它会遍历交易历史记录。如果发现已平仓的交易(尤其是退出交易 (DEAL_ENTRY_OUT)),则 total_deals_out 计数器会增加。如果最近在同一根烛形内有一笔交易平仓,这个计数器或许能帮您找到答案。为了确保交易只在新烛形开始时执行,并避免不必要的立即重新入场,EA 将等到新的烛形出现后才会发起新的交易。

3.2.4.利用 Heikin Ashi 实施风险管理和交易执行

为了保证买卖交易按照 Heikin Ashi 信号执行,我们将在本节中规定交易执行的要求。此外,通过根据预先确定的每笔交易的美元风险和风险回报率(RRR)确定手数大小,我们将整合风险管理。这一策略避免了过度的风险暴露,同时保留了受控交易。

示例:

input  double dollar_risk = 12.0; // How Many Dollars($) Per Trade?
input double RRR = 3;

double ask_price;
double lot_size;
double point_risk;
double take_profit;

// Variable to store the time of the last executed trade
datetime lastTradeBarTime = 0;

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

// Get the opening time of the current bar
   datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);

// Check conditions for opening a buy position
// Ensures Heikin Ashi candle crosses above the moving average
// Limits trades per day and prevents multiple trades in the same candlestick
   if(heikin_open[1] < heikin_ma[1] && heikin_close[1] > heikin_ma[1] &&
      deal_type_buy < (daily_trades / 2) && total_deals_out < 1 &&
      totalPositions < 1 && currentBarTime != lastTradeBarTime)
     {
      // Calculate risk in points (distance from entry to stop loss)
      point_risk = ask_price - heikin_low[1];

      // Calculate take profit based on risk-to-reward ratio (RRR)
      take_profit = ((ask_price - heikin_low[1]) * RRR) + ask_price;

      // Determine lot size based on the dollar risk per trade
      lot_size = CalculateLotSize(_Symbol, dollar_risk, point_risk);

      // Execute a buy trade
      trade.Buy(lot_size, _Symbol, ask_price, heikin_low[1], take_profit);

      // Store the current bar time to prevent multiple trades in the same candle
      lastTradeBarTime = currentBarTime;
     }

// Check conditions for opening a sell position
// Ensures Heikin Ashi candle crosses below the moving average
// Limits trades per day and prevents multiple trades in the same candlestick
   if(heikin_open[1] > heikin_ma[1] && heikin_close[1] < heikin_ma[1] &&
      deal_type_sell < (daily_trades / 2) && total_deals_out < 1 &&
      totalPositions < 1 && currentBarTime != lastTradeBarTime)
     {
      // Calculate risk in points (distance from entry to stop loss)
      point_risk = heikin_high[1] - ask_price;

      // Calculate take profit based on risk-to-reward ratio (RRR)
      take_profit = MathAbs(((heikin_high[1] - ask_price) * RRR) - ask_price);

      // Determine lot size based on the dollar risk per trade
      lot_size = CalculateLotSize(_Symbol, dollar_risk, point_risk);

      // Execute a sell trade
      trade.Sell(lot_size, _Symbol, ask_price, heikin_high[1], take_profit);

      // Store the current bar time to prevent multiple trades in the same candle
      lastTradeBarTime = currentBarTime;
     }
  }

//+------------------------------------------------------------------+
//| Function to calculate the lot size based on risk amount and stop loss
//+------------------------------------------------------------------+
double CalculateLotSize(string symbol, double riskAmount, double stopLossPips)
  {
// Get symbol information
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
   double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);

// Calculate pip value per lot
   double pipValuePerLot = tickValue / point;

// Calculate the stop loss value in currency
   double stopLossValue = stopLossPips * pipValuePerLot;

// Calculate the lot size
   double lotSize = riskAmount / stopLossValue;

// Round the lot size to the nearest acceptable lot step
   double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
   lotSize = MathFloor(lotSize / lotStep) * lotStep;

   return lotSize;
  }

解释:

为了验证趋势,交易执行逻辑取决于 Heikin Ashi 蜡烛图与移动平均线(MA)的交叉。当开盘价低于 MA、收盘价高于 MA 时表示看涨反转,反之则表示看跌反转。系统限制每日交易,确保没有未平仓,只在新烛形上进行交易,以阻止过度交易。风险管理的组成部分包括:在 Heikin Ashi 前期高点或低点设置止损、根据风险回报比 (RRR) 进行获利了结,以及动态调整交易手数。

3.2.5.使用 Heikin Ashi 烛形设置追踪止损

本节的主要目标是实现使用 Heikin Ashi 烛形的追踪止损机制。它确保止损位能够根据 Heikin Ashi 价格的变化动态调整。

示例:

input bool    allow_trailing  = false; // Do you Allow Trailing Stop?

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

   // Check if trailing stop is enabled
   if(allow_trailing == true)
     {
      // Variables to store trade-related information
      double positionProfit = 0;
      double positionopen = 0;
      double positionTP = 0;
      double positionSL = 0;

      // Loop through all open positions
      for(int i = 0; i < PositionsTotal(); i++)
        {
         // Get the ticket number of the position
         ulong ticket = PositionGetTicket(i);

         // Select the position using its ticket number
         if(PositionSelectByTicket(ticket))
           {
            // Check if the position belongs to the EA by verifying the magic number and symbol
            if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id))
              {
               // Retrieve trade details: open price, take profit, profit, and stop loss
               positionopen = PositionGetDouble(POSITION_PRICE_OPEN);
               positionTP = PositionGetDouble(POSITION_TP);
               positionProfit = PositionGetDouble(POSITION_PROFIT);
               positionSL = PositionGetDouble(POSITION_SL);

               // Apply trailing stop logic for buy positions
               if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                 {
                  // Adjust stop loss if Heikin Ashi low is above the entry price and the candle is bullish
                  if(heikin_low[1] > positionopen && heikin_close[1] > heikin_open[1])
                    {
                     trade.PositionModify(ticket, heikin_low[1], positionTP);
                    }
                 }

               // Apply trailing stop logic for sell positions
               if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
                 {
                  // Adjust stop loss if Heikin Ashi high is below the entry price and the candle is bearish
                  if(heikin_high[1] < positionopen && heikin_close[1] < heikin_open[1])
                    {
                     trade.PositionModify(ticket, heikin_high[1], positionTP);
                    }
                 }
              }
           }
        }
     }
  }

解释:

为了确保利润并动态调整止损水平,本节使用 Heikin Ashi 烛形来实现追踪止损系统。allow_trailing 输入变量控制是否启用追踪止损功能;如果设置为 true,系统将遍历所有未平仓位,检索其详细信息,并通过检查幻数和交易品种来验证它们是否属于 EA。为了简化追踪止损逻辑,提取关键交易信息,包括开盘价、止盈价、利润和止损价。

通过验证前一个 Heikin Ashi 低点高于入场价,以及 Heikin Ashi 收盘价高于开盘价,该系统验证了持仓情况,并确认了上涨趋势。为了在允许价格进一步上涨的同时保护利润,如果价格达到 Heikin Ashi 低点,则止损位将设置在 Heikin Ashi 低点。通过确保前一个 Heikin Ashi 最高价低于入场价,并且 Heikin Ashi 收盘价低于开盘价,可以验证卖出交易的下跌趋势。为了保障交易安全并捕捉更多市场波动,如果满足以下条件,止损位将设置在 Heikin Ashi 高点。


结论

在本文中,我们从零开始构建了一个 Heikin Ashi 指标,将其与使用 Heikin Ashi 烛形数据的移动平均线集成,并探讨了如何将自定义指标集成到 EA 中,从而确保自动交易的无缝集成。我们实施了风险管理技术,例如限制每日交易次数、防止在同一根烛形内多次平仓以及动态计算手数。此外,我们还引入了使用 Heikin Ashi 蜡烛图来调整止损水平的追踪止损机制。这些概念为在 MQL5 中构建和完善自动化交易策略提供了坚实的基础。

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/17296

最近评论 | 前往讨论 (2)
dhermanus
dhermanus | 31 5月 2025 在 11:08

你好,伊斯雷尔、

感谢您在博客上花费时间和精力。


我想请教关于 Heikin Ashi自定义指标 代码的问题。

关于 Heikin Ashi Open 公式 :

// HEIKIN ASHI OPEN FORMULA
HA_Open[i] = (HA_Open[i - 1] + HA_Close[i - 1]) / 2.0;

由于您没有计算 HA_Open[i-1)。这不就是 0 吗?


我的建议是 :


if (i == 1){

HA_Open[i] = (open[i - 1] + close[i - 1])/2.0; // 在 HA 第一条杠上,使用正常的开仓/收仓数据即可。

}

else{

// heikin ashi 开仓公式

HA_Open[i] = (HA_Open[i - 1] + HA_Close[i - 1]) / 2.0;


}

Israel Pelumi Abioye
Israel Pelumi Abioye | 31 5月 2025 在 12:28
dhermanus 自定义指标 代码的问题。

关于 Heikin Ashi Open 公式 :

// HEIKIN ASHI OPEN FORMULA
HA_Open[i] = (HA_Open[i - 1] + HA_Close[i - 1]) / 2.0;

由于您没有计算 HA_Open[i-1)。这个值不是 0 吗?


我的建议 :


if (i == 1){

HA_Open[i] = (open[i - 1] + close[i - 1])/2.0; // 在 HA 第一条杠上只需使用正常的开仓/收仓数据

}

else{

// Heikin ashi 开放公式

HA_Open[i] = (HA_Open[i - 1] + HA_Close[i - 1]) / 2.0;


}

谢谢。我会考虑的
在 MQL5 中创建交易管理员面板(第十部分):基于外部资源的界面 在 MQL5 中创建交易管理员面板(第十部分):基于外部资源的界面
今天,我们将深入挖掘 MQL5 的潜力,利用外部资源(例如 BMP 格式的图片)为交易管理面板打造独具风格的主界面。文中演示的策略在打包多种资源(包括图片、声音等)以实现高效分发时尤为实用。欢迎随我们一起探讨,如何利用这些功能为我们的 New_Admin_Panel EA 实现现代、美观的界面设计。
纯 MQL5 货币对强弱指标 纯 MQL5 货币对强弱指标
我们将在 MQL5 中开发货币强势分析的专业指标。这本分步指南将向你展示如何为 MetaTrader 5 开发一款功能强大的交易工具,该工具带有可视化仪表板。您将学习如何计算多个时间周期(H1、H4、D1)内货币对的强度,实现动态数据更新,并创建用户友好的界面。
基于MQL5中表模型的表类和表头类:应用MVC概念 基于MQL5中表模型的表类和表头类:应用MVC概念
本文是致力于使用MVC(模型-视图-控制器)架构范式在MQL5中实现表模型系列文章的第二部分。本文基于先前创建的表模型来开发表类和表头。已经开发的类将构成进一步实现视图和控制器组件的基础,这些内容将在随后的文章中讨论。
交易中的资本管理和带有数据库的交易者家庭会计程序 交易中的资本管理和带有数据库的交易者家庭会计程序
交易者如何管理资金?交易者和投资者如何跟踪支出、收入、资产和负债?我不仅要向你介绍会计软件;我将向您展示一个工具,它可能会成为您在波涛汹涌的交易海洋中可靠的金融导航器。