在MQL5中构建自优化智能交易系统(EA)(第五部分):自适应交易规则(基础篇)
「RSI固定阈值在趋势市里会集体失效」
技术指标给算法交易提供了明确信号,但标准化规则常和行情对着干。以RSI为例,教科书说低于30买、高于70卖是最优,可黄金日线用20周期RSI时,2019年9月到12月这3个月强跌趋势里,读数始终没触碰70做空线,预期信号根本没出现。 有人觉得调周期就能解决,其实未必。做跨市场扫描的人往往锁定了能揭示特定模式的固定周期,不能动;单品种交易者若加大RSI周期,噪音平滑后反而更难触发,减小周期则引入更多杂讯、信号质量下降。 随意改参数可能让EA出现意外行为:该抓的机会没抓到,或者退出条件没完全满足,使得外汇/贵金属这类高风险品种的市场敞口高过预期。图1里那条趋势线直观显示了信号真空区。
用平均走势幅度筛掉弱信号
要让程序根据历史自动调整规则,核心思路是先搞清楚某个品种平时的走势幅度长什么样。只有先建立「平均走势」的基准,才能谈得上识别强劲走势。 具体落地时,把入场门槛设成平均幅度的 2 倍或 3 倍,应用程序就只会吃那些明显强于常态的行情。以 EURUSD 日线为例,若过去 200 根 K 线实体均值约 40 点,那只做实体 ≥80 点的突破信号,噪音交易会少一大截。 这么做有两个实打实的好处:一是不会因阈值定太高导致连续几周不开仓;二是 RSI 这类指标的周期优化可以省掉大半,因为信号强弱改由价格本身的统计分布说了算。外汇与贵金属波动受事件驱动,用历史均值做基准仍属概率性过滤,不保证信号后一定出趋势。
◍ 用 MQL5 搭一个支撑阻力突破的底仓框架
先把基准跑出来,才有后面迭代的参照。这里用 MQL5 写一个简单的 EA:在 XAGUSD 日线图上判定支撑/阻力,再用 RSI 确认突破后进场,止损止盈幅度固定,这样后续收益变化只反映决策逻辑本身的差异。外汇与贵金属杠杆高、滑点跳空频繁,任何回测盈利都不代表实盘胜率。 支撑位看成引发反弹的低价区,阻力位是触发抛压的关注价区;突破任一边,价格往往出现一段单向移动。文中用「当前价 vs 5 天前对应水平」来识别这些价位,并在价格跌破支撑时寻机做多,仅当阻力反向失守才做空。 回测固定用 2017-01-01 至 2025-01-28 的 M15 历史数据,开启「随机延迟」并选「基于真实点差的每 tick」,贴近经纪商实际环境。首版无输入参数,加载后设好日期直接跑策略测试器即可。 首测结果有看点:总净利润 588.60 美元,夏普比率 0.85,作为后续版本要超越的底线。下面这段代码是系统常量与交易库导入部分,先把骨架立住。 代码逐行拆解:RSI_PERIOD 10 表示 RSI 取 10 周期;RSI_PRICE 用收盘价计算;ATR_SIZE 1.5 代表止损宽度为 1.5 倍 ATR;ATR_PERIOD 14 是 ATR 计算周期;TF_1 用日线做主框架,TF_2 用 M15 做触发;VOL 0.1 手固定仓位。#include 引入 Trade.mqh 并实例化 CTrade,后续开平仓都靠它。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Self Adapting Trading Rules.mq5 | class=class="str">"cmt">//| Gamuchirai Ndawana | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Gamuchirai Ndawana" class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" 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=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Libraries we need | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <Trade/Trade.mqh> CTrade Trade; class=class="str">"cmt">//+------------------------------------------------------------------+
「把指标句柄和支撑阻力塞进全局」
EA 骨架搭完之后,真正干活的是全局变量和那几个自定义方法。先把 rsi_handler、atr_handler 两个句柄以及 rsi[]、atr[] 数组、support、resistance 双价挂在全局,后面 tick 里随时能取,不用反复声明。 setup() 在 OnInit 里被调用,负责把指标句柄加载进来:atr 用当前品种、TF_1 周期、ATR_PERIOD 参数;rsi 用 TF_2 周期、RSI_PERIOD 和 RSI_PRICE。支撑取 TF_1 第 5 根 K 线最低价,阻力取同周期第 5 根最高价——这两个值后续作为区间边界,外汇与贵金属波动大,实际只会是动态参考而非硬边界。 OnDeinit 里调 release() 释放句柄,IndicatorRelease 不写的话 MT5 长时间跑多个实例可能漏内存。OnTick 只做一件事:调 update() 刷新变量并找形态,逻辑全塞在用户函数里,主流程保持干净。 想验证就开 MT5 建个 EA,把 TF_1 设成 H1、TF_2 设成 M15,ATR_PERIOD 给 14,编译后看第 5 根 K 的 support/resistance 是否随新柱重置,黄金品种上这种边界重算频率明显高于欧元。
class=class="str">"cmt">//| Global variables | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int rsi_handler,atr_handler; class="type">class="kw">double rsi[],atr[]; class="type">class="kw">double support,resistance; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- setup(); class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { class=class="str">"cmt">//--- release(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- update(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| User defined methods | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Initialize our system variables | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void setup(class="type">void) { class=class="str">"cmt">//Load our technical indicators atr_handler = iATR(Symbol(),TF_1,ATR_PERIOD); rsi_handler = iRSI(Symbol(),TF_2,RSI_PERIOD,RSI_PRICE); resistance = iHigh(Symbol(),TF_1,class="num">5); support = iLow(Symbol(),TF_1,class="num">5); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Let go of resources we are no longer consuming | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void release(class="type">void) { class=class="str">"cmt">//Free up resources we are not class="kw">using IndicatorRelease(atr_handler); IndicatorRelease(rsi_handler); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Update our system variables and look for trading setups | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void update(class="type">void) { class=class="str">"cmt">//Update our system variables class=class="str">"cmt">//Some duties must be performed periodically on the higher time frame { class="kw">static class="type">class="kw">datetime time_stamp;