网络上有成百上千可用于 MetaTrader 平台的自定义指标。如果您决定在您的EA中使用这类非平台内置的自定义指标,那么就需要进行一些额外的工作。
- 获取源代码的重要性:在使用自定义指标时,如果能拥有其 .mq4源代码文件无疑是最佳情况。尽管在没有源代码的情况下(即只有编译后的.ex4或.ex5文件)理论上也可以调用自定义指标,但拥有源代码能让您更容易地理解该指标的内部逻辑,特别是对于确定iCustom()函数中Mode参数(代表不同的指标数据输出缓冲区)的正确缓冲区索引 (buffer indexes) 至关重要。
iCustom() 函数:
MQL4提供了一个专门的内置函数 iCustom() 来处理和调用自定义指标。其基本语法如下:
double iCustom(
   string symbol,        // 品种名称 (NULL 或 Symbol() 表示当前品种)
   int    timeframe,     // 时间周期 (PERIOD_H1 或 0 表示当前周期)
   string indicator_name, // 自定义指标的文件名 (不含扩展名)
   ... indicator_inputs, // 自定义指标的输入参数 (按顺序,类型需匹配)
   int    mode,          // 要获取的指标缓冲区索引 (0, 1, 2, ...)
   int    shift          // K线柱位移 (0为当前K线, 1为前一根, ...)
);
参数详解:
- 
symbol,timeframe,mode,shift: 这些参数的含义与本章前面讨论标准指标函数(如iMA(),iStochastic())时介绍的完全相同。- mode: 对于- iCustom(),此参数指的是您希望从自定义指标中获取哪一个数据缓冲区 (buffer) 的值。自定义指标可以输出多条线或多种数据,每条线/数据通常对应一个缓冲区,索引从 0 开始(0 代表第一个缓冲区,1 代表第二个,依此类推)。
- shift: 指定获取的是哪根 K 线上的指标缓冲区值。
 
- 
indicator_name:string类型,这是您要调用的自定义指标的文件名(不包含.mq4或.ex5扩展名),并且必须与该指标文件在 MetaTrader 平台的“导航器”窗口下“自定义指标”列表中显示的名称完全一致(包括大小写和空格)。- 例如:"Slope Direction Line"或者"super_signal"。
 
- 例如:
- 
... indicator_inputs(指标的输入参数):- 这是 iCustom()函数参数列表中的一个可变部分。您需要在这里按顺序、逐个填入您要调用的那个自定义指标所定义的所有外部输入参数 (extern variables)。
- 如何确定这些参数?
- 最佳方式 (有源代码): 打开自定义指标的 .mq4源代码文件,查看其文件开头部分声明的extern变量。这些变量的声明顺序、数据类型和默认值就是您在iCustom()中需要传入的。
- 次选方式 (无源代码):
- 在图表上手动加载该自定义指标。
- 打开该指标的“属性”对话框,切换到“输入参数 (Inputs)”选项卡。
- 这个选项卡会列出该指标所有可配置的输入参数及其当前值。您需要记录下这些参数的顺序和数据类型(通常可以通过参数名称或对话框中参数旁边的图标来大致判断其数据类型,例如 int,double,bool,string,color等)。
- 在调用 iCustom()时,您需要按照在“输入参数”选项卡中看到的顺序,将这些参数的值填入到indicator_name和mode参数之间。
 
 
- 最佳方式 (有源代码): 打开自定义指标的 
 
