在MQL5中构建自优化智能交易系统(EA)(第五部分):自适应交易规则·综合运用
「宏定义用完后及时撤销」
在 MT5 的 Include 头文件或指标模块末尾,把临时宏常量统一 #undef 是避免命名污染的基本动作。上面这段代码在系统常量注释块之后,连续撤销了 RSI_PERIOD、RSI_PRICE、ATR_PERIOD、ATR_SIZE、TF_1、TF_2、VOL 共 7 个宏。 若不在文件尾做这步清理,其他 mqh 或 ea 再 #define 同名常量时,编译器可能直接报重定义错误,或者更隐蔽地沿用旧值。开 MT5 随便找一个自带指标的源码,搜 #undef 就能看到官方也是这套收尾逻辑。 外汇与贵金属品种波动属性差异大,这类周期类宏(TF_1/TF_2)若泄漏到多周期脚本里,可能让小周期误用大周期参数,实盘前务必确认作用域干净。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Undefine the system constants | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#undef RSI_PERIOD class="macro">#undef RSI_PRICE class="macro">#undef ATR_PERIOD class="macro">#undef ATR_SIZE class="macro">#undef TF_1 class="macro">#undef TF_2 class="macro">#undef VOL class=class="str">"cmt">//+------------------------------------------------------------------+
用动态中点替换固定 70/30 后实盘回测差异
经典 RSI 的 50 中点和 70/30 超买超卖线,在个别品种上可能长期碰不到。做法是先扫一遍指标输出区间,把最高、最低两条平行线做二等分,这条线就是该市场自己的新中点,原版 50 水平仅作参照。 算出新中点后,记录每个 RSI 读数与中点的绝对差值并取平均,得到该市场平均波动幅度。只有当 RSI 变化达到这个平均幅度的两倍(BIG_SIZE=2)才允许触发,相当于把 EA 敏感度握在手里;若把倍数设成 100,基本就永远不会开仓。 支撑阻力改由当前价与 5 根周期前(SUPPORT_PERIOD=5,约一周)价格对比确定,不再写死在指标水平。开仓条件也从固定水平挪到指标内存缓冲区算出的动态水平。 下面这段系统常量就是改动的核心,BARS 取 M15 可用 K 线总数的一半向下取整,BIG_SIZE 控制波动强度门槛: 回测对比很直接:初始版 136 笔交易净利 588.60 美元,动态规则版 121 笔交易净利 703.20 美元。交易次数降约 11%,利润反升约 19.5%,在外汇与贵金属这类高杠杆高风险品种上,这种样本内优势只代表历史倾向,实盘仍需自测。
class=class="str">"cmt">//+---------------------------------------------------------------+ class=class="str">"cmt">//| System constants | class=class="str">"cmt">//+---------------------------------------------------------------+ class="macro">#define RSI_PERIOD class="num">10 class=class="str">"cmt">//The period for our RSI indicator class="macro">#define RSI_PRICE PRICE_CLOSE class=class="str">"cmt">//The price level our RSI should be applied to class="macro">#define ATR_SIZE class="num">1.5 class=class="str">"cmt">//How wide should our Stop loss be? class="macro">#define ATR_PERIOD class="num">14 class=class="str">"cmt">//The period of calculation for our ATR indicator class="macro">#define TF_1 PERIOD_D1 class=class="str">"cmt">//The primary time frame for our trading application class="macro">#define TF_2 PERIOD_M15 class=class="str">"cmt">//The secondary time frame for our trading application class="macro">#define VOL class="num">0.1 class=class="str">"cmt">//Our trading volume class="macro">#define BARS(class="type">int) MathFloor((iBars(Symbol(),TF_2) / class="num">2)) class=class="str">"cmt">//How many bars should we use for our calculation? class="macro">#define BIG_SIZE class="num">2 class=class="str">"cmt">//How many times bigger than the average move should the observed change be? class="macro">#define SUPPORT_PERIOD class="num">5 class=class="str">"cmt">//How far back into the past should we look to find our support and resistance levels?
◍ 用 RSI 偏离中轴抓次级周期反转
这段逻辑跑在比主周期更短的时间框架 TF_2 上,专门处理那些需要更快响应的临门一脚。它只在无持仓时寻找机会,避免来回加仓把账户拖进外汇或贵金属的高波动泥潭。 核心是先算 RSI 的「动态中轴」:取最近 BARS 根 K 线的 RSI 最大值与最小值取平均,再用 MathAbs 算每个值与中轴的距离向量 rsi_growth,取其均值作为常态偏离尺度。当实时 RSI 偏离中轴超过 rsi_growth.Mean() * BIG_SIZE 倍时,才认为偏离显著。 价格层面用 iLow / iHigh 碰阻力 support、resistance 做触发门槛:上方破阻力且 RSI 正向偏离够大就 Sell,下方破支撑且负向偏离够大就 Buy。止损止盈统一用 ATR_SIZE * atr[0] 对称展开,例如 ATR_SIZE=2 时上下各 2 倍 ATR。 别把正态当圣经:rsi_growth.Mean() 只是样本均值,BARS 设 50 还是 200 会直接改变触发灵敏度,建议在 MT5 里把 BARS 和 BIG_SIZE 拉成输入参数来回测不同品种。
class="kw">static class="type">class="kw">datetime time_stamp; class="type">class="kw">datetime current_time = iTime(Symbol(),TF_2,class="num">0); class="type">class="kw">double bid,ask; class=class="str">"cmt">//Update the time if(time_stamp != current_time) { time_stamp = current_time; bid=SymbolInfoDouble(Symbol(),SYMBOL_BID); ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK); class=class="str">"cmt">//Copy the rsi readings into a vector vector rsi_vector = vector::Zeros(BARS); rsi_vector.CopyIndicatorBuffer(rsi_handler,class="num">0,class="num">1,BARS); class=class="str">"cmt">//Let&class="macro">#x27;s see how far the RSI tends to deviate from its centre class="type">class="kw">double rsi_midpoint = ((rsi_vector.Max() + rsi_vector.Min()) / class="num">2); vector rsi_growth = MathAbs(rsi_vector - rsi_midpoint); class=class="str">"cmt">//Check if we have broken either extreme if(PositionsTotal() == class="num">0) { class=class="str">"cmt">//We are looking for opportunities to sell if(iLow(Symbol(),TF_2,class="num">0) > resistance) { if((rsi[class="num">0] - rsi_midpoint) > (rsi_growth.Mean() * BIG_SIZE)) Trade.Sell(VOL,Symbol(),bid,(ask + (ATR_SIZE * atr[class="num">0])),(ask - (ATR_SIZE * atr[class="num">0]))); } class=class="str">"cmt">//We are looking for opportunities to buy if(iHigh(Symbol(),TF_2,class="num">0) < support) { if((rsi[class="num">0] - rsi_midpoint) < (-(rsi_growth.Mean() * BIG_SIZE))) Trade.Buy(VOL,Symbol(),ask,(bid - (ATR_SIZE * atr[class="num">0])),(bid + (ATR_SIZE * atr[class="num">0]))); } } }
「画得少,看得清」
把经典 RSI 的 30/70 换成从当前品种指标分布里挑出的最优边界,等于让阈值跟着市场呼吸,而不是拿教科书硬套。多品种同时盯盘时,这种自适应规则能直接比出各市场真实波动强度,避免被固定水平骗进假信号。 附件里基准版 Self_Adapting_Trading_Rules.mq5 仅 6.2 KB,V2 优化版 7.41 KB,差距不到 1.3 KB 却把静态规则换成了随数据调整的逻辑。外汇与贵金属杠杆高、滑点跳空频繁,直接上实盘前务必在 MT5 策略测试器跑两轮再决定参数。 打开 V2 源码,先改 Input 里的品种列表,让小布替你把那套阈值重算一遍,比肉眼翻几十张图来得准。