MQL5中的结构及其数据打印方法(基础篇)
📘

MQL5中的结构及其数据打印方法(基础篇)

第 1/3 篇

用 MQL5 结构把订单字段一次性打印出来

在 MQL5 里,结构(struct)能把一组相关字段打包成一个类型,方便在调试时整体观察。对做价格行为分析的交易者来说,把某根 K 线的开高低收、成交量、时间打包打印,比逐个 Print 更不容易漏看。 下面这段结构定义了一个常见的行情快照,包含时间、四个价格和成交量。把它塞进数组就能批量存历史 K 线,再用 Print 输出验证数据是否和 MT5 图表一致。 在 MT5 里新建脚本贴入代码,跑在 EURUSD 的 M1 周期上,控制台会按行打出最近 10 根 K 线。外汇与贵金属杠杆高、滑点大,打印出的历史数据仅用于逻辑验证,不代表任何未来走势概率。

MQL5 / C++
class="kw">struct BarData
{
   class="type">class="kw">datetime time;   class=class="str">"cmt">// 对应K线开盘时间
   class="type">class="kw">double  open;    class=class="str">"cmt">// 开盘价
   class="type">class="kw">double  high;    class=class="str">"cmt">// 最高价
   class="type">class="kw">double  low;     class=class="str">"cmt">// 最低价
   class="type">class="kw">double  close;   class=class="str">"cmt">// 收盘价
   class="type">long    volume;  class=class="str">"cmt">// 成交量(tick数)
};

BarData bars[class="num">10];
class=class="str">"cmt">// 取最近10根K线填入结构数组
for(class="type">int i=class="num">0;i<class="num">10;i++)
{
   bars[i].time   = iTime(_Symbol,_Period,i);
   bars[i].open   = iOpen(_Symbol,_Period,i);
   bars[i].high   = iHigh(_Symbol,_Period,i);
   bars[i].low    = iLow(_Symbol,_Period,i);
   bars[i].close  = iClose(_Symbol,_Period,i);
   bars[i].volume = iVolume(_Symbol,_Period,i);
   Print("Bar ",i," time=",bars[i].time," O=",bars[i].open," H=",bars[i].high," L=",bars[i].low," C=",bars[i].close," V=",bars[i].volume);
}

「时间价格与深度数据的结构地图」

在 MT5 的 EA 与指标开发里,三类核心行情结构必须先摸清楚:MqlDateTime 管时间拆解,MqlTick 管逐笔报价,MqlRates 管 K 线聚合,MqlBookInfo 管市场深度。 MqlDateTime 把时间戳拆成 year、mon、day、hour、min、sec、day_of_week、day_of_year 八个字段,方便按周期过滤信号。MqlTick 除 bid/ask/last 外,还带 time_msc(毫秒级)与 flags 分时标志,last_volume 与 volume_real 提供更高精度成交量。 MqlRates 是常规 K 线载体:time、open、high、low、close 之外,tick_volume 与 real_volume 分开记录,spread 直接给出点差原始值。MqlBookInfo 则按 type/price/volume 描述订单簿每一档,适合做深度失衡策略。 原文给出的目录显示,每个结构都配有打印方法与辅助函数,并至少附一段使用示例;开 MT5 按 F1 搜 MqlTick 即可对照字段写第一句 Print(tick.bid)。外汇与贵金属杠杆高,结构用错会直接错判报价精度,回测前先确认字段含义。

◍ MQL5 十二种预置结构到底归谁填

MQL5 里预定义了 12 种结构,用来存和传服务信息。MqlDateTime 管日期时间,MqlRates 给历史价量点差,MqlTick 取实时最优价,MqlBookInfo 拉深度报价,经济日历结构直连源头发宏观事件——这些字段基本由终端子系统或交易服务器回填。 另一类是 MqlParam、MqlTradeRequest 这种,靠你自己在代码里按接口要求填字段,再拿去建指标句柄或发交易请求;MqlTradeCheckResult、MqlTradeResult、MqlTradeTransaction 则是发单前后由系统返回校验与成交响应。 分清楚「谁填」很关键:服务器回填的结构要用 ArrayPrint() 打表看,或逐字段读出来做程序化判断;自己填的结构若字段错漏,OrderSend 直接报错。外汇和贵金属波动大、滑点频繁,结构字段填错可能在高风险时段发出畸形订单。 标准 ArrayPrint() 出的是表格,但盯大量日志时,把结构压成一行(带字段名+值)反而更易扫;有些场景又要展开字段描述和多格式呈现。下边先给个一行打印思路的骨架,后面章节再写完整自定义函数。