- 这是 
总结: 调用 iCustom() 的关键在于正确填写自定义指标的文件名、其所有输入参数(确保顺序、类型和值正确)以及您希望获取的数据缓冲区索引 (mode)。拥有指标的源代码能极大地方便这一过程,尤其是在确定不同数据线对应的 mode 值时。
确定自定义指标参数的便捷方法
如果您拥有自定义指标的 .mq4 源代码文件,那么确定其输入参数及其顺序会变得非常简单。只需打开该源代码文件,在其文件开头部分通常会有一系列以 extern 关键字声明的变量。这些 extern 变量就是该自定义指标暴露给用户的所有输入参数,它们的声明同时指定了参数名称、数据类型和默认值。
您可以直接将这些 extern 变量的声明从指标的源代码复制到您的 EA 的外部变量声明区域,这样就可以在 EA 的输入参数面板中方便地调整这些值了。
iCustom() 函数参数匹配规则 在调用 iCustom() 函数时,务必遵守以下规则:
- 一一对应:自定义指标源代码中声明的每一个 extern变量(即每一个输入参数),都必须在iCustom()函数的相应位置有一个对应的参数值被传入。
- 顺序一致:这些参数在 iCustom()函数调用中出现的顺序,必须与它们在自定义指标源代码中extern变量声明的顺序完全一致。
- 使用常量:对于那些您不打算通过 EA 输入参数来改变的自定义指标设置(例如,某些固定的信息性字符串、描述性文本,或者一些您认为不需要调整的非核心设置),您可以在 iCustom()调用中直接使用常量值(例如,直接写入数字、字符串或 MQL 预定义常量)。
示例:调用 “Slope Direction Line” 自定义指标
假设有一个流行的自定义指标名为 “Slope Direction Line”,在其 .mq4 源代码文件中,其输入参数声明如下:
//---- input parameters (指标源代码中的外部输入参数)
extern int period=80;        // 周期
extern int method=3;         // MA 计算方法 (注释说明 3 代表 MODE_SMA)
extern int price=0;          // 应用价格 (注释说明 0 代表 PRICE_CLOSE)
为了在我们的 EA 中调用这个指标,并使其参数可配置,我们可以在 EA 中也声明相应的外部变量(可以使用不同的变量名,但数据类型应匹配):
// EA 中的外部变量 (用于配置 Slope Direction Line 指标)
extern int ExtSlopePeriod = 80;    // 对应指标的 period
extern int ExtSlopeMethod = 3;     // 对应指标的 method (例如 MODE_SMA)
extern int ExtSlopePrice  = 0;     // 对应指标的 price (例如 PRICE_CLOSE)
那么,在 EA 中调用 iCustom() 函数来获取该指标的值时,代码如下所示:
// 调用 iCustom 获取 Slope Direction Line 指标的值
// 假设我们想获取第一个缓冲区(mode=0)在当前K线(shift=0)的值
double slope_value = iCustom(
    NULL,                     // symbol: 当前图表品种
    0,                        // timeframe: 当前图表周期
    "Slope Direction Line",   // indicator_name: 指标文件名 (不含扩展名)
    ExtSlopePeriod,           // 第1个输入参数: 对应指标的 period
    ExtSlopeMethod,           // 第2个输入参数: 对应指标的 method
    ExtSlopePrice,            // 第3个输入参数: 对应指标的 price
    0,                        // mode: 要获取的指标缓冲区索引 (0 代表第一个缓冲区)
    0                         // shift: K线柱位移 (0 代表当前K线)
);
- 参数解释:
- NULL和- 0分别表示使用当前图表的交易品种和时间周期。
- "Slope Direction Line"是该自定义指标的文件名。
- ExtSlopePeriod,- ExtSlopeMethod,- ExtSlopePrice是我们 EA 中定义的外部变量,它们的值将作为输入传递给自定义指标。
- mode设为- 0,表示我们希望获取该指标的第一个数据缓冲区的值。
- shift设为- 0,表示获取的是当前 K 线上的指标值。
 
理解自定义指标的缓冲区 (Mode 参数)
以上述 “Slope Direction Line” 指标为例,尽管它在图表上可能看起来只显示为一条变色的线,但它实际上可能利用了两个不同的数据缓冲区 (buffers) 来实现这种效果。指标的颜色变化(例如,上涨时为蓝色,下跌时为红色)通常是通过分别在不同的缓冲区中绘制不同状态下的线条来实现的。
- 通过数据窗口观察:如果您将 “Slope Direction Line” 指标加载到图表上,然后打开 MetaTrader 的“数据窗口 ”(通常通过快捷键 Ctrl+D或在“显示”菜单中找到),当您将鼠标悬停在图表的 K 线上时,数据窗口会显示该 K 线上所有指标的计算值。
- 多缓冲区表现:对于 “Slope Direction Line” 这类指标,您可能会在数据窗口中看到它有两个对应的数值输出:
- 第一个值(例如,对应 mode = 0):可能只在指标指示上涨趋势时才显示价格数值(指标线为蓝色时),而在下跌趋势时显示空值 (EMPTY_VALUE) 或某个特定值。
- 第二个值(例如,对应 mode = 1):可能只在指标指示下跌趋势时才显示价格数值(指标线为红色时),而在上涨趋势时显示空值。
 
