656 righe
Nessun fine linea
20 KiB
MQL5
656 righe
Nessun fine linea
20 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| DigitalRogue |
|
|
//| Algorithmic Trading Systems for MetaTrader 5 |
|
|
//| |
|
|
//| Previous Day High/Low + Equilibrium + Discount/Premium + Opens |
|
|
//| Historical daily segments + EA-readable buffers |
|
|
//| Trigger lines now stay on their relevant day |
|
|
//| |
|
|
//| Copyright 2026, DigitalRogue Trading |
|
|
//| All rights reserved. |
|
|
//| https://www.mql5.com/en/users/digitalrogue |
|
|
//+------------------------------------------------------------------+
|
|
#property strict
|
|
#property indicator_chart_window
|
|
#property indicator_plots 9
|
|
#property indicator_buffers 9
|
|
|
|
#property indicator_type1 DRAW_NONE
|
|
#property indicator_type2 DRAW_NONE
|
|
#property indicator_type3 DRAW_NONE
|
|
#property indicator_type4 DRAW_NONE
|
|
#property indicator_type5 DRAW_NONE
|
|
#property indicator_type6 DRAW_NONE
|
|
#property indicator_type7 DRAW_NONE
|
|
#property indicator_type8 DRAW_NONE
|
|
#property indicator_type9 DRAW_NONE
|
|
|
|
#property indicator_label1 "TriggerLong"
|
|
#property indicator_label2 "TriggerShort"
|
|
#property indicator_label3 "PDH"
|
|
#property indicator_label4 "PDL"
|
|
#property indicator_label5 "Equilibrium"
|
|
#property indicator_label6 "Discount"
|
|
#property indicator_label7 "Premium"
|
|
#property indicator_label8 "DailyOpen"
|
|
#property indicator_label9 "WeeklyOpen"
|
|
|
|
double TriggerLongBuffer[];
|
|
double TriggerShortBuffer[];
|
|
double PDHBuffer[];
|
|
double PDLBuffer[];
|
|
double MidBuffer[];
|
|
double DiscountBuffer[];
|
|
double PremiumBuffer[];
|
|
double DOpenBuffer[];
|
|
double WOpenBuffer[];
|
|
|
|
// --- cached current-day values for EA reading
|
|
double g_pdh = EMPTY_VALUE;
|
|
double g_pdl = EMPTY_VALUE;
|
|
double g_mid = EMPTY_VALUE;
|
|
double g_discount = EMPTY_VALUE;
|
|
double g_premium = EMPTY_VALUE;
|
|
double g_dopen = EMPTY_VALUE;
|
|
double g_wopen = EMPTY_VALUE;
|
|
double g_trigger_long = EMPTY_VALUE;
|
|
double g_trigger_short = EMPTY_VALUE;
|
|
|
|
// --- Visual inputs
|
|
input color PDH_Color = clrRed;
|
|
input color PDL_Color = clrDodgerBlue;
|
|
input int LineWidth = 2;
|
|
input bool ShowLabels = false;
|
|
|
|
input bool ShowMidLine = true;
|
|
input bool ShowQLines = true;
|
|
input int Q_Percent = 25;
|
|
input color MidQ_Color = clrSilver;
|
|
input int MidQ_Width = 1;
|
|
input ENUM_LINE_STYLE MidQ_Style = STYLE_DASH;
|
|
|
|
input bool ShowTriggerLines = true;
|
|
input color Trigger_Color = clrYellow;
|
|
input int Trigger_Width = 1;
|
|
input ENUM_LINE_STYLE Trigger_Style = STYLE_DASH;
|
|
|
|
input bool UseEquilibriumLabel = true;
|
|
|
|
// --- Open lines
|
|
input bool ShowDailyOpenLine = true;
|
|
input bool ShowWeeklyOpenLine = false;
|
|
input color Open_Color = clrSilver;
|
|
input int Open_Width = 1;
|
|
input ENUM_LINE_STYLE Open_Style = STYLE_DOT;
|
|
|
|
// --- Alerts
|
|
input bool EnableAlerts = true;
|
|
input bool AlertPDH_PDL = true;
|
|
input bool AlertMid_Q = false;
|
|
input bool AlertOpens = true;
|
|
input bool AlertOnTouch = false;
|
|
input int TouchTolerancePts = 5;
|
|
input bool LimitAlertsToOnePerCandle = true;
|
|
|
|
// --- History
|
|
input int MaxHistoricalDays = 10;
|
|
|
|
// --- current-day active names
|
|
string curPDH_Line = "";
|
|
string curPDL_Line = "";
|
|
string curMID_Line = "";
|
|
string curQLO_Line = "";
|
|
string curQHI_Line = "";
|
|
string curDOPEN_Line = "";
|
|
string curWOPEN_Line = "";
|
|
string curTrigLong_Line = "";
|
|
string curTrigShort_Line = "";
|
|
|
|
string curPDH_Label = "";
|
|
string curPDL_Label = "";
|
|
string curMID_Label = "";
|
|
string curQLO_Label = "";
|
|
string curQHI_Label = "";
|
|
string curDOPEN_Label = "";
|
|
string curWOPEN_Label = "";
|
|
string curTrigLong_Label = "";
|
|
string curTrigShort_Label = "";
|
|
|
|
// --- State
|
|
datetime last_day = 0;
|
|
datetime lastAlertBarTime = 0;
|
|
|
|
int lastSide_PDH = 0;
|
|
int lastSide_PDL = 0;
|
|
int lastSide_MID = 0;
|
|
int lastSide_QLO = 0;
|
|
int lastSide_QHI = 0;
|
|
int lastSide_DOPEN = 0;
|
|
int lastSide_WOPEN = 0;
|
|
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
IndicatorSetString(INDICATOR_SHORTNAME,
|
|
"Prev Day Levels History + Trigger Segments + EA Buffers");
|
|
|
|
SetIndexBuffer(0, TriggerLongBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(1, TriggerShortBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(2, PDHBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(3, PDLBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(4, MidBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(5, DiscountBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(6, PremiumBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(7, DOpenBuffer, INDICATOR_DATA);
|
|
SetIndexBuffer(8, WOpenBuffer, INDICATOR_DATA);
|
|
|
|
ArraySetAsSeries(TriggerLongBuffer, true);
|
|
ArraySetAsSeries(TriggerShortBuffer, true);
|
|
ArraySetAsSeries(PDHBuffer, true);
|
|
ArraySetAsSeries(PDLBuffer, true);
|
|
ArraySetAsSeries(MidBuffer, true);
|
|
ArraySetAsSeries(DiscountBuffer, true);
|
|
ArraySetAsSeries(PremiumBuffer, true);
|
|
ArraySetAsSeries(DOpenBuffer, true);
|
|
ArraySetAsSeries(WOpenBuffer, true);
|
|
|
|
for(int i=0; i<9; i++)
|
|
PlotIndexSetDouble(i, PLOT_EMPTY_VALUE, EMPTY_VALUE);
|
|
|
|
BuildHistoricalLevels(MaxHistoricalDays);
|
|
|
|
datetime d0 = iTime(_Symbol, PERIOD_D1, 0);
|
|
if(d0 != 0)
|
|
last_day = d0;
|
|
|
|
ResetAlertSides();
|
|
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
// keep history on chart by default
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
int OnCalculate(const int rates_total,
|
|
const int prev_calculated,
|
|
const datetime &time[],
|
|
const double &open[],
|
|
const double &high[],
|
|
const double &low[],
|
|
const double &close[],
|
|
const long &tick_volume[],
|
|
const long &volume[],
|
|
const int &spread[])
|
|
{
|
|
if(rates_total <= 0)
|
|
return 0;
|
|
|
|
datetime d0 = iTime(_Symbol, PERIOD_D1, 0);
|
|
if(d0 == 0)
|
|
return rates_total;
|
|
|
|
if(d0 != last_day)
|
|
{
|
|
last_day = d0;
|
|
BuildTodayLevels();
|
|
ResetAlertSides();
|
|
lastAlertBarTime = 0;
|
|
CleanupOldHistory(MaxHistoricalDays);
|
|
}
|
|
else
|
|
{
|
|
if(ShowLabels)
|
|
AlignLabels();
|
|
}
|
|
|
|
// keep buffers valid on every call for EA access
|
|
for(int i=0; i<rates_total; i++)
|
|
{
|
|
TriggerLongBuffer[i] = g_trigger_long;
|
|
TriggerShortBuffer[i] = g_trigger_short;
|
|
PDHBuffer[i] = g_pdh;
|
|
PDLBuffer[i] = g_pdl;
|
|
MidBuffer[i] = g_mid;
|
|
DiscountBuffer[i] = g_discount;
|
|
PremiumBuffer[i] = g_premium;
|
|
DOpenBuffer[i] = g_dopen;
|
|
WOpenBuffer[i] = g_wopen;
|
|
}
|
|
|
|
if(EnableAlerts)
|
|
CheckAlerts(time);
|
|
|
|
return rates_total;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void BuildHistoricalLevels(int daysBack)
|
|
{
|
|
int maxBars = Bars(_Symbol, PERIOD_D1);
|
|
if(maxBars < 3) return;
|
|
|
|
int limit = MathMin(daysBack, maxBars - 2);
|
|
|
|
for(int shift = limit; shift >= 0; shift--)
|
|
BuildDaySet(shift, shift == 0);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void BuildTodayLevels()
|
|
{
|
|
BuildDaySet(0, true);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void BuildDaySet(int shift, bool makeCurrent)
|
|
{
|
|
datetime dayStart = iTime(_Symbol, PERIOD_D1, shift);
|
|
if(dayStart == 0) return;
|
|
|
|
datetime nextDay = dayStart + 86400;
|
|
|
|
double pdh = iHigh(_Symbol, PERIOD_D1, shift + 1);
|
|
double pdl = iLow(_Symbol, PERIOD_D1, shift + 1);
|
|
if(pdh == 0.0 || pdl == 0.0) return;
|
|
|
|
double range = pdh - pdl;
|
|
if(range <= 0.0) return;
|
|
|
|
int q = (int)MathMax(1, MathMin(49, Q_Percent));
|
|
double mid = (pdh + pdl) * 0.5;
|
|
double qlo = pdl + range * (q / 100.0);
|
|
double qhi = pdl + range * (1.0 - (q / 100.0));
|
|
double trigger_long = (qlo + pdl) * 0.5;
|
|
double trigger_short = (qhi + pdh) * 0.5;
|
|
|
|
double dopen = EMPTY_VALUE;
|
|
double wopen = EMPTY_VALUE;
|
|
|
|
if(ShowDailyOpenLine)
|
|
{
|
|
double tmp = iOpen(_Symbol, PERIOD_D1, shift);
|
|
if(tmp > 0.0) dopen = tmp;
|
|
}
|
|
|
|
if(ShowWeeklyOpenLine)
|
|
{
|
|
int wShift = iBarShift(_Symbol, PERIOD_W1, dayStart, false);
|
|
if(wShift >= 0)
|
|
{
|
|
double tmp = iOpen(_Symbol, PERIOD_W1, wShift);
|
|
if(tmp > 0.0) wopen = tmp;
|
|
}
|
|
}
|
|
|
|
string tag = DayTag(dayStart);
|
|
|
|
string pdhLine = "PDH_" + tag;
|
|
string pdlLine = "PDL_" + tag;
|
|
string midLine = "MID_" + tag;
|
|
string qloLine = "QLO_" + tag;
|
|
string qhiLine = "QHI_" + tag;
|
|
string dopenLine = "DOPEN_" + tag;
|
|
string wopenLine = "WOPEN_" + tag;
|
|
string trigLongLine = "TRIGGER_LONG_" + tag;
|
|
string trigShortLine = "TRIGGER_SHORT_" + tag;
|
|
|
|
string pdhLabel = "LBL_PDH_" + tag;
|
|
string pdlLabel = "LBL_PDL_" + tag;
|
|
string midLabel = "LBL_MID_" + tag;
|
|
string qloLabel = "LBL_QLO_" + tag;
|
|
string qhiLabel = "LBL_QHI_" + tag;
|
|
string dopenLabel = "LBL_DOPEN_" + tag;
|
|
string wopenLabel = "LBL_WOPEN_" + tag;
|
|
string trigLongLabel = "LBL_TRIGGER_LONG_" + tag;
|
|
string trigShortLabel = "LBL_TRIGGER_SHORT_" + tag;
|
|
|
|
// draw dated segments
|
|
CreateSegment(pdhLine, dayStart, nextDay, pdh, PDH_Color, LineWidth, STYLE_SOLID);
|
|
CreateSegment(pdlLine, dayStart, nextDay, pdl, PDL_Color, LineWidth, STYLE_SOLID);
|
|
|
|
if(ShowMidLine)
|
|
CreateSegment(midLine, dayStart, nextDay, mid, MidQ_Color, MidQ_Width, MidQ_Style);
|
|
|
|
if(ShowQLines)
|
|
{
|
|
CreateSegment(qloLine, dayStart, nextDay, qlo, MidQ_Color, MidQ_Width, MidQ_Style);
|
|
CreateSegment(qhiLine, dayStart, nextDay, qhi, MidQ_Color, MidQ_Width, MidQ_Style);
|
|
}
|
|
|
|
if(ShowTriggerLines)
|
|
{
|
|
CreateSegment(trigLongLine, dayStart, nextDay, trigger_long, Trigger_Color, Trigger_Width, Trigger_Style);
|
|
CreateSegment(trigShortLine, dayStart, nextDay, trigger_short, Trigger_Color, Trigger_Width, Trigger_Style);
|
|
}
|
|
|
|
if(ShowDailyOpenLine && dopen != EMPTY_VALUE)
|
|
CreateSegment(dopenLine, dayStart, nextDay, dopen, Open_Color, Open_Width, Open_Style);
|
|
|
|
if(ShowWeeklyOpenLine && wopen != EMPTY_VALUE)
|
|
CreateSegment(wopenLine, dayStart, nextDay, wopen, Open_Color, Open_Width, STYLE_DASH);
|
|
|
|
if(ShowLabels)
|
|
{
|
|
CreateLabel(pdhLabel, "PDH", pdh, PDH_Color, nextDay);
|
|
CreateLabel(pdlLabel, "PDL", pdl, PDL_Color, nextDay);
|
|
|
|
if(ShowMidLine)
|
|
CreateLabel(midLabel, (UseEquilibriumLabel ? "EQUILIBRIUM" : "MID"), mid, MidQ_Color, nextDay);
|
|
|
|
if(ShowQLines)
|
|
{
|
|
CreateLabel(qloLabel, "DISCOUNT", qlo, MidQ_Color, nextDay);
|
|
CreateLabel(qhiLabel, "PREMIUM", qhi, MidQ_Color, nextDay);
|
|
}
|
|
|
|
if(ShowTriggerLines)
|
|
{
|
|
CreateLabel(trigLongLabel, "LONG TRIG", trigger_long, Trigger_Color, nextDay);
|
|
CreateLabel(trigShortLabel, "SHORT TRIG", trigger_short, Trigger_Color, nextDay);
|
|
}
|
|
|
|
if(ShowDailyOpenLine && dopen != EMPTY_VALUE)
|
|
CreateLabel(dopenLabel, "D-OPEN", dopen, Open_Color, nextDay);
|
|
|
|
if(ShowWeeklyOpenLine && wopen != EMPTY_VALUE)
|
|
CreateLabel(wopenLabel, "W-OPEN", wopen, Open_Color, nextDay);
|
|
}
|
|
|
|
if(makeCurrent)
|
|
{
|
|
curPDH_Line = pdhLine;
|
|
curPDL_Line = pdlLine;
|
|
curMID_Line = midLine;
|
|
curQLO_Line = qloLine;
|
|
curQHI_Line = qhiLine;
|
|
curDOPEN_Line = dopenLine;
|
|
curWOPEN_Line = wopenLine;
|
|
curTrigLong_Line = trigLongLine;
|
|
curTrigShort_Line = trigShortLine;
|
|
|
|
curPDH_Label = pdhLabel;
|
|
curPDL_Label = pdlLabel;
|
|
curMID_Label = midLabel;
|
|
curQLO_Label = qloLabel;
|
|
curQHI_Label = qhiLabel;
|
|
curDOPEN_Label = dopenLabel;
|
|
curWOPEN_Label = wopenLabel;
|
|
curTrigLong_Label = trigLongLabel;
|
|
curTrigShort_Label = trigShortLabel;
|
|
|
|
// cache current-day values for EA buffers
|
|
g_pdh = pdh;
|
|
g_pdl = pdl;
|
|
g_mid = mid;
|
|
g_discount = qlo;
|
|
g_premium = qhi;
|
|
g_dopen = dopen;
|
|
g_wopen = wopen;
|
|
g_trigger_long = trigger_long;
|
|
g_trigger_short = trigger_short;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void ResetAlertSides()
|
|
{
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
if(bid <= 0) return;
|
|
|
|
lastSide_PDH = Side(bid, g_pdh, 0);
|
|
lastSide_PDL = Side(bid, g_pdl, 0);
|
|
lastSide_MID = Side(bid, g_mid, 0);
|
|
lastSide_QLO = Side(bid, g_discount, 0);
|
|
lastSide_QHI = Side(bid, g_premium, 0);
|
|
lastSide_DOPEN = Side(bid, g_dopen, 0);
|
|
lastSide_WOPEN = Side(bid, g_wopen, 0);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void CheckAlerts(const datetime &time[])
|
|
{
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
if(bid <= 0) return;
|
|
|
|
datetime barTime = time[0];
|
|
int tolPts = (AlertOnTouch ? TouchTolerancePts : 0);
|
|
|
|
if(AlertPDH_PDL)
|
|
{
|
|
if(g_pdh > 0) CheckCrossOrTouch(barTime, "PDH", bid, g_pdh, tolPts, lastSide_PDH);
|
|
if(g_pdl > 0) CheckCrossOrTouch(barTime, "PDL", bid, g_pdl, tolPts, lastSide_PDL);
|
|
}
|
|
|
|
if(AlertMid_Q)
|
|
{
|
|
if(ShowMidLine && g_mid > 0)
|
|
{
|
|
string midTag = (UseEquilibriumLabel ? "EQUILIBRIUM" : "MID");
|
|
CheckCrossOrTouch(barTime, midTag, bid, g_mid, tolPts, lastSide_MID);
|
|
}
|
|
|
|
if(ShowQLines)
|
|
{
|
|
if(g_discount > 0) CheckCrossOrTouch(barTime, "DISCOUNT", bid, g_discount, tolPts, lastSide_QLO);
|
|
if(g_premium > 0) CheckCrossOrTouch(barTime, "PREMIUM", bid, g_premium, tolPts, lastSide_QHI);
|
|
}
|
|
}
|
|
|
|
if(AlertOpens)
|
|
{
|
|
if(ShowDailyOpenLine && g_dopen > 0)
|
|
CheckCrossOrTouch(barTime, "D-OPEN", bid, g_dopen, tolPts, lastSide_DOPEN);
|
|
|
|
if(ShowWeeklyOpenLine && g_wopen > 0)
|
|
CheckCrossOrTouch(barTime, "W-OPEN", bid, g_wopen, tolPts, lastSide_WOPEN);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
int Side(double price, double level, int tolPoints)
|
|
{
|
|
if(level <= 0 || level == EMPTY_VALUE) return 0;
|
|
double tol = tolPoints * _Point;
|
|
if(price > level + tol) return +1;
|
|
if(price < level - tol) return -1;
|
|
return 0;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
bool CanFireThisCandle(datetime barTime)
|
|
{
|
|
if(!LimitAlertsToOnePerCandle) return true;
|
|
return (lastAlertBarTime != barTime);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void MarkAlertFired(datetime barTime)
|
|
{
|
|
if(LimitAlertsToOnePerCandle)
|
|
lastAlertBarTime = barTime;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void FireAlert(datetime barTime, const string msg)
|
|
{
|
|
if(!CanFireThisCandle(barTime)) return;
|
|
Alert(msg);
|
|
Print(msg);
|
|
MarkAlertFired(barTime);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void CheckCrossOrTouch(datetime barTime, const string tag, double bid, double level, int tolPts, int &lastSide)
|
|
{
|
|
if(AlertOnTouch && tolPts > 0)
|
|
{
|
|
double tol = tolPts * _Point;
|
|
if(MathAbs(bid - level) <= tol)
|
|
{
|
|
FireAlert(barTime, StringFormat("%s: TOUCH %s @ %s (Bid=%s)",
|
|
_Symbol, tag,
|
|
DoubleToString(level, _Digits),
|
|
DoubleToString(bid, _Digits)));
|
|
}
|
|
}
|
|
|
|
int sideNow = Side(bid, level, tolPts);
|
|
if(sideNow == 0) return;
|
|
|
|
if(lastSide == 0)
|
|
{
|
|
lastSide = sideNow;
|
|
return;
|
|
}
|
|
|
|
if(sideNow != lastSide)
|
|
{
|
|
string dir = (sideNow > lastSide ? "UP" : "DOWN");
|
|
FireAlert(barTime, StringFormat("%s: CROSS %s %s @ %s (Bid=%s)",
|
|
_Symbol, dir, tag,
|
|
DoubleToString(level, _Digits),
|
|
DoubleToString(bid, _Digits)));
|
|
lastSide = sideNow;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
string DayTag(datetime t)
|
|
{
|
|
MqlDateTime dt;
|
|
TimeToStruct(t, dt);
|
|
return StringFormat("%04d%02d%02d", dt.year, dt.mon, dt.day);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void CreateSegment(const string name,
|
|
datetime t1,
|
|
datetime t2,
|
|
double price,
|
|
color clr,
|
|
int width,
|
|
ENUM_LINE_STYLE style)
|
|
{
|
|
if(price == EMPTY_VALUE || price <= 0.0) return;
|
|
|
|
if(ObjectFind(0, name) == -1)
|
|
ObjectCreate(0, name, OBJ_TREND, 0, t1, price, t2, price);
|
|
|
|
ObjectMove(0, name, 0, t1, price);
|
|
ObjectMove(0, name, 1, t2, price);
|
|
|
|
ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false);
|
|
ObjectSetInteger(0, name, OBJPROP_RAY_LEFT, false);
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
|
ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
|
|
ObjectSetInteger(0, name, OBJPROP_STYLE, style);
|
|
ObjectSetInteger(0, name, OBJPROP_BACK, true);
|
|
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
|
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void CreateLabel(const string name, const string text, double price, color clr, datetime t)
|
|
{
|
|
if(price == EMPTY_VALUE || price <= 0.0) return;
|
|
|
|
if(ObjectFind(0, name) == -1)
|
|
{
|
|
ObjectCreate(0, name, OBJ_TEXT, 0, t, price);
|
|
ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT);
|
|
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 10);
|
|
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
|
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
|
|
}
|
|
|
|
ObjectSetString(0, name, OBJPROP_TEXT, text);
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
|
ObjectMove(0, name, 0, t, price);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void AlignLabels()
|
|
{
|
|
datetime t = iTime(_Symbol, PERIOD_D1, 0) + 86400;
|
|
|
|
AlignOne(curPDH_Label, curPDH_Line, t);
|
|
AlignOne(curPDL_Label, curPDL_Line, t);
|
|
AlignOne(curMID_Label, curMID_Line, t);
|
|
AlignOne(curQLO_Label, curQLO_Line, t);
|
|
AlignOne(curQHI_Label, curQHI_Line, t);
|
|
AlignOne(curDOPEN_Label, curDOPEN_Line, t);
|
|
AlignOne(curWOPEN_Label, curWOPEN_Line, t);
|
|
AlignOne(curTrigLong_Label, curTrigLong_Line, t);
|
|
AlignOne(curTrigShort_Label, curTrigShort_Line, t);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void AlignOne(const string labelName, const string lineName, datetime t)
|
|
{
|
|
if(ObjectFind(0, labelName) == -1) return;
|
|
if(ObjectFind(0, lineName) == -1) return;
|
|
|
|
double p = ObjectGetDouble(0, lineName, OBJPROP_PRICE, 0);
|
|
if(p > 0.0 && p != EMPTY_VALUE)
|
|
ObjectMove(0, labelName, 0, t, p);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void CleanupOldHistory(int keepDays)
|
|
{
|
|
if(keepDays <= 0) return;
|
|
|
|
datetime cutoff = iTime(_Symbol, PERIOD_D1, keepDays);
|
|
if(cutoff <= 0) return;
|
|
|
|
int total = ObjectsTotal(0);
|
|
for(int i = total - 1; i >= 0; i--)
|
|
{
|
|
string name = ObjectName(0, i);
|
|
if(StringLen(name) < 8) continue;
|
|
|
|
string suffix = StringSubstr(name, StringLen(name) - 8, 8);
|
|
if(!IsDigitsOnly(suffix)) continue;
|
|
|
|
datetime objDay = TagToDate(suffix);
|
|
if(objDay > 0 && objDay < cutoff)
|
|
ObjectDelete(0, name);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
bool IsDigitsOnly(const string s)
|
|
{
|
|
for(int i = 0; i < StringLen(s); i++)
|
|
{
|
|
ushort c = StringGetCharacter(s, i);
|
|
if(c < '0' || c > '9')
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
datetime TagToDate(const string tag)
|
|
{
|
|
if(StringLen(tag) != 8) return 0;
|
|
|
|
int y = (int)StringToInteger(StringSubstr(tag, 0, 4));
|
|
int m = (int)StringToInteger(StringSubstr(tag, 4, 2));
|
|
int d = (int)StringToInteger(StringSubstr(tag, 6, 2));
|
|
|
|
MqlDateTime dt;
|
|
dt.year = y;
|
|
dt.mon = m;
|
|
dt.day = d;
|
|
dt.hour = 0;
|
|
dt.min = 0;
|
|
dt.sec = 0;
|
|
|
|
return StructToTime(dt);
|
|
}
|
|
//+------------------------------------------------------------------+ |