扩充策略构建器功能·综合运用
(3/3)· 前两步搭好形态与构建器,这回把手数控制和测试报告缺口一次补上
「止损触发与报告输出的代码骨架」
这段逻辑处理两种退出:价格触及止损线时,系统按 SL 点数记账并回退起始时间。 具体看,当 deal_price 与 iLow 取到的低点之差折算成 point 数大于等于 SL,就判定为空头止损触发:计数加一、记一笔 -SL 的成交、亏损交易数加一、总盈利减 SL、亏损 pip 累加 SL,并把 m_start_date 顺着时间轴后移 j 根 K 线后直接 return。 报告打印函数 PrintReport 先按语言索引填 11 个标签,索引 0 走俄文、非 0 走英文;标签 0 到 10 覆盖总交易数、净利、pip 净利、绝对与最大回撤、多空胜率等。若 total_trades 为 0 直接 return,避免空跑;之后依据 profit_type 列表选中项是否为 1 走不同分支。 在 MT5 里把这段接进 EA 回测,能把「总交易=0 就跳过」和「SL 以低点差计」两处直接验证,外汇与贵金属波动大,此类硬编码止损对滑点敏感,实盘前务必用历史数据跑一遍。
class="kw">return; } class=class="str">"cmt">//--- Stop Loss trigger else if((deal_price-iLow(symbol,StringToTimeframe(tf),copied-j))/SymbolInfoDouble(symbol,SYMBOL_POINT)>=SL) { m_counter++; AddDeal(-SL,m_counter); m_report.loss_trades++; m_report.profit-=SL; m_report.loss_pips+=SL; m_report.long_trades++; m_report.total_trades++; m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),j); class="kw">return; } } m_start_date=m_end_date; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CProgram::PrintReport(class="type">void) { class="type">class="kw">string report_label[class="num">11]; if(m_lang_index==class="num">0) { report_label[class="num">0]="Всего трейдов: "; report_label[class="num">1]="Чистая прибыль: "; report_label[class="num">2]="Прибыль в пунктах: "; report_label[class="num">3]="Абс.просадка баланса: "; report_label[class="num">4]="Макс.просадка баланса: "; report_label[class="num">5]="Корот.трейды/% выигр: "; report_label[class="num">6]="Приб.трейды/% от всех: "; report_label[class="num">7]="Прибыльность: "; report_label[class="num">8]="Фактор восстановления: "; report_label[class="num">9]="Длин.трейды/% выигр: "; report_label[class="num">10]="Убыт.трейды/% от всех: "; } else { report_label[class="num">0]="Total trades: "; report_label[class="num">1]="Total profit: "; report_label[class="num">2]="Total profit(pips): "; report_label[class="num">3]="Balance Drawdown Abs: "; report_label[class="num">4]="Balance Drawdown Max: "; report_label[class="num">5]="Short trades/won %: "; report_label[class="num">6]="Profit trades/% of all: "; report_label[class="num">7]="Profit Factor: "; report_label[class="num">8]="Recovery Factor: "; report_label[class="num">9]="Long trades/won %: "; report_label[class="num">10]="Loss trades/% of all: "; } class=class="str">"cmt">//--- m_report_text[class="num">0].LabelText(report_label[class="num">0]+class="type">class="kw">string(m_report.total_trades)); class=class="str">"cmt">//--- if(m_report.total_trades==class="num">0) class="kw">return; class=class="str">"cmt">//--- if(m_profit_type.GetListViewPointer().SelectedItemIndex()==class="num">1) {
回测面板里的回撤与盈亏比怎么算
这段逻辑干的事很直接:遍历权益曲线数组,实时揪出历史最大利润和最大回撤。maxprofit 从 0 开始爬,每次遇到 data[i] 更大就刷新;maxdd 则用「峰值减当前」的最大值来定义,所以它抓的是水下回撤深度,不是单笔亏损。 代码里第 4 行标签把 maxdd/maxprofit*100 格式化成百分比输出,意味着回撤率相对的是「历史峰值权益」而非初始本金。若某品种 maxprofit 为 5000 点、maxdd 为 800 点,面板会显示 16.00%,这个比值偏低时策略可能倾向抗回撤能力较强,但外汇与贵金属杠杆高,实盘回撤可能偏离回测。 else 分支在没有完整曲线时只填盈亏比:profit_pips 除以 loss_pips,其余三项打「-」。注意它用 pip 而非金额算,点值波动大的贵金属交叉盘要自己换算风险。 最后 11 个文本对象统一 Update(true) 强制重绘,m_reported 置真避免重复刷。开 MT5 把这段塞进你的 CReport 类,改 ArraySize 边界或乘 100 的精度,面板数字立刻变样。
class="type">class="kw">double maxprofit=class="num">0.0,maxdd=class="num">0.0; for(class="type">int i=class="num">1; i<ArraySize(data); i++) { if(data[i]>maxprofit) maxprofit=data[i]; if(maxdd<maxprofit-data[i]) maxdd=maxprofit-data[i]; } m_report_text[class="num">1].LabelText(report_label[class="num">1]+DoubleToString(data[ArraySize(data)-class="num">1],class="num">2)); m_report_text[class="num">3].LabelText(report_label[class="num">3]+class="type">class="kw">string(data[class="num">0]-data[ArrayMinimum(data)])); m_report_text[class="num">4].LabelText(report_label[class="num">4]+DoubleToString(maxdd/maxprofit*class="num">100,class="num">2)+"%"); m_report_text[class="num">7].LabelText(report_label[class="num">7]+DoubleToString(m_all_profit/m_all_losses,class="num">2)); m_report_text[class="num">8].LabelText(report_label[class="num">8]+DoubleToString((data[ArraySize(data)-class="num">1]-data[class="num">0])/maxdd,class="num">2)); } else { m_report_text[class="num">1].LabelText(report_label[class="num">1]+"-"); m_report_text[class="num">3].LabelText(report_label[class="num">3]+"-"); m_report_text[class="num">4].LabelText(report_label[class="num">4]+"-"); m_report_text[class="num">7].LabelText(report_label[class="num">7]+DoubleToString(m_report.profit_pips/(class="type">class="kw">double)m_report.loss_pips,class="num">2)); } m_report_text[class="num">2].LabelText(report_label[class="num">2]+class="type">class="kw">string(m_report.profit)); m_report_text[class="num">5].LabelText(report_label[class="num">5]+class="type">class="kw">string(m_report.short_trades)+"/"+DoubleToString(m_report.profit_short/(class="type">class="kw">double)m_report.short_trades*class="num">100,class="num">1)+"%"); m_report_text[class="num">6].LabelText(report_label[class="num">6]+class="type">class="kw">string(m_report.profit_trades)+"/"+DoubleToString(m_report.profit_trades/(class="type">class="kw">double)m_report.total_trades*class="num">100,class="num">1)+"%"); m_report_text[class="num">9].LabelText(report_label[class="num">9]+class="type">class="kw">string(m_report.long_trades)+"/"+DoubleToString(m_report.profit_long/(class="type">class="kw">double)m_report.long_trades*class="num">100,class="num">1)+"%"); m_report_text[class="num">10].LabelText(report_label[class="num">10]+class="type">class="kw">string(m_report.loss_trades)+"/"+DoubleToString(m_report.loss_trades/(class="type">class="kw">double)m_report.total_trades*class="num">100,class="num">1)+"%"); class=class="str">"cmt">//--- for(class="type">int i=class="num">0; i<class="num">11; i++) m_report_text[i].Update(true); m_reported=true; }
◍ 策略构建器跑通测试的实际操作点
应用更新后,用户手册要同步改,但跑测试的链路本身变化不大。界面语言可改可不改,指标参数有默认值,只动你需要的那几个;品种表默认把市场观察里名字带 USD 的筛出来,去掉勾选就能看全部可用品种。 时间周期和时间帧这两步没动。信号面板做了视觉收敛:禁掉买或卖其中一种信号,相关设置整组隐藏,不会占界面空间。止盈止损逻辑也没改。 利润类型切换是实测要留意的地方:选“点数”,报告里结果按品种点数显示;选“货币”会多出“手数类型 / 手数 / 初始存款”三项,手数类型可选常量或按余额算,直接影响测试里的仓位计算。 步骤 1–8 走完,在表里左键点品种开始测,结果进“报告”,再点开图形看细节。自定义指标参数别写错:缓冲区编号、名称、参数用逗号隔,只认数字值;名称是相对 MQL5/Indicators/ 的路径,子目录要写成 "Examples\\indicator_name",单斜杠换双反斜杠。 常见卡点都有提示弹窗:同一形态触发双向信号、没先测就开图形、测试起止日期填错。外汇与贵金属品种波动剧烈、杠杆风险高,下载对应历史数据再测,否则美林形态类策略可能直接跑不出结果。
「别急着下结论」
把前面几篇攒下的 MQL5 文件包解压后,根目录里必须出现 MQL5 文件夹,且直接丢进 MT5 终端根目录才能跑通。最快的打开方式是在终端里按 Ctrl+Shift+D,或者用右键关联菜单定位,图例里标的位置和实际路径一致。 整套压缩包体积约 1936.58 KB,涵盖语音通知、手动交易工具箱、多币种信号监视等模块的源码;外汇与贵金属行情受杠杆和滑点影响大,直接挂实盘前先在策略测试器里用历史数据验证一遍更稳妥。 作者留过话:半自动和全自动专家模式是后续计划,当前版本只解决文件落地和目录结构。装完别以为就能无脑跟单,先按自己品种调参再说。