mql5/Experts/_FrameWork/Market/Candles.mqh
younglubah-gmail a4b935d8d8
2026-09-30 09:06:59 +02:00

169 lines
No EOL
5 KiB
MQL5

//+------------------------------------------------------------------+
//| Candles.mqh |
//| Copyright 2026, Concrete Goonies |
//| https://concrete-goonies.web.app |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Concrete Goonies"
#property link "https://concrete-goonies.web.app"
#property version "1.00"
class Candles
{
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:
Candles() : m_symbol(""), m_tf(PERIOD_CURRENT), m_barsCount(0),
m_lastBarOpen(0), m_initialized(false), m_asSeries(true) {}
~Candles() {}
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 Candles::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 Candles::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 Candles::Close(int pShift)
{
if(pShift < 0 || pShift >= m_barsCount) return 0.0;
return m_bars[pShift].close;
}
double Candles::High(int pShift)
{
if(pShift < 0 || pShift >= m_barsCount) return 0.0;
return m_bars[pShift].high;
}
double Candles::Low(int pShift)
{
if(pShift < 0 || pShift >= m_barsCount) return 0.0;
return m_bars[pShift].low;
}
double Candles::Open(int pShift)
{
if(pShift < 0 || pShift >= m_barsCount) return 0.0;
return m_bars[pShift].open;
}
datetime Candles::Time(int pShift)
{
if(pShift < 0 || pShift >= m_barsCount) return 0;
return m_bars[pShift].time;
}
long Candles::TickVolume(int pShift)
{
if(pShift < 0 || pShift >= m_barsCount) return 0;
return m_bars[pShift].tick_volume;
}
long Candles::Volume(int pShift)
{
if(pShift < 0 || pShift >= m_barsCount) return 0;
return m_bars[pShift].real_volume;
}
//+------------------------------------------------------------------+