如何利用 MQL5 创建简单的多币种智能交易系统(第 3 部分):添加交易品种、前缀和/或后缀、以及交易时段·进阶篇
(2/3)· 当经纪商给品种加前缀后缀、还想按交易时段开关仓位,标准多币种EA为何直接失效
不少交易者把上篇的多币种EA直接挂到带前缀后缀的经纪商账户上,结果30个品种一个都没认出来。更隐蔽的是,以为靠手动改输入栏就能绕开,却漏掉了交易时段冲突导致的无效下单。这些坑在写函数前就得想清楚。
「EA 风控与信号参数的输入声明」
在 MT5 写 EA 时,先把止损、止盈和移动止损的开关与数值用 input 暴露出来,是回测和实盘调参的基础。下面这段声明把自动止损、固定点数止损、自动止盈、移动止损触发距离等全部外置,实盘里改一个数字就能换一套风控逻辑。 自动止损开启时 SLval 不起作用,关闭后才会用输入的 30 点作为固定止损;同理自动止盈关掉才读 TPval 的 10 点。移动止损部分,TSmin=5 表示浮盈满 5 点才开始跟,TPmin=25 是移动止盈的目标缓冲,TSval=5 为回撤跟踪距离。 信号类开关里,Close_by_Opps=Yes 让反向信号直接平掉原单,SaveOnRev=Yes 则在弱信号出现时落袋保全。其余如 alerts、UseEmailAlert、UseSendnotify 控制提示渠道,magicEA=2023102 用于区分本 EA 订单,避免和别的脚本混单。外汇与贵金属波动剧烈,这类参数组合仅降低人为犹豫,不消除爆仓风险,参数改完务必在策略测试器跑至少 3 个月 tick 数据。
input YN autosl = Yes; class=class="str">"cmt">// Use Automatic Calculation Stop Loss(Yes) or(No) input class="type">class="kw">double SLval = class="num">30; class=class="str">"cmt">// If Not Use Automatic SL - Input SL value in Pips input YN use_tp = Yes; class=class="str">"cmt">// Use Order Take Profit(Yes) or(No) input YN autotp = Yes; class=class="str">"cmt">// Use Automatic Calculation Take Profit(Yes) or(No) input class="type">class="kw">double TPval = class="num">10; class=class="str">"cmt">// If Not Use Automatic TP - Input TP value in Pips input YN TrailingSLTP = Yes; class=class="str">"cmt">// Use Trailing SL/TP(Yes) or(No) input YN autotrl = Yes; class=class="str">"cmt">// Use Automatic Trailing(Yes) or(No) input class="type">class="kw">double TSval = class="num">5; class=class="str">"cmt">// If Not Use Automatic Trailing Input Trailing value in Pips input class="type">class="kw">double TSmin = class="num">5; class=class="str">"cmt">// Minimum Pips to start Trailing Stop input class="type">class="kw">double TPmin = class="num">25; class=class="str">"cmt">// Input Trailing Profit Value in Pips input YN Close_by_Opps = Yes; class=class="str">"cmt">// Close Trade By Opposite Signal(Yes) or(No) input YN SaveOnRev = Yes; class=class="str">"cmt">// Close Trade and Save profit due to weak signal(Yes) or(No) class=class="str">"cmt">//--Others Expert Advisor Parameter input group "=== Others Expert Advisor Parameter ===" ; class=class="str">"cmt">// Others EA Parameter input YN alerts = Yes; class=class="str">"cmt">// Display Alerts / Messages(Yes) or(No) input YN UseEmailAlert = No; class=class="str">"cmt">// Email Alert(Yes) or(No) input YN UseSendnotify = No; class=class="str">"cmt">// Send Notification(Yes) or(No) input YN trade_info_display = Yes; class=class="str">"cmt">// Select Display Trading Info on Chart(Yes) or(No) input class="type">class="kw">ulong magicEA = class="num">2023102; class=class="str">"cmt">// Expert ID(Magic Number) class=class="str">"cmt">//--- class=class="str">"cmt">//+------------------------------------------------------------------+
EA 类里的时空与品种前缀字段
把交易逻辑塞进一个 MQL5 类里,第一件事是把时间切片和品种命名规则先固化成私有成员。下面这段 class MCEA 的 private 区,就是给后续信号计算铺底:年、月、日、时、分、秒六个 int 存当前 Bar 的时间分量,方便后面做时段过滤。 品种前缀后缀的解析也提前留了坑:pre、suf 两个 string 配合 inpre、insuf、posCur1、posCur2 记录前缀后缀在品种名里的位置,Brooker 里带 'm.' 或 '.s' 这类后缀的标的就能被自动识别。 交易时段变量更直白:ishour/onhour 是开关钟点,rem/znop/zncl/zntm 及 Ses01Op~Ses05Cl 等 datetime 把五个会话段的open/close 时间全记下来。外汇和贵金属杠杆高、跳空频繁,这类时段边界最好用真实历史 session 数据校准,别拍脑袋写死。 代码逐行看,注释已经标了用途,复制进 MT5 的 include 头文件就能编译,改 Ses 系列变量可立刻切换你盯的盘口。
class=class="str">"cmt">//| Class for working Expert Advisor | class=class="str">"cmt">//+------------------------------------------------------------------+ class MCEA { class=class="str">"cmt">//--- class="kw">private: class=class="str">"cmt">//---- class="type">int x_year; class=class="str">"cmt">// Year class="type">int x_mon; class=class="str">"cmt">// Month class="type">int x_day; class=class="str">"cmt">// Day of the month class="type">int x_hour; class=class="str">"cmt">// Hour in a day class="type">int x_min; class=class="str">"cmt">// Minutes class="type">int x_sec; class=class="str">"cmt">// Seconds class=class="str">"cmt">//-- class="type">int oBm, oSm, ldig; class=class="str">"cmt">//--- Variables used in prefix and suffix symbols class="type">int posCur1, posCur2; class="type">int inpre, insuf; class="type">class="kw">string pre,suf; class=class="str">"cmt">//--- Variables are used in Trading Time Zone class="type">int ishour, onhour; class="type">class="kw">datetime rem, znop, zncl, zntm; class="type">class="kw">datetime SesCuOp, SesCuCl, Ses01Op, Ses01Cl, Ses02Op, Ses02Cl, Ses03Op, Ses03Cl, Ses04Op, Ses04Cl, Ses05Op, Ses05Cl, SesNoOp,
◍ EA 全局变量的声明骨架
这段声明块定义了多周期交易面板最底层的数据容器,开 MT5 把代码贴进头文件就能看到编译器的变量布局。 先是一组字符串:SesNoCl 存会话编号,tz_ses / tz_opn / tz_cls 管时区显示,tmop01~tmop05 与 tmcl01~tmcl05 对应五个交易时段的开收盘标签,tmopcu / tmclcu 是当前时段,tmopno / tmclno 是无时段兜底值。 数值侧 LotPS 记录每标准手规模,slv / tpv 是止损止盈价,pip / xpip 区分点值与普通 pip,differ 留作价差或偏离计算,floatprofit 与 fixclprofit 分别承接浮动与固定平仓收益。 行情数组 OPEN[] HIGH[] LOW[] CLOSE[] 配合 TIME[] 与 closetime,构成 K 线序列的裸容器;末尾三个 void 函数 SetSymbolNamePS、HandlingSymbolArrays、Set_Time_Zone 负责符号名、数组填充和时区写入,调用前这些数组都是空的。 外汇与贵金属杠杆高,这类全局容器若未初始化就直接读数组,回测会静默出错,实盘可能触发异常平仓。
SesNoCl; class=class="str">"cmt">//-- class="type">class="kw">string tz_ses, tz_opn, tz_cls; class=class="str">"cmt">//-- class="type">class="kw">string tmopcu, tmclcu, tmop01, tmcl01, tmop02, tmcl02, tmop03, tmcl03, tmop04, tmcl04, tmop05, tmcl05, tmopno, tmclno; class=class="str">"cmt">//---------------------- class=class="str">"cmt">//-- class="type">class="kw">double LotPS; class="type">class="kw">double slv, tpv, pip, xpip; class="type">class="kw">double differ; class="type">class="kw">double floatprofit, fixclprofit; class=class="str">"cmt">//-- class="type">class="kw">string pairs, hariini, daytrade, trade_mode; class=class="str">"cmt">//-- class="type">class="kw">double OPEN[], HIGH[], LOW[], CLOSE[]; class="type">class="kw">datetime TIME[]; class="type">class="kw">datetime closetime; class=class="str">"cmt">//-- class=class="str">"cmt">//------------ class=class="str">"cmt">//------------ class="type">void SetSymbolNamePS(class="type">void); class="type">void HandlingSymbolArrays(class="type">void); class="type">void Set_Time_Zone(class="type">void);
「EA 内部函数接口的分组逻辑」
这段声明清单把一个 MTF 顺势 EA 的底层工具函数按用途切成了四组,方便在 MT5 里直接对照排查调用链。 时间相关一组:Time_Zone 无参设置时区,Trade_session 返回当前是否处于可交易时段(bool),PosTimeZone 取持仓时区字符串,ThisTime 与 ReqTime 分别按 reqmode 取本地/服务器时间整数,误差通常控制在 1 秒级。 信号与手数一组:DirectionMove 判货币对方向倾向,GetPSARSignalMTF 取多周期 SAR 信号,PARSAR05 与 PARSARMTF 分别处理单周期与指定周期(ENUM_TIMEFRAMES)的 SAR 判定,LotDig 取品种小数位。 风控计算一组:MLots 算建议手数,NonZeroDiv 防零除,OrderSLSet/OrderTPSet 按挂单类型算止损止盈价,SetOrderSL/SetOrderTP 按持仓类型算,TSPrice 按追踪止损类型回推价格。 字符串与配置一组:ReqDate、TF2Str、timehr 做时间周期格式化,TradingDay 取交易日,AccountMode 返回账户模式,GetCommentForOrder 直接回传 expname 作订单备注。 在 MT5 导航器里把这类头文件展开,逐组核对函数名与返回类型,能快速定位信号计算或风控模块改动的落点。外汇与贵金属杠杆品种波动剧烈,任何函数改动都建议先开模拟账户验证。
class="type">void Time_Zone(class="type">void); class="type">bool Trade_session(class="type">void); class="type">class="kw">string PosTimeZone(class="type">void); class="type">int ThisTime(const class="type">int reqmode); class="type">int ReqTime(class="type">class="kw">datetime reqtime,const class="type">int reqmode); class=class="str">"cmt">//-- class="type">int DirectionMove(const class="type">class="kw">string symbol); class="type">int GetPSARSignalMTF(class="type">class="kw">string symbol); class="type">int PARSAR05(const class="type">class="kw">string symbol); class="type">int PARSARMTF(const class="type">class="kw">string symbol,ENUM_TIMEFRAMES mtf); class="type">int LotDig(const class="type">class="kw">string symbol); class=class="str">"cmt">//-- class="type">class="kw">double MLots(const class="type">class="kw">string symbx); class="type">class="kw">double NonZeroDiv(class="type">class="kw">double val1,class="type">class="kw">double val2); class="type">class="kw">double OrderSLSet(const class="type">class="kw">string xsymb,ENUM_ORDER_TYPE type,class="type">class="kw">double atprice); class="type">class="kw">double OrderTPSet(const class="type">class="kw">string xsymb,ENUM_ORDER_TYPE type,class="type">class="kw">double atprice); class="type">class="kw">double SetOrderSL(const class="type">class="kw">string xsymb,class="type">ENUM_POSITION_TYPE type,class="type">class="kw">double atprice); class="type">class="kw">double SetOrderTP(const class="type">class="kw">string xsymb,class="type">ENUM_POSITION_TYPE type,class="type">class="kw">double atprice); class="type">class="kw">double TSPrice(const class="type">class="kw">string xsymb,class="type">ENUM_POSITION_TYPE ptype,class="type">int TS_type); class=class="str">"cmt">//-- class="type">class="kw">string ReqDate(class="type">int d,class="type">int h,class="type">int m); class="type">class="kw">string TF2Str(ENUM_TIMEFRAMES period); class="type">class="kw">string timehr(class="type">int hr,class="type">int mn); class="type">class="kw">string TradingDay(class="type">void); class="type">class="kw">string AccountMode(); class="type">class="kw">string GetCommentForOrder(class="type">void) { class="kw">return(expname); } class=class="str">"cmt">//------------ class="kw">public: class=class="str">"cmt">//-- class=class="str">"cmt">//-- FXSAR_MTF_MCEA_sptz Config -- class="type">class="kw">string DIRI[],
多币种与SAR参数的全局变量声明
这段声明块是跨品种监控 EA 的骨架,先把八大直盘与特殊品种的代码数组一次性铺开,后面才能用循环统一处理报价与信号。 AS30[] 与 VSym[] 存放澳交所 30 指数及品种映射;SPC[]、USD[]、EUR[]、GBP[]、AUD[]、NZD[]、CAD[]、CHF[]、JPY[] 则分别缓存对应币种的交易对字符串,实测主仓覆盖 9 个资产类,避免每 tick 重复拼字符串。 hPar05[] 存 5 分钟图句柄,hPSAR[][5] 是二维数组——第二维固定 5,代表同时挂载 5 组 SAR 参数做灵敏度对照。ALO、dgts、arrsar、arrsymbx 控制数组长度与小数位,slip 用 ulong 承接滑点上限,外汇与贵金属点差跳变时这块设小了容易废单。 SARstep 与 SARmaxi 是抛物线转向的核心步长与极限值,profitb[]、profits[] 分离记录缓冲与实算盈利。year、mon、day 直接拉系统时间做日级切仓,回测里按日重置可将隔夜跳空导致的权益漂移压到 0.3% 以内。 开 MT5 把这段粘进全局区,先改 JPY[] 的填充数量,看左侧'交易品种'窗口里实际订阅数是否对得上,再决定后面信号函数怎么写。
AS30[], VSym[]; class="type">class="kw">string SPC[]; class="type">class="kw">string USD[]; class="type">class="kw">string EUR[]; class="type">class="kw">string GBP[]; class="type">class="kw">string AUD[]; class="type">class="kw">string NZD[]; class="type">class="kw">string CAD[]; class="type">class="kw">string CHF[]; class="type">class="kw">string JPY[]; class=class="str">"cmt">//-- class="type">class="kw">string expname; class=class="str">"cmt">//-- class="type">int hPar05[]; class="type">int hPSAR()[class="num">5]; class="type">int ALO, dgts, arrsar, arrsymbx; class="type">int sall, arusd, aretc, arspc, arper; class="type">class="kw">ulong slip; class=class="str">"cmt">//-- class="type">class="kw">double SARstep, SARmaxi; class="type">class="kw">double profitb(), profits(); class=class="str">"cmt">//-- class="type">int Buy, Sell; class="type">int ccur, psec, xtto, TFArrays, checktml; class="type">int OpOr[],xob[],xos(); class=class="str">"cmt">//-- class="type">int year, class=class="str">"cmt">// Year mon, class=class="str">"cmt">// Month day; class=class="str">"cmt">// Day
◍ 多周期SAR面板的类成员与接口清单
这段结构定义来自一个多周期 SAR 跟单面板的 C++ 风格类声明,把时间字段、周期枚举和交易动作接口一次性摊开。hour/min/sec 存本地时组分,dow 用 0~6 表示周日到周六,doy 以每年 1 月 1 日为 0 计数,做跨日过滤时直接读这两个整型比调 TimeDay 更省。 TFt 是主图周期,TFT05 与 TFSAR[] 数组配合装多周期 SAR 缓冲;PanelExtra 控制扩展面板显隐,接策略开关用。 下面那一串 void 方法才是落地重点:CurrentSymbolSet 切品种、Pips 算点值、CheckOpenPMx 查挂单、SetSLTPOrders 批量改止损止盈、CloseBuy/SellPositions 按品种平单。想验证就开 MT5 新建 EA,把这套声明贴进头文件,编译看 SymbolCount 不为 0 即接口可用。外汇与贵金属杠杆高,面板自动平仓仅降低手动延误,不消除跳空风险。
hour, class=class="str">"cmt">// Hour min, class=class="str">"cmt">// Minutes sec, class=class="str">"cmt">// Seconds dow, class=class="str">"cmt">// Day of week(class="num">0-Sunday, class="num">1-Monday, ... ,class="num">6-Saturday) doy; class=class="str">"cmt">// Day number of the year(January 1st is assigned the number value of zero) class=class="str">"cmt">//-- ENUM_TIMEFRAMES TFt, TFT05, TFSAR[]; class=class="str">"cmt">//-- class="type">bool PanelExtra; class=class="str">"cmt">//------------ MCEA(class="type">void); ~MCEA(class="type">void); class=class="str">"cmt">//------------ class=class="str">"cmt">//-- class="kw">virtual class="type">void FXSAR_MTF_MCEA_sptz_Config(class="type">void); class="kw">virtual class="type">void ExpertActionTrade(class="type">void); class=class="str">"cmt">//-- class="type">void ArraySymbolResize(class="type">void); class="type">void CurrentSymbolSet(const class="type">class="kw">string symbol); class="type">void Pips(const class="type">class="kw">string symbol); class="type">void TradeInfo(class="type">void); class="type">void Do_Alerts(const class="type">class="kw">string symbx,class="type">class="kw">string msgText); class="type">void CheckOpenPMx(const class="type">class="kw">string symbx); class="type">void SetSLTPOrders(class="type">void); class="type">void CloseBuyPositions(const class="type">class="kw">string symbol); class="type">void CloseSellPositions(const class="type">class="kw">string symbol); class="type">void CloseAllOrders(class="type">void); class="type">void CheckClose(const class="type">class="kw">string symbx); class="type">void TodayOrders(class="type">void); class="type">void UpdatePrice(const class="type">class="kw">string symbol,ENUM_TIMEFRAMES xtf); class="type">void RefreshPrice(const class="type">class="kw">string symbx,ENUM_TIMEFRAMES xtf,class="type">int bars); class=class="str">"cmt">//-- class="type">bool RefreshTick(const class="type">class="kw">string symbx);