运用 MQL5 和 MQL4 开发品种选择和导航实用程序(基础篇)
📘

运用 MQL5 和 MQL4 开发品种选择和导航实用程序(基础篇)

第 1/3 篇

◍ 在 MT5 里挑品种别靠手点

做价格行为分析前,先得把盯盘品种快速筛出来。MetaTrader 5 自带的市场报价窗口在品种多的时候很笨重,手动翻找浪费时间,也打断节奏。 Roman Klymenko 在 2019 年 1 月发布的品种导航实用程序思路,用 MQL5 直接读写市场报价列表,把筛选逻辑交给代码。该帖原始阅读量 3 566,说明这类基础工具在长期交易者里有真实需求。 外汇与贵金属杠杆高、滑点跳空频繁,先锁定要盯的标的再谈形态,比盲目扫所有品种更稳。开 MT5 按 F4 进 MetaEditor,就能照着下面这段把品种名打印出来验证。

MQL5 / C++
class="type">void OnStart()
  {
   class="type">int total=SymbolsTotal(false);
   for(class="type">int i=class="num">0;i<total;i++)
     {
      class="type">class="kw">string name=SymbolName(i,false);
      Print(name);
     }
  }

为什么需要一个自动选股过滤器

有经验的交易者都明白,盯盘最耗神的不是下单和扛单,而是从一堆品种里挑出能做的标的和入场位。 如果你只做 1-2 个货币对,这不算问题;但当你的池子扩到数百支股票加几十个外汇品种时,纯人工扫一遍可能要花掉几个小时,贵金属和高杠杆外汇的高波动还会让这几个小时里的机会窗口不断塌缩。 本文要做的 EA 不替你决策,只干三件实活:按预设条件预先过滤股票,吐出一张合格清单;用这张清单直接简化 MT5 品种导航;在清单里附上制定决策所需的辅助数据。 对交易者来说,这意味着你可以把几个小时的手工筛选压到几秒,但外汇与贵金属属高风险品种,过滤结果只降低工作量,不替代对信号的概率判断。

「跨平台 EA 骨架怎么搭」

