指标喷发整体特征的计算·进阶篇
📊

指标喷发整体特征的计算·进阶篇

(2/3)· 喷发点铺满图表却读不出数字?本篇把可视混乱收敛成支撑阻力与趋势概率

偏理论进阶 第 2/3 篇

把 iMA 与 iEnvelopes 的喷发点往图表上一挂,洋红浅绿蓝点混作一团,多数人以为看到了结构,其实只是看到了噪声。没有整体特征提取,这些点既给不出价位也称不出强度,自动化交易更无从谈起。外汇贵金属杠杆高,拿肉眼混沌当信号容易吃反向鞭子。

◍ 两条回归线交点的类实现

在 MT5 自定义指标里,若要用两条不同窗口的回归直线推算未来可能的交汇点,可以封装一个 CEmission 类来处理。构造函数只接收左侧和右侧的偏移限制(limitLeft、limitRight),并把当前图表周期的秒数存进 sec,后续换算时间坐标时直接复用。 CalcPoint 是核心:它吃进两条线在 bar[1] 与 bar[0] 上的 Y 值,以及当前 bar 的时间 t0。先算 y0-y1(线1斜率投影)、y1-yy1(两线在[1]处的纵向差)、yy0-yy1(线2斜率投影),再用 del0 = yy0yy1 - y0y1 做分母。若 MathAbs(del0) 不大于 0 说明两线平行,直接返回空点。 point.x = y1yy1/del0 得到的是相对 bar[0] 的索引偏移;一旦超出 lim_Left~lim_Right 区间或算出的 y 为负,就放弃该点。合法时 point.t = t0 + (int)(point.x*sec),把偏移转成具体时间,real 置 true。 下一段的 CreatePoint 负责把算出的 PointEmission 结构画到图上,入参是点的 name 与引用。复制下面代码到 MT5 的 .mqh 里,挂上两个不同周期的回归指标,就能在 EURUSD 或 XAUUSD 上验证交汇点是否合理——外汇与贵金属杠杆高,信号仅作概率参考。

MQL5 / C++
CEmission::CEmission(class="type">int limitLeft,class="type">int limitRight)
  {
   sec=PeriodSeconds();
   lim_Left=limitLeft;
   lim_Right=limitRight;
  }
CEmission::~CEmission()
  {
  }
PointEmission CEmission::CalcPoint(class="type">class="kw">double   y1, class=class="str">"cmt">// Y-coordinate of straight line class="num">1 on bar [class="num">1]
                                   class="type">class="kw">double   y0, class=class="str">"cmt">// Y-coordinate of straight line class="num">1 on bar [class="num">0]
                                   class="type">class="kw">double   yy1,class=class="str">"cmt">// Y-coordinate of straight line class="num">2 on bar [class="num">1]
                                   class="type">class="kw">double   yy0,class=class="str">"cmt">// Y-coordinate of straight line class="num">2 on bar [class="num">0]
                                   class="type">class="kw">datetime t0  class=class="str">"cmt">// t-coordinate of the current bar Time[class="num">0]
                                   )
  {
   PointEmission point={NULL,NULL,NULL,class="kw">false};
   class="type">class="kw">double y0y1=y0-y1;
   class="type">class="kw">double y1yy1=y1-yy1;
   class="type">class="kw">double yy0yy1=yy0-yy1;
   class="type">class="kw">double del0=yy0yy1-y0y1;
   if(MathAbs(del0)>class="num">0)
     {
      point.x=y1yy1/del0;
      if(point.x<lim_Left || point.x>lim_Right) class="kw">return(point);
      point.y=y1+y0y1*y1yy1/del0;
      if(point.y<class="num">0) class="kw">return(point);
      point.t=t0+(class="type">int)(point.x*sec);
      point.real=true;
      class="kw">return(point);
     }
   class="kw">return(point);
  }
