连接 NeuroSolutions 神经网络·综合运用
📘

连接 NeuroSolutions 神经网络·综合运用

第 3/3 篇

把训练好的神经网络塞进EA里跑通

接好神经网络的EA,文件落位要先对齐:WeekPattern.dll 和 WeekPattern.nsw 丢进 MQL5\Files\NeuroSolutions\,通用适配层 NeuroSolutionsAdapter.dll 放 MQL5\Libraries\。路径错一处,EA加载就直接哑火。 验证连接是否成功,最实在的办法是开 MT5 策略测试器,用和训练神经网络完全相同的周期跑一遍。如果该周期正是模型所拟合的,盈利曲线倾向走出一段稳定向上——原文示例里那张图就是漂亮的单边上扬,说明 DLL 与权重文件握手正常。 但别被这条曲线骗了。某周期上训练出的净收益,只反映它对那段数据模式的识别力,不预示其他周期也能盈利;外汇与贵金属市场高风险,周期切换后主导形态可能完全变脸。 代码里 CNeuroSolutionsNeuralNet 类把神经网络相关逻辑全收进去了。构造函数拼出 terminal 路径下的 dll 与 nsw 绝对地址;Calc() 取最近6根K线(0号作基准 zlevel,1~5号拆成 open/high/low/close 共20个输入),再经 CalcNeuralNet 调 DLL 算输出。下面这段是类定义与输入填充的核心:

MQL5 / C++
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">class="kw">double</span>&nbsp;&nbsp;&nbsp;&nbsp;Lots = <span class="number">class="num">0.1</span>;
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">// Connect the DLL adapter, class="kw">using which we are going to use the DLL neuronet created in NeuroSolutions</span>
<span class="preprocessor">class="macro">#class="kw">import <span class="class="type">class="kw">string">"NeuroSolutionsAdapter.dll"</span></span>
<span class="keyword">class="type">int</span> CalcNeuralNet(<span class="keyword">class="type">class="kw">string</span> dllPath, <span class="keyword">class="type">class="kw">string</span> weightsPath, <span class="keyword">class="type">class="kw">double</span>&amp; inputs[], <span class="keyword">class="type">class="kw">double</span>&amp; outputs[]);
<span class="preprocessor">class="macro">#class="kw">import </span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class</span> CNeuroSolutionsNeuralNet
{
<span class="keyword">class="kw">private</span>:
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">string</span> dllPath;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Path to a DLL neuronet created in NeuroSolutions</span>
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">string</span> weightsPath; <span class="comment">class=class="str">"cmt">// Path to a file of the neuronet balances</span>
<span class="keyword">class="kw">public</span>:
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> in[<span class="number">class="num">20</span>]; <span class="comment">class=class="str">"cmt">// Neuronet inputs - OHLC of class="num">5 bars</span>
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> out[<span class="number">class="num">1</span>]; <span class="comment">class=class="str">"cmt">// Neuronet outputs - Close of a current bar</span>
&nbsp;&nbsp; CNeuroSolutionsNeuralNet();
&nbsp;&nbsp; <span class="keyword">class="type">bool</span> Calc();
};
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">void</span> CNeuroSolutionsNeuralNet::CNeuroSolutionsNeuralNet()
{
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">string</span> terminal = <span class="functions">TerminalInfoString</span>(<span class="keyword">TERMINAL_PATH</span>);
&nbsp;&nbsp; dllPath&nbsp;&nbsp;&nbsp;&nbsp; = terminal + <span class="class="type">class="kw">string">"\\MQL5\\Files\\NeuroSolutions\\WeekPattern.dll"</span>;
&nbsp;&nbsp; weightsPath = terminal + <span class="class="type">class="kw">string">"\\MQL5\\Files\\NeuroSolutions\\WeekPattern.nsw"</span>;
}
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">bool</span> CNeuroSolutionsNeuralNet::Calc()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Get current quotes for the neuronet</span>
&nbsp;&nbsp; <span class="predefines">class="type">MqlRates</span> rates[], rate;
&nbsp;&nbsp; <span class="functions">CopyRates</span>(<span class="functions">Symbol</span>(), <span class="functions">Period</span>(), <span class="number">class="num">0</span>, <span class="number">class="num">6</span>, rates);
&nbsp;&nbsp; <span class="functions">ArraySetAsSeries</span>(rates, true);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Fill the array of class="kw">input data of the neuronet</span>
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> zlevel=<span class="number">class="num">0</span>;&nbsp;&nbsp;
&nbsp;&nbsp; <span class="keyword">for</span> (<span class="keyword">class="type">int</span> bar=<span class="number">class="num">0</span>; bar&lt;=<span class="number">class="num">5</span>; bar++)
&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rate = rates[bar];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// class="num">0 bar is not taken for class="kw">input</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span> (bar==<span class="number">class="num">0</span>) zlevel=rate.open; <span class="comment">class=class="str">"cmt">// level of price calculation</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// class="num">1-class="num">5 bars are inputed</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="type">int</span> i=(bar-<span class="number">class="num">1</span>)*<span class="number">class="num">4</span>; <span class="comment">class=class="str">"cmt">// class="kw">input number</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; in[i&nbsp;&nbsp;] = rate.open -zlevel;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; in[i+<span class="number">class="num">1</span>] = rate.high -zlevel;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; in[i+<span class="number">class="num">2</span>] = rate.low&nbsp;&nbsp;-zlevel;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; in[i+<span class="number">class="num">3</span>] = rate.close-zlevel;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Calculate the neuronet in the NeuroSolutions DLL(though the DLL adapter)</span>
&nbsp;&nbsp; <span class="keyword">class="type">int</span> res = CalcNeuralNet(dllPath, weightsPath, in, out);
&nbsp;&nbsp; <span class="keyword">class="kw">switch</span> (res)
&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">case</span> <span class="number">class="num">1</span>: <span class="functions">Print</span>(<span class="class="type">class="kw">string">"Error of creating neuronet from DLL \""</span>, dllPath, <span class="class="type">class="kw">string">"\""</span>); <span class="keyword">class="kw">return</span> (class="kw">false);

