//+------------------------------------------------------------------+ //| CandlePatterns.mqh | //| Comprehensive Candlestick Pattern Detection | //| STANDALONE VERSION - No external dependencies | //+------------------------------------------------------------------+ #property copyright "QuarterTheory x VIZION" #property version "2.00" #property strict //================ PATTERN ENUMS ==================// enum ENUM_PATTERN_TYPE { PATTERN_NONE, PATTERN_REVERSAL_BULLISH, PATTERN_REVERSAL_BEARISH, PATTERN_CONTINUATION_BULL, PATTERN_CONTINUATION_BEAR, PATTERN_INDECISION }; enum ENUM_PATTERN_NAME { // Single Candle Patterns PATT_HAMMER, PATT_INVERTED_HAMMER, PATT_HANGING_MAN, PATT_SHOOTING_STAR, PATT_DOJI, PATT_DRAGONFLY_DOJI, PATT_GRAVESTONE_DOJI, PATT_SPINNING_TOP, PATT_MARUBOZU_BULL, PATT_MARUBOZU_BEAR, // Two Candle Patterns PATT_ENGULFING_BULL, PATT_ENGULFING_BEAR, PATT_PIERCING_LINE, PATT_DARK_CLOUD, PATT_TWEEZER_TOP, PATT_TWEEZER_BOTTOM, PATT_HARAMI_BULL, PATT_HARAMI_BEAR, PATT_BULLISH_KICKER, PATT_BEARISH_KICKER, // Three+ Candle Patterns PATT_THREE_WHITE_SOLDIERS, PATT_THREE_BLACK_CROWS, PATT_MORNING_STAR, PATT_EVENING_STAR, PATT_MORNING_DOJI_STAR, PATT_EVENING_DOJI_STAR, PATT_THREE_INSIDE_UP, PATT_THREE_INSIDE_DOWN, PATT_THREE_OUTSIDE_UP, PATT_THREE_OUTSIDE_DOWN, // TheStrat Patterns PATT_2_1_2_BULL_CONT, PATT_2_1_2_BEAR_CONT, PATT_3_1_2_BULL_REV, PATT_3_1_2_BEAR_REV, PATT_2_2_REVERSAL_BULL, PATT_2_2_REVERSAL_BEAR, PATT_2_2_CONTINUATION_BULL, PATT_2_2_CONTINUATION_BEAR }; //================ PATTERN STRUCTURE ==================// struct PatternSignal { ENUM_PATTERN_NAME name; ENUM_PATTERN_TYPE type; string name_str; int strength; int candle_index; double pattern_price; datetime time; bool generates_warn; bool generates_praise; double adjusted_strength; }; //================ GLOBAL VARIABLES ==================// PatternSignal g_ActivePatterns[]; int g_PatternCount = 0; const int MAX_PATTERNS = 100; // Candle measurement constants const double CANDLE_BODY_RATIO = 0.6; const double SHADOW_RATIO = 2.0; const double DOJI_RATIO = 0.1; const double ENGULF_RATIO = 1.0; //+------------------------------------------------------------------+ //| Initialize Pattern Detection System | //+------------------------------------------------------------------+ bool InitializePatternDetection() { ArrayResize(g_ActivePatterns, MAX_PATTERNS); g_PatternCount = 0; Print("✅ Candlestick Pattern Detection System Initialized"); Print(" - 37 pattern types supported"); return true; } //+------------------------------------------------------------------+ //| Main Pattern Detection Entry Point | //+------------------------------------------------------------------+ void DetectAllCandlestickPatterns() { g_PatternCount = 0; MqlRates rates[]; ArraySetAsSeries(rates, true); int copied = CopyRates(_Symbol, _Period, 0, 100, rates); if(copied < 20) return; DetectSingleCandlePatterns(rates); DetectTwoCandlePatterns(rates); DetectThreeCandlePatterns(rates); DetectTheStratPatterns(rates); GenerateSignalsFromPatterns(); if(g_PatternCount > 0) Print("🕯️ Detected ", g_PatternCount, " candlestick patterns"); } //+------------------------------------------------------------------+ //| SINGLE CANDLE PATTERNS | //+------------------------------------------------------------------+ void DetectSingleCandlePatterns(const MqlRates &rates[]) { int idx = 1; if(idx >= ArraySize(rates)) return; double o = rates[idx].open; double h = rates[idx].high; double l = rates[idx].low; double c = rates[idx].close; double body = MathAbs(c - o); double total_range = h - l; double upper_shadow = h - MathMax(o, c); double lower_shadow = MathMin(o, c) - l; if(total_range == 0) return; // HAMMER if(lower_shadow >= SHADOW_RATIO * body && upper_shadow <= 0.3 * body && body / total_range >= 0.2 && IsBearishTrend(rates, idx)) { AddPattern(PATT_HAMMER, PATTERN_REVERSAL_BULLISH, idx, l, rates[idx].time, 85); } // INVERTED HAMMER if(upper_shadow >= SHADOW_RATIO * body && lower_shadow <= 0.3 * body && body / total_range >= 0.2 && IsBearishTrend(rates, idx)) { AddPattern(PATT_INVERTED_HAMMER, PATTERN_REVERSAL_BULLISH, idx, h, rates[idx].time, 75); } // SHOOTING STAR if(upper_shadow >= SHADOW_RATIO * body && lower_shadow <= 0.3 * body && body / total_range >= 0.2 && IsBullishTrend(rates, idx)) { AddPattern(PATT_SHOOTING_STAR, PATTERN_REVERSAL_BEARISH, idx, h, rates[idx].time, 85); } // HANGING MAN if(lower_shadow >= SHADOW_RATIO * body && upper_shadow <= 0.3 * body && body / total_range >= 0.2 && IsBullishTrend(rates, idx)) { AddPattern(PATT_HANGING_MAN, PATTERN_REVERSAL_BEARISH, idx, l, rates[idx].time, 75); } // DOJI if(body / total_range < DOJI_RATIO) { if(lower_shadow > 2 * upper_shadow) AddPattern(PATT_DRAGONFLY_DOJI, PATTERN_REVERSAL_BULLISH, idx, c, rates[idx].time, 70); else if(upper_shadow > 2 * lower_shadow) AddPattern(PATT_GRAVESTONE_DOJI, PATTERN_REVERSAL_BEARISH, idx, c, rates[idx].time, 70); else AddPattern(PATT_DOJI, PATTERN_INDECISION, idx, c, rates[idx].time, 60); } // SPINNING TOP if(body / total_range > DOJI_RATIO && body / total_range < 0.3 && upper_shadow > body && lower_shadow > body) { AddPattern(PATT_SPINNING_TOP, PATTERN_INDECISION, idx, c, rates[idx].time, 55); } // MARUBOZU if(c > o && body / total_range > 0.9) AddPattern(PATT_MARUBOZU_BULL, PATTERN_CONTINUATION_BULL, idx, c, rates[idx].time, 80); if(c < o && body / total_range > 0.9) AddPattern(PATT_MARUBOZU_BEAR, PATTERN_CONTINUATION_BEAR, idx, c, rates[idx].time, 80); } //+------------------------------------------------------------------+ //| TWO CANDLE PATTERNS | //+------------------------------------------------------------------+ void DetectTwoCandlePatterns(const MqlRates &rates[]) { if(ArraySize(rates) < 3) return; int curr = 1, prev = 2; double o1 = rates[prev].open, h1 = rates[prev].high; double l1 = rates[prev].low, c1 = rates[prev].close; double o2 = rates[curr].open, h2 = rates[curr].high; double l2 = rates[curr].low, c2 = rates[curr].close; double body1 = MathAbs(c1 - o1); double body2 = MathAbs(c2 - o2); // BULLISH ENGULFING if(c1 < o1 && c2 > o2 && o2 <= c1 && c2 >= o1 && body2 > body1 * ENGULF_RATIO) { AddPattern(PATT_ENGULFING_BULL, PATTERN_REVERSAL_BULLISH, curr, c2, rates[curr].time, 90); } // BEARISH ENGULFING if(c1 > o1 && c2 < o2 && o2 >= c1 && c2 <= o1 && body2 > body1 * ENGULF_RATIO) { AddPattern(PATT_ENGULFING_BEAR, PATTERN_REVERSAL_BEARISH, curr, c2, rates[curr].time, 90); } // PIERCING LINE if(c1 < o1 && c2 > o2 && o2 < l1 && c2 > (o1 + c1) / 2 && c2 < o1) { AddPattern(PATT_PIERCING_LINE, PATTERN_REVERSAL_BULLISH, curr, c2, rates[curr].time, 85); } // DARK CLOUD COVER if(c1 > o1 && c2 < o2 && o2 > h1 && c2 < (o1 + c1) / 2 && c2 > o1) { AddPattern(PATT_DARK_CLOUD, PATTERN_REVERSAL_BEARISH, curr, c2, rates[curr].time, 85); } // TWEEZER TOP if(MathAbs(h1 - h2) < (h1 - l1) * 0.02 && IsBullishTrend(rates, curr)) { AddPattern(PATT_TWEEZER_TOP, PATTERN_REVERSAL_BEARISH, curr, h2, rates[curr].time, 75); } // TWEEZER BOTTOM if(MathAbs(l1 - l2) < (h1 - l1) * 0.02 && IsBearishTrend(rates, curr)) { AddPattern(PATT_TWEEZER_BOTTOM, PATTERN_REVERSAL_BULLISH, curr, l2, rates[curr].time, 75); } // BULLISH HARAMI if(c1 < o1 && c2 > o2 && o2 > c1 && c2 < o1 && body2 < body1 * 0.5) { AddPattern(PATT_HARAMI_BULL, PATTERN_REVERSAL_BULLISH, curr, c2, rates[curr].time, 70); } // BEARISH HARAMI if(c1 > o1 && c2 < o2 && o2 < c1 && c2 > o1 && body2 < body1 * 0.5) { AddPattern(PATT_HARAMI_BEAR, PATTERN_REVERSAL_BEARISH, curr, c2, rates[curr].time, 70); } // BULLISH KICKER if(c1 < o1 && c2 > o2 && o2 > c1 && body1 > (h1 - l1) * 0.7 && body2 > (h2 - l2) * 0.7) { AddPattern(PATT_BULLISH_KICKER, PATTERN_REVERSAL_BULLISH, curr, o2, rates[curr].time, 95); } // BEARISH KICKER if(c1 > o1 && c2 < o2 && o2 < c1 && body1 > (h1 - l1) * 0.7 && body2 > (h2 - l2) * 0.7) { AddPattern(PATT_BEARISH_KICKER, PATTERN_REVERSAL_BEARISH, curr, o2, rates[curr].time, 95); } } //+------------------------------------------------------------------+ //| THREE+ CANDLE PATTERNS | //+------------------------------------------------------------------+ void DetectThreeCandlePatterns(const MqlRates &rates[]) { if(ArraySize(rates) < 4) return; int c1 = 3, c2 = 2, c3 = 1; // THREE WHITE SOLDIERS if(rates[c1].close > rates[c1].open && rates[c2].close > rates[c2].open && rates[c3].close > rates[c3].open && rates[c2].close > rates[c1].close && rates[c3].close > rates[c2].close) { AddPattern(PATT_THREE_WHITE_SOLDIERS, PATTERN_CONTINUATION_BULL, c3, rates[c3].close, rates[c3].time, 90); } // THREE BLACK CROWS if(rates[c1].close < rates[c1].open && rates[c2].close < rates[c2].open && rates[c3].close < rates[c3].open && rates[c2].close < rates[c1].close && rates[c3].close < rates[c2].close) { AddPattern(PATT_THREE_BLACK_CROWS, PATTERN_CONTINUATION_BEAR, c3, rates[c3].close, rates[c3].time, 90); } // MORNING STAR if(rates[c1].close < rates[c1].open && MathAbs(rates[c2].close - rates[c2].open) < (rates[c1].high - rates[c1].low) * 0.3 && rates[c3].close > rates[c3].open && rates[c3].close > (rates[c1].open + rates[c1].close) / 2) { AddPattern(PATT_MORNING_STAR, PATTERN_REVERSAL_BULLISH, c3, rates[c3].close, rates[c3].time, 95); } // EVENING STAR if(rates[c1].close > rates[c1].open && MathAbs(rates[c2].close - rates[c2].open) < (rates[c1].high - rates[c1].low) * 0.3 && rates[c3].close < rates[c3].open && rates[c3].close < (rates[c1].open + rates[c1].close) / 2) { AddPattern(PATT_EVENING_STAR, PATTERN_REVERSAL_BEARISH, c3, rates[c3].close, rates[c3].time, 95); } } //+------------------------------------------------------------------+ //| THESTRAT PATTERNS | //+------------------------------------------------------------------+ void DetectTheStratPatterns(const MqlRates &rates[]) { if(ArraySize(rates) < 4) return; int strat_type[3]; for(int i = 1; i <= 3; i++) { if(i >= ArraySize(rates)) continue; int curr = i; int prev = i + 1; // Inside bar (1) if(rates[curr].high <= rates[prev].high && rates[curr].low >= rates[prev].low) strat_type[i-1] = 1; // Outside bar (2) else if(rates[curr].high > rates[prev].high && rates[curr].low < rates[prev].low) strat_type[i-1] = 2; // Directional (3) else strat_type[i-1] = 3; } // 2-1-2 BULLISH CONTINUATION if(strat_type[2] == 2 && strat_type[1] == 1 && strat_type[0] == 2 && rates[1].close > rates[1].open) { AddPattern(PATT_2_1_2_BULL_CONT, PATTERN_CONTINUATION_BULL, 1, rates[1].close, rates[1].time, 88); } // 2-1-2 BEARISH CONTINUATION if(strat_type[2] == 2 && strat_type[1] == 1 && strat_type[0] == 2 && rates[1].close < rates[1].open) { AddPattern(PATT_2_1_2_BEAR_CONT, PATTERN_CONTINUATION_BEAR, 1, rates[1].close, rates[1].time, 88); } // 3-1-2 BULLISH REVERSAL if(strat_type[2] == 3 && strat_type[1] == 1 && strat_type[0] == 2 && rates[3].close < rates[3].open && rates[1].close > rates[1].open) { AddPattern(PATT_3_1_2_BULL_REV, PATTERN_REVERSAL_BULLISH, 1, rates[1].close, rates[1].time, 92); } // 3-1-2 BEARISH REVERSAL if(strat_type[2] == 3 && strat_type[1] == 1 && strat_type[0] == 2 && rates[3].close > rates[3].open && rates[1].close < rates[1].open) { AddPattern(PATT_3_1_2_BEAR_REV, PATTERN_REVERSAL_BEARISH, 1, rates[1].close, rates[1].time, 92); } } //+------------------------------------------------------------------+ //| Helper Functions | //+------------------------------------------------------------------+ bool IsBullishTrend(const MqlRates &rates[], int index) { if(index + 10 >= ArraySize(rates)) return false; double ma_fast = 0, ma_slow = 0; for(int i = index; i < index + 5; i++) ma_fast += rates[i].close; ma_fast /= 5; for(int i = index; i < index + 10; i++) ma_slow += rates[i].close; ma_slow /= 10; return ma_fast > ma_slow; } bool IsBearishTrend(const MqlRates &rates[], int index) { return !IsBullishTrend(rates, index); } //+------------------------------------------------------------------+ //| Add Pattern to Array | //+------------------------------------------------------------------+ void AddPattern(ENUM_PATTERN_NAME name, ENUM_PATTERN_TYPE type, int candle_idx, double price, datetime time, int strength) { if(g_PatternCount >= MAX_PATTERNS) return; PatternSignal signal; signal.name = name; signal.type = type; signal.name_str = GetPatternName(name); signal.strength = strength; signal.candle_index = candle_idx; signal.pattern_price = price; signal.time = time; signal.generates_warn = false; signal.generates_praise = false; signal.adjusted_strength = strength; g_ActivePatterns[g_PatternCount] = signal; g_PatternCount++; } //+------------------------------------------------------------------+ //| Get Pattern Name String | //+------------------------------------------------------------------+ string GetPatternName(ENUM_PATTERN_NAME name) { switch(name) { case PATT_HAMMER: return "HAMMER"; case PATT_INVERTED_HAMMER: return "INVERTED_HAMMER"; case PATT_SHOOTING_STAR: return "SHOOTING_STAR"; case PATT_HANGING_MAN: return "HANGING_MAN"; case PATT_DOJI: return "DOJI"; case PATT_DRAGONFLY_DOJI: return "DRAGONFLY_DOJI"; case PATT_GRAVESTONE_DOJI: return "GRAVESTONE_DOJI"; case PATT_SPINNING_TOP: return "SPINNING_TOP"; case PATT_MARUBOZU_BULL: return "MARUBOZU_BULL"; case PATT_MARUBOZU_BEAR: return "MARUBOZU_BEAR"; case PATT_ENGULFING_BULL: return "BULLISH_ENGULFING"; case PATT_ENGULFING_BEAR: return "BEARISH_ENGULFING"; case PATT_PIERCING_LINE: return "PIERCING_LINE"; case PATT_DARK_CLOUD: return "DARK_CLOUD_COVER"; case PATT_TWEEZER_TOP: return "TWEEZER_TOP"; case PATT_TWEEZER_BOTTOM: return "TWEEZER_BOTTOM"; case PATT_HARAMI_BULL: return "BULLISH_HARAMI"; case PATT_HARAMI_BEAR: return "BEARISH_HARAMI"; case PATT_BULLISH_KICKER: return "BULLISH_KICKER"; case PATT_BEARISH_KICKER: return "BEARISH_KICKER"; case PATT_THREE_WHITE_SOLDIERS: return "THREE_WHITE_SOLDIERS"; case PATT_THREE_BLACK_CROWS: return "THREE_BLACK_CROWS"; case PATT_MORNING_STAR: return "MORNING_STAR"; case PATT_EVENING_STAR: return "EVENING_STAR"; case PATT_2_1_2_BULL_CONT: return "2-1-2_BULL_CONTINUATION"; case PATT_2_1_2_BEAR_CONT: return "2-1-2_BEAR_CONTINUATION"; case PATT_3_1_2_BULL_REV: return "3-1-2_BULL_REVERSAL"; case PATT_3_1_2_BEAR_REV: return "3-1-2_BEAR_REVERSAL"; default: return "UNKNOWN"; } } //+------------------------------------------------------------------+ //| Generate WARN/PRAISE Signals | //+------------------------------------------------------------------+ void GenerateSignalsFromPatterns() { for(int i = 0; i < g_PatternCount; i++) { if(g_ActivePatterns[i].type == PATTERN_REVERSAL_BULLISH || g_ActivePatterns[i].type == PATTERN_REVERSAL_BEARISH) { g_ActivePatterns[i].generates_warn = true; } if(g_ActivePatterns[i].type == PATTERN_CONTINUATION_BULL || g_ActivePatterns[i].type == PATTERN_CONTINUATION_BEAR) { g_ActivePatterns[i].generates_praise = true; } if(g_ActivePatterns[i].adjusted_strength >= 85) { string signal_type = g_ActivePatterns[i].generates_warn ? "WARN" : g_ActivePatterns[i].generates_praise ? "PRAISE" : "NEUTRAL"; Print("🕯️ ", signal_type, " - ", g_ActivePatterns[i].name_str, " [Strength: ", (int)g_ActivePatterns[i].adjusted_strength, "%]"); } } } //+------------------------------------------------------------------+ //| Get Pattern Count by Type | //+------------------------------------------------------------------+ int GetPatternCount(ENUM_PATTERN_TYPE type) { int count = 0; for(int i = 0; i < g_PatternCount; i++) { if(g_ActivePatterns[i].type == type) count++; } return count; } //+------------------------------------------------------------------+ //| Get Strongest Pattern | //+------------------------------------------------------------------+ PatternSignal GetStrongestPattern() { PatternSignal strongest; strongest.strength = 0; strongest.adjusted_strength = 0; for(int i = 0; i < g_PatternCount; i++) { if(g_ActivePatterns[i].adjusted_strength > strongest.adjusted_strength) strongest = g_ActivePatterns[i]; } return strongest; } //+------------------------------------------------------------------+ //| Integration Functions | //+------------------------------------------------------------------+ int GetPatternWarnCount() { int count = 0; for(int i = 0; i < g_PatternCount; i++) if(g_ActivePatterns[i].generates_warn) count++; return count; } int GetPatternPraiseCount() { int count = 0; for(int i = 0; i < g_PatternCount; i++) if(g_ActivePatterns[i].generates_praise) count++; return count; } bool IsPatternActive(ENUM_PATTERN_NAME pattern_name) { for(int i = 0; i < g_PatternCount; i++) if(g_ActivePatterns[i].name == pattern_name) return true; return false; }