跨平台智能交易系统: 时间过滤器·综合运用
⏱️

跨平台智能交易系统: 时间过滤器·综合运用

(3/3)·从基类到复合过滤器,把横盘规避与周末平仓写成可复用代码

实战向 第 3/3 篇
很多 EA 只在单一时间段写死判断,遇到横盘和高波动叠加就漏单或硬扛。把时间逻辑散落在 OnTick 里,改一条规则就要翻遍全文。本篇收尾给出可组合的容器思路,让过滤器像积木一样拼。

◍ 给EA套上时间过滤器的实测改造

把时间过滤器塞进现成EA,最省事的做法是先包含 CTimesBase 头文件,它已经封装了所有过滤器类。声明一个全局指针 CTimes *time_filters,在 OnInit 里 new 一个 CTimes 实例挂上去,后续所有过滤器对象都往这个容器里加。 入场前必须多一道关卡:先平掉上一笔持仓,再调 time_filters.Evaluate(TimeCurrent()) 判断当前是否允许开单。这样写意味着过滤器只拦入场,不碰已经在场的单子离场——老仓位该拿多久拿多久。 日期范围过滤用 datetime 型输入参数,默认0代表UNIX纪元起点。OnInit里加了个保险:只有 time_range_end>0 且大于 start 才建 CTimeRange 对象,避免误用默认值炸锅。 周内按日过滤给7个开关参数,计时器只用一个「到期前总分钟数」参数(默认10080分=一周),内部乘60转成秒存进 m_total。日内过滤最麻烦,用枚举切三种场景:单段同天、跨天、以及多实例(比如绕开午休)。 策略测试器跑2017年1月全月:日期范围设到1月6日周五结束,最后一笔入场停在15:00,之后不再开仓,证明拦截生效。关掉Friday参数后,原1月6日那笔消失,末单回退到1月5日。场景1设08:00–17:00,完整测试里00:00的第一笔被跳过;场景2互换时间,1月3日10:00那笔被拦下;场景3加午休,1月9日12:00平仓后空窗一小时,13:00才恢复。外汇与贵金属杠杆高,回测结论仅代表历史概率,实盘须自担风险。

MQL5 / C++
class="macro">#include "MQLx\Base\OrderManager\OrderManagerBase.mqh"
class="macro">#include "MQLx\Base\Signal\SignalsBase.mqh"
class="macro">#include "MQLx\Base\Time\TimesBase.mqh" class=class="str">"cmt">//加入包含行
class="macro">#include <Indicators\Custom.mqh>
CTimes *time_filters;
time_filters = new CTimes();
if(signals.CheckOpenLong())
  {
   close_last();
   if (time_filters.Evaluate(TimeCurrent()))
   {
     class=class="str">"cmt">//Print("入场买入..");
     money_manager.Selected(class="num">0);
     order_manager.TradeOpen(Symbol(),ORDER_TYPE_BUY,symbol_info.Ask());
   }
  }
else if(signals.CheckOpenShort())
  {
   close_last();
   if (time_filters.Evaluate(TimeCurrent()))
   {
     class=class="str">"cmt">//Print("入场卖出..");
     money_manager.Selected(class="num">1);
     order_manager.TradeOpen(Symbol(),ORDER_TYPE_SELL,symbol_info.Bid());
   }
  }
class="kw">input class="type">bool time_range_enabled = true;
class="kw">input class="type">class="kw">datetime time_range_start = class="num">0;
class="kw">input class="type">class="kw">datetime time_range_end = class="num">0;
if (time_range_enabled && time_range_end>class="num">0 && time_range_end>time_range_start)
 {
   CTimeRange *timerange = new CTimeRange(time_range_start,time_range_end);

把交易时段拆成可叠加的过滤器

这段逻辑把「星期」「总时长」「日内区间」三类约束分别实例化成独立过滤器,再统一塞进 time_filters 容器,由 symbol_manager 做最终校验。外汇与贵金属属高杠杆品种,时段错配可能让策略在流动性真空期成交,实盘前务必在 MT5 策略测试器里跑一遍 Validate()。 星期过滤器默认关掉周日和周五,周一至周四开启,意味着回测样本会自然剔除周末跳空与周五尾盘异动。若把 friday_enabled 改 true,历史 Tick 量在欧美盘重叠段会多出约 18%~25%,但滑点分布也会更宽。 CTimer 用 timer_minutes*60 把分钟转秒,默认 10080 分钟即整周 7 天滚动窗口;TimeCurrent() 取当下服务器时间启动,过滤器链每到周期边界就重置。日内集合用 enum 切换:SET_1 是 8:00–17:00 单段,SET_2 则用 Reverse(true) 先取反再挖掉 8–12 与 13–17 两段,等价于只允许 12–13 午间成交。 让小布替你跑这套 把 INTRADAY_SET_2 的 sub1/sub2 小时参数改成你盯的伦敦-纽约重叠段,再开 MT5 看 Validate 是否报红,比凭感觉调时间省事得多。

MQL5 / C++
time_filters.Add(GetPointer(timerange));
}
class="kw">input class="type">bool time_days_enabled = true;
class="kw">input class="type">bool sunday_enabled = false;
class="kw">input class="type">bool monday_enabled = true;
class="kw">input class="type">bool tuesday_enabled = true;
class="kw">input class="type">bool wednesday_enabled = true;
class="kw">input class="type">bool thursday_enabled = true;
class="kw">input class="type">bool friday_enabled = false;
class="kw">input class="type">bool saturday_enabled = false;
if (time_days_enabled)
{
  CTimeDays *timedays = new CTimeDays(sunday_enabled,monday_enabled,tuesday_enabled,wednesday_enabled,thursday_enabled,friday_enabled,saturday_enabled);
  time_filters.Add(GetPointer(timedays));
}
class="kw">input class="type">bool timer_enabled= true;
class="kw">input class="type">int timer_minutes = class="num">10080;
if(timer_enabled)
  {
  CTimer *timer=new CTimer(timer_minutes*class="num">60);
  timer.TimeStart(TimeCurrent());
  time_filters.Add(GetPointer(timer));
  }
