如何在 MetaTrader 5 里快速开发并调试交易策略(基础篇)
◍ 用 MT5 调试器从零搭一个剥头皮逻辑
自动剥头皮系统常被视作算法交易的硬骨头,代码复杂度高,但 MetaTrader 5 自带调试器能把这块骨头啃下来。官方示例文档里提到,基于真实历史数据做策略回测,比靠多年手工交易去摸索入场离场规则要快得多。 调试器那句“没有人可以信任,除了自己”不是鸡汤,是说策略逻辑必须自己逐行跑通。MT5 的可视测试配合瞬时报价分析,能让你在几分钟内看到订单在 K 线上的触发位置,而不是盲写条件。 外汇与贵金属剥头皮受点差和滑点影响极大,属于高风险玩法;任何回测盈利都只是历史概率,实盘可能显著偏离。开 MT5 按 F5 进调试模式,是验证一段新代码的最低成本动作。
用瞬时报价捕捉价格尖峰
要做这类交易,第一步是把通常的 K 线图换成瞬时报价图:每一次 tick 的价格变动都直接画出来,新报价到来时整张图向左推,不留聚合间隔。代码库里有一种现成指标能实现这个效果,比普通分时图更适合观察微观跳动。 思路核心落在相邻两个瞬时报价之间的变化量上。把连续 tick 的差值排成序列,例如:+1, 0, +2, -1, 0, +1, -2, -1, +1, -5, -1, +1, 0, -1, +1, 0, +2, -1, +1, +6…… 这些数字就是每跳点数。 按正态分布近似,两个瞬时报价间 99% 的价格变化落在 3 个标准差以内。我们可以在每笔 tick 进来时实时算标准差,用红、蓝图标标出超出边界的尖峰。外汇与贵金属波动受杠杆影响大、风险高,这类尖峰不代表方向确认,只提示概率上的异常。 标出来之后,交易者可以肉眼挑策略:倾向在尖峰后做均值回归,或等方向变换再跟。方法不复杂,但实盘里要先开 MT5 把指标加载到 EURUSD 的 M1 瞬时图验证刷新逻辑。
+class="num">1, class="num">0, +class="num">2, -class="num">1, class="num">0, +class="num">1, -class="num">2, -class="num">1, +class="num">1, -class="num">5, -class="num">1, +class="num">1, class="num">0, -class="num">1, +class="num">1, class="num">0, +class="num">2, -class="num">1, +class="num">1, +class="num">6, -class="num">1, +class="num">1,...
「在MT5里搭一个瞬时报价捕捉器」
用 MetaEditor 的 MQL 向导起一个新指标,勾选「指标位于单独窗口」,并声明 2 块作图板:一条瞬时报价线(绿色 DRAW_LINE)加一组彩色箭头信号(DRAW_COLOR_ARROW)。输入参数先定两个——ticks 默认 50,代表计算标准差时纳入的瞬时报价样本量;gap 默认 3.0,作为通道宽度的标准差系数。 在 OnInit() 里把三个缓存区映射好:TickPriceBuffer 存报价、SignalBuffer 存箭头位置、SignalColors 存颜色索引。首次调用 OnCalculate() 前要清缓存并把数组标记为序列,这样索引 0 就是最新一笔瞬时报价,向右递减。 把主要处理逻辑塞进独立的 ApplyTick():目前只做一件事,把所有缓存值右移一位,再把最新报价写进 TickPriceBuffer[0]。调试阶段直接挂 EURUSD 这类点差小的品种观察十几分钟,会看到供给价近似于蜡烛收盘价不动,图表拉出一片「高地」。 把代码微调成只保留非零增量的「锯齿」画法,价格跳动就直观了。外汇与贵金属杠杆高、滑点突发,这类指标仅作微观流动性观察,信号出现不代表趋势确认。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| TickSpikeHunter.mq5 | class=class="str">"cmt">//| 版权所有 class="num">2016, MetaQuotes 软件公司| class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "版权所有 class="num">2016, MetaQuotes 软件公司" class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_separate_window class="macro">#class="kw">property indicator_buffers class="num">3 class="macro">#class="kw">property indicator_plots class="num">2 class=class="str">"cmt">//--- 绘制瞬时报价 class="macro">#class="kw">property indicator_label1 "瞬时报价" class="macro">#class="kw">property indicator_type1 DRAW_LINE class="macro">#class="kw">property indicator_color1 clrGreen class="macro">#class="kw">property indicator_style1 STYLE_SOLID class="macro">#class="kw">property indicator_width1 class="num">1 class=class="str">"cmt">//--- 绘制信号 class="macro">#class="kw">property indicator_label2 "信号" class="macro">#class="kw">property indicator_type2 DRAW_COLOR_ARROW class="macro">#class="kw">property indicator_color2 clrRed,clrBlue,C&class="macro">#x27;class="num">0,class="num">0,class="num">0&class="macro">#x27;,C&class="macro">#x27;class="num">0,class="num">0,class="num">0&class="macro">#x27;,C&class="macro">#x27;class="num">0,class="num">0,class="num">0&class="macro">#x27;,C&class="macro">#x27;class="num">0,class="num">0,class="num">0&class="macro">#x27;,C&class="macro">#x27;class="num">0,class="num">0,class="num">0&class="macro">#x27;,C&class="macro">#x27;class="num">0,class="num">0,class="num">0&class="macro">#x27; class="macro">#class="kw">property indicator_style2 STYLE_SOLID class="macro">#class="kw">property indicator_width2 class="num">1 class=class="str">"cmt">//--- 输入参数 input class="type">int ticks=class="num">50; class=class="str">"cmt">// 计算所用瞬时报价数量 input class="type">class="kw">double gap=class="num">3.0; class=class="str">"cmt">// 通道宽度的标准差 class=class="str">"cmt">//--- 指标缓存区 class="type">class="kw">double TickPriceBuffer[]; class="type">class="kw">double SignalBuffer[]; class="type">class="kw">double SignalColors[]; class=class="str">"cmt">//--- 价格变化计数器 class="type">int ticks_counter; class=class="str">"cmt">//--- 第一次指标调用 class="type">bool first; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 自定义指标初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- 指标缓存区映射 SetIndexBuffer(class="num">0,TickPriceBuffer,INDICATOR_DATA); SetIndexBuffer(class="num">1,SignalBuffer,INDICATOR_DATA); SetIndexBuffer(class="num">2,SignalColors,INDICATOR_COLOR_INDEX);
◍ 空值与箭头信号的初始化收尾
指标初始化收尾时,要把两条绘图缓冲的空值设为 0,这样 MT5 在渲染时会直接跳过等于 0 的点,避免画出贴着零轴的假线。 信号线走的是图标模式,代码里把第 1 号缓冲的 PLOT_ARROW 设成 159,也就是 Wingdings 字符集里的那个小圆点箭头;你改这个数就能换信号样式,比如 108 是向下的钩。 ticks_counter 和 first 两个全局变量在 init 里归零和置真,first 这个旗标专门用来在 OnCalculate 首次触发时清缓冲并打上数组序列标记,后面计算才不会错位。 OnCalculate 目前直接 return(rates_total),等于告诉内核「全部 K 线都重算」,对轻量指标没压力;若你后续塞入逐根逻辑,这里得改成 return(prev_calculated) 才不卡顿。外汇与贵金属杠杆高,这类自定义指标仅作辅助参考,信号失效概率不低。
class=class="str">"cmt">//--- 设置空值, 在绘图时应被忽略 PlotIndexSetDouble(class="num">0,PLOT_EMPTY_VALUE,class="num">0); PlotIndexSetDouble(class="num">1,PLOT_EMPTY_VALUE,class="num">0); class=class="str">"cmt">//--- 信号将作为图标输出 PlotIndexSetInteger(class="num">1,PLOT_ARROW,class="num">159); class=class="str">"cmt">//--- 初始化全局变量 ticks_counter=class="num">0; first=true; class=class="str">"cmt">//--- 程序初始化成功 class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 自定义指标迭代函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">class="kw">datetime &time[], const class="type">class="kw">double &open[], const class="type">class="kw">double &high[], const class="type">class="kw">double &low[], const class="type">class="kw">double &close[], const class="type">long &tick_volume[], const class="type">long &volume[], const class="type">int &spread[]) { class=class="str">"cmt">//--- class=class="str">"cmt">//--- 返回 prev_calculated 的数值用于下次调用 class="kw">return(rates_total); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 自定义指标迭代函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">class="kw">datetime &time[], const class="type">class="kw">double &open[], const class="type">class="kw">double &high[], const class="type">class="kw">double &low[], const class="type">class="kw">double &close[], const class="type">long &tick_volume[], const class="type">long &volume[], const class="type">int &spread[]) { class=class="str">"cmt">//--- 在首次调用期间将指标缓存区清零并标记为序列 if(first) {