MT4模式交易法测跟踪止损
📐

MT4模式交易法测跟踪止损

用理想线条复现订单以公平对比退出算法

回测与优化 难度 · 进阶 2026-06-01 5 分钟阅读
#MQL4#跟踪止损#策略测试#模式交易

为什么需要模式交易测试工具

开发订单修改与关闭算法时,最大痛点在于如何公平比较不同方法的结果。策略测试器虽是标准检查机制,但复杂EA若自行计算进场、跟踪与退出,几乎无法获得可重复且相互可比的模式。假设某进场信号持续较长时间,价格在预测方向波动时跟踪止损启动;止损过近会过早平仓,若信号仍有效EA又开新单,便把正确订单收益与提前关闭后其他订单收益混杂,无法隔离评估跟踪算法本身优劣。

本文提出的思路是:将进场与出场点标记在图表,把时间与方向存入文件,再用一个只执行文件命令的EA严格重放。这样所有测试都在同一组数学正确的订单流上进行,不同跟踪止损逻辑的差异才能被干净地度量。

✦ 核心益处
模式交易法把'市场情景'与'交易算法'解耦:情景由线条定义一次,算法可反复更换优化,结果具备直接可比性。

问题说明与线条标记逻辑

在图表中用两种线条代表交易:紫线为理想进出场,用于计算理论最大收益;蓝线为实际特点,如进场延迟、保本位附近平仓,用来测试真实跟踪。蓝线在跟踪后可能出现多个止损触发点:最近距价触发激进跟踪、常规触发耐心跟踪、理想触发最大化收益,也可能因急躁在反转区误触发。

标记原则:进场点应选历史中明显的市场反转处;出场点不要在反转瞬间而是之后,让算法自行完成操作,即使亏损也暴露重设必要。通过这种标记,我们获得可重复的理想区域,后续仅需完善之。

标记工具脚本组详解

为方便标记,作者提供一组脚本:TL_Buy与TL_Sell分别在拖放点创建买/卖趋势线段;TL_Write将所有TL_前缀线条坐标写入文件;TL_Read从文件重绘;TL_Clear清空。线条命名规则统一为前缀TL_+方向字符(B/S)+编号,如TL_B1、TL_S3,便于选择删除与程序识别。

使用方式:把绘制脚本拖到图表,出现线段后拖动端点对齐理想蓝线;运行TL_Write输入文件名保存;测试前用TL_Read可重建编辑。脚本代码透明带注释,算法不复杂但严格依赖对象命名约定。

// TL_Buy 片断 (MQL4)
#include <WinUser32.mqh>
#define _prefix_ "TL_"
int start(){
  int MaxNo=0,i,No; 
  if(WindowOnDropped()!=0) { MessageBox("Script should be dropped in the main window","ERROR", IDOK + MB_ICONERROR); return(1); }
  for(i=0;i<ObjectsTotal();i++) {
    if(StringFind(ObjectName(i),_prefix_)==0) {
      No=StrToInteger(StringSubstr(ObjectName(i),StringLen(_prefix_)+1));
      if(MaxNo<No) MaxNo=No;
    }
  }
  datetime t0=WindowTimeOnDropped(); double p0=WindowPriceOnDropped();
  int width = 5*Period()*60;
  double height = 20*MarketInfo(Symbol(),MODE_TICKSIZE);
  string LineName = _prefix_+"B"+(MaxNo+1);
  ObjectCreate(LineName,OBJ_TREND,0,t0-width,p0-height, t0+width,p0+height);
  ObjectSet(LineName,OBJPROP_RAY,False);
  ObjectSet(LineName,OBJPROP_WIDTH,2);
  ObjectSet(LineName,OBJPROP_COLOR,Blue);
}
// TL_Write 片断 (MQL4)
#include <WinUser32.mqh>
#define _prefix_ "TL_"
#property show_inputs
extern string FileNameForWrite = "TL_DATA.TXT";
int start(){
  int LinesCNT=0,i; string Operation;
  int fh=FileOpen(FileNameForWrite,FILE_CSV|FILE_WRITE,';');
  for(i=0;i<ObjectsTotal();i++) {
    if(StringFind(ObjectName(i),_prefix_)==0) {
      string LineName = ObjectName(i);
      datetime t1=ObjectGet(LineName,OBJPROP_TIME1);
      datetime t2=ObjectGet(LineName,OBJPROP_TIME2);
      double p1=ObjectGet(LineName,OBJPROP_PRICE1);
      double p2=ObjectGet(LineName,OBJPROP_PRICE2);
      LinesCNT++;
      Operation = StringSubstr(ObjectName(i),StringLen(_prefix_),1);
      FileWrite(fh,Operation,TimeToStr(t1),DoubleToStr(p1,Digits),TimeToStr(t2),DoubleToStr(p2,Digits));
    }
  }
  FileClose(fh);
  MessageBox("Stored sections "+(LinesCNT)+" pcs.","Done", IDOK + MB_ICONINFORMATION);
}

