//+------------------------------------------------------------------+ //| SMC_BibleEA | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property strict class CChartDebug { private: long chartId; string prefix; bool enabled; int zCounter; string BuildName(string key) { return prefix + key; } ENUM_LINE_STYLE GetStyle(int style) { if(style == 1) return STYLE_DOT; return STYLE_SOLID; } double RadToDeg(double rad) { return rad * 180.0 / M_PI; } bool CalcLineAngleDeg(datetime t1, double p1, datetime t2, double p2, double &angleDeg) { int x1, y1, x2, y2; if(!ChartTimePriceToXY(chartId, 0, t1, p1, x1, y1)) return false; if(!ChartTimePriceToXY(chartId, 0, t2, p2, x2, y2)) return false; double dx = (double)(x2 - x1); double dy = (double)(y1 - y2); // screen Y grows downward, so invert if(MathAbs(dx) < 0.000001 && MathAbs(dy) < 0.000001) { angleDeg = 0.0; return true; } angleDeg = RadToDeg(MathArctan2(dy, dx)); return true; } color LighterColor(color base, double amount01, double alpha01 = 1.0) { // Clamp inputs if(amount01 < 0.0) amount01 = 0.0; if(amount01 > 1.0) amount01 = 1.0; if(alpha01 < 0.0) alpha01 = 0.0; if(alpha01 > 1.0) alpha01 = 1.0; // Extract original ARGB uint argb = (uint)base; int a = (int)((argb >> 24) & 255); int r = (int)((argb >> 16) & 255); int g = (int)((argb >> 8) & 255); int b = (int)((argb >> 0) & 255); // Lighten toward white r = (int)(r + (255 - r) * amount01/4); g = (int)(g + (255 - g) * amount01/2); b = (int)(b + (255 - b) * amount01*8); // Apply transparency (override alpha) a = (int)(255.0 * alpha01); // Clamp if(r < 0) r = 0; if(r > 255) r = 255; if(g < 0) g = 0; if(g > 255) g = 255; if(b < 0) b = 0; if(b > 255) b = 255; if(a < 0) a = 0; if(a > 255) a = 255; return (color)((a << 24) | (r << 16) | (g << 8) | b); } void EnsureRect(string name, datetime t1, double p1, datetime t2, double p2, color c, bool back, int z, bool fill) { if(ObjectFind(chartId, name) < 0) { ObjectCreate(chartId, name, OBJ_RECTANGLE, 0, t1, p1, t2, p2); ObjectSetInteger(chartId, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(chartId, name, OBJPROP_HIDDEN, true); ObjectSetInteger(chartId, name, OBJPROP_BACK, back); ObjectSetInteger(chartId, name, OBJPROP_ZORDER, z); } else { ObjectMove(chartId, name, 0, t1, p1); ObjectMove(chartId, name, 1, t2, p2); } ObjectSetInteger(chartId, name, OBJPROP_COLOR, c); ObjectSetInteger(chartId, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(chartId, name, OBJPROP_WIDTH, 1); ObjectSetInteger(chartId, name, OBJPROP_FILL, fill); } void EnsureText(string name, datetime t, double price, string text, int fontSize, color c, int z) { if(ObjectFind(chartId, name) < 0) { ObjectCreate(chartId, name, OBJ_TEXT, 0, t, price); ObjectSetInteger(chartId, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(chartId, name, OBJPROP_HIDDEN, true); ObjectSetInteger(chartId, name, OBJPROP_BACK, false); ObjectSetInteger(chartId, name, OBJPROP_ANCHOR, ANCHOR_CENTER); ObjectSetInteger(chartId, name, OBJPROP_ZORDER, z); } else { ObjectMove(chartId, name, 0, t, price); } ObjectSetInteger(chartId, name, OBJPROP_FONTSIZE, fontSize); ObjectSetInteger(chartId, name, OBJPROP_COLOR, c); ObjectSetString(chartId, name, OBJPROP_TEXT, text); } public: void Init(long cid, string pfx, bool en) { chartId = cid; prefix = pfx; enabled = en; zCounter = 0; } void SetEnabled(bool en) { enabled = en; } void ClearAll() { if(!enabled) return; int total = ObjectsTotal(chartId, 0, -1); for(int i = total - 1; i >= 0; i--) { string name = ObjectName(chartId, i, 0, -1); if(StringFind(name, prefix) == 0) ObjectDelete(chartId, name); } ChartRedraw(chartId); } // -------- HUD -------- void Hud(string key, string text) { if(!enabled) return; string name = BuildName("HUD_" + key); if(ObjectFind(chartId, name) < 0) { ObjectCreate(chartId, name, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(chartId, name, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(chartId, name, OBJPROP_XDISTANCE, 10); ObjectSetInteger(chartId, name, OBJPROP_YDISTANCE, 10); ObjectSetInteger(chartId, name, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chartId, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(chartId, name, OBJPROP_HIDDEN, true); ObjectSetInteger(chartId, name, OBJPROP_ZORDER, 200 + zCounter++); } ObjectSetString(chartId, name, OBJPROP_TEXT, text); } // -------- Centered Screen Text -------- void CenteredText(string key, string text) { if(!enabled) return; string name = BuildName("CENTER_" + key); int w = (int)ChartGetInteger(chartId, CHART_WIDTH_IN_PIXELS); int h = (int)ChartGetInteger(chartId, CHART_HEIGHT_IN_PIXELS); if(ObjectFind(chartId, name) < 0) { ObjectCreate(chartId, name, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(chartId, name, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(chartId, name, OBJPROP_ANCHOR, ANCHOR_CENTER); ObjectSetInteger(chartId, name, OBJPROP_FONTSIZE, 14); ObjectSetInteger(chartId, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(chartId, name, OBJPROP_HIDDEN, true); ObjectSetInteger(chartId, name, OBJPROP_ZORDER, 300 + zCounter++); } ObjectSetInteger(chartId, name, OBJPROP_XDISTANCE, w / 2); ObjectSetInteger(chartId, name, OBJPROP_YDISTANCE, h / 2); ObjectSetString(chartId, name, OBJPROP_TEXT, text); } // -------- Horizontal Line (0=solid,1=dotted) -------- void HLine(string key, double price, int style = 0) { if(!enabled) return; string name = BuildName("HL_" + key); if(ObjectFind(chartId, name) < 0) { ObjectCreate(chartId, name, OBJ_HLINE, 0, 0, price); ObjectSetInteger(chartId, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(chartId, name, OBJPROP_HIDDEN, true); ObjectSetInteger(chartId, name, OBJPROP_BACK, true); ObjectSetInteger(chartId, name, OBJPROP_WIDTH, 1); ObjectSetInteger(chartId, name, OBJPROP_ZORDER, 50 + zCounter++); } else { ObjectSetDouble(chartId, name, OBJPROP_PRICE, price); } ObjectSetInteger(chartId, name, OBJPROP_STYLE, GetStyle(style)); } // -------- Trend Line (0=solid,1=dotted) -------- void TrendLine(string key, datetime t1, double p1, datetime t2, double p2, int style = 0) { if(!enabled) return; string name = BuildName("TL_" + key); if(ObjectFind(chartId, name) < 0) { ObjectCreate(chartId, name, OBJ_TREND, 0, t1, p1, t2, p2); ObjectSetInteger(chartId, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(chartId, name, OBJPROP_HIDDEN, true); ObjectSetInteger(chartId, name, OBJPROP_WIDTH, 1); ObjectSetInteger(chartId, name, OBJPROP_RAY_RIGHT, false); ObjectSetInteger(chartId, name, OBJPROP_ZORDER, 60 + zCounter++); } else { ObjectMove(chartId, name, 0, t1, p1); ObjectMove(chartId, name, 1, t2, p2); } ObjectSetInteger(chartId, name, OBJPROP_STYLE, GetStyle(style)); } // -------- Text at price/time -------- void Text(string key, datetime t, double price, string label) { if(!enabled) return; string name = BuildName("TXT_" + key); if(ObjectFind(chartId, name) < 0) { ObjectCreate(chartId, name, OBJ_TEXT, 0, t, price); ObjectSetInteger(chartId, name, OBJPROP_FONTSIZE, 9); ObjectSetInteger(chartId, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(chartId, name, OBJPROP_HIDDEN, true); ObjectSetInteger(chartId, name, OBJPROP_ZORDER, 100 + zCounter++); } else { ObjectMove(chartId, name, 0, t, price); } ObjectSetString(chartId, name, OBJPROP_TEXT, label); } // -------- Line + centered text that rotates with line (0=solid,1=dotted) -------- void LineWithCenteredText(string key, datetime t1, double p1, datetime t2, double p2, string text, int style = 0, color c = clrRed) { if(!enabled) return; string lineName = BuildName("TL_" + key); string txtName = BuildName("TLTXT_" + key); // --- create/update line --- if(ObjectFind(chartId, lineName) < 0) { ObjectCreate(chartId, lineName, OBJ_TREND, 0, t1, p1, t2, p2); ObjectSetInteger(chartId, lineName, OBJPROP_SELECTABLE, false); ObjectSetInteger(chartId, lineName, OBJPROP_HIDDEN, true); ObjectSetInteger(chartId, lineName, OBJPROP_WIDTH, 1); ObjectSetInteger(chartId, lineName, OBJPROP_RAY_RIGHT, false); ObjectSetInteger(chartId, lineName, OBJPROP_ZORDER, 60 + zCounter++); } else { ObjectMove(chartId, lineName, 0, t1, p1); ObjectMove(chartId, lineName, 1, t2, p2); } ObjectSetInteger(chartId, lineName, OBJPROP_STYLE, GetStyle(style)); ObjectSetInteger(chartId, lineName, OBJPROP_COLOR, c); // --- midpoint --- datetime tm = (datetime)((long)t1 + ((long)t2 - (long)t1) / 2); double pm = (p1 + p2) * 0.5; // --- create/update text --- if(ObjectFind(chartId, txtName) < 0) { ObjectCreate(chartId, txtName, OBJ_TEXT, 0, tm, pm); ObjectSetInteger(chartId, txtName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(chartId, txtName, OBJPROP_SELECTABLE, false); ObjectSetInteger(chartId, txtName, OBJPROP_HIDDEN, true); ObjectSetInteger(chartId, txtName, OBJPROP_ANCHOR, ANCHOR_CENTER); ObjectSetInteger(chartId, txtName, OBJPROP_BACK, false); ObjectSetInteger(chartId, txtName, OBJPROP_ZORDER, 120 + zCounter++); } else { ObjectMove(chartId, txtName, 0, tm, pm); } ObjectSetString(chartId, txtName, OBJPROP_TEXT, text); ObjectSetInteger(chartId, txtName, OBJPROP_COLOR, c); // --- angle --- double ang; if(CalcLineAngleDeg(t1, p1, t2, p2, ang)) ObjectSetDouble(chartId, txtName, OBJPROP_ANGLE, ang); } // t1/pTop and t2/pBot can be in any order; function normalizes. void RectWithCenteredText(string symbol, string key, datetime t1, double pTop, datetime t2, double pBot, string text, color borderColor = clrAqua, double insetFrac = 0.06, // padding % for inner fill int fontSize = 10, bool drawOnTop = false) { if(!enabled) return; // Normalize time order datetime leftT = t1; datetime rightT = t2; if(leftT > rightT) { datetime tmp = leftT; leftT = rightT; rightT = tmp; } // Normalize price order (top > bottom) double topP = pTop; double botP = pBot; if(topP < botP) { double tmp = topP; topP = botP; botP = tmp; } long dt = (long)(rightT - leftT); if(dt < 1) dt = 1; double dp = (topP - botP); if(dp <= 0.0) dp = SymbolInfoDouble(symbol, SYMBOL_POINT); if(insetFrac < 0.0) insetFrac = 0.0; if(insetFrac > 0.45) insetFrac = 0.45; long insetT = (long)((double)dt * insetFrac); double insetP = dp * insetFrac; datetime innerL = (datetime)((long)leftT + insetT); datetime innerR = (datetime)((long)rightT - insetT); double innerTop = topP - insetP; double innerBot = botP + insetP; // Safety: if inset collapses, fallback to same if(innerR <= innerL) { innerL = leftT; innerR = rightT; } if(innerTop <= innerBot) { innerTop = topP; innerBot = botP; } datetime midT = (datetime)((long)leftT + ((long)rightT - (long)leftT) / 2); double midP = (topP + botP) * 0.5; color fillColor = LighterColor(borderColor, 0.20, 0.505); string outerName = BuildName("RBOX_OUT_" + key); string innerName = BuildName("RBOX_IN_" + key); string txtName = BuildName("RBOX_TXT_" + key); bool back = true; int z = 420 + zCounter++; EnsureRect(innerName, leftT, topP, rightT, botP, fillColor, back, z + 1, true); EnsureRect(outerName, leftT, topP, rightT, botP, borderColor, back, z, false); EnsureText(txtName, midT, midP, text, fontSize, borderColor, z + 2); } // Call this on CHARTEVENT_CHART_CHANGE to keep angle correct when zoom/scroll changes void RefreshLineText(string key, datetime t1, double p1, datetime t2, double p2) { if(!enabled) return; string txtName = BuildName("TLTXT_" + key); if(ObjectFind(chartId, txtName) < 0) return; datetime tm = (datetime)((long)t1 + ((long)t2 - (long)t1) / 2); double pm = (p1 + p2) * 0.5; ObjectMove(chartId, txtName, 0, tm, pm); double ang; if(CalcLineAngleDeg(t1, p1, t2, p2, ang)) ObjectSetDouble(chartId, txtName, OBJPROP_ANGLE, ang); } }; //+------------------------------------------------------------------+