📘

MT5 里结构识别的起点

MT5 里结构识别的起点

很多做价格行为的老手在 MT5 上卡住,不是因为看不懂图,而是没把‘结构’落成可验证的对象。这一节先不谈指标,只谈怎么在 MT5 环境里把市场结构从裸 K 中剥离出来。 原文示例发布于 2025 年 12 月 19 日 09:10,截至统计节点已有 1227 次访问、0 条评论,说明这类基础结构拆解的冷启动阅读量并不低,但互动弱——读者多半直接拿去开终端验证,而不是留言。 外汇与贵金属自带高杠杆与跳空风险,用结构法过滤信号时,先确认自己是在 H1 以上周期看大势,还是 M15 以下找入场,这两件事混在一起必亏。 下一步建议直接开 MT5 新建一个空白指标工程,把高低点枚举的逻辑手写一遍,比看十篇图文都管用。

「为什么还不能直接画到图表上」

前一篇讲了模板与类型名称,核心收益是用更少精力覆盖多类型需求——开发效率随模板复用率上升而提高,但终端里 print 信息不等于交易可见。想直接把东西画到 MT5 图表上,很多读者已经按捺不住。 目前积累的只是概念底座:结构清晰、能少写重复代码,但缺少把信息正确组织进实际实现的方法。不是编码条件不允许,而是架构上还没准备好,硬上会反复踩基础错误。 接下来要引的新机制,目标是用更小代价换更安全的实现路径,把已学和将学的串起来,避开那些恼人的低级坑。外汇与贵金属杠杆高、滑点突变频繁,架构没打磨好就交互图表,风险会被放大。 先跟着把这套组织方式吃透,后面接图表交互时,MT5 里改几行就能跑,不用推倒重来。

◍ 结构在内存里怎么摆,比你想的重要