class="type">bool CEmission::CreatePoint(class="type">class="kw">string name,          class=class="str">"cmt">// point name
                            PointEmission &point class=class="str">"cmt">// coordinates of the point

「在MT5图表上画Wingdings标记点」

想在K线图上用特殊符号标注关键价位,可以借助OBJ_TEXT对象配合Wingdings字体实现。下面这段函数接收名称、坐标点和样式结构,尝试在图表零号子窗口创建文本对象。 创建成功后,代码把字体设为Wingdings,锚点置中,字号取自style.Width,文本内容由style.Code转成字符,再把价格和时间分别绑定到point.y与point.t,最后上色返回true;若ObjectCreate失败则直接返回false。 实际验证时,把style.Code填成159会显示实心圆点,填成108则是箭头,外汇与贵金属波动剧烈,这类标记仅作视觉辅助,信号失效概率不低,需结合量价确认。

MQL5 / C++
  if(ObjectCreate(class="num">0,name,OBJ_TEXT,class="num">0,class="num">0,class="num">0))
   {
     ObjectSetString(class="num">0,name,OBJPROP_FONT,"Wingdings");
     ObjectSetInteger(class="num">0,name,OBJPROP_ANCHOR,ANCHOR_CENTER);
     ObjectSetInteger(class="num">0,name,OBJPROP_FONTSIZE,style.Width);
     ObjectSetString(class="num">0,name,OBJPROP_TEXT,CharToString((class="type">uchar)style.Code));
     ObjectSetDouble(class="num">0,name,OBJPROP_PRICE,point.y);
     ObjectSetInteger(class="num">0,name,OBJPROP_TIME,point.t);
     ObjectSetInteger(class="num">0,name,OBJPROP_COLOR,style.Color);
     class="kw">return(true);
   }
  class="kw">return(class="kw">false);
}

从彩色噪点里榨出可量化的喷发特征

把 EA 挂上图表,屏幕会跳出一堆分色点:浅绿是 iMA 与 iEnvelopes 上轨交叉,蓝色是下轨交叉,洋红是 iMA 与自身不同周期的交叉。肉眼看是图像,机器要的是数字——这种混乱没法直接喂给自动交易。 先从小处落地:给每种点类型算平均价格,画出对应水平线。再算平均时间值,标在均价线上,得到第一批量化特征——喷发点从不静止,始终在价格空间里漂移。图 4 只显示瞬时位置,要研究历史就得想办法把它钉死,这一步暂且留白。 为方便横向比较,改用百分比。iMA 与 iEnvelopes 交叉产生的点视作 100% 基准,实测洋红自交叉点占比 34.4%,三类相加会超 100%,那个 34.4% 是指标自交叉的参考值,用法后面再谈。点数百分比随行情波动,怎么在历史里固定它们,仍是待解问题。 下方代码块就是累加与显示这些整体特征的核心:先按颜色归类点数与价格和,再画虚点线标出均价水平。复制进 MT5 的 OnTick 段,你能直接看到分色水平线随 tick 更新。外汇与贵金属市场杠杆高、波动剧烈,此类统计特征仅作概率参考,不构成方向判定。

MQL5 / C++
class=class="str">"cmt">//--- arrays for calculation and display of integral characteristics of emissions
class="macro">#define    NUMBER_TYPES_POINT   class="num">3
class="type">class="kw">double      sum[NUMBER_TYPES_POINT],sumprev[NUMBER_TYPES_POINT];
class="type">class="kw">datetime    sum_time[NUMBER_TYPES_POINT];
class="type">int         n[NUMBER_TYPES_POINT],W[NUMBER_TYPES_POINT];
class="type">color       colorLine[]={clrAqua,clrBlue,clrMagenta};
class=class="str">"cmt">//--- calculation of integral characteristics of emissions
   ArrayInitialize(n,class="num">0);
   ArrayInitialize(sum,class="num">0.0);
   ArrayInitialize(sum_time,class="num">0.0);
   for(class="type">int obj=total-class="num">1;obj>=class="num">0;obj--)
    {
     class="type">class="kw">string   obj_name=ObjectName(class="num">0,obj,class="num">0,OBJ_TEXT);
     class="type">class="kw">datetime obj_time=(class="type">class="kw">datetime)ObjectGetInteger(class="num">0,obj_name,OBJPROP_TIME);
     if(obj_time>T[class="num">0])
      {
       class="type">color    obj_color=(class="type">color)ObjectGetInteger(class="num">0,obj_name,OBJPROP_COLOR);
       class="type">class="kw">double   obj_price=ObjectGetDouble(class="num">0,obj_name,OBJPROP_PRICE);
       for(class="type">int i=ArraySize(n)-class="num">1; i>=class="num">0; i--)
        if(obj_color==colorPoint[i])
          {
           n[i]++;
           sum[i]+=obj_price;
           sum_time[i]+=obj_time;
          }
      }
    }