◍ 把神经网络输出接进开平仓逻辑

上面那段神经网络封装返回 true 时,预测值已经躺在 out[0] 里,调用方不用再做任何加工。OnTick 里先跑 NN.Calc(),成功就把 out[0] 赋给 Prognoze,失败则清零——这个 0 既是错误标记也是「不交易」信号。 Trade() 的核心只有两条规则:持仓方向与预测相反就平,没仓且预测非零就按符号开。具体看,BUY 仓遇到 Prognoze<=0 平,SELL 仓遇到 Prognoze>=0 平;空仓时 Prognoze>0 买、<0 卖。 外汇和贵金属杠杆高,神经网络预测失准会连续触发反向平仓,实盘前务必在 MT5 策略测试器用历史数据跑一遍,确认 Calc() 的失败分支不会把 Prognoze 卡在旧值上。 Lots 变量在贴出的片段里未定义,编译前要在文件头补一个 double Lots=0.01; 之类的赋值,否则 Buy/Sell 调用会报未声明标识符。

MQL5 / C++
   case class="num">2: Print("Error of loading balances to neuronet from the file "", weightsPath, """); class="kw">return (class="kw">false);
   case class="num">3: Print("Error of calculation of neuronet");   class="kw">return (class="kw">false);
   }
   
  class=class="str">"cmt">// Output of the neuronet has appeared in the array out, you shouldn&class="macro">#x27;t do anything with it
  class="kw">return (true);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
CNeuroSolutionsNeuralNet NN;
class="type">class="kw">double Prognoze;
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Trade\Trade.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
  class=class="str">"cmt">// Get the price prediction from the neuronet
  if (NN.Calc()) Prognoze = NN.out[class="num">0];
  else            Prognoze = class="num">0;
  class=class="str">"cmt">// Perform necessary trade actions
  Trade();
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void Trade()
  {
  class=class="str">"cmt">// Close an open position if it is opposite to the prediction
  if(PositionSelect(_Symbol))
    {
      class="type">long type=PositionGetInteger(POSITION_TYPE);
      class="type">bool close=class="kw">false;
      if((type == POSITION_TYPE_BUY)   && (Prognoze <= class="num">0)) close = true;
      if((type == POSITION_TYPE_SELL) && (Prognoze >= class="num">0)) close = true;
      if(close)
        {
         CTrade trade;
         trade.PositionClose(_Symbol);
        }
    }
  class=class="str">"cmt">// If there is no positions, open one according to the prediction
  if((Prognoze!=class="num">0) && (!PositionSelect(_Symbol)))
    {
      CTrade trade;
      if(Prognoze > class="num">0) trade.Buy(Lots);
      if(Prognoze < class="num">0) trade.Sell(Lots);
    }
  }

「画得少,看得清」

把神经网络接进 MT5 做自动分析,本质仍是对历史样本拟合:weekpattern.mq5 仅 3.78 KB,weekpattern-export.mq5 2.04 KB,配套 dll_nsw.zip 383.37 KB,文件体积本身就说明轻量,但轻量不等于对未来有决定作用。 评论区里有交易者直言,参数优化只是历史盈利最大化的变形,靠这套直接盈利大概率会落空;也有用户卡在 VC6 生成 DLL 的环境上,证明落地门槛不在算法而在工具链。 真要验证,下载那两个 mq5 在策略测试器跑 EURUSD 周线,看样本外曲线是否塌方,比读任何结论都实在。外汇与贵金属波动剧烈,高杠杆下这类实验只该用模拟盘。

常见问题

把模型导出的权重和拓扑结构写成EA里的计算函数,用历史样本归一化后逐根K线调用前向传播,输出值接进交易判断。
设一个阈值区间,输出大于上轨只做多、小于下轨只做空,中间观望;同时用波动率过滤掉极端行情的误触。
小布可加载你的模型输出做实时信号标注,自动把多空概率画在图上,你只看关键区就行。
市场分布漂移导致输入特征失效,建议每季度用近期数据重训,并只保留鲁棒性高的少数特征。
只留一条净方向线和阈值带,其余中间量收进缓冲区,做到画得少、看得清。