基于LSTM的趋势预测在趋势跟踪策略中的应用(基础篇)
📘

基于LSTM的趋势预测在趋势跟踪策略中的应用(基础篇)

第 1/3 篇

◍ 用LSTM给MT5趋势跟踪加一层预判

把LSTM塞进趋势跟踪系统,核心不是让它替你下单,而是先对价格序列做一步或多步前向推断,再用推断结果去过滤顺势单的信号。MT5本身不直接跑神经网络,常见做法是离线用Python训好模型,导出权重,通过DLL或文件管道在EA里调用推理。 原文给出的实测样本在2025年10月15日发布,单篇浏览1042、收藏2,说明这类结合AIGC的思路在散户圈还属早期尝鲜阶段,验证成本不低。外汇与贵金属杠杆高,模型误判会放大亏损,任何LSTM输出都只能当概率参考。 落地时建议先拿EURUSD的H1历史跑通数据预处理:把收盘价做min-max缩放喂入滑动窗口,看网络倾向给出的下一根方向是否和均线系统一致,不一致就跳过开仓。

「为什么拿LSTM啃MT5的时序」

LSTM 是 RNN 的一个变体,核心改动是引入了门控单元,让网络在反向传播时梯度不至于很快衰减到零,从而能记住几百根 K 线之前的形态。传统 RNN 在序列一长就容易忘事,外汇和贵金属的日线、小时线又恰好是强时序、高噪声的数据,这正好是 LSTM 想解决的典型场景。 本文这条链路会走通五块:先讲 LSTM 的概念和为什么适合趋势任务,再从 MT5 拽数据到 Python,训练完把模型塞回 MQL5 跑信号,最后用统计回测看胜率和回撤。外汇和贵金属杠杆高、跳空频繁,任何模型都只是概率优势,不是确定性答案。 你现在就可以开 MT5 的「文件→打开数据文件夹」,确认 MT5 的终端目录结构,后面从 Python 读 MQL5/Files 或直连交易终端拿 CSV 都从这儿起步。

为什么拿LSTM去筛趋势信号

趋势跟踪在单边市能赚钱,到了震荡市却常高买低卖。学术回测显示,黄金交叉这类经典法子放在长周期、跨市场、多周期里确实有效,只不过绝对收益一般,胜在盈利稳定。它的利润主要来自极端行情,单笔盈利远超平均亏损,靠「收紧止损+让利润奔跑」做出低胜率、高盈亏比。 LSTM 是 RNN 的升级版,用记忆单元长期存信息,绕开了梯度消失,抓时序里的长期依赖很拿手。做回归时它能把输入特征和连续输出之间的时序关系建模得很准,天然适合金融序列预测。 本文的切入点是一个假设:趋势明显的市场里,趋势跟踪表现优于无趋势市场。所以我们用 LSTM 去预测未来趋势方向,把趋势强度不够时的劣质信号过滤掉。 衡量趋势强度选了 ADX,这是市场趋势性主流指标。和常规用法不同,我们不去看当前 ADX,而是预测它的未来值——因为高 ADX 常常意味着趋势快走完了,那时候再进场多半错过最好的窗口。外汇与贵金属杠杆高、波动剧烈,这类信号过滤只降低概率层面的劣质交易,不消除风险。

◍ 用EA把RSI和ADX落盘成训练素材

做回归预测前先定特征:RSI反映短线相对强弱,ADX本身作为目标相关量,再加最近K线收益率做平稳化表征。特征组合你可以自己换,但必须保证平稳性,否则后续模型训练会引入伪回归。 本例取2020-01-01至2024-01-01的小时级数据训练,2024-01-01至2025-01-01留作样本外测试,时间切分直接决定过拟合能否被暴露。 EA通过iRSI()和iADX()抓当前值,仅在K线数变化时写盘,表头为time、ADX、RSI、Stationary。策略测试器单次跑完,文件落在/Tester/Agent-sth000/MQL5/Files,由fileName参数指定名字。 标签建议用未来10根K线的ADX均值。这样入场时趋势还没完全走完,又不至于眺望太远导致信号失效,能抓到后续方向性行情的概率更高。 进Python后按分号切列、删空行、time设索引,再建output列算未来10个ADX均值,最后清掉因未来值不足而留下的残余空行。外汇与贵金属杠杆高,样本外表现不代表实盘稳健,验证前先用小本金或模拟盘跑。

