如何利用 MQL5 检测趋势和图表形态·综合运用
(3/3)· 把前两份基础铺垫收口,用一套 MQL5 程序把趋势与经典反转形态一次跑通
「双底识别的画线逻辑与高低点回溯」
在 MT5 脚本里标记双底,第一步是把两个低点连成趋势线。下面这段用 ObjectCreate 画了一条名为 bottomLine 的 OBJ_TREND,右向射线开启,线宽设 3、颜色绿,方便肉眼在图上直接看支撑区。 ObjectCreate(0,"bottomLine",OBJ_TREND,0,iTime(Symbol(),Period(),low2),iLow(Symbol(),Period(),low2),iTime(Symbol(),Period(),low1),iLow(Symbol(),Period(),low1)); ObjectSetInteger(0,"bottomLine",OBJPROP_COLOR,clrGreen); ObjectSetInteger(0,"bottomLine",OBJPROP_WIDTH,3); ObjectSetInteger(0,"bottomLine",OBJPROP_RAY_RIGHT,true); 判定条件很直白:当 lowVal1>=lowVal2 且 highVal1<highVal2,视为潜在双底(Potential Double Bottom);若 highVal1 也突破前高 highVal2,则标为确认双底(Double Bottom)。这两个条件都用 Comment 把当前/前次高低点数值打印到图表左上角,省去肉眼比对。 高低点定位靠两个辅助函数。getNextMove 用 iHighest / iLowest 在指定 count 根 K 线内取极值偏移;getmove 则循环调用 getNextMove,直到返回的极值位置与当前柱一致,相当于在 ZigZag 式步长下锁定某一腿的拐点。外汇与贵金属波动大,双底失效概率不低,实盘前务必在策略测试器用历史数据验证触发频率。
ObjectCreate(class="num">0,"bottomLine",OBJ_TREND,class="num">0,iTime(Symbol(),Period(),low2),iLow(Symbol(),Period(),low2),iTime(Symbol(),Period(),low1),iLow(Symbol(),Period(),low1)); ObjectSetInteger(class="num">0,"bottomLine",OBJPROP_COLOR,clrGreen); ObjectSetInteger(class="num">0,"bottomLine",OBJPROP_WIDTH,class="num">3); ObjectSetInteger(class="num">0,"bottomLine",OBJPROP_RAY_RIGHT,true); if(lowVal1>=lowVal2&&highVal1<highVal2) { Comment("Potential Double Bottom", "\nCurrent High ",highVal1,"\nPrevious High ",highVal2, "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2); } else if(lowVal1>=lowVal2&&highVal1>highVal2) { Comment("Double Bottom", "\nCurrent High ",highVal1,"\nPrevious High ",highVal2, "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2); } else Comment(" "); } class="type">int getmove(class="type">int move, class="type">int count, class="type">int startPos) { if(move!=MODE_HIGH && move!=MODE_LOW) class="kw">return (-class="num">1); class="type">int currentBar=startPos; class="type">int moveReturned=getNextMove(move,count*class="num">2+class="num">1,currentBar-count); while(moveReturned!=currentBar) { currentBar=getNextMove(move,count,currentBar+class="num">1); moveReturned=getNextMove(move,count*class="num">2+class="num">1,currentBar-count); } class="kw">return(currentBar); } class="type">int getNextMove(class="type">int move, class="type">int count, class="type">int startPos) { if(startPos<class="num">0) { count +=startPos; startPos =class="num">0; } class="kw">return((move==MODE_HIGH)? iHighest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos): iLowest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos)); } if(lowVal1>=lowVal2&&highVal1<highVal2) { Comment("Potential Double Bottom", "\nCurrent High ",highVal1,"\nPrevious High ",highVal2, "\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2); } else if(lowVal1>=lowVal2&&highVal1>highVal2)
◍ 把双底形态参数打到图表上
在 MT5 自定义指标或 EA 里,最直接验证双底识别逻辑的办法,就是把关键极值实时打印到图表左上角。下面这段代码利用 Comment 把当前与前置的高低点一并输出,省去你翻图表比对。
{
Comment("Double Bottom",
"\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
"\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
}
{
Comment("Double Bottom",
"\nCurrent High ",highVal1,"\nPrevious High ",highVal2,
"\nCurrent Low ",lowVal1,"\nPrevious Low ",lowVal2);
}把高低点检测扩成自己的形态库
前面几节把高点低点、趋势方向、双顶双底的检测逻辑都拆开了,核心思路是用 getmove 抓最近两段极值,再用价格关系判断形态。掌握这套骨架后,你可以继续往里加头肩、旗形、箱型等结构,不必每次都重写框架。 附带四个 mq5 文件可直接在 MT5 打开验证:moveFinder 约 2.24 KB、trendFinder 约 3.18 KB、DT_patternFinder 与 DB_patternFinder 各约 3.04 KB。跑起来后建议先改 checkBars 参数,从默认 20 根 K 线调到 50 观察信号密度变化。 下面这段是把趋势和双顶双底判断合并的片段,注意它用 NormaliceDouble 到 5 位小数,做外汇或贵金属时若品种报价小数位不同需同步调整,否则比较会偏。外汇贵金属杠杆高、回撤快,任何形态信号都只是概率倾向,实盘前请用策略测试器过一遍。 别把示例当终点 作者原版只覆盖了双顶双底,但 getmove 返回的是极值索引,你只要替换后面的判断分支就能挂自己的形态。比如头肩可定义为三高三低且中间高最高,写个独立函数比改原 if 更稳。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|TrendDTDB.mq5 class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">input class="type">int checkBars= class="num">20; class="type">int high1, high2, low1, low2; class="type">class="kw">double highVal1, highVal2, lowVal1, lowVal2; class="type">void OnInit() { PonEtiquetas(); } class="type">void OnTick() { high1=getmove(MODE_HIGH,checkBars,class="num">0); high2=getmove(MODE_HIGH,checkBars,high1+class="num">1); highVal1=NormalizeDouble(iHigh(_Symbol,_Period,high1),class="num">5); highVal2=NormalizeDouble(iHigh(_Symbol,_Period,high2),class="num">5); low1=getmove(MODE_LOW,checkBars,class="num">0); low2=getmove(MODE_LOW,checkBars,low1+class="num">1); lowVal1=NormalizeDouble(iLow(_Symbol,_Period,low1),class="num">5); lowVal2=NormalizeDouble(iLow(_Symbol,_Period,low2),class="num">5); class=class="str">"cmt">// DOBLE TECHO if(highVal1<=highVal2&&lowVal1>lowVal2){ObjectSetString((class="type">int)_Symbol,"LS0",OBJPROP_TEXT,class="num">0,"Potencial DT-DOBLE TECHO"); DibujaArriba("DTP",clrGreen);} else if(highVal1<=highVal2&&lowVal1<lowVal2){ObjectSetString((class="type">int)_Symbol,"LS0",OBJPROP_TEXT,class="num">0,"DT-DOBLE TECHO");DibujaArriba("DT",clrGreen);}
「双顶双底与趋势状态的图形标注逻辑」
这段判定把相邻两段的高低点拿来比对,直接决定图表上标什么字、画什么箭头。若第一段低点不低于第二段且高点更低,倾向识别为潜在双底(DOBLE SUELO),在「LS1」标签写 Potencial DS,并调用 DibujaAbajo 用红色画下方标记;反之若高点更高则判双顶(DOBLE TECHO),绿色画上方。 趋势分支更直白:lowVal1>lowVal2 且 highVal1>highVal2 时标 UP 多头趋势并绿色画上;highVal1<highVal2 且 lowVal1<lowVal2 则标 DOWN 空头趋势红色画下;都不满足就标 SW 横盘并清掉趋势对象。外汇与贵金属波动受杠杆放大,这类形态只是概率倾向,实战须自验。 getmove 与 getNextMove 是底层取极值棒的函数。getNextMove 用 iHighest / iLowest 在指定 count 根内取最高或最低棒序号;getmove 通过循环把「当前棒」与「前推 count 根内的大级别极值棒」对齐,返回稳定拐点位置。把下面代码贴进 MT5 指标工程,改 lowVal1 等变量来源即可跑出自己的标注。
else{ObjectSetString((class="type">int)_Symbol,"LS0",OBJPROP_TEXT,class="num">0,"."); ObjectsDeleteAll(class="num">0,"DT",class="num">0,-class="num">1);} class=class="str">"cmt">//DOBLE SUELO if(lowVal1>=lowVal2&&highVal1<highVal2){ObjectSetString((class="type">int)_Symbol,"LS1",OBJPROP_TEXT,class="num">0,"Potencial DS-DOBLE SUELO"); DibujaAbajo("DSP",clrRed);} else if(lowVal1>=lowVal2&&highVal1>highVal2){ObjectSetString((class="type">int)_Symbol,"LS1",OBJPROP_TEXT,class="num">0,"DT-DOBLE TECHO");DibujaArriba("DS",clrGreen);} else{ObjectSetString((class="type">int)_Symbol,"LS1",OBJPROP_TEXT,class="num">0,"."); ObjectsDeleteAll(class="num">0,"DS",class="num">0,-class="num">1);} class=class="str">"cmt">// 趋势发现者 if(lowVal1>lowVal2&&highVal1>highVal2){ObjectSetString((class="type">int)_Symbol,"LS2",OBJPROP_TEXT,class="num">0,"UP-Tendencia ALCISTA");DibujaArriba("T",clrGreen);} else if(highVal1<highVal2&&lowVal1<lowVal2){ObjectSetString((class="type">int)_Symbol,"LS2",OBJPROP_TEXT,class="num">0,"DOWN-Tendencia BAJISTA");DibujaAbajo("T",clrRed);} else{ObjectSetString((class="type">int)_Symbol,"LS2",OBJPROP_TEXT,class="num">0,"SW-Tendencia LATERAL"); ObjectsDeleteAll(class="num">0,"T",class="num">0,-class="num">1);} } class="type">int getmove(class="type">int move, class="type">int count, class="type">int startPos) { if(move!=MODE_HIGH && move!=MODE_LOW) class="kw">return (-class="num">1); class="type">int currentBar=startPos; class="type">int moveReturned=getNextMove(move,count*class="num">2+class="num">1,currentBar-count); while(moveReturned!=currentBar) { currentBar=getNextMove(move,count,currentBar+class="num">1); moveReturned=getNextMove(move,count*class="num">2+class="num">1,currentBar-count); } class="kw">return(currentBar); } class="type">int getNextMove(class="type">int move, class="type">int count, class="type">int startPos) { if(startPos<class="num">0) { count +=startPos; startPos =class="num">0; } class="kw">return((move==MODE_HIGH)? iHighest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos): iLowest(Symbol(),Period(),(ENUM_SERIESMODE)move,count,startPos)); } class="type">void DibujaArriba(class="type">class="kw">string Nombre, class="type">int mColor) { ObjectDelete(class="num">0,Nombre);
◍ 画趋势线与角标标签的落地代码
在 MT5 里用 EA 自动标双顶双底,光算出了 high1/high2、low1/low2 还不够,得把线画出来并且给出视觉提示。下面这段把上沿趋势线、下沿趋势线以及两个角标文字一次性建好,复制进你的脚本就能直接看到图形输出。 DibujaArriba 和 DibujaAbajo 两个函数先用 ObjectDelete 清掉同名旧对象,再用 ObjectCreate 以 OBJ_TREND 类型连线:上端取 high2 与 high1 两根 K 线的时间与最高价,下端取 low2 与 low1 的最低价,右射线 OBJPROP_RAY_RIGHT 设为 true 让线向右延伸。线宽固定为 1,颜色由传入的 mColor 控制。 PonEtiquetas 则在图表右下角(OBJPROP_CORNER=4)堆两个 OBJ_LABEL:LS0 距右边 800 像素、距底 50 像素,绿色 Arial 25 号字;LS1 同样右距 800、底距 90,红色同字号。初始文字都是 "-",实际跑的时候你可以在外面把 OBJPROP_TEXT 改成 "双顶"/"双底" 之类。外汇和贵金属波动大,这类标注只作形态参考,信号失效的概率不低。 别把标签位置当摆设 XDISTANCE 写死 800 在 4K 屏上可能贴边,跑之前先按自己图表宽度改这个值,否则标签容易被推出可视区。
ObjectCreate(class="num">0,Nombre,OBJ_TREND,class="num">0,iTime(Symbol(),Period(),high2),iHigh(Symbol(),Period(),high2),iTime(Symbol(),Period(),high1),iHigh(Symbol(),Period(),high1)); ObjectSetInteger(class="num">0,Nombre,OBJPROP_COLOR,mColor); ObjectSetInteger(class="num">0,Nombre,OBJPROP_WIDTH,class="num">1); ObjectSetInteger(class="num">0,Nombre,OBJPROP_RAY_RIGHT,true); } class="type">void DibujaAbajo(class="type">class="kw">string Nombre, class="type">int mColor) { ObjectDelete(class="num">0,Nombre); ObjectCreate(class="num">0,Nombre,OBJ_TREND,class="num">0,iTime(Symbol(),Period(),low2),iLow(Symbol(),Period(),low2),iTime(Symbol(),Period(),low1),iLow(Symbol(),Period(),low1)); ObjectSetInteger(class="num">0,Nombre,OBJPROP_COLOR,mColor); ObjectSetInteger(class="num">0,Nombre,OBJPROP_WIDTH,class="num">1); ObjectSetInteger(class="num">0,Nombre,OBJPROP_RAY_RIGHT,true); } class="type">void PonEtiquetas() { ObjectCreate((class="type">int)_Symbol,"LS0", OBJ_LABEL, class="num">0, class="num">0, class="num">0); ObjectSetString(class="num">0,"LS0",OBJPROP_FONT,"Arial"); ObjectSetInteger(class="num">0,"LS0",OBJPROP_FONTSIZE,class="num">25); ObjectSetInteger(class="num">0,"LS0",OBJPROP_COLOR,clrGreen); ObjectSetInteger(class="num">0,"LS0", OBJPROP_CORNER, class="num">4); ObjectSetInteger(class="num">0,"LS0", OBJPROP_XDISTANCE, class="num">800); ObjectSetInteger(class="num">0,"LS0", OBJPROP_YDISTANCE, class="num">50); ObjectSetString((class="type">int)_Symbol,"LS0",OBJPROP_TEXT,class="num">0,"-"); ObjectCreate((class="type">int)_Symbol,"LS1", OBJ_LABEL, class="num">0, class="num">0, class="num">0); ObjectSetString(class="num">0,"LS1",OBJPROP_FONT,"Arial"); ObjectSetInteger(class="num">0,"LS1",OBJPROP_FONTSIZE,class="num">25); ObjectSetInteger(class="num">0,"LS1",OBJPROP_COLOR,clrRed); ObjectSetInteger(class="num">0,"LS1", OBJPROP_CORNER, class="num">4); ObjectSetInteger(class="num">0,"LS1", OBJPROP_XDISTANCE, class="num">800); ObjectSetInteger(class="num">0,"LS1", OBJPROP_YDISTANCE, class="num">90);
记住这一条就够了
上面这段把两个 OBJ_LABEL 控件挂到了图表上:LS1 先清文本,LS2 用 Arial 25 号字、WhiteSmoke 色,锚定在右下角(CORNER=4),X 偏移 800 像素、Y 偏移 130 像素,最后文本置为“-”。 在 MT5 里跑这段,你能直接看到右上/右下角出现两块预留标签位——它们本身不画信号,只是给后续动态文字(比如小布盯盘的状态播报)留坑。 外汇和贵金属波动剧烈,这类界面元素只解决“信息往哪放”,不替代任何风控;真要接策略,先把标签坐标在你的屏幕分辨率下测一遍,避免被价格窗遮住。
ObjectSetString((class="type">int)_Symbol,"LS1",OBJPROP_TEXT,class="num">0,"-"); ObjectCreate((class="type">int)_Symbol,"LS2", OBJ_LABEL, class="num">0, class="num">0, class="num">0); ObjectSetString(class="num">0,"LS2",OBJPROP_FONT,"Arial"); ObjectSetInteger(class="num">0,"LS2",OBJPROP_FONTSIZE,class="num">25); ObjectSetInteger(class="num">0,"LS2",OBJPROP_COLOR,clrWhiteSmoke); ObjectSetInteger(class="num">0,"LS2", OBJPROP_CORNER, class="num">4); ObjectSetInteger(class="num">0,"LS2", OBJPROP_XDISTANCE, class="num">800); ObjectSetInteger(class="num">0,"LS2", OBJPROP_YDISTANCE, class="num">130); ObjectSetString((class="type">int)_Symbol,"LS2",OBJPROP_TEXT,class="num">0,"-");