#ifndef CANDLE_PATTERNS_MQH #define CANDLE_PATTERNS_MQH enum ENUM_PATTERN_TYPE { PATTERN_NONE, PATTERN_REVERSAL_BULLISH, PATTERN_REVERSAL_BEARISH, PATTERN_CONTINUATION_BULL, PATTERN_CONTINUATION_BEAR, PATTERN_INDECISION }; enum ENUM_PATTERN_NAME { PATT_HAMMER, PATT_INVERTED_HAMMER, PATT_HANGING_MAN, PATT_SHOOTING_STAR, PATT_DOJI, PATT_SPINNING_TOP, PATT_MARUBOZU_BULL, PATT_MARUBOZU_BEAR, PATT_ENGULFING_BULL, PATT_ENGULFING_BEAR, PATT_TWEEZER_TOP, PATT_TWEEZER_BOTTOM, PATT_THREE_WHITE_SOLDIERS, PATT_THREE_BLACK_CROWS, PATT_MORNING_STAR, PATT_EVENING_STAR, PATT_2_1_2_BULL_CONT, PATT_2_1_2_BEAR_CONT, PATT_3_1_2_BULL_REV, PATT_3_1_2_BEAR_REV }; 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; }; PatternSignal g_ActivePatterns[]; int g_PatternCount = 0; const int MAX_PATTERNS = 100; const double SHADOW_RATIO = 2.0; const double DOJI_RATIO = 0.1; const double ENGULF_RATIO = 1.0; string GetPatternName(const ENUM_PATTERN_NAME name) { switch(name) { case PATT_HAMMER: return "HAMMER"; case PATT_INVERTED_HAMMER: return "INVERTED_HAMMER"; case PATT_HANGING_MAN: return "HANGING_MAN"; case PATT_SHOOTING_STAR: return "SHOOTING_STAR"; case PATT_DOJI: return "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 "ENGULFING_BULL"; case PATT_ENGULFING_BEAR: return "ENGULFING_BEAR"; case PATT_TWEEZER_TOP: return "TWEEZER_TOP"; case PATT_TWEEZER_BOTTOM: return "TWEEZER_BOTTOM"; 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_CONT"; case PATT_2_1_2_BEAR_CONT: return "2-1-2_BEAR_CONT"; case PATT_3_1_2_BULL_REV: return "3-1-2_BULL_REV"; case PATT_3_1_2_BEAR_REV: return "3-1-2_BEAR_REV"; default: return "UNKNOWN"; } } bool IsBullishTrend(const MqlRates &rates[], const int index) { if(index + 10 >= ArraySize(rates)) return false; double ma_fast = 0.0, ma_slow = 0.0; for(int i = index; i < index + 5; ++i) ma_fast += rates[i].close; for(int i = index; i < index + 10; ++i) ma_slow += rates[i].close; ma_fast /= 5.0; ma_slow /= 10.0; return (ma_fast > ma_slow); } bool IsBearishTrend(const MqlRates &rates[], const int index) { return !IsBullishTrend(rates, index); } void AddPattern(const ENUM_PATTERN_NAME name, const ENUM_PATTERN_TYPE type, const int candle_idx, const double price, const datetime time, const 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 = (double)strength; g_ActivePatterns[g_PatternCount++] = signal; } 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 range = h - l; if(range <= 0.0) return; double up_shadow = h - MathMax(o, c); double lo_shadow = MathMin(o, c) - l; if(lo_shadow >= SHADOW_RATIO * body && up_shadow <= 0.3 * body && IsBearishTrend(rates, idx)) AddPattern(PATT_HAMMER, PATTERN_REVERSAL_BULLISH, idx, l, rates[idx].time, 85); if(up_shadow >= SHADOW_RATIO * body && lo_shadow <= 0.3 * body && IsBullishTrend(rates, idx)) AddPattern(PATT_SHOOTING_STAR, PATTERN_REVERSAL_BEARISH, idx, h, rates[idx].time, 85); if(body / range < DOJI_RATIO) AddPattern(PATT_DOJI, PATTERN_INDECISION, idx, c, rates[idx].time, 60); if(body / range > 0.9) { if(c > o) AddPattern(PATT_MARUBOZU_BULL, PATTERN_CONTINUATION_BULL, idx, c, rates[idx].time, 80); if(c < o) AddPattern(PATT_MARUBOZU_BEAR, PATTERN_CONTINUATION_BEAR, idx, c, rates[idx].time, 80); } } void DetectTwoCandlePatterns(const MqlRates &rates[]) { if(ArraySize(rates) < 3) return; int curr = 1, prev = 2; double o1 = rates[prev].open, c1 = rates[prev].close; double o2 = rates[curr].open, c2 = rates[curr].close; double b1 = MathAbs(c1 - o1), b2 = MathAbs(c2 - o2); if(c1 < o1 && c2 > o2 && o2 <= c1 && c2 >= o1 && b2 > b1 * ENGULF_RATIO) AddPattern(PATT_ENGULFING_BULL, PATTERN_REVERSAL_BULLISH, curr, c2, rates[curr].time, 90); if(c1 > o1 && c2 < o2 && o2 >= c1 && c2 <= o1 && b2 > b1 * ENGULF_RATIO) AddPattern(PATT_ENGULFING_BEAR, PATTERN_REVERSAL_BEARISH, curr, c2, rates[curr].time, 90); if(MathAbs(rates[prev].high - rates[curr].high) <= (rates[prev].high - rates[prev].low) * 0.02) AddPattern(PATT_TWEEZER_TOP, PATTERN_REVERSAL_BEARISH, curr, rates[curr].high, rates[curr].time, 75); if(MathAbs(rates[prev].low - rates[curr].low) <= (rates[prev].high - rates[prev].low) * 0.02) AddPattern(PATT_TWEEZER_BOTTOM, PATTERN_REVERSAL_BULLISH, curr, rates[curr].low, rates[curr].time, 75); } void DetectThreeCandlePatterns(const MqlRates &rates[]) { if(ArraySize(rates) < 4) return; int c1 = 3, c2 = 2, c3 = 1; 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); 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); } void DetectTheStratPatterns(const MqlRates &rates[]) { if(ArraySize(rates) < 4) return; int st[3] = {0, 0, 0}; for(int i = 1; i <= 3; ++i) { int curr = i; int prev = i + 1; if(rates[curr].high <= rates[prev].high && rates[curr].low >= rates[prev].low) st[i - 1] = 1; else if(rates[curr].high > rates[prev].high && rates[curr].low < rates[prev].low) st[i - 1] = 2; else st[i - 1] = 3; } if(st[2] == 2 && st[1] == 1 && st[0] == 2) { if(rates[1].close > rates[1].open) AddPattern(PATT_2_1_2_BULL_CONT, PATTERN_CONTINUATION_BULL, 1, rates[1].close, rates[1].time, 88); if(rates[1].close < rates[1].open) AddPattern(PATT_2_1_2_BEAR_CONT, PATTERN_CONTINUATION_BEAR, 1, rates[1].close, rates[1].time, 88); } if(st[2] == 3 && st[1] == 1 && st[0] == 2) { if(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); if(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); } } 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; } } bool InitializePatternDetection() { ArrayResize(g_ActivePatterns, MAX_PATTERNS); g_PatternCount = 0; return true; } 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(); } 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; } #endif