神经网络在交易中的实际应用 (第二部分). 计算机视觉·综合运用
📘

神经网络在交易中的实际应用 (第二部分). 计算机视觉·综合运用

第 3/3 篇

「从 CSV 灌入指标并初始化交易环境」

这段初始化逻辑干了两件事:先把外部导出的 Indicator.csv 读进内存,再配置 CTrade 的下单容差与异步模式。外汇与贵金属波动剧烈,用共享文件跨进程传指标值是常见的轻量做法,但 csv 一旦缺失或格式错列,数组越界会直接让 EA 在 OnInit 阶段返回失败。 第一次开文件只为了数行数:用 FileIsEnding 配合 IsStopped 做循环终止条件,每读一行 bars++,随后 ArrayResize 把 Buf_0、Buf_1 拉到对应长度。这里 bars 就是历史样本量,回测前打开文件看一眼行数就能知道缓冲区够不够。 第二次重开同一文件才真正填数据:k 从 0 自增,Buf_0[k] 存原始值,当 k>=Period-1 时做窗口求和再除以 Period,相当于手搓一个 Period 周期移动平均写进 Buf_1。deviation 写死 10 点、填充方式 RETURN、异步开 true——这三行决定了后续下单的滑点容忍与执行节奏,实盘前建议按品种点差重设 deviation。 OnDeinit 留空,OnTick 开头先取本地时间结构、品种小数位和ask价,为后面比价下单做准备。注意 TimeCurrent 返回的是服务器时间,做时段过滤时要和 broker 时区对齐。

