2026-08-04 23:54:09 +02:00 | | | //+------------------------------------------------------------------+
|
| | | //| BarSeries.mqh |
|
| | | //| Copyright 2025, MetaQuotes Ltd. |
|
| | | //| https://www.mql5.com |
|
| | | //+------------------------------------------------------------------+
|
| | | #property copyright "Copyright 2025, MetaQuotes Ltd."
|
| | | #property link "https://www.mql5.com"
|
2026-08-14 16:18:08 +02:00 | | | #property version "1.10"
|
2026-08-04 23:54:09 +02:00 | | |
|
| | | class BarSeries
|
2026-08-14 16:18:08 +02:00 | | | {
|
2026-08-04 23:54:09 +02:00 | | | private:
|
2026-08-06 00:18:27 +02:00 | | | string m_symbol;
|
| | | ENUM_TIMEFRAMES m_tf;
|
| | | MqlRates m_bars[];
|
| | | int m_barsCount;
|
| | | datetime m_lastBarOpen;
|
| | | bool m_initialized;
|
2026-08-14 16:18:08 +02:00 | | | bool m_asSeries; // true = index 0 is the most recent bar
|
2026-08-06 00:18:27 +02:00 | | |
|
2026-08-04 23:54:09 +02:00 | | | public:
|
2026-08-14 16:18:08 +02:00 | | | BarSeries() : m_symbol(""), m_tf(PERIOD_CURRENT), m_barsCount(0),
|
| | | m_lastBarOpen(0), m_initialized(false), m_asSeries(true) {}
|
| | | ~BarSeries() {}
|
2026-08-06 00:18:27 +02:00 | | |
|
2026-08-14 16:18:08 +02:00 | | | bool Initialize(string symbol, ENUM_TIMEFRAMES tf, int initialBars, bool asSeries = true);
|
| | | bool Update(int barsCount = 3);
|
2026-08-06 00:18:27 +02:00 | | |
|
| | | double Close(int pShift);
|
| | | double High(int pShift);
|
| | | double Low(int pShift);
|
| | | double Open(int pShift);
|
| | | datetime Time(int pShift);
|
| | | long TickVolume(int pShift);
|
| | | long Volume(int pShift);
|
| | |
|
| | | const MqlRates operator[](int index) const { return m_bars[index]; }
|
| | | int Size() const { return m_barsCount; }
|
| | | datetime LastBarOpen() const { return m_lastBarOpen; }
|
2026-08-14 16:18:08 +02:00 | | | };
|
| | |
|
2026-08-04 23:54:09 +02:00 | | | //+------------------------------------------------------------------+
|
2026-08-14 16:18:08 +02:00 | | | //| Initialize |
|
2026-08-04 23:54:09 +02:00 | | | //+------------------------------------------------------------------+
|
2026-08-14 16:18:08 +02:00 | | | bool BarSeries::Initialize(string symbol, ENUM_TIMEFRAMES tf,
|
| | | int initialBars, bool asSeries)
|
| | | {
|
| | | m_symbol = symbol;
|
| | | m_tf = tf;
|
| | | m_asSeries = asSeries;
|
| | |
|
| | | // Reset state
|
| | | m_initialized = false;
|
| | | m_barsCount = 0;
|
| | | m_lastBarOpen = 0;
|
| | | ArrayFree(m_bars);
|
| | |
|
| | | if(initialBars <= 0)
|
| | | return false;
|
| | |
|
| | | // Resize and set the series flag BEFORE copying so that CopyRates
|
| | | // fills the array in the desired direction.
|
2026-08-06 00:18:27 +02:00 | | | ArrayResize(m_bars, initialBars);
|
2026-08-14 16:18:08 +02:00 | | | ArraySetAsSeries(m_bars, m_asSeries);
|
2026-08-06 00:18:27 +02:00 | | |
|
2026-08-14 16:18:08 +02:00 | | | int copied = CopyRates(m_symbol, m_tf, 0, initialBars, m_bars);
|
| | | if(copied <= 0)
|
| | | {
|
| | | ArrayFree(m_bars);
|
| | | return false;
|
| | | }
|
| | |
|
| | | m_barsCount = copied;
|
| | |
|
| | | // With the series flag set, the latest bar is always at index 0
|
| | | // if m_asSeries == true, otherwise at index copied-1.
|
| | | m_lastBarOpen = (m_asSeries) ? m_bars[0].time : m_bars[copied-1].time;
|
| | |
|
| | | m_initialized = true;
|
| | | return true;
|
| | | }
|
2026-08-06 00:18:27 +02:00 | | |
|
2026-08-04 23:54:09 +02:00 | | | //+------------------------------------------------------------------+
|
2026-08-14 16:18:08 +02:00 | | | //| Update – refresh with the most recent barsCount candles |
|
2026-08-04 23:54:09 +02:00 | | | //+------------------------------------------------------------------+
|
2026-08-06 00:18:27 +02:00 | | | bool BarSeries::Update(int barsCount = 3)
|
2026-08-14 16:18:08 +02:00 | | | {
|
2026-08-06 00:18:27 +02:00 | | | if(!m_initialized)
|
| | | return false;
|
| | |
|
2026-08-14 16:18:08 +02:00 | | | if(barsCount <= 0)
|
| | | return false;
|
| | |
|
| | | // Temporary array with the same series direction as m_bars
|
2026-08-06 00:18:27 +02:00 | | | MqlRates rates[];
|
| | | ArrayResize(rates, barsCount);
|
2026-08-14 16:18:08 +02:00 | | | ArraySetAsSeries(rates, m_asSeries);
|
| | |
|
2026-08-06 00:18:27 +02:00 | | | int copied = CopyRates(m_symbol, m_tf, 0, barsCount, rates);
|
2026-08-14 16:18:08 +02:00 | | | if(copied != barsCount)
|
2026-08-06 00:18:27 +02:00 | | | return false;
|
| | |
|
2026-08-14 16:18:08 +02:00 | | | // Identify the latest bar time in the freshly copied data
|
| | | datetime newLastBarOpen = (m_asSeries) ? rates[0].time : rates[copied-1].time;
|
2026-08-06 00:18:27 +02:00 | | |
|
2026-08-14 16:18:08 +02:00 | | | // If the latest bar hasn't changed, no new bar has formed
|
| | | if(newLastBarOpen == m_lastBarOpen)
|
| | | return false;
|
| | |
|
| | | // Replace internal buffer, preserving series flag
|
| | | ArrayFree(m_bars);
|
2026-08-06 00:18:27 +02:00 | | | ArrayResize(m_bars, copied);
|
2026-08-14 16:18:08 +02:00 | | | ArraySetAsSeries(m_bars, m_asSeries);
|
| | |
|
| | | // Copy element by element – safest with series flags
|
| | | for(int i = 0; i < copied; i++)
|
| | | m_bars[i] = rates[i];
|
| | |
|
| | | m_barsCount = copied;
|
| | | m_lastBarOpen = newLastBarOpen;
|
2026-08-06 00:18:27 +02:00 | | | return true;
|
2026-08-14 16:18:08 +02:00 | | | }
|
2026-08-09 07:14:59 +02:00 | | |
|
2026-08-06 00:18:27 +02:00 | | | //+------------------------------------------------------------------+
|
2026-08-14 16:18:08 +02:00 | | | //| Accessor helpers |
|
2026-08-06 00:18:27 +02:00 | | | //+------------------------------------------------------------------+
|
| | | double BarSeries::Close(int pShift)
|
2026-08-14 16:18:08 +02:00 | | | {
|
| | | if(pShift < 0 || pShift >= m_barsCount) return 0.0;
|
2026-08-06 00:18:27 +02:00 | | | return m_bars[pShift].close;
|
2026-08-14 16:18:08 +02:00 | | | }
|
2026-08-06 00:18:27 +02:00 | | |
|
| | | double BarSeries::High(int pShift)
|
2026-08-14 16:18:08 +02:00 | | | {
|
| | | if(pShift < 0 || pShift >= m_barsCount) return 0.0;
|
2026-08-06 00:18:27 +02:00 | | | return m_bars[pShift].high;
|
2026-08-14 16:18:08 +02:00 | | | }
|
2026-08-06 00:18:27 +02:00 | | |
|
| | | double BarSeries::Low(int pShift)
|
2026-08-14 16:18:08 +02:00 | | | {
|
| | | if(pShift < 0 || pShift >= m_barsCount) return 0.0;
|
2026-08-06 00:18:27 +02:00 | | | return m_bars[pShift].low;
|
2026-08-14 16:18:08 +02:00 | | | }
|
2026-08-06 00:18:27 +02:00 | | |
|
| | | double BarSeries::Open(int pShift)
|
2026-08-14 16:18:08 +02:00 | | | {
|
| | | if(pShift < 0 || pShift >= m_barsCount) return 0.0;
|
2026-08-06 00:18:27 +02:00 | | | return m_bars[pShift].open;
|
2026-08-14 16:18:08 +02:00 | | | }
|
2026-08-06 00:18:27 +02:00 | | |
|
| | | datetime BarSeries::Time(int pShift)
|
2026-08-14 16:18:08 +02:00 | | | {
|
| | | if(pShift < 0 || pShift >= m_barsCount) return 0;
|
2026-08-06 00:18:27 +02:00 | | | return m_bars[pShift].time;
|
2026-08-14 16:18:08 +02:00 | | | }
|
2026-08-06 00:18:27 +02:00 | | |
|
| | | long BarSeries::TickVolume(int pShift)
|
2026-08-14 16:18:08 +02:00 | | | {
|
| | | if(pShift < 0 || pShift >= m_barsCount) return 0;
|
2026-08-06 00:18:27 +02:00 | | | return m_bars[pShift].tick_volume;
|
2026-08-14 16:18:08 +02:00 | | | }
|
2026-08-06 00:18:27 +02:00 | | |
|
| | | long BarSeries::Volume(int pShift)
|
2026-08-14 16:18:08 +02:00 | | | {
|
| | | if(pShift < 0 || pShift >= m_barsCount) return 0;
|
2026-08-06 00:18:27 +02:00 | | | return m_bars[pShift].real_volume;
|
2026-08-14 16:18:08 +02:00 | | | }
|
| | | //+------------------------------------------------------------------+
|