2026-10-22 00:55:54 +08:00 | | | //+------------------------------------------------------------------+
|
| | | //| 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"
|
2026-10-31 12:15:21 +08:00 | | | #include "ObjSync.mqh"
|
| | |
|
| | | int g_dz_fail=0; // objects the chart refused in the current pass
|
2026-10-22 00:55:54 +08:00 | | |
|
| | | struct SDzStyle
|
| | | {
|
2026-10-31 12:15:21 +08:00 | | | bool fill,show_tested,show_broken,midline,labels,swings,show_filtered;
|
2026-10-22 00:55:54 +08:00 | | | int draw_bars,max_zones,extend;
|
2026-10-31 12:15:21 +08:00 | | | color dem,sup,broken,outline;
|
2026-10-22 00:55:54 +08:00 | | | };
|
| | |
|
| | | 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)
|
| | | {
|
2026-10-31 12:15:21 +08:00 | | | if(!OsPlace(name,OBJ_RECTANGLE,2,t1,top,t2,bot))
|
2026-10-22 00:55:54 +08:00 | | | {
|
2026-10-31 12:15:21 +08:00 | | | g_dz_fail++;
|
| | | return;
|
2026-10-22 00:55:54 +08:00 | | | }
|
2026-10-31 12:15:21 +08:00 | | | 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);
|
2026-10-22 00:55:54 +08:00 | | | }
|
| | |
|
| | | 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)
|
| | | {
|
2026-10-31 12:15:21 +08:00 | | | if(!OsPlace(name,OBJ_TREND,2,t1,p1,t2,p2))
|
2026-10-22 00:55:54 +08:00 | | | {
|
2026-10-31 12:15:21 +08:00 | | | g_dz_fail++;
|
| | | return;
|
2026-10-22 00:55:54 +08:00 | | | }
|
2026-10-31 12:15:21 +08:00 | | | 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);
|
2026-10-22 00:55:54 +08:00 | | | }
|
| | |
|
| | | void DzText(const string name,const datetime t,const double p,const string text,const color clr,const ENUM_ANCHOR_POINT anchor)
|
| | | {
|
2026-10-31 12:15:21 +08:00 | | | if(!OsPlace(name,OBJ_TEXT,1,t,p,0,0))
|
2026-10-22 00:55:54 +08:00 | | | {
|
2026-10-31 12:15:21 +08:00 | | | g_dz_fail++;
|
| | | return;
|
2026-10-22 00:55:54 +08:00 | | | }
|
2026-10-31 12:15:21 +08:00 | | | OsStr(name,OBJPROP_TEXT,text);
|
| | | OsInt(name,OBJPROP_COLOR,clr);
|
| | | OsInt(name,OBJPROP_FONTSIZE,8);
|
| | | OsInt(name,OBJPROP_ANCHOR,anchor);
|
2026-10-22 00:55:54 +08:00 | | | }
|
| | |
|
| | | 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");
|
2026-10-31 12:15:21 +08:00 | | | 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);
|
2026-10-22 00:55:54 +08:00 | | | }
|
| | |
|
2026-10-31 12:15:21 +08:00 | | | //--- 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,
|
2026-10-22 00:55:54 +08:00 | | | const datetime right_t,const int budget,const SDzStyle &st,const int digits)
|
| | | {
|
| | | int drawn=0;
|
2026-10-31 12:15:21 +08:00 | | | bool htf=(tfname!="");
|
2026-10-22 00:55:54 +08:00 | | | 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;
|
2026-10-31 12:15:21 +08:00 | | | 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))
|
2026-10-22 00:55:54 +08:00 | | | continue;
|
| | | bool dem=(e.zones[z].dir>0),live=(e.zones[z].end<0);
|
2026-10-31 12:15:21 +08:00 | | | color clr=(!shown || s==DZ_BROKEN ? st.broken : (htf ? st.outline : (dem ? st.dem : st.sup)));
|
2026-10-22 00:55:54 +08:00 | | | datetime t2=(live ? right_t : t[e.zones[z].end]);
|
2026-10-31 12:15:21 +08:00 | | | string tip=StringFormat("%s%s zone %s - %s, made %s, %s",htf ? tfname+" " : "",dem ? "Demand" : "Supply",
|
2026-10-22 00:55:54 +08:00 | | | DoubleToString(e.zones[z].bot,digits),DoubleToString(e.zones[z].top,digits),
|
| | | TimeToString(t[e.zones[z].born]),DzStateText(e.zones[z]));
|
2026-10-31 12:15:21 +08:00 | | | 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";
|
2026-10-22 00:55:54 +08:00 | | | string nm=StringFormat("%sZ%d",pfx,z);
|
2026-10-31 12:15:21 +08:00 | | | 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)
|
2026-10-22 00:55:54 +08:00 | | | DzSeg(nm+"m",t[e.zones[z].t0],e.zones[z].mid,t2,e.zones[z].mid,clr,STYLE_DOT,tip);
|
2026-10-31 12:15:21 +08:00 | | | if(st.labels && shown)
|
2026-10-22 00:55:54 +08:00 | | | {
|
2026-10-31 12:15:21 +08:00 | | | string lb=(htf ? tfname+" " : "")+(dem ? "D" : "S")+(e.zones[z].retests>1 ? "x"+IntegerToString(e.zones[z].retests) : "");
|
2026-10-22 00:55:54 +08:00 | | | 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;
|
2026-10-31 12:15:21 +08:00 | | | if(st.swings && !htf)
|
2026-10-22 00:55:54 +08:00 | | | for(int b=e.nb-1; b>=0 && shown<budget; b--)
|
| | | {
|
| | | if(t[e.breaks[b].at]<from_t)
|
| | | break;
|
2026-10-31 12:15:21 +08:00 | | | if(e.breaks[b].zone<0 || !e.zones[e.breaks[b].zone].ok)
|
2026-10-22 00:55:54 +08:00 | | | 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
|
| | | //+------------------------------------------------------------------+
|