价格直方图(市场概况)及其在 MQL5 中的实施·综合运用
(3/3)· 从平衡失衡识别到代码落地,前三篇铺垫的概念在这里被拼成完整交易拼图
◍ 用 tick 厚度挖出当日 POC 与次级区间
这段逻辑干的事很直接:把一天内的高低点按 _Point 拆成离散价格水平,再统计每个水平在 K 线里被 tick 穿过的次数,次数最高的就是成交量剖面里的 POC。外汇和贵金属市场跳空频繁,这种基于 tick 厚度的算法比单纯看收盘价分布更贴近真实成交密集区,但点值误差会直接扭曲层级索引,属于高风险品种下的高精度玩法。 核心先算层级总数:count = (high_day - low_day) / _Point + 1,然后 ArrayResize 出 ThicknessOfLevel 数组并清零。随后双层循环里,对每根 K 线从 iLow[i] 走到 iHigh[i],每跨一个 _Point 就让对应 Index 的计数加一,等于把每根 bar 的实体区间摊平到价格轴上。 找主 POC 只需 ArrayMaximum 取最大计数下标,poc_value = low_day + _Point * MaxLevel。次级 POC 区间则靠 range_percent 容忍度:低于主峰计数 range_min 的水平一律忽略,从两端向中间扫出 DownLine 和 UpLine,若扫空则回退到 MaxLevel,最终得到 second_poc_bottom / second_poc_top。 直方图输出那块有个细节:当 count > 100 时 step = count / 100,把剖面压缩到最多 100 行显示,避免 MT5 文本框被刷爆。Delta 用总时间减去一个 H1 周期再除以主峰计数,估算的是主 POC 上每单位厚度的平均时间跨度,可拿来肉眼判断堆积是快还是慢。
rates_total=CopyLow(NULL,PERIOD_M1,time_start,time_end,iLow); if(rates_total<class="num">0) PrintErrorOnCopyFunction("CopyLow",_Symbol,PERIOD_M1,GetLastError()); err++; } while((rates_time<=class="num">0 || (rates_total!=rates_high && rates_total!=rates_time)) && err<AMOUNT_OF_ATTEMPTS&&!IsStopped()); if(err>=AMOUNT_OF_ATTEMPTS) { class="kw">return(false); } poc_start_time=iTime[class="num">0]; high_day=iHigh[ArrayMaximum(iHigh,class="num">0,WHOLE_ARRAY)]; low_day=iLow[ArrayMinimum(iLow,class="num">0,WHOLE_ARRAY)]; class="type">int count=class="type">int((high_day-low_day)/_Point)+class="num">1; class=class="str">"cmt">// 对每一个水平,计算某一价格在此持续的时间 class="type">int ThicknessOfLevel[]; class=class="str">"cmt">// 创建一个数组用于计算ticks ArrayResize(ThicknessOfLevel,count); ArrayInitialize(ThicknessOfLevel,class="num">0); for(class="type">int i=class="num">0;i<rates_total;i++) { class="type">class="kw">double C=iLow[i]; while(C<iHigh[i]) { class="type">int Index=class="type">int((C-low_day)/_Point); ThicknessOfLevel[Index]++; C+=_Point; } } class="type">int MaxLevel=ArrayMaximum(ThicknessOfLevel,class="num">0,count); poc_value=low_day+_Point*MaxLevel; class=class="str">"cmt">// 查找次级POCs class="type">int range_min=ThicknessOfLevel[MaxLevel]-ThicknessOfLevel[MaxLevel]*range_percent/class="num">100; class="type">int DownLine=class="num">0; class="type">int UpLine=class="num">0; for(class="type">int i=class="num">0;i<count;i++) { if(ThicknessOfLevel[i]>=range_min) { DownLine=i; break; } } for(class="type">int i=count-class="num">1;i>class="num">0;i--) { if(ThicknessOfLevel[i]>=range_min) { UpLine=i; break; } } if(DownLine==class="num">0) DownLine=MaxLevel; if(UpLine==class="num">0) UpLine=MaxLevel; second_poc_top=low_day+_Point*UpLine; second_poc_bottom=low_day+_Point*DownLine; class=class="str">"cmt">// 直方图信息 if(show_histogram) { class="type">class="kw">datetime Delta=(iTime[rates_total-class="num">1]-iTime[class="num">0]-PeriodSeconds(PERIOD_H1))/ThicknessOfLevel[MaxLevel]; class="type">int step=class="num">1; if(count>class="num">100) step=count/class="num">100; class=class="str">"cmt">// 计算直方图的步长(最大100行) ListHistogramInner.Clear(); ListHistogramOuter.Clear(); for(class="type">int i=class="num">0;i<count;i+=step) {
用趋势线对象搭出成交量分布带
这段逻辑干的事,是把每个价位档画成一条水平趋势线,从 POC 起始时间拉到当前 bar 之后一段 Δ 距离。内层档(DownLine~UpLine 之间)归 ListHistogramInner,外层档归 ListHistogramOuter,颜色分开管理,方便后续区分「核心密集区」和「边缘稀薄区」。 string name=TimeToString(time_start)+" "+IntegerToString(i); double StartY= low_day+_Point*i; datetime EndX= iTime[0]+(ThicknessOfLevel[i])*Delta; CChartObjectTrend *obj=new CChartObjectTrend(); obj.Create(0,name,0,poc_start_time,StartY,EndX,StartY); obj.Background(true); if(i>=DownLine && i<=UpLine) { obj.Color(inner_color); ListHistogramInner.Add(obj); } else { obj.Color(outer_color); ListHistogramOuter.Add(obj); } 上面代码逐行拆:第1行用起始时间加档位序号拼出对象名,避免重名;第2行 StartY 是 low_day 加上 i 个点的偏移,即该档位对应的价格;第3行 EndX 把线尾推到最新 K 线时间再加 ThicknessOfLevel[i]*Delta,控制每条线长短;第4~5行新建趋势线对象并绑定到图表0、用四个坐标点画水平线;第6行 Background(true) 让它衬在蜡烛后面;后面 if-else 按档位内外决定着色与归队。 数据回填部分从直方图的末时间一路拉到当前 tick,用 CopyTime/CopyHigh/CopyLow 抓 M1 历史,失败重试上限由 AMOUNT_OF_ATTEMPTS 控制。若 rates_time==0 说明当天无任何触碰,标记 virgin=true 表示「原始水平」;否则扫一遍,只要某根 M1 K 线的高低价跨过 poc_value,就记下穿透时间 poc_end_time,否则也判为 virgin。 外汇与贵金属属高风险品种,这种「水平是否被触碰」的判断只反映历史概率,不能推定后续必破或必守。实盘前建议在 MT5 用策略测试器跑一段 XAUUSD 的 M1 数据,把 AMOUNT_OF_ATTEMPTS 调到 3~5 看对象创建会不会漏帧。
class="type">class="kw">string name=TimeToString(time_start)+" "+IntegerToString(i); class="type">class="kw">double StartY= low_day+_Point*i; class="type">class="kw">datetime EndX= iTime[class="num">0]+(ThicknessOfLevel[i])*Delta; CChartObjectTrend *obj=new CChartObjectTrend(); obj.Create(class="num">0,name,class="num">0,poc_start_time,StartY,EndX,StartY); obj.Background(true); if(i>=DownLine && i<=UpLine) { obj.Color(inner_color); ListHistogramInner.Add(obj); } else { obj.Color(outer_color); ListHistogramOuter.Add(obj); } class=class="str">"cmt">// 我们从直方图的最后时间开始接收数据,一直到当前时间 err=class="num">0; do { rates_time=CopyTime(NULL,PERIOD_M1,time_end,last_tick.time,iTime); rates_high=CopyHigh(NULL,PERIOD_M1,time_end,last_tick.time,iHigh); rates_total=CopyLow(NULL,PERIOD_M1,time_end,last_tick.time,iLow); err++; } while((rates_time<=class="num">0 || (rates_total!=rates_high && rates_total!=rates_time)) && err<AMOUNT_OF_ATTEMPTS); class=class="str">"cmt">// 如果没有历史数据,当前天是原始水平,则我们提升颜色 if(rates_time==class="num">0) { virgin=true; } else class=class="str">"cmt">// 否则我们检查历史数据 { for(index=class="num">0;index<rates_total;index++) if(poc_value<iHigh[index] && poc_value>iLow[index]) break; if(index<rates_total) class=class="str">"cmt">// 如果水平被穿越 poc_end_time=iTime[index]; else virgin=true; } if(POCLine==NULL) { POCLine=new CChartObjectTrend(); POCLine.Create(class="num">0,TimeToString(time_start)+" POC ",class="num">0,poc_start_time,poc_value,class="num">0,class="num">0); } POCLine.Color(poc_color); RefreshPOCs();
「直方图必须配确认信号才靠谱」
价格直方图这类工具本质上靠的是盘手直觉,信号本身容易骗线,实盘里必须叠加确认层——比如成交量突变、关键位假破或更高周期方向,单独看柱宽很容易在震荡里反复打脸。 有读者在 MT5 5.00 Build 3661(AMP Futures 版)编译 ClassPriceHistogram.mqh 时,第 375 行报错,手动替换一处符号后跑通,说明旧版头文件在新构建里存在兼容坑,下载附件包(pricehistogram.mq5 仅 6.75 KB)后先本地编译验证再挂实盘。 外汇与贵金属杠杆高、滑点猛,这类剖面工具只作概率参考,别当成进出场唯一依据;真要用的话,拿历史数据回放调一下分箱宽度,比盲目信「轮廓清晰=突破」更实在。
class="type">void CPriceHistogram::Delete()
◍ 把这条线请下神坛
原文第 104 行把 >= 改回 < 的修正,本质是把直方图判定逻辑从“达标”翻转为“未达标”,这类单行改动在 tickvolume 类指标里极易引发信号反转,验证时请直接开 MT5 调那一行看柱体颜色变化。
社区里用 tickvolume 做标普 500 差价合约、或换 realvolume 做 CME 期货,只是数据源切换,不改喷发点群和 DLL 绑定的底层结构;喷发靠多指标相交生成星云状点群,多货币 EA 只是把老 diversification 思路用最简框架落地。
外汇与贵金属自带高杠杆高风险,这类工具链再花哨也只是辅助,那条被神化的阈值线,调回小于号之后就该从神坛下来了。