创建指标实例的简单方法:iCustom
MQL5 提供了以下两个用于从程序中创建指标实例的函数:iCustom和IndicatorCreate。第一个函数涉及传递参数列表,这些参数必须在程序编译时就已知。第二个函数允许在程序执行过程中利用所调用指标的参数动态形成数组。这种高级模式将在 指标创建的高级方式:IndicatorCreate章节中讨论。
int iCustom(const string symbol, ENUM_TIMEFRAMES timeframe, const string pathname, ...)
该函数为指定交易品种和时间范围创建一个指标。在 symbol参数中,NULL 可用于表示当前图表的交易品种,而在 timeframe 中,0 用于设置当前周期。
在pathname参数中,指定指标名称(不含扩展名的 ex5 文件名),也可选择性指定路径。路径的更多细节详见下文说明。
pathname引用的指标必须已编译。
该函数将返回指标句柄,如果发生错误,则返回 INVALID_HANDLE。要调用本章所述且属于指标程序控制组的其他函数时,需要使用该句柄。该句柄是一个整数,用于在调用程序中唯一标识所创建的指标实例。
iCustom函数原型中的省略号表示该指标的实际参数列表。这些参数的类型和顺序必须与(指标代码中)的形式参数一致。但允许从参数列表末尾开始省略部分值。对于调用代码中未指定的参数,创建的指标将使用对应 input参数的默认值。
例如,如果指标接受两个输入变量:周期 (input int WorkPeriod = 14) 和价格类型 (input ENUM_APPLIED_PRICE WorkPrice = PRICE_CLOSE),则可以通过以下不同详细程度调用 iCustom:
- iCustom(_Symbol, _Period, 21, PRICE_TYPICAL):为整个列表的参数设置值
- iCustom(_Symbol, _Period, 21):设置第一个参数,第二个参数被省略,其将接收值 PRICE_CLOSE
- iCustom(_Symbol, _Period):两个参数均被省略,将获取 14 和 PRICE_CLOSE
不能省略参数列表开头或中间的参数。
如果创建的指标采用简版 OnCalculate,则最后一个额外参数(除指标内部定义的输入变量列表外)可以是用于构建指标的价格类型。这类似于指标特性对话框中的Apply to下拉列表。此外,在该额外参数中,你可以传递一个先前创建的其他指标的句柄(见下文示例)。在这种情况下,新创建的指标将使用指定句柄的第一个指标缓冲区进行计算。换言之,程序员可以设置一个指标基于另一个指标进行计算。
MQL5 未提供编程方式来查询特定第三方指标是采用简版还是完整版 OnCalculate实现,即在通过iCustom 创建指标时是否允许传递额外句柄。此外,如果通过额外句柄指定的指标包含多个缓冲区,MQL5 不允许选择缓冲区编号。
我们回到 pathname参数。
路径是包含至少一个反斜杠 ('\') 或正斜杠 ('/') 的字符串,它们是文件系统中用作文件夹和文件层级结构分隔符的特殊字符。你可以使用正斜杠或反斜杠,但反斜杠需要进行“转义”,即必须书写两次。这是因为反斜杠是一种控制字符,可构成许多服务代码,例如制表符 ('\t')、换行符 ('\n') 等等(请参阅 字符类型章节)。
如果路径以斜杠开头,则称为绝对路径,其根目录为所有 MQL5 源代码的目录。例如,在 pathname参数中指定字符串 "/MyIndicator",将查找 MQL5/MyIndicator.ex5 文件,带目录 "/Exercise/MyIndicator" 的较长路径则对应 MQL5/Exercise/MyIndicator.ex5。
如果pathname参数包含一个或多个斜杠但不以斜杠开头,则该路径称为相对路径,因为此时它被视为相对于两个预定义位置之一。首先,会从调用 MQL 程序所在文件夹开始搜索指标文件。如果未找到,则继续在指标公共文件夹 MQL5/Indicators中搜索。
在包含斜杠的路径中,最右侧斜杠右侧的部分被视为文件名,之前的部分构成文件夹层级结构。例如,路径 "Folder/SubFolder/Filename" 对应两个子文件夹:Folder中的 SubFolder,以及 SubFolder 中的 Filename 文件。
最简单的情况是pathname参数不包含任何斜杠。这种情况下,它仅指定文件名。它同样会基于上述两个搜索起点进行解析。
例如:MyExpert.ex5EA 交易位于文件夹 MQL5/Experts/Examples 中,其中包含iCustom(_Symbol, _Period, "MyIndicator") 调用。此处相对路径简化为(空路径),仅包含文件名。因此,指标搜索将从 MQL5/Experts/Examples/文件夹开始,结合文件名 MyIndicator,构成路径MQL5/Experts/Examples/MyIndicator.ex5。如果在此目录中未找到该指标,将继续在指标根文件夹中搜索,即通过具体路径和文件名 MQL5/Indicators/MyIndicator.ex5查找。
如果在这两个位置均未找到指标,函数将返回 INVALID_HANDLE,并将错误码 4802 (ERR_INDICATOR_CANNOT_CREATE) 设置为 _LastError。
更复杂的情况是,如果pathname不仅包含文件名,还包含目录,例如 "TradeSignals/MyIndicator"。此时,指定路径会被附加到调用程序所在文件夹中,形成以下搜索路径:MQL5/Experts/Examples/TradeSignals/MyIndicator.ex5。如果搜索失败,相同路径会被附加到 MQL5/Indicators,即搜索文件MQL5/Indicators/TradeSignals/MyIndicator.ex5。请注意,如果使用反斜杠作为分隔符,应写成双反斜杠,例如 iCustom(_Symbol, _Period, "TradeSignals\\MyIndicator")。
要释放不再使用的指标占用的计算机内存,可使用 IndicatorRelease 数并传入该指标的句柄。
在测试使用指标的程序时需特别注意。如果在 iCustom调用中,pathname 参数被指定为常量字符串,编译器将自动检测所需指标,并将其与被测试程序一并传递给测试程序。否则,如果该参数通过表达式计算或从外部获取(例如用户 input),则必须在源代码中通过 #property tester_indicator 指定特性:
#property tester_indicator "indicator_name.ex5" |
这意味着程序中仅能测试预先已知的自定义指标。
以新指标 UseWPR1.mq5为例,其 OnInit 处理程序会创建上一章讨论的 IndWPR 指标的句柄(务必编译 IndWPR,因为iCustom 会下载 ex5 文件)。UseWPR1中收到的处理程序尚未使用,我们仅研究获取处理程序的可行性并验证操作是否成功。因此,新指标无需设置缓冲区。
#property indicator_separate_window
|
该指标将创建空白子窗口,但暂时不显示任何内容。这属正常行为。
我们来测试几种获取描述符的方式,对应不同的 pathname值:
- 绝对路径,以斜杠开头且包含完整的文件夹层级结构(从 MQL5 根目录开始),此处以第五章指标为示例:"/Indicators/MQL5Book/p5/IndWPR"
- 仅名称 "IndWPR",用于在调用指标 UseWPR1.mq5所在的同一文件夹中搜索(两个指标位于同一文件夹中)
- 相对标准目录 MQL5/Indicators的指标示例的文件夹层级路径,即 "MQL5Book/p5/IndWPR"(注意开头无斜杠)
- 仅名称,与第 2 点类似,但使用不存在的指标名称 "IndWPR NonExistent"
- 绝对路径,与第 1 点类似,但使用未转义的反斜杠,即 "\Indicators\MQL5Book\p5\IndWPR"
- 完全等同于第 2 点。
int OnInit()
|
由于句柄变量未被使用,因此将其声明为局部变量。需特别说明,虽然局部 handle变量在 OnInit 函数退出时被删除,但这并不影响句柄本身:只要“父”指标UseWPR 在执行,句柄就会持续存在。我们只是在代码中丢失了这些句柄的值,但这并没问题,因为此处未在任何地方使用它们。在后续将讨论的实际指标示例中,句柄一定会被存储(通常存储在全局变量中)并使用。
也无需担心资源泄漏:当从图表中删除UseWPR指标时,终端将自动清除其创建的所有句柄。关于句柄显式释放的原则及必要性,将在通过 IndicatorRelease 删除指标实例 章节中详细说明。
上述 OnInit代码将生成以下日志条目:
iCustom(_Symbol,_Period,/Indicators/MQL5Book/p5/IndWPR)=10 / ok
|
如我们所看到,除了第 4 种情况(调用不存在的指标)外,其他情况均会收到有效句柄值 10、11、12 和 13。句柄值为 - 1 (INVALID_HANDLE)。
另外需要注意,第 5 行代码在编译时会生成多条“无法识别的字符转义序列”警告。这是由于我们未对反斜杠进行转义处理而导致的。我们也很幸运,指令执行成功,因为如果任何文件夹或文件的名称以支持的转义序列中的某个字母开头,则序列的解析将会违背名称的预期读取。例如,如果同一文件夹中有个名为 "test" 的指标,而我们尝试通过路径 "MQL5Book\p5\test" 创建该指标,我们将会收到 INVALID_HANDLE 和错误码 4802。这是因为 '\t' 是制表符,因此终端会将查找 "MQL5Book\p5<nbsp> est"。正确条目应为 "MQL5Book\\p5\\test"。因此,使用正斜杠更为简便。
还需要注意的是,虽然所有成功的变体都引用同一个指标MQL5/Indicators/MQL5Book/p5/IndWPR.ex5,且实际上路径 1、2、3 和 5 是等效的,但终端仍将它们视为不同的字符串,这就是我们得到不同描述符值的原因。只有当选项 6 完全复制选项 2 时,才会返回相同的描述符值 11。
为什么句柄编号从 10 开始?更小的数值是为系统保留的。如上所述,对于使用简版OnCalculate的指标,最后一个参数可用于传递价格类型或另一个指标的句柄,该指标的缓冲区将被用于计算新创建的指标实例。由于 ENUM_APPLIED_PRICE 枚举元素拥有各自的常量值,它们占据 10 以下的区域。更多详情,请参阅 定义指标的数据源。
在接下来的 UseWPR2.mq5示例中,我们将实现一个指标:该指标将创建IndWPR 的实例,并通过句柄检查其计算进度。但在此之前,你需要先了解一个新函数 BarsCalculated。