220 lines
8.1 KiB
MQL5
220 lines
8.1 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|