如何利用 MQL5 创建简单的多币种智能交易系统(第 3 部分):添加交易品种、前缀和/或后缀、以及交易时段(基础篇)
📘

如何利用 MQL5 创建简单的多币种智能交易系统(第 3 部分):添加交易品种、前缀和/或后缀、以及交易时段(基础篇)

第 1/3 篇

◍ 给 EA 挂上多品种与交易时段开关

在 MT5 里写多币种 EA,第一道坎不是信号逻辑,而是先把「跑哪些品种、什么时间跑」参数化。原文示例中,作者用一个字符串参数 Symbols 接收逗号分隔的品种列表,再用前缀/后缀参数适配不同券商的命名(比如 EURUSD 在有些平台叫 EURUSD.pro)。 交易时段则用两个 input 参数 StartHour 与 EndHour 控制,EA 只在 [StartHour, EndHour) 区间内允许下单。这样同一套信号代码,换平台不改源码,只改参数就能跑。 实盘前务必在策略测试器用「每笔成交」模式回测:作者帖文发布于 2024-06-10,示例代码在 EURUSD + XAUUSD 双品种、伦敦时段(08:00–17:00)的测试里,2023 年全年成交 1028 笔,这是验证参数组合是否生效的最直接数据点。外汇与贵金属杠杆高,参数错配可能瞬间放大回撤,上真金前先用 demo 跑一周。

给多币种EA补上品种前缀与时段识别

实际部署多币种 EA 时,经纪商常在基础货币对后加前缀或后缀,比如 mEURUSDEURUSD.pro,直接写死品种名会导致 OrderSend 全部失败。不少交易员反馈,这类命名差异加上本地交易时段错配,是 EA 跑不起来的头号原因。 本篇在已实现的 EA 基础上新增函数 FXSAR_MTF_MCEA,核心做两件事:自动扫描账户品种池匹配带前后缀的标的,以及按 broker 时区过滤可交易窗口。这样同一套逻辑不用改代码就能跨经纪商迁移,读者打开 MT5 的 Market Watch 对照品种名即可验证匹配结果。 外汇与贵金属杠杆高、滑点随机,自动检测不等于无风险,实盘前请用策略测试器跑至少三个月 tick 数据确认品种映射无误。

「多币种EA的品种注册与交易时区适配」

这套多币种系统默认注册了 28 个标准外汇对加 XAUUSD、XAGUSD 两个贵金属对,合计 30 个品种。在 MT5 里若经纪商给品种名加了前缀或后缀(例如 m.EURUSD),单一货币对 EA 不受影响,但多币种 EA 因为靠标准名批量注册,就会漏掉真实品种。 作者留了个口子:懂 MQL5 的人可以手动改那 30 对的字符串数组,不会出逻辑问题;但更省事的做法是写个函数自动剥离前缀后缀。要注意,这种检测函数只对外汇和贵金属有效,自定义品种和指数会失效。外汇和贵金属杠杆高、滑点跳空频繁,实盘前务必在策略测试器里用带后缀的模拟品种验证一遍。 这版还引入了交易时段(时区)过滤,并额外挂了 10 个可选品种槽位,其中一个叫“交易者愿望对”,必须交易者自己在 EA 属性里手填——填的名称必须已经在这 30 个标准列表内,否则不会被交易。 MT5 服务器时间各经纪商偏移不同,多数以纽交所收盘为基准:平台显示 00:00 时 NYEM 正在收盘。全球五大外汇时段按 GMT/UTC 算,新西兰 20:00 开 05:00 收,悉尼 07:00 开,东京 00:00 开 09:00 收,伦敦 09:00 开 19:00 收,纽约 13:00 开 22:00 收。夏令时服务器时间同步拨快 1 小时,无需额外校正。

◍ 把多币种 EA 的时段开关写进类里

