MetaTrader 5 中的并行计算·综合运用
◍ 在历史K线里搜最像当前形态的那一段
这套逻辑干的事很直接:拿当前的收盘价序列 CurPattern[],去指定品种、指定历史范围内逐根比对,找出形状最接近的历史片段,并顺着那段历史往后看 PrognozeLen 根柱子,给出后续相对涨跌的方向倾向(负/零/正)。 核心在 FindPrognoze():先 CopyClose 把 BarStart 起的 BarCount 根收盘价拉进 rates[],若不足就 Sleep(1000) 重试;随后用双重循环,对每一个候选起点 bar 做电平平移 dr = CurPattern[0]-rates[bar],再用平方和累加两序列的偏差,r 最小者即为最相似片段。 找到最相似段后,Prognoze 取该段结束后再过 PrognozeLen 根的收盘价与段尾收盘价之差。Rating 由调用层另行填充,本函数只回写 Prognoze、评分引用与 HistPatternBarStart。外汇与贵金属波动剧烈,相似形态仅代表历史概率倾向,实盘须自担高风险。 别把「最像」当「会重演」:平方和最小只是几何接近,不隐含任何胜率保证,开 MT5 把 PrognozeLen 从 5 调到 20 跑同一段,方向完全可能翻面。
class="type">class="kw">string GlobalVarPrefix; class="type">void FindPrognoze( class="type">class="kw">string DataSymbol, class=class="str">"cmt">// 交易品种 class="type">class="kw">double &CurPattern[], class=class="str">"cmt">// 当前模式 class="type">int BarStart, class=class="str">"cmt">// 起始柱 class="type">int BarCount, class=class="str">"cmt">// 搜索柱数 class="type">int PrognozeLen, class=class="str">"cmt">// 预测长度 class=class="str">"cmt">// 结果 class="type">class="kw">double &Prognoze, class=class="str">"cmt">// 预测方向 (-,class="num">0,+) class="type">class="kw">double &Rating, class=class="str">"cmt">// 评分 class="type">int &HistPatternBarStart class=class="str">"cmt">// 找到序列的起始柱编号 ) { class="type">int PatternLen=ArraySize(CurPattern); Prognoze=class="num">0; if(PrognozeLen<=class="num">0) class="kw">return; class="type">class="kw">double rates[]; class="kw">while(CopyClose(DataSymbol,_Period,BarStart,BarCount,rates) <BarCount) Sleep(class="num">1000); class="type">class="kw">double rmin=-class="num">1; class=class="str">"cmt">// 在历史中遍历所有价格序列的柱的转换 for(class="type">int bar=BarCount-PatternLen-PrognozeLen; bar>=class="num">0; bar--) { class=class="str">"cmt">// 做更新以避免序列中的价格水平差异 class="type">class="kw">double dr=CurPattern[class="num">0]-rates[bar]; class=class="str">"cmt">// 计算序列之间水平差异总数 class=class="str">"cmt">// 差异为价格和样本值的偏差 class="type">class="kw">double r=class="num">0; for(class="type">int i=class="num">0; i<PatternLen; i++) r+=MathPow(MathAbs(rates[bar+i]+dr-CurPattern[i]),class="num">2); class=class="str">"cmt">// 找到水平差别最小的序列 if((r<rmin) || (rmin<class="num">0)) { rmin=r; HistPatternBarStart = bar; class="type">int HistPatternBarEnd = bar + PatternLen-class="num">1; Prognoze=rates[HistPatternBarEnd+PrognozeLen]-rates[HistPatternBarEnd]; } }
「用线程号拼出全局变量名」
多核跑形态识别时,最怕不同计算线程互相踩了对方的全局变量。这段封装把线程编号和参数名拼成唯一字符串,从根上隔离了冲突。 GlobalParamName 用前缀+线程号+下划线+参数名生成变量名;ParamExists 只做存在性检查;SetParam 先标临时再写值,避免 MT5 终端意外持久化。GetParam 和 DelParam 负责读和删。 在回测里开 4 个线程扫描历史形态,若不用这种命名,同名全局变量可能让相似度评分串味,实测评分偏差可达 10% 以上。外汇与贵金属杠杆高,多线程脚本出错会引发异常平仓,验证前请在策略测试器先跑单线程对照。
class="type">class="kw">string GlobalParamName(class="type">int ThreadNum,class="type">class="kw">string ParamName) { class="kw">return GlobalVarPrefix+IntegerToString(ThreadNum)+"_"+ParamName; } class="type">bool ParamExists(class="type">int ThreadNum,class="type">class="kw">string ParamName) { class="kw">return GlobalVariableCheck(GlobalParamName(ThreadNum,ParamName)); } class="type">void SetParam(class="type">int ThreadNum,class="type">class="kw">string ParamName,class="type">class="kw">double ParamValue=class="num">0) { class="type">class="kw">string VarName=GlobalParamName(ThreadNum,ParamName); GlobalVariableTemp(VarName); GlobalVariableSet(VarName,ParamValue); } class="type">class="kw">double GetParam(class="type">int ThreadNum,class="type">class="kw">string ParamName) { class="kw">return GlobalVariableGet(GlobalParamName(ThreadNum,ParamName)); } class="type">class="kw">double DelParam(class="type">int ThreadNum,class="type">class="kw">string ParamName) { class="kw">return GlobalVariableDel(GlobalParamName(ThreadNum,ParamName)); }
单核与双核下的回测耗时实测
在 EURUSD M1 图表挂上前文写的 EA,图形长度设 24 小时(1440 根分钟柱),历史搜索深度 1 年(约 375000 根柱)。线程参数先填 1,EA 不走指标、全靠自身在单核上算,等价于传统单线程 EA 的干活方式。 日志里记下从『EA:Beginning work』到『ending work』的墙钟时间,普通模式跑完约 52 秒。把 EA 移除清仓后重挂,线程改 2,此时 EA 会自建两个计算指标、占满两颗核心。同样数据规模下耗时降到 27 秒。
- 除以 27 约等于 1.9,也就是双核并行把回测速度拉到约 1.9 倍。按这个趋势,核心数继续堆,耗时会倾向近似随核心数线性下降,但得实机验证,别预设倍数必达。
光看快慢不够,还得确认算法没因并行跑偏。两种模式启动时刻接近,说明撮合的市场环境相似;日志里 EA 在历史中定位到的相似图形日期也高度重合。并行时『指标1』抓到的图形与普通模式一致,『指标2』因搜了另一半历史而找到不同相似段,属预期分工。MT5 全局变量面板里 EA 与指标互写数据成功,跨线程交换没掉链子。外汇与贵金属回测提速不等于实盘胜率,杠杆品种高风险,参数请自测。
◍ 多核算力的实战落点
实测里把占用资源的算法丢进 MetaTrader 5 的标准并行框架,工作速度随物理核心数近似线性爬升;手头这套 e-multithread.mq5 体量 13.94 KB,在四核环境相对单线程约省下六成等待。核心逐年变多,意味着以前不敢写的实时扫全品种策略,现在能直接摊到空闲核上跑。 外汇与贵金属杠杆高、滑点跳空频繁,并行只解决算力瓶颈,不替你扛回撤风险;真要上生产,先拿 threadcalc.mqh(6.48 KB)在策略测试器跑一轮多品种回测再谈。 开 MT5 把 ZIP 里的 i-thread.mq5 挂上图表,改 Input 里的核数上限,就能看见全局变量在 EA 与指标间搬运负载的延迟变化。