//+------------------------------------------------------------------+ //| SND_RBR_DBD.mq5 | //| Assistant AI - Gemini | //+------------------------------------------------------------------+ #property copyright "Assistant AI" #property link "" #property version "1.00" #property indicator_chart_window #property indicator_buffers 0 #property indicator_plots 0 //--- Input Parameters input string Deskripsi_Kriteria = "=== Kriteria Pola ==="; input double InpImbalancePips = 10.0; // Minimal Body Imbalance (Pips) input double InpBaseMaxBodyRatio = 0.4; // Maksimal Rasio Body/Range pada Base input string Deskripsi_Warna = "=== Pengaturan Warna ==="; input color InpColorRBRFresh = clrDodgerBlue; // RBR Fresh Zone input color InpColorRBRTested = clrDarkBlue; // RBR Tested Zone input color InpColorDBDFresh = clrRed; // DBD Fresh Zone input color InpColorDBDTested = clrDarkRed; // DBD Tested Zone // Global variables double point_size; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { // Menyesuaikan ukuran pip untuk broker 3/5 digit if(Digits() == 5 || Digits() == 3) point_size = Point() * 10; else point_size = Point(); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Menghapus semua kotak yang digambar oleh indikator ini saat dihapus ObjectsDeleteAll(0, "SND_ZONE_"); } //+------------------------------------------------------------------+ //| 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[]) { if(rates_total < 4) return(0); // Mengatur array sebagai timeseries (index 0 adalah candle terbaru) ArraySetAsSeries(time, true); ArraySetAsSeries(open, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true); int limit = rates_total - prev_calculated; if(limit > rates_total - 4) limit = rates_total - 4; // Loop untuk mencari pola RBR dan DBD for(int i = limit; i >= 1; i--) { // Kita menggunakan pola 3 candle: // Candle 3 (index i+2): Gerakan awal (Rally/Drop) // Candle 2 (index i+1): Konsolidasi (Base) // Candle 1 (index i) : Gerakan Imbalance (Rally/Drop) double c3_open = open[i+2], c3_close = close[i+2], c3_high = high[i+2], c3_low = low[i+2]; double c2_open = open[i+1], c2_close = close[i+1], c2_high = high[i+1], c2_low = low[i+1]; double c1_open = open[i], c1_close = close[i], c1_high = high[i], c1_low = low[i]; // Kalkulasi ukuran double c3_body = MathAbs(c3_close - c3_open) / point_size; double c2_body = MathAbs(c2_close - c2_open); double c2_range = c2_high - c2_low; double c1_body = MathAbs(c1_close - c1_open) / point_size; bool is_c2_base = (c2_range > 0) ? (c2_body / c2_range <= InpBaseMaxBodyRatio) : false; bool is_c3_imbalance = c3_body >= InpImbalancePips; bool is_c1_imbalance = c1_body >= InpImbalancePips; // --- Deteksi RBR (Rally - Base - Rally) --- if(c3_close > c3_open && is_c3_imbalance && // Rally 1 is_c2_base && // Base c1_close > c1_open && is_c1_imbalance) // Rally 2 (Imbalance) { DrawZone(time[i+1], time[0], c2_high, c2_low, 1, i, high, low, time); } // --- Deteksi DBD (Drop - Base - Drop) --- if(c3_close < c3_open && is_c3_imbalance && // Drop 1 is_c2_base && // Base c1_close < c1_open && is_c1_imbalance) // Drop 2 (Imbalance) { DrawZone(time[i+1], time[0], c2_high, c2_low, -1, i, high, low, time); } } return(rates_total); } //+------------------------------------------------------------------+ //| Fungsi untuk Menggambar dan Mengecek Zone | //+------------------------------------------------------------------+ void DrawZone(datetime time_start, datetime time_end, double base_high, double base_low, int type, int current_index, const double &high[], const double &low[], const datetime &time[]) { string obj_name = "SND_ZONE_" + TimeToString(time_start); // Mengecek apakah area ini sudah disentuh oleh harga setelahnya (Tested Zone) bool is_tested = false; datetime tested_time = time_end; // Loop dari candle setelah imbalance sampai candle terbaru untuk mencari sentuhan for(int j = current_index - 1; j >= 0; j--) { if(type == 1) // RBR { // Jika harga terendah candle ke-j masuk atau menembus base if(low[j] <= base_high) { is_tested = true; tested_time = time[j]; break; } } else if(type == -1) // DBD { // Jika harga tertinggi candle ke-j masuk atau menembus base if(high[j] >= base_low) { is_tested = true; tested_time = time[j]; break; } } } // Menentukan warna berdasarkan tipe dan status (Fresh/Tested) color zone_color; if(type == 1) zone_color = is_tested ? InpColorRBRTested : InpColorRBRFresh; else zone_color = is_tested ? InpColorDBDTested : InpColorDBDFresh; // Jika zone sudah tested, hentikan gambar di candle tempat terjadinya sentuhan datetime draw_end_time = is_tested ? tested_time : time[0] + PeriodSeconds() * 10; // Membuat Rectangle di Chart if(ObjectFind(0, obj_name) < 0) { ObjectCreate(0, obj_name, OBJ_RECTANGLE, 0, time_start, base_high, draw_end_time, base_low); ObjectSetInteger(0, obj_name, OBJPROP_COLOR, zone_color); ObjectSetInteger(0, obj_name, OBJPROP_FILL, true); ObjectSetInteger(0, obj_name, OBJPROP_BACK, true); // Agar kotak tidak bisa di-klik sembarangan oleh user ObjectSetInteger(0, obj_name, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, obj_name, OBJPROP_HIDDEN, true); } else { // Update Rectangle jika sudah ada (terutama untuk menggeser time_end jika masih fresh) ObjectSetInteger(0, obj_name, OBJPROP_TIME, 1, draw_end_time); ObjectSetInteger(0, obj_name, OBJPROP_COLOR, zone_color); } } //+------------------------------------------------------------------+