533 lines
18 KiB
MQL5
533 lines
18 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| ZoneObject.mqh |
| |
//| Copyright 2025, MetaQuotes Ltd. |
| |
//| https://www.mql5.com |
| |
//+------------------------------------------------------------------+
| |
#property copyright "Copyright 2025, MetaQuotes Ltd."
| |
#property link "https://www.mql5.com"
| |
#property version "1.00"
| |
| |
#include "../Market/TimeHelpers.mqh"
| |
#include <ABCD/chartObjects.mqh>
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
class ZoneObject
| |
{
| |
protected:
| |
int chartId;
| |
ENUM_TIMEFRAMES tf;
| |
| |
string name;
| |
string symbol;
| |
string suffix;
| |
| |
string zoneText;
| |
string zoneObjName;
| |
string zoneTextObjName;
| |
| |
string _zoneText;
| |
string _zoneObjName;
| |
string _zoneTextObjName;
| |
| |
color levelColor;
| |
color textColor;
| |
| |
| |
int pxDiff;
| |
bool isDrawExt;
| |
| |
TimePrice a;
| |
TimePrice b;
| |
TimePrice c, d;
| |
bool isBull;
| |
bool isBroken;
| |
| |
// --- Dirty tracking ---
| |
bool m_dirty, txt_dirty, _txt_dirty;
| |
bool m_lastVisible;
| |
bool m_lastExtVisible;
| |
| |
datetime m_from_time, m_to_time;
| |
double m_min_price, m_max_price;
| |
| |
virtual void Draw(int zoomLevel, int minZoom) = 0;
| |
virtual void DrawExt(int zoomLevel, int minZoom);
| |
virtual void DrawText(string name, string text, TimePrice &_a, double angle = 0);
| |
| |
// Object deletion
| |
| |
virtual bool IsExtVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice);
| |
virtual bool IsVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice)
| |
{
| |
m_from_time = fromTime;
| |
m_to_time = toTime;
| |
m_min_price = minPrice;
| |
m_max_price = maxPrice;
| |
| |
return false;
| |
}
| |
void MarkDirty() { m_dirty = true; }
| |
public:
| |
ZoneObject(string _name, string _symbol, ENUM_TIMEFRAMES _tf, bool _isBull, string _zoneTxt0, int _chartId = 0) : suffix("visuals_"),
| |
m_dirty(true), txt_dirty(true), _txt_dirty(true), name(_name), symbol(_symbol), isBull(_isBull), zoneText(_zoneTxt0), tf(_tf), chartId(_chartId), pxDiff(6),
| |
levelColor(clrRosyBrown), textColor(clrRosyBrown), m_lastExtVisible(false), isDrawExt(true)
| |
{
| |
zoneObjName = suffix + name + "_" + GetTimeframeDescription(tf);
| |
zoneTextObjName = suffix + name + "_txt_" + GetTimeframeDescription(tf);
| |
| |
_zoneObjName = suffix + name + "_ext_" + GetTimeframeDescription(tf);
| |
_zoneTextObjName = suffix + name + "_txt_ext_" + GetTimeframeDescription(tf);
| |
| |
}
| |
~ZoneObject() {Delete();}
| |
| |
void UpdateDraw(int zoomLevel, datetime fromTime, datetime toTime, double minPrice, double maxPrice, int minZoom = 2);
| |
| |
virtual void SetFontSize(int fontSize);
| |
| |
// Property setters (call MarkDirty when changed)
| |
void SetText(string txt) { if(zoneText != txt) { zoneText = txt; MarkDirty(); } }
| |
void SetExtText(string txt) { if(_zoneText != txt) { _zoneText = txt; MarkDirty(); } }
| |
void SetColor(color clr) { if(levelColor != clr) { levelColor = clr; MarkDirty(); } }
| |
void SetBothColor(color clr) { if(levelColor != clr) { levelColor = clr; textColor = clr; MarkDirty(); } }
| |
// void SetExtEnd(TimePrice &_a) {c = _a; MarkDirty();}
| |
void SetBull(bool _isBull, TimePrice &_a);
| |
void SetDrawExt(bool _isDrawExt);
| |
// void SetFontSize(int fontSize);
| |
void SetAtBack(bool back = true);
| |
void SetFill(bool fill = true);
| |
virtual void SetA(TimePrice &_a) {a = _a; MarkDirty();}
| |
virtual void SetB(TimePrice &_b) {b = _b; MarkDirty();}
| |
| |
| |
void Delete();
| |
void DeleteAllText();
| |
| |
void DeleteLevel();
| |
void DeleteLevelText();
| |
| |
void DeleteExtender();
| |
void DeleteExtenderText();
| |
| |
string GetText() { return zoneText;}
| |
string GetName() { return name;}
| |
ENUM_TIMEFRAMES GetTimeFrame() { return tf;}
| |
string GetSymbol() { return symbol;}
| |
TimePrice GetA() { return a;}
| |
TimePrice GetB() { return b;}
| |
};
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool ZoneObject:: IsExtVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice)
| |
{
| |
// Print("[PointLevel]::IsVisible() ",fromTime," :: ", a.x,"-- ", a.x >= fromTime);
| |
return (d.x >= fromTime && c.x <= toTime && d.y >= minPrice && c.y <= maxPrice);
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneObject::SetDrawExt(bool _isDrawExt = false)
| |
{
| |
| |
isDrawExt = _isDrawExt;
| |
| |
MarkDirty();
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneObject::UpdateDraw(int zoomLevel, datetime fromTime, datetime toTime, double minPrice, double maxPrice, int minZoom = 2)
| |
{
| |
// 1. Check visibility for main level
| |
bool isVisible = IsVisible(fromTime, toTime, minPrice, maxPrice);
| |
bool visibilityChanged = (isVisible != m_lastVisible);
| |
m_lastVisible = isVisible;
| |
| |
// 2. Check visibility for extender
| |
bool isExtVisible = IsExtVisible(fromTime, toTime, minPrice, maxPrice);
| |
bool visibilityExtChanged = (isExtVisible != m_lastExtVisible);
| |
m_lastExtVisible = isExtVisible;
| |
| |
// 3. Handle MAIN level
| |
if(!isVisible)
| |
{
| |
if(visibilityChanged)
| |
DeleteLevel(); // main left visible area → delete objects
| |
}
| |
else
| |
{
| |
// main is visible – redraw if dirty OR just became visible
| |
if(visibilityChanged || m_dirty || txt_dirty)
| |
Draw(zoomLevel, minZoom);
| |
}
| |
| |
// 4. Handle EXTENDER level (same dirty flag)
| |
if(!isExtVisible)
| |
{
| |
| |
if(visibilityExtChanged)
| |
DeleteExtender(); // ext left visible area → delete objects
| |
}
| |
else
| |
{
| |
// ext is visible – redraw if dirty OR just became visible
| |
if(visibilityExtChanged || m_dirty || _txt_dirty)
| |
{
| |
DrawExt(zoomLevel, minZoom);
| |
}
| |
}
| |
// 5. Clear the shared dirty flag only after both components have been processed
| |
if(m_dirty)
| |
m_dirty = false;
| |
| |
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneObject::DrawExt(int zoomLevel, int minZoom)
| |
{
| |
| |
if(!isDrawExt)
| |
{
| |
return;
| |
}
| |
if(ObjectFind(chartId, _zoneObjName) < 0)
| |
{
| |
ObjectCreate(chartId, _zoneObjName, OBJ_TREND, 0, c.x, c.y, d.x, d.y);
| |
GetVisibilityMaskManual(_zoneObjName, tf);
| |
}
| |
else
| |
{
| |
ObjectMove(chartId, _zoneObjName, 0, c.x, c.y);
| |
ObjectMove(chartId, _zoneObjName, 1, d.x, d.y);
| |
| |
}
| |
| |
ObjectSetInteger(chartId, _zoneObjName, OBJPROP_COLOR, levelColor);
| |
ObjectSetInteger(chartId, _zoneObjName, OBJPROP_STYLE, STYLE_DOT);
| |
| |
if(_zoneText != "" && zoomLevel >= minZoom)
| |
{
| |
double angle;
| |
CalcLineAngleDeg(chartId, c.x, c.y, d.x, d.y, angle);
| |
DrawText(_zoneTextObjName, _zoneText, c.CenterCoord(d), angle);
| |
}
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneObject::DeleteExtender()
| |
{
| |
DeleteExtenderText();
| |
ObjectDelete(chartId, _zoneObjName);
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneObject::DeleteExtenderText()
| |
{
| |
ObjectDelete(chartId, _zoneTextObjName);
| |
_txt_dirty = true;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneObject::SetFontSize(int fontSize)
| |
{
| |
ObjectSetInteger(chartId, _zoneTextObjName, OBJPROP_FONTSIZE, fontSize);
| |
ObjectSetInteger(chartId, zoneTextObjName, OBJPROP_FONTSIZE, fontSize);
| |
MarkDirty();
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneObject::Delete()
| |
{
| |
| |
DeleteLevel();
| |
DeleteExtender();
| |
MarkDirty();
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneObject::DeleteLevel()
| |
{
| |
bool res1 = ObjectDelete(chartId, zoneObjName);
| |
// Print("Deleting main object: ", zoneObjName, " on chart ", chartId, " -> Result: ", res1);
| |
| |
txt_dirty = true;
| |
DeleteLevelText(); // also modify this one
| |
}
| |
| |
void ZoneObject::DeleteLevelText()
| |
{
| |
bool res2 = ObjectDelete(chartId, zoneTextObjName);
| |
// Print("Deleting text object: ", zoneTextObjName, " on chart ", chartId, " -> Result: ", res2);
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneObject:: DeleteAllText()
| |
{
| |
DeleteExtenderText();
| |
DeleteLevelText();
| |
txt_dirty = true;
| |
_txt_dirty = true;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneObject::DrawText(string _name, string text, TimePrice &_a, double angle = 0)
| |
{
| |
int x = 0, y = 0;
| |
ChartTimePriceToXY(chartId, 0, _a.x, _a.y, x, y);
| |
| |
if(!isBull)
| |
y += pxDiff;
| |
else
| |
y -= pxDiff;
| |
| |
int sub_window = 0;
| |
| |
datetime newTime = 0;
| |
double newPrice = 0.0;
| |
| |
bool result = ChartXYToTimePrice(chartId, x, y, sub_window, newTime, newPrice);
| |
| |
if(ObjectFind(chartId, _name) < 0)
| |
{
| |
ObjectCreate(chartId, _name, OBJ_TEXT, 0, _a.x, newPrice);
| |
GetVisibilityMaskManual(_name, tf);
| |
}
| |
else
| |
ObjectMove(chartId, _name, 0, _a.x, _a.y);
| |
| |
ObjectSetString(chartId, _name, OBJPROP_TEXT, text);
| |
ObjectSetInteger(chartId, _name, OBJPROP_COLOR, textColor);
| |
ObjectSetDouble(chartId, _name, OBJPROP_ANGLE, angle);
| |
| |
if(!isBull)
| |
ObjectSetInteger(chartId, _name, OBJPROP_ANCHOR, ANCHOR_UPPER);
| |
else
| |
ObjectSetInteger(chartId, _name, OBJPROP_ANCHOR, ANCHOR_LOWER);
| |
| |
if(txt_dirty)
| |
txt_dirty = false;
| |
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
class PointLevel : public ZoneObject
| |
{
| |
private:
| |
| |
public:
| |
PointLevel(string _name, string _symbol, ENUM_TIMEFRAMES _tf, bool _isBull, TimePrice &_point, string txt = ""):
| |
ZoneObject(_name, _symbol, _tf, _isBull, txt)
| |
{
| |
a.Set(_point);
| |
b.Set(_point);
| |
c.Set(_point);
| |
d.Set(TimeCurrent(), c.y);
| |
}
| |
virtual void SetFontSize(int fontSize)
| |
{
| |
ObjectSetInteger(chartId, _zoneTextObjName, OBJPROP_FONTSIZE, fontSize);
| |
ObjectSetInteger(chartId, zoneTextObjName, OBJPROP_FONTSIZE, fontSize);
| |
ObjectSetInteger(chartId, zoneObjName, OBJPROP_FONTSIZE, (long)MathMax(fontSize * 0.5, 8));
| |
MarkDirty();
| |
}
| |
virtual void SetA(TimePrice &_point)
| |
{
| |
a = _point;
| |
b.Set(_point);
| |
c.Set(_point);
| |
d.Set(TimeCurrent(), c.y);
| |
MarkDirty();
| |
| |
}
| |
| |
| |
virtual bool IsVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice)
| |
{
| |
ZoneObject::IsVisible(fromTime, toTime, minPrice, maxPrice);
| |
| |
return (a.x >= fromTime && a.x <= toTime && a.y >= minPrice && a.y <= maxPrice);
| |
}
| |
| |
virtual void Draw(int zoomLevel, int minZoom)
| |
{
| |
if(ObjectFind(chartId, zoneObjName) < 0)
| |
{
| |
ObjectCreate(chartId, zoneObjName, OBJ_TEXT, 0, a.x, a.y, b.x, b.y);
| |
GetVisibilityMaskManual(zoneObjName, tf);
| |
}
| |
else
| |
ObjectMove(chartId, zoneObjName, 0, a.x, a.y);
| |
| |
ObjectSetInteger(chartId, zoneObjName, OBJPROP_COLOR, levelColor);
| |
ObjectSetString(chartId, zoneObjName, OBJPROP_TEXT, "●");
| |
ObjectSetInteger(chartId, zoneObjName, OBJPROP_ANCHOR, ANCHOR_CENTER);
| |
| |
if(zoneText != "" && zoomLevel >= minZoom)
| |
{
| |
double angle;
| |
CalcLineAngleDeg(chartId, a.x, a.y, b.x, b.y, angle);
| |
DrawText(zoneTextObjName, zoneText, a.CenterCoord(b), angle);
| |
}
| |
if(isDrawExt)
| |
{
| |
DrawExt(zoomLevel, minZoom);
| |
}
| |
| |
}
| |
};
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
class TrendLevel : public ZoneObject
| |
{
| |
private:
| |
| |
public:
| |
TrendLevel(string _name, string _symbol, ENUM_TIMEFRAMES _tf, bool _isBull, TimePrice &_point, TimePrice &_pointB, string txt = ""):
| |
ZoneObject(_name, _symbol, _tf, _isBull, txt)
| |
{
| |
a.Set(_point);
| |
b.Set(_pointB);
| |
c.Set(_point.MaxT(_pointB));
| |
d.Set(TimeCurrent(), c.y);
| |
| |
}
| |
| |
| |
virtual void SetB(TimePrice &_point)
| |
{
| |
| |
b.Set(_point);
| |
c.Set(a.MaxT(b));
| |
d.Set(TimeCurrent(), c.y);
| |
MarkDirty();
| |
| |
}
| |
| |
| |
| |
virtual bool IsVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice)
| |
{
| |
ZoneObject::IsVisible(fromTime, toTime, minPrice, maxPrice);
| |
| |
return MathMax(a.x, b.x) >= fromTime && MathMin(a.x, b.x) <= toTime &&
| |
MathMax(a.y, b.y) >= minPrice && MathMin(a.y, b.y) <= maxPrice;
| |
}
| |
| |
virtual void Draw(int zoomLevel, int minZoom)
| |
{
| |
if(ObjectFind(chartId, zoneObjName) < 0)
| |
{
| |
ObjectCreate(chartId, zoneObjName, OBJ_TREND, 0, a.x, a.y, b.x, b.y);
| |
GetVisibilityMaskManual(zoneObjName, tf);
| |
}
| |
ObjectSetInteger(chartId, zoneObjName, OBJPROP_COLOR, levelColor);
| |
| |
if(zoneText != "" && zoomLevel >= minZoom)
| |
{
| |
double angle;
| |
CalcLineAngleDeg(chartId, a.x, a.y, b.x, b.y, angle);
| |
DrawText(zoneTextObjName, zoneText, a.CenterCoord(b), angle);
| |
| |
if(isDrawExt)
| |
{
| |
DrawExt(zoomLevel, minZoom);
| |
}
| |
}
| |
}
| |
};
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
class RectLevel : public ZoneObject
| |
{
| |
private:
| |
| |
public:
| |
RectLevel(string _name, string _symbol, ENUM_TIMEFRAMES _tf, bool _isBull, TimePrice &_point, TimePrice &_pointB, string txt = ""):
| |
ZoneObject(_name, _symbol, _tf, _isBull, txt)
| |
{
| |
a.Set(_point);
| |
b.Set(_pointB);
| |
c.Set(_point.MaxT(_pointB).x, _point.Max(_pointB).y);
| |
d.Set(TimeCurrent(), _point.Min(_pointB).y);
| |
}
| |
| |
virtual bool IsVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice)
| |
{
| |
ZoneObject::IsVisible(fromTime, toTime, minPrice, maxPrice);
| |
| |
return MathMax(a.x, b.x) >= fromTime && MathMin(a.x, b.x) <= toTime &&
| |
MathMax(a.y, b.y) >= minPrice && MathMin(a.y, b.y) <= maxPrice;
| |
}
| |
| |
virtual void DrawText(string _name, string text, TimePrice &_a, double angle = 0)
| |
{
| |
if(ObjectFind(chartId, _name) < 0)
| |
{
| |
ObjectCreate(chartId, _name, OBJ_TEXT, 0, _a.x, _a.y);
| |
GetVisibilityMaskManual(_name, tf);
| |
}
| |
| |
ObjectSetString(chartId, _name, OBJPROP_TEXT, text);
| |
ObjectSetInteger(chartId, _name, OBJPROP_COLOR, textColor);
| |
ObjectSetDouble(chartId, _name, OBJPROP_ANGLE, angle);
| |
ObjectSetInteger(chartId, _name, OBJPROP_ANCHOR, ANCHOR_CENTER);
| |
if(txt_dirty)
| |
txt_dirty = false;
| |
| |
}
| |
| |
virtual void DrawExt(int zoomLevel, int minZoom)
| |
{
| |
if(ObjectFind(chartId, _zoneObjName) < 0)
| |
{
| |
ObjectCreate(chartId, _zoneObjName, OBJ_RECTANGLE, 0, c.x, c.y, d.x, d.y);
| |
GetVisibilityMaskManual(_zoneObjName, tf);
| |
}
| |
ObjectSetInteger(chartId, _zoneObjName, OBJPROP_COLOR, levelColor);
| |
ObjectSetInteger(chartId, _zoneObjName, OBJPROP_STYLE, STYLE_DOT);
| |
if(_zoneText != "" && zoomLevel >= minZoom)
| |
{
| |
| |
DrawText(_zoneTextObjName, _zoneText, c.CenterCoord(d));
| |
}
| |
}
| |
| |
virtual void Draw(int zoomLevel, int minZoom)
| |
{
| |
if(ObjectFind(chartId, zoneObjName) < 0)
| |
{
| |
ObjectCreate(chartId, zoneObjName, OBJ_RECTANGLE, 0, a.x, a.y, b.x, b.y);
| |
GetVisibilityMaskManual(zoneObjName, tf);
| |
}
| |
ObjectSetInteger(chartId, zoneObjName, OBJPROP_COLOR, levelColor);
| |
| |
if(zoneText != "" && zoomLevel >= minZoom)
| |
{
| |
| |
| |
DrawText(zoneTextObjName, zoneText, a.CenterCoord(b));
| |
| |
if(isDrawExt)
| |
{
| |
DrawExt(zoomLevel, minZoom);
| |
}
| |
}
| |
}
| |
};
| |
//+------------------------------------------------------------------+
|