TradeObjects: 基于 MetaTrader 图形对象的自动化交易·综合运用
📐

TradeObjects: 基于 MetaTrader 图形对象的自动化交易·综合运用

(3/3)·从信号判定到订单追踪,一套开源工具如何把画线变成可回测的执行逻辑

含代码示例偏理论 第 3/3 篇
很多人以为在图表上拖一根趋势线就能让 EA 自动跟单,实际多数现成程序要么功能阉割要么年久失修。本篇把前三篇铺垫的对象识别与信号逻辑收拢成一套可改写的开源框架,MT4 测试器里还能练手感。

◍ 从图形对象反推价格坐标的取数逻辑

在 MT5 里手动画的趋势线、通道、斐波那契,都能被 EA 用 ObjectGetDouble / ObjectGetInteger 读回数值,关键是你得按对象类型分支处理。 趋势线取两个锚点的价格,用柱索引 i1、i2 做斜率 k 和截距 b 的线性回归,返回 b 供后续计算;水平线直接返回单点价格,竖线返回 EMPTY_VALUE 表示不参与价格运算。 通道对象要取三个时间点 dt1/dt2/dt3,转成柱索引后先做合法性判断:若 i1<=i2 或任一索引为 -1,直接 Print 报错并返回 0,避免错算。其斜率与截距算法同趋势线,但多出 dy 偏差与 auxiliary 偏移量,用于衡量第三点相对回归线的距离。 斐波那契对象则约定 61.8 等级作限价入场参考,38.2 与 161.8 分别倾向用于止损和止盈边界;外汇与贵金属波动剧烈,这类层级仅作概率参考,实战须自担高风险。

MQL5 / C++
class="type">class="kw">double p1 = ObjectGetDouble(class="num">0, name, OBJPROP_PRICE, class="num">0);
class="type">class="kw">double p2 = ObjectGetDouble(class="num">0, name, OBJPROP_PRICE, class="num">1);

class="type">class="kw">double k = -(p1 - p2)/(i2 - i1);
class="type">class="kw">double b = -(i1 * p2 - i2 * p1)/(i2 - i1);

