MQL5 简介(第 5 部分):MQL5 数组函数入门指南·进阶篇
(2/3)· 从 ArrayFree 到 ArrayIsSeries,六个函数把数组内存与方向控制一次讲透
◍ 动态数组清空与复用:ArrayFree 的实际动作
在 MT5 的 MQL5 环境里,动态数组占的是堆内存,跑久了不释放容易拖慢 EA 或指标。ArrayFree() 就是直接把这块内存还回去,数组回到未初始化状态,后续还能靠 ArrayResize() 重新开张。 下面这段是可直接贴进 MT5 脚本跑的示例,先建一个 5 元素数组填 10~50,打印索引 0 得到 10,然后 ArrayFree 释放,再 Resize 成 3 元素填 100~300,重打索引 0 得到 100。释放后若取消注释去访问 dynamicArray[0],终端会报数组越界运行时错误。 代码逐行拆解: ArrayFree(array[]) —— 入参为待释放的动态数组,无返回值,执行后数组 size 归零。 void OnStart() —— 脚本入口。 int dynamicArray[] —— 声明未定长动态数组。 ArrayResize(dynamicArray, 5) —— 扩到 5 格。 赋值 0~4 索引为 10/20/30/40/50。 Print 打释放前索引 0:输出 10。 ArrayFree(dynamicArray) —— 清空内存。 注释掉的访问行:避免释放后读脏数据报错。 ArrayResize(dynamicArray, 3) 再填 100/200/300。 Print 打重赋值后索引 0:输出 100,证明数组可复用。 做高频 tick 处理的指标时,每根 K 线末尾调一次 ArrayFree 清掉临时序列,可能比一直留着大数组更稳。外汇与贵金属波动剧烈,这类内存习惯虽不直接交易,但能降低卡顿导致的滑点概率。
ArrayFree(array[] class=class="str">"cmt">// dynamic array to be freed); class="type">void OnStart() { class=class="str">"cmt">// Declare a dynamic array class="type">int dynamicArray[]; class=class="str">"cmt">// Resize the dynamic array and assign values 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; class=class="str">"cmt">// Print elements before freeing the array Print("Index class="num">0 before freeing: ", dynamicArray[class="num">0]); class=class="str">"cmt">// Output will be class="num">10 class=class="str">"cmt">// Free the dynamic array using ArrayFree ArrayFree(dynamicArray); class=class="str">"cmt">// Attempting to access elements after freeing(should result in an error) class=class="str">"cmt">// Print("Index class="num">0 after freeing: ", dynamicArray[class="num">0]); class=class="str">"cmt">// Reassign new values to the array ArrayResize(dynamicArray, class="num">3); dynamicArray[class="num">0] = class="num">100; dynamicArray[class="num">1] = class="num">200; dynamicArray[class="num">2] = class="num">300; class=class="str">"cmt">// Print elements after reassigning values Print("Index class="num">0 after reassigning: ", dynamicArray[class="num">0]); class=class="str">"cmt">// Output will be class="num">100 }
把数组尾元素当头读的索引反转
MQL5 里数组默认从下标 0 向尾部递增,但金融序列数据通常最新的在末尾。ArraySetAsSeries() 就是用来把动态数组的索引方向整体反转的函数:第二个参数传 true,原本的最后一个元素会变成下标 0,方便你直接按时间倒序取数,不必每次都用 ArraySize()-1-i 去倒推。 它只对动态数组生效,静态数组大小在编译期固定,调用了也不会改变内存布局。写 EA 或指标时,如果你从 CopyClose() 拿到收盘价缓冲,再套一层 ArraySetAsSeries(),就能用 close[0] 直接表示当前 K 线收价,close[1] 是前一根,逻辑上比正序缓冲直观很多。 下面这段示例先给 5 个元素的动态数组填了 10~50,打印一遍后再反转。反转前 Index 0 输出 10、Index 4 输出 50;反转后 Index 0 输出 50、Index 4 输出 10,索引映射完全颠倒。开 MT5 新建脚本粘进去跑,终端里能直接看到这两组打印差异。 别把正态当圣经:反转只改索引视角,不改内存里数据的物理顺序,所以和其他正序数组做逐元素运算时要先确认两边视角一致,否则会出现错位比对。
ArraySetAsSeries( array[], class=class="str">"cmt">// array to be set as series class="type">bool flag class=class="str">"cmt">// true denotes reverse order of indexing ); class="type">void OnStart() { class=class="str">"cmt">// Declare a dynamic array class="type">int magicalArray[]; class=class="str">"cmt">// Assign values to the array ArrayResize(magicalArray, class="num">5); magicalArray[class="num">0] = class="num">10; magicalArray[class="num">1] = class="num">20; magicalArray[class="num">2] = class="num">30; magicalArray[class="num">3] = class="num">40; magicalArray[class="num">4] = class="num">50; class=class="str">"cmt">// Print elements before setting as series Print("Elements before setting as series:"); Print("Index class="num">0: ", magicalArray[class="num">0]); Print("Index class="num">1: ", magicalArray[class="num">1]); Print("Index class="num">2: ", magicalArray[class="num">2]); Print("Index class="num">3: ", magicalArray[class="num">3]); Print("Index class="num">4: ", magicalArray[class="num">4]); class=class="str">"cmt">// Set the array as a series ArraySetAsSeries(magicalArray, true); class=class="str">"cmt">// Print elements after setting as series Print("Elements after setting as series:"); Print("Index class="num">0: ", magicalArray[class="num">0]); Print("Index class="num">1: ", magicalArray[class="num">1]); Print("Index class="num">2: ", magicalArray[class="num">2]); Print("Index class="num">3: ", magicalArray[class="num">3]); Print("Index class="num">4: ", magicalArray[class="num">4]); } class="type">class="kw">double magicalArray[]; ArrayResize(magicalArray, class="num">5); magicalArray[class="num">0] = class="num">10; magicalArray[class="num">1] = class="num">20; magicalArray[class="num">2] = class="num">30; magicalArray[class="num">3] = class="num">40; magicalArray[class="num">4] = class="num">50; Print("Elements before setting as series:"); Print("Index class="num">0: ", magicalArray[class="num">0]); class=class="str">"cmt">// output will be class="num">10 Print("Index class="num">1: ", magicalArray[class="num">1]); class=class="str">"cmt">// output will be class="num">20 Print("Index class="num">2: ", magicalArray[class="num">2]); class=class="str">"cmt">// output will be class="num">30 Print("Index class="num">3: ", magicalArray[class="num">3]); class=class="str">"cmt">// output will be class="num">40 Print("Index class="num">4: ", magicalArray[class="num">4]); class=class="str">"cmt">// output will be class="num">50 ArraySetAsSeries(magicalArray, true); Print("Elements after setting as series:");
「数组倒序打印的实测输出」
在 MT5 的 MQL5 环境里,若先前把数组按 50、40、30、20、10 的顺序写入 magicalArray,下面这段打印代码会按索引 0 到 4 依次吐出对应值。 直接看输出:索引 0 得到 50,索引 1 得到 40,索引 2 得到 30,索引 3 得到 20,索引 4 得到 10。这说明数组下标从 0 起算,且写入顺序与内存排列一致。 开 MT5 新建脚本粘入下列代码,跑一遍就能确认你的终端行为是否一致;若你用了 ArraySort 或反向填充,输出会不同,需另行核对。
Print("Index class="num">0: ", magicalArray[class="num">0]); class=class="str">"cmt">// output will be class="num">50 Print("Index class="num">1: ", magicalArray[class="num">1]); class=class="str">"cmt">// output will be class="num">40 Print("Index class="num">2: ", magicalArray[class="num">2]); class=class="str">"cmt">// output will be class="num">30 Print("Index class="num">3: ", magicalArray[class="num">3]); class=class="str">"cmt">// output will be class="num">20 Print("Index class="num">4: ", magicalArray[class="num">4]); class=class="str">"cmt">// output will be class="num">10
◍ 用 ArrayGetAsSeries 摸清数组访问方向
在 MT5 的 MQL5 里,ArrayGetAsSeries() 只干一件事:返回数组是否挂了 AS_SERIES 标志。返回 true 意味着元素按反向序列访问,也就是下标 0 对应最新一根 K 线或末尾数据;返回 false 则是普通数组,从头到尾按物理顺序读。写 EA 或指标时若搞错这个方向,取 timeSeriesArray[0] 可能拿到的是最新值而不是第一个赋值,回测里会直接错位。 下面这段代码把两个动态 int 数组都Resize成 5 个元素,timeSeriesArray 赋值 1~5 后调用 ArraySetAsSeries(...,true),regularArray 赋值 5~1 且不设序列标志。随后用 ArrayGetAsSeries 分别判断:前者会打印“从尾到头访问”,后者打印“保持原顺序”。跑完你能在 Experts 日志里看到两条明确输出,验证标志位确实生效。 别把正态当圣经 很多新手以为数组下标永远从左到右,但 MQL5 里 CopyBuffer 拉出来的价格缓冲默认就是 AS_SERIES。用 ArrayGetAsSeries 先探一下再写循环,能少踩一堆“为什么我算的均线是反的”的坑。外汇与贵金属杠杆高,这类底层访问错误可能让策略信号整体反向,实盘前务必在策略测试器里跑一遍确认。
class="type">bool ArrayGetAsSeries( array[] class=class="str">"cmt">// // The array that is being examined for its time series configuration. ); class="type">void OnStart() { class=class="str">"cmt">// Declare two dynamic arrays class="type">int timeSeriesArray[]; class="type">int regularArray[]; class=class="str">"cmt">// Resize the arrays to have class="num">5 elements ArrayResize(timeSeriesArray, class="num">5); ArrayResize(regularArray, class="num">5); class=class="str">"cmt">// Assign values to the arrays timeSeriesArray[class="num">0] = class="num">1; timeSeriesArray[class="num">1] = class="num">2; timeSeriesArray[class="num">2] = class="num">3; timeSeriesArray[class="num">3] = class="num">4; timeSeriesArray[class="num">4] = class="num">5; regularArray[class="num">0] = class="num">5; regularArray[class="num">1] = class="num">4; regularArray[class="num">2] = class="num">3; regularArray[class="num">3] = class="num">2; regularArray[class="num">4] = class="num">1; class=class="str">"cmt">// Set the time series flag for the first array ArraySetAsSeries(timeSeriesArray, true); class=class="str">"cmt">// Check if the dynamic arrays follow the time series convention using if statements if(ArrayGetAsSeries(timeSeriesArray)) { Print("timeSeriesArray is a time series. Elements are accessed from end to beginning."); } else { Print("timeSeriesArray maintains its original order. Elements are accessed from beginning to end."); } if(ArrayGetAsSeries(regularArray)) { Print("regularArray is a time series. Elements are accessed from end to beginning."); } else { Print("regularArray maintains its original order. Elements are accessed from beginning to end."); } } class=class="str">"cmt">// Declare two dynamic arrays class="type">int timeSeriesArray[]; class="type">int regularArray[]; class=class="str">"cmt">// Resize the arrays to have class="num">5 elements ArrayResize(timeSeriesArray, class="num">5); ArrayResize(regularArray, class="num">5); class=class="str">"cmt">// Assign values to the arrays timeSeriesArray[class="num">0] = class="num">1; timeSeriesArray[class="num">1] = class="num">2; timeSeriesArray[class="num">2] = class="num">3; timeSeriesArray[class="num">3] = class="num">4; timeSeriesArray[class="num">4] = class="num">5; regularArray[class="num">0] = class="num">5; regularArray[class="num">1] = class="num">4; regularArray[class="num">2] = class="num">3; regularArray[class="num">3] = class="num">2; regularArray[class="num">4] = class="num">1; class=class="str">"cmt">// Set the time series flag for the first array ArraySetAsSeries(timeSeriesArray, true); class=class="str">"cmt">// Check if the dynamic arrays follow the time series convention using if statements
用 ArrayGetAsSeries 判明数组方向
MT5 里不少历史数据数组默认是「时间序列」形态,索引 0 对应的是最新一根 K 线,越往后越旧。自己建的普通数组则相反,索引 0 是开头,顺序存取。 写 EA 或指标时若搞错方向,取 close[1] 可能拿到的是倒数第二根而不是你以为的「第二根」,回测结果会 silently 失真。 下面这段直接拷进 MT5 脚本就能跑,分别测两个数组的序列属性: [CODE] if(ArrayGetAsSeries(timeSeriesArray)) { Print("timeSeriesArray is a time series. Elements are accessed from end to beginning."); } else { Print("regularArray maintains its original order. Elements are accessed from beginning to end."); } if(ArrayGetAsSeries(regularArray)) { Print("regularArray is a time series. Elements are accessed from end to beginning."); } else { Print("regularArray maintains its original order. Elements are accessed from beginning to end."); } [/CODE] 逐行看:ArrayGetAsSeries 返回 true 表示该数组按时间倒序存,访问要从尾到头;返回 false 就是原顺序从头到尾。timeSeriesArray 通常是 CopyClose 这类函数填进来的,大概率是 true;regularArray 是你自己 ArrayResize 出来的,一般 false。 开 MT5 新建脚本,声明这两个数组各塞点数据,跑完去专家日志看输出,比看文档直观。外汇与贵金属杠杆高,代码逻辑错一处可能让实盘下单反向,验证完再上。
if(ArrayGetAsSeries(timeSeriesArray)) { Print("timeSeriesArray is a time series. Elements are accessed from end to beginning."); } else { Print("regularArray maintains its original order. Elements are accessed from beginning to end."); } if(ArrayGetAsSeries(regularArray)) { Print("regularArray is a time series. Elements are accessed from end to beginning."); } else { Print("regularArray maintains its original order. Elements are accessed from beginning to end."); }
「用 ArrayIsSeries 辨认时间序列数组」
在 MT5 写指标或 EA 时,开盘、最高、最低、收盘这类带时间维度的数据通常以时间序列数组形式存在。ArrayIsSeries() 只干一件事:判断你传进去的数组是不是时间序列,是就返回 true,不是就返回 false,它完全不关心索引是正向还是反向。 容易混的是 ArrayGetAsSeries(),后者只看数组是否设成逆序访问(从后往前读),而 ArrayIsSeries() 看的是“内容是否随时间变化”。两者互补:一个管方向,一个管时间属性。处理外汇或贵金属行情数组时,搞错这两者的区别可能让技术指标计算逻辑整体偏移。 下面这段演示代码很直白:先建一个 double 动态数组,塞了 5 个数值,再丢给 ArrayIsSeries() 判断。 bool isSeries = ArrayIsSeries(priceSeries); 跑出来打印的是“This array is not a time series.”——因为手动 ArrayResize 加赋值的数组,即便你心里当它时序,MT5 底层并不认它是时间序列。新手常在这里卡住:以为填了 1.1 到 1.5 就像价格走势,其实系统只认由图表或内置序列绑定的数组。 开 MT5 新建脚本把代码贴进去跑一遍,你能直接看到 false 的结果;想让它返回 true,得用 CopyClose 这类函数把真实 K 线收盘价绑进数组再判断。外汇和贵金属杠杆高、波动快,用错数组类型可能导致信号错位,实盘前务必在策略测试器里验证。
class="type">bool ArrayIsSeries( array[] class=class="str">"cmt">//the array you want to check if it&class="macro">#x27;s a timeseries. ) class="type">void OnStart() { class=class="str">"cmt">// Declare an array class="type">class="kw">double priceSeries[]; class=class="str">"cmt">// Resize the array and assign values(considering it as a time series) ArrayResize(priceSeries, class="num">5); priceSeries[class="num">0] = class="num">1.1; priceSeries[class="num">1] = class="num">1.2; priceSeries[class="num">2] = class="num">1.3; priceSeries[class="num">3] = class="num">1.4; priceSeries[class="num">4] = class="num">1.5; class=class="str">"cmt">// Check if the array is a time series class="type">bool isSeries = ArrayIsSeries(priceSeries); class=class="str">"cmt">// Print the result if(isSeries) { Print("This array is a time series!"); } else { Print("This array is not a time series."); } } class=class="str">"cmt">// Declare an array class="type">class="kw">double priceSeries[]; class=class="str">"cmt">// Resize the array and assign values(considering it as a time series) ArrayResize(priceSeries, class="num">5); priceSeries[class="num">0] = class="num">1.1; priceSeries[class="num">1] = class="num">1.2; priceSeries[class="num">2] = class="num">1.3; priceSeries[class="num">3] = class="num">1.4; priceSeries[class="num">4] = class="num">1.5; class=class="str">"cmt">// Check if the array is a time series class="type">bool isSeries = ArrayIsSeries(priceSeries); class=class="str">"cmt">// Print the result if (isSeries) { Print("This array is a time series!"); } else { Print("This array is not a time series."); }