From bb6005fe9645a2230e77a91d58bb50106528fe79 Mon Sep 17 00:00:00 2001 From: Toh4iem9 Date: Fri, 28 Nov 2025 10:25:52 +0100 Subject: [PATCH] refactor: Refactored Murrey Math Lines with modular architecture --- .../MyIndicators/Murrey_Math_Line_X.mq5 | 491 +++--------------- 1 file changed, 67 insertions(+), 424 deletions(-) diff --git a/Indicators/MyIndicators/Murrey_Math_Line_X.mq5 b/Indicators/MyIndicators/Murrey_Math_Line_X.mq5 index fb0a01b..98198db 100644 --- a/Indicators/MyIndicators/Murrey_Math_Line_X.mq5 +++ b/Indicators/MyIndicators/Murrey_Math_Line_X.mq5 @@ -1,341 +1,32 @@ //+------------------------------------------------------------------+ -//| Murrey_Math_Line_X.mq5 | -//| Copyright 2025, xxxxxxxx| -//| | +//| Murrey_Math_Line_X_Pro.mq5 | +//| Copyright 2025, xxxxxxxx | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, xxxxxxxx" -#property version "2.50" // REFACTORED: Robust handling of MTF history loading -#property description "A clean, self-contained, object-oriented Murrey Math Lines indicator with MTF support." +#property version "3.00" // REFACTORED: Refactored Murrey Math Lines with modular architecture +#property description "A clean, object-oriented Murrey Math Lines indicator with MTF support." #property indicator_chart_window #property indicator_plots 0 -//+------------------------------------------------------------------+ -//| CLASS: CMurreyMathCalculator | -//| Handles the core MTF calculation for Murrey Math levels. | -//+------------------------------------------------------------------+ -class CMurreyMathCalculator - { -private: - string m_symbol; - ENUM_TIMEFRAMES m_timeframe; - int m_period; - int m_step_back; +//--- Includes +#include +#include -public: - CMurreyMathCalculator(string symbol, ENUM_TIMEFRAMES timeframe, int period, int step_back); - ~CMurreyMathCalculator(void) {}; +//--- Input Parameters +input int InpPeriod = 64; // Calculation Period +input ENUM_TIMEFRAMES InpUpperTimeframe = PERIOD_H4; // Calculation Timeframe +input int InpStepBack = 0; // Step Back (Shift) - bool CalculateLevels(double &levels[]); - }; - -//+------------------------------------------------------------------+ -//| CMurreyMathCalculator: Constructor | -//+------------------------------------------------------------------+ -CMurreyMathCalculator::CMurreyMathCalculator(string symbol, ENUM_TIMEFRAMES timeframe, int period, int step_back) : - m_symbol(symbol), m_timeframe(timeframe), m_period(period), m_step_back(step_back) - { - } - -//+------------------------------------------------------------------+ -//| CMurreyMathCalculator: The core MTF calculation algorithm | -//+------------------------------------------------------------------+ -bool CMurreyMathCalculator::CalculateLevels(double &levels[]) - { - int bars_to_copy = m_period + m_step_back; - if(bars_to_copy <= 0) - bars_to_copy = 1; - - double htf_high[], htf_low[]; - -// REFACTORED: Check available bars before copying to avoid unnecessary errors - int bars_available = (int)SeriesInfoInteger(m_symbol, m_timeframe, SERIES_BARS_COUNT); - if(bars_available < bars_to_copy) - { - // Silently fail, OnCalculate will try again on the next tick - return false; - } - - int copied_high = CopyHigh(m_symbol, m_timeframe, 0, bars_to_copy, htf_high); - int copied_low = CopyLow(m_symbol, m_timeframe, 0, bars_to_copy, htf_low); - -// REFACTORED: Instead of printing an error, just return false. -// This indicates a temporary state, not a fatal error. - if(copied_high < bars_to_copy || copied_low < bars_to_copy) - { - return false; - } - - ArraySetAsSeries(htf_high, false); - ArraySetAsSeries(htf_low, false); - - int rates_total = ArraySize(htf_high); - int start_pos = rates_total - 1 - m_step_back; - - int min_idx = ArrayMinimum(htf_low, start_pos - m_period + 1, m_period); - int max_idx = ArrayMaximum(htf_high, start_pos - m_period + 1, m_period); - - double v1 = htf_low[min_idx]; - double v2 = htf_high[max_idx]; - - double fractal = 0; - if((v2 <= 250000) && (v2 > 25000)) - fractal = 100000; - else - if((v2 <= 25000) && (v2 > 2500)) - fractal = 10000; - else - if((v2 <= 2500) && (v2 > 250)) - fractal = 1000; - else - if((v2 <= 250) && (v2 > 25)) - fractal = 100; - else - if((v2 <= 25) && (v2 > 12.5)) - fractal = 12.5; - else - if((v2 <= 12.5) && (v2 > 6.25)) - fractal = 12.5; - else - if((v2 <= 6.25) && (v2 > 3.125)) - fractal = 6.25; - else - if((v2 <= 3.125) && (v2 > 1.5625)) - fractal = 3.125; - else - if((v2 <= 1.5625) && (v2 > 0.390625)) - fractal = 1.5625; - else - if((v2 <= 0.390625) && (v2 > 0)) - fractal = 0.1953125; - - if(fractal == 0) - return false; - double range = v2 - v1; - if(range <= 0) - return false; - - double sum = MathFloor(MathLog(fractal / range) / MathLog(2)); - double octave = fractal * (MathPow(0.5, sum)); - double mn = MathFloor(v1 / octave) * octave; - double mx; - if(mn + octave > v2) - mx = mn + octave; - else - mx = mn + (2 * octave); - - double x1=0, x2=0, x3=0, x4=0, x5=0, x6=0; - if((v1 >= (3 * (mx - mn) / 16 + mn)) && (v2 <= (9 * (mx - mn) / 16 + mn))) - x2 = mn + (mx - mn) / 2; - if((v1 >= (mn - (mx - mn) / 8)) && (v2 <= (5 * (mx - mn) / 8 + mn)) && (x2 == 0)) - x1 = mn + (mx - mn) / 2; - if((v1 >= (mn + 7 * (mx - mn) / 16)) && (v2 <= (13 * (mx - mn) / 16 + mn))) - x4 = mn + 3 * (mx - mn) / 4; - if((v1 >= (mn + 3 * (mx - mn) / 8)) && (v2 <= (9 * (mx - mn) / 8 + mn)) && (x4 == 0)) - x5 = mx; - if((v1 >= (mn + (mx - mn) / 8)) && (v2 <= (7 * (mx - mn) / 8 + mn)) && (x1 == 0) && (x2 == 0) && (x4 == 0) && (x5 == 0)) - x3 = mn + 3 * (mx - mn) / 4; - if((x1 + x2 + x3 + x4 + x5) == 0) - x6 = mx; - double finalH = x1 + x2 + x3 + x4 + x5 + x6; - - double y1=0, y2=0, y3=0, y4=0, y5=0, y6=0; - if(x1 > 0) - y1 = mn; - if(x2 > 0) - y2 = mn + (mx - mn) / 4; - if(x3 > 0) - y3 = mn + (mx - mn) / 4; - if(x4 > 0) - y4 = mn + (mx - mn) / 2; - if(x5 > 0) - y5 = mn + (mx - mn) / 2; - if((finalH > 0) && ((y1 + y2 + y3 + y4 + y5) == 0)) - y6 = mn; - double finalL = y1 + y2 + y3 + y4 + y5 + y6; - - double dmml = (finalH - finalL) / 8; - if(dmml <= 0) - return false; - - levels[0] = (finalL - dmml * 2); - for(int i = 1; i < 13; i++) - levels[i] = levels[i - 1] + dmml; - - return true; - } - -//+------------------------------------------------------------------+ -//| CLASS: CMurreyMathDrawer (Unchanged) | -//+------------------------------------------------------------------+ -class CMurreyMathDrawer - { -private: - long m_chart_id; - string m_prefix; - string m_font_face; - int m_font_size; - bool m_label_side_right; - color m_colors[13]; - int m_widths[13]; - string m_line_text[13]; - void CreateOrMoveHLine(int index, double price); - void CreateOrMoveText(int index, datetime time, double price); -public: - CMurreyMathDrawer(string prefix, string font, int size, bool right_side); - ~CMurreyMathDrawer(void); - void SetLineStyles(const color &colors[], const int &widths[]); - void DrawLevels(const datetime &time[], const double &levels[]); - }; -//+------------------------------------------------------------------+ -//| | -//+------------------------------------------------------------------+ -CMurreyMathDrawer::CMurreyMathDrawer(string prefix, string font, int size, bool right_side) : m_prefix(prefix), m_font_face(font), m_font_size(size), m_label_side_right(right_side) - { - m_chart_id = ChartID(); - m_line_text[0] = "[-2/8]P Extreme Overshoot"; - m_line_text[1] = "[-1/8]P Overshoot"; - m_line_text[2] = "[0/8]P Ultimate Support"; - m_line_text[3] = "[1/8]P Weak, Stop & Reverse"; - m_line_text[4] = "[2/8]P Pivot, Reverse"; - m_line_text[5] = "[3/8]P Bottom of Trading Range"; - m_line_text[6] = "[4/8]P Major S/R Pivot"; - m_line_text[7] = "[5/8]P Top of Trading Range"; - m_line_text[8] = "[6/8]P Pivot, Reverse"; - m_line_text[9] = "[7/8]P Weak, Stop & Reverse"; - m_line_text[10] = "[8/8]P Ultimate Resistance"; - m_line_text[11] = "[+1/8]P Overshoot"; - m_line_text[12] = "[+2/8]P Extreme Overshoot"; - } -//+------------------------------------------------------------------+ -//| | -//+------------------------------------------------------------------+ -CMurreyMathDrawer::~CMurreyMathDrawer(void) { ObjectsDeleteAll(m_chart_id, m_prefix); } -//+------------------------------------------------------------------+ -//| | -//+------------------------------------------------------------------+ -void CMurreyMathDrawer::SetLineStyles(const color &colors[], const int &widths[]) { ArrayCopy(m_colors, colors); ArrayCopy(m_widths, widths); } -//+------------------------------------------------------------------+ -//| | -//+------------------------------------------------------------------+ -void CMurreyMathDrawer::DrawLevels(const datetime &time[], const double &levels[]) - { - int rates_total = ArraySize(time); - if(rates_total < 2) - return; - int first_bar_idx = (int)ChartGetInteger(m_chart_id, CHART_FIRST_VISIBLE_BAR, 0); - if(m_label_side_right) - { - first_bar_idx = 1; - } - if(first_bar_idx < 1 || first_bar_idx >= rates_total) - return; - datetime label_time = time[first_bar_idx - 1]; - for(int i = 0; i < 13; i++) - { - CreateOrMoveHLine(i, levels[i]); - CreateOrMoveText(i, label_time, levels[i]); - } - } -//+------------------------------------------------------------------+ -//| | -//+------------------------------------------------------------------+ -void CMurreyMathDrawer::CreateOrMoveHLine(int index, double price) - { - string name = m_prefix + "line_" + (string)index; - if(ObjectFind(m_chart_id, name) < 0) - { - ObjectCreate(m_chart_id, name, OBJ_HLINE, 0, 0, price); - ObjectSetInteger(m_chart_id, name, OBJPROP_STYLE, STYLE_SOLID); - ObjectSetInteger(m_chart_id, name, OBJPROP_COLOR, m_colors[index]); - ObjectSetInteger(m_chart_id, name, OBJPROP_WIDTH, m_widths[index]); - ObjectSetInteger(m_chart_id, name, OBJPROP_BACK, true); - } - else - { - ObjectMove(m_chart_id, name, 0, 0, price); - } - } -//+------------------------------------------------------------------+ -//| | -//+------------------------------------------------------------------+ -void CMurreyMathDrawer::CreateOrMoveText(int index, datetime time, double price) - { - string name = m_prefix + "text_" + (string)index; - if(ObjectFind(m_chart_id, name) < 0) - { - ObjectCreate(m_chart_id, name, OBJ_TEXT, 0, time, price); - ObjectSetString(m_chart_id, name, OBJPROP_TEXT, m_line_text[index]); - ObjectSetInteger(m_chart_id, name, OBJPROP_FONTSIZE, m_font_size); - ObjectSetString(m_chart_id, name, OBJPROP_FONT, m_font_face); - ObjectSetInteger(m_chart_id, name, OBJPROP_COLOR, m_colors[index]); - ObjectSetInteger(m_chart_id, name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER); - } - else - { - ObjectMove(m_chart_id, name, 0, time, price); - } - } - -//+------------------------------------------------------------------+ -//| CLASS: CMurreyMathController | -//+------------------------------------------------------------------+ -class CMurreyMathController - { -private: - CMurreyMathCalculator *m_calculator; - CMurreyMathDrawer *m_drawer; - double m_mml_levels[13]; -public: - CMurreyMathController(void); - ~CMurreyMathController(void); - bool Initialize(ENUM_TIMEFRAMES timeframe, int period, int stepBack, bool labelSideRight, string fontFace, int fontSize, string prefix); - void SetLineStyles(const color &colors[], const int &widths[]); - bool Update(const datetime &time[]); // REFACTORED: Returns bool for success - }; -//+------------------------------------------------------------------+ -//| | -//+------------------------------------------------------------------+ -CMurreyMathController::CMurreyMathController(void) : m_calculator(NULL), m_drawer(NULL) { ArrayInitialize(m_mml_levels, 0.0); } -//+------------------------------------------------------------------+ -//| | -//+------------------------------------------------------------------+ -CMurreyMathController::~CMurreyMathController(void) { if(CheckPointer(m_calculator) != POINTER_INVALID) delete m_calculator; if(CheckPointer(m_drawer) != POINTER_INVALID) delete m_drawer; } -//+------------------------------------------------------------------+ -//| | -//+------------------------------------------------------------------+ -bool CMurreyMathController::Initialize(ENUM_TIMEFRAMES timeframe, int period, int stepBack, bool labelSideRight, string fontFace, int fontSize, string prefix) - { - m_calculator = new CMurreyMathCalculator(_Symbol, timeframe, period, stepBack); - m_drawer = new CMurreyMathDrawer(prefix, fontFace, fontSize, labelSideRight); - if(CheckPointer(m_calculator) == POINTER_INVALID || CheckPointer(m_drawer) == POINTER_INVALID) - { - Print("Failed to initialize Murrey Math components."); - return false; - } - return true; - } -//+------------------------------------------------------------------+ -//| | -//+------------------------------------------------------------------+ -void CMurreyMathController::SetLineStyles(const color &colors[], const int &widths[]) { if(CheckPointer(m_drawer) != POINTER_INVALID) m_drawer.SetLineStyles(colors, widths); } -// REFACTORED: Update now returns true on success, false on failure. -bool CMurreyMathController::Update(const datetime &time[]) - { - if(m_calculator.CalculateLevels(m_mml_levels)) - { - m_drawer.DrawLevels(time, m_mml_levels); - return true; // Success - } - return false; // Failure (e.g., not enough history yet) - } - -//--- Indicator Input Parameters (Unchanged) --- -input int InpPeriod = 64; -input ENUM_TIMEFRAMES InpUpperTimeframe = PERIOD_H4; -input int InpStepBack = 0; +//--- Restored Enum exactly as requested enum enum_side { Left, Right }; -input enum_side InpLabelSide = Left; +input enum_side InpLabelSide = Left; // Label Position + +input group "Visual Settings" +input string InpFontFace = "Verdana"; // Font Face +input int InpFontSize = 10; // Font Size +input string InpObjectPrefix = "MML_Pro-"; // Object Prefix + input group "Line Colors" input color InpClr_m2_8 = clrDimGray; input color InpClr_m1_8 = clrDimGray; @@ -350,6 +41,7 @@ input color InpClr_7_8 = clrGoldenrod; input color InpClr_8_8 = clrDarkOrange; input color InpClr_p1_8 = clrDimGray; input color InpClr_p2_8 = clrDimGray; + input group "Line Widths" input int InpWdth_m2_8 = 1; input int InpWdth_m1_8 = 1; @@ -364,73 +56,59 @@ input int InpWdth_7_8 = 1; input int InpWdth_8_8 = 1; input int InpWdth_p1_8 = 1; input int InpWdth_p2_8 = 1; -input group "Labels" -input string InpFontFace = "Verdana"; -input int InpFontSize = 10; -input string InpObjectPrefix = "MML-"; -//--- Global controller object --- -CMurreyMathController *g_murreyMath; +//--- Global Objects +CMurreyMathCalculator *g_calculator; +CMurreyMathDrawer *g_drawer; +double g_levels[13]; //+------------------------------------------------------------------+ -//| Custom indicator initialization function (Unchanged) | +//| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { - g_murreyMath = new CMurreyMathController(); - if(CheckPointer(g_murreyMath) == POINTER_INVALID) - { - Print("Error creating CMurreyMathController object."); + g_calculator = new CMurreyMathCalculator(); + g_drawer = new CMurreyMathDrawer(); + + ENUM_TIMEFRAMES calc_tf = (InpUpperTimeframe == PERIOD_CURRENT) ? Period() : InpUpperTimeframe; + +//--- Init Calculator + if(!g_calculator.Init(_Symbol, calc_tf, InpPeriod, InpStepBack)) return(INIT_FAILED); - } - ENUM_TIMEFRAMES calc_timeframe = (InpUpperTimeframe == PERIOD_CURRENT) ? (ENUM_TIMEFRAMES)Period() : InpUpperTimeframe; - if(!g_murreyMath.Initialize(calc_timeframe, InpPeriod, InpStepBack, (InpLabelSide == Right), InpFontFace, InpFontSize, InpObjectPrefix)) - { - return(INIT_FAILED); - } - color colors[13]; - int widths[13]; - colors[0]=InpClr_m2_8; - widths[0]=InpWdth_m2_8; - colors[1]=InpClr_m1_8; - widths[1]=InpWdth_m1_8; - colors[2]=InpClr_0_8; - widths[2]=InpWdth_0_8; - colors[3]=InpClr_1_8; - widths[3]=InpWdth_1_8; - colors[4]=InpClr_2_8; - widths[4]=InpWdth_2_8; - colors[5]=InpClr_3_8; - widths[5]=InpWdth_3_8; - colors[6]=InpClr_4_8; - widths[6]=InpWdth_4_8; - colors[7]=InpClr_5_8; - widths[7]=InpWdth_5_8; - colors[8]=InpClr_6_8; - widths[8]=InpWdth_6_8; - colors[9]=InpClr_7_8; - widths[9]=InpWdth_7_8; - colors[10]=InpClr_8_8; - widths[10]=InpWdth_8_8; - colors[11]=InpClr_p1_8; - widths[11]=InpWdth_p1_8; - colors[12]=InpClr_p2_8; - widths[12]=InpWdth_p2_8; - g_murreyMath.SetLineStyles(colors, widths); + +//--- Init Drawer +//--- We convert the enum to bool here for the drawer, keeping the drawer simple + bool is_right = (InpLabelSide == Right); + string final_prefix = InpObjectPrefix + IntegerToString(ChartID()) + "_"; + + g_drawer.Init(final_prefix, InpFontFace, InpFontSize, is_right); + +//--- Set Styles + color colors[13] = {InpClr_m2_8, InpClr_m1_8, InpClr_0_8, InpClr_1_8, InpClr_2_8, InpClr_3_8, + InpClr_4_8, InpClr_5_8, InpClr_6_8, InpClr_7_8, InpClr_8_8, InpClr_p1_8, InpClr_p2_8 + }; + int widths[13] = {InpWdth_m2_8, InpWdth_m1_8, InpWdth_0_8, InpWdth_1_8, InpWdth_2_8, InpWdth_3_8, + InpWdth_4_8, InpWdth_5_8, InpWdth_6_8, InpWdth_7_8, InpWdth_8_8, InpWdth_p1_8, InpWdth_p2_8 + }; + + g_drawer.SetLineStyles(colors, widths); + return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ -//| Custom indicator deinitialization function (Unchanged) | +//| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { - if(CheckPointer(g_murreyMath) != POINTER_INVALID) - delete g_murreyMath; + if(CheckPointer(g_calculator) == POINTER_DYNAMIC) + delete g_calculator; + if(CheckPointer(g_drawer) == POINTER_DYNAMIC) + delete g_drawer; } //+------------------------------------------------------------------+ -//| REFACTORED: Custom indicator iteration function | +//| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, @@ -443,62 +121,27 @@ int OnCalculate(const int rates_total, const long &volume[], const int &spread[]) { - if(CheckPointer(g_murreyMath) == POINTER_INVALID) + if(rates_total < 2) return 0; -//--- This state variable tracks if the first successful calculation has happened - static bool is_initialized = false; - -//--- Phase 1: Initialization Loop --- -// Keep trying on every tick until the first calculation is successful - if(!is_initialized) - { - if(g_murreyMath.Update(time)) - { - is_initialized = true; // Success! Move to maintenance phase. - ChartRedraw(); - } - else - { - // Data not ready yet, try again on the next OnCalculate call. - // We can draw a "Loading..." text to inform the user. - Comment("Murrey Math: Loading ", EnumToString(InpUpperTimeframe), " data..."); - return(rates_total); - } - } - -//--- Phase 2: Maintenance Mode --- -// Once initialized, only update on new HTF bars or chart scrolls. - Comment(""); // Clear the "Loading..." message +//--- Ensure time array is series for drawing logic ArraySetAsSeries(time, true); - ENUM_TIMEFRAMES calc_timeframe = (InpUpperTimeframe == PERIOD_CURRENT) ? (ENUM_TIMEFRAMES)Period() : InpUpperTimeframe; +//--- 1. Try to Calculate +//--- If this returns false (not enough data), we do NOT draw. + bool success = g_calculator.Calculate(g_levels); - static datetime last_htf_bar_time = 0; - datetime htf_time[]; - bool new_htf_bar = false; - - if(CopyTime(_Symbol, calc_timeframe, 0, 1, htf_time) > 0) + if(success) { - if(htf_time[0] > last_htf_bar_time) - { - last_htf_bar_time = htf_time[0]; - new_htf_bar = true; - } + //--- 2. Draw only on success + g_drawer.Draw(time, g_levels); } - - static int last_first_visible_bar = -1; - int current_first_visible_bar = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR, 0); - bool chart_scrolled = (current_first_visible_bar != last_first_visible_bar); - last_first_visible_bar = current_first_visible_bar; - - if(new_htf_bar || chart_scrolled) + else { - g_murreyMath.Update(time); - ChartRedraw(); + // Optional: Logic for when data is loading (e.g. Comment) + // But per request, we just don't draw the levels yet. } return(rates_total); } //+------------------------------------------------------------------+ -//+------------------------------------------------------------------+