MQL5 简介(第 5 部分):MQL5 数组函数入门指南(基础篇)
「MQL5 数组函数先认这几个」
MQL5 里数组不是简单容器,它是指标缓冲、历史价序列、信号数组的底层载体。新手常卡在动态扩容和越界访问,其实标准库已经给了现成函数。 ArraySize() 返回元素总数,ArrayResize() 改长度,ArrayInitialize() 批量填值。这三个加起来能覆盖八成自定义指标的缓冲初始化需求。 注意:MT5 历史数据数组默认是按时间倒序存的,即 index 0 是最新一根 K 线。写循环时若按正序遍历去算均线,结果会整体偏移一根,回测里这种错位能让夏普率虚高 0.3 以上。外汇与贵金属杠杆高,数组逻辑错一处,实盘可能直接触发异常平仓。
◍ MQL5 数组函数为什么值得新手啃
很多刚碰 MT5 自动交易的人对数组函数有畏难情绪,觉得那是老手才玩得转的东西。其实 MQL5 里围绕数组封装了一批现成函数,把排序、扩容、拷贝、比较这些脏活都包好了,新手只要逐行看懂,就能直接拿来拼自己的策略逻辑。 本篇会集中拆 13 个最常用数组函数:ArrayBsearch、ArrayResize、ArrayCopy、ArrayCompare、ArrayFree、ArraySetAsSeries、ArrayGetAsSeries、ArrayIsSeries、ArrayInitialize、ArrayFill、ArrayIsDynamic、ArrayMaximum、ArrayMinimum。你在 MT5 里按 F1 搜其中任意一个,都能看到官方声明,但本文重点是让你明白每行调用在实盘脚本里到底干了什么。 外汇和贵金属杠杆高、滑点随机,用数组缓存历史 K 线或指标缓冲时,搞错 AsSeries 方向可能让信号整体反向,风险不小。先把这些函数吃透,后面写 EA 才不会在数组边界上栽跟头。
二分查找只认升序数组
在 MT5 里对一维数组做定位,ArrayBsearch() 走的是二分查找,时间复杂度约 O(log n),比手动遍历快得多。但它有个硬前提:数组必须按从小到大升序排好。若传入未排序或乱序数据,返回的位置可能既不是命中索引也不是合理插入点,结果不可信。 函数签名是 int ArrayBsearch(array[], value),返回整型。命中时给目标值索引;未命中时给「应该插入的位置」索引,这点常被忽略——你拿返回值当存在性判断前得先比对一下 array[返回值] 是否等于 value。 下面这段可直接贴进 MT5 脚本跑。sortedArray 已是 {10,20,30,40,50},搜 30 会返回索引 2,终端输出 Found the resultIndex at index 2。把 searchValue 改成 25,返回值是 2(建议插在 20 之后、30 之前),这时 array[2] 是 30 不等于 25,说明未命中。外汇与贵金属行情序列处理前务必先排序,这类数值查找出错可能引发信号错位,杠杆市场高风险。 别把返回索引当命中证据 很多新手以为 ArrayBsearch 返回非负就是找到了。实际上未命中也会返回一个插入建议位,必须用 array[resultIndex]==value 做一次确认,否则后续逻辑会把「该插在哪」误当成「已存在」。
class="type">int ArrayBsearch(array[],value); class="type">void OnStart() { class=class="str">"cmt">// Declare an array of sorted numbers class="type">class="kw">double sortedArray[] = {class="num">10, class="num">20, class="num">30, class="num">40, class="num">50}; class=class="str">"cmt">// Value to search for class="type">class="kw">double searchValue = class="num">30; class=class="str">"cmt">// Call ArrayBsearch function class="type">int resultIndex = ArrayBsearch(sortedArray, searchValue); class=class="str">"cmt">// Print out the index of class="num">30 in the array Print("Found the resultIndex at index ", resultIndex); class=class="str">"cmt">// The output will be index class="num">2 }
「运行时改数组大小的 ArrayResize 实操」
MT5 里静态数组在编译期就锁死长度,比如 int staticArray[5] 永远只占 5 个 int 的内存,跑起来想塞第 6 个只会越界报错。动态数组声明时不写长度,int dynamicArray[] 这种写法就是把尺寸决定权留给运行时。
ArrayResize(array[], new_size, reserve_size=0) 三个参数里,前两个是硬需求:第一个传数组引用,第二个传改完后的元素总数。第三个 reserve_size 可选,提前多预留空间能减少后续频繁重分配的开销,默认 0 表示不预留。
下面这段可直接贴进 MT5 脚本跑。先扩到 5 个元素并填 10~50,Print 索引 2 得到 30;再扩到 8 个,原值保留,新填 60~80,Print 索引 6 得到 70。外汇与贵金属 EA 开发中用动态数组缓冲 tick 或挂单序列时,这种扩容不会丢历史数据。
别把 reserve_size 当万能缓存
预留空间只是内存层预分配,ArrayResize 返回的可用索引上限仍是 new_size-1,超了照样越界。真要批量接数据,new_size 得按实际峰值设,reserve 只用来摊薄重分配次数。
class=class="str">"cmt">// Static array declaration class="type">int staticArray[class="num">5] = {class="num">1, class="num">2, class="num">3, class="num">4, class="num">5}; class=class="str">"cmt">// Dynamic array declaration class="type">int dynamicArray[]; ArrayResize( array[], class=class="str">"cmt">// Reference to the array to be resized new_size, class=class="str">"cmt">// New size for the array reserve_size = class="num">0 class=class="str">"cmt">// Optional space reserved for future elements ); class="type">void OnStart() { class=class="str">"cmt">// Dynamic array declaration class="type">int dynamicArray[]; class=class="str">"cmt">// Resizing the dynamic array to have class="num">5 elements ArrayResize(dynamicArray, class="num">5); class=class="str">"cmt">// Assigning values to dynamic array elements dynamicArray[class="num">0] = class="num">10; dynamicArray[class="num">1] = class="num">20; dynamicArray[class="num">2] = class="num">30; dynamicArray[class="num">3] = class="num">40; dynamicArray[class="num">4] = class="num">50; class=class="str">"cmt">// Accessing elements in a dynamic array Print("Element at index class="num">2: ", dynamicArray[class="num">2]); class=class="str">"cmt">// Output: class="num">30 class=class="str">"cmt">// Resizing the dynamic array to have class="num">8 elements ArrayResize(dynamicArray, class="num">8); class=class="str">"cmt">// Assigning values to the additional elements dynamicArray[class="num">5] = class="num">60; dynamicArray[class="num">6] = class="num">70; dynamicArray[class="num">7] = class="num">80; class=class="str">"cmt">// Accessing elements after resizing Print("Element at index class="num">6: ", dynamicArray[class="num">6]); class=class="str">"cmt">// Output: class="num">70 } class="type">int dynamicArray[]; ArrayResize(dynamicArray, class="num">5); dynamicArray[class="num">0] = class="num">10; dynamicArray[class="num">1] = class="num">20; dynamicArray[class="num">2] = class="num">30; dynamicArray[class="num">3] = class="num">40; dynamicArray[class="num">4] = class="num">50; Print("Element at index class="num">2: ", dynamicArray[class="num">2]); class=class="str">"cmt">// Output: class="num">30 ArrayResize(dynamicArray, class="num">8); dynamicArray[class="num">5] = class="num">60; dynamicArray[class="num">6] = class="num">70; dynamicArray[class="num">7] = class="num">80; Print("Element at index class="num">6: ", dynamicArray[class="num">6]); class=class="str">"cmt">// Output: class="num">70
◍ 用 ArrayCopy 在数组间抽数而不动原数组
做 EA 或指标时,常要把一个价格序列里的某一段抽出来单独算,比如从 5 个品种报价里只取 3 个做价差。ArrayCopy 就是干这个的:它能在两个数组之间按索引区间搬数据,源数组保持原样,目标数组从你指定的位置开始写。 函数原型里 dst_array 是接收方,src_array 是供给方;dst_start 决定从目标数组哪一位开始覆盖,src_start 决定从源数组哪一位起读,count 不填或给 WHOLE_ARRAY 就一直读到源尾。这套参数让你能精确控制「搬哪段、放哪位」,不用手写循环。 下面这段代码先把两个动态数组各扩到 5 个元素并填值,sourceArray 是 1~5,destinationArray 是 10~50;随后调用 ArrayCopy 从 sourceArray 的 0 位起、往 destinationArray 的 5 位起写入整段。注意目标数组原本只有 5 位,写入后会自动扩容,所以最后取 destinationArray[7] 会拿到值 3(即 sourceArray[2]),证明复制链是对的。 别把正态当圣经:dst_start 超过当前大小会自动撑大数组,但如果你之后按固定长度读,可能越界报数组越界错误;贵金属与外汇行情高波动,数组里存的是跳空后的残价时,复制前最好先判 src_total 和 dst_limit。
ArrayCopy( dst_array[], class=class="str">"cmt">// The destination array to receive copied elements src_array[], class=class="str">"cmt">// The source array from which elements will be copied dst_start=class="num">0, class=class="str">"cmt">// The index in the destination array to start writing from src_start=class="num">0, class=class="str">"cmt">// The index in the source array from which to start copying count class=class="str">"cmt">// The number of elements to copy; class="kw">default is to copy the entire array ); 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">2; sourceArray[class="num">2] = class="num">3; sourceArray[class="num">3] = class="num">4; sourceArray[class="num">4] = class="num">5; destinationArray[class="num">0] = class="num">10; destinationArray[class="num">1] = class="num">20; destinationArray[class="num">2] = class="num">30; destinationArray[class="num">3] = class="num">40; destinationArray[class="num">4] = class="num">50; class=class="str">"cmt">// Copy elements from sourceArray to destinationArray starting from index class="num">1 ArrayCopy(destinationArray, sourceArray, class="num">5, class="num">0, WHOLE_ARRAY); class=class="str">"cmt">// Print the value of the element at index class="num">7 in destinationArray Comment("Value at index class="num">7 in destinationArray: ", destinationArray[class="num">7]); } class="type">int sourceArray[]; class="type">int destinationArray[]; ArrayResize(sourceArray, class="num">5); ArrayResize(destinationArray, class="num">5); sourceArray[class="num">0] = class="num">1; sourceArray[class="num">1] = class="num">2; sourceArray[class="num">2] = class="num">3; sourceArray[class="num">3] = class="num">4; sourceArray[class="num">4] = class="num">5; destinationArray[class="num">0] = class="num">10; destinationArray[class="num">1] = class="num">20; destinationArray[class="num">2] = class="num">30; destinationArray[class="num">3] = class="num">40; destinationArray[class="num">4] = class="num">50; ArrayCopy(destinationArray, sourceArray, class="num">5, class="num">0, WHOLE_ARRAY); Comment("Value at index class="num">7 in destinationArray: ", destinationArray[class="num">7]);
用 ArrayCompare 给数组关系定性
ArrayCompare 从两个数组的索引 0 开始逐位比对,遇到第一个不相等的元素就停止,并依据该位置数值大小返回关系结论。全部元素一致则返回 0;前者该位更小返回 -1,更大返回 1;若数组类型不兼容或参数非法则返回 -2。 它的五个参数里,start1 与 start2 控制各自起始偏移,默认都是 0;count 默认 WHOLE_ARRAY 表示比到较短者末尾。若你只比尾部 3 根 K 线的收盘价数组,把 start1、start2 设成各自长度减 3、count 设 3 即可,不必拷贝子数组。 下面这段可直接贴进 MT5 脚本跑:ListA 为 {1,2,3,4,5},ListB 为 {1,2,3,4,6},从 0 比到尾。前四位相等,索引 4 处 5 小于 6,因此 result 为 -1,终端会打印 ListA is less than ListB。把 ListB[4] 改回 5 再跑一次,返回就变成 0。外汇与贵金属行情高波动,用数组缓存历史 tick 做比对时,注意数组越界与类型匹配可能引发 -2。
class="type">int ArrayCompare(const class="type">void& array1[], const class="type">void& array2[], class="type">int start1 = class="num">0, class="type">int start2 = class="num">0, class="type">int count = WHOLE_ARRAY); class="type">void OnStart() { class=class="str">"cmt">// Declare two arrays class="type">int ListA[] = {class="num">1, class="num">2, class="num">3, class="num">4, class="num">5}; class="type">int ListB[] = {class="num">1, class="num">2, class="num">3, class="num">4, class="num">6}; class=class="str">"cmt">// Use ArrayCompare to compare the arrays class="type">int result = ArrayCompare(ListA, ListB, class="num">0, class="num">0, WHOLE_ARRAY); class=class="str">"cmt">// Print the result if(result == -class="num">1) { Print("ListA is less than ListB"); } else if(result == class="num">0) { Print("ListA is equal to ListB"); } else if(result == class="num">1) { Print("ListA is greater than ListB"); } else if(result == -class="num">2) { Print("Error: Incompatible arrays or invalid parameters"); } } class="type">int ListA[] = {class="num">1, class="num">2, class="num">3, class="num">4, class="num">5}; class="type">int ListB[] = {class="num">1, class="num">2, class="num">3, class="num">4, class="num">6};