文件定位与测试器目录陷阱

文件目录是极易出错点。普通脚本只能在 experts\files 下创建文件,但策略测试器运行时访问的是 tester\files 同名文件夹。因此每次用TL_Write生成或更新文件后,必须手动从 experts\files 复制到 tester\files,否则EA读不到而报错退出。

⚠ 常见坑
修改了线条并重写文件,却忘记复制到tester\files,会导致TL_Trader报'Error opening file'且零交易。每次改动都需重复复制。

测试EA TL_Trader工作机制

TL_Trader读取模式文件,在每次价格变动遍历所有线条段,核心三块:到达段首的开仓块、到达段尾的平仓块、跟踪止损处理块。订单注释写入起止时间标记(如t1=t2),避免同段重复开仓;若跟踪提前平仓,段结束仍按注释时间强制关闭,保证不多次开同段单。

参数ProcedTrailing可关跟踪,得到最悲观基线,与优化结果对比。因线条无先后,每tick全扫,用内部时间格式比对。代码展示开仓前查历史与现单防重,平仓按段尾时间执行。

// TL_Trader 片断 (MQL4)
extern string FileNameForRead = "TL_DATA.TXT";
extern double Lots = 0.1;
extern double StopLoss = 0;
extern double TrailingStop = 30;
extern bool   ProcedTrailing=true;
int start(){
  int fh=FileOpen(FileNameForRead,FILE_CSV|FILE_READ,';');
  if(fh<0) { MessageBox("Error opening file","ERROR",IDOK+MB_ICONERROR); return(1); }
  while(true){
    string Operation=FileReadString(fh);
    if(FileIsEnding(fh)) break;
    string st1=FileReadString(fh); string sp1=FileReadString(fh);
    string st2=FileReadString(fh); string sp2=FileReadString(fh);
    datetime t1=StrToTime(st1); double p1=StrToDouble(sp1);
    datetime t2=StrToTime(st2); double p2=StrToDouble(sp2);
    if(t1>t2) { /* swap */ }
    string MarkComent = t1+"="+t2;
    for(int i=0;i<OrdersTotal();i++){
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) continue;
      if(OrderComment()==MarkComent && TimeCurrent()>=t2) {
        if(OrderType()==OP_BUY) OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet);
        else OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet);
      }
    }
    bool OrderNotPresent=true;
    if(t1<=TimeCurrent() && TimeCurrent()<t2){
      for(int i=0;i<OrdersTotal();i++){
        if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) continue;
        if(OrderComment()==MarkComent) { OrderNotPresent=false; break; }
      }
      if(OrderNotPresent) {
        if(Operation=="B") { if(StopLoss<=0) SL=0; else SL=Ask-StopLoss*Point;
          ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,SL,0,MarkComent,1235,0,Blue); }
        else { if(StopLoss<=0) SL=0; else SL=Bid+StopLoss*Point; /* sell */ }
      }
    }
  }
  FileClose(fh);
}

实例结果与优化解读

作者用14行集合TL_DATA.txt测试。禁跟踪时结果糟糕;简单优化跟踪止损大小得出95点最佳,与时段柱统计98%分位吻合。启用优化后收益显著提升,且所有订单精确在段首建立。需注意:若让订单留至段尾可能获'无与伦比'收益,但那来自重复开仓而非跟踪机制。本工具目标是评跟踪有效性,故同开同清、不长持是关键。

✦ 读结果姿势
比较优化前后净利润差,才反映跟踪算法贡献;绝对收益高可能是模式重开单红利,非算法优。

扩展方向与社区协作

进一步可在同图测多跟踪算法:段首开多单各跟各算法,重叠比较数量与质量。亦可换进场战术线条比战术。作者作为程序员寻交易员合作实盘化。本文为'裸版'研究工具,后续拟发算法对比篇。原文链接 https://www.mql5.com/zh/articles/1527 提供ZIP下载TL_Trader.zip。

常见问题

用TL_Buy/TL_Sell拖出蓝线,TL_Write存为CSV,复制到tester/files,TL_Trader读文件重放订单,切换ProcedTrailing与TrailingStop优化对比。
因测试器读tester\files而非experts\files,每次TL_Write后须手动复制TL_DATA.TXT到MT4目录tester\files下。
将进出场固化为文件指令,EA只按段首开段尾平,注释防重开,使不同跟踪逻辑跑同一订单流,差异纯来自算法。
在TL_Trader参数设ProcedTrailing=false,此时无 trailing 处理,收益最低,用于和启用跟踪的优化结果对照。