168 lines
No EOL
5 KiB
MQL5
168 lines
No EOL
5 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| BarSeries.mqh |
| |
//| Copyright 2025, MetaQuotes Ltd. |
| |
//| https://www.mql5.com |
| |
//+------------------------------------------------------------------+
| |
#property copyright "Copyright 2025, MetaQuotes Ltd."
| |
#property link "https://www.mql5.com"
| |
#property version "1.10"
| |
| |
class BarSeries
| |
{
| |
private:
| |
string m_symbol;
| |
ENUM_TIMEFRAMES m_tf;
| |
MqlRates m_bars[];
| |
int m_barsCount;
| |
datetime m_lastBarOpen;
| |
bool m_initialized;
| |
bool m_asSeries; // true = index 0 is the most recent bar
| |
| |
public:
| |
BarSeries() : m_symbol(""), m_tf(PERIOD_CURRENT), m_barsCount(0),
| |
m_lastBarOpen(0), m_initialized(false), m_asSeries(true) {}
| |
~BarSeries() {}
| |
| |
bool Initialize(string symbol, ENUM_TIMEFRAMES tf, int initialBars, bool asSeries = true);
| |
bool Update(int barsCount = 3);
| |
| |
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; }
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Initialize |
| |
//+------------------------------------------------------------------+
| |
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.
| |
ArrayResize(m_bars, initialBars);
| |
ArraySetAsSeries(m_bars, m_asSeries);
| |
| |
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;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Update – refresh with the most recent barsCount candles |
| |
//+------------------------------------------------------------------+
| |
bool BarSeries::Update(int barsCount = 3)
| |
{
| |
if(!m_initialized)
| |
return false;
| |
| |
if(barsCount <= 0)
| |
return false;
| |
| |
// Temporary array with the same series direction as m_bars
| |
MqlRates rates[];
| |
ArrayResize(rates, barsCount);
| |
ArraySetAsSeries(rates, m_asSeries);
| |
| |
int copied = CopyRates(m_symbol, m_tf, 0, barsCount, rates);
| |
if(copied != barsCount)
| |
return false;
| |
| |
// Identify the latest bar time in the freshly copied data
| |
datetime newLastBarOpen = (m_asSeries) ? rates[0].time : rates[copied-1].time;
| |
| |
// 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);
| |
ArrayResize(m_bars, copied);
| |
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;
| |
return true;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Accessor helpers |
| |
//+------------------------------------------------------------------+
| |
double BarSeries::Close(int pShift)
| |
{
| |
if(pShift < 0 || pShift >= m_barsCount) return 0.0;
| |
return m_bars[pShift].close;
| |
}
| |
| |
double BarSeries::High(int pShift)
| |
{
| |
if(pShift < 0 || pShift >= m_barsCount) return 0.0;
| |
return m_bars[pShift].high;
| |
}
| |
| |
double BarSeries::Low(int pShift)
| |
{
| |
if(pShift < 0 || pShift >= m_barsCount) return 0.0;
| |
return m_bars[pShift].low;
| |
}
| |
| |
double BarSeries::Open(int pShift)
| |
{
| |
if(pShift < 0 || pShift >= m_barsCount) return 0.0;
| |
return m_bars[pShift].open;
| |
}
| |
| |
datetime BarSeries::Time(int pShift)
| |
{
| |
if(pShift < 0 || pShift >= m_barsCount) return 0;
| |
return m_bars[pShift].time;
| |
}
| |
| |
long BarSeries::TickVolume(int pShift)
| |
{
| |
if(pShift < 0 || pShift >= m_barsCount) return 0;
| |
return m_bars[pShift].tick_volume;
| |
}
| |
| |
long BarSeries::Volume(int pShift)
| |
{
| |
if(pShift < 0 || pShift >= m_barsCount) return 0;
| |
return m_bars[pShift].real_volume;
| |
}
| |
//+------------------------------------------------------------------+ |