利用 MQL5 和 MQL4 实现的选择和导航工具: 把数据添加到图表中·综合运用
📘

利用 MQL5 和 MQL4 实现的选择和导航工具: 把数据添加到图表中·综合运用

第 3/3 篇

用 Gerchik's ATR 给日内波动划边界

ATR 衡量交易品种在一段时间内的平均真实波幅,是判断「今天还值不值得做」的硬指标。想抓 100 点行情,但品种日均范围只有 50 点,成功概率天然偏低,这类日子最好离场。 另一种实战经验:若价格单日已走过日均 ATR 的 80% 以上,同向追单意义不大,只剩反向博弈的可能空间。外汇与贵金属杠杆高,波幅突变频繁,这条仅作概率参考而非进场保证。 Gerchik's ATR 与传统 ATR 的区别在于只认「异常柱」:实体波动大于均值 2 倍、或小于 0.3 倍的柱子直接剔除,避免被噪音稀释。常规 ATR 取 5 或 3 日,本函数拉取日线近 8 根,但有效采样上限锁在 5 根。 下面这段 MT5 函数可直接塞进 EA 或脚本,在图表左上角注释里打印 ATR 与「今日已走百分比」,开 MT5 挂上就能看: [CODE] <span class="comment">/* 为指定交易品种返回表示 Gerchik ATR 的字符串: symname - 交易品种名称 */</span> <span class="keyword">string</span> getmeatr_symbol(<span class="keyword">string</span> symname){ &nbsp;&nbsp; <span class="keyword">string</span> msg=<span class="string">""</span>; &nbsp;&nbsp; <span class="predefines">MqlRates</span> rates[]; &nbsp;&nbsp; <span class="functions">ArraySetAsSeries</span>(rates, <span class="macro">true</span>); &nbsp;&nbsp; <span class="keyword">double</span> atr=<span class="number">0</span>; &nbsp;&nbsp; <span class="keyword">double</span> pre_atr=<span class="number">0</span>; &nbsp;&nbsp; <span class="keyword">int</span> curDigits=(<span class="keyword">int</span>) <span class="functions">SymbolInfoInteger</span>(symname, <span class="macro">SYMBOL_DIGITS</span>); &nbsp;&nbsp; <span class="keyword">string</span> currencyS=<span class="functions">SymbolInfoString</span>(symname, <span class="macro">SYMBOL_CURRENCY_PROFIT</span>); &nbsp;&nbsp; <span class="keyword">int</span> count=<span class="number">0</span>; &nbsp;&nbsp; <span class="keyword">int</span> copied=<span class="functions">CopyRates</span>(symname, <span class="macro">PERIOD_D1</span>, <span class="number">0</span>, <span class="number">8</span>, rates); &nbsp;&nbsp; <span class="keyword">if</span>(copied&gt;<span class="number">1</span>){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">1</span>; j&lt;copied; j++){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pre_atr+=rates[j].high-rates[j].low; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pre_atr/=(copied-<span class="number">1</span>); &nbsp;&nbsp; } &nbsp;&nbsp; <span class="keyword">if</span>(pre_atr&gt;<span class="number">0</span>){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span>(<span class="keyword">int</span> j=<span class="number">1</span>; j&lt;copied; j++){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">if</span>( rates[j].high-rates[j].low &gt; pre_atr*<span class="number">2</span> ) <span class="keyword">continue</span>; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">if</span>( rates[j].high-rates[j].low &lt; pre_atr*<span class="number">0.3</span> ) <span class="keyword">continue</span>; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">if</span>( ++count &gt; <span class="number">5</span> ){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">break</span>; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; atr+=rates[j].high-rates[j].low; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>( count &gt; <span class="number">5</span> ){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count=<span class="number">5</span>; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;atr= <span class="functions">NormalizeDouble</span>(atr/count, curDigits ); &nbsp;&nbsp; } &nbsp;&nbsp; <span class="keyword">if</span>(atr&gt;<span class="number">0</span>){ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">StringAdd</span>(msg, <span class="string">"ATR: "</span>+(<span class="keyword">string</span>) <span class="functions">DoubleToString</span>(atr, curDigits)+<span class="string">" "</span>+currencyS+<span class="string">", today: "</span>+<span class="functions">DoubleToString</span>(rates[<span class="number">0</span>].high-rates[<span class="number">0</span>].low, curDigits)+<span class="string">" "</span>+currencyS+<span class="string">" ("</span>+(<span class="keyword">string</span>) (<span class="keyword">int</span>) (((rates[<span class="number">0</span>].high-rates[<span class="number">0</span>].low)/atr)*<span class="number">100</span>) +<span class="string">"%)"</span>); &nbsp;&nbsp; } &nbsp;&nbsp; &nbsp;&nbsp; <span class="keyword">return</span> msg; } [/CODE] 逐行看:先取品种小数位与盈利货币,CopyRates 拉 8 根 D1 数据;第一轮循环算朴素平均波幅 pre_atr。第二轮剔除 &gt;2 倍和 &lt;0.3 倍的异常柱,最多累加 5 根有效柱求均值,NormalizeDouble 按品种精度收敛。最后拼出字符串,含 ATR 值、今日高低差及占 ATR 百分比——例如显示「today: 40%」就意味着当日空间还剩六成概率可动。 别把正态当圣经 异常柱过滤是 Gerchik 版本的核心,但 2 倍 / 0.3 倍阈值写死在循环里。黄金隔夜跳空常出 3 倍柱,若你主要做 XAUUSD,可把 2 改成 2.5 再跑,看图表注释的数值是否更贴合实际可交易区间。

