//+------------------------------------------------------------------+ //| RBR_DBD_Base.mq5| //| Coding Partner | //+------------------------------------------------------------------+ #property indicator_chart_window #property indicator_plots 0 //---- author of the indicator #property copyright "Copyright © 2026, 4kk4" //---- link to the website of the author #property link "http://www.metaquotes.net/" //---- indicator version #property version "1.0" //--- Input Parameter input double InpBaseMaxBodyRatio = 0.4; // Maksimal rasio body pada tiap candle Base input int InpMinMovePoints = 100; // Minimal ukuran Poin untuk Rally/Drop input int InpMaxBaseCandles = 5; // Maksimal jumlah candle Base input int InpMaxHistory = 2000; // Maksimal riwayat candle yang dipindai input color InpColorDemand = clrMidnightBlue;// Warna Zona Demand (RBR) input color InpColorSupply = clrMaroon; // Warna Zona Supply (DBD) //--- Variabel Global untuk Prefix Objek string obj_prefix = "BaseZone_"; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { ObjectsDeleteAll(0, obj_prefix); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(0, obj_prefix); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { ArraySetAsSeries(time, true); ArraySetAsSeries(open, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true); if(rates_total < InpMaxBaseCandles + 5) return(0); int limit = rates_total - prev_calculated; if(InpMaxHistory>0 && prev_calculated == 0){ if(prev_calculated == 0) { limit = (int)MathMin(InpMaxHistory, rates_total - InpMaxBaseCandles - 3); } } //Print("Bars scanned : " + (string) limit); // 1. PENCARIAN BASE BARU DI DATA HISTORIS for(int i = limit; i >= 1 && !IsStopped(); i--) { if (i + InpMaxBaseCandles + 2 >= rates_total) continue; double body2 = MathAbs(open[i] - close[i]) / _Point; bool isRally2 = (close[i] > open[i] && body2 >= InpMinMovePoints); bool isDrop2 = (close[i] < open[i] && body2 >= InpMinMovePoints); if(!isRally2 && !isDrop2) continue; for(int j = 1; j <= InpMaxBaseCandles; j++) { int baseIdx = i + j; double bodyBase = MathAbs(open[baseIdx] - close[baseIdx]); double rangeBase = high[baseIdx] - low[baseIdx]; bool isBaseCandle = false; if(rangeBase > 0) isBaseCandle = ((bodyBase / rangeBase) <= InpBaseMaxBodyRatio); if(!isBaseCandle) break; int move1Idx = i + j + 1; double body1 = MathAbs(open[move1Idx] - close[move1Idx]) / _Point; // Skenario RBR (Demand) if(isRally2 && (close[move1Idx] > open[move1Idx] && body1 >= InpMinMovePoints)) { double baseHigh = 0.0; double baseLow = 999999.0; for(int k = 1; k <= j; k++) { if(high[i+k] > baseHigh) baseHigh = high[i+k]; if(low[i+k] < baseLow) baseLow = low[i+k]; } datetime time_start = time[move1Idx-1]; datetime time_end = time[0] + PeriodSeconds() * 20; for(int k = i; k >= 0; k--) { if(close[k] < baseLow) { time_end = time[k]; break; } } // Menggunakan variabel warna InpColorDemand DrawZone("RBR", time_start, time_end, baseHigh, baseLow, InpColorDemand); break; } // Skenario DBD (Supply) else if(isDrop2 && (close[move1Idx] < open[move1Idx] && body1 >= InpMinMovePoints)) { double baseHigh = 0.0; double baseLow = 999999.0; for(int k = 1; k <= j; k++) { if(high[i+k] > baseHigh) baseHigh = high[i+k]; if(low[i+k] < baseLow) baseLow = low[i+k]; } datetime time_start = time[move1Idx-1]; datetime time_end = time[0] + PeriodSeconds() * 20; for(int k = i; k >= 0; k--) { if(close[k] > baseHigh) { time_end = time[k]; break; } } // Menggunakan variabel warna InpColorSupply DrawZone("DBD", time_start, time_end, baseHigh, baseLow, InpColorSupply); break; } } } // 2. PEMBARUAN ZONA AKTIF SECARA REAL-TIME UpdateLiveZones(time, close); return(rates_total); } //+------------------------------------------------------------------+ //| Fungsi kustom untuk menggambar atau memperbarui kotak zona | //+------------------------------------------------------------------+ void DrawZone(string type, datetime time_start, datetime time_end, double high, double low, color clr) { string obj_name = obj_prefix + type + "_" + TimeToString(time_start); if(ObjectFind(0, obj_name) < 0) { if(time_start == time_end) time_end = time_start + PeriodSeconds(); ObjectCreate(0, obj_name, OBJ_RECTANGLE, 0, time_start, high, time_end, low); ObjectSetInteger(0, obj_name, OBJPROP_COLOR, clr); ObjectSetInteger(0, obj_name, OBJPROP_FILL, true); ObjectSetInteger(0, obj_name, OBJPROP_BACK, true); ObjectSetString(0, obj_name, OBJPROP_TOOLTIP, type + " Zone"); ObjectSetInteger(0, obj_name, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, obj_name, OBJPROP_HIDDEN, true); } else { ObjectSetInteger(0, obj_name, OBJPROP_TIME, 1, time_end); } } //+------------------------------------------------------------------+ //| Fungsi untuk memantau dan memotong kotak aktif saat pergerakan live | //+------------------------------------------------------------------+ void UpdateLiveZones(const datetime &time[], const double &close[]) { int total_objects = ObjectsTotal(0, 0, OBJ_RECTANGLE); for(int obj = total_objects - 1; obj >= 0; obj--) { string name = ObjectName(0, obj, 0, OBJ_RECTANGLE); if(StringFind(name, obj_prefix) == 0) { datetime time_end = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 1); if(time_end > time[0]) { double baseHigh = ObjectGetDouble(0, name, OBJPROP_PRICE, 0); double baseLow = ObjectGetDouble(0, name, OBJPROP_PRICE, 1); if(StringFind(name, "RBR") >= 0 && close[0] < baseLow) { ObjectSetInteger(0, name, OBJPROP_TIME, 1, time[0]); } else if(StringFind(name, "DBD") >= 0 && close[0] > baseHigh) { ObjectSetInteger(0, name, OBJPROP_TIME, 1, time[0]); } else { ObjectSetInteger(0, name, OBJPROP_TIME, 1, time[0] + PeriodSeconds() * 20); } } } } } //+------------------------------------------------------------------+