class="kw">return b;
}
else if(type == OBJ_HLINE)
{
  class="kw">return ObjectGetDouble(class="num">0, name, OBJPROP_PRICE, class="num">0);
}
else if(type == OBJ_VLINE)
{
  class="kw">return EMPTY_VALUE; class=class="str">"cmt">// 不应是 null, 否则不使用
}
else if(type == OBJ_CHANNEL)
{
  class="type">class="kw">datetime dt1 = (class="type">class="kw">datetime)ObjectGetInteger(class="num">0, name, OBJPROP_TIME, class="num">0);
  class="type">class="kw">datetime dt2 = (class="type">class="kw">datetime)ObjectGetInteger(class="num">0, name, OBJPROP_TIME, class="num">1);
  class="type">class="kw">datetime dt3 = (class="type">class="kw">datetime)ObjectGetInteger(class="num">0, name, OBJPROP_TIME, class="num">2);
  class="type">int i1 = iBarShift(NULL, class="num">0, dt1, true);
  class="type">int i2 = iBarShift(NULL, class="num">0, dt2, true);
  class="type">int i3 = iBarShift(NULL, class="num">0, dt3, true);
  if(i1 <= i2 || i1 == -class="num">1 || i2 == -class="num">1 || i3 == -class="num">1)
  {
    Print("Incorrect channel: ", name);
    class="kw">return class="num">0;
  }
  class="type">class="kw">double p1 = ObjectGetDouble(class="num">0, name, OBJPROP_PRICE, class="num">0);
  class="type">class="kw">double p2 = ObjectGetDouble(class="num">0, name, OBJPROP_PRICE, class="num">1);
  class="type">class="kw">double p3 = ObjectGetDouble(class="num">0, name, OBJPROP_PRICE, class="num">2);
  
  class="type">class="kw">double k = -(p1 - p2)/(i2 - i1);
  class="type">class="kw">double b = -(i1 * p2 - i2 * p1)/(i2 - i1);
  
  class="type">class="kw">double dy = i3 * k + b - p3;
  
  auxiliary = p3 - i3 * k;
  
  class="kw">return b;
}
else if(type == OBJ_FIBO)
{
  class=class="str">"cmt">// 回撤等级 class="num">61.8 是入场点 (buy/sell limit),
  class=class="str">"cmt">// class="num">38.2 和 class="num">161.8 作为止损/止盈

「从斐波那契对象抓取价格与时间的坑」

读 MT5 里的斐波那契工具,不能假设第一点时间一定早于第二点。下面这段逻辑先抓两个端点的价格和时间,若 dt2 早于 dt1 就交换价格变量,否则后续按 (p2-p1) 乘.levelvalue 算出来的辅助线会整体反向。 double p1 = ObjectGetDouble(0, name, OBJPROP_PRICE, 0); // 端点0的价格 double p2 = ObjectGetDouble(0, name, OBJPROP_PRICE, 1); // 端点1的价格 datetime dt1 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 0); // 端点0时间 datetime dt2 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 1); // 端点1时间 if(dt2 < dt1) { swap(p1, p2); } // 时间倒挂则换价格 价格计算用 levelvalue 索引 4、2、6 分别对应主位、辅助线与另一斐波位:(p2-p1)*LevelValue(4)+p1 才是正确投影价。若你手绘时从右往左拉工具,索引不校正会偏掉整段。 checkTime 用 OBJ_VLINE 类型且时间等于 Time[0] 来判断当前K线竖线,disableLine 则在宽度>1时减宽并改 ZORDER,否则置背底并调暗颜色——这是避免旧线干扰点击的实用写法。外汇与贵金属波动剧烈,这类对象读取逻辑先在模拟盘验证再上实盘。

MQL5 / C++
class="type">class="kw">double p1 = ObjectGetDouble(class="num">0, name, OBJPROP_PRICE, class="num">0);
class="type">class="kw">double p2 = ObjectGetDouble(class="num">0, name, OBJPROP_PRICE, class="num">1);
class="type">class="kw">datetime dt1 = (class="type">class="kw">datetime)ObjectGetInteger(class="num">0, name, OBJPROP_TIME, class="num">0);
class="type">class="kw">datetime dt2 = (class="type">class="kw">datetime)ObjectGetInteger(class="num">0, name, OBJPROP_TIME, class="num">1);

if(dt2 < dt1)
{
  swap(p1, p2);
}

class="type">class="kw">double price = (p2 - p1) * ObjectGetDouble(class="num">0, name, OBJPROP_LEVELVALUE, class="num">4) + p1;
auxiliary = (p2 - p1) * ObjectGetDouble(class="num">0, name, OBJPROP_LEVELVALUE, class="num">2) + p1;
auxiliaryFibo = (p2 - p1) * ObjectGetDouble(class="num">0, name, OBJPROP_LEVELVALUE, class="num">6) + p1;
class="kw">return price;
}
class="kw">return class="num">0;
}
class="type">bool checkTime(class="kw">const class="type">class="kw">string name)
{
  class="kw">return (ObjectGetInteger(class="num">0, name, OBJPROP_TYPE) == OBJ_VLINE
    && (class="type">class="kw">datetime)ObjectGetInteger(class="num">0, name, OBJPROP_TIME, class="num">0) == Time[class="num">0]);
}
class="type">void disableLine(class="kw">const class="type">class="kw">string name)
{
  class="type">int width = (class="type">int)ObjectGetInteger(class="num">0, name, OBJPROP_WIDTH);
  if(width > class="num">1)
  {
    ObjectSetInteger(class="num">0, name, OBJPROP_WIDTH, width - class="num">1);
    ObjectSetInteger(class="num">0, name, OBJPROP_ZORDER, TimeCurrent());
  }
  else
  {
    ObjectSetInteger(class="num">0, name, OBJPROP_BACK, true);
    ObjectSetInteger(class="num">0, name, OBJPROP_COLOR, darken((class="type">color)ObjectGetInteger(class="num">0, name, OBJPROP_COLOR)));
  }
  display();
}
  if(last != class="num">0 && (TimeCurrent() - last) / PeriodSeconds() < EventInterval)
  {
    class="kw">return class="num">0;
  }

