//+------------------------------------------------------------------+ //| RBD_DBR_Pivots.mq5 | //| Diterjemahkan oleh Gemini | //+------------------------------------------------------------------+ #property indicator_chart_window #property indicator_buffers 1 #property indicator_plots 1 //--- Plot ZigZag #property indicator_label1 "Pivot ZigZag" #property indicator_type1 DRAW_SECTION #property indicator_color1 clrGold #property indicator_style1 STYLE_SOLID #property indicator_width1 2 //--- Inputs input int InpMaxBars = 1000; // Maksimal Bar Historis yang Dipindai input int InpLen = 3; // Lookback Pivot (Syarat Kiri & Kanan Pivot) input int InpMaxSearch = 15; // Maksimal Pencarian Candle Kaki input color InpRallyCol = clrTeal; // Warna Kotak Rally input color InpDropCol = clrCrimson; // Warna Kotak Drop input color InpExtWickCol = clrOrange; // Warna Penanda Ekstrem Pivot input color InpZigZagCol = clrGold; // Warna Garis Zig-Zag //--- Toggles Tampilan input bool InpShowBoxes = true; // Opsi Menampilkan Kotak Zona (RBD/DBR) input bool InpShowLines = true; // Opsi Menampilkan Garis Pivot Horizontal input bool InpShowZigZag = true; // Opsi Menampilkan Garis Zig-Zag input int InpHideLen = 0; // Batas Tampil Historis (0 = Tampil Semua) //--- Buffer double ZigZagBuffer[]; //+------------------------------------------------------------------+ //| Helper: Mendapatkan Prefix unik berdasarkan Timeframe | //+------------------------------------------------------------------+ string GetPrefix() { return "RBD_" + EnumToString(_Period) + "_"; } //+------------------------------------------------------------------+ //| Custom indicator initialization & deinitialization | //+------------------------------------------------------------------+ int OnInit() { SetIndexBuffer(0, ZigZagBuffer, INDICATOR_DATA); PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_SECTION); PlotIndexSetInteger(0, PLOT_LINE_COLOR, InpZigZagCol); ArraySetAsSeries(ZigZagBuffer, true); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { ObjectsDeleteAll(0, "RBD_"); ChartRedraw(0); } //+------------------------------------------------------------------+ //| Helper functions untuk Candle & Body | //+------------------------------------------------------------------+ bool IsGC(int index, const double &open[], const double &close[]) { return close[index] > open[index]; } bool IsRC(int index, const double &open[], const double &close[]) { return close[index] < open[index]; } double GetHighestBody(int idx1, int idx2, const double &open[], const double &close[]) { int start = MathMin(idx1, idx2); int end = MathMax(idx1, idx2); double maxVal = MathMax(open[start], close[start]); for(int i = start + 1; i <= end; i++) { maxVal = MathMax(maxVal, MathMax(open[i], close[i])); } return maxVal; } double GetLowestBody(int idx1, int idx2, const double &open[], const double &close[]) { int start = MathMin(idx1, idx2); int end = MathMax(idx1, idx2); double minVal = MathMin(open[start], close[start]); for(int i = start + 1; i <= end; i++) { minVal = MathMin(minVal, MathMin(open[i], close[i])); } return minVal; } //+------------------------------------------------------------------+ //| 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(open, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true); ArraySetAsSeries(time, true); if (rates_total < InpMaxSearch * 2) return 0; int limit = rates_total - prev_calculated; if (limit == 0) limit = 1; if (prev_calculated == 0) { limit = rates_total - InpLen - 1; if (InpMaxBars > 0 && limit > InpMaxBars) { limit = InpMaxBars; } ObjectsDeleteAll(0, "RBD_"); ArrayInitialize(ZigZagBuffer, EMPTY_VALUE); } else { for(int i = limit; i >= 0; i--) ZigZagBuffer[i] = EMPTY_VALUE; } string currentPrefix = GetPrefix(); for(int shift = limit; shift >= 0; shift--) { // 1. Line Break Logic if (InpShowLines) { int totalObjects = ObjectsTotal(0, 0, OBJ_TREND); for(int i = totalObjects - 1; i >= 0; i--) { string name = ObjectName(0, i, 0, OBJ_TREND); if(StringFind(name, currentPrefix + "Line") == 0 && ObjectGetInteger(0, name, OBJPROP_RAY_RIGHT)) { double price = ObjectGetDouble(0, name, OBJPROP_PRICE, 0); bool isBroken = false; if(StringFind(name, currentPrefix + "LineLow_") == 0 && close[shift] < price) isBroken = true; else if(StringFind(name, currentPrefix + "LineHigh_") == 0 && close[shift] > price) isBroken = true; if(isBroken) { ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false); ObjectSetInteger(0, name, OBJPROP_TIME, 1, time[shift]); ObjectSetDouble(0, name, OBJPROP_PRICE, 1, price); } } } } if(shift + InpLen >= rates_total || shift - InpLen < 0) continue; // 2. Cek Pivot Valid (Murni Price Action High/Low) bool isHighest = true, isLowest = true; for(int j=1; j<=InpLen; j++) { if(high[shift+j] > high[shift] || high[shift-j] >= high[shift]) isHighest = false; if(low[shift+j] < low[shift] || low[shift-j] <= low[shift]) isLowest = false; } // --- Deteksi Pematangan RBD --- if(isHighest && IsGC(shift + 1, open, close)) { int d1 = -1, d2 = -1, rcCount = 0; for(int j = shift - 1; j >= MathMax(0, shift - InpMaxSearch); j--) { if(IsRC(j, open, close)) { rcCount++; if(rcCount == 1) d1 = j; else if(rcCount == 2) { d2 = j; break; } } } if(rcCount >= 2 && close[d2] < close[d1]) { int r1 = -1; for(int j = shift + 1; j <= MathMin(rates_total-1, shift + InpMaxSearch); j++) { if(IsGC(j, open, close)) { r1 = j; break; } } if(r1 != -1) { DrawPivotHigh(shift, r1, d2, open, high, low, close, time); if (InpShowZigZag) ZigZagBuffer[shift] = high[shift]; } } } // --- Deteksi Pematangan DBR --- if(isLowest && IsRC(shift + 1, open, close)) { int r1 = -1, r2 = -1, gcCount = 0; for(int j = shift - 1; j >= MathMax(0, shift - InpMaxSearch); j--) { if(IsGC(j, open, close)) { gcCount++; if(gcCount == 1) r1 = j; else if(gcCount == 2) { r2 = j; break; } } } if(gcCount >= 2 && close[r2] > close[r1]) { int d1 = -1; for(int j = shift + 1; j <= MathMin(rates_total-1, shift + InpMaxSearch); j++) { if(IsRC(j, open, close)) { d1 = j; break; } } if(d1 != -1) { DrawPivotLow(shift, d1, r2, open, high, low, close, time); if (InpShowZigZag) ZigZagBuffer[shift] = low[shift]; } } } } if(InpHideLen > 0) { int thresholdIndex = MathMin(InpHideLen + InpMaxSearch, rates_total - 1); datetime thresholdTime = time[thresholdIndex]; for(int i = ObjectsTotal(0) - 1; i >= 0; i--) { string name = ObjectName(0, i); if(StringFind(name, currentPrefix) == 0) { datetime objTime = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 0); if(objTime < thresholdTime) ObjectDelete(0, name); } } for(int i = thresholdIndex; i < rates_total; i++) { ZigZagBuffer[i] = EMPTY_VALUE; } } ChartRedraw(0); return(rates_total); } //+------------------------------------------------------------------+ //| Helper Draw: Extreme Wick Marker | //+------------------------------------------------------------------+ void DrawExtWick(int idx, color col, double price, bool isAbove, const datetime &time[]) { string extName = GetPrefix() + "ExtWick_" + TimeToString(time[idx]); if(ObjectFind(0, extName) >= 0) return; ObjectCreate(0, extName, OBJ_ARROW, 0, time[idx], price); ObjectSetInteger(0, extName, OBJPROP_ARROWCODE, 119); // Diamond ObjectSetInteger(0, extName, OBJPROP_ANCHOR, isAbove ? ANCHOR_BOTTOM : ANCHOR_TOP); ObjectSetInteger(0, extName, OBJPROP_COLOR, col); ObjectSetInteger(0, extName, OBJPROP_WIDTH, 2); } //+------------------------------------------------------------------+ //| Fungsi Menggambar Zone Pivot High (RBD) | //+------------------------------------------------------------------+ void DrawPivotHigh(int idxBase, int idxLeft, int idxRight, const double &open[], const double &high[], const double &low[], const double &close[], const datetime &time[]) { string prefix = GetPrefix(); string timeStr = TimeToString(time[idxBase]); string lineName = prefix + "LineHigh_" + timeStr; if(ObjectFind(0, lineName) >= 0) return; if (InpShowBoxes) { double hst_rally = GetHighestBody(idxLeft, idxBase, open, close); double lst_rally = GetLowestBody(idxLeft, idxBase, open, close); double hst_drop = GetHighestBody(idxBase, idxRight, open, close); double lst_drop = GetLowestBody(idxBase, idxRight, open, close); string rallyBox = prefix + "BoxRally_" + timeStr; string dropBox = prefix + "BoxDrop_" + timeStr; ObjectCreate(0, rallyBox, OBJ_RECTANGLE, 0, time[idxLeft], hst_rally, time[idxBase], lst_rally); ObjectSetInteger(0, rallyBox, OBJPROP_COLOR, InpRallyCol); ObjectSetInteger(0, rallyBox, OBJPROP_BGCOLOR, InpRallyCol); ObjectSetInteger(0, rallyBox, OBJPROP_FILL, true); ObjectSetInteger(0, rallyBox, OBJPROP_BACK, true); ObjectCreate(0, dropBox, OBJ_RECTANGLE, 0, time[idxBase], hst_drop, time[idxRight], lst_drop); ObjectSetInteger(0, dropBox, OBJPROP_COLOR, InpDropCol); ObjectSetInteger(0, dropBox, OBJPROP_BGCOLOR, InpDropCol); ObjectSetInteger(0, dropBox, OBJPROP_FILL, true); ObjectSetInteger(0, dropBox, OBJPROP_BACK, true); } if (InpShowLines) { ObjectCreate(0, lineName, OBJ_TREND, 0, time[idxBase], high[idxBase], time[idxRight], high[idxBase]); ObjectSetInteger(0, lineName, OBJPROP_COLOR, InpDropCol); ObjectSetInteger(0, lineName, OBJPROP_RAY_RIGHT, true); ObjectSetInteger(0, lineName, OBJPROP_WIDTH, 2); ObjectSetInteger(0, lineName, OBJPROP_BACK, false); } DrawExtWick(idxBase, InpExtWickCol, high[idxBase], true, time); } //+------------------------------------------------------------------+ //| Fungsi Menggambar Zone Pivot Low (DBR) | //+------------------------------------------------------------------+ void DrawPivotLow(int idxBase, int idxLeft, int idxRight, const double &open[], const double &high[], const double &low[], const double &close[], const datetime &time[]) { string prefix = GetPrefix(); string timeStr = TimeToString(time[idxBase]); string lineName = prefix + "LineLow_" + timeStr; if(ObjectFind(0, lineName) >= 0) return; if (InpShowBoxes) { double hst_drop = GetHighestBody(idxLeft, idxBase, open, close); double lst_drop = GetLowestBody(idxLeft, idxBase, open, close); double hst_rally = GetHighestBody(idxBase, idxRight, open, close); double lst_rally = GetLowestBody(idxBase, idxRight, open, close); string dropBox = prefix + "BoxDrop_" + timeStr; string rallyBox = prefix + "BoxRally_" + timeStr; ObjectCreate(0, dropBox, OBJ_RECTANGLE, 0, time[idxLeft], hst_drop, time[idxBase], lst_drop); ObjectSetInteger(0, dropBox, OBJPROP_COLOR, InpDropCol); ObjectSetInteger(0, dropBox, OBJPROP_BGCOLOR, InpDropCol); ObjectSetInteger(0, dropBox, OBJPROP_FILL, true); ObjectSetInteger(0, dropBox, OBJPROP_BACK, true); ObjectCreate(0, rallyBox, OBJ_RECTANGLE, 0, time[idxBase], hst_rally, time[idxRight], lst_rally); ObjectSetInteger(0, rallyBox, OBJPROP_COLOR, InpRallyCol); ObjectSetInteger(0, rallyBox, OBJPROP_BGCOLOR, InpRallyCol); ObjectSetInteger(0, rallyBox, OBJPROP_FILL, true); ObjectSetInteger(0, rallyBox, OBJPROP_BACK, true); } if (InpShowLines) { ObjectCreate(0, lineName, OBJ_TREND, 0, time[idxBase], low[idxBase], time[idxRight], low[idxBase]); ObjectSetInteger(0, lineName, OBJPROP_COLOR, InpRallyCol); ObjectSetInteger(0, lineName, OBJPROP_RAY_RIGHT, true); ObjectSetInteger(0, lineName, OBJPROP_WIDTH, 2); ObjectSetInteger(0, lineName, OBJPROP_BACK, false); } DrawExtWick(idxBase, InpExtWickCol, low[idxBase], false, time); } //+------------------------------------------------------------------+