掌握 MQL5:从入门到精通(第四部分):关于数组、函数和全局终端变量(基础篇)
「数组、函数与全局终端变量先过一遍」
MQL5 里数组不是简单容器,它分静态、动态、多维三类,动态数组在运行时可用 ArrayResize 改变长度,这是写 EA 时缓存 K 线序列的常用手段。函数分自定义与内置,自定义函数必须声明返回类型,否则编译直接报错;全局终端变量则通过 GlobalVariableSet 写入终端内存,跨 EA 也能读取,适合做多图表协同的状态锁。 外汇与贵金属杠杆高、滑点跳空频繁,用全局变量做跨品种信号同步时,要意识到终端重启会清空未持久化的全局变量,概率上可能造成信号断层。 下面这段演示了动态数组扩容与全局变量写入的最小骨架,开 MT5 新建脚本粘进去能直接编译跑通:
class="type">int arr[]; class="type">int init_flag=class="num">0; class="type">void OnStart() { ArrayResize(arr,class="num">10); for(class="type">int i=class="num">0;i<class="num">10;i++) arr[i]=i; GlobalVariableSet("my_flag",class="num">1); init_flag=(class="type">int)GlobalVariableGet("my_flag"); }
从变量到数组:MQL5 数据体系的三个跳板
前面的文章已经把 MQL5 里「数据怎么存」讲透了:数据要么进变量、要么进常量;MQL5 是强类型语言,每条数据都有类型,编译器靠类型分配内存并拦掉逻辑错误。类型分简单(基本)和复杂(用户定义)两种。要用这些数据,至少得声明一个函数,而任意代码块都能拆到独立文件里,再用 #include 预处理器拉进项目。 这一节往后要铺三个全局主题:数据数组,用来收尾程序内部的数据主体部分;全局终端变量,解决不同 MQL5 程序之间的简单数据交换;以及函数自身特性与它和变量之间的相互作用。 对外汇、贵金属交易者来说,MT5 里的强类型不是语法洁癖——类型搞错可能在回测里不报错,但实盘高频tick处理时直接内存异常,这类风险在杠杆品种上会被放大。
◍ 数组在 MQL5 里的真实用法
数组就是一堆同类型数据的容器,声明时写清类型和名字,再跟一对方括号,里面可以给定元素个数。MQL5 里价格历史、K线开盘时间、成交量这类序列数据,天然适合用数组装。 索引从 0 开始是铁律,最后一个元素的位置永远是 size - 1。比如一个长度为 2 的数组,能用的下标只有 0 和 1,写 myArray[2] 会越界报错。 声明时直接用花括号初始化也行,和结构体的写法一样,省得后面逐条赋值。下面这段在 MT5 里建个脚本跑一下,日志会打出 315 和 4.00,验证下标和初始化逻辑。
<span class="keyword">class="type">int</span> myArray[<span class="number">class="num">2</span>]; <span class="comment">class=class="str">"cmt">// Describes an array of integers containing two elements</span> <span class="comment">class=class="str">"cmt">// Fill the array with values:</span> myArray[<span class="number">class="num">0</span>] = <span class="number">class="num">3</span>; myArray[<span class="number">class="num">1</span>] = <span class="number">class="num">315</span>; <span class="comment">class=class="str">"cmt">// Output the last element of this array to the log:</span> <span class="functions">Print</span>(myArray[<span class="number">class="num">1</span>]); <span class="comment">class=class="str">"cmt">// class="num">315</span> <span class="keyword">class="type">class="kw">double</span> anotherArray[<span class="number">class="num">2</span>] = {<span class="number">class="num">4.0</span>, <span class="number">class="num">5.2</span>}; <span class="comment"> class=class="str">"cmt">// A two-element array is initialized with two values</span> <span class="functions">Print</span>( <span class="functions">DoubleToString</span>(anotherArray[<span class="number">class="num">0</span>],class="num">2) ); <span class="comment">class=class="str">"cmt">// Output class="num">4.00</span>
「嵌套方括号就是多维数组的骨架」
把数组当成容器,它里面还能套数组,这就成了多维数组。用书籍做类比最直观:字符排成行是第一维,行堆成段落是第二维,段落凑成一页是第三维。MQL5 里每多一层嵌套,就往左加一对方括号,外层容器括号始终在内侧容器的左边。 语言层面有两条硬限制:总维数不能超过 4,任意单一维度的最大元素数为 2147483647。初始化写法跟一维几乎一样,只是用花括号逐层列出子数组的元素。 下面这段代码把一维到三维的声明、赋值和读取都摊开了。char 类型的 paragraphArray[2][22] 表示 2 行 22 列;Print(CharToString(paragraphArray[1][3])) 会输出 'a',因为第二行(索引1)按序填了 'T','h','a'… 所以第 4 个字符是 a。末尾的 arrayToInitialize 用双层花括号直接初始化了 2×5 的整型矩阵。
class="type">char stringArray[class="num">21]; class=class="str">"cmt">// One-dimensional array class="type">char paragraphArray[class="num">2][class="num">22]; class=class="str">"cmt">// Two-dimensional array class="type">char pageArray[class="num">3][class="num">2][class="num">22]; class=class="str">"cmt">// Three-dimensional array class=class="str">"cmt">// Filling a two-dimensional array paragraphArray[class="num">0][class="num">0]=&class="macro">#x27;T&class="macro">#x27;; paragraphArray[class="num">0][class="num">1]=&class="macro">#x27;h&class="macro">#x27;; class=class="str">"cmt">// … paragraphArray[class="num">1][class="num">20]=&class="macro">#x27;n&class="macro">#x27;; paragraphArray[class="num">1][class="num">21]=&class="macro">#x27;.&class="macro">#x27;; class=class="str">"cmt">// Access to an arbitrary element of a two-dimensional array Print(CharToString(paragraphArray[class="num">1][class="num">3])); class=class="str">"cmt">// Will print "a" (why?) class="type">int arrayToInitialize [class="num">2][class="num">5] = { {class="num">1,class="num">2,class="num">3,class="num">4,class="num">5}, {class="num">6,class="num">7,class="num">8,class="num">9,class="num">10} }
动态数组的内存预留与尾插套路
MQL5 里处理历史记录、订单列表这类数量随时变的集合,静态数组不够用,得用动态数组:声明时方括号留空,初始长度就是 0,直接下标访问会直接崩程序。 开用之前必须调 ArrayResize 定大小。这个函数第一参是数组本身,第二参是新长度,第三参 reserve_size 可省略;若提前知道上限(比如最多 100 条),把第三参设成 100,实际长度仍由第二参决定(示例里是 1),但系统一次性多留 99 个元素的冗余内存。 为什么不直接每次加一个元素就 Resize 一次?因为每次向操作系统要内存,对 CPU 来说都是一笔不短的开销,要 1 个和要一批耗时差不多。所以一次预留一大块再慢慢填,比零敲碎打扩展快得多;当然内存有限,预留多少得在速度和占用间取平衡。 往尾端加数据是动态数组最高频的动作。ArraySize 返回当前元素数,配合 ArraySize+1 再 ArrayResize 就能追加。示例里 reserve 设 2,加第二个元素时只有写值那行变,其余完全重复,实际工程里应该抽成自定义函数。 多维动态数组只许第一维不写长度,例如 int a[][12] 合法,int b[][] 直接编译报错。真要双动态维,用结构包一个动态数组再搞结构数组,或者以后用类解决。 外汇与贵金属 EA 里这类数组操作出错会直接终止策略,复盘前建议在 MT5 策略测试器里跑一遍下方代码确认行为。
class="type">int dinamicArray []; ArrayResize(dinamicArray, class="num">1); class=class="str">"cmt">// The first parameter is the array and the second is the new size ArrayResize(dinamicArray,class="num">1, class="num">100); class=class="str">"cmt">// The third parameter is the reserved(excess) size dinamicArray[class="num">0] = class="num">3; class=class="str">"cmt">// Now our array contains exactly one element(see example class="num">7), its index is class="num">0 class="type">int size, class=class="str">"cmt">// Number of elements in the array lastElemet; class=class="str">"cmt">// Index of the last element class="type">char stringArray[]; class=class="str">"cmt">// Our example dynamic array. class=class="str">"cmt">// Immediately description its size is class="num">0 (array cannot contain elements) class=class="str">"cmt">// add an element to the end. size = ArraySize(stringArray); class=class="str">"cmt">// Find the current size of the array size++; class=class="str">"cmt">// Array size should increase by class="num">1 ArrayResize(stringArray, size, class="num">2); class=class="str">"cmt">// Resize the array. In our example, the array will have no more than two elements. lastElemet = size - class="num">1; class=class="str">"cmt">// Numbering starts from class="num">0, so the number of the last element is class="num">1 less than the size of the array stringArray[lastElement] = &class="macro">#x27;H&class="macro">#x27;; class=class="str">"cmt">// Write the value class=class="str">"cmt">// Now add one more element. The sequence of actions is absolutely the same. size = ArraySize(stringArray); class=class="str">"cmt">// Find the current size if the array size++; class=class="str">"cmt">// Array size should increase by class="num">1 ArrayResize(stringArray, size, class="num">2); class=class="str">"cmt">// Resize the array. In our example, the array will have no more than two elements. lastElemet = size - class="num">1; class=class="str">"cmt">// Numbering starts from class="num">0, so the number of the last element is class="num">1 less than the size of the array stringArray[lastElement] = &class="macro">#x27;i&class="macro">#x27;; class=class="str">"cmt">// Write the value class=class="str">"cmt">// Note that when adding the second element in this way, only on line changes: class=class="str">"cmt">// the one that writes the actual value to a specific cell. class=class="str">"cmt">// class=class="str">"cmt">// This means that the solution can be written in a shorter form. For example, by creating a separate custom function for it. class="type">int a [][class="num">12]; class=class="str">"cmt">// It&class="macro">#x27;s ok class=class="str">"cmt">// class="type">int b [][]; // Compilation error: only the first index can be dynamic class="kw">struct DinArray { class="type">int a []; }; DinArray dinamicTwoDimensions []; class=class="str">"cmt">// Dynamic array of structures ArrayResize( dinamicTwoDimensions, class="num">1 ); class=class="str">"cmt">// Set size of the outer dimension ArrayResize( dinamicTwoDimensions[class="num">0].a, class="num">1 ); class=class="str">"cmt">// Set size of the internal dimension
◍ 给动态二维数组首格赋值
在 MQL5 里用结构体数组套数组做动态二维存储时,第一维下标 0 对应首个结构体实例,其内部的 a[] 数组成员需用显式下标写入。 上面这行把 dinamicTwoDimensions[0].a[0] 设为 12,等价于在首行首列单元格写入一个整型数据,后续可借此做矩阵类的价格状态缓存。 外汇与贵金属市场波动剧烈、杠杆风险高,这类内存结构仅用于本地计算辅助,不代表任何方向判断。
dinamicTwoDimensions[class="num">0].a[class="num">0] = class="num">12; class=class="str">"cmt">// Use cell to write data