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