4kk4.MQL5/Indicators/RBD DBR Pivot.mq5
clarissya 2394964283 Scripts
2026-04-27 07:14:01 +07:00

297 行
无行尾
14 KiB
MQL5

//+------------------------------------------------------------------+
//| 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);
}
//+------------------------------------------------------------------+