393 lines
14 KiB
MQL5
393 lines
14 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| 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);
| |
}
| |
};
| |
//+------------------------------------------------------------------+
|