构建K线图趋势约束模型(第8部分):EA的开发(一)·综合运用
(3/3)·把5000根K线压到1000根、拆出独立脚本后,EA如何把手动信号变成可跑的自动策略
很多交易者把指标塞进EA时,直接照搬原缓冲区逻辑,结果图表卡死、矩形不显示却查不出原因。历史K线默认拉到5000根、多缓冲区叠加,终端悄无声息地拖慢了整个分析流程。本篇接上两篇的约束模型,收尾讲清怎么用脚本分流再合回EA。
用 MQL5 交易请求结构下多单与空单
当指标缓冲 Buffer7[0] 不是 EMPTY_VALUE 时,视为多单触发信号;Buffer6[0] 非空则对应空单信号。这两段逻辑都依赖外部传入的 StopLoss、TakeProfit(以点数计)、Lots、Slippage、MagicNumber 等变量,实盘前需在输入参数里给明确数值,外汇与贵金属杠杆高,止损点数设错可能瞬间放大回撤。 多单部分先取 SYMBOL_ASK 作为入场价,SL 用 ask 减 StopLoss*_Point 并 NormalizeDouble 到品种精度,TP 则加 TakeProfit*_Point;空单反过来用 SYMBOL_BID,SL 在 bid 之上、TP 在 bid 之下。两者都走 MqlTradeRequest + OrderSend 的市价单路径,返回 retcode 不为 0 就 Print 错误码,便于在 MT5 Experts 日志里抓失效原因。 下面这段是原文多单与空单的下单核心,可直接贴进 EA 的 OnTick 信号判断之后验证:
class=class="str">"cmt">//--- Check for a buy signal if (Buffer7[class="num">0] != EMPTY_VALUE) { class="type">class="kw">double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); class="type">class="kw">double sl = NormalizeDouble(ask - StopLoss * _Point, _Digits); class="type">class="kw">double tp = NormalizeDouble(ask + TakeProfit * _Point, _Digits); class=class="str">"cmt">//--- Prepare the buy order request class="type">MqlTradeRequest request; class="type">MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = Lots; request.type = ORDER_TYPE_BUY; request.price = ask; request.sl = sl; request.tp = tp; request.deviation = Slippage; request.magic = MagicNumber; request.comment = "Buy Order"; class=class="str">"cmt">//--- Send the buy order if (!OrderSend(request, result)) { Print("Error opening buy order: ", result.retcode); } else { Print("Buy order opened successfully! Ticket: ", result.order); } } class=class="str">"cmt">//--- Check for a sell signal if (Buffer6[class="num">0] != EMPTY_VALUE) { class="type">class="kw">double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); class="type">class="kw">double sl = NormalizeDouble(bid + StopLoss * _Point, _Digits); class="type">class="kw">double tp = NormalizeDouble(bid - TakeProfit * _Point, _Digits); class=class="str">"cmt">//--- Prepare the sell order request class="type">MqlTradeRequest request; class="type">MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); request.volume = Lots; request.type = ORDER_TYPE_SELL; request.price = bid; request.sl = sl; request.tp = tp; request.deviation = Slippage; request.magic = MagicNumber; request.comment = "Sell Order"; class=class="str">"cmt">//--- Send the sell order if (!OrderSend(request, result)) { Print("Error opening sell order: ", result.retcode); } else { Print("Sell order opened successfully! Ticket: ", result.order); } }
◍ 回测与图表事件钩子的空壳写法
EA 框架里除了 OnInit/OnTick,还有一组专门对接交易动作、策略测试器和图表交互的系统函数,上面这段代码把它们的骨架都列出来了。 OnTrade 在每笔成交或订单状态变化时被触发,这里留空代表暂不处理实时交易事件;OnTester 必须返回 double,是优化时自定义评分的出口,现在写死返回 0.0 等于没给测试器任何额外判据。 OnTesterInit 与 OnTesterDeinit 分别在跑回测前和跑完后调用,适合做计数器清零或文件收尾;OnTesterPass 则在每一组参数优化跑完时执行,可以用来把中间结果落盘。 OnChartEvent 接收 id、lparam、dparam、sparam 四个参数,覆盖点击、按键、对象拖拽等界面动作。把这些函数原样塞进 MT5 的 EA 模板,编译能通过但不会产生任何交易行为,仅作为后续填充逻辑的占位。外汇与贵金属品种波动剧烈,用此类空壳做实盘前务必接真实风控。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Trade function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTrade() { class=class="str">"cmt">//--- Handle trade events if necessary } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Tester function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double OnTester() { class="type">class="kw">double ret = class="num">0.0; class=class="str">"cmt">//--- Custom calculations for strategy tester class="kw">return(ret); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| TesterInit function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTesterInit() { class=class="str">"cmt">//--- Initialization for the strategy tester } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| TesterPass function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTesterPass() { class=class="str">"cmt">//--- Code executed after each pass in optimization } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| TesterDeinit function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTesterDeinit() { class=class="str">"cmt">//--- Cleanup after tester runs } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| ChartEvent function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam) { class=class="str">"cmt">//--- Handle chart events here } class=class="str">"cmt">//+------------------------------------------------------------------+
「在策略测试器里跑通趋势约束EA」
在 MetaEditor 里点「编译」或「运行」,先把程序编过再丢进策略测试器。本次编译无报错,直接对 Boom 500 指数做回测。 从导航栏调出策略测试器面板,正式跑之前能改参数。EA 里默认手数是 0.1 手,但 Boom 500 指数的点值结构不同,实测至少得调到 0.2 手才不会因合约规格被拒单或信号失真。 点右下角「开始」后,系统在测试期内走出一条平滑上行的净值曲线,回测可视化里趋势约束逻辑没有明显反指。外汇与指数类品种杠杆高、跳空频繁,回测顺滑不代表实盘同概率盈利,上 MT5 用同样参数自己跑一遍最实在。
把工具请下神坛
风险回报矩形把未平仓的潜在盈亏边界直接画在图上,在贵金属与外汇这种秒级跳动的盘口里,交易者少算一次心算、就少一次被情绪带偏的可能。它本质只是个视觉锚点,逼你盯住原计划,而不是替你预测方向。 Trend Constraint V1.09 指标负责标趋势和反转区,EA 接信号下单并管风险,两者打通后策略更成体系;但外汇/贵金属高杠杆,协同再顺也只提高概率,不消除爆仓风险。 三个文件各司其职:指标源码 20.95 KB、矩形脚本 7.94 KB、EA 6.79 KB。装 EA 前先确认指标已载入且放行了 DLL 依赖,否则会报 4802 无法创建自定义指标。工具跑通了,剩下的纪律归你自己。