MQL5 / C++
class="macro">#include <FileCSV.mqh>
CFileCSV csvFile;
class="type">int barsTotal = class="num">0;
class="type">int handleRsi;
class="type">int handleAdx;
class="type">class="kw">string headers[] = {
   "time",
   "ADX",
   "RSI",
   "Stationary"
};
class="type">class="kw">string data[class="num">1000000][class="num">4];
class="type">int indexx = class="num">0;
vector xx;
input class="type">class="kw">string fileName = "XAU-1h-class="num">2020-class="num">2024.csv";
input class="type">bool SaveData = true;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {class=class="str">"cmt">//Initialize model
   handleRsi = iRSI(_Symbol,PERIOD_CURRENT,class="num">14,PRICE_CLOSE);
   handleAdx = iADX(_Symbol,PERIOD_CURRENT,class="num">14);
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert deinitialization function                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(const class="type">int reason)
  {
   if (!SaveData) class="kw">return;
   if(csvFile.Open(fileName, FILE_WRITE|FILE_ANSI))
     {
      class=class="str">"cmt">//Write the header
      csvFile.WriteHeader(headers);
      class=class="str">"cmt">//Write data rows
      csvFile.WriteLine(data);
      class=class="str">"cmt">//Close the file
      csvFile.Close();
     }
   else
     {
      Print("File opening error!");
     }
  }

class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
   class="type">int bars = iBars(_Symbol,PERIOD_CURRENT);

   if (barsTotal!= bars){
      barsTotal = bars;
      class="type">class="kw">double rsi[];
      class="type">class="kw">double adx[];
      CopyBuffer(handleAdx,class="num">0,class="num">1,class="num">1,adx);
      CopyBuffer(handleRsi,class="num">0,class="num">1,class="num">1,rsi);

「把MT5导出的指标快照喂给pandas」

上面这段是 MQL5 侧把每根K线的关键字段写进二维数组的收尾逻辑,随后用 Python 端把 CSV 读进来做监督学习的标签构造。 data[indexx][0] 存的是账户交易服务器时间,避免跨时区对齐出错;1、2 位分别是 ADX 和 RSI 的当前值,保留两位小数;3 位是上一根K线的实体涨跌幅占收盘价比例,精度到千分位,用来刻画波动强度。 Python 这边读的是名为 XAU-1h-2020-2024.csv 的黄金1小时数据,分隔符是分号。把 output 列定义为 10 根K线后的 ADX 值再取10期均值,等于用「未来两周均值ADX」当回归目标;删掉最后10行防泄漏,并从第10行起截,保证滚动窗口有数。 开 MT5 把这段导出逻辑接在你自己的指标计算后,跑完直接丢进 pandas 这套流程,就能拿到一份带滞后标签的训练集。外汇与贵金属杠杆高,信号仅作概率参考,实盘前务必样本外验证。

MQL5 / C++
  data[indexx][class="num">0] = (class="type">class="kw">string)TimeTradeServer();
  data[indexx][class="num">1] = DoubleToString(adx[class="num">0], class="num">2);
  data[indexx][class="num">2] = DoubleToString(rsi[class="num">0], class="num">2);
  data[indexx][class="num">3] = DoubleToString((iClose(_Symbol,PERIOD_CURRENT,class="num">1)-iOpen(_Symbol,PERIOD_CURRENT,class="num">1))/iClose(_Symbol,PERIOD_CURRENT,class="num">1),class="num">3);
  indexx++;
  }
}
class="kw">import pandas as pd
data = pd.read_csv(&class="macro">#x27;XAU-1h-class="num">2020-class="num">2024.csv&class="macro">#x27;, sep=&class="macro">#x27;;&class="macro">#x27;)
data= data.dropna().set_index(&class="macro">#x27;time&class="macro">#x27;)
data[&class="macro">#x27;output&class="macro">#x27;] = data[&class="macro">#x27;ADX&class="macro">#x27;].shift(-class="num">10)
data = data[:-class="num">10]
data[&class="macro">#x27;output&class="macro">#x27;]= data[&class="macro">#x27;output&class="macro">#x27;].rolling(window=class="num">10).mean()
data = data[class="num">9:]

常见问题

可以拿LSTM对历史价格时序做二分类预判,输出趋势持续概率,用来过滤假突破信号,但只能提高胜率倾向,不保证每次都对。
把每个周期的RSI、ADX数值按时间对齐导成CSV,用pandas做滑窗拼接和归一化,就能喂进模型做序列训练。
小布内置了AIGC时序分析,打开对应品种页就能直接看趋势概率诊断,把模型训练和信号筛选的重复劳动交给他。
均线在震荡市噪音大,LSTM能学长周期依赖关系,对趋势延续性判断更细腻,适合作为趋势跟踪的过滤层。
没有统一阈值,建议在回测里看不同分位下的盈亏比,实盘外汇贵金属高风险,0.7以上也仅作参考倾向。