2026-08-04 23:54:09 +02:00 | | | //+------------------------------------------------------------------+
|
| | | //| TimeHelpers.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"
|
| | |
|
2026-08-09 07:14:59 +02:00 | | | #include "../Configs/SymbolConfig.mqh"
|
2026-08-04 23:54:09 +02:00 | | |
|
| | | //+------------------------------------------------------------------+
|
2026-08-09 07:14:59 +02:00 | | | //| TimePrice.mqh |
|
2026-08-04 23:54:09 +02:00 | | | //+------------------------------------------------------------------+
|
| | |
|
2026-08-09 07:14:59 +02:00 | | | struct TimePrice
|
2026-08-04 23:54:09 +02:00 | | | {
|
2026-08-09 07:14:59 +02:00 | | | int i;
|
| | | datetime x;
|
| | | double y;
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
2026-08-04 23:54:09 +02:00 | | |
|
2026-08-09 07:14:59 +02:00 | | | void Reset()
|
| | | {
|
| | | x = 0;
|
| | | y = 0.0;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | |
|
| | | void Set(datetime timeValue, double priceValue)
|
| | | {
|
| | |
|
| | | x = timeValue;
|
| | | y = priceValue;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | |
|
| | | void Set(int index, datetime timeValue, double priceValue)
|
| | | {
|
| | | i = index;
|
| | | x = timeValue;
|
| | | y = priceValue;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
2026-08-04 23:54:09 +02:00 | | |
|
2026-08-09 07:14:59 +02:00 | | | void Set(TimePrice &tp)
|
| | | {
|
| | | Set(tp.i, tp.x, tp.y);
|
| | | }
|
| | |
|
| | | bool Ok()
|
| | | {
|
| | | return x > 0 && y > 0.0;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | |
|
| | | TimePrice CenterCoord(TimePrice &p2)
|
| | | {
|
| | | if(!Ok() && !p2.Ok())
|
| | | {
|
| | |
|
| | | return p2;
|
| | | }
|
| | |
|
| | | if(!Ok())
|
| | | return p2;
|
| | |
|
| | | if(!p2.Ok())
|
| | | return *this;
|
| | |
|
| | | TimePrice tp;
|
| | |
|
| | | datetime tx = x + (p2.x - x) / 2;
|
| | | double py = (y + p2.y) / 2.0;
|
| | |
|
| | | tp.Set(tx, py);
|
| | |
|
| | | return tp;
|
| | | }
|
| | |
|
| | | string ToString()
|
| | | {
|
| | | return "Time: " + TimeToString(x) + " | Price: " + DoubleToString(y);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | TimePrice Min(TimePrice &p2)
|
| | | {
|
| | | TimePrice tp;
|
| | |
|
| | | if(y <= p2.y)
|
| | | tp.Set(i, x, y);
|
| | | else
|
| | | tp.Set(p2.i, p2.x, p2.y);
|
| | |
|
| | | return tp;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | TimePrice Max(TimePrice &p2)
|
| | | {
|
| | | TimePrice tp;
|
| | |
|
| | | if(y >= p2.y)
|
| | | tp.Set(i, x, y);
|
| | | else
|
| | | tp.Set(p2.i, p2.x, p2.y);
|
| | |
|
| | | return tp;
|
| | | }
|
2026-08-14 16:18:08 +02:00 | | |
|
| | | TimePrice MaxT(TimePrice &p2)
|
| | | {
|
| | | TimePrice tp;
|
| | |
|
| | | if(x >= p2.x)
|
| | | tp.Set(i, x, y);
|
| | | else
|
| | | tp.Set(p2.i, p2.x, p2.y);
|
| | |
|
| | | return tp;
|
| | | }
|
2026-08-06 00:18:27 +02:00 | | | };
|
2026-08-09 07:14:59 +02:00 | | |
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | |
|
2026-08-04 23:54:09 +02:00 | | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | class TimeHelpers
|
| | | {
|
| | | private:
|
| | | datetime SubtractDays(datetime time, int days);
|
| | | public:
|
| | | TimeHelpers() {}
|
| | | ~TimeHelpers() {}
|
| | |
|
| | | datetime PreviousDayStart(datetime currentTime);
|
| | | datetime PreviousWeekStart(datetime currentTime, int weekOffset = 1);
|
| | | datetime FirstOfPreviousMonths(datetime currentTime, int monthsBack = 1);
|
2026-08-09 07:14:59 +02:00 | | | // Inside class TimeHelpers, add:
|
2026-08-14 16:18:08 +02:00 | | | datetime NextRelaunchTime(datetime currentTime, ENUM_TIME_FETCH_TYPE fetchType, int count);
|
2026-08-04 23:54:09 +02:00 | | | };
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | datetime TimeHelpers:: SubtractDays(datetime time, int days)
|
| | | {
|
| | | MqlDateTime dt;
|
| | | TimeToStruct(time, dt);
|
| | |
|
| | | dt.day -= days;
|
| | |
|
| | | while(dt.day <= 0)
|
| | | {
|
| | | dt.mon--;
|
| | | if(dt.mon <= 0)
|
| | | {
|
| | | dt.mon = 12;
|
| | | dt.year--;
|
| | | }
|
| | | // Get days in the previous month
|
| | | MqlDateTime lastDay;
|
| | | lastDay.year = dt.year;
|
| | | lastDay.mon = dt.mon;
|
| | | lastDay.day = 1;
|
| | | lastDay.hour = 0;
|
2026-08-06 00:18:27 +02:00 | | | lastDay.min = 1;
|
2026-08-04 23:54:09 +02:00 | | | lastDay.sec = 0;
|
| | |
|
| | | datetime firstOfMonth = StructToTime(lastDay);
|
| | | datetime lastOfPrevMonth = firstOfMonth - 1;
|
| | |
|
| | | TimeToStruct(lastOfPrevMonth, lastDay);
|
| | | dt.day += lastDay.day; // add days of previous month
|
| | | }
|
| | |
|
| | | dt.hour = 0;
|
| | | dt.min = 0;
|
| | | dt.sec = 0;
|
| | |
|
| | | return StructToTime(dt);
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | datetime TimeHelpers:: PreviousDayStart(datetime currentTime)
|
| | | {
|
| | | return SubtractDays(currentTime, 1);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | datetime TimeHelpers::PreviousWeekStart(datetime currentTime, int weekOffset = 1)
|
| | | {
|
| | | MqlDateTime dt;
|
| | | TimeToStruct(currentTime, dt);
|
| | |
|
| | | int dow = dt.day_of_week; // 0=Sunday, 1=Monday, ..., 6=Saturday
|
| | | int offsetToMonday;
|
| | |
|
| | | if(dow == 0)
|
| | | offsetToMonday = 6; // Sunday -> go back 6 days to Monday
|
| | | else
|
| | | offsetToMonday = dow - 1; // Monday=0, Tuesday=1, ...
|
| | |
|
| | | // total days to subtract: offset to Monday of current week + 7 * weekOffset
|
| | | int totalDays = offsetToMonday + 7 * weekOffset;
|
| | |
|
| | | return SubtractDays(currentTime, totalDays);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | datetime TimeHelpers::FirstOfPreviousMonths(datetime currentTime, int monthsBack)
|
| | | {
|
| | | MqlDateTime dt;
|
| | | TimeToStruct(currentTime, dt);
|
| | | // 1. Go to the first day of the current month (00:00)
|
| | | datetime firstCurrent = SubtractDays(currentTime, dt.day - 1);
|
| | |
|
| | | // 2. Subtract monthsBack months by subtracting full months day by day
|
| | | for(int i = 0; i < monthsBack; i++)
|
| | | {
|
| | | // Get the last day of the previous month = firstCurrent - 1 second
|
| | | datetime lastPrev = firstCurrent - 1;
|
| | | TimeToStruct(lastPrev, dt);
|
| | |
|
| | | int daysInPrevMonth = dt.day; // day-of-month gives days count
|
| | | firstCurrent = SubtractDays(firstCurrent, daysInPrevMonth);
|
| | | }
|
| | |
|
| | | return firstCurrent; // already at 00:00
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
2026-08-09 07:14:59 +02:00 | | | //| |
|
2026-08-04 23:54:09 +02:00 | | | //+------------------------------------------------------------------+
|
2026-08-09 07:14:59 +02:00 | | | datetime TimeHelpers::NextRelaunchTime(
|
| | | datetime currentTime,
|
| | | ENUM_TIME_FETCH_TYPE fetchType,
|
| | | int count)
|
2026-08-14 16:18:08 +02:00 | | | {
|
2026-08-09 07:14:59 +02:00 | | | MqlDateTime dt;
|
| | | TimeToStruct(currentTime, dt);
|
| | |
|
2026-08-14 16:18:08 +02:00 | | | // Prevent invalid values
|
2026-08-10 20:23:58 +02:00 | | | if(count < 1)
|
| | | count = 1;
|
| | |
|
2026-08-09 07:14:59 +02:00 | | | dt.hour = 0;
|
| | | dt.min = 0;
|
| | | dt.sec = 0;
|
| | |
|
2026-08-10 20:23:58 +02:00 | | | datetime baseTime = StructToTime(dt);
|
| | |
|
2026-08-09 07:14:59 +02:00 | | | switch(fetchType)
|
2026-08-14 16:18:08 +02:00 | | | {
|
2026-08-10 20:23:58 +02:00 | | | //============================================================
|
| | | // PREVIOUS DAY
|
| | | // count = 1 → tomorrow
|
| | | // count = 2 → 2 days from today
|
| | | // count = 3 → 3 days from today
|
| | | //============================================================
|
2026-08-09 07:14:59 +02:00 | | | case FETCH_PREVIOUS_DAY:
|
2026-08-14 16:18:08 +02:00 | | | {
|
2026-08-10 20:23:58 +02:00 | | | return baseTime + (count * 86400);
|
2026-08-14 16:18:08 +02:00 | | | }
|
2026-08-09 07:14:59 +02:00 | | |
|
2026-08-10 20:23:58 +02:00 | | | //============================================================
|
| | | // PREVIOUS WEEK
|
| | | // count = 1 → next Monday
|
| | | // count = 2 → Monday after next
|
| | | // count = 3 → 3 Mondays from now
|
| | | //============================================================
|
2026-08-09 07:14:59 +02:00 | | | case FETCH_PREVIOUS_WEEK:
|
2026-08-14 16:18:08 +02:00 | | | {
|
2026-08-09 07:14:59 +02:00 | | | int dow = dt.day_of_week == 0 ? 7 : dt.day_of_week;
|
2026-08-10 20:23:58 +02:00 | | |
|
| | | // Days until next Monday
|
| | | int daysUntilMonday = 8 - dow;
|
| | |
|
| | | // Additional complete weeks
|
| | | int additionalWeeks = (count - 1) * 7;
|
| | |
|
| | | return baseTime
|
| | | + (daysUntilMonday + additionalWeeks) * 86400;
|
2026-08-14 16:18:08 +02:00 | | | }
|
2026-08-09 07:14:59 +02:00 | | |
|
2026-08-10 20:23:58 +02:00 | | | //============================================================
|
| | | // FIRST PREVIOUS MONTH
|
| | | // count = 1 → first day of next month
|
| | | // count = 2 → first day two months from now
|
| | | // count = 3 → first day three months from now
|
| | | //============================================================
|
2026-08-09 07:14:59 +02:00 | | | case FETCH_FIRST_PREVIOUS_MONTH:
|
2026-08-14 16:18:08 +02:00 | | | {
|
2026-08-09 07:14:59 +02:00 | | | dt.day = 1;
|
| | |
|
2026-08-10 20:23:58 +02:00 | | | dt.mon += count;
|
| | |
|
| | | while(dt.mon > 12)
|
2026-08-14 16:18:08 +02:00 | | | {
|
2026-08-10 20:23:58 +02:00 | | | dt.mon -= 12;
|
2026-08-09 07:14:59 +02:00 | | | dt.year++;
|
2026-08-14 16:18:08 +02:00 | | | }
|
2026-08-09 07:14:59 +02:00 | | |
|
| | | return StructToTime(dt);
|
2026-08-14 16:18:08 +02:00 | | | }
|
2026-08-09 07:14:59 +02:00 | | |
|
2026-08-10 20:23:58 +02:00 | | | //============================================================
|
2026-08-09 07:14:59 +02:00 | | | default:
|
| | | return 0;
|
2026-08-14 16:18:08 +02:00 | | | }
|
| | | }
|
| | | //+------------------------------------------------------------------+
|