4kk4.MQL5/Indicators/RBR_DBD_Base.mq5

220 lines
8.1 KiB
MQL5
Raw Permalink Normal View History

2026-04-27 07:14:01 +07:00
//+------------------------------------------------------------------+
//| 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);
}
}
}
}
}
//+------------------------------------------------------------------+