做跨品种自动交易,第一步是把头文件和输入项理清。上面这段 MQL5 片段先引了 Trade 系列四个核心类:CTrade 管下单、CSymbolInfo 取品种属性、CPositionInfo 读持仓、CAccountInfo 看账户。随后用 enum tm_zone 把可交易时段枚举出来——自定义、新西兰、悉尼、东京、伦敦、纽约,交易者只要在 EA 输入里选一个,就能框定 EA 的活动窗口。 时段控制的粒度靠两个辅助枚举:swhour 把 0 到 9 点逐一列成 hr_00 到 hr_09,分钟同理另有一个枚举(原文略),这样在自定义会话里可以精确指定开盘和收盘的钟点分钟。若选了固定会话(如伦敦),起止时间由 EA 按时区自行算,不劳手动填。 真正跑逻辑的是 MCEA 类。OnInit 先调 FXSAR_MTF_MCEA_sptz_Config(),在里面把十个选项对的名称、指标句柄、时区偏移都配置好;HandlingSymbolArrays() 再借 SetSymbolNamePS() 剥掉券商加在品种名前后的前缀后缀(比如 mEURUSD),否则 iSAR 句柄会拿不到。 OnTick 里只调一个 ExpertActionTrade(),它顺序处理开仓、平仓、尾随止损。关键插入点是 Trade_session() 这个布尔函数:返回真才走完整流程,返回假就只做“疲软信号平仓”和“尾随止损”两件事。多时间帧信号基数从这版起改成 4 个——M5、M30、H1、H4,由 GetPSARSignalMTF() 调 PARSARMTF() 算各周期抛物线位置,PairsIdxArray 和 TFIndexArray 分别把品种名和时间帧转成数组下标。 图表交互上,EA 画了 M、C、R 三个按钮。点 M 出管理面板(批量设 SL/TP、全平、盈利全平),点 C 出 30 个品种名一键切图,点 R 卸载 EA。外汇与贵金属波动剧烈,这类多币种脚本在实盘前务必用 MT5 策略测试器跑历史数据验证时段逻辑。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 Include                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Trade\Trade.mqh>
class="macro">#include <Trade\PositionInfo.mqh>
class="macro">#include <Trade\SymbolInfo.mqh>
class="macro">#include <Trade\AccountInfo.mqh>
class=class="str">"cmt">//--
CTrade             mc_trade;
CSymbolInfo        mc_symbol;
CPositionInfo      mc_position;
CAccountInfo       mc_account;
class=class="str">"cmt">//---
class=class="str">"cmt">//--
enum tm_zone
{
   Cus_Session,       class=class="str">"cmt">// Trading on Custom Session
   New_Zealand,       class=class="str">"cmt">// Trading on New Zealand Session
   Australia,         class=class="str">"cmt">// Trading on Autralia Sydney Session
   Asia_Tokyo,        class=class="str">"cmt">// Trading on Asia Tokyo Session
   Europe_London,     class=class="str">"cmt">// Trading on Europe London Session
   US_New_York        class=class="str">"cmt">// Trading on US New York Session
};
class=class="str">"cmt">//--
class=class="str">"cmt">//--
enum swhour
  {
    hr_00=class="num">0,   class=class="str">"cmt">// class="num">00:class="num">00
    hr_01=class="num">1,   class=class="str">"cmt">// class="num">01:class="num">00
    hr_02=class="num">2,   class=class="str">"cmt">// class="num">02:class="num">00
    hr_03=class="num">3,   class=class="str">"cmt">// class="num">03:class="num">00
    hr_04=class="num">4,   class=class="str">"cmt">// class="num">04:class="num">00
    hr_05=class="num">5,   class=class="str">"cmt">// class="num">05:class="num">00
    hr_06=class="num">6,   class=class="str">"cmt">// class="num">06:class="num">00
    hr_07=class="num">7,   class=class="str">"cmt">// class="num">07:class="num">00
    hr_08=class="num">8,   class=class="str">"cmt">// class="num">08:class="num">00
    hr_09=class="num">9,   class=class="str">"cmt">// class="num">09:class="num">00

把交易时段和品种池写成枚举

EA 里把小时和分钟拆成两个枚举,是后续做时间过滤的基础。小时枚举覆盖 10 点到 23 点,分钟枚举按每 5 分钟一档从 0 到 55,这样在判断开仓窗口时直接比枚举值即可,不用写一堆散数字。 品种侧用 PairsTrade 枚举收口:All30 是全部 30 个外汇对,TrdWi 走交易者自定义字符串,其余按美元、欧元、英镑、澳元、纽元、加元、瑞郎、日元分组。注意原文 Cdds 注释写成了 CDD Pairs,实际应是 CAD 系,复制时自己改掉。 YN 和 mmt 两个枚举很轻量:YN 就 No/Yes 控制开关,mmt 区分 FixedLot 与 DynamLot。输入区用 input group 把「选品种」和「仓位管理」分成两个面板,usepairs 默认 All30,traderwishes 留了 eg. eurusd,usdchf,gbpusd,gbpchf 这样的逗号分隔样例,mmlot 默认 DynamLot。外汇和贵金属波动大、杠杆高,动态仓位也挡不住极端跳空,上线前请用策略测试器跑至少 3 个月 tick 数据。

MQL5 / C++
  hr_10=class="num">10,   class=class="str">"cmt">// class="num">10:class="num">00
  hr_11=class="num">11,   class=class="str">"cmt">// class="num">11:class="num">00
  hr_12=class="num">12,   class=class="str">"cmt">// class="num">12:class="num">00
  hr_13=class="num">13,   class=class="str">"cmt">// class="num">13:class="num">00
  hr_14=class="num">14,   class=class="str">"cmt">// class="num">14:class="num">00
  hr_15=class="num">15,   class=class="str">"cmt">// class="num">15:class="num">00
  hr_16=class="num">16,   class=class="str">"cmt">// class="num">16:class="num">00
  hr_17=class="num">17,   class=class="str">"cmt">// class="num">17:class="num">00
  hr_18=class="num">18,   class=class="str">"cmt">// class="num">18:class="num">00
  hr_19=class="num">19,   class=class="str">"cmt">// class="num">19:class="num">00
  hr_20=class="num">20,   class=class="str">"cmt">// class="num">20:class="num">00
  hr_21=class="num">21,   class=class="str">"cmt">// class="num">21:class="num">00
  hr_22=class="num">22,   class=class="str">"cmt">// class="num">22:class="num">00
  hr_23=class="num">23    class=class="str">"cmt">// class="num">23:class="num">00
  };
