490 lines
17 KiB
MQL5
490 lines
17 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:
| |||
long 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;
| |||
| |||
// --- 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 _zoneText, int _chartId = 0) : suffix("visuals_"),
| |||
m_dirty(true), txt_dirty(true), _txt_dirty(true), name(_name), symbol(_symbol), isBull(_isBull), zoneText(_zoneText), 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);
| |||
void SetA(TimePrice &_a) {a = _a; MarkDirty();}
| |||
void SetB(TimePrice &_b) {b = _b; MarkDirty();}
| |||
| |||
| |||
void Delete();
| |||
void DeleteAllText();
| |||
| |||
void DeleteLevel();
| |||
void DeleteLevelText();
| |||
| |||
void DeleteExtender();
| |||
void DeleteExtenderText();
| |||
| |||
string GetName() { return name;}
| |||
string GetTimeFrame() { return tf;}
| |||
string GetSymbol() { return symbol;}
| |||
};
| |||
//+------------------------------------------------------------------+
| |||
//| |
| |||
//+------------------------------------------------------------------+
| |||
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);
| |||
}
| |||
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()
| |||
{
| |||
ObjectDelete(chartId, zoneObjName);
| |||
txt_dirty = true;
| |||
DeleteLevelText();
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| |
| |||
//+------------------------------------------------------------------+
| |||
void ZoneObject::DeleteLevelText()
| |||
{
| |||
ObjectDelete(chartId, zoneTextObjName);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| |
| |||
//+------------------------------------------------------------------+
| |||
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);
| |||
}
| |||
| |||
ObjectCreate(chartId, name, OBJ_TEXT, 0, _a.x, newPrice);
| |||
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, MathMax(fontSize * 0.5, 8));
| |||
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);
| |||
}
| |||
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 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);
| |||
}
| |||
}
| |||
}
| |||
};
| |||
//+------------------------------------------------------------------+
|