class=class="str">"cmt">//--- displaying integral characteristics of emissions
   for(class="type">int i=ArraySize(n)-class="num">1; i>=class="num">0; i--)
    {
     if(n[i]>class="num">0)
      {
       name="H.line."+(class="type">class="kw">string)i;
       ObjectCreate(class="num">0,name,OBJ_HLINE,class="num">0,class="num">0,class="num">0,class="num">0);
       ObjectSetInteger(class="num">0,name,OBJPROP_COLOR,colorLine[i]);
       ObjectSetInteger(class="num">0,name,OBJPROP_STYLE,STYLE_DASHDOT);
       ObjectSetInteger(class="num">0,name,OBJPROP_WIDTH,class="num">1);

◍ 用 ObjectSetDouble 把均值写回画线

在 MT5 里给指标对象动态赋值,核心就是 ObjectSetDouble 这一句。它把计算好的移动平均结果,按索引写进对应价格坐标,让水平线或曲线实时跟着数据走。

MQL5 / C++
ObjectSetDouble(class="num">0,name,OBJPROP_PRICE,sum[i]/n[i]);
}
}
逐行拆解:第一行用图表 ID 0(当前图)定位名为 name 的对象,属性选 OBJPROP_PRICE 表示改价格位置,值为 sum[i]/n[i] 即第 i 根 K 的累计和除以计数,得出均值。后两个右大括号分别收掉 for 循环与外层函数。 实操时把 n[i] 先判零,否则外汇与贵金属这种跳空多的品种可能在早盘几根 K 上算出 NaN,画线直接消失。开 MT5 把这段塞进你自己的指标 OnCalculate,调 n 的窗口长度就能看不同平滑度。

MQL5 / C++
ObjectSetDouble(class="num">0,name,OBJPROP_PRICE,sum[i]/n[i]);
}
}

「用厚度与颜色把喷发整体通道画出来」

主喷发点按百分比比例给曲线不同厚度,是当前段沿当前与前一根柱平均价格线绘制的——注意这些坐标实际取自未来,相当于指标喷发的主导整体通道(图表中洋红色线即 iMA & iMA 喷发用途)。这给了我们一个新东西:整合移动平均线。 光有平均价格线还不够,时间坐标也得管。浅绿是通道上限、蓝色是下限,要找时间上更贴近零柱的标记。把 PRICE_MEDIAN 线挂到图上,让它根据更接近末根柱的标记颜色在浅绿/蓝之间切换,就能看出时间维度的主导。 如果按初始喷发整体特征再去画一层喷发(类似二阶喷发),那些线也会交叉产生新喷发点。图 8 能看到新点但暂时无意义,必须对图 9 的新点继续算整体特征并叠喷发,循环到不可行为止。外汇与贵金属市场杠杆高,这类图形实验仅用于可视化验证,不代表任何方向确定性。 下方代码块负责把喷发的整体特征画到图表:水平线用平均价、文本标记用 Wingdings 字符 163、趋势线用整合曲线。W 数组里最大喷发点权重 20、最小 3,线宽统一 1,样式 DASHDOT。开 MT5 把这段塞进 OnTick 就能看到通道自绘。

