//+------------------------------------------------------------------+ //| CBarsbydate.mqh | //| Based on Price.mqh (Andrew Young) | //+------------------------------------------------------------------+ #property strict #property copyright "Andrew Young" #property link "http://www.expertadvisorbook.com" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CBarsbydate { private: int BarCount; string symbol; public: MqlRates bar[]; CBarsbydate(void); // --- Range loading --- // Loads bars from [fromTime .. toTime] inclusive-ish (broker availability rules apply) int UpdateRange(string pSymbol, ENUM_TIMEFRAMES pPeriod, datetime fromTime, datetime toTime); // Convenience: // useMonths=true -> monthsCount means months back // useMonths=false -> monthsCount means candle count int UpdateFromMonthsBack(string pSymbol, ENUM_TIMEFRAMES pPeriod, int monthsCount, bool useMonths = true); // --- Accessors --- 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); // --- Extract arrays --- void GetCloses(double &closes[]); void GetOpens(double &opens[]); void GetHighs(double &highs[]); void GetLows(double &lows[]); void GetTimes(datetime ×[]); void GetVolumes(long &volumes[]); int GetBarCount(); int IndexByTime(datetime t); double Point(); int Size() { return ArraySize(bar); } bool UpdateLastBar(const MqlRates &rate); bool AppendBar(const MqlRates &rate); private: datetime FirstOfPreviousMonths(datetime currentTime, int monthsBack); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CBarsbydate::CBarsbydate(void) { BarCount = 0; // IMPORTANT: // index 0 = oldest, index BarCount - 1 = newest. // DO NOT ArraySetAsSeries(bar, true). } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ datetime CBarsbydate::FirstOfPreviousMonths(datetime currentTime, int monthsBack) { MqlDateTime dt; TimeToStruct(currentTime, dt); dt.mon -= monthsBack; while(dt.mon <= 0) { dt.mon += 12; dt.year--; } dt.day = 1; dt.hour = 0; dt.min = 0; dt.sec = 0; return StructToTime(dt); } //+------------------------------------------------------------------+ //| Load bars by time range | //| bar[0] oldest, bar[count-1] newest | //+------------------------------------------------------------------+ int CBarsbydate::UpdateRange(string pSymbol, ENUM_TIMEFRAMES pPeriod, datetime fromTime, datetime toTime) { ArrayResize(bar, 0); BarCount = 0; symbol = pSymbol; if(fromTime <= 0 || toTime <= 0 || toTime <= fromTime) { Print("CBarsbydate::UpdateRange invalid times. from=", TimeToString(fromTime), " to=", TimeToString(toTime)); return 0; } MqlRates temp[]; ArraySetAsSeries(temp, false); int copied = CopyRates(pSymbol, pPeriod, fromTime, toTime, temp); if(copied <= 0) { Print("CBarsbydate::UpdateRange CopyRates failed. err=", GetLastError(), " from=", TimeToString(fromTime), " to=", TimeToString(toTime)); return 0; } ArrayResize(bar, copied); for(int i = 0; i < copied; i++) bar[i] = temp[i]; BarCount = copied; return copied; } //+------------------------------------------------------------------+ //| Convenience: | //| useMonths=true -> monthsCount means months back | //| useMonths=false -> monthsCount means candle count | //+------------------------------------------------------------------+ int CBarsbydate::UpdateFromMonthsBack(string pSymbol, ENUM_TIMEFRAMES pPeriod, int monthsCount, bool useMonths = true) { symbol = pSymbol; if(monthsCount <= 0) { Print("CBarsbydate::UpdateFromMonthsBack invalid monthsCount=", monthsCount); ArrayResize(bar, 0); BarCount = 0; return 0; } ArrayResize(bar, 0); BarCount = 0; if(useMonths) { datetime toTime = TimeCurrent(); datetime fromTime = FirstOfPreviousMonths(toTime, monthsCount); return UpdateRange(pSymbol, pPeriod, fromTime, toTime); } MqlRates temp[]; ArraySetAsSeries(temp, false); int copied = CopyRates(pSymbol, pPeriod, 0, monthsCount, temp); if(copied <= 0) { Print("CBarsbydate::UpdateFromMonthsBack CopyRates by count failed. err=", GetLastError(), " count=", monthsCount); return 0; } ArrayResize(bar, copied); for(int i = 0; i < copied; i++) bar[i] = temp[i]; BarCount = copied; return copied; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CBarsbydate::Point() { return SymbolInfoDouble(symbol, SYMBOL_POINT); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int CBarsbydate::GetBarCount(void) { return BarCount; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CBarsbydate::Close(int pShift) { if(pShift < 0 || pShift >= BarCount) return 0.0; return bar[pShift].close; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CBarsbydate::High(int pShift) { if(pShift < 0 || pShift >= BarCount) return 0.0; return bar[pShift].high; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CBarsbydate::Low(int pShift) { if(pShift < 0 || pShift >= BarCount) return 0.0; return bar[pShift].low; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CBarsbydate::Open(int pShift) { if(pShift < 0 || pShift >= BarCount) return 0.0; return bar[pShift].open; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ datetime CBarsbydate::Time(int pShift) { if(pShift < 0 || pShift >= BarCount) return 0; return bar[pShift].time; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ long CBarsbydate::TickVolume(int pShift) { if(pShift < 0 || pShift >= BarCount) return 0; return bar[pShift].tick_volume; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ long CBarsbydate::Volume(int pShift) { if(pShift < 0 || pShift >= BarCount) return 0; return bar[pShift].real_volume; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CBarsbydate::GetCloses(double &closes[]) { ArrayResize(closes, BarCount); for(int i = 0; i < BarCount; i++) closes[i] = bar[i].close; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CBarsbydate::GetOpens(double &opens[]) { ArrayResize(opens, BarCount); for(int i = 0; i < BarCount; i++) opens[i] = bar[i].open; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CBarsbydate::GetHighs(double &highs[]) { ArrayResize(highs, BarCount); for(int i = 0; i < BarCount; i++) highs[i] = bar[i].high; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CBarsbydate::GetLows(double &lows[]) { ArrayResize(lows, BarCount); for(int i = 0; i < BarCount; i++) lows[i] = bar[i].low; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CBarsbydate::GetTimes(datetime ×[]) { ArrayResize(times, BarCount); for(int i = 0; i < BarCount; i++) times[i] = bar[i].time; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CBarsbydate::GetVolumes(long &volumes[]) { ArrayResize(volumes, BarCount); for(int i = 0; i < BarCount; i++) volumes[i] = bar[i].tick_volume; } //+------------------------------------------------------------------+ //| Index by exact time match | //| Returns index in bar[] (0 oldest) or -1 | //+------------------------------------------------------------------+ int CBarsbydate::IndexByTime(datetime t) { if(t == 0 || BarCount == 0) return -1; for(int i = 0; i < BarCount; i++) { if(bar[i].time == t) return i; } return -1; } //+------------------------------------------------------------------+ //| Replace current newest bar if time matches | //+------------------------------------------------------------------+ bool CBarsbydate::UpdateLastBar(const MqlRates &rate) { if(BarCount <= 0) return false; int last = BarCount - 1; if(bar[last].time != rate.time) return false; bar[last] = rate; return true; } //+------------------------------------------------------------------+ //| Append a newer bar | //+------------------------------------------------------------------+ bool CBarsbydate::AppendBar(const MqlRates &rate) { if(BarCount > 0) { int last = BarCount - 1; if(rate.time < bar[last].time) return false; if(rate.time == bar[last].time) { bar[last] = rate; return true; } } int newSize = BarCount + 1; int resized = ArrayResize(bar, newSize); if(resized != newSize) return false; bar[newSize - 1] = rate; BarCount = newSize; return true; } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+