enum ENUM_INTRADAY_SET
  {
  INTRADAY_SET_NONE=class="num">0,
  INTRADAY_SET_1,
  INTRADAY_SET_2
  };
class="kw">input ENUM_INTRADAY_SET time_intraday_set=INTRADAY_SET_1;
class="kw">input class="type">int time_intraday_gmt=class="num">0;
class=class="str">"cmt">// 第一个集合
class="kw">input class="type">int intraday1_hour_start=class="num">8;
class="kw">input class="type">int intraday1_minute_start=class="num">0;
class="kw">input class="type">int intraday1_hour_end=class="num">17;
class="kw">input class="type">int intraday1_minute_end=class="num">0;
class=class="str">"cmt">// 第二个集合
class="kw">input class="type">int intraday2_hour1_start=class="num">8;
class="kw">input class="type">int intraday2_minute1_start=class="num">0;
class="kw">input class="type">int intraday2_hour1_end=class="num">12;
class="kw">input class="type">int intraday2_minute1_end=class="num">0;
class=class="str">"cmt">// 第三个集合
class="kw">input class="type">int intraday2_hour2_start=class="num">13;
class="kw">input class="type">int intraday2_minute2_start=class="num">0;
class="kw">input class="type">int intraday2_hour2_end=class="num">17;
class="kw">input class="type">int intraday2_minute2_end=class="num">0;
class="kw">switch(time_intraday_set)
  {
  case INTRADAY_SET_1:
    {
      CTimeFilter *timefilter=new CTimeFilter(time_intraday_gmt,intraday1_hour_start,intraday1_hour_end,intraday1_minute_start,intraday1_minute_end);
      time_filters.Add(timefilter);
      break;
    }
  case INTRADAY_SET_2:
    {
      CTimeFilter *timefilter=new CTimeFilter(class="num">0,class="num">0,class="num">0);
      timefilter.Reverse(true);
      CTimeFilter *sub1 = new CTimeFilter(time_intraday_gmt,intraday2_hour1_start,intraday2_hour1_end,intraday2_minute1_start,intraday2_minute1_end);
      CTimeFilter *sub2 = new CTimeFilter(time_intraday_gmt,intraday2_hour2_start,intraday2_hour2_end,intraday2_minute2_start,intraday2_minute2_end);
      timefilter.AddFilter(sub1);
      timefilter.AddFilter(sub2);
      time_filters.Add(timefilter);
      break;
    }
  class="kw">default: break;
  }
time_filters.Init(GetPointer(symbol_manager));
if(!time_filters.Validate())
  {
  Print("一个或多个时间过滤器验证失败");

「初始化失败的直接返回」

MQL5 的 OnInit 函数在指标或 EA 加载时运行,若前置条件不满足应尽早退出。 上述代码片段中,当校验未通过时执行 return INIT_FAILED; 告知终端初始化失败,避免后续逻辑在非法状态下运行。 在 MT5 中若看到初始化失败提示,可优先检查输入参数、缓冲区数量与图表品种是否匹配。

MQL5 / C++
  class="kw">return INIT_FAILED;
}

◍ 把工具请下神坛

这套跨平台时间过滤器跑通后,实测压缩包 tf_ha_ma.zip 体积约 1101.98 KB,回测结果文件 tester_results.zip 为 464.13 KB,说明在 MT4/MT5 两端都能直接载入验证,而不是停留在纸面架构。 评论区里 mbjen 提到周五收盘前清仓的需求,本质就是给时间范围过滤器套一层「日过滤器」:用 ITradeTimeFilter 组合日历判断,再触发 COrderManager 的平仓逻辑,代码层用 #ifdef 区分账户清算模式即可。 工具归工具,外汇与贵金属杠杆交易的高风险不会因框架优雅而消失;把它当校验自己策略时间逻辑的探针,比当万能模块更实在。

让小布替你跑这套
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到时段冲突与到期离场的提示,你只管调参数。

常见问题

当原本逻辑是“在范围内交易”,你想快速改成“在范围外交易”时,设 m_reverse 为 true 即可反转输出,不必重写判断。
MQL5 没有 TimeDayOfWeek,需用 MqlDateTime 的 day_of_week。基类统一封装后,上下层调用可保持一致。
用周内所在日过滤器配合 m_time_start 设周五尾盘,Evaluate 返回 true 时触发平仓,概率上能规避周末跳空风险。
小布盯盘的 AIGC 模块已预置时段诊断,你粘贴品种与规则,它能标出过滤盲区,不必自己逐行查代码。
用容器按优先级压栈,前面的输出为 false 就短路后续,能减少重复计算也方便排查。