指标喷发整体特征的计算(基础篇)
指标喷发强度的整体刻画
在 MT5 里做价格行为分析时,单看某一根 K 线的指标突变容易误判,更稳妥的做法是把「喷发」当作一段时间内的整体特征来算。所谓喷发,通常指指标值在短时间内偏离其近期均值达到统计显著的距离,这类现象在贵金属与外汇的跳空时段尤其密集。 以 Sergey Pavlov 在 2014-03-10 发布的公开示例为例,该指标累计被社区查看 4685 次、评论 9 条,说明这类「整体特征计算」的需求在实盘群体里一直有存在感。你可以在 MT5 自带的自定义指标编辑器里,把一段回看窗口内的指标极值差除以平均绝对偏差,得到一个无量纲的喷发系数。 外汇与贵金属杠杆高、滑点随机,喷发系数只描述「偏离强度」,不预示方向;系数超过 3 倍历史中位数的窗口,后续波动率倾向放大,但是否进场仍要看结构。
◍ 喷发到底是哪根线上的什么点
指标喷发把分析重心从指标线本身挪到了它向过去或未来“喷”出的点集上。一个喷发点,本质是所选指标特定线在某个交叉处生成的坐标;同类点有抱团倾向,密集簇可能吸引价格,也可能把价格推离,这是后续做支撑阻力预判的底层逻辑。 实际标绘时,线数和平均周期、方差都是随机挑的,只要两条线在空间有交叉就能出喷发。上面这段代码用 iMA 与 iEnvelopes 做底:ENV 数组列了 11 个包络偏差,从 0.01 到 1.5237;MA 数组是 7 条均线周期 4/7/11/19/31/51/85,ma_period 固定为 140。CEmission 实例 EnvMa(0,300) 负责两条线交叉点的计算与显示。 喷发点用 Text 这类图形对象画,锚点要对准 K 线中心,尺寸可大范围调,这给“复杂喷发”留了很大扩展空间。外汇和贵金属波动大、杠杆高,这类标绘只是概率层面的结构参考,不能直接当入场信号。
class=class="str">"cmt">//--- external variable for storing averaging period of the iEnvelopes indicator input class="type">int ma_period=class="num">140; class=class="str">"cmt">// averaging period of the iEnvelopes indicator class=class="str">"cmt">//--- array for storing deviations of the iEnvelopes indicator class="type">class="kw">double ENV[]={class="num">0.01,class="num">0.0165,class="num">0.0273,class="num">0.0452,class="num">0.0747,class="num">01234,class="num">0.204,class="num">0.3373,class="num">0.5576,class="num">0.9217,class="num">1.5237}; class=class="str">"cmt">//--- array for storing iMA indicator periods class="type">int MA[]={class="num">4,class="num">7,class="num">11,class="num">19,class="num">31,class="num">51,class="num">85}; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| emission_of_MA_envelope.mq5 | class=class="str">"cmt">//| Copyright class="num">2013, DC2008 | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2013, DC2008" class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//--- class="macro">#include <GetIndicatorBuffers.mqh> class="macro">#include <Emission.mqh> class=class="str">"cmt">//--- external variable for storing averaging period of the iEnvelopes indicator input class="type">int ma_period=class="num">140; class=class="str">"cmt">// averaging period of the iEnvelopes indicator class=class="str">"cmt">//--- array for storing deviations of the iEnvelopes indicator class="type">class="kw">double ENV[]={class="num">0.01,class="num">0.0165,class="num">0.0273,class="num">0.0452,class="num">0.0747,class="num">01234,class="num">0.204,class="num">0.3373,class="num">0.5576,class="num">0.9217,class="num">1.5237}; class=class="str">"cmt">//--- array for storing the iMA indicator periods class="type">int MA[]={class="num">4,class="num">7,class="num">11,class="num">19,class="num">31,class="num">51,class="num">85}; class=class="str">"cmt">//--- array for storing pointers to the iMA and iEnvelopes indicators class="type">int handle_MA[]; class="type">int handle_Envelopes[]; class=class="str">"cmt">//--- market data class="type">class="kw">datetime T[],prevTimeBar=class="num">0; class="type">class="kw">double H[],L[]; class="macro">#define HL(a, b) (a+b)/class="num">2 class=class="str">"cmt">//--- class instances CEmission EnvMa(class="num">0,class="num">300); PointEmission pEmission; class=class="str">"cmt">//--- drawing styles for points of emission class="macro">#define COLOR_UPPER C&class="macro">#x27;class="num">51,class="num">255,class="num">255&class="macro">#x27;
「初始化里把均线和轨道指针一次建好」
EA 启动阶段最该做干净的事,是把后续 tick 要反复调用的指标句柄先建好,而不是每次报价都去动态申请。下面这段代码在 OnInit 里同时挂了两组指标:一组是多条周期不同的 SMA(中位数价),一组是包络带(Envelopes)。 颜色宏先定调:上轨用 C'0,51,255'(蓝),下轨 C'0,51,255' 实际是蓝、品红 MA 用 C'255,51,255',三个颜色写进 colorPoint 数组,配合 styleUpper / styleLower / styleMA 里统一的 158 号箭头、SMALL 尺寸,图表上三类点一眼能分。 建句柄的循环很直白:先按 MA 数组长度 resize handle_MA,再 iMA(NULL,0,MA[i],0,MODE_SMA,PRICE_MEDIAN) 逐条拿句柄;任一返回负值就 Print 出错周期并打印 GetLastError(),直接 return(-1) 终止加载。ENV 数组同理走 iEnvelopes,参数里 ma_period 和 ENV[i] 偏移百分比是外部传进来的,没写死。 OnDeinit 这里是空壳,没做句柄释放——MT5 终端退出时会自动回收,但如果你打算热重载参数,得自己补清理。OnTick 函数体被截断,真正的信号判断在后面章节。外汇和贵金属杠杆高,这类多指标 EA 在滑点扩大时可能漏信号,上 MT5 用策略测试器跑历史段先验证句柄是否零报错。
class="macro">#define COLOR_LOWER C&class="macro">#x27;class="num">0,class="num">51,class="num">255&class="macro">#x27; class="macro">#define COLOR_MA C&class="macro">#x27;class="num">255,class="num">51,class="num">255&class="macro">#x27; class="type">color colorPoint[]={COLOR_UPPER,COLOR_LOWER,COLOR_MA}; CodeColor styleUpper={class="num">158,COLOR_UPPER,SMALL}; CodeColor styleLower={class="num">158,COLOR_LOWER,SMALL}; CodeColor styleMA={class="num">158,COLOR_MA,SMALL}; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { ArraySetAsSeries(T,true); ArraySetAsSeries(H,true); ArraySetAsSeries(L,true); class=class="str">"cmt">//--- class="type">int size=ArraySize(MA); ArrayResize(handle_MA,size); class=class="str">"cmt">//--- create a pointer to the object - the iMA indicator for(class="type">int i=class="num">0; i<size; i++) { handle_MA[i]=iMA(NULL,class="num">0,MA[i],class="num">0,MODE_SMA,PRICE_MEDIAN); class=class="str">"cmt">//--- if an error occurs when creating the object, print the message if(handle_MA[i]<class="num">0) { Print("The iMA object[",MA[i],"] has not been created: Error = ",GetLastError()); class=class="str">"cmt">//--- forced program termination class="kw">return(-class="num">1); } } class=class="str">"cmt">//--- size=ArraySize(ENV); ArrayResize(handle_Envelopes,size); class=class="str">"cmt">//--- create a pointer to the object - the iEnvelopes indicator for(class="type">int i=class="num">0; i<size; i++) { handle_Envelopes[i]=iEnvelopes(NULL,class="num">0,ma_period,class="num">0,MODE_SMA,PRICE_MEDIAN,ENV[i]); class=class="str">"cmt">//--- if an error occurs when creating the object, print the message if(handle_Envelopes[i]<class="num">0) { Print("The iEnvelopes object[",ENV[i],"] has not been created: Error = ",GetLastError()); class=class="str">"cmt">//--- forced program termination class="kw">return(-class="num">1); } } class=class="str">"cmt">//--- class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { class=class="str">"cmt">//--- } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick()
抓取轨道与均线交叠的坐标
在 MT5 的 EA 或脚本里,先把当前品种、当前周期最近两根 K 线的时间、最高、最低抓进数组,这是后续所有几何计算的坐标基准。 CopyTime(NULL,0,0,2,T) 取 2 根 Bar 的时间戳,CopyHigh / CopyLow 同理取高低价;只用 2 根是因为交叠检测只依赖相邻两根的线性插值,数据量越小重绘越快。 下面这段把 iMA 与 iEnvelopes 的上下轨缓冲分别读入 ibMA[] 和 ibEnvelopesUpper[] / ibEnvelopesLower[],注意 CopyBufferAsSeries 最后一个参数 true 表示按时间序列正序填充,避免和 T[0] 对位错乱。 双重循环里对每一对 (均线 i, 轨道 j) 调用 EnvMa.CalcPoint,用上轨或下轨的两根值加均线两根值去解交点;若 pEmission.real 为真,就用含 GTC 毫秒数的 name 把点画到图上,保证同帧多次绘制不重名。 外汇与贵金属杠杆高,这类交点只是价格行为的几何标记,不代表方向确认,实盘前请在策略测试器用历史数据验证命中率。
{
class=class="str">"cmt">//--- market data
CopyTime(NULL,class="num">0,class="num">0,class="num">2,T);
CopyHigh(NULL,class="num">0,class="num">0,class="num">2,H);
CopyLow(NULL,class="num">0,class="num">0,class="num">2,L);
class=class="str">"cmt">//--- fill the declared arrays with current values from all indicator buffers
class="type">class="kw">string name;
class="type">uint GTC=GetTickCount();
class=class="str">"cmt">//---- indicator buffers
class="type">class="kw">double ibMA[],ibMA1[]; class=class="str">"cmt">// arrays for the iMA indicator
class="type">class="kw">double ibEnvelopesUpper[]; class=class="str">"cmt">// array for the iEnvelopes indicator(UPPER_LINE)
class="type">class="kw">double ibEnvelopesLower[]; class=class="str">"cmt">// array for the iEnvelopes indicator(LOWER_LINE)
for(class="type">int i=ArraySize(handle_MA)-class="num">1; i>=class="num">0; i--)
{
if(!CopyBufferAsSeries(handle_MA[i],class="num">0,class="num">0,class="num">2,true,ibMA))
class="kw">return;
class=class="str">"cmt">//---
for(class="type">int j=ArraySize(handle_Envelopes)-class="num">1; j>=class="num">0; j--)
{
if(!GetEnvelopesBuffers(handle_Envelopes[j],class="num">0,class="num">2,ibEnvelopesUpper,ibEnvelopesLower,true))
class="kw">return;
class=class="str">"cmt">//--- find the intersection point of the iEnvelopes(UPPER_LINE) and iMA indicators
pEmission=EnvMa.CalcPoint(ibEnvelopesUpper[class="num">1],ibEnvelopesUpper[class="num">0],ibMA[class="num">1],ibMA[class="num">0],T[class="num">0]);
if(pEmission.real) class=class="str">"cmt">// if the intersection point is found, draw it in the chart
{
name="iEnvelopes(UPPER_LINE)"+(class="type">class="kw">string)j+"=iMA"+(class="type">class="kw">string)i+(class="type">class="kw">string)GTC;
EnvMa.CreatePoint(name,pEmission,styleUpper);
}
class=class="str">"cmt">//--- find the intersection point of the iEnvelopes(LOWER_LINE) and iMA indicators
pEmission=EnvMa.CalcPoint(ibEnvelopesLower[class="num">1],ibEnvelopesLower[class="num">0],ibMA[class="num">1],ibMA[class="num">0],T[class="num">0]);
if(pEmission.real) class=class="str">"cmt">// if the intersection point is found, draw it in the chart
{
name="iEnvelopes(LOWER_LINE)"+(class="type">class="kw">string)j+"=iMA"+(class="type">class="kw">string)i+(class="type">class="kw">string)GTC;
EnvMa.CreatePoint(name,pEmission,styleLower);
}
}
class=class="str">"cmt">//---
for(class="type">int j=ArraySize(handle_MA)-class="num">1; j>=class="num">0; j--)
{
if(i!=j)
{
if(!CopyBufferAsSeries(handle_MA[j],class="num">0,class="num">0,class="num">2,true,ibMA1))
class="kw">return;◍ 双均线交点落点与图表清理
这段逻辑干两件事:先算两条 iMA 指标在当前与上一根 K 线的交点,命中就在图上画点;再每根 K 线清一次旧对象,避免文本堆满画布。 交点计算靠 EnvMa.CalcPoint,喂入的是两条均线在 [1](前一根)和 [0](当前根)的报价,以及当前时间 T[0]。返回结构体里 real 为 true 才说明真交上了,此时拼一个带计数后缀的 name,调 CreatePoint 把标记钉在图上去。 清理段用 ObjectsTotal(0,0,-1) 拿全量对象数,只在 T[0] 大于 prevTimeBar(即新 BAR 成型)时跑一次。倒序遍历所有 OBJ_TEXT,凡时间早于当前 BAR 时间的直接 ObjectDelete 抹掉,最后 Comment 把残留对象数打印在左上角。 BIG 与 SMALL 两个宏分别定成 7 和 3,是交点标记两种尺寸,后面画点风格会按信号强弱二选一。外汇与贵金属杠杆高,这类辅助标记只帮你定位,不等于方向确认,实盘前请在 MT5 策略测试器里先跑一遍看对象数是否随 BAR 归零。
class=class="str">"cmt">//--- find the intersection point of the iMA and iMA indicators pEmission=EnvMa.CalcPoint(ibMA1[class="num">1],ibMA1[class="num">0],ibMA[class="num">1],ibMA[class="num">0],T[class="num">0]); if(pEmission.real) class=class="str">"cmt">// if the intersection point is found, draw it in the chart { name="iMA"+(class="type">class="kw">string)j+"=iMA"+(class="type">class="kw">string)i+(class="type">class="kw">string)GTC; EnvMa.CreatePoint(name,pEmission,styleMA); } } } } class=class="str">"cmt">//--- deletion of the graphical objects of emission not to stuff the chart if(T[class="num">0]>prevTimeBar) class=class="str">"cmt">// class="kw">delete once per bar { class="type">int total=ObjectsTotal(class="num">0,class="num">0,-class="num">1); prevTimeBar=T[class="num">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]) ObjectDelete(class="num">0,obj_name); } Comment("Emission © DC2008 Objects = ",total); } class=class="str">"cmt">//--- } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Emission.mqh | class=class="str">"cmt">//| Copyright class="num">2013, DC2008 | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2013, DC2008" class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#define BIG class="num">7 class=class="str">"cmt">// point size class="macro">#define SMALL class="num">3 class=class="str">"cmt">// point size class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| pMABB structure | class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">struct PointEmission { class="type">class="kw">double x; class=class="str">"cmt">// X-coordinate of the time point
「射线交点的结构体与基类实现」
把两条射线在相邻两根 bar 上的端点坐标喂进去,就能算出它们的理论交点。下面这套结构体先把「点」和「样式」拆开存,避免绘图逻辑和坐标计算耦合。 PointEmission 里 y 是价格坐标、t 是时间坐标、real 标记该点是否真实存在(比如平行线永不相交时 real=false)。CodeColor 则绑定了符号代码、颜色和线宽,方便同一类信号复用视觉风格。 CEmission 作为基类,用 lim_Left / lim_Right 限制信号只在最近若干根 bar 内可见,防止历史图形堆积拖慢 MT5 图表刷新。CalcPoint 接收 y1,y0(线1在[1]、[0]的Y值)与 yy1,yy0(线2对应值)及当前 bar 时间 t0,返回交点;CreatePoint 负责按 name 和 style 真正画到图上。 开 MT5 新建 EA 把这段直接贴进头文件,改 lim_Left=50、lim_Right=0,就能只显示最近 50 根 bar 内的射线交点,外汇与贵金属波动剧烈,这类信号仅作辅助、实战需自担高风险。
class="type">class="kw">double y; class=class="str">"cmt">// Y-coordinate of the price point class="type">class="kw">datetime t; class=class="str">"cmt">// t-coordinate of the point&class="macro">#x27;s time class="type">bool real; class=class="str">"cmt">// whether the point exists }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CodeColor structure | class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">struct CodeColor { class="type">long Code; class=class="str">"cmt">// point symbol code class="type">color Color; class=class="str">"cmt">// point class="type">color class="type">int Width; class=class="str">"cmt">// point size }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Base class for emissions | class=class="str">"cmt">//+------------------------------------------------------------------+ class CEmission { class="kw">private: class="type">int sec; class="type">int lim_Left; class=class="str">"cmt">// limiting range of visibility in bars class="type">int lim_Right; class=class="str">"cmt">// limiting range of visibility in bars class="kw">public: PointEmission 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] ); class="type">bool CreatePoint(class="type">class="kw">string name, class=class="str">"cmt">// point name PointEmission &point, class=class="str">"cmt">// coordinates of the point CodeColor &style); class=class="str">"cmt">// point drawing style CEmission(class="type">int limitLeft,class="type">int limitRight); ~CEmission(); };