MQL5 / C++
<span class="comment">class=class="str">"cmt">/*
为指定交易品种返回表示 Gerchik ATR 的字符串:
symname - 交易品种名称
*/</span>
<span class="keyword">class="type">class="kw">string</span> getmeatr_symbol(<span class="keyword">class="type">class="kw">string</span> symname){
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">string</span> msg=<span class="class="type">class="kw">string">""</span>;
&nbsp;&nbsp; <span class="predefines">class="type">MqlRates</span> rates[];
&nbsp;&nbsp; <span class="functions">ArraySetAsSeries</span>(rates, <span class="macro">true</span>);
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> atr=<span class="number">class="num">0</span>;
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> pre_atr=<span class="number">class="num">0</span>;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> curDigits=(<span class="keyword">class="type">int</span>) <span class="functions">SymbolInfoInteger</span>(symname, <span class="macro">SYMBOL_DIGITS</span>);
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">string</span> currencyS=<span class="functions">SymbolInfoString</span>(symname, <span class="macro">SYMBOL_CURRENCY_PROFIT</span>);
&nbsp;&nbsp; <span class="keyword">class="type">int</span> count=<span class="number">class="num">0</span>;
&nbsp;&nbsp; <span class="keyword">class="type">int</span> copied=<span class="functions">CopyRates</span>(symname, <span class="macro">PERIOD_D1</span>, <span class="number">class="num">0</span>, <span class="number">class="num">8</span>, rates);
&nbsp;&nbsp; <span class="keyword">if</span>(copied&gt;<span class="number">class="num">1</span>){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span>(<span class="keyword">class="type">int</span> j=<span class="number">class="num">1</span>; j&lt;copied; j++){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pre_atr+=rates[j].high-rates[j].low;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pre_atr/=(copied-<span class="number">class="num">1</span>);
&nbsp;&nbsp; }
&nbsp;&nbsp; <span class="keyword">if</span>(pre_atr&gt;<span class="number">class="num">0</span>){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">for</span>(<span class="keyword">class="type">int</span> j=<span class="number">class="num">1</span>; j&lt;copied; j++){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">if</span>( rates[j].high-rates[j].low &gt; pre_atr*<span class="number">class="num">2</span> ) <span class="keyword">class="kw">continue</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">if</span>( rates[j].high-rates[j].low &lt; pre_atr*<span class="number">class="num">0.3</span> ) <span class="keyword">class="kw">continue</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">if</span>( ++count &gt; <span class="number">class="num">5</span> ){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">break</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; atr+=rates[j].high-rates[j].low;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>( count &gt; <span class="number">class="num">5</span> ){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count=<span class="number">class="num">5</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;atr= <span class="functions">NormalizeDouble</span>(atr/count, curDigits );
&nbsp;&nbsp; }
&nbsp;&nbsp; <span class="keyword">if</span>(atr&gt;<span class="number">class="num">0</span>){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">StringAdd</span>(msg, <span class="class="type">class="kw">string">"ATR: "</span>+(<span class="keyword">class="type">class="kw">string</span>) <span class="functions">DoubleToString</span>(atr, curDigits)+<span class="class="type">class="kw">string">" "</span>+currencyS+<span class="class="type">class="kw">string">", today: "</span>+<span class="functions">DoubleToString</span>(rates[<span class="number">class="num">0</span>].high-rates[<span class="number">class="num">0</span>].low, curDigits)+<span class="class="type">class="kw">string">" "</span>+currencyS+<span class="class="type">class="kw">string">" ("</span>+(<span class="keyword">class="type">class="kw">string</span>) (<span class="keyword">class="type">int</span>) (((rates[<span class="number">class="num">0</span>].high-rates[<span class="number">class="num">0</span>].low)/atr)*<span class="number">class="num">100</span>) +<span class="class="type">class="kw">string">"%)"</span>);
&nbsp;&nbsp; }
&nbsp;&nbsp; 
&nbsp;&nbsp; <span class="keyword">class="kw">return</span> msg;
}

◍ 用引导品种定日内交易方向

做日内最怕盲开单。老手常先看‘引导品种’的当前方向:美股里至少盯道指或标普,再配一个外围指数(中国、欧洲或日本),加上银行、科技、能源等板块龙头,以及油、金、美元报价。当这些标的日线倾向一致,顺该方向开单的概率更高,逆着做容易吃亏。 经纪商对同一品种命名不同,没法写死代码监控,所以实用程序留了 7 个字符串型参数,让你按自己平台填代号。引导方向不会自动刷新——切到别的图表标签或按 R 才更新,这点容易漏看。 下方函数按品种名回传方向字符串,并在 D1、H1 两周期上算出涨跌百分比与点值。比如某品种 D1 收高于开,会输出类似 ‘X: D1 +0.83% (+120.5 USD)’ 的注释,挂在启动图表的左上角。外汇贵金属波动受引导品种影响明显,但杠杆风险高,方向一致也只是参考而非保证。

MQL5 / C++
class=class="str">"cmt">/*
通过指定的交易品种返回具有价格移动方向的字符串:
symname - 交易品种名称
show - 字符串中是否应显示交易品种名称标签
*/
class="type">class="kw">string getmeinfo_symbol(class="type">class="kw">string symname, class="type">bool show=true){
   class="type">MqlRates rates2[];
   ArraySetAsSeries(rates2, true);
   class="type">class="kw">string msg="";
   if(CopyRates(symname, PERIOD_D1, class="num">0, class="num">1, rates2)>class="num">0){
      if(show){
         StringAdd(msg, (class="type">class="kw">string) symname+": ");
      }
      StringAdd(msg, "D1 ");
      if( rates2[class="num">0].close > rates2[class="num">0].open ){
         StringAdd(msg, "+"+DoubleToString(((rates2[class="num">0].close-rates2[class="num">0].open)/rates2[class="num">0].close)*class="num">100, class="num">2) +"% (+"+DoubleToString(rates2[class="num">0].close-rates2[class="num">0].open, (class="type">int) SymbolInfoInteger(symname, SYMBOL_DIGITS))+" "+SymbolInfoString(symname, SYMBOL_CURRENCY_PROFIT)+")");
      }else{
         if( rates2[class="num">0].close < rates2[class="num">0].open ){
            StringAdd(msg, "-"+DoubleToString(((rates2[class="num">0].open-rates2[class="num">0].close)/rates2[class="num">0].close)*class="num">100, class="num">2) +"% (-"+DoubleToString(rates2[class="num">0].open-rates2[class="num">0].close, (class="type">int) SymbolInfoInteger(symname, SYMBOL_DIGITS))+" "+SymbolInfoString(symname, SYMBOL_CURRENCY_PROFIT)+")");
         }else{
            StringAdd(msg, "class="num">0%");
         }
      }
   }
   if(CopyRates(symname, PERIOD_H1, class="num">0, class="num">1, rates2)>class="num">0){
      StringAdd(msg, ", H1 ");
      if( rates2[class="num">0].close > rates2[class="num">0].open ){
         StringAdd(msg, "+"+DoubleToString(((rates2[class="num">0].close-rates2[class="num">0].open)/rates2[class="num">0].close)*class="num">100, class="num">2)+"% (+"+DoubleToString(rates2[class="num">0].close-rates2[class="num">0].open, (class="type">int) SymbolInfoInteger(symname, SYMBOL_DIGITS))+" "+SymbolInfoString(symname, SYMBOL_CURRENCY_PROFIT)+")");
      }else{
         if( rates2[class="num">0].close < rates2[class="num">0].open ){

「把单根K线跌幅拼进提示串」

这段逻辑干的活很具体:算当前品种最新一根K线的实体跌幅,再拼成一段带百分比和绝对点值的字符串塞进 msg。 先取 rates2[0] 的 open 和 close,用 (open-close)/close*100 得到百分比,DoubleToString 保留两位小数;若 open 大于 close 才拼 '-X.XX% (-Y.YY 报价币)',否则只写 '0%'。 验证方法:在 MT5 里建一个脚本,把 symname 换成 "XAUUSD",打印 msg 看黄金最新一根日线若收阴,是否会输出类似 '-0.83% (-15.20 USD)' 的片段。外汇与贵金属波动剧烈,该数值仅反映已发生K线,不预示后续方向。

MQL5 / C++
      StringAdd(msg, "-"+DoubleToString(((rates2[class="num">0].open-rates2[class="num">0].close)/rates2[class="num">0].close)*class="num">100, class="num">2)+"% (-"+DoubleToString(rates2[class="num">0].open-rates2[class="num">0].close, (class="type">int) SymbolInfoInteger(symname, SYMBOL_DIGITS))+" "+SymbolInfoString(symname, SYMBOL_CURRENCY_PROFIT)+")");
      }else{
         StringAdd(msg, "class="num">0%");
      }
   }
   }

   class="kw">return msg;
}

让打开的选项卡自己刷新

想在 MT5 里让当前打开的选项卡内容自动滚动更新,而不是每次手动敲 R,核心就一个参数:自动更新秒数。把它设成大于 0 的值,实用程序就会按你给的间隔去拉新数据。 注意这里的间隔不是硬准时。代码逻辑是「达到 N 秒就触发」,但实际刷新会落在 N 再加上一两秒的窗口里,属于宽松定时,不是精准心跳。 实现落在 OnTimer 里,依赖 OnInit 阶段用当前时间初始化的 lastUpd 变量做基准。下面这段就是判断与触发本体,直接搬进你的 EA 或脚本就能跑: [CODE] // 指定秒数后更新选项卡的内容 if( autoUpdateAfter>0 && TimeCurrent()-lastUpd >= autoUpdateAfter ){ start_symbols(); lastUpd=TimeCurrent(); } [/CODE] 逐行拆一下:第一注释行说明用途;第二行先卡 autoUpdateAfter 大于 0,再用 TimeCurrent() 减 lastUpd 得出已过去秒数,够阈值才进块;第三行 start_symbols() 负责重抓品种数据填进选项卡;第四行把 lastUpd 重置为本次触发时刻,形成滚动计时。外汇与贵金属波动剧烈、滑点频发,自动刷新仅解决「看新」问题,不替你过滤假突破风险。

MQL5 / C++
  class=class="str">"cmt">// 指定秒数后更新选项卡的内容
  if( autoUpdateAfter>class="num">0 && TimeCurrent()-lastUpd >= autoUpdateAfter ){
    start_symbols();
    lastUpd=TimeCurrent();
  }

◍ 把基本面参数直接打进MT5专家日志

做股票前先看公司基本面的交易者,多半不想每次切去财经站查数据。在MT5里写个实用程序,用EA挂上图表时把财务参数直接吐到「专家」选项卡,能省掉来回切换的麻烦。 财务数据没写死在代码里——早期尝试硬编码曾触发内存不足报错,所以改成分时器代码对应的 .dat 文件,统一放在终端 Files 目录下的 finder 文件夹。定位路径:文件菜单 → 打开数据文件夹 → 进 MQL5(或 MQL4)目录 → Files。文章撰写时这批参数在数月内有效,过期后需自行更新数据包。 代码先清洗交易品种名:去掉部分券商加的 # 前缀以及 .us / .eu 后缀,再转小写,避免文件匹配失败。若你的券商还加了别的尾巴,得在 printTicker 里补一行 StringReplace 自己过滤。 匹配逻辑分两层:优先用清洗后的 ticker 找 finder\\ticker.dat;找不到再用公司名 name.dat 搜。两个都不存在就打印「没有交易品种数据」。下面这段是核心输出函数,逐行拆完你就能改路径或加过滤器。 别把后缀过滤当万能 不同券商对分时器的命名规则差异很大,只删 .us / .eu 可能漏掉如 .ex 或数字后缀,导致文件永远匹配不上。挂EA后先开专家日志看是否报「没有交易品种数据」,再回头补替换规则。

MQL5 / C++
class="type">void printTicker(class="type">class="kw">string ticker, class="type">class="kw">string name){
  class="type">bool isYes=false;
  class="type">int filehandle;
  
  class=class="str">"cmt">// 从分时器中删除多余的后缀和前缀
  class=class="str">"cmt">// 并将其转换为小写
  StringToLower(ticker);
  StringReplace(ticker, "#", "");
  StringReplace(ticker, ".us", "");
  StringReplace(ticker, ".eu", "");
  
  class=class="str">"cmt">// 将公司名称转换为小写
  StringToLower(name);
  
  ResetLastError(); 
  class=class="str">"cmt">// 如果“finder”文件夹中包含公司的数据,则显示该文件夹。
  if( FileIsExist("finder\\"+ticker+".dat") ){
      isYes=true;
      filehandle=FileOpen("finder\\"+ticker+".dat",FILE_READ|FILE_TXT); 
      if(filehandle!=INVALID_HANDLE){ 
          class="type">int str_size; 
          class="type">class="kw">string str; 
          Print("----------------");
          class="kw">while(!FileIsEnding(filehandle)){ 
              str_size=FileReadInteger(filehandle,INT_VALUE); 
              str=FileReadString(filehandle,str_size); 
              Print(str);
          }
          FileClose(filehandle); 
      }
  }else if( FileIsExist("finder\\"+name+".dat") ){
      isYes=true;
      filehandle=FileOpen("finder\\"+name+".dat",FILE_READ|FILE_TXT); 
      if(filehandle!=INVALID_HANDLE){ 
          class="type">int str_size; 
          class="type">class="kw">string str; 
          Print("----------------");
          class="kw">while(!FileIsEnding(filehandle)){ 
              str_size=FileReadInteger(filehandle,INT_VALUE); 
              str=FileReadString(filehandle,str_size); 
              Print(str);
          }
          FileClose(filehandle); 
      }
  }
  
  class=class="str">"cmt">// 否则,通知未找到数据
  if(!isYes){
      Print("没有交易品种数据");
  }
}

「在图表上盯住当日盈亏与交易笔数」

给 EA 加一个开关,能在图表上直接显示当日的净利润、总交易次数、盈利与亏损笔数(按这个顺序排)。外汇和贵金属波动剧烈,这类实时统计只作盘感参考,不代表任何收益保证,重仓下可能一天就吃掉账户大头。 MQL5 里不能像 MQL4 那样直接拿一个函数就完事。我们用全局变量 needHistory 控制历史读取:OnInit 里置 true,首次跑 getmetoday_profit 会从历史补齐当天数据,随后置 false;之后靠 OnTrade 监听平仓事件,发现持仓数减少就把 needHistory 重新置 true,触发下一次历史重载。 注意一个坑:图表上的当日利润不会自动刷新。你切到别的品种标签再切回来,或按 R 重绘,数据才可能更新。验证时开 MT5 挂上这段,平掉一单后切标签看数字跳变。 下面这段是核心逻辑,OnTrade 只在 MQL5 下编译,getmetoday_profit 同时兼容两版:

MQL5 / C++
class="macro">#ifdef __MQL5__
  class="type">void OnTrade(){
    if(orders_total==PositionsTotal()){
      class="kw">return;
    }
    if(orders_total>PositionsTotal()){
      needHistory=true;
    }
    orders_total=(class="type">class="kw">ushort) PositionsTotal();
  }
class="macro">#endif
class=class="str">"cmt">/*
返回包含今天利润/亏损的字符串。
*/
class="type">class="kw">string getmetoday_profit(){
  class="type">class="kw">string msg="";
  class="type">class="kw">double curProfit=class="num">0;
  class="type">int tradeAll=class="num">0;
  class="type">int tradeMinus=class="num">0;
  class="type">int tradePlus=class="num">0;

  class="type">MqlDateTime curD;
  TimeCurrent(curD);
  curD.hour=class="num">0;
  curD.min=class="num">0;
  curD.sec=class="num">0;

  class="macro">#ifdef __MQL5__
    if( needHistory ){
      HistorySelect(StructToTime(curD),TimeCurrent());
    }
    class="type">uint totalHistory=HistoryDealsTotal();
    class="type">class="kw">double currHistory=class="num">0;
    class="type">class="kw">ulong ticket_history;
    for(class="type">uint j=class="num">0;j<totalHistory;j++){
      if((ticket_history=HistoryDealGetTicket(j))>class="num">0 ){
        class="type">class="kw">double profitHistory=HistoryDealGetDouble(ticket_history,DEAL_PROFIT);
        profitHistory+=HistoryDealGetDouble(ticket_history,DEAL_COMMISSION);
        profitHistory+=HistoryDealGetDouble(ticket_history,DEAL_SWAP);
        if(profitHistory!=class="num">0){
          currHistory+=profitHistory;
        }
      }
    }
    
  class="macro">#else
    class="type">int cntMyPos=OrdersHistoryTotal();
    if(cntMyPos>class="num">0){
      for(class="type">int ti=cntMyPos-class="num">1; ti>=class="num">0; ti--){
        if(OrderSelect(ti,SELECT_BY_POS,MODE_HISTORY)==false) class="kw">continue;
        if( OrderCloseTime()<StructToTime(curD) ){ class="kw">continue; }
        class="type">class="kw">double tmpProfit=OrderProfit();
        tmpProfit+=OrderSwap();
        tmpProfit+=OrderCommission();

日内盈亏与多空笔数的面板拼装

这段逻辑出现在统计函数末尾,负责把当天已平仓交易的汇总结果拼进提示字符串。它先对每笔临时盈亏 tmpProfit 做符号判断:大于 0 计入 tradePlus,小于 0 计入 tradeMinus,同时 tradeAll 自增、curProfit 累加,三者构成日内基础战绩。 外汇与贵金属品种波动随机性高,这类计数仅反映历史样本,不预示后续胜率,实盘须清醒认知高风险。 最后当 tradeAll>0 时,用 StringAdd 把『Today: 盈亏额 币种 (总笔数;盈利笔数;亏损笔数)』格式的文本追加进 msg,例如『Today: 12.50 USD (8;5;3)』。直接把这段接在你自己的统计函数后,开 MT5 用脚本跑一天便能看到面板输出。

MQL5 / C++
      tradeAll++;
      if(tmpProfit>class="num">0){
         tradePlus++;
      }else if(tmpProfit<class="num">0){
         tradeMinus++;
      }
      curProfit+=tmpProfit;
     }
   }
   class="macro">#endif

   if(tradeAll>class="num">0){
      StringAdd(msg, "Today: "+DoubleToString(curProfit, class="num">2)+" "+AccountInfoString(ACCOUNT_CURRENCY)+" ("+(class="type">class="kw">string) tradeAll+";+"+(class="type">class="kw">string) tradePlus+";-"+(class="type">class="kw">string) tradeMinus+")");
   }

   class="kw">return msg;
}

◍ 把这条线请下神坛

紫线标的是年高低、红线是自定义级、绿线是昨开昨收——这些在 finder 工具里只是辅助参照,不是决策依据。外汇和贵金属波动受流动性与事件驱动,任何一条画线单独看都可能误导,高杠杆下误读成本极高。 工具真正值钱的是 All/LONG/SHORT/Range 的筛选与 R 键手动刷新(默认 D1,改 M15 后可见日内四舍五入线 0.00/0.50 或 0.x1000/0.xx500)。点交易品种按钮进图,用导航键横跳,或塞进 homework 分组跟踪,比盯着某根线更实在。 ZIP 里 _finder.ex5 354.46 KB、_finder.mq5 258.68 KB,MT5 加载后先调「过滤设置」和「图表设置」两组参数,跑通自己的品种池再说。线只是地图,地图不等于地形。

常见问题

用 Gerchik's ATR 取近期平均真实波幅,按倍数在图表画出上下轨,价格触碰边界时日内反转概率偏高,但外汇贵金属高风险,仅作参考。
把和美指或避险资产联动强的品种放一起看,引导品种先动的方向往往暗示主交易品种倾向,开盘后对照确认再动手。
小布盯盘的 AIGC 已内置这类诊断,打开对应品种页就能看到单根跌幅提示串和当日盈亏交易笔数,不用自己写代码。
在脚本里把利率或就业数据作为参数打印进专家日志,每天开盘自动记录,后期翻日志就能对照行情找规律。
用导航工具的事件钩子在品种切换或新K线时触发刷新,选项卡会按设定重绘数据,省去来回点界面的麻烦。