很多经纪商至今没给 MT5 账户,所以这套 EA 模板从一开始就埋了 MT4 兼容线:和向导生成的模板几乎一样,唯独多了 #property strict。这行在 MT4 里是正常运行的硬要求,对 MT5 没副作用,提前写进模板省得后面改。 初始化阶段用 EventSetTimer(1) 把定时器拉起来,意味着 OnTimer 每秒被调用一次,用来统计图表上按钮的点击次数;OnDeinit 里调 EventKillTimer() 撤掉定时器并清掉 EA 建的图形对象。 品种清单塞进 CArrayString 对象,图表操作靠 Chart 对象,两者对应的 MQH 在 #property 块之后、函数之外用 #include 引入。按钮宽高在 include 之后设成常量,开 MT5 照抄下面代码就能跑通空壳。 外汇和贵金属杠杆高、滑点狠,拿这模板做实盘前先在策略测试器里验证定时器与对象响应。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 _finder.mq5  |
class=class="str">"cmt">//|                                                                 版权所有 class="num">2018, MetaQuotes 软件公司 |
class=class="str">"cmt">//|                                                                 [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Klymenko Roman(needtome@icloud.com)"
class="macro">#class="kw">property link      "https:class=class="str">"cmt">//logmy.net"
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property strict
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 智能系统初始化函数                                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- 创建定时器
   EventSetTimer(class="num">1);
   
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 智能系统逆初始化函数                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
  {
   EventKillTimer();
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 定时器函数                                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTimer()
  {
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnChartEvent(class="kw">const class="type">int id,        class=class="str">"cmt">// 事件 ID
                  class="kw">const class="type">long& lparam,  class=class="str">"cmt">// 长整数型事件参数
                  class="kw">const class="type">class="kw">double& dparam, class=class="str">"cmt">// 双精度型事件参数
                  class="kw">const class="type">class="kw">string& sparam) class=class="str">"cmt">// 字符串型事件参数
  {
}
class="macro">#include <Arrays\ArrayString.mqh>
class="macro">#include <Charts\Chart.mqh>

◍ 按钮尺寸的两个编译期常量

在 MT5 面板开发里,先把按钮的几何尺寸用宏固定下来,比在运行时反复传参更省事。下面两行把高度锁死在 20 像素、宽度锁死在 100 像素,编译后就直接替换进代码,没有函数调用开销。 如果你要在 4K 屏或高 DPI 账户终端上跑 EA,这两个值可能偏小,建议先在策略测试器的可视化模式里拖一下看实际占比,再回头改宏。外汇与贵金属品种波动快,面板反应区域太小容易点错下单,属于实操里的高频坑。

MQL5 / C++
class="macro">#define BTN_HEIGHT(class="num">20)
class="macro">#define BTN_WIDTH(class="num">100)

EA 输入参数与两个枚举类型的门道

EA 的管控逻辑完全由输入参数驱动。在 MT5 里直接看这组输入,能立刻反推出后面要写哪几个过滤函数——比如点差过滤、价格区间过滤、交易时段过滤。 代码里有两个自定义类型是枚举,必须定义在 input 之前。ValueOfSpread 决定品种点差展示条件:点差超过价格 0.1% 算「增加」,默认 hide_SPREAD=spread_b1 即只显示点差小于 0.1% 的品种;若经纪商符合条件的品种太少,可改选 spread_b15 或 spread_l1 等其它档位。 StartHour 枚举列的是主要市场开放周期,注意 9 am 不代表「只在 9 点开单的品种才显示」,而是显示 9:05 这类临近时刻准备开单的品种;hour_4pm 对应 16:30 开市的美股,hour_10am 偏俄欧股,hour_9am 是部分指数,hour_0am 则因外汇全天候运行而恒匹配。 逐行拆一下关键输入:noSYMBmarketWath=true 表示不在市场观察中隐藏品种;noSYMBwithPOS=true 表示有持仓时隐藏;hide_PRICE_HIGH 和 hide_PRICE_LOW 设为 0 代表不按价格高低过滤;hideProhibites 与 hideClosed 默认 true 会剔掉禁交易和已收市的标的;hideATRcents=0.00 表示不按 ATR 美元值屏蔽。图表类输入里 addInfoWatch=false 不会把图挂进市场观察,viewCandle、viewVolumes、showInfoSymbol、showNameSymbol 默认开,负责烛条、成交量、方向箭头和品名的显示。 开 MT5 把这段枚举和 input 贴进 EA 头文件,改 hide_SPREAD 档位跑一遍,就能验证不同点差阈值下市场观察窗口里剩下的品种数量差异。

MQL5 / C++
<span class="keyword">sinput</span> <span class="keyword">class="type">class="kw">string</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delimeter_01=<span class="class="type">class="kw">string">""</span>; <span class="comment">       class=class="str">"cmt">// --- 过滤设置 ---</span>
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; noSYMBmarketWath=<span class="macro">true</span>; <span class="comment"> class=class="str">"cmt">// 是否不在市场观察中隐藏</span>
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; noSYMBwithPOS=<span class="macro">true</span>; <span class="comment">    class=class="str">"cmt">// 若有持仓则隐藏</span>
<span class="keyword">class="kw">input</span> ValueOfSpread&nbsp;&nbsp;hide_SPREAD=spread_b1; <span class="comment"> class=class="str">"cmt">// 在点差情况下隐藏</span>
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">uint</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hide_PRICE_HIGH=<span class="number">class="num">0</span>; <span class="comment">     class=class="str">"cmt">// 若价格太高则隐藏</span>
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">uint</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hide_PRICE_LOW=<span class="number">class="num">0</span>; <span class="comment">      class=class="str">"cmt">// 若价格太低则隐藏</span>
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hideProhibites=<span class="macro">true</span>; <span class="comment">   class=class="str">"cmt">// 若禁止交易则隐藏</span>
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hideClosed=<span class="macro">true</span>; <span class="comment">       class=class="str">"cmt">// 若市场收市则隐藏</span>
<span class="keyword">class="kw">input</span> StartHour&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hide_HOURS=hour_any; <span class="comment">   class=class="str">"cmt">// 若有开放时间则显示</span>
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">class="kw">double</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hideATRcents=<span class="number">class="num">0.00</span>; <span class="comment">     class=class="str">"cmt">// 如果 ATR 小于设定的美元值则隐藏</span>
<span class="keyword">sinput</span> <span class="keyword">class="type">class="kw">string</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delimeter_02=<span class="class="type">class="kw">string">""</span>; <span class="comment">       class=class="str">"cmt">// --- 图表设置 ---</span>
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; addInfoWatch=<span class="macro">false</span>; <span class="comment">    class=class="str">"cmt">// 将图表添加到市场观察中</span>
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; viewCandle=<span class="macro">true</span>; <span class="comment">       class=class="str">"cmt">// 打开烛条图表</span>
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; viewVolumes=<span class="macro">true</span>; <span class="comment">      class=class="str">"cmt">// 显示逐笔报价交易量</span>
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; showInfoSymbol=<span class="macro">true</span>; <span class="comment">   class=class="str">"cmt">// 显示走势方向</span>
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">bool</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; showNameSymbol=<span class="macro">true</span>; <span class="comment">   class=class="str">"cmt">// 显示品名</span><span class="comment"></span>
<span class="keyword">enum</span> ValueOfSpread
&nbsp;&nbsp;{
&nbsp;&nbsp; spread_no,<span class="comment">class=class="str">"cmt">//无</span>
&nbsp;&nbsp; spread_b05,<span class="comment">class=class="str">"cmt">// &gt; class="num">0.05%</span>
&nbsp;&nbsp; spread_b1,<span class="comment">class=class="str">"cmt">// &gt; class="num">0.1</span><span class="comment"><span class="comment">%</span> </span>
&nbsp;&nbsp; spread_b15,<span class="comment">class=class="str">"cmt">// &gt; class="num">0.15</span><span class="comment"><span class="comment">%</span> </span>
&nbsp;&nbsp; spread_l15,<span class="comment">class=class="str">"cmt">// &lt; class="num">0.15</span><span class="comment"><span class="comment">%</span> </span>
&nbsp;&nbsp; spread_l1,<span class="comment">class=class="str">"cmt">// &lt; class="num">0.1</span><span class="comment"><span class="comment">%</span> </span>
&nbsp;&nbsp;}; 
<span class="keyword">enum</span> StartHour
&nbsp;&nbsp;{
&nbsp;&nbsp; hour_any, <span class="comment">class=class="str">"cmt">//任何时间</span>
&nbsp;&nbsp; hour_9am,<span class="comment"> class=class="str">"cmt">// class="num">9 am</span>
&nbsp;&nbsp; hour_10am,<span class="comment">class=class="str">"cmt">// class="num">10 am </span>
&nbsp;&nbsp; hour_4pm,<span class="comment"> class=class="str">"cmt">// class="num">4 pm </span>
&nbsp;&nbsp; hour_0am, <span class="comment">class=class="str">"cmt">// 午夜</span>
&nbsp;&nbsp;};

「EA 全局可见的变量先铺好」

在写标准函数逻辑前,先把整支 EA 都要用的变量在 input 段之后声明,作用域覆盖全部函数,避免后面反复传参。 这些变量各有明确分工:exprefix 给 EA 画的所有图形对象统一加 "finder" 前缀,防止和手动画的线、别的 EA 对象重名;arrPanel1 是 CArrayString 类型,装的是满足筛选条件的品种名;panel1val 记录当前在面板里指向 arrPanel1 的哪一行。 图表侧用两个数组接住:charts[] 存 CChart 对象本身,curChartID[] 存对应的图表 ID(long 型),初期都只会有 1 个元素,但按数组写方便以后扩展多图表。 input 参数已经把筛选条件定死了几个默认值——hide_SPREAD 默认 spread_b1(即隐藏点差 >0.1% 的品种),noSYMBmarketWath 和 noSYMBwithPOS 默认 true(隐藏不在市场观察里、或有持仓的品种),BTN_HEIGHT 与 BTN_WIDTH 分别用宏定成 20 和 100,是后面按钮控件的像素尺寸。外汇和贵金属波动大、点差跳变频繁,这类隐藏规则仅降低干扰,不预示任何方向。 把下面代码直接贴进 MT5 的 MQ5 文件,编译后能在导航树里看到这些全局符号,方便下一步接筛选函数时直接引用。

MQL5 / C++
class=class="str">"cmt">// 在 EA 创建的所有图形对象名称中添加的前缀:
class="type">class="kw">string exprefix="finder";
class=class="str">"cmt">// 符合我们条件的品种数组:
CArrayString arrPanel1;
class=class="str">"cmt">// arrPanel1 数组中当前品种的索引:
class="type">int panel1val;
class=class="str">"cmt">// 用于存储 EA 创建的图表的数组(此刻此处只有一个图表):
CChart charts[];
class=class="str">"cmt">// 用于存储指向 EA 创建的图表的指针的数组(此刻此处只有一个指针):
class="type">long curChartID[];
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                    _finder.mq5  |
class=class="str">"cmt">//|                    版权所有 class="num">2018, MetaQuotes 软件公司 |
class=class="str">"cmt">//|                                         [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Klymenko Roman(needtome@icloud.com)"
class="macro">#class="kw">property link      "https:class=class="str">"cmt">//logmy.net"
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property strict
class="macro">#include <Arrays\ArrayString.mqh>
class="macro">#include <Charts\Chart.mqh>
class="macro">#define BTN_HEIGHT(class="num">20)
class="macro">#define BTN_WIDTH(class="num">100)
enum ValueOfSpread
  {
   spread_no,class=class="str">"cmt">//无
   spread_b05,class=class="str">"cmt">// > class="num">0.05 %
   spread_b1,class=class="str">"cmt">// > class="num">0.1 %
   spread_b15,class=class="str">"cmt">// > class="num">0.15 %
   spread_l15,class=class="str">"cmt">// < class="num">0.15 %
   spread_l1,class=class="str">"cmt">// < class="num">0.1 %
   };
enum StartHour
  {
   hour_any,class=class="str">"cmt">//任何时间
   hour_9am,class=class="str">"cmt">// class="num">9 am
   hour_10am,class=class="str">"cmt">// class="num">10 am 
   hour_4pm,class=class="str">"cmt">// class="num">4 pm 
   hour_0am,class=class="str">"cmt">// 午夜
   };
class="kw">input class="type">bool         noSYMBmarketWath=true; class=class="str">"cmt">// 在市场观察中隐藏品种<
class="kw">input class="type">bool         noSYMBwithPOS=true;    class=class="str">"cmt">// 若有持仓则隐藏
class="kw">input ValueOfSpread  hide_SPREAD=spread_b1; class=class="str">"cmt">// 隐藏有点差品种
class="kw">input class="type">uint          hide_PRICE_HIGH=class="num">0;     class=class="str">"cmt">// 隐藏价格太高品种 (class="num">0 - 不隐藏)
class="kw">input class="type">uint          hide_PRICE_LOW=class="num">0;      class=class="str">"cmt">// 隐藏价格太低品种 (class="num">0 - 不隐藏)
class="kw">input class="type">bool          hideProhibites=true;   class=class="str">"cmt">// 隐藏不可交易品种
class="kw">input StartHour     hide_HOURS=hour_any;   class=class="str">"cmt">// 显示仅在某时刻开放的品种
class="kw">input class="type">bool          viewCandle=true;       class=class="str">"cmt">// 打开烛条图表
class=class="str">"cmt">// 在 EA 创建的所有图形对象名称中添加的前缀:
class="type">class="kw">string exprefix="finder";
class=class="str">"cmt">// 符合我们条件的品种数组:
CArrayString arrPanel1;
class=class="str">"cmt">// arrPanel1 数组中当前品种的索引:
class="type">int panel1val;
class=class="str">"cmt">// 用于存储 EA 创建的图表的数组(此刻此处只有一个图表):
CChart charts[];
class=class="str">"cmt">// 用于存储指向 EA 创建的图表的指针的数组(此刻此处只有一个指针):
class="type">long curChartID[];
class=class="str">"cmt">//+------------------------------------------------------------------+

常见问题

用一段脚本按条件(如点差、波动率)自动列出符合的品种,省去手动逐个点击,开盘前跑一遍即可。
先定义好输入参数和两个枚举类型控制筛选逻辑,再声明全局变量存结果,之后填具体过滤规则就行。
可以,小布盯盘的 AIGC 已内置品种筛选与诊断,打开对应品种页就能直接看到符合条件的机会提示。
把尺寸提为编译期常量或输入参数,调的时候只改参数或常量值,避免动主体逻辑重新编译。
在初始化阶段就把跨函数要用的变量铺好,否则函数间传值混乱,筛选结果可能丢失或报错。