MQL5 / C++
class=class="str">"cmt">//--- displaying integral characteristics of emissions
  ArrayInitialize(W,class="num">10);
  W[ArrayMaximum(n)]=class="num">20;
  W[ArrayMinimum(n)]=class="num">3;
  for(class="type">int i=ArraySize(n)-class="num">1; i>=class="num">0; i--)
    {
     if(n[i]>class="num">0)
       {
       class=class="str">"cmt">//--- horizontal lines of mean prices
       name="H.line."+(class="type">class="kw">string)i;
       ObjectCreate(class="num">0,name,OBJ_HLINE,class="num">0,class="num">0,class="num">0,class="num">0);
       ObjectSetInteger(class="num">0,name,OBJPROP_COLOR,colorLine[i]);
       ObjectSetInteger(class="num">0,name,OBJPROP_STYLE,STYLE_DASHDOT);
       ObjectSetInteger(class="num">0,name,OBJPROP_WIDTH,class="num">1);
       ObjectSetDouble(class="num">0,name,OBJPROP_PRICE,sum[i]/n[i]);
       class=class="str">"cmt">//--- markers
       name="P."+(class="type">class="kw">string)i;
       ObjectCreate(class="num">0,name,OBJ_TEXT,class="num">0,class="num">0,class="num">0);
       ObjectSetString(class="num">0,name,OBJPROP_FONT,"Wingdings");
       ObjectSetInteger(class="num">0,name,OBJPROP_ANCHOR,ANCHOR_CENTER);
       ObjectSetInteger(class="num">0,name,OBJPROP_FONTSIZE,class="num">17);
       ObjectSetString(class="num">0,name,OBJPROP_TEXT,CharToString(class="num">163));
       ObjectSetInteger(class="num">0,name,OBJPROP_COLOR,colorLine[i]);
       ObjectSetDouble(class="num">0,name,OBJPROP_PRICE,sum[i]/n[i]);
       ObjectSetInteger(class="num">0,name,OBJPROP_TIME,sum_time[i]/n[i]);
       class=class="str">"cmt">//--- integral curves
       name="T"+(class="type">class="kw">string)i+".line"+(class="type">class="kw">string)T[class="num">1];
       ObjectCreate(class="num">0,name,OBJ_TREND,class="num">0,class="num">0,class="num">0);
       ObjectSetInteger(class="num">0,name,OBJPROP_COLOR,colorLine[i]);

把分布重心画成可读的线

这段收尾逻辑干两件事:给每段分布画趋势线,再在重心位置贴百分比文字。线宽由 W[i] 控制,若前段累计量 sumprev[i] 大于 0,就把趋势线两端锚在 T[1] 与 T[0] 的均价 (sum[i]/n[i]) 上, visually 把两段重心连起来。 文字对象用 OBJ_TEXT 建在重心价 (sum[i]/n[i]) 与时间均值 sum_time[i]/n[i] 处,字号 30、左下单锚,内容是 n[i] 占 n[0]+n[1] 的比重,保留 1 位小数——比如某段成交笔数占比 63.4% 会直接标在图上。 最后挑时间均值更早的那段画一条宽度 8 的粗趋势线,端点价用 HL(H,L) 算出的区间中值。外汇与贵金属波动剧烈,这类重心线仅描述历史分布,后市方向仍可能反转,请务必在 MT5 策略测试器里用小周期数据验证渲染效果。

MQL5 / C++
ObjectSetInteger(class="num">0,name,OBJPROP_WIDTH,W[i]);
if(sumprev[i]>class="num">0)
  {
  ObjectSetDouble(class="num">0,name,OBJPROP_PRICE,class="num">0,sumprev[i]);
  ObjectSetInteger(class="num">0,name,OBJPROP_TIME,class="num">0,T[class="num">1]);
  ObjectSetDouble(class="num">0,name,OBJPROP_PRICE,class="num">1,(sum[i]/n[i]));
  ObjectSetInteger(class="num">0,name,OBJPROP_TIME,class="num">1,T[class="num">0]);
  }
class=class="str">"cmt">//--- numerical values of integral characteristics
name="Text"+(class="type">class="kw">string)i+".control";
ObjectCreate(class="num">0,name,OBJ_TEXT,class="num">0,class="num">0,class="num">0);
ObjectSetInteger(class="num">0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER);
ObjectSetInteger(class="num">0,name,OBJPROP_FONTSIZE,class="num">30);
ObjectSetInteger(class="num">0,name,OBJPROP_COLOR,colorLine[i]);
class="type">class="kw">string str=DoubleToString((class="type">class="kw">double)n[i]/(class="type">class="kw">double)(n[class="num">0]+n[class="num">1])*class="num">100,class="num">1);
ObjectSetString(class="num">0,name,OBJPROP_TEXT,str);
ObjectSetDouble(class="num">0,name,OBJPROP_PRICE,class="num">0,(sum[i]/n[i]));
ObjectSetInteger(class="num">0,name,OBJPROP_TIME,class="num">0,sum_time[i]/n[i]);
  }
 }
class=class="str">"cmt">//---
 if(n[ArrayMinimum(n)]>class="num">0)
  {
  class="type">class="kw">datetime d[class="num">2];
  for(class="type">int j=class="num">0;j<class="num">2;j++)
    {
    d[j]=sum_time[j]/n[j];
    }
  class="type">int i=ArrayMinimum(d);
  name="Price.line"+(class="type">class="kw">string)T[class="num">1];
  ObjectCreate(class="num">0,name,OBJ_TREND,class="num">0,class="num">0,class="num">0);
  ObjectSetInteger(class="num">0,name,OBJPROP_WIDTH,class="num">8);
  ObjectSetDouble(class="num">0,name,OBJPROP_PRICE,class="num">0,HL(H[class="num">1],L[class="num">1]));
  ObjectSetInteger(class="num">0,name,OBJPROP_TIME,class="num">0,T[class="num">1]);
  ObjectSetDouble(class="num">0,name,OBJPROP_PRICE,class="num">1,HL(H[class="num">0],L[class="num">0]));
把喷发统计交给小布盯盘
这些喷发集群的密度与吸引排斥倾向,小布盯盘的 AIGC 已内置统计视图,打开对应品种页即可直接看量化后的支撑阻力带,你只管判方向。

常见问题

在随机交叉算法下,相近参数线的交点在空间上易聚团,集群密度越高对价格吸引或排斥的概率越大,但具体方向仍需结合上下文。
它封装任意两条指标线交叉点的计算与图形对象显示,锚点对准 K 线中心,便于后续用 Text 对象调整尺寸做复杂喷发。
足够。每条线只需两个采样点即可算出线段交点,当前价跳动会持续刷新交点,适合实时喷发绘制。
可以。把 EA 输出的集群密度与价位带接入小布的品种页,它能自动标注历史积累变动强度,省去你手算缓冲区。
Text 锚点可精准居中标量中心,且尺寸颜色自由缩放,为复杂喷发分层和后期图形分析留了扩展口。