MQL5 简介(第 5 部分):MQL5 数组函数入门指南·综合运用
一次性铺平数组初值的坑
在 MT5 的 EA 或指标开发里,数字数组如果只是声明而不清场,里面可能是随机垃圾值。ArrayInitialize() 干的事很单纯:把数组里每一个元素都刷成你指定的同一个数,省掉手写循环逐个赋值的麻烦。 它只管「当前已分配」的那段内存。上面例子里先 ArrayResize 到 3,再手动写了 10/20/30,随后 ArrayInitialize(myArray, 0) 一调,三个槽位全变 0——Print 出来 myArray[0] 到 [2] 都是 0。 别把 resize 当万能补丁。例子后半段 ArrayResize 到 5,新增的 [3]、[4] 并不会被之前的 0 自动覆盖,得自己写 myArray[3]=40、myArray[4]=50。也就是说,扩展出来的空间初始值是未定义的,靠 ArrayInitialize 只保住老地盘。 外汇和贵金属脚本里用这函数做缓冲数组清零时,记住它不碰将来扩容的部分,否则回测里可能读到脏数据导致信号计算偏差。开 MT5 把下面代码贴进脚本跑一遍,看输出窗口验证 0 与 40/50 的分界就明白了。
class="type">int ArrayInitialize( array[], class=class="str">"cmt">// initialized array value class=class="str">"cmt">// value that will be set ); class="type">void OnStart() { class=class="str">"cmt">// Declare a dynamic array class="type">int myArray[]; class=class="str">"cmt">// Resize the array to have an initial size(let&class="macro">#x27;s use class="num">3 elements) ArrayResize(myArray, class="num">3); class=class="str">"cmt">// Assign values to all elements before initialization myArray[class="num">0] = class="num">10; myArray[class="num">1] = class="num">20; myArray[class="num">2] = class="num">30; class=class="str">"cmt">// Assign values to all elements before initialization myArray[class="num">0] = class="num">10; myArray[class="num">1] = class="num">20; myArray[class="num">2] = class="num">30; class=class="str">"cmt">// Initialize the array with a value(let&class="macro">#x27;s use class="num">0.0) ArrayInitialize(myArray, class="num">0); class=class="str">"cmt">// Print the values of all elements after initialization Print("Values after initialization:"); Print("myArray[class="num">0] = ", myArray[class="num">0]); class=class="str">"cmt">// outpot wil be class="num">0 Print("myArray[class="num">1] = ", myArray[class="num">1]); class=class="str">"cmt">// outpot wil be class="num">0 Print("myArray[class="num">2] = ", myArray[class="num">2]); class=class="str">"cmt">// outpot wil be class="num">0 class=class="str">"cmt">// Resize the array to have class="num">5 elements ArrayResize(myArray, class="num">5); class=class="str">"cmt">// Assign values to the additional elements after resizing myArray[class="num">3] = class="num">40; myArray[class="num">4] = class="num">50; class=class="str">"cmt">// Print the values of all elements after resizing Print("Values after resizing:"); Print("myArray[class="num">3] = ", myArray[class="num">3]); class=class="str">"cmt">// outpot wil be class="num">40 Print("myArray[class="num">4] = ", myArray[class="num">4]); class=class="str">"cmt">// outpot wil be class="num">50 }
◍ 动态扩容后如何塞入新元素
MQL5 的数组在声明时若未指定大小,运行时可用 ArrayResize 按需扩容。下面这段代码先把 myArray 扩到 5 个元素,再给下标 3、4 赋值并打印验证。 扩容后原有元素保持不变,新位置只是被分配了内存,不赋值就读会拿到初始零值。因此务必在 ArrayResize 之后显式写入,否则逻辑上容易把“空位”当成有效信号。 这段代码跑完,终端会输出 myArray[3]=40 与 myArray[4]=50,说明下标从 0 起算、扩容追加在尾部。实盘里用数组缓存指标缓冲区时,这个细节直接决定你取到的 K 线是不是最新一根。
ArrayResize(myArray, class="num">5); class=class="str">"cmt">// Assign values to the additional elements after resizing myArray[class="num">3] = class="num">40; myArray[class="num">4] = class="num">50; class=class="str">"cmt">// Print the values of all elements after resizing Print("Values after resizing:"); Print("myArray[class="num">3] = ", myArray[class="num">3]); class=class="str">"cmt">// Output will be class="num">40 Print("myArray[class="num">4] = ", myArray[class="num">4]); class=class="str">"cmt">// Output will be class="num">50
「用 ArrayFill 批量写数组片段」
MT5 里处理动态数组时,逐个下标赋值既啰嗦又容易漏写。ArrayFill 直接按「起始索引 + 长度 + 值」三段式把一段元素一口气写完,代码可读性和容错都更好,尤其在你用 EA 维护多货币对状态表、批量重置指标缓冲时很省事。 它和 ArrayInitialize 的区别很实在:后者是全体元素统一刷同一个值,ArrayFill 只动你指定的那一段。比如先把前 5 个格子填 42、后 5 个填 99,用 ArrayInitialize 就做不到分段异构初始化。 下面这段可直接贴进 MT5 脚本跑,数组先Resize成 10,再用两次 ArrayFill 分段填值,Print 出来验证:Shelf 0~4 输出 42,Shelf 5~9 输出 99。外汇与贵金属行情波动剧烈、杠杆风险高,这类数组初始化仅用于逻辑验证,不代表任何交易信号。 代码逐行看:ArrayFill 第一参是目标数组,第二参 start 是起始下标(从 0 计),第三参 count 是连续填充个数,第四参 value 是填入内容;OnStart 里先声明 int roomShelves[],ArrayResize 拉到 10 格,两次 ArrayFill 分别覆盖 0~4 和 5~9,后面 10 条 Print 逐格打印确认。
ArrayFill( array[], class=class="str">"cmt">// array to be filled start, class=class="str">"cmt">// Starting slot(index) for filling count, class=class="str">"cmt">// Number of slots to fill value class=class="str">"cmt">// The value to fill the slots with ); class="type">void OnStart() { class=class="str">"cmt">// Declare an array of shelves class="type">int roomShelves[]; class=class="str">"cmt">// Set the size of the array(number of shelves) ArrayResize(roomShelves, class="num">10); class=class="str">"cmt">// Fill the first class="num">5 shelves with books(value class="num">42) ArrayFill(roomShelves, class="num">0, class="num">5, class="num">42); class=class="str">"cmt">// Fill the next class="num">5 shelves with toys(value class="num">99) ArrayFill(roomShelves, class="num">5, class="num">5, class="num">99); class=class="str">"cmt">// Display the contents of the shelves after filling Print("Contents of the shelves after filling:"); Print("Shelf class="num">0: ", roomShelves[class="num">0]); class=class="str">"cmt">// output will be class="num">42 Print("Shelf class="num">1: ", roomShelves[class="num">1]); class=class="str">"cmt">// output will be class="num">42 Print("Shelf class="num">2: ", roomShelves[class="num">2]); class=class="str">"cmt">// output will be class="num">42 Print("Shelf class="num">3: ", roomShelves[class="num">3]); class=class="str">"cmt">// output will be class="num">42 Print("Shelf class="num">4: ", roomShelves[class="num">4]); class=class="str">"cmt">// output will be class="num">42 Print("Shelf class="num">5: ", roomShelves[class="num">5]); class=class="str">"cmt">// output will be class="num">99 Print("Shelf class="num">6: ", roomShelves[class="num">6]); class=class="str">"cmt">// output will be class="num">99 Print("Shelf class="num">7: ", roomShelves[class="num">7]); class=class="str">"cmt">// output will be class="num">99 Print("Shelf class="num">8: ", roomShelves[class="num">8]); class=class="str">"cmt">// output will be class="num">99 Print("Shelf class="num">9: ", roomShelves[class="num">9]); class=class="str">"cmt">// output will be class="num">99 }
用 ArrayIsDynamic 区分动静数组
在 MT5 的 MQL5 环境里,数组分两种:编译期就定死长度的静态数组,和运行时能靠 ArrayResize 改大小的动态数组。ArrayIsDynamic() 只干一件事——接收数组引用,返回 true 或 false,让你在 EA 或脚本跑起来时知道手里的容器能不能扩缩。 这个函数本身不抛异常、不修改数组,纯粹做类型探测。实际用处在于:若返回 false,你往里写数据前必须自己保证下标不越界;返回 true,才敢在循环里动态扩容存 tick 或指标缓冲。 下面这段可直接贴进 MT5 脚本跑,验证两种声明方式的差异: bool ArrayIsDynamic(array[] // array to be checked); void OnStart() { // Declare a static array int staticArray[5]; // Declare a dynamic array int dynamicArray[]; // Check if the static array is dynamic bool isStaticArrayDynamic = ArrayIsDynamic(staticArray); if(isStaticArrayDynamic) { Print("The staticArray is dynamic."); // This message won't be printed. } else { Print("The staticArray is static, meaning its size is fixed."); } // Check if the dynamic array is dynamic bool isDynamicArrayDynamic = ArrayIsDynamic(dynamicArray); if(isDynamicArrayDynamic) { Print("The dynamicArray is dynamic, meaning its size can be changed."); } else { Print("The dynamicArray is static."); // This message won't be printed. } } 逐行看:第 1 行是函数原型,形参为数组引用;OnStart 里先声明定长 5 的 staticArray 和未定长的 dynamicArray。对 staticArray 调用返回 false,日志会打「size is fixed」;对 dynamicArray 调用返回 true,日志打「size can be changed」。 开 MT5 新建脚本粘进去,按 F5 编译运行,终端日志会先后输出两条确认信息。外汇与贵金属交易用 EA 处理多周期缓冲时,先探明数组性质再写逻辑,能少踩越界崩溃的坑,但自动化仍伴随高风险,参数误用可能导致异常平仓。
class="type">bool ArrayIsDynamic(array[] class=class="str">"cmt">// array to be checked); class="type">void OnStart() { class=class="str">"cmt">// Declare a class="kw">static array class="type">int staticArray[class="num">5]; class=class="str">"cmt">// Declare a dynamic array class="type">int dynamicArray[]; class=class="str">"cmt">// Check if the class="kw">static array is dynamic class="type">bool isStaticArrayDynamic = ArrayIsDynamic(staticArray); if(isStaticArrayDynamic) { Print("The staticArray is dynamic."); class=class="str">"cmt">// This message won&class="macro">#x27;t be printed. } else { Print("The staticArray is class="kw">static, meaning its size is fixed."); } class=class="str">"cmt">// Check if the dynamic array is dynamic class="type">bool isDynamicArrayDynamic = ArrayIsDynamic(dynamicArray); if(isDynamicArrayDynamic) { Print("The dynamicArray is dynamic, meaning its size can be changed."); } else { Print("The dynamicArray is class="kw">static."); class=class="str">"cmt">// This message won&class="macro">#x27;t be printed. } }
◍ 用 ArrayMaximum 锁定数组峰值位置
在 MT5 的 EA 或指标开发里,经常要从一串数值里找出最大那个对应的下标。ArrayMaximum 就是干这个的:它按数组序列扫描,返回最大元素的索引;若指定区间没找到就给 -1。外汇和贵金属行情波动剧烈,用这类函数提取极值时务必意识到信号可能随品种跳空而失真,属于高风险操作。 函数原型带三个参数:array[] 是待查数组,start 设定从哪个下标开始扫,count 默认 WHOLE_ARRAY 表示扫到底。实际写策略时,比如只关心最近 20 根 K 线的最高收盘价,就把 start 设成 ArraySize-20、count 设 20,避免全数组无效计算。 下面这段可直接贴进 MT5 跑,myArray 手动写了 6 个整数 {42,18,56,31,75,23},最大是 75 在索引 4。OnStart 里调用后若 maxIndex 不是 -1 就打印值和位置,否则报没找到。你可以改数组内容验证返回值变化。
class="type">int ArrayMaximum( array[], class=class="str">"cmt">// Array for search start, class=class="str">"cmt">// Index to start checking with count = WHOLE_ARRAY class=class="str">"cmt">// Number of checked elements(class="kw">default: search in the entire array) ); class="type">void OnStart() { class=class="str">"cmt">// Declare an array with integer values class="type">int myArray[] = {class="num">42, class="num">18, class="num">56, class="num">31, class="num">75, class="num">23}; class=class="str">"cmt">// Find the maximum value and its index class="type">int maxIndex = ArrayMaximum(myArray); class=class="str">"cmt">// Check if a maximum was found if(maxIndex != -class="num">1) { Print("The maximum value in the array is: ", myArray[maxIndex]); Print("Index of the maximum value: ", maxIndex); } else { Print("No maximum value found in the array."); } }
「用 ArrayMinimum 锁定数组里的低点位置」
在 MT5 的 MQL5 环境里,ArrayMinimum() 专门用来返回数值数组第一维中最小元素的索引。它吃得起不同长度的数组,而且会按数组当前的序列顺序(serial/as-series)去判断,避免你把时间倒序的行情数组错判位置。 函数签名是 int ArrayMinimum(array[], start, count = WHOLE_ARRAY)。start 指定从哪个下标开始扫,count 给搜索长度,缺省 WHOLE_ARRAY 就是扫到底。若范围内找不到合法最小值,返回 -1——写 EA 时拿这个 -1 做防御比裸用索引稳。 下面这段可直接贴进 MT5 脚本跑,myArray 写死成 {10,5,8,3,12},全量搜索后最小值是 3,落在下标 3,终端会打印「Index of the minimum element: 3」。外汇与贵金属波动剧烈,任何基于数组极值做的信号都只是概率倾向,实盘前请在策略测试器里用历史数据验证。 别把正态当圣经:数组里若有多个相等的最小值,ArrayMinimum 只返回第一个命中的下标,不会告诉你重复次数,需要全扫得自己写循环。
class="type">int ArrayMinimum(array[],start,count = WHOLE_ARRAY); class="type">void OnStart() { class=class="str">"cmt">// Declare an integer array class="type">int myArray[] = {class="num">10, class="num">5, class="num">8, class="num">3, class="num">12}; class=class="str">"cmt">// Find the index of the minimum element in the entire array class="type">int minIndex = ArrayMinimum(myArray, class="num">0, WHOLE_ARRAY); class=class="str">"cmt">// Print the result Print("Index of the minimum element: ", minIndex); }
把工具请下神坛
前面拆过的 13 个数组函数——ArrayBsearch、ArrayCopy、ArrayCompare、ArrayResize、ArrayFree、ArraySetAsSeries、ArrayGetAsSeries、ArrayInitialize、ArrayFill、ArrayIsDynamic、ArrayIsSeries、ArrayMaximum、ArrayMinimum——已经能覆盖大多数 EA 里对序列和动态数组的常规操作。比如用 ArraySetAsSeries 把时间序列翻转后,ArrayMaximum 返回的索引直接对应 K 线位置,省掉手写倒序循环。 作者刻意没把 ArrayInsert、ArraySort、ArraySize 这批放进来,下一期才会补。评论区里有人纠结 ArrayCopy 和 ArrayInsert:前者是覆盖写、不挪原有元素,后者是移位插入、保结构;静态数组插过量时只能从索引 0 整体替换。这条区别开 MT5 按 F4 写两行就能验证。 外汇和贵金属杠杆高、滑点随机,数组算错一个索引可能让仓位反向。函数只是零件,跑之前用策略测试器先打历史数据,别把现成接口当圣旨。