MQL5 / C++
class="type">bool send1;
class="type">bool send0;
class="type">int h=class="num">4;
class="type">int k;
class="type">int K;
class="type">int bars;
class="type">int Handle;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//---
   Handle=FileOpen("Indicator.csv",FILE_CSV|FILE_SHARE_READ|FILE_ANSI|FILE_COMMON,";");
   class="kw">while(!FileIsEnding(Handle)&& !IsStopped())
     {
       StringToTime(FileReadString(Handle));
       bars++;
     }
   FileClose(Handle);
   ArrayResize(Buf_0,bars);
   ArrayResize(Buf_1,bars);
   Handle=FileOpen("Indicator.csv",FILE_CSV|FILE_SHARE_READ|FILE_ANSI|FILE_COMMON,";");
   class="kw">while(!FileIsEnding(Handle)&& !IsStopped())
     {
       Count=StringToTime(FileReadString(Handle));
       Buf_0[k]=StringToDouble(FileReadString(Handle));
       h=Period-class="num">1;
       if(k>=h)
         {
          class="kw">while(h>=class="num">0)
            {
             Buf_1[k]=Buf_1[k]+Buf_0[k-h];
             h--;
            }
          Buf_1[k]=Buf_1[k]/Period;
         }
       k++;
     }
   FileClose(Handle);
   class="type">int deviation=class="num">10;
   trade.SetDeviationInPoints(deviation);
   trade.SetTypeFilling(ORDER_FILLING_RETURN);
   trade.SetAsyncMode(true);
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert deinitialization function                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
  {
class=class="str">"cmt">//---
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
class=class="str">"cmt">//---
   class="type">MqlDateTime stm;
   TimeToStruct(TimeCurrent(),stm);
   class="type">int    digits=(class="type">int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
   class="type">class="kw">double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
   class="type">class="kw">double PriceAsk=SymbolInfoDouble(_Symbol,SYMBOL_ASK);

◍ 用H1形态与时段过滤挂双向单

这段逻辑先把买卖两边的止损止盈算清楚:以当前 Bid/Ask 加减点数再 NormalizeDouble 到品种精度,若对应盈亏点数为 0 则把 SL/TP 置 0,意味着不下保护性价位。外汇与贵金属杠杆高,SL=0 在极端跳空时可能让浮亏无限扩大,实盘前务必确认这是你想要的。 开仓信号绑死在两根缓冲线 Buf_0 与 Buf_1 的死叉/金叉上:Buy1 要求前一根 Buf_0>Buf_1 且当前反转为 Buf_0<Buf_1,同时 H1 周期的前一根低点低于前两根低点(iLow(NULL,PERIOD_H1,1)<iLow(NULL,PERIOD_H1,2)),并且系统小时落在 H1~H2 之间。Sell0 则镜像处理,用高点突破和 H3~H4 时段过滤。 平仓不靠 TP/SL 被动触发,而是等反向交叉出现就市价平掉:Buy1 在 Buf_0 重新上穿 Buf_1 且 H1 高点抬高时 close,Sell0 在 Buf_0 下穿且 L 低点走低时 close。每轮 K++ 推进一根柱,相当于逐根扫描。把 H1/H2/H3/H4 设成你盯的流动性窗口,开 MT5 把这段直接塞进 EA 的 OnTick 就能验证信号频率。

MQL5 / C++
  class="type">class="kw">double PriceBid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
  class="type">class="kw">double SL1=NormalizeDouble(PriceBid-LossBuy*point,digits);
  class="type">class="kw">double TP1=NormalizeDouble(PriceAsk+ProfitBuy*point,digits);
  class="type">class="kw">double SL0=NormalizeDouble(PriceAsk+LossSell*point,digits);
  class="type">class="kw">double TP0=NormalizeDouble(PriceBid-ProfitSell*point,digits);
  if(LossBuy==class="num">0)
      SL1=class="num">0;
  if(ProfitBuy==class="num">0)
      TP1=class="num">0;
  if(LossSell==class="num">0)
      SL0=class="num">0;
  if(ProfitSell==class="num">0)
      TP0=class="num">0;
class=class="str">"cmt">//---------Buy1
  if(send1==class="kw">false && K>class="num">0 && Buf_0[K-class="num">1]>Buf_1[K-class="num">1] && Buf_0[K]<Buf_1[K] && iLow(NULL,PERIOD_H1,class="num">1)<iLow(NULL,PERIOD_H1,class="num">2) && stm.hour>H1 && stm.hour<H2 && H1<H2)
    {
      send1=trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,class="num">1,PriceAsk,SL1,TP1);
      TicketBuy1 = trade.ResultDeal();
    }
  if(send1==true && K>class="num">0 && Buf_0[K-class="num">1]<Buf_1[K-class="num">1] && Buf_0[K]>Buf_1[K] && iHigh(NULL,PERIOD_H1,class="num">1)>iHigh(NULL,PERIOD_H1,class="num">2))
    {
      trade.PositionClose(TicketBuy1);
      send1=class="kw">false;
    }
class=class="str">"cmt">//---------Sell0
  if(send0==class="kw">false && K>class="num">0 && Buf_0[K-class="num">1]<Buf_1[K-class="num">1] && Buf_0[K]>Buf_1[K] && iHigh(NULL,PERIOD_H1,class="num">1)>iHigh(NULL,PERIOD_H1,class="num">2) && stm.hour>H3 && stm.hour<H4 && H3<H4)
    {
      send0=trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,class="num">1,PriceBid,SL0,TP0);
      TicketSell0 = trade.ResultDeal();
    }
  if(send0==true && K>class="num">0 && Buf_0[K-class="num">1]>Buf_1[K-class="num">1] && Buf_0[K]<Buf_1[K] && iLow(NULL,PERIOD_H1,class="num">1)<iLow(NULL,PERIOD_H1,class="num">2))
    {
      trade.PositionClose(TicketSell0);
      send0=class="kw">false;
    }
  K++;
}

拆开CNN特征图看它到底学了什么

很多人把卷积网络当黑匣子,其实中间层的特征图能直接打印出来看。在 CNN 文件夹里跑 Visual.ipynb,载入已训练的 cnn_Open11.hdf5,就能观察网络在分类前到底对图像里的哪些形态敏感。 具体做法:把输入图(如 Train/Buy/Buy81.png,尺寸 449×449)转成数组并除以 255 归一化,然后新建一个裁剪模型,只取到 layers[0]、[3]、[6] 的输出来分别看第 0、3、6 卷积层。打印第 0 层第 18 号特征图时,网络明显高亮了看涨烛形;但下跌烛形和抛物线点因颜色相同,在这一层难以区分。 再看第 3 层第 2 号特征图,网络把全部烛形按不同影线高亮,画面有立体感,可下跌影线和抛物线仍是同色。第 3 层第 5 号图里抛物线点重叠,CNN 把它当横盘标志——说明训练集里这类样本被归到了别类,需要单独加一个「横盘」类别重训。 这种可视检查不是炫技。它让训练任务的边界更清楚:哪类形态混色、哪类该拆类、哪层噪声大,都能直接定位。外汇与贵金属行情高风险,模型结论只代表历史样本下的倾向,实盘前务必在 MT5 用自有数据复跑验证。

