构建和测试 Aroon 交易系统·综合运用
(3/3)·把 Aroon 的算法逻辑、策略组装与自动回测串成一条可落地的工程链,省去你手动试错的几十小时
「Aroon 交叉触发下单的 EA 骨架」
下面这段 MQL5 片段把 Aroon 指标的上下轨交叉直接接成了市价单触发逻辑,属于典型的信号转执行写法。默认参数里 periodInp=25、slLvl=200 点、tpLvl=600 点,EURUSD 这类品种在 M5 上约对应 20 美元止损、60 美元止盈(按 1 手计),外汇和贵金属杠杆高,实盘前务必在策略测试器跑一遍。 代码先通过 iCustom 挂一个名为 "Aroon" 的自定义指标,OnTick 里用 barsTotal 做新 K 线过滤,只在换柱时跑一次信号判断,避免同根 K 线重复发单。CopyBuffer 取最近 3 根的上轨与下轨,再把数组设成时间序列,使 [0] 为最新值、[1] 为前一根。 交叉判定很直白:前一根上轨低于下轨、当前上轨高于下轨,视为多头反转,按 SYMBOL_ASK 市价买入并挂 200 点止损、600 点止盈;反向则卖出。这种写法没有过滤滑点和点差扩散,在重要数据行情里可能触发偏不利成交,概率上需自行加一层 spread 判断。
CopyBuffer(aroon,class="num">0,class="num">0,class="num">3,upBuffer); CopyBuffer(aroon,class="num">1,class="num">0,class="num">3,downBuffer); ArraySetAsSeries(upBuffer,true); ArraySetAsSeries(downBuffer,true); class="type">class="kw">double prevUpValue = upBuffer[class="num">1]; class="type">class="kw">double prevDownValue = downBuffer[class="num">1]; class="type">class="kw">double upValue = upBuffer[class="num">0]; class="type">class="kw">double downValue = downBuffer[class="num">0]; if(prevUpValue<prevDownValue && upValue>downValue) { class="type">class="kw">double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK); class="type">class="kw">double slVal=ask - slLvl*_Point; class="type">class="kw">double tpVal=ask + tpLvl*_Point; trade.Buy(lotSize,_Symbol,ask,slVal,tpVal); } if(prevUpValue>prevDownValue && upValue<downValue) { class="type">class="kw">double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); class="type">class="kw">double slVal=bid + slLvl*_Point; class="type">class="kw">double tpVal=bid - tpLvl*_Point; trade.Sell(lotSize,_Symbol,bid,slVal,tpVal); } } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| AroonLevelsEA.mq5 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <trade/trade.mqh> input class="type">int periodInp = class="num">25; class=class="str">"cmt">// Period input class="type">int shiftInp = class="num">0; class=class="str">"cmt">// Shift input class="type">class="kw">double lotSize=class="num">1; input class="type">class="kw">double slLvl=class="num">200; input class="type">class="kw">double tpLvl=class="num">600; class="type">int aroon; class="type">int barsTotal; CTrade trade; class="type">int OnInit() { barsTotal=iBars(_Symbol,PERIOD_CURRENT); aroon = iCustom(_Symbol,PERIOD_CURRENT,"Aroon",periodInp,shiftInp); class="kw">return(INIT_SUCCEEDED); } class="type">void OnDeinit(const class="type">int reason) { Print("EA is removed"); } class="type">void OnTick() { class="type">int bars=iBars(_Symbol,PERIOD_CURRENT); if(barsTotal != bars) { barsTotal=bars; class="type">class="kw">double upBuffer[], downBuffer[]; CopyBuffer(aroon,class="num">0,class="num">0,class="num">3,upBuffer);
用 Aroon 下穿阈值触发双向挂单
Aroon Down 线从上方跌破 10,往往意味着近期下跌趋势强度衰减,价格可能转向。代码里用 downBuffer[1] 与 downBuffer[0] 做跨根比较,捕捉这一穿越瞬间,并以市价买方式介入。 反过来,当下行线从下方上穿 50,说明下跌动能重新占优,逻辑翻转做空。两个分支都依赖 CopyBuffer 取最近 3 根数值,并用 ArraySetAsSeries 把数组改成时间倒序,保证 [0] 是最新根、[1] 是前一根。 止损止盈直接用 slLvl、tpLvl 乘 _Point 换算,买单 SL 在 ask 下方、卖单 SL 在 bid 上方。外汇与贵金属杠杆高,这类穿越信号在震荡市可能连续假突破,实盘前先在 MT5 策略测试器跑一轮 EURUSD 15M 看胜率分布。
CopyBuffer(aroon,class="num">1,class="num">0,class="num">3,downBuffer); ArraySetAsSeries(upBuffer,true); ArraySetAsSeries(downBuffer,true); class="type">class="kw">double prevDownValue = downBuffer[class="num">1]; class="type">class="kw">double downValue = downBuffer[class="num">0]; if(prevDownValue> class="num">10 && downValue<class="num">10) { class="type">class="kw">double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK); class="type">class="kw">double slVal=ask - slLvl*_Point; class="type">class="kw">double tpVal=ask + tpLvl*_Point; trade.Buy(lotSize,_Symbol,ask,slVal,tpVal); } if(prevDownValue < class="num">50 && downValue>class="num">50) { class="type">class="kw">double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); class="type">class="kw">double slVal=bid + slLvl*_Point; class="type">class="kw">double tpVal=bid - tpLvl*_Point; trade.Sell(lotSize,_Symbol,bid,slVal,tpVal); } } } class="type">class="kw">double prevDownValue = downBuffer[class="num">1]; class="type">class="kw">double downValue = downBuffer[class="num">0]; if(prevDownValue> class="num">10 && downValue<class="num">10) { class="type">class="kw">double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK); class="type">class="kw">double slVal=ask - slLvl*_Point; class="type">class="kw">double tpVal=ask + tpLvl*_Point; trade.Buy(lotSize,_Symbol,ask,slVal,tpVal); } if(prevDownValue < class="num">50 && downValue>class="num">50) { class="type">class="kw">double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); class="type">class="kw">double slVal=bid + slLvl*_Point; class="type">class="kw">double tpVal=bid - tpLvl*_Point; trade.Sell(lotSize,_Symbol,bid,slVal,tpVal); }
◍ Aroon 两种策略的回测数据对比
把 Aroon 交叉和 Aroon 水平两条思路放进 MT5 策略测试器,统一跑 2023-01-01 至 2023-12-31 整年,分别压 15 分钟和 1 小时周期,才有可比性。评价只看六个硬指标:净利润=毛利减毛损、相对余额回撤=过程最大浮亏、利润因子=毛利/毛损、预期回报=单笔均值、恢复因子=抗损复原力、夏普比率=风险调整后稳定性。
- 分钟下交叉策略净利润 14791、回撤 6.78%、因子 1.17、预期 24.53、恢复 1.91、夏普 2.23;同周期水平策略净利润冲到 42417.30,但回撤也放大到 12.91%,因子 1.21、预期 29.62、恢复 2.27、夏普 1.88。
切到 1 小时,交叉策略净利润降到 6242.20,可回撤仅 1.80%,因子 1.39、预期 53.81、恢复 2.43、夏普 3.23;水平策略净利润 16001.10、回撤 5.11%、因子 1.30、预期 41.89、恢复 2.68、夏普 2.61。 横向看,15 分钟水平策略净利润最高(42417.30),1 小时交叉策略在回撤(1.80%)、因子(1.39)、预期(53.81)、夏普(3.23)四项占优,1 小时水平策略恢复因子最高(2.68)。外汇与贵金属杠杆高,上述为历史回测、样本仅一年,实盘可能偏离,选哪套得对照你自己的回撤承受线和交易频率。
「把这条线请下神坛」
Aroon 上行/下行线在 15 分钟与 1 小时框架里跑过交叉与水平两套逻辑:交叉法按上下线谁压谁自动开买卖仓,水平法以 10 与 50 两条阈值触发反向单。回测样本只覆盖这两个周期,没跨日线及以上,外推到其他周期前得自己重跑。 它本质只是把 N 根 K 线内高低点出现位置折成百分比,单独用容易被横盘刷掉手续费。外汇与贵金属杠杆高、滑点跳空频繁,任何指标策略都只是概率倾向,不是通关文牒。 真要落地,把 AroonLevelsEA.mq5 拖进 MT5 先改 10/50 参数看 EURUSD 的 1H 表现,再决定加不加过滤。指标是工具不是神,能启发系统思路就够了。