#property copyright "Your Name" #property link "https://www.yourwebsite.com" #property version "1.02" #property indicator_chart_window #property indicator_buffers 2 #property indicator_plots 2 //--- Input parameters input int atr_period = 14; // ATR Period input double atr_threshold = 0.0005; // ATR Threshold for Consolidation (e.g., 0.0005 for EURUSD, 5.0 for XAUUSD) input int range_period = 20; // Period to check price range input double range_multiplier = 2.0; // Price range multiplier (relative to ATR) input bool enable_alerts = true; // Enable breakout alerts input bool draw_rectangles = true; // Draw rectangles for consolidation zones //--- Indicator buffers double UpperBuffer[]; double LowerBuffer[]; //--- Global variables int atr_handle = INVALID_HANDLE; int zone_count = 0; // For unique rectangle names //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Set indicator buffers SetIndexBuffer(0, UpperBuffer, INDICATOR_DATA); SetIndexBuffer(1, LowerBuffer, INDICATOR_DATA); PlotIndexSetString(0, PLOT_LABEL, "Upper Consolidation"); PlotIndexSetString(1, PLOT_LABEL, "Lower Consolidation"); PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE); PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_LINE); PlotIndexSetInteger(0, PLOT_LINE_STYLE, STYLE_SOLID); PlotIndexSetInteger(1, PLOT_LINE_STYLE, STYLE_SOLID); PlotIndexSetInteger(0, PLOT_LINE_COLOR, clrRed); PlotIndexSetInteger(1, PLOT_LINE_COLOR, clrBlue); //--- Create ATR handle atr_handle = iATR(_Symbol, _Period, atr_period); if(atr_handle == INVALID_HANDLE) { Print("Failed to create ATR handle, error: ", GetLastError()); return(INIT_FAILED); } //--- Check available bars int available_bars = Bars(_Symbol, _Period); if(available_bars < range_period + atr_period) { Print("Insufficient bars: ", available_bars, ", required: ", range_period + atr_period); return(INIT_FAILED); } //--- Log initialization Print("Indicator initialized. Symbol: ", _Symbol, ", Timeframe: ", Period(), ", ATR Period: ", atr_period, ", Range Period: ", range_period); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(atr_handle != INVALID_HANDLE) IndicatorRelease(atr_handle); ObjectsDeleteAll(0, "Consolidation_"); // Remove all rectangles Print("Indicator deinitialized, reason: ", reason); } //+------------------------------------------------------------------+ //| 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[]) { //--- Ensure enough bars if(rates_total < range_period + atr_period) { Print("Not enough bars: ", rates_total, ", required: ", range_period + atr_period); return(0); } //--- Set arrays as series ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true); ArraySetAsSeries(time, true); ArraySetAsSeries(UpperBuffer, true); ArraySetAsSeries(LowerBuffer, true); //--- Calculate ATR double atr[]; ArraySetAsSeries(atr, true); ArrayResize(atr, rates_total); if(CopyBuffer(atr_handle, 0, 0, rates_total, atr) <= 0) { Print("Failed to copy ATR buffer, error: ", GetLastError()); return(0); } //--- Initialize buffers for first calculation if(prev_calculated == 0) { ArrayInitialize(UpperBuffer, EMPTY_VALUE); ArrayInitialize(LowerBuffer, EMPTY_VALUE); } //--- Loop through bars int start = prev_calculated == 0 ? range_period : MathMax(range_period, prev_calculated - 1); for(int i = start; i < rates_total && !IsStopped(); i++) { //--- Calculate range over range_period int max_idx = ArrayMaximum(high, i - range_period + 1, range_period); int min_idx = ArrayMinimum(low, i - range_period + 1, range_period); if(max_idx == -1 || min_idx == -1) { Print("Error: Invalid array index at bar ", i); continue; } double max_high = high[max_idx]; double min_low = low[min_idx]; double price_range = max_high - min_low; //--- Log values for debugging if(i == rates_total - 1) // Log only for the latest bar { Print("Bar ", i, ": ATR = ", DoubleToString(atr[i], _Digits), ", Price Range = ", DoubleToString(price_range, _Digits), ", Threshold = ", DoubleToString(atr_threshold, _Digits), ", Range Limit = ", DoubleToString(atr[i] * range_multiplier, _Digits)); } //--- Check for consolidation if(atr[i] < atr_threshold && price_range < atr[i] * range_multiplier) { UpperBuffer[i] = max_high; LowerBuffer[i] = min_low; //--- Draw rectangle for the consolidation zone if(draw_rectangles && i >= range_period) { string obj_name = "Consolidation_" + IntegerToString(zone_count++) + "_" + TimeToString(time[i]); ObjectCreate(0, obj_name, OBJ_RECTANGLE, 0, time[i], max_high, time[i - range_period + 1], min_low); ObjectSetInteger(0, obj_name, OBJPROP_COLOR, clrRed); ObjectSetInteger(0, obj_name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, obj_name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, obj_name, OBJPROP_FILL, false); ObjectSetInteger(0, obj_name, OBJPROP_BACK, false); } //--- Check for breakout (on latest bar only) if(enable_alerts && i == rates_total - 1) { if(close[i] > UpperBuffer[i] && (i == 0 || close[i-1] <= UpperBuffer[i-1])) Alert("Breakout Up at ", TimeToString(time[i]), ": Price = ", DoubleToString(close[i], _Digits)); if(close[i] < LowerBuffer[i] && (i == 0 || close[i-1] >= LowerBuffer[i-1])) Alert("Breakout Down at ", TimeToString(time[i]), ": Price = ", DoubleToString(close[i], _Digits)); } } else { UpperBuffer[i] = EMPTY_VALUE; LowerBuffer[i] = EMPTY_VALUE; } } return(rates_total); }