神经网络在交易中的实际应用 (第二部分). 计算机视觉·综合运用
「从 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 时区对齐。
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 就能验证信号频率。
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 用自有数据复跑验证。
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 拖进图表就能跑通基础流程,外汇和贵金属波动剧烈,任何信号都只是概率倾向,实盘前先用历史数据验一遍分类边界。