//+------------------------------------------------------------------+ //| Orderblocks_MT5_WithBuffers.mq5 | //| Copyright 2025, Nephew_Sam | //| Modifié pour exposer des buffers signal | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Nephew_Sam" #property link "" #property version "2.00" #property indicator_chart_window #property indicator_buffers 2 #property indicator_plots 2 //--- Indicator plots #property indicator_label1 "Bullish OB" #property indicator_type1 DRAW_ARROW #property indicator_color1 clrBlue #property indicator_width1 3 #property indicator_label2 "Bearish OB" #property indicator_type2 DRAW_ARROW #property indicator_color2 clrRed #property indicator_width2 3 //--- Input parameters input group "••••••••••••••••••• Fractals ••••••••••••••••••••••" input bool showFractals = false; // Show Fractal Points ? input string filterFractal = "3"; // Filter 3/5 bar fractal input group "••••••••••••••••••• Orderblocks •••••••••••••••••••" input string findObType = "Close"; // Find OB after fractal break of close/HL input bool filterFvgs = true; // Filter only OB that follow with FVG ? input int fvgDistance = 3; // Max bars between the OB and FVG input string lineHeight = "Body"; // Line Height input bool delLines = true; // Delete lines after fill ? input group "•••••••••••••••••••• Styles ••••••••••••••••••••••••" input string lines_style = "Solid"; // Lines style input int line_length = 5; // Length of lines input int linesWidth = 2; // Lines Width input color bear_line_color = clrRed; // Bear OB Line color input color bull_line_color = clrBlue; // Bull OB Line color //--- Buffers double BullOBBuffer[]; // Signal haussier double BearOBBuffer[]; // Signal baissier //--- Global variables int bars_back = 500; double fractal_highs[]; datetime fractal_high_times[]; double fractal_lows[]; datetime fractal_low_times[]; struct OrderBlock { datetime time; double high; double low; double body_level; string type; bool active; int bar_index; // Ajout pour tracker la barre }; OrderBlock orderblocks[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Associer les buffers SetIndexBuffer(0, BullOBBuffer, INDICATOR_DATA); SetIndexBuffer(1, BearOBBuffer, INDICATOR_DATA); //--- Définir les codes de flèches PlotIndexSetInteger(0, PLOT_ARROW, 233); // Flèche haut pour Bull OB PlotIndexSetInteger(1, PLOT_ARROW, 234); // Flèche bas pour Bear OB //--- Définir les valeurs vides PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); //--- Initialiser les tableaux ArrayResize(fractal_highs, 0); ArrayResize(fractal_high_times, 0); ArrayResize(fractal_lows, 0); ArrayResize(fractal_low_times, 0); ArrayResize(orderblocks, 0); //--- Initialiser les buffers avec EMPTY_VALUE ArrayInitialize(BullOBBuffer, EMPTY_VALUE); ArrayInitialize(BearOBBuffer, EMPTY_VALUE); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| 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); ArraySetAsSeries(BullOBBuffer, true); ArraySetAsSeries(BearOBBuffer, true); int limit = rates_total - prev_calculated; if (limit > bars_back) limit = bars_back; //--- Réinitialiser les buffers pour les nouvelles barres if (prev_calculated == 0) { ArrayInitialize(BullOBBuffer, EMPTY_VALUE); ArrayInitialize(BearOBBuffer, EMPTY_VALUE); } for (int i = limit; i >= 1; i--) { // Check for fractals if (IsFractalHigh(high, i)) { AddFractalHigh(high[i], time[i]); if (showFractals) CreateFractalArrow("FractalHigh_" + IntegerToString(i), time[i], high[i], 242, clrRed, true); } if (IsFractalLow(low, i)) { AddFractalLow(low[i], time[i]); if (showFractals) CreateFractalArrow("FractalLow_" + IntegerToString(i), time[i], low[i], 241, clrBlue, false); } } // Process orderblocks ProcessOrderBlocks(time, open, high, low, close); // Clean up filled orderblocks CleanFilledOrderBlocks(high[0], low[0]); return(rates_total); } //+------------------------------------------------------------------+ //| Check if current bar is a fractal high | //+------------------------------------------------------------------+ bool IsFractalHigh(const double &high[], int index) { if (filterFractal == "3") { if (index < 2 || index >= ArraySize(high) - 1) return false; return (high[index-1] > high[index] && high[index-1] > high[index-2]); } else // 5-bar fractal { if (index < 4 || index >= ArraySize(high) - 1) return false; return (high[index-2] > high[index] && high[index-2] > high[index-1] && high[index-2] > high[index-3] && high[index-2] > high[index-4]); } } //+------------------------------------------------------------------+ //| Check if current bar is a fractal low | //+------------------------------------------------------------------+ bool IsFractalLow(const double &low[], int index) { if (filterFractal == "3") { if (index < 2 || index >= ArraySize(low) - 1) return false; return (low[index-1] < low[index] && low[index-1] < low[index-2]); } else // 5-bar fractal { if (index < 4 || index >= ArraySize(low) - 1) return false; return (low[index-2] < low[index] && low[index-2] < low[index-1] && low[index-2] < low[index-3] && low[index-2] < low[index-4]); } } //+------------------------------------------------------------------+ //| Add fractal high to array | //+------------------------------------------------------------------+ void AddFractalHigh(double price, datetime bar_time) { int size = ArraySize(fractal_highs); ArrayResize(fractal_highs, size + 1); ArrayResize(fractal_high_times, size + 1); fractal_highs[size] = price; fractal_high_times[size] = bar_time; } //+------------------------------------------------------------------+ //| Add fractal low to array | //+------------------------------------------------------------------+ void AddFractalLow(double price, datetime bar_time) { int size = ArraySize(fractal_lows); ArrayResize(fractal_lows, size + 1); ArrayResize(fractal_low_times, size + 1); fractal_lows[size] = price; fractal_low_times[size] = bar_time; } //+------------------------------------------------------------------+ //| Check for bullish imbalance | //+------------------------------------------------------------------+ bool BullishImb(const double &high[], const double &low[], const double &close[], int i) { if (i < 2) return false; return (close[i+1] > high[i+2] && low[i] > high[i+2]); } //+------------------------------------------------------------------+ //| Check for bearish imbalance | //+------------------------------------------------------------------+ bool BearishImb(const double &high[], const double &low[], const double &close[], int i) { if (i < 2) return false; return (close[i+1] < low[i+2] && high[i] < low[i+2]); } //+------------------------------------------------------------------+ //| Process orderblocks based on fractal breaks | //+------------------------------------------------------------------+ void ProcessOrderBlocks(const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[]) { // Process bearish orderblocks for (int i = ArraySize(fractal_lows) - 1; i >= 0; i--) { double test_price = (findObType == "Close") ? close[0] : low[0]; if (test_price < fractal_lows[i]) { int idx = FindBearishOrderBlock(time, open, high, low, close, fractal_low_times[i]); if (idx > 0) { CreateBearishOrderBlock(time[idx], open[idx], high[idx], low[idx], idx); } // Remove processed fractal RemoveFractalLow(i); } } // Process bullish orderblocks for (int i = ArraySize(fractal_highs) - 1; i >= 0; i--) { double test_price = (findObType == "Close") ? close[0] : high[0]; if (test_price > fractal_highs[i]) { int idx = FindBullishOrderBlock(time, open, high, low, close, fractal_high_times[i]); if (idx > 0) { CreateBullishOrderBlock(time[idx], open[idx], high[idx], low[idx], idx); } // Remove processed fractal RemoveFractalHigh(i); } } } //+------------------------------------------------------------------+ //| Find bearish orderblock | //+------------------------------------------------------------------+ int FindBearishOrderBlock(const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], datetime fractal_time) { int idx = 0; double max_high = low[0]; for (int k = 0; k < bars_back; k++) { if (time[k] < fractal_time) break; if (close[k] > open[k] && high[k] > max_high) { idx = k; max_high = high[k]; } } return idx; } //+------------------------------------------------------------------+ //| Find bullish orderblock | //+------------------------------------------------------------------+ int FindBullishOrderBlock(const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], datetime fractal_time) { int idx = 0; double min_low = high[0]; for (int k = 0; k < bars_back; k++) { if (time[k] < fractal_time) break; if (close[k] < open[k] && low[k] < min_low) { idx = k; min_low = low[k]; } } return idx; } //+------------------------------------------------------------------+ //| Create bearish orderblock | //+------------------------------------------------------------------+ void CreateBearishOrderBlock(datetime bar_time, double bar_open, double bar_high, double bar_low, int bar_index) { OrderBlock ob; ob.time = bar_time; ob.high = bar_high; ob.low = bar_low; ob.body_level = (lineHeight == "Body") ? bar_open : bar_low; ob.type = "Bear"; ob.active = true; ob.bar_index = bar_index; int size = ArraySize(orderblocks); ArrayResize(orderblocks, size + 1); orderblocks[size] = ob; //--- ACTIVER LE BUFFER : signal baissier sur la barre de l'OB BearOBBuffer[bar_index] = bar_high; // Flèche au-dessus de la barre // Create lines datetime end_time = bar_time + line_length * PeriodSeconds(); string name_prefix = "BearOB_" + IntegerToString(size) + "_"; CreateLine(name_prefix + "High", bar_time, end_time, bar_high, bar_high, bear_line_color); CreateLine(name_prefix + "Body", bar_time, end_time, ob.body_level, ob.body_level, bear_line_color); } //+------------------------------------------------------------------+ //| Create bullish orderblock | //+------------------------------------------------------------------+ void CreateBullishOrderBlock(datetime bar_time, double bar_open, double bar_high, double bar_low, int bar_index) { OrderBlock ob; ob.time = bar_time; ob.high = bar_high; ob.low = bar_low; ob.body_level = (lineHeight == "Body") ? bar_open : bar_high; ob.type = "Bull"; ob.active = true; ob.bar_index = bar_index; int size = ArraySize(orderblocks); ArrayResize(orderblocks, size + 1); orderblocks[size] = ob; //--- ACTIVER LE BUFFER : signal haussier sur la barre de l'OB BullOBBuffer[bar_index] = bar_low; // Flèche en-dessous de la barre // Create lines datetime end_time = bar_time + line_length * PeriodSeconds(); string name_prefix = "BullOB_" + IntegerToString(size) + "_"; CreateLine(name_prefix + "Low", bar_time, end_time, bar_low, bar_low, bull_line_color); CreateLine(name_prefix + "Body", bar_time, end_time, ob.body_level, ob.body_level, bull_line_color); } //+------------------------------------------------------------------+ //| Create line object | //+------------------------------------------------------------------+ void CreateLine(string name, datetime time1, datetime time2, double price1, double price2, color line_color) { if (ObjectFind(0, name) >= 0) ObjectDelete(0, name); ObjectCreate(0, name, OBJ_TREND, 0, time1, price1, time2, price2); ObjectSetInteger(0, name, OBJPROP_COLOR, line_color); ObjectSetInteger(0, name, OBJPROP_WIDTH, linesWidth); ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false); if (lines_style == "Dashed") ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DASH); else if (lines_style == "Dotted") ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT); else ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); } //+------------------------------------------------------------------+ //| Create fractal arrow | //+------------------------------------------------------------------+ void CreateFractalArrow(string name, datetime time, double price, int arrow_code, color arrow_color, bool above) { if (ObjectFind(0, name) >= 0) ObjectDelete(0, name); ObjectCreate(0, name, OBJ_ARROW, 0, time, price); ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrow_code); ObjectSetInteger(0, name, OBJPROP_COLOR, arrow_color); ObjectSetInteger(0, name, OBJPROP_WIDTH, 2); } //+------------------------------------------------------------------+ //| Remove fractal high at index | //+------------------------------------------------------------------+ void RemoveFractalHigh(int index) { int size = ArraySize(fractal_highs); for (int i = index; i < size - 1; i++) { fractal_highs[i] = fractal_highs[i + 1]; fractal_high_times[i] = fractal_high_times[i + 1]; } ArrayResize(fractal_highs, size - 1); ArrayResize(fractal_high_times, size - 1); } //+------------------------------------------------------------------+ //| Remove fractal low at index | //+------------------------------------------------------------------+ void RemoveFractalLow(int index) { int size = ArraySize(fractal_lows); for (int i = index; i < size - 1; i++) { fractal_lows[i] = fractal_lows[i + 1]; fractal_low_times[i] = fractal_low_times[i + 1]; } ArrayResize(fractal_lows, size - 1); ArrayResize(fractal_low_times, size - 1); } //+------------------------------------------------------------------+ //| Clean filled orderblocks | //+------------------------------------------------------------------+ void CleanFilledOrderBlocks(double current_high, double current_low) { for (int i = ArraySize(orderblocks) - 1; i >= 0; i--) { if (!orderblocks[i].active) continue; bool filled = false; if (orderblocks[i].type == "Bear" && current_high >= orderblocks[i].high) filled = true; else if (orderblocks[i].type == "Bull" && current_low <= orderblocks[i].low) filled = true; if (filled) { orderblocks[i].active = false; //--- EFFACER LE SIGNAL du buffer quand l'OB est rempli if (orderblocks[i].type == "Bear") BearOBBuffer[orderblocks[i].bar_index] = EMPTY_VALUE; else BullOBBuffer[orderblocks[i].bar_index] = EMPTY_VALUE; if (delLines) { string prefix = (orderblocks[i].type == "Bear") ? "BearOB_" : "BullOB_"; prefix += IntegerToString(i) + "_"; ObjectDelete(0, prefix + "High"); ObjectDelete(0, prefix + "Low"); ObjectDelete(0, prefix + "Body"); } } } } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Clean up all objects created by this indicator int total = ObjectsTotal(0); for (int i = total - 1; i >= 0; i--) { string name = ObjectName(0, i); if (StringFind(name, "BearOB_") >= 0 || StringFind(name, "BullOB_") >= 0 || StringFind(name, "FractalHigh_") >= 0 || StringFind(name, "FractalLow_") >= 0) { ObjectDelete(0, name); } } }