#property strict #property indicator_separate_window #property indicator_plots 0 #property indicator_buffers 0 input int BoxSizePoints = 500; input int InstanceID = 1; input int MaxBricks = 3000; input int BrickWidthPx = 10; input int BrickGapPx = 1; input int LeftMarginPx = 8; input int RightMarginPx = 8; input int TopMarginPx = 8; input int BottomMarginPx = 8; input datetime AnchorDateTime = D'2026.01.01 00:00'; struct RenkoBrick { double open; double close; int direction; }; RenkoBrick Bricks[]; double BoxSize=0.0; bool HasAnchor=false; double Anchor=0.0; double LastClose=0.0; int LastDirection=0; ulong LastTickMsc=0; double LastTickBid=0.0; int SubWindow=-1; bool HistoryLoaded=false; int HistoryAttempts=0; datetime HistoryFrom=0; datetime HistoryTo=0; int LastCopiedTicks=-1; int StableCopyCount=0; string ObjectPrefix(){ return "RENKO500_I"+IntegerToString(InstanceID)+"_"; } string IndicatorName(){ return "Renko 500 #"+IntegerToString(InstanceID); } void AddBrick(const double open_price,const double close_price,const int direction) { int count=ArraySize(Bricks); if(count>=MaxBricks) { for(int i=1;i=Anchor+BoxSize){ AddBrick(Anchor,Anchor+BoxSize,+1); LastClose=Anchor+BoxSize; LastDirection=+1; } else if(price<=Anchor-BoxSize){ AddBrick(Anchor,Anchor-BoxSize,-1); LastClose=Anchor-BoxSize; LastDirection=-1; } return; } if(LastDirection>0) { while(price>=LastClose+BoxSize){ AddBrick(LastClose,LastClose+BoxSize,+1); LastClose+=BoxSize; } if(price<=LastClose-2.0*BoxSize) { AddBrick(LastClose-BoxSize,LastClose-2.0*BoxSize,-1); LastClose-=2.0*BoxSize; LastDirection=-1; while(price<=LastClose-BoxSize){ AddBrick(LastClose,LastClose-BoxSize,-1); LastClose-=BoxSize; } } return; } while(price<=LastClose-BoxSize){ AddBrick(LastClose,LastClose-BoxSize,-1); LastClose-=BoxSize; } if(price>=LastClose+2.0*BoxSize) { AddBrick(LastClose+BoxSize,LastClose+2.0*BoxSize,+1); LastClose+=2.0*BoxSize; LastDirection=+1; while(price>=LastClose+BoxSize){ AddBrick(LastClose,LastClose+BoxSize,+1); LastClose+=BoxSize; } } } void ProcessTick(const ulong time_msc,const double bid) { if(bid<=0.0) return; if(time_msc==LastTickMsc && bid==LastTickBid) return; LastTickMsc=time_msc; LastTickBid=bid; ProcessPrice(bid); } void DeleteObjects() { if(SubWindow<0) return; string prefix=ObjectPrefix(); int total=ObjectsTotal(0,SubWindow,-1); for(int i=total-1;i>=0;i--) { string name=ObjectName(0,i,SubWindow,-1); if(StringFind(name,prefix)==0) ObjectDelete(0,name); } } int GetEffectiveRightMarginPx(const int width) { int margin=RightMarginPx; bool chart_shift=(bool)ChartGetInteger(0,CHART_SHIFT,0); if(chart_shift) { double shift_size=ChartGetDouble(0,CHART_SHIFT_SIZE,0); if(shift_size>0.0) margin+=(int)MathRound(width*shift_size/100.0); } return margin; } void DrawBrick(const int index,const int slot,const double min_price,const double max_price,const int height) { if(index<0 || index>=ArraySize(Bricks)) return; if(height<=TopMarginPx+BottomMarginPx+5) return; double top=MathMax(Bricks[index].open,Bricks[index].close); double bottom=MathMin(Bricks[index].open,Bricks[index].close); double range=max_price-min_price; if(range<=0.0) return; double usable=height-TopMarginPx-BottomMarginPx; int y1=TopMarginPx+(int)MathRound((max_price-top)/range*usable); int y2=TopMarginPx+(int)MathRound((max_price-bottom)/range*usable); int brick_height=y2-y1; if(brick_height<2) brick_height=2; int x=LeftMarginPx+slot*(BrickWidthPx+BrickGapPx); string name=ObjectPrefix()+IntegerToString(index); if(!ObjectCreate(0,name,OBJ_RECTANGLE_LABEL,SubWindow,0,0)) return; ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_UPPER); ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y1); ObjectSetInteger(0,name,OBJPROP_XSIZE,BrickWidthPx); ObjectSetInteger(0,name,OBJPROP_YSIZE,brick_height); if(Bricks[index].direction>0){ ObjectSetInteger(0,name,OBJPROP_BGCOLOR,clrLimeGreen); ObjectSetInteger(0,name,OBJPROP_COLOR,clrGreen); } else { ObjectSetInteger(0,name,OBJPROP_BGCOLOR,clrTomato); ObjectSetInteger(0,name,OBJPROP_COLOR,clrRed); } ObjectSetInteger(0,name,OBJPROP_BORDER_TYPE,BORDER_FLAT); ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,name,OBJPROP_SELECTED,false); ObjectSetInteger(0,name,OBJPROP_HIDDEN,true); ObjectSetInteger(0,name,OBJPROP_BACK,false); } void DrawRenko() { if(SubWindow<0) return; int total=ArraySize(Bricks); if(total<=0) return; int width=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS); int height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,SubWindow); if(width<=0 || height<=0) return; int step=BrickWidthPx+BrickGapPx; if(step<=0) return; int effective_right_margin=GetEffectiveRightMarginPx(width); int available_slots=(width-LeftMarginPx-effective_right_margin)/step; if(available_slots<1) available_slots=1; int visible=available_slots; if(visible>total) visible=total; int start=total-visible; int slot_offset=available_slots-visible; double min_price=DBL_MAX,max_price=-DBL_MAX; for(int i=start;inow) return false; HistoryFrom=AnchorDateTime; HistoryTo=now; return true; } bool LoadHistory() { if(HistoryFrom==0 || HistoryTo==0) { if(!PrepareHistoryRange()){ Print("Renko500 #",InstanceID,": invalid anchor date/time"); return false; } } MqlTick ticks[]; ResetLastError(); int copied=CopyTicksRange(_Symbol,ticks,COPY_TICKS_INFO,(ulong)HistoryFrom*1000,(ulong)HistoryTo*1000); int error=GetLastError(); HistoryAttempts++; if(copied<=0) { Print("Renko500 #",InstanceID,": history not ready. Attempt=",HistoryAttempts," Error=",error); return false; } if(error!=0) { Print("Renko500 #",InstanceID,": history synchronization incomplete. Attempt=",HistoryAttempts," copied=",copied," Error=",error); return false; } if(copied==LastCopiedTicks) StableCopyCount++; else StableCopyCount=0; LastCopiedTicks=copied; Print("Renko500 #",InstanceID,": CopyTicksRange attempt=",HistoryAttempts," copied=",copied," stable=",StableCopyCount); if(StableCopyCount<2) return false; ResetRenkoState(); int processed=0; for(int i=0;i