384 lines
14 KiB
MQL5
384 lines
14 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| ZoneLevel.mqh |
| |
//| Copyright 2025, MetaQuotes Ltd. |
| |
//+------------------------------------------------------------------+
| |
#property copyright "Copyright 2025, MetaQuotes Ltd."
| |
#property link "https://www.mql5.com"
| |
#property version "1.00"
| |
| |
#include <ABCD/chartObjects.mqh>
| |
#include "../Utils/TimePrice.mqh"
| |
| |
class VisualSystem; // Forward declaration
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
class ZoneLevel
| |
{
| |
protected:
| |
long chartId;
| |
string name;
| |
string levelText;
| |
string levelObjName;
| |
string levelTextObjName;
| |
string levelExtText;
| |
string levelExtObjName;
| |
string levelExtTextObjName;
| |
string suffix;
| |
| |
color levelColor;
| |
color textColor;
| |
color mutedColor;
| |
| |
int width, pxDiff;
| |
TimePrice a;
| |
TimePrice b;
| |
TimePrice c;
| |
TimePrice d;
| |
| |
bool isBull;
| |
bool isDrawExt;
| |
| |
// --- Dirty tracking ---
| |
bool m_dirty; // true if level needs redraw
| |
bool m_lastVisible; // previous visibility state
| |
bool m_lastExtVisible; // previous visibility state
| |
| |
datetime m_from_time, m_to_time;
| |
double m_min_price, m_max_price;
| |
| |
| |
| |
public:
| |
ZoneLevel() : m_dirty(true), m_lastVisible(false), m_lastExtVisible(false) {}
| |
ZoneLevel(string _name, string text = "", string _levelExtText = "", int _chartId = 0, color clr = clrWhite, int _width = 1);
| |
~ZoneLevel() { Delete(); }
| |
| |
// Core methods
| |
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;
| |
}
| |
virtual bool IsExtVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice);
| |
virtual void Draw(long chart_id, VisualSystem* vs) {}
| |
virtual void DrawExt(long chart_id, VisualSystem* vs);
| |
// --- Dirty-aware render ---
| |
void UpdateDraw(VisualSystem* vs, datetime fromTime, datetime toTime, double minPrice, double maxPrice);
| |
| |
// Mark dirty when any property changes
| |
void MarkDirty() { m_dirty = true; }
| |
| |
// Property setters (call MarkDirty when changed)
| |
void SetText(string txt) { if(levelText != txt) { levelText = txt; MarkDirty(); } }
| |
void SetExtText(string txt) { if(levelExtText != txt) { levelExtText = 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);
| |
| |
// Getter
| |
string GetName() const { return name; }
| |
| |
// Object deletion
| |
void Delete();
| |
void DeleteText();
| |
void DeleteExtender();
| |
void DeleteExtenderText();
| |
void DeleteLevel();
| |
void DeleteLevelText();
| |
| |
| |
datetime GetFromTime() const { return m_from_time; } // store as member
| |
datetime GetToTime() const { return m_to_time; }
| |
double GetMinPrice() const { return m_min_price; }
| |
double GetMaxPrice() const { return m_max_price; }
| |
| |
protected:
| |
void DrawText(string name, string text, TimePrice &_a, double angle = 0);
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
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);
| |
| |
if(MathAbs(dx) < 0.000001 && MathAbs(dy) < 0.000001)
| |
{
| |
angleDeg = 0.0;
| |
return true;
| |
}
| |
| |
angleDeg = RadToDeg(MathArctan2(dy, dx));
| |
return true;
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Constructor |
| |
//+------------------------------------------------------------------+
| |
ZoneLevel::ZoneLevel(string _name, string text = "", string _levelExtText = "", int _chartId = 0, color clr = clrWhite, int _width = 1)
| |
: name(_name), levelText(text), levelExtText(_levelExtText), width(_width), chartId(_chartId),
| |
levelColor(clr), textColor(clr), isBull(false), isDrawExt(false), pxDiff(6), m_dirty(true), m_lastVisible(false), m_lastExtVisible(false)
| |
{
| |
suffix = "VIS_";
| |
levelObjName = suffix + _name;
| |
levelTextObjName = suffix + "txt_" + _name;
| |
levelExtTextObjName = suffix + "txt_ext_" + _name;
| |
levelExtObjName = suffix + "ext_" + _name;
| |
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Dirty-aware update – called by VisualSystem::Render() |
| |
//+------------------------------------------------------------------+
| |
void ZoneLevel::UpdateDraw(VisualSystem* vs,
| |
datetime fromTime, datetime toTime,
| |
double minPrice, double maxPrice)
| |
{
| |
// 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)
| |
{
| |
Draw(chartId, vs);
| |
}
| |
}
| |
| |
// 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)
| |
{
| |
DrawExt(chartId, vs);
| |
}
| |
}
| |
| |
// 5. Clear the shared dirty flag only after both components have been processed
| |
if(m_dirty)
| |
m_dirty = false;
| |
}
| |
//+------------------------------------------------------------------+
| |
//| Set Bull direction (moves text anchor) |
| |
//+------------------------------------------------------------------+
| |
void ZoneLevel::SetBull(bool _isBull, TimePrice &_a)
| |
{
| |
if(isBull == _isBull && a.x == _a.x && a.y == _a.y)
| |
return; // no change
| |
isBull = _isBull;
| |
a = _a;
| |
MarkDirty();
| |
| |
// Convert original to screen X,Y, adjust, then update object position
| |
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;
| |
ChartXYToTimePrice(chartId, x, y, sub_window, newTime, newPrice);
| |
ObjectSetDouble(chartId, name, OBJPROP_PRICE, newPrice);
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
bool ZoneLevel:: 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 ZoneLevel::SetDrawExt(bool _isDrawExt)
| |
{
| |
| |
isDrawExt = _isDrawExt;
| |
| |
MarkDirty();
| |
}
| |
//+------------------------------------------------------------------+
| |
//| Set font size for both text objects |
| |
//+------------------------------------------------------------------+
| |
void ZoneLevel::SetFontSize(int fontSize)
| |
{
| |
ObjectSetInteger(chartId, levelTextObjName, OBJPROP_FONTSIZE, fontSize);
| |
ObjectSetInteger(chartId, levelExtTextObjName, OBJPROP_FONTSIZE, fontSize);
| |
// Font change doesn't require redraw of geometry, but we mark dirty anyway
| |
MarkDirty();
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneLevel::SetAtBack(bool back = true)
| |
{
| |
ObjectSetInteger(chartId, name, OBJPROP_BACK, back);
| |
MarkDirty();
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneLevel::SetFill(bool fill = true)
| |
{
| |
ObjectSetInteger(chartId, name, OBJPROP_FILL, fill);
| |
MarkDirty();
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Draw text helper |
| |
//+------------------------------------------------------------------+
| |
void ZoneLevel::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;
| |
ChartXYToTimePrice(chartId, x, y, sub_window, newTime, newPrice);
| |
| |
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);
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneLevel::DrawExt(long chart_id, VisualSystem* vs)
| |
{
| |
if(a.Ok() && b.Ok())
| |
d = b;
| |
else
| |
if(a.Ok() && !b.Ok())
| |
{
| |
d = a;
| |
}
| |
| |
if(ObjectFind(chartId, levelExtObjName) < 0)
| |
{
| |
ObjectCreate(chartId, levelExtObjName, OBJ_TREND, 0, d.x, d.y, c.x, c.y);
| |
}
| |
ObjectSetInteger(chartId, levelExtObjName, OBJPROP_WIDTH, 1);
| |
ObjectSetInteger(chartId, levelExtObjName, OBJPROP_COLOR, levelColor);
| |
ObjectSetInteger(chartId, levelExtObjName, OBJPROP_STYLE, STYLE_DOT);
| |
ObjectSetInteger(chartId, levelExtObjName, OBJPROP_BACK, false);
| |
| |
| |
if(levelExtText != "" && vs.Zoom() >= 2)
| |
{
| |
double angle;
| |
CalcLineAngleDeg(d.x, d.y, c.x, c.y, angle);
| |
DrawText(levelExtTextObjName, levelExtText, d.CenterCoord(c), angle);
| |
// SetFontSize(vs.GetScaledFontSize(12));
| |
}
| |
}
| |
//+------------------------------------------------------------------+
| |
//| Delete all objects |
| |
//+------------------------------------------------------------------+
| |
void ZoneLevel::Delete()
| |
{
| |
// Print("Delete");
| |
DeleteExtender();
| |
DeleteLevel();
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneLevel::DeleteText()
| |
{
| |
DeleteLevelText();
| |
DeleteExtenderText();
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneLevel::DeleteExtender()
| |
{
| |
DeleteExtenderText();
| |
ObjectDelete(chartId, levelExtObjName);
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneLevel::DeleteExtenderText()
| |
{
| |
ObjectDelete(chartId, levelExtTextObjName);
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneLevel::DeleteLevel()
| |
{
| |
ObjectDelete(chartId, levelObjName);
| |
DeleteLevelText();
| |
}
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
void ZoneLevel::DeleteLevelText()
| |
{
| |
ObjectDelete(chartId, levelTextObjName);
| |
}
| |
//+------------------------------------------------------------------+
|