- 第一个值(例如,对应 
因此,当使用 iCustom() 调用这类多缓冲区的自定义指标时,正确选择 mode 参数(即您想获取哪个缓冲区的数据)至关重要。如果您有指标的源代码,可以从中直接了解到各个缓冲区的作用和索引。如果没有源代码,数据窗口是一个非常有用的工具,可以帮助您观察和推断不同 mode 值对应的是指标的哪部分数据。
确定自定义指标的缓冲区索引 (Mode 参数)
当调用自定义指标,特别是那些在图表上绘制多条线或输出多种数据的指标时,正确设置 iCustom() 函数中的 Mode 参数(即缓冲区索引)至关重要。Mode 参数告诉 iCustom() 我们想要获取的是指标的哪一部分数据。
1. 从指标源代码中寻找线索 (最佳方法)
确定正确 Mode 索引的最简单直接的方法是查看该自定义指标的 .mq4 源代码文件。关键信息通常位于指标的 OnInit() 函数中,具体关注以下两个函数调用:
- SetIndexBuffer(int index, double buffer[]): 这个函数用于将一个数组(例如- UptrendBuffer)与一个特定的指标缓冲区索引(- index,从0开始)关联起来。这个- index就是我们在- iCustom()中需要用作- Mode参数的值。
- SetIndexStyle(int index, int type, ...): 这个函数设置特定索引的缓冲区的绘图样式(例如线型、颜色、宽度)。虽然它不直接给出- Mode值,但结合- SetIndexBuffer和数组名称,可以帮助我们推断哪个缓冲区对应图表上的哪条线。
示例分析 (假设某个自定义指标的函数中有如下代码):
// 指标源代码 init() 函数中的示例片段
SetIndexBuffer(0, UptrendBuffer);      // 缓冲区 0 关联到 UptrendBuffer 数组
SetIndexBuffer(1, DowntrendBuffer);    // 缓冲区 1 关联到 DowntrendBuffer 数组
SetIndexBuffer(2, CalculationHelperBuffer); // 缓冲区 2 可能仅用于内部计算
// ... 其他 SetIndex... 调用 ...
SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2); // 设置缓冲区 0 (UptrendBuffer) 画实线
SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 2); // 设置缓冲区 1 (DowntrendBuffer) 画实线
// 注意:缓冲区 2 (CalculationHelperBuffer) 可能没有对应的 SetIndexStyle,或者类型为 DRAW_NONE
- 推断:
- 从 SetIndexBuffer(0, UptrendBuffer)和数组名UptrendBuffer,我们可以合理推断,缓冲区索引0对应的是代表上升趋势的数据。
- 类似地,缓冲区索引 1对应的是代表下降趋势的数据 (DowntrendBuffer)。
- 缓冲区 2(CalculationHelperBuffer) 如果没有在图表上绘制(例如没有对应的SetIndexStyle,或者样式是DRAW_NONE),那么它很可能只是指标内部计算用的辅助缓冲区,通常不作为iCustom()的目标Mode。
 
- 从 
2. 在EA中调用 iCustom() (以上述 “Slope Direction Line” 为例)
基于上述分析(假设 “Slope Direction Line” 的上升趋势线对应缓冲区 0,下降趋势线对应缓冲区 1),我们在 EA 中调用 iCustom() 时应如下设置:
// 假设 ExtSlopePeriod, ExtSlopeMethod, ExtSlopePrice 是 EA 中定义的外部输入参数
// 获取前一根K线(shift=1)的 "Slope Direction Line" 指标值
// 获取上升趋势线的值 (使用 mode = 0)
double slopeUpValue = iCustom(NULL, 0, "Slope Direction Line",
                              ExtSlopePeriod, ExtSlopeMethod, ExtSlopePrice,
                              0, // mode: 缓冲区索引 0 (对应 UptrendBuffer)
                              1  // shift: 获取前一根K线的值
                             );
