| //+------------------------------------------------------------------+
|
| //| DzDraw.mqh |
|
| //| Copyright 2026, Allan Niñal |
|
| //| https://www.allanninal.dev/mql5 |
|
| //+------------------------------------------------------------------+
|
| //| Draws a CDzEngine's zones on the chart: rectangles (fresh filled,|
|
| //| tested outlined, broken grey up to the bar that broke them), the |
|
| //| middle line, a short label and the broken swing. Objects share |
|
| //| one prefix and are rebuilt only when a bar closes. |
|
| //| MT4 edition: MT4 fills a rectangle only when it is drawn in the |
|
| //| background, so OBJPROP_BACK follows the fill (else as MT5). |
|
| //+------------------------------------------------------------------+
|
| // SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0 (see LICENSE)
|
| #property strict
|
|
|
| #ifndef DZDRAW_MQH
|
| #define DZDRAW_MQH
|
|
|
| #include "DzEngine.mqh"
|
| #include "ObjSync.mqh"
|
|
|
| int g_dz_fail=0; // objects the chart refused in the current pass
|
|
|
| struct SDzStyle
|
| {
|
| bool fill,show_tested,show_broken,midline,labels,swings,show_filtered;
|
| int draw_bars,max_zones,extend;
|
| color dem,sup,broken,outline;
|
| };
|
|
|
| void DzRect(const string name,const datetime t1,const double top,const datetime t2,const double bot,const color clr,
|
| const bool fill,const ENUM_LINE_STYLE style,const string tip)
|
| {
|
| if(!OsPlace(name,OBJ_RECTANGLE,2,t1,top,t2,bot))
|
| {
|
| g_dz_fail++;
|
| return;
|
| }
|
| OsInt(name,OBJPROP_COLOR,clr);
|
| OsInt(name,OBJPROP_FILL,fill);
|
| OsInt(name,OBJPROP_STYLE,style);
|
| OsInt(name,OBJPROP_BACK,fill);
|
| OsInt(name,OBJPROP_WIDTH,1);
|
| OsStr(name,OBJPROP_TOOLTIP,tip);
|
| }
|
|
|
| void DzSeg(const string name,const datetime t1,const double p1,const datetime t2,const double p2,const color clr,
|
| const ENUM_LINE_STYLE style,const string tip)
|
| {
|
| if(!OsPlace(name,OBJ_TREND,2,t1,p1,t2,p2))
|
| {
|
| g_dz_fail++;
|
| return;
|
| }
|
| OsInt(name,OBJPROP_COLOR,clr);
|
| OsInt(name,OBJPROP_STYLE,style);
|
| OsInt(name,OBJPROP_RAY_RIGHT,false);
|
| OsInt(name,OBJPROP_BACK,true);
|
| OsStr(name,OBJPROP_TOOLTIP,tip);
|
| }
|
|
|
| void DzText(const string name,const datetime t,const double p,const string text,const color clr,const ENUM_ANCHOR_POINT anchor)
|
| {
|
| if(!OsPlace(name,OBJ_TEXT,1,t,p,0,0))
|
| {
|
| g_dz_fail++;
|
| return;
|
| }
|
| OsStr(name,OBJPROP_TEXT,text);
|
| OsInt(name,OBJPROP_COLOR,clr);
|
| OsInt(name,OBJPROP_FONTSIZE,8);
|
| OsInt(name,OBJPROP_ANCHOR,anchor);
|
| }
|
|
|
| string DzStateText(const SDzZone &z)
|
| {
|
| if(z.state==DZ_FRESH)
|
| return("fresh");
|
| if(z.state==DZ_TESTED)
|
| return(z.retests>1 ? StringFormat("tested, %d entries",z.retests) : "tested");
|
| if(z.state==DZ_BROKEN)
|
| return("broken");
|
| return("dropped (zone limit)");
|
| }
|
|
|
| //--- a small corner label with the validated market structure after the last closed bar
|
| void DzTrendLabel(const string name,CDzEngine &e,const string tfname,const ENUM_BASE_CORNER corner,const int x,const int y,
|
| const color clr,const int digits)
|
| {
|
| if(e.done<1)
|
| return;
|
| string tx=StringFormat("%sValidated trend: %s (validated %s %s)",tfname!="" ? tfname+" " : "",e.Trend()>0 ? "up" : "down",
|
| e.Trend()>0 ? "low" : "high",DoubleToString(e.ValidLevel(),digits));
|
| if(!OsPlace(name,OBJ_LABEL,1,0,0,0,0))
|
| {
|
| g_dz_fail++;
|
| return;
|
| }
|
| OsInt(name,OBJPROP_CORNER,corner);
|
| OsInt(name,OBJPROP_ANCHOR,(corner==CORNER_RIGHT_UPPER || corner==CORNER_RIGHT_LOWER) ? ANCHOR_RIGHT_UPPER : ANCHOR_LEFT_UPPER);
|
| OsInt(name,OBJPROP_XDISTANCE,x);
|
| OsInt(name,OBJPROP_YDISTANCE,y);
|
| OsInt(name,OBJPROP_COLOR,clr);
|
| OsInt(name,OBJPROP_FONTSIZE,9);
|
| OsStr(name,OBJPROP_TEXT,tx);
|
| }
|
|
|
| //--- draw the zones of one engine; `t` = bar times of the series it ran on. `tfname` != "" marks a higher timeframe.
|
| int DzDrawEngine(CDzEngine &e,const datetime &t[],const string pfx,const string tfname,const datetime from_t,
|
| const datetime right_t,const int budget,const SDzStyle &st,const int digits)
|
| {
|
| int drawn=0;
|
| bool htf=(tfname!="");
|
| for(int z=e.nz-1; z>=0 && drawn<budget; z--)
|
| {
|
| if(t[e.zones[z].born]<from_t)
|
| break; // zones are stored in birth order: all older ones are out too
|
| int s=e.zones[z].state;
|
| bool shown=e.zones[z].ok; // false: left out by the trend or reward-to-risk option
|
| if(s==DZ_DROPPED || (s==DZ_TESTED && !st.show_tested) || (s==DZ_BROKEN && !st.show_broken) || (!shown && !st.show_filtered))
|
| continue;
|
| bool dem=(e.zones[z].dir>0),live=(e.zones[z].end<0);
|
| color clr=(!shown || s==DZ_BROKEN ? st.broken : (htf ? st.outline : (dem ? st.dem : st.sup)));
|
| datetime t2=(live ? right_t : t[e.zones[z].end]);
|
| string tip=StringFormat("%s%s zone %s - %s, made %s, %s",htf ? tfname+" " : "",dem ? "Demand" : "Supply",
|
| DoubleToString(e.zones[z].bot,digits),DoubleToString(e.zones[z].top,digits),
|
| TimeToString(t[e.zones[z].born]),DzStateText(e.zones[z]));
|
| tip+=StringFormat(", validated trend %s, reward-to-risk %.1f",e.zones[z].trend>0 ? "up" : "down",e.zones[z].rr);
|
| if(!shown)
|
| tip+=" - left out by the trend / reward-to-risk option";
|
| string nm=StringFormat("%sZ%d",pfx,z);
|
| bool fill=(st.fill && !htf && s==DZ_FRESH && shown);
|
| DzRect(nm,t[e.zones[z].t0],e.zones[z].top,t2,e.zones[z].bot,clr,fill,!shown ? STYLE_DOT : (s==DZ_TESTED ? STYLE_DASH : STYLE_SOLID),tip);
|
| if(st.midline && shown)
|
| DzSeg(nm+"m",t[e.zones[z].t0],e.zones[z].mid,t2,e.zones[z].mid,clr,STYLE_DOT,tip);
|
| if(st.labels && shown)
|
| {
|
| string lb=(htf ? tfname+" " : "")+(dem ? "D" : "S")+(e.zones[z].retests>1 ? "x"+IntegerToString(e.zones[z].retests) : "");
|
| DzText(nm+"l",t[e.zones[z].t0],dem ? e.zones[z].bot : e.zones[z].top,lb,clr,dem ? ANCHOR_LEFT_UPPER : ANCHOR_LEFT_LOWER);
|
| }
|
| drawn++;
|
| }
|
| int shown=0;
|
| if(st.swings && !htf)
|
| for(int b=e.nb-1; b>=0 && shown<budget; b--)
|
| {
|
| if(t[e.breaks[b].at]<from_t)
|
| break;
|
| if(e.breaks[b].zone<0 || !e.zones[e.breaks[b].zone].ok)
|
| continue;
|
| bool up=(e.breaks[b].dir>0);
|
| DzSeg(StringFormat("%sB%d",pfx,b),t[e.breaks[b].frm],e.breaks[b].level,t[e.breaks[b].at],e.breaks[b].level,
|
| up ? st.dem : st.sup,STYLE_DOT,up ? "Swing high closed through with displacement" : "Swing low closed through with displacement");
|
| shown++;
|
| }
|
| return(drawn);
|
| }
|
|
|
| #endif
|
| //+------------------------------------------------------------------+
|