MQL5 简介(第 5 部分):MQL5 数组函数入门指南·进阶篇
🧮

MQL5 简介(第 5 部分):MQL5 数组函数入门指南·进阶篇

(2/3)· 从 ArrayFree 到 ArrayIsSeries,六个函数把数组内存与方向控制一次讲透

含代码示例偏理论 第 2/3 篇
很多人以为 ArrayBsearch 能直接搜任意数组,结果返回错位索引还查不出原因。数组函数大多有前置条件,排序状态或序列方向不对,输出就会 silently 失真。先弄清每个函数的隐含契约,比背参数表更省时间。

◍ 动态数组清空与复用: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 清掉临时序列,可能比一直留着大数组更稳。外汇与贵金属波动剧烈,这类内存习惯虽不直接交易,但能降低卡顿导致的滑点概率。

MQL5 / C++
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 新建脚本粘进去跑,终端里能直接看到这两组打印差异。 别把正态当圣经:反转只改索引视角,不改内存里数据的物理顺序,所以和其他正序数组做逐元素运算时要先确认两边视角一致,否则会出现错位比对。

MQL5 / C++
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 或反向填充,输出会不同,需另行核对。

MQL5 / C++
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 先探一下再写循环,能少踩一堆“为什么我算的均线是反的”的坑。外汇与贵金属杠杆高,这类底层访问错误可能让策略信号整体反向,实盘前务必在策略测试器里跑一遍确认。

MQL5 / C++
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 新建脚本,声明这两个数组各塞点数据,跑完去专家日志看输出,比看文档直观。外汇与贵金属杠杆高,代码逻辑错一处可能让实盘下单反向,验证完再上。

MQL5 / C++
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 线收盘价绑进数组再判断。外汇和贵金属杠杆高、波动快,用错数组类型可能导致信号错位,实盘前务必在策略测试器里验证。

MQL5 / C++
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.");
}
把序列方向诊断交给小布
ArraySetAsSeries 与 ArrayGetAsSeries 的方向翻转常导致指标计算偏差,这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到当前数组的序列状态,你只管核对逻辑。

常见问题

释放后内存归还系统,再访问会得到空或非法数据,应在 Free 前完成所有取值或拷贝。
对动态与静态数组均可设序列方向,但时间序列对象本身的方向由平台管理,手动翻转可能冲突。
用 ArrayGetAsSeries 取布尔值,再结合 ArrayIsSeries 确认其是否为平台序列对象,二者含义不同。
可以,小布在品种页会标注数组序列状态异常,辅助你定位因 AsSeries 设错引起的信号偏移。
说明该数组大小在编译期固定,不能用 ArrayResize 改变长度,只能整体重初始化。