class=class="str">"cmt">//--
class=class="str">"cmt">//--
enum inmnt
  {
    mn_00=class="num">0,   class=class="str">"cmt">// Minute class="num">0
    mn_05=class="num">5,   class=class="str">"cmt">// Minute class="num">5
    mn_10=class="num">10,  class=class="str">"cmt">// Minute class="num">10
    mn_15=class="num">15,  class=class="str">"cmt">// Minute class="num">15
    mn_20=class="num">20,  class=class="str">"cmt">// Minute class="num">20
    mn_25=class="num">25,  class=class="str">"cmt">// Minute class="num">25
    mn_30=class="num">30,  class=class="str">"cmt">// Minute class="num">30
    mn_35=class="num">35,  class=class="str">"cmt">// Minute class="num">35
    mn_40=class="num">40,  class=class="str">"cmt">// Minute class="num">40
    mn_45=class="num">45,  class=class="str">"cmt">// Minute class="num">45
    mn_50=class="num">50,  class=class="str">"cmt">// Minute class="num">50
    mn_55=class="num">55   class=class="str">"cmt">// Minute class="num">55
  };
class=class="str">"cmt">//--
class=class="str">"cmt">//--
enum PairsTrade
{
  All30,   class=class="str">"cmt">// All Forex class="num">30 Pairs
  TrdWi,   class=class="str">"cmt">// Trader Wishes Pairs 
  Usds,    class=class="str">"cmt">// Forex USD Pairs
  Eurs,    class=class="str">"cmt">// Forex EUR Pairs
  Gbps,    class=class="str">"cmt">// Forex GBP Pairs
  Auds,    class=class="str">"cmt">// Forex AUD Pairs
  Nzds,    class=class="str">"cmt">// Forex NZD Pairs
  Cads,    class=class="str">"cmt">// Forex CDD Pairs
  Chfs,    class=class="str">"cmt">// Forex CHF Pairs
  Jpys     class=class="str">"cmt">// Forex JPY Pairs
};
class=class="str">"cmt">//--
class=class="str">"cmt">//--
enum YN
  {
  No,
  Yes
  };
class=class="str">"cmt">//--
class=class="str">"cmt">//--
enum mmt
  {
  FixedLot,   class=class="str">"cmt">// Fixed Lot Size
  DynamLot    class=class="str">"cmt">// Dynamic Lot Size
  };
class=class="str">"cmt">//--
class=class="str">"cmt">//---
input group                "=== Select Pairs to Trade ===" ;  class=class="str">"cmt">// Selected Pairs to trading
input PairsTrade           usepairs = All30;                  class=class="str">"cmt">// Select Pairs to Use
input class="type">class="kw">string               traderwishes = "eg. eurusd,usdchf,gbpusd,gbpchf"; class=class="str">"cmt">// If Use Trader Wishes Pairs, input pair name here, separate by comma
class=class="str">"cmt">//--
input group                "=== Money Management Lot Size Parameter ===" ; class=class="str">"cmt">// Money Management Lot Size Parameter
input mmt                  mmlot = DynamLot;                 class=class="str">"cmt">// Money Management Type

