//+------------------------------------------------------------------+ //| HarmonicPatternFinderV2_Optimized.mq5 | //| Copyright 2016-2024, Andre S. Enger. | //| Optimized version with performance fixes | //+------------------------------------------------------------------+ #property copyright "Copyright 2016-2024, Andre S. Enger." #property link "andre_enger@hotmail.com" #property version "2.10" #property description "Optimized harmonic pattern indicator with input validation and performance improvements" #property indicator_chart_window #property indicator_buffers 2 #property indicator_plots 1 #property indicator_label1 "Zig Zag" #property indicator_type1 DRAW_ZIGZAG #property indicator_color1 clrNONE #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- Describes patterns struct PATTERN_DESCRIPTOR { double ab2xa_min; double ab2xa_max; double bc2ab_min; double bc2ab_max; double cd2bc_min; double cd2bc_max; double ad2xa_min; double ad2xa_max; double cd2xc_min; double cd2xc_max; double xc2xa_min; double xc2xa_max; double cd2ab_min; double cd2ab_max; }; //--- Identifies drawn patterns struct PATTERN_INSTANCE { int patternIndex; int patternBufferIndex; bool bullish; bool overlapping; datetime XDateTime; datetime ADateTime; datetime BDateTime; datetime CDateTime; datetime DDateTime; double X; double A; double B; double C; double D; double PRZ; }; //--- Number keys of patterns enum PATTERN_INDEX { TRENDLIKE1_ABCD=0, TRENDLIKE2_ABCD, PERFECT_ABCD, IDEAL1_ABCD, IDEAL2_ABCD, RANGELIKE_ABCD, ALT127_TRENDLIKE1_ABCD, ALT127_TRENDLIKE2_ABCD, ALT127_PERFECT_ABCD, ALT127_IDEAL1_ABCD, ALT127_IDEAL2_ABCD, ALT127_RANGELIKE_ABCD, REC_TRENDLIKE1_ABCD, REC_TRENDLIKE2_ABCD, REC_PERFECT_ABCD, REC_IDEAL1_ABCD, REC_IDEAL2_ABCD, REC_RANGELIKE_ABCD, GARTLEY, BAT, ALTBAT, FIVEO, BUTTERFLY, CRAB, DEEPCRAB, THREEDRIVES, CYPHER, SHARK, NENSTAR, BLACKSWAN, WHITESWAN, ONE2ONE, NEWCYPHER, NAVARRO200, LEONARDO, KANE, GARFLY, MAXBAT, MAXGARTLEY, MAXBUTTERFLY, GARTLEY113, BUTTERFLY113, ANTI_GARTLEY, ANTI_BAT, ANTI_ALTBAT, ANTI_FIVEO, ANTI_BUTTERFLY, ANTI_CRAB, ANTI_DEEPCRAB, ANTI_THREEDRIVES, ANTI_CYPHER, ANTI_SHARK, ANTI_NENSTAR, ANTI_BLACKSWAN, ANTI_WHITESWAN, ANTI_ONE2ONE, ANTI_NEWCYPHER, ANTI_NAVARRO200, ANTI_LEONARDO, ANTI_KANE, ANTI_GARFLY, ANTI_MAXBAT, ANTI_MAXGARTLEY, ANTI_MAXBUTTERFLY, ANTI_GARTLEY113, ANTI_BUTTERFLY113 }; //--- ZigZag selection enum ZIGZAGTYPE { FASTZZ, ALEXSTAL, SWINGCHART }; //--- Constants and macros #define SIZE_PATTERN_BUFFER 10 #define NUM_PATTERNS 66 #define NON_EXISTENT_DATETIME D'19.07.1980 12:30:27' #define MIN_PATTERN_RATIO 0.1 #define MAX_PATTERN_RATIO 10.0 const string _identifier="HPF"; //--- User Inputs with validation comments input string indicatorSettings="-=Indicator Settings=-"; input ZIGZAGTYPE zztype=ALEXSTAL; input int zzperiod=12; // Range: 1-100 input int zzamplitude=10; // Range: 0-1000 input int zzminmotion=0; // Range: 0-1000 input int SwingSize=200; // Range: 10-10000 points input int BarsAnalyzed=200; // Range: 10-500 (lower = faster) input int History=1000; // Range: 100-5000 input int MaxSamePoints=2; // Range: 0-5 input double SlackRange=0.01; // Range: 0.001-0.1 input double SlackUnary=0.1; // Range: 0.01-1.0 input int MaxPatternsPerBar=10; // NEW: Limit patterns per bar (performance) input bool EnableOptimizations=true; // NEW: Enable performance optimizations input string indicatorColors="-=Display Settings=-"; input color ClrBull=clrLightSkyBlue; input color ClrBear=clrSalmon; input color ClrBull4P=clrBlue; input color ClrBear4P=clrRed; input color ClrBullProjection=clrSeaGreen; input color ClrBearProjection=clrDarkOrange; input color ClrRatio=clrGray; input bool Fill_Patterns=false; input bool Show_descriptions=true; input bool Show_PRZ=true; input bool EmergingPatterns=true; input bool OneAheadProjection=false; input bool showPatternNames=false; input int l_width=2; input int l_width4p=2; input int l_width_proj=2; input int Font_size=8; input ENUM_LINE_STYLE Style_5P=STYLE_SOLID; input ENUM_LINE_STYLE Style_4P=STYLE_DASH; input ENUM_LINE_STYLE Style_Proj=STYLE_DASHDOTDOT; input ENUM_LINE_STYLE Style_Ratio=STYLE_DOT; input ENUM_LINE_STYLE Style_PRZ=STYLE_DASHDOT; input string indicatorPatternsQuick="-=Patterns Quick=-"; input bool Show_abcd=true; input bool Show_alt127_abcd=true; input bool Show_rec_abcd=true; input bool Show_patterns=true; input bool Show_antipatterns=false; //--- Pattern definitions (abbreviated - full definitions at end) PATTERN_DESCRIPTOR trendlike1_abcd; PATTERN_DESCRIPTOR trendlike2_abcd; // ... (all 66 patterns defined in original) //--- Global variables PATTERN_DESCRIPTOR _patterns[]; string _patternNames[]; int _patternCounter[]; int _zzHandle; double peaks[]; double troughs[]; PATTERN_INSTANCE _patternInstances[]; int _patternInstanceCounter=0; int _maxPatternInstances=100; PATTERN_INSTANCE _projectionInstances[]; int _projectionInstanceCounter=0; int _maxProjectionInstances=100; PATTERN_INSTANCE _drawnProjectionInstances[]; int _drawnProjectionInstanceCounter=0; int _maxDrawnProjectionInstances=100; int _lastPeak=0; int _lastTrough=0; double _lastPeakValue=0; double _lastTroughValue=0; bool _lastDirection=false; datetime _timeOfInit=0; // Cached ZigZag data for optimization datetime _cachedPeaksTime[]; datetime _cachedTroughsTime[]; int _cachedPeaksCount=0; int _cachedTroughsCount=0; // Performance tracking int _patternsFoundThisBar=0; int _barsProcessed=0; //+------------------------------------------------------------------+ //| Input Validation | //+------------------------------------------------------------------+ bool ValidateInputs() { // Validate ZigZag parameters if(zzperiod < 1 || zzperiod > 100) { Alert("ERROR: zzperiod must be between 1 and 100"); return false; } if(zzamplitude < 0 || zzamplitude > 1000) { Alert("ERROR: zzamplitude must be between 0 and 1000"); return false; } if(zzminmotion < 0 || zzminmotion > 1000) { Alert("ERROR: zzminmotion must be between 0 and 1000"); return false; } if(SwingSize < 10 || SwingSize > 10000) { Alert("ERROR: SwingSize must be between 10 and 10000"); return false; } if(BarsAnalyzed < 10 || BarsAnalyzed > 500) { Alert("ERROR: BarsAnalyzed must be between 10 and 500"); return false; } if(History < 100 || History > 5000) { Alert("ERROR: History must be between 100 and 5000"); return false; } if(MaxSamePoints < 0 || MaxSamePoints > 5) { Alert("ERROR: MaxSamePoints must be between 0 and 5"); return false; } if(SlackRange < 0.001 || SlackRange > 0.1) { Alert("ERROR: SlackRange must be between 0.001 and 0.1"); return false; } if(SlackUnary < 0.01 || SlackUnary > 1.0) { Alert("ERROR: SlackUnary must be between 0.01 and 1.0"); return false; } if(MaxPatternsPerBar < 1 || MaxPatternsPerBar > 50) { Alert("ERROR: MaxPatternsPerBar must be between 1 and 50"); return false; } if(Font_size < 6 || Font_size > 24) { Alert("ERROR: Font_size must be between 6 and 24"); return false; } if(l_width < 1 || l_width > 5) { Alert("ERROR: l_width must be between 1 and 5"); return false; } return true; } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Validate all inputs first if(!ValidateInputs()) return(INIT_FAILED); //--- Initialize arrays with error checking ArrayResize(_patterns, NUM_PATTERNS); ArrayResize(_patternNames, NUM_PATTERNS); ArrayResize(_patternCounter, NUM_PATTERNS); if(ArrayResize(_patternInstances, _maxPatternInstances) < _maxPatternInstances) { Print("Error allocating _patternInstances array"); return(INIT_FAILED); } if(ArrayResize(_projectionInstances, _maxProjectionInstances) < _maxProjectionInstances) { Print("Error allocating _projectionInstances array"); return(INIT_FAILED); } if(ArrayResize(_drawnProjectionInstances, _maxDrawnProjectionInstances) < _maxDrawnProjectionInstances) { Print("Error allocating _drawnProjectionInstances array"); return(INIT_FAILED); } ArrayFill(_patternCounter, 0, NUM_PATTERNS, 0); //--- Initialize ZigZag with error handling _zzHandle = InitializeZigZag(); if(_zzHandle == INVALID_HANDLE) { Print("ERROR: Failed to initialize ZigZag indicator"); return(INIT_FAILED); } //--- Set indicator buffers SetIndexBuffer(0, peaks, INDICATOR_DATA); SetIndexBuffer(1, troughs, INDICATOR_DATA); //--- Fill pattern definitions InitializePatterns(); _timeOfInit = TimeCurrent(); Print("HarmonicPatternFinderV2_Optimized initialized successfully"); Print("Optimization mode: ", EnableOptimizations ? "ENABLED" : "DISABLED"); Print("Max patterns per bar: ", MaxPatternsPerBar); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Initialize ZigZag based on selected type | //+------------------------------------------------------------------+ int InitializeZigZag() { int handle = INVALID_HANDLE; switch(zztype) { case FASTZZ: // Assuming FastZZ is a custom indicator handle = iCustom(_Symbol, PERIOD_CURRENT, "FastZZ", SwingSize); break; case ALEXSTAL: // Assuming Alexstal ZigZag handle = iCustom(_Symbol, PERIOD_CURRENT, "alexstal_zigzagprof", zzperiod, zzamplitude, zzminmotion); break; case SWINGCHART: // Assuming SwingChart handle = iCustom(_Symbol, PERIOD_CURRENT, "swingchart", SwingSize); break; } return handle; } //+------------------------------------------------------------------+ //| Initialize all pattern definitions | //+------------------------------------------------------------------+ void InitializePatterns() { // AB=CD Patterns (example - add all 66 patterns as in original) trendlike1_abcd.ab2xa_min = 0.382; trendlike1_abcd.ab2xa_max = 0.618; trendlike1_abcd.bc2ab_min = 0.382; trendlike1_abcd.bc2ab_max = 0.618; trendlike1_abcd.cd2bc_min = 1.0; trendlike1_abcd.cd2bc_max = 1.0; trendlike2_abcd.ab2xa_min = 0.382; trendlike2_abcd.ab2xa_max = 0.618; trendlike2_abcd.bc2ab_min = 0.618; trendlike2_abcd.bc2ab_max = 0.786; trendlike2_abcd.cd2bc_min = 1.0; trendlike2_abcd.cd2bc_max = 1.0; // Add remaining pattern definitions here... // (Copy from original file) // Map patterns to array _patterns[TRENDLIKE1_ABCD] = trendlike1_abcd; _patternNames[TRENDLIKE1_ABCD] = "Trendlike AB=CD #1"; _patterns[TRENDLIKE2_ABCD] = trendlike2_abcd; _patternNames[TRENDLIKE2_ABCD] = "Trendlike AB=CD #2"; // ... Continue for all 66 patterns } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Cleanup if(_zzHandle != INVALID_HANDLE) IndicatorRelease(_zzHandle); ObjectsDeleteAll(0, _identifier); Print("HarmonicPatternFinderV2_Optimized deinitialized. Reason: ", reason); } //+------------------------------------------------------------------+ //| Reinitialize indicator | //+------------------------------------------------------------------+ void OnReinit() { _patternInstanceCounter = 0; _drawnProjectionInstanceCounter = 0; ArrayFill(_patternCounter, 0, NUM_PATTERNS, 0); for(int i = ObjectsTotal(0, 0, -1) - 1; i >= 0; i--) { string name = ObjectName(0, i, 0, -1); if(StringFind(name, "U " + _identifier + StringFormat("%x", _timeOfInit)) != -1 || StringFind(name, "D " + _identifier + StringFormat("%x", _timeOfInit)) != -1) ObjectDelete(0, name); } } //+------------------------------------------------------------------+ //| Optimized OnCalculate | //+------------------------------------------------------------------+ 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[]) { //--- Initialize or continue calculation int start = 0; if(prev_calculated > rates_total || prev_calculated <= 0) OnReinit(); else start = prev_calculated - 1; start = MathMax(1, start); //--- Validate ZigZag data if(BarsCalculated(_zzHandle) < rates_total) { Print("ZigZag not calculated yet"); return prev_calculated > 0 ? prev_calculated : 0; } //--- Copy ZigZag data with size check if(CopyBuffer(_zzHandle, 0, 0, rates_total, peaks) <= 0 || CopyBuffer(_zzHandle, 1, 0, rates_total, troughs) <= 0) { Print("Failed to copy ZigZag buffers"); return prev_calculated > 0 ? prev_calculated : 0; } //--- Cache peak/trough times for optimization if(EnableOptimizations) CacheZigZagTimes(rates_total, time); //--- Main loop with performance optimizations for(int bar = start; bar < rates_total && !IsStopped(); bar++) { _patternsFoundThisBar = 0; // Skip bars outside history range if(bar < rates_total - History) continue; // Find last significant peaks/troughs int lastPeak = FindLastPeak(bar, peaks); int lastTrough = FindLastTrough(bar, troughs); if(lastPeak < 0 || lastTrough < 0) continue; double lastPeakValue = peaks[lastPeak]; double lastTroughValue = troughs[lastTrough]; // Skip if no change from last calculation if(lastPeakValue == _lastPeakValue && lastTroughValue == _lastTroughValue && bar > start) continue; // Determine swing direction bool endsInTrough = lastTrough > lastPeak; if(lastTrough == lastPeak) { int zzDirection = ZigZagDirection(lastPeak, peaks, troughs); if(zzDirection == 0) continue; endsInTrough = (zzDirection == -1); } // Update display on direction change if(_lastDirection != endsInTrough || (lastPeak > _lastPeak && lastTrough > _lastTrough)) { UndisplayProjections(); UndisplayPatterns(); if(Show_PRZ) UndisplayPRZs(); _patternInstanceCounter = 0; } // Save state _lastPeak = lastPeak; _lastTrough = lastTrough; _lastPeakValue = lastPeakValue; _lastTroughValue = lastTroughValue; _lastDirection = endsInTrough; // OPTIMIZATION: Limit patterns checked per bar int patternsChecked = 0; // Check each pattern for(int patternIndex = 0; patternIndex < NUM_PATTERNS && !IsStopped(); patternIndex++) { // Check if we should display this pattern if(!ShouldDisplayPattern(patternIndex)) continue; // OPTIMIZATION: Skip if max patterns reached if(_patternsFoundThisBar >= MaxPatternsPerBar) break; patternsChecked++; // Get pattern constraints PATTERN_DESCRIPTOR pattern = _patterns[patternIndex]; // Find pattern matches with optimization if(EnableOptimizations) FindPatternOptimized(patternIndex, pattern, bar, lastPeak, lastTrough, endsInTrough, time, peaks, troughs); else FindPatternStandard(patternIndex, pattern, bar, lastPeak, lastTrough, endsInTrough, time, peaks, troughs); } } return rates_total; } //+------------------------------------------------------------------+ //| Cache ZigZag peak/trough times for faster lookup | //+------------------------------------------------------------------+ void CacheZigZagTimes(int rates_total, const datetime &time[]) { ArrayResize(_cachedPeaksTime, rates_total); ArrayResize(_cachedTroughsTime, rates_total); _cachedPeaksCount = 0; _cachedTroughsCount = 0; for(int i = 0; i < rates_total; i++) { if(IsProperValue(peaks[i])) { _cachedPeaksTime[_cachedPeaksCount] = time[i]; _cachedPeaksCount++; } if(IsProperValue(troughs[i])) { _cachedTroughsTime[_cachedTroughsCount] = time[i]; _cachedTroughsCount++; } } } //+------------------------------------------------------------------+ //| Check if pattern should be displayed | //+------------------------------------------------------------------+ bool ShouldDisplayPattern(int patternIndex) { // AB=CD patterns if(patternIndex <= RANGELIKE_ABCD && !Show_abcd) return false; if(patternIndex >= ALT127_TRENDLIKE1_ABCD && patternIndex <= ALT127_RANGELIKE_ABCD && !Show_alt127_abcd) return false; if(patternIndex >= REC_TRENDLIKE1_ABCD && patternIndex <= REC_RANGELIKE_ABCD && !Show_rec_abcd) return false; // 5-point patterns if(patternIndex >= GARTLEY && patternIndex <= BUTTERFLY113 && !Show_patterns) return false; // Anti patterns if(patternIndex >= ANTI_GARTLEY && !Show_antipatterns) return false; // Individual pattern checks (add all from original) // ... return true; } //+------------------------------------------------------------------+ //| Optimized pattern finder | //+------------------------------------------------------------------+ void FindPatternOptimized(int patternIndex, PATTERN_DESCRIPTOR &pattern, int bar, int lastPeak, int lastTrough, bool endsInTrough, const datetime &time[], double &peaks[], double &troughs[]) { // OPTIMIZATION: Pre-calculate constraints bool ab2xaConstraint = (pattern.ab2xa_max != 0 && pattern.ab2xa_min != 0); bool ad2xaConstraint = (pattern.ad2xa_max != 0 && pattern.ad2xa_min != 0); bool bc2abConstraint = (pattern.bc2ab_max != 0 && pattern.bc2ab_min != 0); bool cd2bcConstraint = (pattern.cd2bc_max != 0 && pattern.cd2bc_min != 0); // Skip patterns without proper constraints if(!ab2xaConstraint && !ad2xaConstraint && !bc2abConstraint && !cd2bcConstraint) return; // OPTIMIZATION: Limit X search range int xStart = MathMax(bar - BarsAnalyzed, 1); int xEnd = bar; // Quick check: need at least 4 pivots for 5-point pattern int pivotCount = CountPivotsInRange(xStart, bar, peaks, troughs); if(pivotCount < 4) return; // Search for X points for(int xIdx = xStart; xIdx <= xEnd && _patternsFoundThisBar < MaxPatternsPerBar; xIdx++) { // OPTIMIZATION: Skip if not a valid pivot if(!IsValidPivot(xIdx, peaks, troughs)) continue; // ... (continue with A, B, C, D loops similar to original but with early exits) // For brevity, showing the structure - full implementation would follow original logic } } //+------------------------------------------------------------------+ //| Standard pattern finder (original algorithm) | //+------------------------------------------------------------------+ void FindPatternStandard(int patternIndex, PATTERN_DESCRIPTOR &pattern, int bar, int lastPeak, int lastTrough, bool endsInTrough, const datetime &time[], double &peaks[], double &troughs[]) { // Original implementation from HarmonicPatternFinderV2 // (Copy the full nested loop logic from original file) } //+------------------------------------------------------------------+ //| Helper: Find last peak | //+------------------------------------------------------------------+ int FindLastPeak(int bar, double &peaks[]) { for(int i = bar; i >= 0; i--) { if(IsProperValue(peaks[i])) return i; } return -1; } //+------------------------------------------------------------------+ //| Helper: Find last trough | //+------------------------------------------------------------------+ int FindLastTrough(int bar, double &troughs[]) { for(int i = bar; i >= 0; i--) { if(IsProperValue(troughs[i])) return i; } return -1; } //+------------------------------------------------------------------+ //| Helper: Count pivots in range | //+------------------------------------------------------------------+ int CountPivotsInRange(int start, int end, double &peaks[], double &troughs[]) { int count = 0; for(int i = start; i <= end && i < ArraySize(peaks); i++) { if(IsProperValue(peaks[i]) || IsProperValue(troughs[i])) count++; } return count; } //+------------------------------------------------------------------+ //| Helper: Check if valid pivot | //+------------------------------------------------------------------+ bool IsValidPivot(int idx, double &peaks[], double &troughs[]) { return IsProperValue(peaks[idx]) || IsProperValue(troughs[idx]); } //+------------------------------------------------------------------+ //| Helper: Check proper value (not empty) | //+------------------------------------------------------------------+ bool IsProperValue(double value) { return (value != 0 && value != EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Helper: Get ZigZag direction | //+------------------------------------------------------------------+ int ZigZagDirection(int bar, double &peaks[], double &troughs[]) { // Look back to determine direction for(int i = bar - 1; i >= 0; i--) { if(IsProperValue(peaks[i])) return 1; // Up if(IsProperValue(troughs[i])) return -1; // Down } return 0; } //+------------------------------------------------------------------+ //| Placeholder functions - implement as needed | //+------------------------------------------------------------------+ void UndisplayProjections() { } void UndisplayPatterns() { } void UndisplayPRZs() { } void DisplayProjection(int idx, bool bullish, datetime xdt, double x, datetime adt, double a, datetime bdt, double b, datetime cdt, double c, datetime ddt, double d) { } void DisplayProjection(int idx, bool bullish, datetime adt, double a, datetime bdt, double b, datetime cdt, double c, datetime ddt, double d) { } bool Is4PointPattern(int patternIndex) { return patternIndex <= REC_RANGELIKE_ABCD; } bool Overlaps(int patternIdx, datetime xdt, datetime adt, datetime bdt, datetime cdt, datetime ddt) { return false; } //+------------------------------------------------------------------+