Untitled

 avatar
unknown
plain_text
13 days ago
19 kB
5
Indexable
module Analyzers
  class PatternAnalysis < Base
    BULLISH_PATTERNS = {
      'bullish_belt_hold' => { weight: 1.2, volume_condition: :high, confirmation: :ema_cross },
      'three_white_soldiers' => { weight: 1.7, volume_condition: :sustained, confirmation: :volume_increase },
      'volume_breakout' => { weight: 1.5, volume_condition: :extreme, confirmation: :resistance_break },
      'spring' => { weight: 1.3, volume_condition: :high, confirmation: :follow_through },
      'morning_star' => { weight: 1.6, volume_condition: :breakout, confirmation: :third_candle_confirmation },
      'piercing_line' => { weight: 1.2, volume_condition: :high, confirmation: :next_candle_confirmation },
      'marubozu_bullish' => { weight: 1.3, volume_condition: :high, confirmation: :continuation },
      'double_bottom' => { weight: 1.4, volume_condition: :breakout, confirmation: :neckline_break },
      'bullish_harami' => { weight: 1.1, volume_condition: :normal, confirmation: :next_candle_confirmation },
      'inverted_hammer' => { weight: 1.1, volume_condition: :normal, confirmation: :next_candle_bullish },
      'rsi_bullish_divergence' => { weight: 1.3, volume_condition: :none, confirmation: :rsi_oversold },
      'hammer' => { weight: 1.1, volume_condition: :normal, confirmation: :next_candle_bullish },
      'bullish_engulfing' => { weight: 1.5, volume_condition: :high, confirmation: :next_candle_confirmation },
      'unique_three_river_bottom' => { weight: 1.4, volume_condition: :high, confirmation: :next_candle_confirmation },
      'breakaway_bullish' => { weight: 1.5, volume_condition: :breakout, confirmation: :gap_hold },
      'bullish_kicker' => { weight: 1.5, volume_condition: :extreme, confirmation: :gap_hold },
      'bullish_separating_lines' => { weight: 1.2, volume_condition: :normal, confirmation: :trend_confirmation },
      'abandoned_baby_bullish' => { weight: 1.4, volume_condition: :high, confirmation: :gap_fill },
      'tweezer_bottom' => { weight: 1.2, volume_condition: :high, confirmation: :next_candle_bullish },
      'rounding_bottom' => { weight: 1.5, volume_condition: :sustained, confirmation: :breakout_high_volume },
      'mat_hold_bullish' => { weight: 1.4, volume_condition: :sustained, confirmation: :pullback_continuation },
      'thrusting_line' => { weight: 1.2, volume_condition: :high, confirmation: :next_candle_confirmation },
      'dragonfly_doji' => { weight: 1.2, volume_condition: :high, confirmation: :next_candle_bullish },
      'long_legged_doji_bullish' => { weight: 1.1, volume_condition: :normal, confirmation: :next_candle_confirmation },
      'meeting_lines_bullish' => { weight: 1.2, volume_condition: :high, confirmation: :trend_confirmation },
      'stick_sandwich' => { weight: 1.3, volume_condition: :high, confirmation: :next_candle_confirmation },
      'tristar_bullish' => { weight: 1.1, volume_condition: :normal, confirmation: :next_candle_bullish },
      'symmetrical_triangle_bullish' => { weight: 1.4, volume_condition: :breakout, confirmation: :breakout_direction },
      'triple_bottom' => { weight: 1.6, volume_condition: :breakout, confirmation: :neckline_break },
      'doji_star_bullish' => { weight: 1.1, volume_condition: :normal, confirmation: :next_candle_bullish },
      'cup_with_handle' => { weight: 1.6, volume_condition: :breakout, confirmation: :handle_breakout },
      'flag_bullish' => { weight: 1.3, volume_condition: :breakout, confirmation: :flag_pole_ratio },
      'pennant_bullish' => { weight: 1.3, volume_condition: :breakout, confirmation: :volume_contraction },
      'ascending_triangle' => { weight: 1.5, volume_condition: :breakout, confirmation: :resistance_break },
      'descending_triangle_bullish' => { weight: 1.4, volume_condition: :breakout, confirmation: :support_hold },
      'belt_hold_bullish' => { weight: 1.2, volume_condition: :high, confirmation: :next_candle_confirmation },
      'rising_three_methods' => { weight: 1.3, volume_condition: :sustained, confirmation: :continuation },
      'three_line_strike_bullish' => { weight: 1.3, volume_condition: :breakout, confirmation: :volume_spike },
      'falling_wedge_bullish' => { weight: 1.4, volume_condition: :breakout, confirmation: :wedge_breakout },
      'macd_crossover' => { weight: 1.2, volume_condition: :normal, confirmation: :macd_line_cross }
    }.freeze

    BEARISH_PATTERNS = {
      'bearish_belt_hold' => { weight: 1.2, volume_condition: :high, confirmation: :ema_cross },
      'head_and_shoulders' => { weight: 1.6, volume_condition: :breakout, confirmation: :neckline_break },
      'volume_spike' => { weight: 1.3, volume_condition: :extreme, confirmation: :price_rejection },
      'bearish_engulfing' => { weight: 1.5, volume_condition: :high, confirmation: :next_candle_confirmation },
      'dark_cloud_cover' => { weight: 1.4, volume_condition: :high, confirmation: :next_candle_confirmation },
      'evening_star' => { weight: 1.6, volume_condition: :breakout, confirmation: :third_candle_confirmation },
      'upthrust' => { weight: 1.4, volume_condition: :high, confirmation: :volume_confirmation },
      'low_volume_pullback' => { weight: 1.1, volume_condition: :low, confirmation: :continuation },
      'abandoned_baby_bearish' => { weight: 1.4, volume_condition: :high, confirmation: :gap_fill },
      'three_black_crows' => { weight: 1.7, volume_condition: :sustained, confirmation: :volume_increase },
      'marubozu_bearish' => { weight: 1.3, volume_condition: :high, confirmation: :continuation },
      'rounding_top' => { weight: 1.5, volume_condition: :sustained, confirmation: :breakout_high_volume },
      'bearish_harami' => { weight: 1.1, volume_condition: :normal, confirmation: :next_candle_confirmation },
      'tweezer_top' => { weight: 1.2, volume_condition: :high, confirmation: :next_candle_bearish },
      'bearish_kicker' => { weight: 1.5, volume_condition: :extreme, confirmation: :gap_hold },
      'bearish_separating_lines' => { weight: 1.2, volume_condition: :normal, confirmation: :trend_confirmation },
      'gravestone_doji' => { weight: 1.2, volume_condition: :high, confirmation: :next_candle_bearish },
      'long_legged_doji_bearish' => { weight: 1.1, volume_condition: :normal, confirmation: :next_candle_confirmation },
      'meeting_lines_bearish' => { weight: 1.2, volume_condition: :high, confirmation: :trend_confirmation },
      'two_crows' => { weight: 1.2, volume_condition: :high, confirmation: :next_candle_confirmation },
      'upside_gap_two_crows' => { weight: 1.3, volume_condition: :high, confirmation: :gap_fill },
      'tristar_bearish' => { weight: 1.1, volume_condition: :normal, confirmation: :next_candle_bearish },
      'symmetrical_triangle_bearish' => { weight: 1.4, volume_condition: :breakout, confirmation: :breakout_direction },
      'head_and_shoulders_top' => { weight: 1.6, volume_condition: :breakout, confirmation: :neckline_break },
      'triple_top' => { weight: 1.6, volume_condition: :breakout, confirmation: :neckline_break },
      'doji_star_bearish' => { weight: 1.1, volume_condition: :normal, confirmation: :next_candle_bearish },
      'breakaway_bearish' => { weight: 1.5, volume_condition: :breakout, confirmation: :gap_hold },
      'mat_hold_bearish' => { weight: 1.4, volume_condition: :sustained, confirmation: :pullback_continuation },
      'rising_wedge_bearish' => { weight: 1.4, volume_condition: :breakout, confirmation: :wedge_breakout },
      'flag_bearish' => { weight: 1.3, volume_condition: :breakout, confirmation: :flag_pole_ratio },
      'pennant_bearish' => { weight: 1.3, volume_condition: :breakout, confirmation: :volume_contraction },
      'ascending_triangle_bearish' => { weight: 1.5, volume_condition: :breakout, confirmation: :support_break },
      'descending_triangle' => { weight: 1.5, volume_condition: :breakout, confirmation: :resistance_hold },
      'belt_hold_bearish' => { weight: 1.2, volume_condition: :high, confirmation: :next_candle_confirmation },
      'falling_three_methods' => { weight: 1.3, volume_condition: :sustained, confirmation: :continuation },
      'three_line_strike_bearish' => { weight: 1.3, volume_condition: :breakout, confirmation: :volume_spike },
      'identical_three_crows' => { weight: 1.3, volume_condition: :high, confirmation: :volume_confirmation }
    }.freeze

    def analyze
      bullish = @current.patterns.select { |p| BULLISH_PATTERNS.key?(p) }
      bearish = @current.patterns.select { |p| BEARISH_PATTERNS.key?(p) }

      {
        bullish_patterns: bullish,
        bearish_patterns: bearish,
        bullish_score: bullish.sum { |p| BULLISH_PATTERNS[p][:weight] },
        bearish_score: bearish.sum { |p| BEARISH_PATTERNS[p][:weight] },
        net_score: bullish.sum { |p| BULLISH_PATTERNS[p][:weight] } - bearish.sum { |p| BEARISH_PATTERNS[p][:weight] },
        strongest_pattern: find_strongest_pattern(bullish + bearish),
        volume_analysis: analyze_pattern_volume(bullish + bearish),
        confirmations: check_confirmations(bullish + bearish),
        pattern_combinations: analyze_pattern_combinations(bullish, bearish),
        multi_timeframe_confirmation: check_multi_timeframe_confirmation(bullish + bearish),
        trend_alignment: check_trend_alignment(bullish + bearish),
        pattern_trend: analyze_pattern_trend
      }
    end

    private

    def analyze_pattern_combinations(bullish, bearish)
      combinations = {
        reversal_combinations: check_reversal_combinations(bullish, bearish),
        continuation_combinations: check_continuation_combinations(bullish, bearish),
        volume_confirmation_combinations: check_volume_confirmation_combinations(bullish + bearish),
        indicator_confirmation_combinations: check_indicator_confirmation_combinations(bullish + bearish)
      }

      combinations.merge!(find_high_probability_combinations(bullish, bearish))
      combinations
    end

    def check_reversal_combinations(bullish, bearish)
      {
        double_bottom_with_hammer: bullish.include?('double_bottom') && bullish.include?('hammer'),
        head_and_shoulders_with_volume: bearish.include?('head_and_shoulders') && check_volume_condition(:breakout),
        morning_star_with_rsi: bullish.include?('morning_star') && @current.rsi_14 < 30,
        evening_star_with_macd: bearish.include?('evening_star') && @current.macd_line < @current.macd_signal
      }
    end

    def check_continuation_combinations(bullish, bearish)
      {
        flag_with_volume: (bullish.include?('flag_bullish') || bearish.include?('flag_bearish')) && check_volume_condition(:breakout),
        pennant_with_trend: (bullish.include?('pennant_bullish') && @current.trend_slope > 0) || 
                          (bearish.include?('pennant_bearish') && @current.trend_slope < 0),
        triangle_with_breakout: (bullish.include?('ascending_triangle') && @current.close > @current.resistance_level) ||
                              (bearish.include?('descending_triangle') && @current.close < @current.support_level)
      }
    end

    def check_volume_confirmation_combinations(patterns)
      patterns.each_with_object({}) do |pattern, result|
        condition = if BULLISH_PATTERNS.key?(pattern)
                      BULLISH_PATTERNS[pattern][:volume_condition]
                    else
                      BEARISH_PATTERNS[pattern][:volume_condition]
                    end
        result[pattern] = check_volume_condition(condition)
      end
    end

    def check_indicator_confirmation_combinations(patterns)
      patterns.each_with_object({}) do |pattern, result|
        confirmation = if BULLISH_PATTERNS.key?(pattern)
                         BULLISH_PATTERNS[pattern][:confirmation]
                       else
                         BEARISH_PATTERNS[pattern][:confirmation]
                       end
        result[pattern] = check_confirmation(confirmation)
      end
    end

    def find_high_probability_combinations(bullish, bearish)
      high_prob = {}
      
      if bullish.include?('three_white_soldiers') && bullish.include?('volume_breakout') && @current.trend_slope > 0
        high_prob[:strong_bullish_continuation] = true
      end

      if bearish.include?('three_black_crows') && bearish.include?('volume_spike') && @current.trend_slope < 0
        high_prob[:strong_bearish_continuation] = true
      end

      if bullish.include?('hammer') && bullish.include?('double_bottom') && @current.rsi_14 < 30
        high_prob[:bullish_reversal_combo] = true
      end

      if bearish.include?('gravestone_doji') && bearish.include?('head_and_shoulders') && @current.rsi_14 > 70
        high_prob[:bearish_reversal_combo] = true
      end

      high_prob
    end

    def check_multi_timeframe_confirmation(patterns)
      return {} unless @history.size >= 5

      patterns.each_with_object({}) do |pattern, result|
        higher_tf_confirmed = case pattern
        when *BULLISH_PATTERNS.keys
          @history.last(3).any? { |i| i.patterns.include?(pattern) } && @current.trend_slope > 0
        when *BEARISH_PATTERNS.keys
          @history.last(3).any? { |i| i.patterns.include?(pattern) } && @current.trend_slope < 0
        else
          false
        end

        result[pattern] = higher_tf_confirmed
      end
    end

    def check_trend_alignment(patterns)
      patterns.each_with_object({}) do |pattern, result|
        aligned = case pattern
        when *BULLISH_PATTERNS.keys
          @current.trend_slope > 0 || @current.ema_8 > @current.ema_20
        when *BEARISH_PATTERNS.keys
          @current.trend_slope < 0 || @current.ema_8 < @current.ema_20
        else
          false
        end

        result[pattern] = aligned
      end
    end

    def find_strongest_pattern(patterns)
      return nil if patterns.empty?

      patterns.max_by do |pattern|
        if BULLISH_PATTERNS.key?(pattern)
          BULLISH_PATTERNS[pattern][:weight]
        else
          BEARISH_PATTERNS[pattern][:weight]
        end
      end
    end

    def analyze_pattern_volume(patterns)
      patterns.each_with_object({}) do |pattern, result|
        condition = if BULLISH_PATTERNS.key?(pattern)
                      BULLISH_PATTERNS[pattern][:volume_condition]
                    else
                      BEARISH_PATTERNS[pattern][:volume_condition]
                    end

        result[pattern] = {
          condition: condition,
          current_volume: @current.volume,
          volume_ratio: (@current.volume.to_f / @avg_volume).round(2),
          meets_condition: check_volume_condition(condition)
        }
      end
    end

    def check_volume_condition(condition)
      ratio = @current.volume.to_f / @avg_volume

      case condition
      when :extreme then ratio > 3.0
      when :high then ratio > 1.5
      when :normal then ratio.between?(0.75, 1.5)
      when :low then ratio < 0.75
      when :sustained then check_sustained_volume
      when :breakout then ratio > 2.0 && @current.close > @current.resistance_level
      when :none then true
      else false
      end
    end

    def check_sustained_volume
      return false unless @history.size >= 3

      @history.last(3).all? { |i| i.volume > @avg_volume * 1.2 }
    end

    def check_confirmations(patterns)
      patterns.each_with_object({}) do |pattern, result|
        confirmation_type = if BULLISH_PATTERNS.key?(pattern)
                              BULLISH_PATTERNS[pattern][:confirmation]
                            else
                              BEARISH_PATTERNS[pattern][:confirmation]
                            end

        result[pattern] = {
          confirmation_type: confirmation_type,
          confirmed: check_confirmation(confirmation_type)
        }
      end
    end

    def check_confirmation(confirmation_type)
      case confirmation_type
      when :ema_cross
        @current.ema_8 > @current.ema_20 && @current.ema_20 > @current.ema_50
      when :volume_increase
        @current.volume > @history.last(3).map(&:volume).max
      when :resistance_break
        @current.close > @current.resistance_level
      when :follow_through
        @candles[-1].close > @candles[-2].high
      when :third_candle_confirmation
        @candles.size >= 3 && @candles[-1].close > @candles[-3].high
      when :next_candle_confirmation
        @candles.size >= 2 && @candles[-1].close > @candles[-2].close
      when :next_candle_bullish
        @candles.size >= 2 && @candles[-1].bullish?
      when :continuation
        @current.trend_slope > 0
      when :neckline_break
        @current.close > @current.resistance_level
      when :rsi_oversold
        @current.rsi_14 < 30
      when :gap_hold
        @candles.size >= 2 && (@candles[-1].low > @candles[-2].high || @candles[-1].high < @candles[-2].low)
      when :trend_confirmation
        @current.trend_slope > 0 && @current.macd_line > @current.macd_signal
      when :wedge_breakout
        (@current.bollinger_upper - @current.bollinger_lower) < (@current.keltner_upper - @current.keltner_lower)
      when :flag_pole_ratio
        (@current.close - @current.low) / (@current.high - @current.low) > 0.7
      when :volume_contraction
        @current.volume < @history.last(3).map(&:volume).average
      when :support_hold
        @current.close > @current.support_level
      when :volume_spike
        @current.volume > @history.last(5).map(&:volume).max
      when :macd_line_cross
        @current.macd_line > @current.macd_signal
      when :price_rejection
        @current.close < @current.open && @current.upper_shadow > (@current.high - @current.low) * 0.6
      when :gap_fill
        @candles.size >= 3 && @candles[-1].close.between?(@candles[-3].low, @candles[-3].high)
      when :support_break
        @current.close < @current.support_level
      when :resistance_hold
        @current.close < @current.resistance_level
      when :handle_breakout
        @current.close > @current.resistance_level && @current.volume > @avg_volume * 1.5
      when :next_candle_bearish
        @candles.size >= 2 && @candles[-1].bearish?
      when :breakout_direction
        (@current.bollinger_upper - @current.bollinger_lower) > (@history[-3].bollinger_upper - @history[-3].bollinger_lower)
      when :volume_confirmation
        @current.volume > @avg_volume * 1.5
      else
        false
      end
    end

    def analyze_pattern_trend
      {
        short_term: {
          ema_8_vs_20: @current.ema_8 > @current.ema_20 ? :bullish : :bearish,
          rsi: @current.rsi_14 > 50 ? :bullish : :bearish,
          macd: @current.macd_line > @current.macd_signal ? :bullish : :bearish
        },
        medium_term: {
          ema_20_vs_50: @current.ema_20 > @current.ema_50 ? :bullish : :bearish,
          trend_slope: @current.trend_slope > 0 ? :bullish : :bearish,
          adx: @current.adx > 25 ? :strong_trend : :weak_trend
        }
      }
    end
  end
end
Editor is loading...
Leave a Comment