2026-03-04 17:09:32 +02:00 | | | //+------------------------------------------------------------------+
|
| | | //| 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
|
2026-03-15 02:46:38 +02:00 | | | r = (int)(r + (255 - r) * amount01/4);
|
| | | g = (int)(g + (255 - g) * amount01/2);
|
| | | b = (int)(b + (255 - b) * amount01*8);
|
2026-03-04 17:09:32 +02:00 | | |
|
| | | // 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;
|
| | |
|
2026-03-15 02:46:38 +02:00 | | | color fillColor = LighterColor(borderColor, 0.20, 0.505);
|
2026-03-04 17:09:32 +02:00 | | |
|
| | | 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);
|
| | | }
|
| | | };
|
| | | //+------------------------------------------------------------------+
|