神经网络实验(第 2 部分):智能神经网络优化·进阶篇
「区间随机值的字符串补位处理」
在 MT5 脚本里批量生成模拟报价时,常用 RNDfromCI(Min, Max) 取置信区间随机值,再用 DoubleToString(...,1) 统一留 1 位小数。上面这段连续给 sx2~sx11、ss0~ss2 赋值,就是一次性铺了 13 个带 1 位小数格式的字符串变量。 但 DoubleToString 在数值恰好为整数时可能不显小数点,比如返回 "7" 而非 "7.0"。下面的 if 判断用 StringFind(变量,".",0)==-1 检测缺小数点,缺了就手动拼 ".0"。sw0~sw15、sb0~sb3、sx0 都走了这套补位,保证后续拼接的报表或面板每一格宽度对齐。 外汇与贵金属模拟数据高风险,这套补位只解决显示一致性,不改变随机分布本身。开 MT5 把这段代码塞进 OnInit 跑一遍,打印 sx2 和 sw0,看整数情形是否真补出 .0。
sx2=DoubleToString(RNDfromCI(Min, Max),class="num">1); sx3=DoubleToString(RNDfromCI(Min, Max),class="num">1); sx4=DoubleToString(RNDfromCI(Min, Max),class="num">1); sx5=DoubleToString(RNDfromCI(Min, Max),class="num">1); sx6=DoubleToString(RNDfromCI(Min, Max),class="num">1); sx7=DoubleToString(RNDfromCI(Min, Max),class="num">1); sx8=DoubleToString(RNDfromCI(Min, Max),class="num">1); sx9=DoubleToString(RNDfromCI(Min, Max),class="num">1); sx10=DoubleToString(RNDfromCI(Min, Max),class="num">1); sx11=DoubleToString(RNDfromCI(Min, Max),class="num">1); ss0=DoubleToString(RNDfromCI(Min, Max),class="num">1); ss1=DoubleToString(RNDfromCI(Min, Max),class="num">1); ss2=DoubleToString(RNDfromCI(Min, Max),class="num">1); if(StringFind(sw0,".", class="num">0) == -class="num">1 ){sw0=sw0+".class="num">0";} if(StringFind(sw1,".", class="num">0) == -class="num">1 ){sw1=sw1+".class="num">0";} if(StringFind(sw2,".", class="num">0) == -class="num">1 ){sw2=sw2+".class="num">0";} if(StringFind(sw3,".", class="num">0) == -class="num">1 ){sw3=sw3+".class="num">0";} if(StringFind(sw4,".", class="num">0) == -class="num">1 ){sw4=sw4+".class="num">0";} if(StringFind(sw5,".", class="num">0) == -class="num">1 ){sw5=sw5+".class="num">0";} if(StringFind(sw6,".", class="num">0) == -class="num">1 ){sw6=sw6+".class="num">0";} if(StringFind(sw7,".", class="num">0) == -class="num">1 ){sw7=sw7+".class="num">0";} if(StringFind(sw8,".", class="num">0) == -class="num">1 ){sw8=sw8+".class="num">0";} if(StringFind(sw9,".", class="num">0) == -class="num">1 ){sw9=sw9+".class="num">0";} if(StringFind(sw10,".", class="num">0) == -class="num">1 ){sw10=sw10+".class="num">0";} if(StringFind(sw11,".", class="num">0) == -class="num">1 ){sw11=sw11+".class="num">0";} if(StringFind(sw12,".", class="num">0) == -class="num">1 ){sw12=sw12+".class="num">0";} if(StringFind(sw13,".", class="num">0) == -class="num">1 ){sw13=sw13+".class="num">0";} if(StringFind(sw14,".", class="num">0) == -class="num">1 ){sw14=sw14+".class="num">0";} if(StringFind(sw15,".", class="num">0) == -class="num">1 ){sw15=sw15+".class="num">0";} if(StringFind(sb0,".", class="num">0) == -class="num">1 ){sb0=sb0+".class="num">0";} if(StringFind(sb1,".", class="num">0) == -class="num">1 ){sb1=sb1+".class="num">0";} if(StringFind(sb2,".", class="num">0) == -class="num">1 ){sb2=sb2+".class="num">0";} if(StringFind(sb3,".", class="num">0) == -class="num">1 ){sb3=sb3+".class="num">0";} if(StringFind(sx0,".", class="num">0) == -class="num">1 ){sx0=sx0+".class="num">0";}
补齐小数位再落盘权重
这段逻辑干的事很直白:把 11 个 sx 系字符串和 3 个 ss 系字符串先过一遍,若里头找不到小数点(StringFind 返回 -1),就手动拼上 .0。外汇与贵金属参数序列化时,缺小数点会被 StringToDouble 当成整数处理,权重计算可能直接偏掉一格,属于典型的高风险静默 bug。
| 补齐后把所有字段用逗号拼成 str,交给 VerifyPassagesInFile 做一遍校验。只有返回 true,才允许用 FileOpen 以 FILE_WRITE | FILE_READ | FILE_CSV | FILE_COMMON | FILE_ANSI 打开共享目录下的优化文件,并 Seek 到尾部追加写入 38 个列(sw0~sw15、sb0~sb3、sx0~sx11、ss0~ss2)。 |
|---|
写盘成功紧接着把 sw0 到 sw11 共 12 个权重字符串用 StringToDouble 灌进 weight[0]~weight[11]。在 MT5 里跑这类 EA 时,可以故意把某个 sx 传成纯整数,观察文件里是否补了 .0,以此验证这段防护是否生效。
if(StringFind(sx1,".", class="num">0) == -class="num">1 ){sx1=sx1+".class="num">0";} if(StringFind(sx2,".", class="num">0) == -class="num">1 ){sx2=sx2+".class="num">0";} if(StringFind(sx3,".", class="num">0) == -class="num">1 ){sx3=sx3+".class="num">0";} if(StringFind(sx4,".", class="num">0) == -class="num">1 ){sx4=sx4+".class="num">0";} if(StringFind(sx5,".", class="num">0) == -class="num">1 ){sx5=sx5+".class="num">0";} if(StringFind(sx6,".", class="num">0) == -class="num">1 ){sx6=sx6+".class="num">0";} if(StringFind(sx7,".", class="num">0) == -class="num">1 ){sx7=sx7+".class="num">0";} if(StringFind(sx8,".", class="num">0) == -class="num">1 ){sx8=sx8+".class="num">0";} if(StringFind(sx9,".", class="num">0) == -class="num">1 ){sx9=sx9+".class="num">0";} if(StringFind(sx10,".", class="num">0) == -class="num">1 ){sx10=sx10+".class="num">0";} if(StringFind(sx11,".", class="num">0) == -class="num">1 ){sx11=sx11+".class="num">0";} if(StringFind(ss0,".", class="num">0) == -class="num">1 ){ss0=ss0+".class="num">0";} if(StringFind(ss1,".", class="num">0) == -class="num">1 ){ss1=ss1+".class="num">0";} if(StringFind(ss2,".", class="num">0) == -class="num">1 ){ss2=ss2+".class="num">0";} str=sw0+","+sw1+","+sw2+","+sw3+","+sw4+","+sw5+","+sw6+","+sw7+","+sw8+","+sw9+","+sw10+","+sw11+","+sw12+","+sw13+","+sw14+","+sw15 +","+sb0+","+sb1+","+sb2+","+sb3 +","+sx0+","+sx1+","+sx2+","+sx3+","+sx4+","+sx5+","+sx6+","+sx7+","+sx8+","+sx9+","+sx10+","+sx11 +","+ss0+","+ss1+","+ss2; if (VerifyPassagesInFile(str)==true) { ResetLastError(); filehandle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";"); if(filehandle!=INVALID_HANDLE) { Print("FileOpen OK"); FileSeek(filehandle, class="num">0, SEEK_END); FileWrite(filehandle,Passages,sw0,sw1,sw2,sw3,sw4,sw5,sw6,sw7,sw8,sw9,sw10,sw11,sw12,sw13,sw14,sw15, sb0,sb1,sb2,sb3, sx0,sx1,sx2,sx3,sx4,sx5,sx6,sx7,sx8,sx9,sx10,sx11, ss0,ss1,ss2); FileClose(filehandle); weight[class="num">0]=StringToDouble(sw0); weight[class="num">1]=StringToDouble(sw1); weight[class="num">2]=StringToDouble(sw2); weight[class="num">3]=StringToDouble(sw3); weight[class="num">4]=StringToDouble(sw4); weight[class="num">5]=StringToDouble(sw5); weight[class="num">6]=StringToDouble(sw6); weight[class="num">7]=StringToDouble(sw7); weight[class="num">8]=StringToDouble(sw8); weight[class="num">9]=StringToDouble(sw9); weight[class="num">10]=StringToDouble(sw10); weight[class="num">11]=StringToDouble(sw11);
◍ 权重数组回填与文件校验的衔接点
这段代码片段在做两件事:先把外部读入的字符串变量逐一并转换成 double,写进 weight[12] 到 weight[34] 的连续槽位;随后用 r、q 两个标志位控制读取成功与否。 从下标看,weight[12..15] 对应 sw 系列 4 个参数,weight[16..19] 是 sb 系列 4 个,weight[20..31] 是 sx 系列 12 个,weight[32..34] 是 ss 系列 3 个,合计 23 个权重值被灌入数组。若文件打开失败,Print 打出 FileOpen ERROR 及 GetLastError() 码,并把 q 置 0,调用方应据此跳过后续优化逻辑。 紧接着的 VerifyPassagesInFile 函数预声明了 str 到 str36 共 37 个 string 局部变量,并在入口用 FileIsExist(OptimizationFileName) 判断配置文件是否真实存在——这一步是防止脏读的必要栅栏。开 MT5 把这段贴进 EA,改 OptimizationFileName 路径跑一次,就能看到 q=0 的报错分支在什么环境下触发。外汇与贵金属品种下此类文件 IO 受限于终端沙箱目录,路径写错会直接走 else 分支,属高风险配置环节。
weight[class="num">12]=StringToDouble(sw12); weight[class="num">13]=StringToDouble(sw13); weight[class="num">14]=StringToDouble(sw14); weight[class="num">15]=StringToDouble(sw15); weight[class="num">16]=StringToDouble(sb0); weight[class="num">17]=StringToDouble(sb1); weight[class="num">18]=StringToDouble(sb2); weight[class="num">19]=StringToDouble(sb3); weight[class="num">20]=StringToDouble(sx0); weight[class="num">21]=StringToDouble(sx1); weight[class="num">22]=StringToDouble(sx2); weight[class="num">23]=StringToDouble(sx3); weight[class="num">24]=StringToDouble(sx4); weight[class="num">25]=StringToDouble(sx5); weight[class="num">26]=StringToDouble(sx6); weight[class="num">27]=StringToDouble(sx7); weight[class="num">28]=StringToDouble(sx8); weight[class="num">29]=StringToDouble(sx9); weight[class="num">30]=StringToDouble(sx10); weight[class="num">31]=StringToDouble(sx11); weight[class="num">32]=StringToDouble(ss0); weight[class="num">33]=StringToDouble(ss1); weight[class="num">34]=StringToDouble(ss2); r=class="num">1; q=class="num">1; } else { Print("FileOpen ERROR, error ",GetLastError()); q=class="num">0; } } } } class="type">bool VerifyPassagesInFile(class="type">class="kw">string st){ class="type">class="kw">string str=""; class="type">class="kw">string str1=""; class="type">class="kw">string str2=""; class="type">class="kw">string str3=""; class="type">class="kw">string str4=""; class="type">class="kw">string str5=""; class="type">class="kw">string str6=""; class="type">class="kw">string str7=""; class="type">class="kw">string str8=""; class="type">class="kw">string str9=""; class="type">class="kw">string str10=""; class="type">class="kw">string str11=""; class="type">class="kw">string str12=""; class="type">class="kw">string str13=""; class="type">class="kw">string str14=""; class="type">class="kw">string str15=""; class="type">class="kw">string str16=""; class="type">class="kw">string str17=""; class="type">class="kw">string str18=""; class="type">class="kw">string str19=""; class="type">class="kw">string str20=""; class="type">class="kw">string str21=""; class="type">class="kw">string str22=""; class="type">class="kw">string str23=""; class="type">class="kw">string str24=""; class="type">class="kw">string str25=""; class="type">class="kw">string str26=""; class="type">class="kw">string str27=""; class="type">class="kw">string str28=""; class="type">class="kw">string str29=""; class="type">class="kw">string str30=""; class="type">class="kw">string str31=""; class="type">class="kw">string str32=""; class="type">class="kw">string str33=""; class="type">class="kw">string str34=""; class="type">class="kw">string str35=""; class="type">class="kw">string str36=""; if (FileIsExist(OptimizationFileName)== true ){
「用 CSV 共享区存取优化参数」
EA 在 MT5 里跨品种传递优化结果,常见做法是落盘到公共目录的 CSV。先用 ResetLastError 清掉线程级错误码,再以 FILE_COMMON 标记把文件开在终端共享区,这样不同图表上的实例都能读到同一份参数表。 下面这段代码演示了打开与读取两个动作:写模式打开若拿到 INVALID_HANDLE,就打印 GetLastError 的具体码;读模式打开成功后进入逐行解析。FILE_CSV 配合分号分隔,意味着每列是一次优化出的某个输入变量。 读取循环里一口气 FileReadString 了 36 次,对应一行里 36 个字段。str1 通常是标识列,真正拼回参数字符串时从 str2 起用逗号连到 str21,说明这张表头 21 个有效参数、其余列可能留作备注或版本号。外汇与贵金属品种做这种共享优化请留意:跨实例读写若碰上并发,文件锁缺失可能导致某次读取拿到半行,概率上会引发参数错位。 验证方式很直接:把 OptimizationFileName 指向 Common\Files 下的自定义名,在策略测试器跑完优化后让 EA 写一次,再开第二个 EURUSD 实例看是否打印 FileOpen OK 并读满 36 列。
ResetLastError(); filehandle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";"); if(filehandle!=INVALID_HANDLE) { Print("FileCreate OK"); } else Print("FileCreate ERROR, error ",GetLastError()); FileClose(filehandle); } ResetLastError(); filehandle = FileOpen(OptimizationFileName,FILE_WRITE|FILE_READ|FILE_CSV|FILE_COMMON|FILE_ANSI, ";"); if(filehandle!=INVALID_HANDLE) { Print("FileOpen OK"); } else Print("FileOpen ERROR, error ",GetLastError()); class=class="str">"cmt">//--- read the data from the file class="kw">while(!FileIsEnding(filehandle) && !IsStopped()) { str1=FileReadString(filehandle); str2=FileReadString(filehandle); str3=FileReadString(filehandle); str4=FileReadString(filehandle); str5=FileReadString(filehandle); str6=FileReadString(filehandle); str7=FileReadString(filehandle); str8=FileReadString(filehandle); str9=FileReadString(filehandle); str10=FileReadString(filehandle); str11=FileReadString(filehandle); str12=FileReadString(filehandle); str13=FileReadString(filehandle); str14=FileReadString(filehandle); str15=FileReadString(filehandle); str16=FileReadString(filehandle); str17=FileReadString(filehandle); str18=FileReadString(filehandle); str19=FileReadString(filehandle); str20=FileReadString(filehandle); str21=FileReadString(filehandle); str22=FileReadString(filehandle); str23=FileReadString(filehandle); str24=FileReadString(filehandle); str25=FileReadString(filehandle); str26=FileReadString(filehandle); str27=FileReadString(filehandle); str28=FileReadString(filehandle); str29=FileReadString(filehandle); str30=FileReadString(filehandle); str31=FileReadString(filehandle); str32=FileReadString(filehandle); str33=FileReadString(filehandle); str34=FileReadString(filehandle); str35=FileReadString(filehandle); str36=FileReadString(filehandle); str=str2+","+str3+","+str4+","+str5+","+str6+","+str7+","+str8+","+str9+","+str10+","+str11 +","+str12+","+str13+","+str14+","+str15+","+str16+","+str17+","+str18+","+str19+","+str20+","+str21
写文件前的重复校验与收尾
这段逻辑出现在指标把 36 个字符串变量拼接成一行 CSV 内容的末尾。str22 到 str36 依次用逗号连接,补全了前面未列出的字段,最终得到完整行串 str。 紧接着用 if(str==st) 做一道防护:若新拼出的行与已有内容 st 完全一致,说明这次写入是重复数据,直接 FileClose 并 return(false),避免历史记录被刷重复。 若校验通过,函数走到最外层 FileClose(filehandle) 后 return(true),告知调用方写入成功。外汇与贵金属行情跳动频繁,这类去重判断能显著降低日志文件膨胀,但高频品种仍建议手动定期清理,相关操作属高风险环境下的工程细节,请自行在 MT5 策略测试器验证行为。
+","+str22+","+str23+","+str24+","+str25+","+str26+","+str27+","+str28+","+str29+","+str30+","+str31+","+str32+","+str33+","+str34+","+str35+","+str36; if(str==st){FileClose(filehandle); class="kw">return(false);} } FileClose(filehandle); class="kw">return(true); }