把时间拆进八个整型字段

MqlDateTime 是 MT5 里承载日期时间的基础结构,内部就是八个 int 字段:年、月、日、时、分、秒,外加星期几和年内第几天(1 月 1 日计为 0)。想填结构别手写,用 TimeCurrent()、TimeGMT()、TimeLocal()、TimeTradeServer() 这几个都有引用传参的重载,或者直接用 TimeToStruct() 把 datetime(1970-01-01 起秒数)灌进去,成功返回 true。 TimeToString() 只吃 datetime 不吃结构,别绕回去转类型。要整结构打印,用 ArrayPrint() 最直:它按表输出简单结构数组,列是字段、行是数组元素。只显示一个时间就开长度为 1 的数组;若按 D1 拉一个交易周,通常就是 5 根 K 线对应 5 行。 外汇和贵金属行情受杠杆与跳空影响,时间字段错位可能让回测与实盘偏差放大,验证前先确认终端时区与交易服务器时区。

MQL5 / C++
class="kw">struct class="type">MqlDateTime
  {
   class="type">int year;            class=class="str">"cmt">// year
   class="type">int mon;             class=class="str">"cmt">// month
   class="type">int day;             class=class="str">"cmt">// day
   class="type">int hour;            class=class="str">"cmt">// hour
   class="type">int min;             class=class="str">"cmt">// minutes
   class="type">int sec;             class=class="str">"cmt">// seconds
   class="type">int day_of_week;     class=class="str">"cmt">// day of the week(class="num">0-Sunday, class="num">1-Monday, ... ,class="num">6-Saturday)
   class="type">int day_of_year;     class=class="str">"cmt">// number of a day in the year(1st of January has number class="num">0)
   };
class="type">bool  TimeToStruct(
   class="type">class="kw">datetime       dt,           class=class="str">"cmt">// date and time
   class="type">MqlDateTime&   dt_struct     class=class="str">"cmt">// structure for accepting values 
   );

「把 MqlDateTime 打成人能读的日志」

MT5 里 MqlDateTime 结构默认靠 ArrayPrint() 输出,可读性很差。上面那段样例里,2023 年 7 月 17 日 12:08:37 在日志里只显示成 2023 7 17 12 8 37 1 1971 是周一、197 是年内第几天,扫一眼根本分不清。 直接封装一个 MqlDateTimePrint() 把结构塞进单元素数组再 ArrayPrint,能原样打印单条时间;若要批量,就传 datetime 数组填进 MqlDateTime 数组统一输出。 真要省脑力的话,自己写辅助函数把星期和月份转成文字。MQL5 有内建星期枚举,转小写仅首字母大写即可;月份没有枚举,用 switch 按数字返字符串。再包一层返回「DW, Month DD, YYYY, HH:MM:SS」短格式的函数,日志里看柱形时间一眼就懂。 表格视图也能做:给字段标题传缩进与宽度参数(默认 0 即无缩进),年、月、日、时、分、秒、星期、年内天数的字段就能对齐排开。下面这段代码是最小可跑样例,开 MT5 新建脚本粘进去就能验证当前时间的结构打印。

