//+------------------------------------------------------------------+ //| SmartFibonacci_Enhanced.mq5 | //| Copyright 2025, Enhanced | //+------------------------------------------------------------------+ #property copyright "Copyright 2025" #property indicator_chart_window #property indicator_buffers 3 #property indicator_plots 3 #property indicator_color1 DodgerBlue #property indicator_color2 LimeGreen #property indicator_color3 Gold //--- Constants #define MAX_HISTORY_RECORDS 4 #define FIBO_DATA_FIELDS 4 #define TEXT_POSITION_OFFSET 500 #define LINE_EXTEND_BARS 800 //--- Input parameters - Smart Fibonacci input int SF_TimeFrame = 0; // Chart Period (0=current) input int SF_ScanBars = 200; // Bars for Wave Analysis input double SF_MinDisplayLevel = -5.0; // Minimum Level Threshold input int SF_LinePattern = 0; // Line Pattern Style input bool SF_ShowLabels = true; // Display Level Text input string SF_TextFont = "Arial"; // Text Font Name input int SF_TextHeight = 7; // Text Height Size input bool SF_UseBodyMode = false; // Body-to-Body Mode (JPY) input int SF_RetracementCount = 1; // Retracement Display Count input color SF_PrimaryColor = DarkGray; // Primary Color Theme input color SF_SecondaryColor = Lime; // Secondary Color Theme input color SF_TertiaryColor = HotPink; // Tertiary Color Theme input color SF_QuaternaryColor = Cyan; // Quaternary Color Theme //--- Input parameters - Wave Detection input int WaveDetectionDepth = 5; // Wave Detection Depth input int WaveDeviationPips = 3; // Wave Deviation Pips input int WaveBackStep = 1; // Wave Back Step input bool WaveLineVisible = false; // Show Wave Detection Line //--- Global variables string SF_IndicatorName; int SF_MaxRetracementCount; bool validation_error_flag = false; bool fetch_history_flag = false; int detection_level = 3; //--- Fibonacci levels configuration const int TOTAL_FIBO_LEVELS = 23; double RetracementRatios[23] = { 0.0, 1.0, 0.114, 0.236, 0.382, 0.500, 0.618, 0.764, 0.886, 1.272, 1.382, 1.618, 2.000, 2.618, 4.236, 4.618, -0.272, -0.382, -0.618, -1.0, -1.618, -3.236, -3.618 }; string RetracementLabels[23] = { "0.0", "100.0", "88.6", "76.4", "61.8", "50.0", "38.2", "23.6", "11.4", "127.2", "138.2", "161.8", "200.0", "261.8", "423.6", "461.8", "-27.2", "-38.2", "-61.8", "-100.0", "-161.8", "-323.6", "-361.8" }; //--- Fibo history storage double sf_retracement_colors[MAX_HISTORY_RECORDS]; double sf_retracement_history[MAX_HISTORY_RECORDS][FIBO_DATA_FIELDS]; //--- Indicator buffers double WaveBuffer[]; double PeakMapBuffer[]; double TroughMapBuffer[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { SF_IndicatorName = MQLInfoString(MQL_PROGRAM_NAME); // Set buffers as series ArraySetAsSeries(WaveBuffer, true); ArraySetAsSeries(PeakMapBuffer, true); ArraySetAsSeries(TroughMapBuffer, true); // Assign buffers SetIndexBuffer(0, WaveBuffer); SetIndexBuffer(1, PeakMapBuffer); SetIndexBuffer(2, TroughMapBuffer); // Configure plot visibility PlotIndexSetInteger(0, PLOT_DRAW_TYPE, WaveLineVisible ? DRAW_SECTION : DRAW_NONE); PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_NONE); PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_NONE); PlotIndexSetString(0, PLOT_LABEL, "SF_WaveDetection"); PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0); PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, 0.0); // Initialize Fibo settings SF_MaxRetracementCount = MathMin(SF_RetracementCount, MAX_HISTORY_RECORDS); ArrayInitialize(sf_retracement_history, 0.0); sf_retracement_colors[0] = SF_PrimaryColor; sf_retracement_colors[1] = SF_SecondaryColor; sf_retracement_colors[2] = SF_TertiaryColor; sf_retracement_colors[3] = SF_QuaternaryColor; IndicatorSetString(INDICATOR_SHORTNAME, SF_IndicatorName + "(" + IntegerToString(SF_TimeFrame) + "," + IntegerToString(WaveDetectionDepth) + "," + IntegerToString(WaveDeviationPips) + "," + IntegerToString(WaveBackStep) + ")"); 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[]) { // Skip if no new data if(rates_total < WaveDetectionDepth) return(0); // Validate timeframe setting if(SF_TimeFrame != 0) { if(SF_TimeFrame < Period()) { if(!validation_error_flag) { Alert("SmartFibonacci: SF_TimeFrame must be >= current chart period!"); validation_error_flag = true; } return(0); } else if(SF_TimeFrame > Period()) { validation_error_flag = false; // Call indicator on higher timeframe iCustom(NULL, ConvertTimeFrame(SF_TimeFrame), SF_IndicatorName, SF_TimeFrame, SF_ScanBars, SF_MinDisplayLevel, SF_LinePattern, SF_ShowLabels, SF_TextFont, SF_TextHeight, SF_UseBodyMode, SF_RetracementCount, SF_PrimaryColor, SF_SecondaryColor, SF_TertiaryColor, SF_QuaternaryColor, WaveDetectionDepth, WaveDeviationPips, WaveBackStep, WaveLineVisible); return(rates_total); } } validation_error_flag = false; // Calculate Wave Detection ComputeWaveDetection(rates_total, prev_calculated, high, low, time); // Draw Fibonacci levels RenderSmartFibonacci(time); return(rates_total); } //+------------------------------------------------------------------+ //| Calculate Wave Detection indicator | //+------------------------------------------------------------------+ void ComputeWaveDetection(const int rates_total, const int prev_calculated, const double &high[], const double &low[], const datetime &time[]) { ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(time, true); int calc_limit, wave_counter = 0, search_direction = 0; int last_peak_pos = 0, last_trough_pos = 0; double test_value, result_value; double current_trough = 0, current_peak = 0, previous_peak = 0, previous_trough = 0; // Determine calculation range if(prev_calculated == 0) { ArrayInitialize(WaveBuffer, 0.0); ArrayInitialize(PeakMapBuffer, 0.0); ArrayInitialize(TroughMapBuffer, 0.0); calc_limit = rates_total - WaveDetectionDepth; fetch_history_flag = true; } else { // Incremental calculation - only recalculate recent bars calc_limit = rates_total - prev_calculated + WaveDetectionDepth + 1; // Find last state int idx = 0; while(wave_counter < detection_level && idx < 100) { result_value = WaveBuffer[idx]; if(result_value != 0) wave_counter++; idx++; } idx--; if(idx < calc_limit) calc_limit = idx; // Restore state if(TroughMapBuffer[idx] != 0) { current_trough = TroughMapBuffer[idx]; search_direction = 1; } else if(PeakMapBuffer[idx] != 0) { current_peak = PeakMapBuffer[idx]; search_direction = -1; } // Clear buffers in calculation range for(int j = calc_limit - 1; j >= 0; j--) { WaveBuffer[j] = 0.0; TroughMapBuffer[j] = 0.0; PeakMapBuffer[j] = 0.0; } } // Find extremums for(int shift = calc_limit; shift >= 0; shift--) { // Find local minimum test_value = low[ArrayMinimum(low, shift, WaveDetectionDepth)]; if(test_value == previous_trough) test_value = 0.0; else { previous_trough = test_value; if((low[shift] - test_value) > (WaveDeviationPips * _Point)) test_value = 0.0; else { for(int back = 1; back <= WaveBackStep; back++) { result_value = TroughMapBuffer[shift + back]; if((result_value != 0) && (result_value > test_value)) TroughMapBuffer[shift + back] = 0.0; } } } TroughMapBuffer[shift] = (low[shift] == test_value) ? test_value : 0.0; // Find local maximum test_value = high[ArrayMaximum(high, shift, WaveDetectionDepth)]; if(test_value == previous_peak) test_value = 0.0; else { previous_peak = test_value; if((test_value - high[shift]) > (WaveDeviationPips * _Point)) test_value = 0.0; else { for(int back = 1; back <= WaveBackStep; back++) { result_value = PeakMapBuffer[shift + back]; if((result_value != 0) && (result_value < test_value)) PeakMapBuffer[shift + back] = 0.0; } } } PeakMapBuffer[shift] = (high[shift] == test_value) ? test_value : 0.0; } // Build final Wave line if(search_direction == 0) { previous_trough = 0; previous_peak = 0; } else { previous_trough = current_trough; previous_peak = current_peak; } for(int shift = calc_limit; shift >= 0; shift--) { switch(search_direction) { case 0: // Look for first peak or valley if(previous_trough == 0 && previous_peak == 0) { if(PeakMapBuffer[shift] != 0) { previous_peak = high[shift]; last_peak_pos = shift; search_direction = -1; WaveBuffer[shift] = previous_peak; } if(TroughMapBuffer[shift] != 0) { previous_trough = low[shift]; last_trough_pos = shift; search_direction = 1; WaveBuffer[shift] = previous_trough; } } break; case 1: // Look for peak if(TroughMapBuffer[shift] != 0 && TroughMapBuffer[shift] < previous_trough && PeakMapBuffer[shift] == 0 && last_trough_pos > 0) { WaveBuffer[last_trough_pos] = 0.0; last_trough_pos = shift; previous_trough = TroughMapBuffer[shift]; WaveBuffer[shift] = previous_trough; } if(PeakMapBuffer[shift] != 0 && TroughMapBuffer[shift] == 0) { previous_peak = PeakMapBuffer[shift]; last_peak_pos = shift; WaveBuffer[shift] = previous_peak; search_direction = -1; } break; case -1: // Look for valley if(PeakMapBuffer[shift] != 0 && PeakMapBuffer[shift] > previous_peak && TroughMapBuffer[shift] == 0 && last_peak_pos > 0) { WaveBuffer[last_peak_pos] = 0.0; last_peak_pos = shift; previous_peak = PeakMapBuffer[shift]; WaveBuffer[shift] = previous_peak; } if(TroughMapBuffer[shift] != 0 && PeakMapBuffer[shift] == 0) { previous_trough = TroughMapBuffer[shift]; last_trough_pos = shift; WaveBuffer[shift] = previous_trough; search_direction = 1; } break; } } } //+------------------------------------------------------------------+ //| Draw Fibonacci retracements automatically | //+------------------------------------------------------------------+ void RenderSmartFibonacci(const datetime &time[]) { ArraySetAsSeries(time, true); double wave_points[4]; int wave_positions[4]; ArrayInitialize(wave_points, 0); ArrayInitialize(wave_positions, 0); // Find last 4 Wave points int points_found = 0; for(int i = 0; i < SF_ScanBars && points_found < 4; i++) { if(WaveBuffer[i] != 0.0) { wave_points[points_found] = WaveBuffer[i]; wave_positions[points_found] = i; points_found++; } } if(points_found < 4) return; // Not enough Wave points // Determine Fibonacci parameters based on pattern bool create_new_retracement = false; datetime start_time = 0, end_time = 0; double start_price = 0, end_price = 0; // Pattern 1: Uptrend continuation if(wave_points[3] < wave_points[2] && wave_points[1] < wave_points[2] && wave_points[3] <= wave_points[1] && wave_points[1] < wave_points[0]) { if(SF_UseBodyMode) { double body_low, body_high; ExtractBodyPrices(wave_positions[3], wave_positions[2], body_low, body_high); start_price = body_low; end_price = body_high; } else { start_price = wave_points[3]; end_price = wave_points[2]; } start_time = time[wave_positions[3]]; end_time = time[wave_positions[2]]; create_new_retracement = true; } // Pattern 2: Downtrend continuation else if(wave_points[3] > wave_points[2] && wave_points[1] > wave_points[2] && wave_points[3] >= wave_points[1] && wave_points[1] > wave_points[0]) { if(SF_UseBodyMode) { double body_low, body_high; ExtractBodyPrices(wave_positions[2], wave_positions[3], body_low, body_high); start_price = body_high; end_price = body_low; } else { start_price = wave_points[3]; end_price = wave_points[2]; } start_time = time[wave_positions[3]]; end_time = time[wave_positions[2]]; create_new_retracement = true; } // Pattern 3: Recent upswing else if(wave_points[3] < wave_points[2] && wave_points[1] < wave_points[3] && wave_points[0] > wave_points[1]) { if(SF_UseBodyMode) { double body_low, body_high; ExtractBodyPrices(wave_positions[1], wave_positions[2], body_low, body_high); start_price = body_low; end_price = body_high; } else { start_price = wave_points[1]; end_price = wave_points[2]; } start_time = time[wave_positions[1]]; end_time = time[wave_positions[2]]; create_new_retracement = true; } // Pattern 4: Recent downswing else if(wave_points[3] > wave_points[2] && wave_points[1] > wave_points[3] && wave_points[0] < wave_points[1]) { if(SF_UseBodyMode) { double body_low, body_high; ExtractBodyPrices(wave_positions[2], wave_positions[1], body_low, body_high); start_price = body_high; end_price = body_low; } else { start_price = wave_points[1]; end_price = wave_points[2]; } start_time = time[wave_positions[1]]; end_time = time[wave_positions[2]]; create_new_retracement = true; } // Update Retracement history if new pattern detected if(create_new_retracement && (start_time != (datetime)sf_retracement_history[0][0] || end_time != (datetime)sf_retracement_history[0][2])) { // Shift history for(int i = SF_MaxRetracementCount - 1; i > 0; i--) { for(int j = 0; j < FIBO_DATA_FIELDS; j++) { sf_retracement_history[i][j] = sf_retracement_history[i-1][j]; } } // Store new Retracement sf_retracement_history[0][0] = (double)start_time; sf_retracement_history[0][1] = start_price; sf_retracement_history[0][2] = (double)end_time; sf_retracement_history[0][3] = end_price; } // Draw all Fibonacci retracements RemoveOldRetracements(); for(int i = 0; i < SF_MaxRetracementCount && sf_retracement_history[i][0] > 0; i++) { RenderRetracementLevels((datetime)sf_retracement_history[i][0], sf_retracement_history[i][1], (datetime)sf_retracement_history[i][2], sf_retracement_history[i][3], (color)sf_retracement_colors[i], i, time); } } //+------------------------------------------------------------------+ //| Get body prices for Body Mode | //+------------------------------------------------------------------+ void ExtractBodyPrices(int pos1, int pos2, double &body_low, double &body_high) { double open_array1[], close_array1[], open_array2[], close_array2[]; ArraySetAsSeries(open_array1, true); ArraySetAsSeries(close_array1, true); ArraySetAsSeries(open_array2, true); ArraySetAsSeries(close_array2, true); CopyOpen(_Symbol, _Period, 0, pos1 + 1, open_array1); CopyClose(_Symbol, _Period, 0, pos1 + 1, close_array1); CopyOpen(_Symbol, _Period, 0, pos2 + 1, open_array2); CopyClose(_Symbol, _Period, 0, pos2 + 1, close_array2); body_low = close_array1[pos1]; if(open_array1[pos1] < close_array1[pos1]) body_low = open_array1[pos1]; body_high = close_array2[pos2]; if(open_array2[pos2] > close_array2[pos2]) body_high = open_array2[pos2]; } //+------------------------------------------------------------------+ //| Draw Fibonacci levels | //+------------------------------------------------------------------+ void RenderRetracementLevels(datetime start_time, double start_price, datetime end_time, double end_price, color theme_color, int retracement_id, const datetime &time[]) { ArraySetAsSeries(time, true); double price_range = end_price - start_price; datetime extension_time = time[0] + LINE_EXTEND_BARS * PeriodSeconds(); // Draw base line DrawTrendLine(start_time, start_price, end_time, end_price, theme_color, retracement_id); // Draw level lines for(int i = 0; i < TOTAL_FIBO_LEVELS; i++) { if(RetracementRatios[i] >= SF_MinDisplayLevel) { double level_price = start_price + RetracementRatios[i] * price_range; DrawHorizontalLevel(start_time, extension_time, level_price, RetracementLabels[i], theme_color, retracement_id, i, time); } } } //+------------------------------------------------------------------+ //| Draw base trend line | //+------------------------------------------------------------------+ void DrawTrendLine(datetime start_time, double start_price, datetime end_time, double end_price, color line_color, int retracement_id) { string object_name = StringFormat("SFIB_Trend%d_%d_%d_%d_%d", retracement_id, SF_TimeFrame, WaveDetectionDepth, WaveDeviationPips, WaveBackStep); if(ObjectFind(0, object_name) >= 0) ObjectDelete(0, object_name); ObjectCreate(0, object_name, OBJ_TREND, 0, start_time, start_price, end_time, end_price); ObjectSetInteger(0, object_name, OBJPROP_COLOR, line_color); ObjectSetInteger(0, object_name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, object_name, OBJPROP_RAY_RIGHT, false); ObjectSetInteger(0, object_name, OBJPROP_BACK, true); } //+------------------------------------------------------------------+ //| Draw single Fibonacci level line | //+------------------------------------------------------------------+ void DrawHorizontalLevel(datetime start_time, datetime extension_time, double level_price, string level_label, color line_color, int retracement_id, int level_index, const datetime &time[]) { ArraySetAsSeries(time, true); string line_object = StringFormat("SFIB_Level%d_%d_%s_%.5f", retracement_id, level_index, level_label, level_price); string text_object = StringFormat("SFIB_Text%d_%d_%s", retracement_id, level_index, level_label); // Draw line if(ObjectFind(0, line_object) >= 0) ObjectDelete(0, line_object); ObjectCreate(0, line_object, OBJ_TREND, 0, start_time, level_price, extension_time, level_price); ObjectSetInteger(0, line_object, OBJPROP_STYLE, SF_LinePattern); ObjectSetInteger(0, line_object, OBJPROP_COLOR, line_color); ObjectSetInteger(0, line_object, OBJPROP_WIDTH, 1); ObjectSetInteger(0, line_object, OBJPROP_RAY_RIGHT, false); ObjectSetInteger(0, line_object, OBJPROP_BACK, true); // Draw label if(SF_ShowLabels) { if(ObjectFind(0, text_object) >= 0) ObjectDelete(0, text_object); datetime text_time = time[0] + TEXT_POSITION_OFFSET * PeriodSeconds(); ObjectCreate(0, text_object, OBJ_TEXT, 0, text_time, level_price); ObjectSetString(0, text_object, OBJPROP_TEXT, level_label + " - " + DoubleToString(level_price, _Digits)); ObjectSetInteger(0, text_object, OBJPROP_FONTSIZE, SF_TextHeight); ObjectSetString(0, text_object, OBJPROP_FONT, SF_TextFont); ObjectSetInteger(0, text_object, OBJPROP_COLOR, line_color); } } //+------------------------------------------------------------------+ //| Delete old Fibonacci objects | //+------------------------------------------------------------------+ void RemoveOldRetracements() { ObjectsDeleteAll(0, "SFIB", 0, OBJ_TREND); ObjectsDeleteAll(0, "SFIB", 0, OBJ_TEXT); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { RemoveOldRetracements(); Comment(""); } //+------------------------------------------------------------------+ //| Migrate old timeframe values to ENUM_TIMEFRAMES | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES ConvertTimeFrame(int tf) { switch(tf) { case 0: return PERIOD_CURRENT; case 1: return PERIOD_M1; case 5: return PERIOD_M5; case 15: return PERIOD_M15; case 30: return PERIOD_M30; case 60: return PERIOD_H1; case 240: return PERIOD_H4; case 1440: return PERIOD_D1; case 10080: return PERIOD_W1; case 43200: return PERIOD_MN1; case 2: return PERIOD_M2; case 3: return PERIOD_M3; case 4: return PERIOD_M4; case 6: return PERIOD_M6; case 10: return PERIOD_M10; case 12: return PERIOD_M12; case 16385: return PERIOD_H1; case 16386: return PERIOD_H2; case 16387: return PERIOD_H3; case 16388: return PERIOD_H4; case 16390: return PERIOD_H6; case 16392: return PERIOD_H8; case 16396: return PERIOD_H12; case 16408: return PERIOD_D1; case 32769: return PERIOD_W1; case 49153: return PERIOD_MN1; default: return PERIOD_CURRENT; } } //+------------------------------------------------------------------+