//+------------------------------------------------------------------+ //| Seconds Chart.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include input int NumberDaysBack=1; string symbolName; bool firstfill; bool justCreated; bool firstRun; datetime LastCopyEnd=0; long id; bool ratesupdated=false; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- int M1bars=1320*NumberDaysBack; LastCopyEnd=iTime(Symbol(),PERIOD_M1,M1bars); justCreated = false; if(SymbolInfoInteger(_Symbol, SYMBOL_CUSTOM)) { Alert("" + _Symbol + " is a custom symbol. Only built-in symbol can be used as a host."); return INIT_FAILED; } symbolName = Symbol() + "_1 Seconds_1"; if(!SymbolSelect(symbolName, true)) { const SYMBOL Symb(symbolName); Symb.CloneProperties(_Symbol); justCreated = true; if(!SymbolSelect(symbolName, true)) { Alert("Can't select symbol:", symbolName, " err:", GetLastError()); return INIT_FAILED; } } reset(); EventSetMillisecondTimer(1); firstfill=true; firstRun = true; return INIT_SUCCEEDED; //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- EventKillTimer(); SymbolSelect(symbolName,false); CustomSymbolDelete(symbolName); //; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnTimer() { /* remove customrate updat fom firstRun do one and for rates arraty update rates in batches Turn it around open chart and then begin to add*/ ratesupdated=false; bool firstRunhold=firstRun; if(firstRunhold) { if(!TerminalInfoInteger(TERMINAL_CONNECTED)) { Print("Waiting for connection..."); return; } // NB! Since some MT5 build function SeriesInfoInteger(SERIES_SYNCHRONIZED) does not work properly anymore // and returns false always, so replaced with SymbolIsSynchronized // if(!SeriesInfoInteger(_Symbol, _Period, SERIES_SYNCHRONIZED)) if(!SymbolIsSynchronized(_Symbol)) { Print("Unsynchronized, skipping ticks..."); return; } UpdateSymbolChart(); /* if(justCreated) { id = ChartOpen(symbolName, PERIOD_M1); if(id == 0) { Alert("Can't open new chart for ", symbolName, ", code: ", GetLastError()); } else { Sleep(1000); ChartSetSymbolPeriod(id, symbolName, PERIOD_M1); ChartSetInteger(id, CHART_MODE, CHART_CANDLES); } justCreated = false; } */ firstRun = false; return; } if(firstRunhold==false) { UpdateSymbolChart(); } if(ratesupdated==true) { //if update was successful do a redraw and set update successful to false //redraw ratesupdated=false; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void UpdateSymbolChart() { datetime nowtime=TimeCurrent(); if(nowtime>LastCopyEnd) { //while goon MqlRates r[]; int rindex=-1; datetime secondbartime=LastCopyEnd; //conver to milliseconds ulong StartCopyMilliSecs=GetMilliSecondsInDatetime(LastCopyEnd); ulong EndCopyMilliSecs=GetMilliSecondsInDatetime(nowtime); MqlTick RawTicksArray[]; int number=CopyTicksRange(Symbol(),RawTicksArray,COPY_TICKS_ALL,StartCopyMilliSecs,EndCopyMilliSecs); int sizearray=ArraySize(RawTicksArray); if(firstfill) { Print(LastCopyEnd," ",nowtime," ",sizearray); } bool firstArrayElement=true; int tickprocessed=0; bool gooning=true; if(sizearray==0) { gooning=false; } while(gooning) { if(RawTicksArray[tickprocessed].time>=nowtime) { gooning=false; } if(RawTicksArray[tickprocessed].timesecondbartime) { rindex++; ArrayResize(r,rindex+1,100); r[rindex].close=RawTicksArray[tickprocessed].bid; r[rindex].high=RawTicksArray[tickprocessed].bid; r[rindex].low=RawTicksArray[tickprocessed].bid; r[rindex].open=RawTicksArray[tickprocessed].bid; r[rindex].real_volume=long(RawTicksArray[tickprocessed].volume_real); r[rindex].spread=int((RawTicksArray[tickprocessed].ask-RawTicksArray[tickprocessed].bid)/Point()); r[rindex].tick_volume=long(RawTicksArray[tickprocessed].volume); r[rindex].time=secondbartime; secondbartime=RawTicksArray[tickprocessed].time; } tickprocessed++; if(tickprocessed>=sizearray-1) { gooning=false; } } } if(firstfill) { Print("Filling the custom symbol with ",rindex," bars"); } if(rindex>=0) { //Print(rindex); int startcopy=0; int copycount=100; for(int x=0;x<=rindex;x++) { MqlRates rprime[]; ArrayCopy(rprime,r,0,x,1); CustomRatesUpdate(symbolName, rprime) ; } if(firstfill) { Print("Filled the custom symbol"); firstfill=false; } ratesupdated=true; } LastCopyEnd=nowtime; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ ulong GetMilliSecondsInDatetime(datetime time) { ulong msecs=ulong(time*1000); return(msecs); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void reset() { ResetLastError(); int deleted = CustomRatesDelete(symbolName, 0, LONG_MAX); int err = GetLastError(); if(err != ERR_SUCCESS) { Alert("CustomRatesDelete failed, ", err); return; } else { Print("Rates deleted: ", deleted); } ResetLastError(); deleted = CustomTicksDelete(symbolName, 0, LONG_MAX); if(deleted == -1) { Print("CustomTicksDelete failed ", GetLastError()); return; } else { Print("Ticks deleted: ", deleted); } // wait for changes to take effect in background (asynchronously) int size; do { Sleep(1000); MqlTick array[]; size = CopyTicks(symbolName, array, COPY_TICKS_ALL, 0, 10); Print("Remaining ticks: ", size); } while(size > 0); // NB. // still this can not work everytime as expected // if getting ERR_CUSTOM_TICKS_WRONG_ORDER or similar error - the last resort // is to wipe out the custom symbol manually from GUI, and then restart this EA } //+------------------------------------------------------------------+