MQL5 / C++
class="type">void OnStart()
  {
class=class="str">"cmt">//--- Declare a date structure variable
   class="type">MqlDateTime  time;
class=class="str">"cmt">//--- Get the current time and at the same time fill in the date structure
   TimeCurrent(time);
class=class="str">"cmt">//--- Declare an array with the class="type">MqlDateTime type and write the data of the filled structure into it

   class="type">MqlDateTime array[class="num">1];
   array[class="num">0]=time;
class=class="str">"cmt">//--- Display the header and time using the standard ArrayPrint()
   Print("Time current(ArrayPrint):");
   ArrayPrint(array);
   class=class="str">"cmt">/* Sample output:
       Time current(ArrayPrint):
         [year] [mon] [day] [hour] [min] [sec] [day_of_week] [day_of_year]
       [class="num">0]   class="num">2023     class="num">7    class="num">17     class="num">12     class="num">8    class="num">37             class="num">1           class="num">197
   */
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Take a date structure and display its data to the journal.        |
class=class="str">"cmt">//| Use ArrayPrint() for display                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void MqlDateTimePrint(class="kw">const class="type">MqlDateTime& time_struct)
  {
class=class="str">"cmt">//--- Declare an array with the class="type">MqlDateTime type and write the data of the obtained structure into it
   class="type">MqlDateTime array[class="num">1];
   array[class="num">0]=time_struct;
class=class="str">"cmt">//--- Print the array
   ArrayPrint(array);
   class=class="str">"cmt">/* Sample output:
       Time current(ArrayPrint):
         [year] [mon] [day] [hour] [min] [sec] [day_of_week] [day_of_year]
       [class="num">0]   class="num">2023     class="num">7    class="num">17     class="num">12     class="num">8    class="num">37             class="num">1           class="num">197
   */
  }
class="type">void OnStart()
  {
class=class="str">"cmt">//--- Declare a date structure variable
   class="type">MqlDateTime  time;
class=class="str">"cmt">//--- Get the current time and at the same time fill in the date structure
   TimeCurrent(time);
class=class="str">"cmt">//--- Display the header and time using the standard ArrayPrint()
   Print("Time current(ArrayPrint):");
   MqlDateTimePrint(time);
   /* Sample output:
       Time current(ArrayPrint):
         [year] [mon] [day] [hour] [min] [sec] [day_of_week] [day_of_year]

◍ 把时间数组拆成可读结构

做跨周期或批量 K 线统计时,datetime 整数不直观,直接打印就是一串秒数,肉眼难对应到「周几、第几天」。下面这个函数把 datetime 数组整体转成 MqlDateTime 结构数组,再用 ArrayPrint 输出成表。 以 GBPUSD H1 最近 10 根 bar 为例,转换后首行输出为:2023 年 7 月 17 日 7 时 0 分 0 秒,星期一,年内第 197 天。这种结构让你在 EA 日志里一眼看清时间分布,而不是去心算时间戳。 函数先按传入数组大小 Resize 结构体数组,再用 TimeToStruct 逐元素转换,任何一步失败都会带错误码打印出来,方便你定位是空数组还是运行时错误。外汇与贵金属行情受杠杆与跳空影响,这类时间解析仅用于辅助判断,不代表任何方向概率。

MQL5 / C++
class="type">void MqlDateTimePrint(class="kw">const class="type">class="kw">datetime& array_time[])
  {
class=class="str">"cmt">//--- Declare a dynamic array of the class="type">MqlDateTime type
   class="type">MqlDateTime array_struct[];
class=class="str">"cmt">//--- Get the size of the array passed to the function
   class="type">int total=(class="type">int)array_time.Size();
class=class="str">"cmt">//--- If an empty array is passed, report on that and leave the function
   if(total==class="num">0)
     {
       PrintFormat("%s: Error. Empty array.",__FUNCTION__);
       class="kw">return;
     }
class=class="str">"cmt">//--- Change the size of the class="type">MqlDateTime array to match the size of the class="type">class="kw">datetime array
   ResetLastError();
   if(ArrayResize(array_struct,total)!=total)
     {
       PrintFormat("%s: ArrayResize() failed. Error %s",__FUNCTION__,(class="type">class="kw">string)GetLastError());
       class="kw">return;
     }
class=class="str">"cmt">//--- Convert dates from the class="type">class="kw">datetime array into the date structure in the class="type">MqlDateTime array
   for(class="type">int i=class="num">0;i<total;i++)
     {
       ResetLastError();
       if(!TimeToStruct(array_time[i],array_struct[i]))
         PrintFormat("%s: [%s] TimeToStruct() failed. Error %s",__FUNCTION__,(class="type">class="kw">string)i,(class="type">class="kw">string)GetLastError());
     }
class=class="str">"cmt">//--- Print the filled class="type">MqlDateTime array
   ArrayPrint(array_struct);
  }

常见问题

用结构把订单字段打包成一个变量,再逐字段打印,避免零散变量漏写。复制文章里的结构定义到编辑器即可直接跑。
深度数据有对应的预置结构地图,买卖挂单量价分别归在固定字段。按文章给的字段表对照读取就不会错位。
小布可接管结构打印与日志生成的重复劳动,你打开对应品种页就能直接看整理好的字段,不必手敲代码。
拆成整型便于写日志和做条件判断,直接打时间戳人读不懂。用文章里的格式化片段可转成可读文本。
把数组元素逐个填进时间结构再打印,文章给了拆数组的循环示例,复制改品种名就能验证。