MQL5 / C++
from tensorflow.python.keras.preprocessing.image class="kw">import ImageDataGenerator
from tensorflow.keras.models class="kw">import Model, load_model
class="kw">import pandas as as pd
from tensorflow.python.keras.preprocessing class="kw">import image 
class="kw">import matplotlib.pyplot as plt
class="kw">import numpy as np
model=load_model(&class="macro">#x27;cnn_Open11.hdf5&class="macro">#x27;)
model.summary()
img_path=&class="macro">#x27;Train/Buy/Buy81.png&class="macro">#x27;
img=image.load_img(img_path,target_size=(class="num">449,class="num">449))
plt.figure(figsize=(class="num">8, class="num">8))
plt.imshow(img)
plt.show
x=image.img_to_array(img)
x=np.expand_dims(x,axis=class="num">0)
x/=class="num">255
model=Model(inputs=model.class="kw">input, outputs=model.layers[class="num">0].output)
class="macro">#model=Model(inputs=model.class="kw">input, outputs=model.layers[class="num">3].output)
class="macro">#model=Model(inputs=model.class="kw">input, outputs=model.layers[class="num">6].output)
model.summary()
model=model.predict(x)
print(model.shape)
im=model[class="num">0,:,:,class="num">18]
plt.figure(figsize=(class="num">10, class="num">10))
plt.imshow(im)
plt.show()
rows=class="num">12
filters=model.shape[-class="num">1]
size=model.shape[class="num">1]
cols=filtersclass=class="str">"cmt">//rows
display_grid=np.zeros((cols*size,rows*size))
for col in range(cols):
    for row in range(rows):
        channel_image=model[class="num">0,:,:,col*rows+row]
        channel_image-=channel_image.mean()
        channel_image/=channel_image.std()
        channel_image*=class="num">64
        channel_image+=class="num">128
        channel_image=np.clip(channel_image,class="num">0,class="num">255).astype(&class="macro">#x27;uint8&class="macro">#x27;)
        display_grid[col*size:(col+class="num">1)*size,row*size:(row+class="num">1)*size]=channel_image
scale=class="num">1./size
plt.figure(figsize=(scale*display_grid.shape[class="num">1],scale*display_grid.shape[class="num">1]))
plt.grid(False)
plt.imshow(display_grid,aspect=&class="macro">#x27;auto&class="macro">#x27;,cmap=&class="macro">#x27;viridis&class="macro">#x27;)

「画得少,看得清」

把卷积思路搬进技术分析,好处不在炫技,而在把训练数据准备这一步大幅简化:用图表截图代替繁琐的数值预处理,内部可视化还能直接看出哪些输入对训练质量拉动最大。 优化环节别贪复杂,简单策略也要和网路任务对齐——训练数组按类别切分,做机器人时把这套条件原样接进去。 附带的 NWI.mq5、CNNet.mq5、TestCNN.mq5 几个文件加起来不到 25 KB,开 MT5 把 CNN.tpl 拖进图表就能跑通基础流程,外汇和贵金属波动剧烈,任何信号都只是概率倾向,实盘前先用历史数据验一遍分类边界。

常见问题

用脚本读取 CSV 逐行映射到对应 K 线指标字段,再初始化订单环境与品种参数,即可在本地复现历史形态。
只在流动性较高的固定时段(如欧美重叠)允许挂单,其余时间禁用双向单,能明显降低无效成交概率。
可以,小布能直接解析特征图并标出激活区域,告诉你模型大概率在盯哪类影线或吞没结构,省去手动拆图。
拆开能看到每层响应的是边缘还是局部形态,帮你判断模型是否过拟合噪声;用热力图标注后肉眼可辨。
只保留过滤后的触发线和一个形态标记,其余折叠;视觉负担降下来后,错单倾向也会少一些。