// 获取下降趋势线的值 (使用 mode = 1)
double slopeDownValue = iCustom(NULL, 0, "Slope Direction Line",
                                ExtSlopePeriod, ExtSlopeMethod, ExtSlopePrice,
                                1, // mode: 缓冲区索引 1 (对应 DowntrendBuffer)
                                1  // shift: 获取前一根K线的值
                               );
- 注意:
- Mode参数分别设为- 0和- 1来获取不同的数据线。
- Shift参数设为- 1,表示我们获取的是前一根已完成 K 线上的指标值。
 
3. 验证 Mode 参数的正确性 (重要步骤)
强烈建议您通过实际测试来验证为 iCustom() 选择的 Mode 参数是否正确。
- 使用 Print()函数: 在您的 EA 中,紧随iCustom()调用之后,添加Print()语句来输出获取到的指标值。Print("Slope Up (bar 1): ", slopeUpValue, ", Slope Down (bar 1): ", slopeDownValue, ", Time of bar 1: ", TimeToStr(Time[1])); // Time[1] 是前一根K线的开盘时间
- 策略测试器回测:
- 在策略测试器中运行您的 EA,建议使用“仅开盘价”测试模型(对于检查K线完成时的指标值通常足够)。
- 确保 iCustom()中的Shift参数设置为1(或其他您想验证的K线柱)。
 
- 查看日志: EA 运行后,在策略测试器的“日志 (Journal)”选项卡中查看 Print()函数的输出。- 示例日志输出: Slope Up (bar 1): 2147483647.00000000, Slope Down (bar 1): 1.50483900, Time of bar 1: 2009.11.26 16:00
- 解读 EMPTY_VALUE:2147483647(或EMPTY_VALUE常量,对于double类型通常是DBL_MAX) 是 MQL 中表示指标缓冲区在该点“无有效值”的特殊数值。对于 “Slope Direction Line” 这类指标,当趋势向上时,下降趋势线缓冲区可能返回EMPTY_VALUE;反之亦然。
 
- 示例日志输出: 
- 与图表数据窗口对比:
- 在策略测试器窗口中点击“打开图表”按钮。这会打开一个测试过程中的图表,并且您的自定义指标通常会自动加载上去。
- 在图表上找到日志中打印出的 Time所对应的那根 K 线。
- 打开“数据窗口”(快捷键 Ctrl+D),将鼠标悬停在该 K 线上。
- 仔细对比数据窗口中该指标显示的值与您日志中打印出的 slopeUpValue和slopeDownValue。
- 如果不匹配,回到 iCustom()函数调用处,尝试调整Mode参数(例如,如果Mode=0得到的值不对,试试Mode=1,以此类推),然后重新运行测试,直到日志输出与数据窗口显示一致为止。
 
4. 在 EA 中使用 “Slope Direction Line” 指标构建交易逻辑
根据该指标的设计原理:
- 当趋势向上时,SlopeUp(假设对应上涨缓冲区)会返回一个有效的价格水平值,而SlopeDown(对应下跌缓冲区)会返回EMPTY_VALUE。
- 当趋势向下时,情况相反:SlopeUp返回EMPTY_VALUE,而SlopeDown返回有效价格。
因此,交易条件可以这样设置:
if (slopeUpValue != EMPTY_VALUE && slopeDownValue == EMPTY_VALUE)
{
    // 上升趋势条件满足 (例如,产生买入信号)
}
if (slopeUpValue == EMPTY_VALUE && slopeDownValue != EMPTY_VALUE)
{
    // 下降趋势条件满足 (例如,产生卖出信号)
}
这些条件实质上是检查哪条数据线(哪个缓冲区)当前有值,哪条没有,从而判断趋势方向。


 
			