线画上去之后EA怎么替你跑单

头肩形态被识别后,EA会在图表上拉出一条红色水平线作为卖单触发位,并在注释里列出当前受控对象。止损只认DefaultStopLoss参数,行情存活期由蓝色垂直虚线框死,到了这条线无论盈亏都平仓,原激活线变暗沉底。 菲波纳奇默认只管到61.8突破,等级颜色走独立设置、不和对角线同色。价格碰等级时按38.2挂止损、远端挂止盈开一笔(截图上161.8那档没显示出来)。上方阻力线出来后叠蓝色通道,辅助线压在主线下,通道自己算止损距离,突破后新多单手数算出来是0.04——存款1000美元、杠杆1:500的设定下,'-1'描述代表按保证金1%开仓,而非写死的Lot=0.01。 两笔多单用一条红色虚线统杀平仓。之后价格进走廊,上下各一条水平虚线挂限价单、灰色竖线管激活,下方挂单写了0.02手、24根H1柱失效;触发后sell limit成交,buy limit挨到周一过期。再放灰竖线等于全平指令,空多通杀。 EA在日志里留痕,例如2017.07.06 10:05:27触发通道突破后open #3 buy 0.04 EURUSD at 1.13478 sl: 1.12908;19:02:18碰止盈线close #3 at 1.14093、close #2 at 1.14093。这些过期对象示例里没删,纯粹当操作凭证。 MT5测试器跑这套得先把模板改名tester.tpl,平台不让在测试器里直接加载编辑模板;初始存款1000、1:500、DefaultStopLoss=-1(亏保证金1%)是标准演示设定。外汇与贵金属杠杆交易风险高,模板行为只是历史片段演示,实盘参数请自行在MT5回测验证。

◍ 画得少,看得清

这套半自动思路的核心,是把交易触发权交还给图表上的几根线:趋势线、水平线、垂线,再加上菲波纳奇通道与网格,就能让 EA 替你发市价单、挂单,或在形态出现时弹提醒。MT5 附件包体积 29.13 KB,MT4 的仅 11.97 KB,轻量到可以直接拖进终端试。 代码全开源,支持的对象类型你随时能自己加。有用户在 2022 年 4 月反馈,更新 fxsaber 维护的 MT4Orders.mqh 依赖库后,EA 才从 231 个编译错误走到能跑,但偶尔只认水平线、其余时间在「睡觉」——说明对象识别逻辑仍依赖你画线的规范程度与库版本匹配。 外汇与贵金属属高风险品种,任何基于画线对象的自动触发都只是概率倾向,实盘前请在策略测试器用历史数据验一遍识别率再上真金。

把对象信号诊断交给小布
这些图形对象的边界判定与信号触发逻辑,小布盯盘的 AIGC 已内置到对应品种页,打开即可看到对象级别的多空触发状态,你只管确认策略偏好。

常见问题

二者均在二维空间形成逻辑边界,价格触及或穿越边界时按预设解释生成突破或回滚信号,具体映射在对象属性里配置,代码层已抽象出统一处理接口。
小布盯盘内置的是对象信号的可视化诊断,并非直接运行 MQL 源码;若需执行级自动化,仍需在 MT4/MT5 部署本文给出的 EA 工程,二者互补而非替代。
MT5 实盘端对象可正常读写,仅测试器禁交互;保留 MT5 编译分支是为实盘用户,而历史技能打磨只能退回 MT4 测试器,所以双版本都有存在必要。
可以,水平线定价位边界、垂直线定时间边界,二者交集即触发平仓条件,程序在边界交叉检测里分别处理两类对象并做逻辑与判断。