MQL5 里结构(struct)和联合(union)长得很像,但内存布局完全是两回事。联合是所有成员挤同一块内存,大小由最大成员决定;结构里每个成员独立占位,总字节数是各成员之和。把同样的 double、int、short 塞进联合只要 8 字节,塞进结构就是 8+4+2 = 14 字节,这个差值在 MT5 里跑一下就能验证。 第二条规则很硬:结构体内元素的声明顺序,直接决定它们在内存中的排列顺序。我们做组对照——两个成员类型和名字完全一致的结构,只是声明顺序不同,通过联合共享同一块内存赋值,再分别从不同结构读同一个偏移,终端打印出来的值会不一样。图 05 那种「内存没动、读出来变了」的怪象,根源就是你在读「被解释的内存位置」而不是变量本身。 这事儿平时写指标不显山露水,一旦涉及把结构写进文件或跨进程传数据就炸。BMP 文件头就是按固定结构顺序排的,你若中途调换结构里字段顺序,旧文件按新结构读就错位。做持久化前给结构加版本号,是性价比最高的防坑手段。外汇和贵金属 EA 涉及文件存参时,这种错位可能让风控参数静默失效,属典型高风险暗坑。 [CODE] 01. //+------------------------------------------------------------------+

  • #property copyright "Daniel Jose"
  • //+------------------------------------------------------------------+
  • void OnStart(void)
  • {
  • struct st_01
  • {[/CODE]
MQL5 / C++
class="num">01. class=class="str">"cmt">//+------------------------------------------------------------------+
class="num">02. class="macro">#class="kw">property copyright "Daniel Jose"
class="num">03. class=class="str">"cmt">//+------------------------------------------------------------------+
class="num">04. class="type">void OnStart(class="type">void)
class="num">05. {
class="num">06.     class="kw">struct st_01
class="num">07.     {

struct 与 union 的内存占用差异

在 MQL5 里定义复合类型时,struct 和 union 最易被混淆。struct 的每个成员独占内存,union 的所有成员共享同一段内存,这一区别直接决定你开仓辅助结构占多少字节。 下面这段可直接贴进 MT5 脚本跑:struct st_01 含 short/int/double 各一,union un_01 同构型但共享内存。PrintFormat 输出显示,st_01 大小为 16 字节,un_01 大小为 8 字节(double 对齐主导)。 [CODE] #property copyright "Daniel Jose" void OnStart(void) { struct st_01 { short value_01; int value_02; double value_03; }; union un_01 { short value_01; int value_02; double value_03; }; PrintFormat("Size of struct is: [%d bytes]\nSize of union is: [%d bytes]", sizeof(st_01), sizeof(un_01)); } [/CODE] 逐行看:struct 定义三成员各占空间,short(2)+int(4)+double(8) 经对齐补位到 16;union 定义后三成员叠放,取最大成员 double 的 8 字节。sizeof 运算符在运行时返回实际字节数。 若你做多品种持仓快照,用 union 装临时数值能省一半内存,但注意共享区写后即覆盖,别在同一根 K 线里又读旧值。外汇与贵金属杠杆高,这类底层结构错误可能让风控模块算错敞口,实盘前务必在策略测试器验证。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
class="type">void OnStart(class="type">void)
{
   class="kw">struct st_01
   {
      class="type">short  value_01;
      class="type">int    value_02;
      class="type">class="kw">double value_03;
   };
   union un_01
   {
      class="type">short  value_01;
      class="type">int    value_02;
      class="type">class="kw">double value_03;
   };
   PrintFormat("Size of class="kw">struct is: [%d bytes]\nSize of union is: [%d bytes]", class="kw">sizeof(st_01), class="kw">sizeof(un_01));
}

「共用体里改一个字段另一个跟着变」

MQL5 的 union 让多个结构共享同一段内存,这在做底层数据观测时很有用,但坑也明显:你以为只写了 data_01,其实 data_02 也被改了。下面这段把 st_01 和 st_02 塞进同一个 un_01,两者首字节布局一致,所以互相覆盖。 代码里先用 ZeroMemory 把 Info 整块清零,再给 Info.data_01.value_01 赋 -18254。由于 data_01 和 data_02 的 value_01 都落在共用体开头,PrintX(Info.data_02.value_01) 打出来的不是 0,而是 -18254——这就是内存共享的直接证据。 把 st_02 的成员顺序调换(value_02 在前、value_01 在后),同样的赋值会让 data_02.value_01 读到错位数据,ArrayPrint(Info.arr) 能看出字节排布差异。外汇和贵金属 EA 里用这类技巧解析报文或紧凑存储时,高风险在于字节对齐和编译器padding,建议开 MT5 跑一遍确认自己平台的大小端和补齐规则。 别把结构顺序当无所谓 成员声明顺序直接决定共用体内的偏移。st_01 与 st_02 仅调换两个字段,就可能让同一赋值产生完全不同的跨结构读取结果,写前先 ArrayPrint 核对。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
class="macro">#define PrintX(X) Print(class="macro">#X, " is ", X)
class="type">void OnStart(class="type">void)
{
   class="kw">struct st_01
   {
      class="type">short  value_01;
      class="type">int    value_02;
      class="type">class="kw">double value_03;
   };

   class="kw">struct st_02
   {
      class="type">short  value_01;
      class="type">int    value_02;
      class="type">class="kw">double value_03;
   };

   union un_01
   {
      st_01 data_01;
      st_02 data_02;
      class="type">uchar arr[class="kw">sizeof(st_01)];
   };

   un_01 Info;

   ZeroMemory(Info);
   Info.data_01.value_01 = -class="num">18254;

   ArrayPrint(Info.arr);

   PrintX(Info.data_02.value_01);
}

◍ 用 FileWriteStruct 把结构体直接落盘

MQL5 里做本地缓存不必自己拼字节,FileWriteStruct 能按内存布局把整个结构体写进二进制文件。上面这段脚本先定义了一个含 short、int、double 三字段的结构体 st_01,用 ZeroMemory 清零后打印出 sizeof(Info) 的实际占用——在常见 64 位 MT5 环境下往往是 16 字节而非直观的 14 字节,因为编译器对 double 做了 8 字节对齐。

逻辑上它先尝试以 FILE_READFILE_BIN 打开 Testing.bin,若文件不存在就走 else 分支:给三个字段赋具体值(-8096、78934、10000.674),再以 FILE_WRITEFILE_BIN 打开并写回。注意第 31 行 FileWriteStruct 的返回值就是实际写入字节数,和前面 sizeof 打印应对得上。

跑完之后用 PrintX 宏把三个值回显到日志,你可以直接开 MT5 新建脚本粘进去编译,首次运行写文件、二次运行读文件,验证值是否被原样还原。外汇与贵金属品种数据本地化存盘虽方便,但实盘环境文件权限和路径异常可能引发句柄错误,属于高风险操作,建议先在策略测试器里跑通。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define PrintX(X) Print(class="macro">#X, " is ", X)
class=class="str">"cmt">//+----------------+
class="macro">#define def_FileName "Testing.bin"
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart(class="type">void)
{
   class="kw">struct st_01
   {
      class="type">short  value_01;
      class="type">int    value_02;
      class="type">class="kw">double value_03;
   }Info;

   class="type">int     handle;

   ZeroMemory(Info);
   
   PrintFormat("Size of class="kw">struct is %d bytes", class="kw">sizeof(Info));

   if ((handle = FileOpen(def_FileName, FILE_READ | FILE_BIN)) != INVALID_HANDLE)
      PrintFormat("Reading %d bytes of file data.", FileReadStruct(handle, Info));
   else
   {
      Info.value_01 = -class="num">8096;
      Info.value_02 = class="num">78934;
      Info.value_03 = class="num">1e4 + class="num">0.674;

      PrintFormat("Writing %d bytes of data in the file.", FileWriteStruct(handle = FileOpen(def_FileName, FILE_WRITE | FILE_BIN), Info));
   }
   FileClose(handle);

   PrintX(Info.value_01);
   PrintX(Info.value_02);
   PrintX(Info.value_03);
}
class=class="str">"cmt">//+------------------------------------------------------------------+

一点提醒

这套结构入门写到这算第一轮,前面几篇铺垫的概念不是装饰,而是你之后在 MT5 里写 struct 传参、做继承时不迷路的地基。 附件 Anexo.zip 只有 2.24 KB,建议直接下到本地用 MetaEditor 打开,照着里面两条基础规则改一处字段名再编译,比纯看文接收得快。 外汇和贵金属杠杆高、滑点跳空频繁,任何代码结构上的理解偏差都可能放大实盘风险,练手阶段务必先在策略测试器跑历史数据。 等这两条规则变成肌肉记忆,后面继承、指标那几篇才能接得上,不至于回头补坑。

常见问题

定义一个含时间、价格、方向字段的 struct 数组,每次出现破位把数据压进数组,后续直接遍历比对即可,比全局变量散着存更清晰。
编译器会按最大字段做内存对齐,比如混用 int 和 double 时会补空位,用 sizeof() 打印实际占用,别手动加字段算。
可以,小布能读取你的结构标记并生成带时间点与形态的复盘摘要,打开对应品种页就能直接看,不用自己手抄。
不是错,union 所有字段共用同一段内存,改其中一个会覆盖其余,存互斥状态才用共用体,否则用 struct。
用 FileWriteStruct 把整个结构体按二进制直接写文件,读取用 FileReadStruct,比拼字段写字符串快且不易错位。