利用Python进行季节性过滤并为EA的ONNX深度学习模型选择时间周期·综合运用
📘

利用Python进行季节性过滤并为EA的ONNX深度学习模型选择时间周期·综合运用

第 3/3 篇

NextDays取值换周期实测

把原 2 小时框架里的 NextDays 变量挪到 30 分钟周期上跑 EURUSD,分别试了 1 天、12 小时、6 小时三种跨度。微调后曲线表现更顺,于是继续往下压:4 小时、2 小时、1 小时各跑一轮。 观察下来,在 30 分钟主周期下,取约 8 到 12 根 30 分钟 K 线(即 4–6 小时窗口)作为 NextDay 跨度,结果明显更稳。这给短周期过滤提供了一个可复用的起点。 既然目标是多拿盈利单,下一步直接压到 5 分钟周期试水,NextDay 分别设 5 分钟、1 小时、30 分钟三档,看能否在更碎的节奏里依然跑赢。外汇与贵金属属高风险品种,上述现象仅代表历史样本倾向,实盘须自验。 下面这段是 NextDay 翻日重置与当日高低捕获的核心逻辑,注意它用 PERIOD_D1 对齐自然日,与你在短周期里设的 NextDay 跨度是两回事: if(TimeCurrent()>=ExtNextDay) { GetMinMax(); //--- set next day time ExtNextDay=TimeCurrent(); ExtNextDay-=ExtNextDay%PeriodSeconds(PERIOD_D1); ExtNextDay+=PeriodSeconds(PERIOD_D1); } void GetMinMax(void) { vectorf close; close.CopyRates(_Symbol,PERIOD_D1,COPY_RATES_CLOSE,0,SAMPLE_SIZE); ExtMin=close.Min(); ExtMax=close.Max(); } 逐行拆解:第 1 行判断当前时间是否越过预设的 ExtNextDay 边界;进入块后先调 GetMinMax 抓取已结束日的极值;注释标明此处是在重设“下一日”时间点;随后把 ExtNextDay 赋为当前时间,并用取模 PeriodSeconds(PERIOD_D1) 把时间截断到自然日零点,再加回一天秒数得到次日零点;GetMinMax 里用 vectorf 拷贝日线收盘价序列,取最小最大存入 ExtMin / ExtMax,供后续过滤条件使用。

MQL5 / C++
  if(TimeCurrent()>=ExtNextDay)
    {
    GetMinMax();
    class=class="str">"cmt">//--- set next day time
    ExtNextDay=TimeCurrent();
    ExtNextDay-=ExtNextDay%PeriodSeconds(PERIOD_D1);
    ExtNextDay+=PeriodSeconds(PERIOD_D1);
    }
class="type">void GetMinMax(class="type">void)
  {
  vectorf close;
  close.CopyRates(_Symbol,PERIOD_D1,COPY_RATES_CLOSE,class="num">0,SAMPLE_SIZE);
  ExtMin=close.Min();
  ExtMax=close.Max();
  }

◍ 给持仓加一道时间锁

更高周期的信号往往比小周期更稳,但想换别的EA也完全可行,MT5里可挂的变体很多。想在1小时图跑得更省心,可以给策略加时间或K线数限制:比如切到次日12小时周期后,规定订单成交后持有不超过12小时,避免隔夜漂移把浮盈吐回去。 下面这段逻辑就是干这事儿的。先判断预测类别不小于0,有仓就查平仓、无仓就查开仓,并记录开仓时刻;之后每 tick 算当前与开仓的时间差,一旦达到 days*PeriodSeconds(PERIOD_H12) 且差值非0,就市价平掉。外汇与贵金属杠杆高,这种硬止损能压住尾部风险,但触发后是否顺势仍看盘面,不保证盈亏。 代码逐行拆: if(ExtPredictedClass>=0) —— 模型输出类别≥0才进入主流程 if(PositionSelect(_Symbol)) CheckForClose(); —— 选中当前品种持仓就去检查平仓条件 else { CheckForOpen(); —— 没仓就检查开仓 started_time1 = rates[0].time; —— 记下单根K线时间作开仓基准 string started_time1_str = TimeToString(started_time1); —— 转字符串便于打印 int total = PositionsTotal(); —— 统计总持仓数 if(total>0) { —— 有仓才跑计时 datetime started_time2 = rates[0].time; —— 取当前时间 int segundos = started_time2 - started_time1; —— 算秒数差 Print(...) 三行 —— 把起止时间和差打印到日志 if((segundos >= days*PeriodSeconds(PERIOD_H12)) && segundos != 0) —— 差达到12小时整数倍且非0 ExtTrade.PositionClose(_Symbol,3); —— 以偏离3的市价平仓 把 days 设成1,就是「成交后最多拿12小时」;设成2就是24小时。开MT5把这段塞进EA的OnTick,跑EURUSD的H1回测,日志里能看到每笔持仓精确存活秒数。

MQL5 / C++
  if(ExtPredictedClass>=class="num">0)
    {
      if(PositionSelect(_Symbol))
        CheckForClose();
      else
       {
       CheckForOpen();
       started_time1 = rates[class="num">0].time;
       class="type">class="kw">string started_time1_str = TimeToString(started_time1);
class="type">int total = PositionsTotal();
  if(total>class="num">0)
   {
     class="type">class="kw">datetime started_time2 = rates[class="num">0].time;
     class="type">class="kw">string started_time2_str = TimeToString(started_time2);
     class="type">int segundos = started_time2 - started_time1;
     Print("Tiempo class="num">1: ", started_time1_str);
     Print("Tiempo class="num">2: ", started_time2_str);
     Print("Diferencia en segundos: ", segundos);
     if((segundos >= days*PeriodSeconds(PERIOD_H12)) && segundos != class="num">0)
      {
       Print("closing position----------------");
       ExtTrade.PositionClose(_Symbol,class="num">3);
      }
   }

「收束」

整套 EURUSD H1 的 LSTM+季节性实验跑下来,附带工程文件里最重的是 data_febs.zip(6574.19 KB)与 seasonal_feb_concat.zip(6548.13 KB),说明特征拼接主要吃历史季节性序列的存储和预处理。三个过滤版本 ONNX 模型体积都在 884 KB 上下,MT5 端对应的 mq5 预测脚本也仅 8 KB 左右,实盘加载负担很低。 外汇与贵金属属高风险品种,这类深度学习信号只代表历史样本中的概率倾向,换周期或滑点变大都可能让边缘优势消失。真要验证,先把 ONNX.eurusd.H1.120.Prediction_FILTERED_v2.mq5 拖进策略测试器,用 1_hour_files.zip 里的样本回放一遍,再决定要不要上模拟盘。

常见问题

正常。换周期后样本量和噪声结构都变了,建议同参数下跑至少3年数据对比命中率再决定用哪个周期。
在订单逻辑里加一个日期区间判断,命中季节性空窗就禁止开新仓,已有仓设硬止损不追加。
可以。小布能按品种历史季节性分布标出低胜率月份,并直接在品种页提示你收束交易时段。
要。不同周期量纲差很多,不标准化模型容易过拟合到数值尺度而非真实规律。
不一定。先看样本是否含极端行情年,再缩短过滤窗口重测,外汇贵金属高风险,需多次交叉验证。