MQL5 简介(第 6 部分):MQL5 中的数组函数新手指南 (二)(基础篇)
数组排序与查找的内建函数怎么用
MQL5 给数组操作准备了一批内建函数,新手最容易忽略的是 ArraySort 和 ArrayBsearch 这对组合。前者原地排序,后者在已排序数组上做二分查找,比手写循环快得多,尤其在 EA 初始化时处理历史样本。 ArraySort 默认升序,第二个参数可指定起始下标、第三个参数指定排序元素个数,还能用第四个参数选降序。若数组未排序就直接调 ArrayBsearch,返回的下标是无效的,这是实盘里常见的隐性 bug。 下面这段演示了先排序再查找目标值 42 的位置,若返回 -1 表示不存在。读者可把代码贴进 MT5 的脚本里跑一遍,改一下数组元素看返回值变化。 外汇与贵金属杠杆交易高风险,任何函数行为都应在策略测试器里验证后再上实盘。
class="type">class="kw">double arr[class="num">5] = {class="num">3.1, class="num">42.0, class="num">7.7, class="num">1.2, class="num">42.0}; ArraySort(arr); class="type">int idx = ArrayBsearch(arr, class="num">42.0); Print("found at ", idx);
◍ 把数组函数一次摸清
这一节接着前面打下的底子,把 MQL5 里剩下的数组处理函数逐个过一遍。上一节只讲了部分基础数组函数,这一节补上 ArrayPrint、ArrayInsert、ArraySize、ArrayRange、ArrayRemove、ArraySwap、ArrayReverse、ArraySort 这八个,凑齐日常 EA 开发里最常用的数组工具集。 无论你写策略时是要动态插数据、删元素,还是给报价序列排序,这几个函数基本都能覆盖。把它们的参数和行为摸准,后面写指标缓冲区和历史仓位遍历会顺手很多。 下面列出本节要拆的八个函数名,建议开 MT5 按 F1 对照每个函数的返回值类型自己敲一遍demo:ArrayPrint 打印、ArrayInsert 插入、ArraySize 取长度、ArrayRange 取维度、ArrayRemove 删除、ArraySwap 交换、ArrayReverse 反转、ArraySort 排序。
「用 ArrayPrint 把数组摊在日志里看个透」
MT5 脚本跑起来后,数组里到底存了什么值,靠肉眼盯变量是不现实的。ArrayPrint() 就是干这活的:把任意类型数组直接丢进专家日志,调试时能在算法不同节点快速验证数据有没有写错。 函数原型是 ArrayPrint(array[], digits, separator, start, count, flags)。digits 控制浮点小数位,separator 设元素间隔符,start 和 count 决定打印起点与数量,flags 则是格式化开关,比如 ARRAYPRINT_HEADER 打字段名、ARRAYPRINT_INDEX 打左侧索引、ARRAYPRINT_LIMIT 只露首尾各 100 个元素。 下面这段是 double 数组的最小用例:5 个元素 {1.46647, 2.76628, 3.83367, 4.36636, 5.9393},用 2 位小数、空格分隔、从 0 打印全部,日志会直接出 1.47 2.77 3.83 4.37 5.94。 结构数组也没压力。定义含 name/age/address/time 的 StudentsDetails,塞三个学生后用 ArrayPrint(Students, 0, " | ", 0, WHOLE_ARRAY) 输出,管道符隔开字段,时间按默认日期+时分秒显示。 flags 之间是独立生效的,加哪个就只改那部分。挂 ARRAYPRINT_HEADER 后,结构数组顶部多出 [name] [age] [address] [time] 标签,但因为没有 ARRAYPRINT_INDEX,左侧就不带序号。时间细节还能用 ARRAYPRINT_MINUTES 只留时分、ARRAYPRINT_SECONDS 细化到秒,多个 flag 可叠加,例如 HEADER+MINUTES 既带列名又省掉日期秒数。外汇与贵金属 EA 调试涉及真实报价数组时,注意日志暴露数据仅用于本地验证,实盘环境有高风险,参数误用可能导致逻辑误判。
ArrayPrint(array[], digit , Separator, Start, Count, Flags); class="type">void OnStart() { class=class="str">"cmt">// Define an array of doubles class="type">class="kw">double ThisArray[] = { class="num">1.46647, class="num">2.76628, class="num">3.83367, class="num">4.36636, class="num">5.9393}; class=class="str">"cmt">// Print the entire array using ArrayPrint with class="num">2 decimal places and a single spacing between array elements ArrayPrint(Array,class="num">2," ",class="num">0,WHOLE_ARRAY); } class="type">void OnStart() { class=class="str">"cmt">// Define a structure for storing information about students class="kw">struct StudentsDetails { class="type">class="kw">string name; class="type">int age; class="type">class="kw">string address; class="type">class="kw">datetime time; class=class="str">"cmt">// Add a time field }; class=class="str">"cmt">// Create an array of Students structures StudentsDetails Students[class="num">3]; class=class="str">"cmt">// Fill in details for each person Students[class="num">0].name = "Abioye"; Students[class="num">0].age = class="num">25; Students[class="num">0].address = "class="num">123 MQL5 St"; Students[class="num">0].time = TimeCurrent(); Students[class="num">1].name = "Israel"; Students[class="num">1].age = class="num">26; Students[class="num">1].address = "class="num">456 MQL4 St"; Students[class="num">1].time = TimeCurrent(); Students[class="num">2].name = "Pelumi"; Students[class="num">2].age = class="num">27; Students[class="num">2].address = "class="num">789 MetaQuotes St"; Students[class="num">2].time = TimeCurrent(); class=class="str">"cmt">// Print the details of each person using ArrayPrint ArrayPrint(Students, class="num">0, " | ", class="num">0, WHOLE_ARRAY); } class="kw">struct StudentsDetails {
用结构体数组存多组交易对象信息
在 MT5 的 EA 或脚本里,若要把若干账户或标的的静态属性一次性管起来,直接定义结构体再开定长数组比散变量清爽得多。下面这段声明了一个含姓名、年龄、地址和时间的四字段结构,并实例化长度为 3 的数组。 [CODE] struct StudentsDetails { string name; int age; string address; datetime time; // Add a time field }; StudentsDetails Students[3]; Students[0].name = "Abioye"; Students[0].age = 25; Students[0].address = "123 MQL5 St"; Students[0].time = TimeCurrent(); Students[1].name = "Israel"; Students[1].age = 26; Students[1].address = "456 MQL4 St"; Students[1].time = TimeCurrent(); Students[2].name = "Pelumi"; Students[2].age = 27; Students[2].address = "789 MetaQuotes St"; Students[2].time = TimeCurrent(); ArrayPrint(Students, 0, " | ", 0, WHOLE_ARRAY); ArrayPrint(Students, 0, " | ", 0, WHOLE_ARRAY,ARRAYPRINT_HEADER); ArrayPrint(Students, 0, " | ", 0, WHOLE_ARRAY,ARRAYPRINT_MINUTES);
| ArrayPrint(Students,0," | ",0,WHOLE_ARRAY,ARRAYPRINT_HEADER | ARRAYPRINT_MINUTES); |
|---|
[/CODE] 逐行看:struct 定义里 datetime time 用 TimeCurrent() 赋值,抓的是脚本运行那一刻的服务器时间;数组下标 0~2 分别填了三组样例,年龄 25/26/27 只是演示数据。 ArrayPrint 的四种调用值得在 MT5 里实测:不带旗标时纯打数据;加 ARRAYPRINT_HEADER 会带字段名表头;ARRAYPRINT_MINUTES 把时间只显示到分钟;两者按位或同用则表头+分钟格式叠加。跑完你会在Experts日志看到不同排版,验证结构体数组的列式输出逻辑。
class="kw">struct StudentsDetails { class="type">class="kw">string name; class="type">int age; class="type">class="kw">string address; class="type">class="kw">datetime time; class=class="str">"cmt">// Add a time field }; StudentsDetails Students[class="num">3]; Students[class="num">0].name = "Abioye"; Students[class="num">0].age = class="num">25; Students[class="num">0].address = "class="num">123 MQL5 St"; Students[class="num">0].time = TimeCurrent(); Students[class="num">1].name = "Israel"; Students[class="num">1].age = class="num">26; Students[class="num">1].address = "class="num">456 MQL4 St"; Students[class="num">1].time = TimeCurrent(); Students[class="num">2].name = "Pelumi"; Students[class="num">2].age = class="num">27; Students[class="num">2].address = "class="num">789 MetaQuotes St"; Students[class="num">2].time = TimeCurrent(); ArrayPrint(Students, class="num">0, " | ", class="num">0, WHOLE_ARRAY); ArrayPrint(Students, class="num">0, " | ", class="num">0, WHOLE_ARRAY,ARRAYPRINT_HEADER); ArrayPrint(Students, class="num">0, " | ", class="num">0, WHOLE_ARRAY,ARRAYPRINT_MINUTES); ArrayPrint(Students,class="num">0," | ",class="num">0,WHOLE_ARRAY,ARRAYPRINT_HEADER | ARRAYPRINT_MINUTES);
◍ 插入和覆盖两种数组操作的分水岭
在 MQL5 里动数组,先分清你要的是「塞进去」还是「换掉」。ArrayInsert 从目标数组的 DestinationIndexStart 开始,把源数组的元素整段挪进中间,原有元素被向后平移腾位置;ArrayCopy 则是从指定索引起直接覆盖,原值被抹掉。两者都吃五个参数:目标数组、源数组、目标起始索引、源起始索引、插入或复制个数。 静态数组有个坑:若源元素数量 ≥ 数组总大小,ArrayInsert 不会生效,除非从索引 0 插,那样等于整块替换。动态数组没这限制,插多少自动扩容。 下面这段可直接丢进 MT5 脚本跑。SourceArray 初值 [1,3,5,7,9],DestinationArray 初值 [15,20,25,30,35];在索引 2 用 ArrayInsert 整段插入后,DestinationArray 变成 [15,20,1,3,5,7,9],原 25,30,35 被挤到尾端。同样起点换 ArrayCopy,则变成 [15,20,1,3,5],后两位被覆盖。外汇与贵金属 EA 开发中误用覆盖可能丢掉历史缓冲,属高风险操作,参数索引务必手核。 别把静态数组当动态用 静态数组插超量直接静默失败,调试时只会发现数组没变。需要弹性拼接就先 ArrayResize 或声明为动态数组,再调插入。
ArrayInsert(DestinationArray[],SourceArray[],DestinationIndexStart,SourceIndexStart,count); class="type">void OnStart() { class=class="str">"cmt">// Declare two dynamic arrays class="type">int SourceArray[]; class="type">int DestinationArray[]; class=class="str">"cmt">// Resizing the dynamic arrays to have class="num">5 elements each ArrayResize(SourceArray, class="num">5); ArrayResize(DestinationArray, class="num">5); class=class="str">"cmt">// Assigning values to dynamic array elements SourceArray[class="num">0] = class="num">1; SourceArray[class="num">1] = class="num">3; SourceArray[class="num">2] = class="num">5; SourceArray[class="num">3] = class="num">7; SourceArray[class="num">4] = class="num">9; class=class="str">"cmt">// Assigning different values to DestinationArray DestinationArray[class="num">0] = class="num">15; DestinationArray[class="num">1] = class="num">20; DestinationArray[class="num">2] = class="num">25; DestinationArray[class="num">3] = class="num">30; DestinationArray[class="num">4] = class="num">35; class=class="str">"cmt">// Print the elements of SourceArray before ArrayInsert/ArrayCopy Print("Elements of SourceArray before ArrayInsert/ArrayCopy: "); ArrayPrint(SourceArray, class="num">2, " ", class="num">0, WHOLE_ARRAY); class=class="str">"cmt">// Print the elements of DestinationArray before ArrayInsert/ArrayCopy Print("Elements of DestinationArray before ArrayInsert/ArrayCopy: "); ArrayPrint(DestinationArray, class="num">2, " ", class="num">0, WHOLE_ARRAY); class=class="str">"cmt">// Using ArrayInsert to insert SourceArray into DestinationArray at index class="num">2 ArrayInsert(DestinationArray, SourceArray, class="num">2, class="num">0, WHOLE_ARRAY); class=class="str">"cmt">// Print the modified DestinationArray after ArrayInsert Print("Elements of DestinationArray after using ArrayInsert: "); ArrayPrint(DestinationArray, class="num">2, " ", class="num">0, WHOLE_ARRAY); class=class="str">"cmt">// Reset DestinationArray to demonstrate ArrayCopy ArrayFree(DestinationArray); ArrayResize(DestinationArray, class="num">5); DestinationArray[class="num">0] = class="num">15; DestinationArray[class="num">1] = class="num">20;