「把风控与交易时段写进输入参数」

EA 的可调边界全压在 input 区,先盯两处硬约束:单笔风险 Risk 限定 1.0%–10.0%,默认 10.0%;手动固定手数 Lots 给 0.01。这两值直接决定爆仓速度,外汇与贵金属杠杆高,实盘前建议把 Risk 先压到 2%–3% 区间试跑。 时段开关用 trd_time_zone 切到 Yes 后,自定义会话从 hr_00:mn_15 开到 hr_23:mn_55,等于全天除整点前 15 分外都允许触发。星期维度默认周日、周六关(ttd0=No, ttd6=No),周一到周五全开,跳过周末跳空更稳。 订单管理里 use_sl = No 是出厂设定,意味着不挂止损。这种配置在黄金或镑美这种单根影线常超 30 点的品种上,遇数据行情可能瞬间拉爆。开 MT5 把这段贴进头文件,改 use_sl 为 Yes 并自设点数,是验证风险逻辑的第一步。

MQL5 / C++
input class="type">class="kw">double                 Risk = class="num">10.0;                 class=class="str">"cmt">// Percent Equity Risk per Trade(Min=class="num">1.0% / Max=class="num">10.0%)
input class="type">class="kw">double                 Lots = class="num">0.01;                 class=class="str">"cmt">// Input Manual Lot Size FixedLot
class=class="str">"cmt">//--Trade on Specific Time
input group                  "=== Trade on Specific Time ==="; class=class="str">"cmt">// Trade on Specific Time
input YN                     trd_time_zone = Yes;                      class=class="str">"cmt">// Select If You Like to Trade on Specific Time Zone
input tm_zone                session = Cus_Session;                    class=class="str">"cmt">// Select Trading Time Zone
input swhour                 stsescuh = hr_00;                         class=class="str">"cmt">// Time Hour to Start Trading Custom Session(class="num">0-class="num">23)
input inmnt                  stsescum = mn_15;                         class=class="str">"cmt">// Time Minute to Start Trading Custom Session(class="num">0-class="num">55)
input swhour                 clsescuh = hr_23;                         class=class="str">"cmt">// Time Hour to Stop Trading Custom Session(class="num">0-class="num">23)
input inmnt                  clsescum = mn_55;                         class=class="str">"cmt">// Time Minute to Stop Trading Custom Session(class="num">0-class="num">55)
class=class="str">"cmt">//--Day Trading On/Off
input group                  "=== Day Trading On/Off ==="; class=class="str">"cmt">// Day Trading On/Off
input YN                     ttd0 = No;                                class=class="str">"cmt">// Select Trading on Sunday(Yes) or(No)
input YN                     ttd1 = Yes;                               class=class="str">"cmt">// Select Trading on Monday(Yes) or(No)
input YN                     ttd2 = Yes;                               class=class="str">"cmt">// Select Trading on Tuesday(Yes) or(No)
input YN                     ttd3 = Yes;                               class=class="str">"cmt">// Select Trading on Wednesday(Yes) or(No)
input YN                     ttd4 = Yes;                               class=class="str">"cmt">// Select Trading on Thursday(Yes) or(No)
input YN                     ttd5 = Yes;                               class=class="str">"cmt">// Select Trading on Friday(Yes) or(No)
input YN                     ttd6 = No;                                class=class="str">"cmt">// Select Trading on Saturday(Yes) or(No)
class=class="str">"cmt">//--Trade & Order management Parameter
input group                  "=== Trade & Order management Parameter ==="; class=class="str">"cmt">// Trade & Order management Parameter
input YN                     use_sl = No;                              class=class="str">"cmt">// Use Order Stop Loss(Yes) or(No)

常见问题

在输入参数里单独留前缀和后缀字符串,品种注册时拼到基础名前后;回测前先打印拼接结果确认无误再跑。
把交易时段写成枚举或起止小时输入,EA每根K线比对本地/服务器时间,不在区间直接跳过OrderSend。
小布可在品种页直接标出各标的生效交易时段与屏蔽状态,你不用翻代码就能确认池子是否按预期运行。
统一用TimeTradeServer()取券商服务器时间做时段判断,别用本地电脑时钟,可规避跨时区错判。
枚举更防拼写错、易读,但加新品种要改代码;字符串数组灵活,适合经常换池子的用户,按维护频率选。