Indicator · Pine Script v6 · On the chart

Pattern Recognition [WavesUnchained]

by casoon

Pattern Recognition [WavesUnchained] automatically identifies chart patterns including ascending, descending, and symmetrical triangles, rising and falling wedges, and parallel channels.

View on GitHub

Repaints

GitHub stars9on the repository
Pine versionv6current
Lines of code1,221
Settings31inputs
LicenceMIT
Last updateyesterday27 Oct 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

1,221 lines of Pine Script v6 from archive/indicators/pattern_recognition/pattern_recognition.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

pattern_recognition.pine
//@version=6
// ============================================================================
// Pattern Recognition [WavesUnchained]
// Version: 1.0
// Author: WavesUnchained
// Build: 2025-12-20 07:34:59
// ============================================================================
// Advanced pattern recognition with technical validation and breakout detection
//
// Features:
//   ✓ Triangle Detection (Ascending, Descending, Symmetrical)
//   ✓ Wedge Detection (Rising, Falling)
//   ✓ Channel Detection (Parallel channels)
//   ✓ Multi-point pivot validation
//   ✓ Volume-confirmed breakouts
//   ✓ RSI divergence confirmation
//   ✓ Stochastic RSI filtering
//   ✓ Real-time pattern table
//   ✓ Target projection with Fibonacci
//   ✓ Smart alerts for breakouts
//
// ============================================================================
indicator("Pattern Recognition [WavesUnchained]",
     overlay=true,
     max_lines_count=500,
     max_labels_count=100)

// ============================================================================
// PATTERN RECOGNITION - MAIN ENTRY POINT
// ============================================================================
// Advanced pattern recognition with technical validation and breakout detection
//
// Features:
//   ✓ Triangle Detection (Ascending, Descending, Symmetrical)
//   ✓ Wedge Detection (Rising, Falling)
//   ✓ Channel Detection (Parallel channels)
//   ✓ Multi-point pivot validation
//   ✓ Volume-confirmed breakouts
//   ✓ RSI divergence confirmation
//   ✓ Stochastic RSI filtering
//   ✓ Real-time pattern table
//   ✓ Smart alerts for breakouts
//
// Author: WavesUnchained
// Version: 1.0
// ============================================================================

// ============================================================================
// MODULES (inlined by build system)
// ============================================================================
// Module order:
// 1. types.pine        - Type definitions
// 2. storage.pine      - Var arrays for patterns/pivots
// 3. inputs.pine       - User inputs
// 4. helpers.pine      - Helper functions
// 5. pivot_engine.pine - Pivot detection
// 6. detection_*.pine  - Pattern detection modules
// 7. visualization.pine - Drawing
// 8. info_table.pine   - Info panel

// ============================================================================
// PATTERN DETECTION PIPELINE
// ============================================================================

// Pivot detection happens in pivot_engine module (var arrays)
// Pattern detection and storage happens in visualization module

// ============================================================================
// ALERTS
// ============================================================================

// Triangle Breakouts

// ============================================================================
// INLINE MODULES (Combined at build time)
// ============================================================================

// ============================================================================
// MODULE: types
// ============================================================================

// ============================================================================
// TYPE DEFINITIONS
// ============================================================================

// Pattern Types
type PatternType
    string name
    bool isBullish
    bool isBearish
    bool isNeutral

// Triangle Pattern
type TrianglePattern
    int startBar
    int endBar
    float upperLine_y1
    float upperLine_y2
    float lowerLine_y1
    float lowerLine_y2
    int upperTouches
    int lowerTouches
    string triangleType
    float confidence
    bool active
    line upperLine
    line lowerLine
    label patternLabel

// Wedge Pattern
type WedgePattern
    int startBar
    int endBar
    float upperLine_y1
    float upperLine_y2
    float lowerLine_y1
    float lowerLine_y2
    int upperTouches
    int lowerTouches
    string wedgeType
    bool hasRSIDivergence
    bool hasStochDivergence
    float confidence
    bool active
    line upperLine
    line lowerLine
    label patternLabel

// Channel Pattern
type ChannelPattern
    int startBar
    int endBar
    float upperLine_y1
    float upperLine_y2
    float lowerLine_y1
    float lowerLine_y2
    int upperTouches
    int lowerTouches
    string channelType
    bool isParallel
    float confidence
    bool active
    line upperLine
    line lowerLine
    line midLine
    label patternLabel

// Technical Indicators State
type TechnicalState
    float rsi
    float stochK
    float stochD
    bool stochCrossUp
    bool stochCrossDown
    bool inOverbought
    bool inOversold
    float volumeRatio

// Divergence Detection
type Divergence
    bool regularBullish
    bool regularBearish
    bool hiddenBullish
    bool hiddenBearish
    int pivotIndex
    float strength


// ============================================================================
// MODULE: storage
// ============================================================================

// ============================================================================
// STORAGE & STATE VARIABLES
// ============================================================================

// Pattern Storage
var array<TrianglePattern> trianglePatterns = array.new<TrianglePattern>()
var array<WedgePattern> wedgePatterns = array.new<WedgePattern>()
var array<ChannelPattern> channelPatterns = array.new<ChannelPattern>()

// Pivot Detection
var array<float> pivotHighs = array.new<float>()
var array<int> pivotHighBars = array.new<int>()
var array<float> pivotLows = array.new<float>()
var array<int> pivotLowBars = array.new<int>()

// Technical Indicators History
var array<float> rsiHistory = array.new<float>()
var array<float> stochKHistory = array.new<float>()
var array<float> stochDHistory = array.new<float>()
var array<float> volumeHistory = array.new<float>()

// State Tracking
var int lastUpdateBar = 0
var bool isNewBar = false


// ============================================================================
// MODULE: inputs
// ============================================================================

// ============================================================================
// INPUT DEFINITIONS
// ============================================================================

// Pattern Detection Settings
bool enableTriangles = input.bool(true, "Enable Triangles", group="Pattern Detection")
bool enableWedges = input.bool(true, "Enable Wedges", group="Pattern Detection")
bool enableChannels = input.bool(true, "Enable Channels", group="Pattern Detection")

// Pattern Validation
int minTouchPoints = input.int(2, "Minimum Touch Points", minval=2, maxval=5, group="Validation")
int minPatternBars = input.int(15, "Minimum Pattern Bars", minval=10, maxval=100, group="Validation")
float touchTolerance = input.float(0.5, "Touch Tolerance %", minval=0.1, maxval=2.0, step=0.1, group="Validation")

// Breakout Settings
bool requireVolumeConfirm = input.bool(true, "Require Volume Confirmation", group="Breakout")
float volumeMultiplier = input.float(1.5, "Volume Spike Multiplier", minval=1.0, maxval=3.0, step=0.1, group="Breakout")
bool requireCloseOutside = input.bool(true, "Require Close Outside Pattern", group="Breakout")

// Technical Validation
bool useRSI = input.bool(true, "Use RSI Filter", group="Technical Indicators")
int rsiLength = input.int(14, "RSI Length", minval=5, maxval=50, group="Technical Indicators")
int rsiOverbought = input.int(70, "RSI Overbought", minval=60, maxval=90, group="Technical Indicators")
int rsiOversold = input.int(30, "RSI Oversold", minval=10, maxval=40, group="Technical Indicators")

// Stochastic RSI Settings
bool useStochRSI = input.bool(true, "Use Stochastic RSI", group="Technical Indicators")
int stochRsiLength = input.int(14, "Stoch RSI Length", minval=5, maxval=50, group="Technical Indicators")
int stochRsiSmoothK = input.int(3, "Stoch RSI %K Smoothing", minval=1, maxval=10, group="Technical Indicators")
int stochRsiSmoothD = input.int(3, "Stoch RSI %D Smoothing", minval=1, maxval=10, group="Technical Indicators")
int stochRsiOverbought = input.int(80, "Stoch RSI Overbought", minval=70, maxval=95, group="Technical Indicators")
int stochRsiOversold = input.int(20, "Stoch RSI Oversold", minval=5, maxval=30, group="Technical Indicators")

// Divergence Detection
bool detectDivergence = input.bool(true, "Detect Divergence", group="Advanced")
int divergenceLookback = input.int(5, "Divergence Lookback", minval=3, maxval=20, group="Advanced")

// Volume Settings
bool useVolume = input.bool(true, "Use Volume Filter", group="Volume")
int volumeMA = input.int(20, "Volume MA Length", minval=5, maxval=100, group="Volume")

// Visualization
bool showPatternLabels = input.bool(true, "Show Pattern Labels", group="Display")
bool showTrendLines = input.bool(true, "Show Trend Lines", group="Display")
bool showTargets = input.bool(true, "Show Price Targets", group="Display")
bool showConfidence = input.bool(true, "Show Confidence %", group="Display")

// Colors
color colorAscending = input.color(color.new(color.green, 0), "Ascending/Bullish", group="Colors")
color colorDescending = input.color(color.new(color.red, 0), "Descending/Bearish", group="Colors")
color colorNeutral = input.color(color.new(color.gray, 0), "Neutral/Symmetric", group="Colors")
color colorBreakout = input.color(color.new(color.yellow, 0), "Breakout Signal", group="Colors")


// ============================================================================
// MODULE: helpers
// ============================================================================

// ============================================================================
// HELPER FUNCTIONS
// ============================================================================

// Calculate Stochastic RSI
f_stoch_rsi() =>
    float rsiValue = ta.rsi(close, stochRsiLength)
    float lowestRSI = ta.lowest(rsiValue, stochRsiLength)
    float highestRSI = ta.highest(rsiValue, stochRsiLength)
    float stochRSI = highestRSI - lowestRSI != 0 ? (rsiValue - lowestRSI) / (highestRSI - lowestRSI) * 100 : 0
    float k = ta.sma(stochRSI, stochRsiSmoothK)
    float d = ta.sma(k, stochRsiSmoothD)
    [k, d]

// Get Technical State
f_get_technical_state() =>
    TechnicalState state = TechnicalState.new()

    // RSI
    state.rsi := useRSI ? ta.rsi(close, rsiLength) : 50.0

    // Stochastic RSI
    [k, d] = f_stoch_rsi()
    state.stochK := useStochRSI ? k : 50.0
    state.stochD := useStochRSI ? d : 50.0

    // Crossover detection
    state.stochCrossUp := useStochRSI and ta.crossover(k, d)
    state.stochCrossDown := useStochRSI and ta.crossunder(k, d)

    // Overbought/Oversold
    state.inOverbought := state.stochK > stochRsiOverbought
    state.inOversold := state.stochK < stochRsiOversold

    // Volume
    float avgVolume = ta.sma(volume, volumeMA)
    state.volumeRatio := useVolume and avgVolume > 0 ? volume / avgVolume : 1.0

    state

// Calculate line slope (angle)
f_line_slope(float y1, float y2, int x1, int x2) =>
    float slope = x2 - x1 != 0 ? (y2 - y1) / (x2 - x1) : 0
    slope

// Check if lines are converging
f_lines_converging(float upperSlope, float lowerSlope, bool isAscending) =>
    bool converging = false
    if isAscending
        // For ascending pattern: upper slope < lower slope
        converging := upperSlope < lowerSlope
    else
        // For descending pattern: upper slope > lower slope (both negative)
        converging := upperSlope > lowerSlope
    converging

// Check if lines are parallel (Channel detection)
f_lines_parallel(float upperSlope, float lowerSlope, float tolerance) =>
    float diff = math.abs(upperSlope - lowerSlope)
    float avg = (math.abs(upperSlope) + math.abs(lowerSlope)) / 2
    bool isParallel = avg > 0 ? (diff / avg) < tolerance : diff < tolerance / 100
    isParallel

// Check if price touches line (within tolerance)
f_price_touches_line(float price, float lineY, float tolerance) =>
    float toleranceRange = lineY * (tolerance / 100)
    bool touches = math.abs(price - lineY) <= toleranceRange
    touches

// Count touches on line
f_count_line_touches(int startBar, int endBar, float y1, float y2, bool isUpper, float tolerance) =>
    int touches = 0
    int barRange = endBar - startBar

    if barRange > 0
        for i = 0 to math.min(barRange, 100)
            int barsBack = endBar - startBar - i
            // Ensure barsBack is within valid range [0, 10000]
            if barsBack >= 0 and barsBack <= 10000
                float priceAtBar = isUpper ? high[barsBack] : low[barsBack]
                float progress = i / barRange
                float lineYAtBar = y1 + (y2 - y1) * progress

                if f_price_touches_line(priceAtBar, lineYAtBar, touchTolerance)
                    touches += 1

    touches

// Check for breakout
f_check_breakout(float upperY, float lowerY, TechnicalState tech) =>
    bool bullishBreakout = close > upperY
    bool bearishBreakout = close < lowerY

    // Volume confirmation
    bool volumeConfirmed = not requireVolumeConfirm or tech.volumeRatio >= volumeMultiplier

    // Close confirmation
    bool closeConfirmed = not requireCloseOutside or (bullishBreakout or bearishBreakout)

    bool hasBreakout = (bullishBreakout or bearishBreakout) and volumeConfirmed and closeConfirmed
    bool isBullish = bullishBreakout

    [hasBreakout, isBullish]

// Detect Regular Divergence (Reversal signal)
f_detect_regular_divergence(array<float> pivots, array<float> indicator, bool isHigh) =>
    bool hasDivergence = false

    if array.size(pivots) >= 2 and array.size(indicator) >= 2
        float pivot1 = array.get(pivots, array.size(pivots) - 2)
        float pivot2 = array.get(pivots, array.size(pivots) - 1)
        float ind1 = array.get(indicator, array.size(indicator) - 2)
        float ind2 = array.get(indicator, array.size(indicator) - 1)

        if isHigh
            // Bearish: Price makes Higher High, Indicator makes Lower High
            hasDivergence := pivot2 > pivot1 and ind2 < ind1
        else
            // Bullish: Price makes Lower Low, Indicator makes Higher Low
            hasDivergence := pivot2 < pivot1 and ind2 > ind1

    hasDivergence

// Detect Hidden Divergence (Continuation signal)
f_detect_hidden_divergence(array<float> pivots, array<float> indicator, bool isHigh) =>
    bool hasDivergence = false

    if array.size(pivots) >= 2 and array.size(indicator) >= 2
        float pivot1 = array.get(pivots, array.size(pivots) - 2)
        float pivot2 = array.get(pivots, array.size(pivots) - 1)
        float ind1 = array.get(indicator, array.size(indicator) - 2)
        float ind2 = array.get(indicator, array.size(indicator) - 1)

        if isHigh
            // Bullish Hidden: Price makes Lower High, Indicator makes Higher High
            hasDivergence := pivot2 < pivot1 and ind2 > ind1
        else
            // Bearish Hidden: Price makes Higher Low, Indicator makes Lower Low
            hasDivergence := pivot2 > pivot1 and ind2 < ind1

    hasDivergence

// Calculate pattern confidence score
f_calculate_confidence(int touches, TechnicalState tech, bool hasDivergence, string patternType) =>
    float confidence = 0.0

    // Base score from touches (40%)
    confidence += math.min(touches / 5.0, 1.0) * 40

    // Technical confirmation (30%)
    float techScore = 0.0
    if patternType == "RISING_WEDGE" or patternType == "DESCENDING_TRIANGLE"
        // Bearish patterns
        techScore += tech.stochK > stochRsiOverbought ? 10 : 0
        techScore += tech.rsi > rsiOverbought ? 10 : 0
        techScore += tech.stochCrossDown ? 10 : 0
    else if patternType == "FALLING_WEDGE" or patternType == "ASCENDING_TRIANGLE"
        // Bullish patterns
        techScore += tech.stochK < stochRsiOversold ? 10 : 0
        techScore += tech.rsi < rsiOversold ? 10 : 0
        techScore += tech.stochCrossUp ? 10 : 0
    else
        // Neutral patterns - less weight
        techScore += 15

    confidence += techScore

    // Divergence bonus (20%)
    confidence += hasDivergence ? 20 : 0

    // Volume confirmation (10%)
    confidence += tech.volumeRatio >= volumeMultiplier ? 10 : 0

    math.min(confidence, 100.0)

// Calculate price target
f_calculate_target(float patternHeight, float breakoutPrice, bool isBullish) =>
    float target = isBullish ? breakoutPrice + patternHeight : breakoutPrice - patternHeight
    target


// ============================================================================
// MODULE: pivot_engine
// ============================================================================

// ============================================================================
// PIVOT DETECTION ENGINE
// ============================================================================

// Pivot detection parameters
int pivotLeftBars = 5
int pivotRightBars = 5
int maxPivotsStored = 10

// Detect and store pivots
isNewBar := barstate.isconfirmed

if isNewBar
    // Detect pivot highs
    float pivotHigh = ta.pivothigh(high, pivotLeftBars, pivotRightBars)
    if not na(pivotHigh)
        if array.size(pivotHighs) >= maxPivotsStored
            array.shift(pivotHighs)
            array.shift(pivotHighBars)
        array.push(pivotHighs, pivotHigh)
        array.push(pivotHighBars, bar_index - pivotRightBars)
    
    // Detect pivot lows
    float pivotLow = ta.pivotlow(low, pivotLeftBars, pivotRightBars)
    if not na(pivotLow)
        if array.size(pivotLows) >= maxPivotsStored
            array.shift(pivotLows)
            array.shift(pivotLowBars)
        array.push(pivotLows, pivotLow)
        array.push(pivotLowBars, bar_index - pivotRightBars)
    
    // Update technical indicator history
    TechnicalState currentTech = f_get_technical_state()
    
    if array.size(rsiHistory) >= 50
        array.shift(rsiHistory)
    array.push(rsiHistory, currentTech.rsi)
    
    if array.size(stochKHistory) >= 50
        array.shift(stochKHistory)
    array.push(stochKHistory, currentTech.stochK)
    
    if array.size(stochDHistory) >= 50
        array.shift(stochDHistory)
    array.push(stochDHistory, currentTech.stochD)
    
    if array.size(volumeHistory) >= 50
        array.shift(volumeHistory)
    array.push(volumeHistory, volume)


// ============================================================================
// MODULE: detection_triangles
// ============================================================================

// ============================================================================
// TRIANGLE DETECTION
// ============================================================================

// Detect Ascending Triangle
// Characteristics: Horizontal resistance + ascending support
f_detect_ascending_triangle(TechnicalState tech) =>
    bool found = false
    TrianglePattern pattern = na

    if enableTriangles and array.size(pivotHighs) >= 2 and array.size(pivotLows) >= 2
        // Check for horizontal resistance (highs at same level)
        float avgHigh = array.avg(pivotHighs)
        bool horizontalResistance = true

        for i = 0 to math.min(array.size(pivotHighs) - 1, 4)
            float deviation = math.abs(array.get(pivotHighs, i) - avgHigh) / avgHigh
            if deviation > 0.02  // 2% tolerance
                horizontalResistance := false
                break

        if horizontalResistance
            // Check for ascending support
            int startIdx = array.size(pivotLows) - 1
            int endIdx = 0
            float startLow = array.get(pivotLows, startIdx)
            float endLow = array.get(pivotLows, endIdx)

            if endLow > startLow  // Support is rising
                int startBar = array.get(pivotLowBars, startIdx)
                int endBar = bar_index

                // Count touches
                int upperTouches = array.size(pivotHighs)
                int lowerTouches = f_count_line_touches(startBar, endBar, startLow, endLow, false, touchTolerance)

                // Calculate slopes
                float upperSlope = f_line_slope(avgHigh, avgHigh, startBar, endBar)
                float lowerSlope = f_line_slope(startLow, endLow, startBar, endBar)

                // Verify convergence
                if f_lines_converging(upperSlope, lowerSlope, true) and (endBar - startBar) >= minPatternBars
                    // Check divergence for additional confirmation
                    bool hasRSIDivergence = detectDivergence ? f_detect_hidden_divergence(pivotLows, stochKHistory, false) : false
                    bool hasStochDivergence = detectDivergence ? f_detect_hidden_divergence(pivotLows, stochKHistory, false) : false

                    // Calculate confidence with Stoch RSI
                    float confidence = f_calculate_confidence( upperTouches + lowerTouches, tech, hasRSIDivergence or hasStochDivergence, "ASCENDING_TRIANGLE" )

                    // Bullish bias - check if Stoch RSI supports
                    if confidence >= 50 and (not useStochRSI or tech.stochK < 80)
                        pattern := TrianglePattern.new( startBar, endBar, avgHigh, avgHigh, startLow, endLow, upperTouches, lowerTouches, "ASCENDING", confidence, true, na, na, na )
                        found := true

    [found, pattern]

// Detect Descending Triangle
// Characteristics: Descending resistance + horizontal support
f_detect_descending_triangle(TechnicalState tech) =>
    bool found = false
    TrianglePattern pattern = na

    if enableTriangles and array.size(pivotHighs) >= 2 and array.size(pivotLows) >= 2
        // Check for horizontal support (lows at same level)
        float avgLow = array.avg(pivotLows)
        bool horizontalSupport = true

        for i = 0 to math.min(array.size(pivotLows) - 1, 4)
            float deviation = math.abs(array.get(pivotLows, i) - avgLow) / avgLow
            if deviation > 0.02  // 2% tolerance
                horizontalSupport := false
                break

        if horizontalSupport
            // Check for descending resistance
            int startIdx = array.size(pivotHighs) - 1
            int endIdx = 0
            float startHigh = array.get(pivotHighs, startIdx)
            float endHigh = array.get(pivotHighs, endIdx)

            if endHigh < startHigh  // Resistance is falling
                int startBar = array.get(pivotHighBars, startIdx)
                int endBar = bar_index

                // Count touches
                int upperTouches = f_count_line_touches(startBar, endBar, startHigh, endHigh, true, touchTolerance)
                int lowerTouches = array.size(pivotLows)

                // Calculate slopes
                float upperSlope = f_line_slope(startHigh, endHigh, startBar, endBar)
                float lowerSlope = f_line_slope(avgLow, avgLow, startBar, endBar)

                // Verify convergence
                if f_lines_converging(upperSlope, lowerSlope, false) and (endBar - startBar) >= minPatternBars
                    // Check divergence for additional confirmation
                    bool hasRSIDivergence = detectDivergence ? f_detect_regular_divergence(pivotHighs, stochKHistory, true) : false
                    bool hasStochDivergence = detectDivergence ? f_detect_regular_divergence(pivotHighs, stochKHistory, true) : false

                    // Calculate confidence with Stoch RSI
                    float confidence = f_calculate_confidence( upperTouches + lowerTouches, tech, hasRSIDivergence or hasStochDivergence, "DESCENDING_TRIANGLE" )

                    // Bearish bias - check if Stoch RSI supports
                    if confidence >= 50 and (not useStochRSI or tech.stochK > 20)
                        pattern := TrianglePattern.new( startBar, endBar, startHigh, endHigh, avgLow, avgLow, upperTouches, lowerTouches, "DESCENDING", confidence, true, na, na, na )
                        found := true

    [found, pattern]

// Detect Symmetrical Triangle
// Characteristics: Both lines converging toward apex
f_detect_symmetrical_triangle(TechnicalState tech) =>
    bool found = false
    TrianglePattern pattern = na

    if enableTriangles and array.size(pivotHighs) >= 2 and array.size(pivotLows) >= 2
        // Get first and last pivots
        int startIdxHigh = array.size(pivotHighs) - 1
        int endIdxHigh = 0
        int startIdxLow = array.size(pivotLows) - 1
        int endIdxLow = 0

        float startHigh = array.get(pivotHighs, startIdxHigh)
        float endHigh = array.get(pivotHighs, endIdxHigh)
        float startLow = array.get(pivotLows, startIdxLow)
        float endLow = array.get(pivotLows, endIdxLow)

        // Both resistance falling and support rising
        if endHigh < startHigh and endLow > startLow
            int startBar = math.min(array.get(pivotHighBars, startIdxHigh), array.get(pivotLowBars, startIdxLow))
            int endBar = bar_index

            // Count touches
            int upperTouches = f_count_line_touches(startBar, endBar, startHigh, endHigh, true, touchTolerance)
            int lowerTouches = f_count_line_touches(startBar, endBar, startLow, endLow, false, touchTolerance)

            // Calculate slopes
            float upperSlope = f_line_slope(startHigh, endHigh, startBar, endBar)
            float lowerSlope = f_line_slope(startLow, endLow, startBar, endBar)

            // Slopes should be roughly symmetrical (opposite but similar magnitude)
            float slopeRatio = math.abs(upperSlope) > 0 ? math.abs(lowerSlope / upperSlope) : 0
            bool isSymmetrical = slopeRatio > 0.7 and slopeRatio < 1.3

            if isSymmetrical and (endBar - startBar) >= minPatternBars
                // Neutral pattern - direction determined by breakout
                // Stoch RSI helps predict direction
                bool hasRSIDivergence = false
                bool hasStochDivergence = false

                // Calculate confidence (neutral pattern, no inherent divergence)
                float confidence = f_calculate_confidence( upperTouches + lowerTouches, tech, false, "SYMMETRICAL_TRIANGLE" )

                if confidence >= 45  // Lower threshold for neutral patterns
                    pattern := TrianglePattern.new( startBar, endBar, startHigh, endHigh, startLow, endLow, upperTouches, lowerTouches, "SYMMETRICAL", confidence, true, na, na, na )
                    found := true

    [found, pattern]

// Main Triangle Detection Function
f_scan_triangles(TechnicalState tech) =>
    array<TrianglePattern> newPatterns = array.new<TrianglePattern>()

    // Scan for ascending triangles
    [foundAsc, patternAsc] = f_detect_ascending_triangle(tech)
    if foundAsc
        array.push(newPatterns, patternAsc)

    // Scan for descending triangles
    [foundDesc, patternDesc] = f_detect_descending_triangle(tech)
    if foundDesc
        array.push(newPatterns, patternDesc)

    // Scan for symmetrical triangles
    [foundSym, patternSym] = f_detect_symmetrical_triangle(tech)
    if foundSym
        array.push(newPatterns, patternSym)

    newPatterns


// ============================================================================
// MODULE: detection_wedges
// ============================================================================

// ============================================================================
// WEDGE DETECTION (Reversal Patterns)
// ============================================================================

// Rising Wedge - Bearish Reversal
// Both lines ascending but converging, often shows bearish divergence
f_detect_rising_wedge(TechnicalState tech) =>
    bool found = false
    WedgePattern pattern = na
    
    if enableWedges and array.size(pivotHighs) >= 2 and array.size(pivotLows) >= 2
        // Both highs and lows ascending
        int startIdxHigh = array.size(pivotHighs) - 1
        int startIdxLow = array.size(pivotLows) - 1
        float startHigh = array.get(pivotHighs, startIdxHigh)
        float endHigh = array.get(pivotHighs, 0)
        float startLow = array.get(pivotLows, startIdxLow)
        float endLow = array.get(pivotLows, 0)
        
        // Both lines must be ascending
        if endHigh > startHigh and endLow > startLow
            int startBar = math.min(array.get(pivotHighBars, startIdxHigh), array.get(pivotLowBars, startIdxLow))
            int endBar = bar_index
            
            float upperSlope = f_line_slope(startHigh, endHigh, startBar, endBar)
            float lowerSlope = f_line_slope(startLow, endLow, startBar, endBar)
            
            // Lower line should have steeper slope (converging)
            if lowerSlope > upperSlope and (endBar - startBar) >= minPatternBars
                int upperTouches = f_count_line_touches(startBar, endBar, startHigh, endHigh, true, touchTolerance)
                int lowerTouches = f_count_line_touches(startBar, endBar, startLow, endLow, false, touchTolerance)
                
                // Rising Wedge is BEARISH - look for bearish divergence
                bool hasRSIDivergence = detectDivergence ? f_detect_regular_divergence(pivotHighs, rsiHistory, true) : false
                bool hasStochDivergence = detectDivergence ? f_detect_regular_divergence(pivotHighs, stochKHistory, true) : false
                
                float confidence = f_calculate_confidence( upperTouches + lowerTouches, tech, hasRSIDivergence or hasStochDivergence, "RISING_WEDGE" )
                
                // Strong filter: Rising wedge needs overbought + divergence for high confidence
                if confidence >= 55 and (not useStochRSI or tech.inOverbought or hasStochDivergence)
                    pattern := WedgePattern.new( startBar, endBar, startHigh, endHigh, startLow, endLow, upperTouches, lowerTouches, "RISING", hasRSIDivergence, hasStochDivergence, confidence, true, na, na, na )
                    found := true
    
    [found, pattern]

// Falling Wedge - Bullish Reversal
// Both lines descending but converging, often shows bullish divergence
f_detect_falling_wedge(TechnicalState tech) =>
    bool found = false
    WedgePattern pattern = na
    
    if enableWedges and array.size(pivotHighs) >= 2 and array.size(pivotLows) >= 2
        // Both highs and lows descending
        int startIdxHigh = array.size(pivotHighs) - 1
        int startIdxLow = array.size(pivotLows) - 1
        float startHigh = array.get(pivotHighs, startIdxHigh)
        float endHigh = array.get(pivotHighs, 0)
        float startLow = array.get(pivotLows, startIdxLow)
        float endLow = array.get(pivotLows, 0)
        
        // Both lines must be descending
        if endHigh < startHigh and endLow < startLow
            int startBar = math.min(array.get(pivotHighBars, startIdxHigh), array.get(pivotLowBars, startIdxLow))
            int endBar = bar_index
            
            float upperSlope = f_line_slope(startHigh, endHigh, startBar, endBar)
            float lowerSlope = f_line_slope(startLow, endLow, startBar, endBar)
            
            // Upper line should have steeper (negative) slope (converging)
            if upperSlope < lowerSlope and (endBar - startBar) >= minPatternBars
                int upperTouches = f_count_line_touches(startBar, endBar, startHigh, endHigh, true, touchTolerance)
                int lowerTouches = f_count_line_touches(startBar, endBar, startLow, endLow, false, touchTolerance)
                
                // Falling Wedge is BULLISH - look for bullish divergence
                bool hasRSIDivergence = detectDivergence ? f_detect_regular_divergence(pivotLows, rsiHistory, false) : false
                bool hasStochDivergence = detectDivergence ? f_detect_regular_divergence(pivotLows, stochKHistory, false) : false
                
                float confidence = f_calculate_confidence( upperTouches + lowerTouches, tech, hasRSIDivergence or hasStochDivergence, "FALLING_WEDGE" )
                
                // Strong filter: Falling wedge needs oversold + divergence for high confidence
                if confidence >= 55 and (not useStochRSI or tech.inOversold or hasStochDivergence)
                    pattern := WedgePattern.new( startBar, endBar, startHigh, endHigh, startLow, endLow, upperTouches, lowerTouches, "FALLING", hasRSIDivergence, hasStochDivergence, confidence, true, na, na, na )
                    found := true
    
    [found, pattern]

// Main Wedge Detection
f_scan_wedges(TechnicalState tech) =>
    array<WedgePattern> newPatterns = array.new<WedgePattern>()
    
    [foundRising, patternRising] = f_detect_rising_wedge(tech)
    if foundRising
        array.push(newPatterns, patternRising)
    
    [foundFalling, patternFalling] = f_detect_falling_wedge(tech)
    if foundFalling
        array.push(newPatterns, patternFalling)
    
    newPatterns


// ============================================================================
// MODULE: detection_channels
// ============================================================================

// ============================================================================
// CHANNEL DETECTION (Trend Patterns)
// ============================================================================

// Detect Ascending Channel - Bullish Trend
f_detect_ascending_channel(TechnicalState tech) =>
    bool found = false
    ChannelPattern pattern = na

    if enableChannels and array.size(pivotHighs) >= 2 and array.size(pivotLows) >= 2
        int startIdxHigh = array.size(pivotHighs) - 1
        int startIdxLow = array.size(pivotLows) - 1
        float startHigh = array.get(pivotHighs, startIdxHigh)
        float endHigh = array.get(pivotHighs, 0)
        float startLow = array.get(pivotLows, startIdxLow)
        float endLow = array.get(pivotLows, 0)

        // Both ascending
        if endHigh > startHigh and endLow > startLow
            int startBar = math.min(array.get(pivotHighBars, startIdxHigh), array.get(pivotLowBars, startIdxLow))
            int endBar = bar_index

            float upperSlope = f_line_slope(startHigh, endHigh, startBar, endBar)
            float lowerSlope = f_line_slope(startLow, endLow, startBar, endBar)

            // Lines should be parallel
            bool isParallel = f_lines_parallel(upperSlope, lowerSlope, 0.15)

            if isParallel and (endBar - startBar) >= minPatternBars
                int upperTouches = f_count_line_touches(startBar, endBar, startHigh, endHigh, true, touchTolerance)
                int lowerTouches = f_count_line_touches(startBar, endBar, startLow, endLow, false, touchTolerance)

                // Hidden bullish divergence confirms trend continuation
                bool hasHiddenDiv = detectDivergence ? f_detect_hidden_divergence(pivotLows, stochKHistory, false) : false

                float confidence = f_calculate_confidence( upperTouches + lowerTouches, tech, hasHiddenDiv, "ASCENDING_CHANNEL" )

                if confidence >= 50
                    // Calculate midline
                    float midY1 = (startHigh + startLow) / 2
                    float midY2 = (endHigh + endLow) / 2

                    pattern := ChannelPattern.new( startBar, endBar, startHigh, endHigh, startLow, endLow, upperTouches, lowerTouches, "ASCENDING", isParallel, confidence, true, na, na, na, na )
                    found := true

    [found, pattern]

// Detect Descending Channel - Bearish Trend
f_detect_descending_channel(TechnicalState tech) =>
    bool found = false
    ChannelPattern pattern = na

    if enableChannels and array.size(pivotHighs) >= 2 and array.size(pivotLows) >= 2
        int startIdxHigh = array.size(pivotHighs) - 1
        int startIdxLow = array.size(pivotLows) - 1
        float startHigh = array.get(pivotHighs, startIdxHigh)
        float endHigh = array.get(pivotHighs, 0)
        float startLow = array.get(pivotLows, startIdxLow)
        float endLow = array.get(pivotLows, 0)

        // Both descending
        if endHigh < startHigh and endLow < startLow
            int startBar = math.min(array.get(pivotHighBars, startIdxHigh), array.get(pivotLowBars, startIdxLow))
            int endBar = bar_index

            float upperSlope = f_line_slope(startHigh, endHigh, startBar, endBar)
            float lowerSlope = f_line_slope(startLow, endLow, startBar, endBar)

            // Lines should be parallel
            bool isParallel = f_lines_parallel(upperSlope, lowerSlope, 0.15)

            if isParallel and (endBar - startBar) >= minPatternBars
                int upperTouches = f_count_line_touches(startBar, endBar, startHigh, endHigh, true, touchTolerance)
                int lowerTouches = f_count_line_touches(startBar, endBar, startLow, endLow, false, touchTolerance)

                // Hidden bearish divergence confirms trend continuation
                bool hasHiddenDiv = detectDivergence ? f_detect_hidden_divergence(pivotHighs, stochKHistory, true) : false

                float confidence = f_calculate_confidence( upperTouches + lowerTouches, tech, hasHiddenDiv, "DESCENDING_CHANNEL" )

                if confidence >= 50
                    float midY1 = (startHigh + startLow) / 2
                    float midY2 = (endHigh + endLow) / 2

                    pattern := ChannelPattern.new( startBar, endBar, startHigh, endHigh, startLow, endLow, upperTouches, lowerTouches, "DESCENDING", isParallel, confidence, true, na, na, na, na )
                    found := true

    [found, pattern]

// Detect Horizontal Channel - Range-bound
f_detect_horizontal_channel(TechnicalState tech) =>
    bool found = false
    ChannelPattern pattern = na

    if enableChannels and array.size(pivotHighs) >= 3 and array.size(pivotLows) >= 3
        // Check if highs are roughly at same level
        float avgHigh = array.avg(pivotHighs)
        float avgLow = array.avg(pivotLows)

        bool horizontalTop = true
        bool horizontalBottom = true

        for i = 0 to math.min(array.size(pivotHighs) - 1, 4)
            if math.abs(array.get(pivotHighs, i) - avgHigh) / avgHigh > 0.02
                horizontalTop := false
                break

        for i = 0 to math.min(array.size(pivotLows) - 1, 4)
            if math.abs(array.get(pivotLows, i) - avgLow) / avgLow > 0.02
                horizontalBottom := false
                break

        if horizontalTop and horizontalBottom
            int startBar = math.min(array.get(pivotHighBars, array.size(pivotHighBars) - 1),
                                     array.get(pivotLowBars, array.size(pivotLows) - 1))
            int endBar = bar_index

            if (endBar - startBar) >= minPatternBars
                int upperTouches = array.size(pivotHighs)
                int lowerTouches = array.size(pivotLows)

                // No inherent divergence in horizontal range
                float confidence = f_calculate_confidence( upperTouches + lowerTouches, tech, false, "HORIZONTAL_CHANNEL" )

                if confidence >= 45
                    float midY = (avgHigh + avgLow) / 2

                    pattern := ChannelPattern.new( startBar, endBar, avgHigh, avgHigh, avgLow, avgLow, upperTouches, lowerTouches, "HORIZONTAL", true, confidence, true, na, na, na, na )
                    found := true

    [found, pattern]

// Main Channel Detection
f_scan_channels(TechnicalState tech) =>
    array<ChannelPattern> newPatterns = array.new<ChannelPattern>()

    [foundAsc, patternAsc] = f_detect_ascending_channel(tech)
    if foundAsc
        array.push(newPatterns, patternAsc)

    [foundDesc, patternDesc] = f_detect_descending_channel(tech)
    if foundDesc
        array.push(newPatterns, patternDesc)

    [foundHoriz, patternHoriz] = f_detect_horizontal_channel(tech)
    if foundHoriz
        array.push(newPatterns, patternHoriz)

    newPatterns


// ============================================================================
// MODULE: visualization
// ============================================================================

// ============================================================================
// VISUALIZATION
// ============================================================================

// Get current technical state
TechnicalState tech = f_get_technical_state()

// Pattern Detection
if isNewBar and array.size(pivotHighs) >= 2 and array.size(pivotLows) >= 2
    // Scan for patterns
    array<TrianglePattern> newTriangles = f_scan_triangles(tech)
    array<WedgePattern> newWedges = f_scan_wedges(tech)
    array<ChannelPattern> newChannels = f_scan_channels(tech)
    
    // Store new patterns (limit to prevent drawing overload)
    if array.size(newTriangles) > 0
        for i = 0 to math.min(array.size(newTriangles) - 1, 2)
            if array.size(trianglePatterns) >= 10
                array.shift(trianglePatterns)
            array.push(trianglePatterns, array.get(newTriangles, i))
    
    if array.size(newWedges) > 0
        for i = 0 to math.min(array.size(newWedges) - 1, 2)
            if array.size(wedgePatterns) >= 10
                array.shift(wedgePatterns)
            array.push(wedgePatterns, array.get(newWedges, i))
    
    if array.size(newChannels) > 0
        for i = 0 to math.min(array.size(newChannels) - 1, 2)
            if array.size(channelPatterns) >= 10
                array.shift(channelPatterns)
            array.push(channelPatterns, array.get(newChannels, i))

// Draw Triangle Patterns
if showTrendLines and array.size(trianglePatterns) > 0
    for i = 0 to array.size(trianglePatterns) - 1
        TrianglePattern tri = array.get(trianglePatterns, i)
        if tri.active
            // Determine color
            color lineColor = tri.triangleType == "ASCENDING" ? colorAscending : tri.triangleType == "DESCENDING" ? colorDescending : colorNeutral
            
            // Draw upper line
            if na(tri.upperLine)
                tri.upperLine := line.new(tri.startBar, tri.upperLine_y1, tri.endBar, tri.upperLine_y2,
                     color=lineColor, width=2, style=line.style_solid)
            else
                line.set_xy1(tri.upperLine, tri.startBar, tri.upperLine_y1)
                line.set_xy2(tri.upperLine, tri.endBar, tri.upperLine_y2)
            
            // Draw lower line
            if na(tri.lowerLine)
                tri.lowerLine := line.new(tri.startBar, tri.lowerLine_y1, tri.endBar, tri.lowerLine_y2,
                     color=lineColor, width=2, style=line.style_solid)
            else
                line.set_xy1(tri.lowerLine, tri.startBar, tri.lowerLine_y1)
                line.set_xy2(tri.lowerLine, tri.endBar, tri.lowerLine_y2)
            
            // Draw label
            if showPatternLabels and na(tri.patternLabel)
                string labelText = tri.triangleType + " △"
                if showConfidence
                    labelText += "\n" + str.tostring(tri.confidence, "#") + "%"
                
                float labelY = (tri.upperLine_y2 + tri.lowerLine_y2) / 2
                tri.patternLabel := label.new(tri.endBar, labelY, labelText,
                     color=lineColor, textcolor=color.white, style=label.style_label_left, size=size.small)

// Draw Wedge Patterns
if showTrendLines and array.size(wedgePatterns) > 0
    for i = 0 to array.size(wedgePatterns) - 1
        WedgePattern wedge = array.get(wedgePatterns, i)
        if wedge.active
            color lineColor = wedge.wedgeType == "RISING" ? colorDescending : colorAscending
            
            if na(wedge.upperLine)
                wedge.upperLine := line.new(wedge.startBar, wedge.upperLine_y1, wedge.endBar, wedge.upperLine_y2,
                     color=lineColor, width=2, style=line.style_dashed)
            else
                line.set_xy1(wedge.upperLine, wedge.startBar, wedge.upperLine_y1)
                line.set_xy2(wedge.upperLine, wedge.endBar, wedge.upperLine_y2)
            
            if na(wedge.lowerLine)
                wedge.lowerLine := line.new(wedge.startBar, wedge.lowerLine_y1, wedge.endBar, wedge.lowerLine_y2,
                     color=lineColor, width=2, style=line.style_dashed)
            else
                line.set_xy1(wedge.lowerLine, wedge.startBar, wedge.lowerLine_y1)
                line.set_xy2(wedge.lowerLine, wedge.endBar, wedge.lowerLine_y2)
            
            if showPatternLabels and na(wedge.patternLabel)
                string labelText = wedge.wedgeType + " ◆"
                if wedge.hasStochDivergence
                    labelText += " [DIV]"
                if showConfidence
                    labelText += "\n" + str.tostring(wedge.confidence, "#") + "%"
                
                float labelY = (wedge.upperLine_y2 + wedge.lowerLine_y2) / 2
                wedge.patternLabel := label.new(wedge.endBar, labelY, labelText,
                     color=lineColor, textcolor=color.white, style=label.style_label_left, size=size.small)

// Draw Channel Patterns
if showTrendLines and array.size(channelPatterns) > 0
    for i = 0 to array.size(channelPatterns) - 1
        ChannelPattern chan = array.get(channelPatterns, i)
        if chan.active
            color lineColor = chan.channelType == "ASCENDING" ? colorAscending : chan.channelType == "DESCENDING" ? colorDescending : colorNeutral
            
            if na(chan.upperLine)
                chan.upperLine := line.new(chan.startBar, chan.upperLine_y1, chan.endBar, chan.upperLine_y2,
                     color=lineColor, width=1, style=line.style_solid)
            else
                line.set_xy1(chan.upperLine, chan.startBar, chan.upperLine_y1)
                line.set_xy2(chan.upperLine, chan.endBar, chan.upperLine_y2)
            
            if na(chan.lowerLine)
                chan.lowerLine := line.new(chan.startBar, chan.lowerLine_y1, chan.endBar, chan.lowerLine_y2,
                     color=lineColor, width=1, style=line.style_solid)
            else
                line.set_xy1(chan.lowerLine, chan.startBar, chan.lowerLine_y1)
                line.set_xy2(chan.lowerLine, chan.endBar, chan.lowerLine_y2)
            
            // Draw midline
            if na(chan.midLine)
                float midY1 = (chan.upperLine_y1 + chan.lowerLine_y1) / 2
                float midY2 = (chan.upperLine_y2 + chan.lowerLine_y2) / 2
                chan.midLine := line.new(chan.startBar, midY1, chan.endBar, midY2,
                     color=color.new(lineColor, 70), width=1, style=line.style_dotted)
            
            if showPatternLabels and na(chan.patternLabel)
                string labelText = chan.channelType + " ▭"
                if showConfidence
                    labelText += "\n" + str.tostring(chan.confidence, "#") + "%"
                
                float labelY = (chan.upperLine_y2 + chan.lowerLine_y2) / 2
                chan.patternLabel := label.new(chan.endBar, labelY, labelText,
                     color=lineColor, textcolor=color.white, style=label.style_label_left, size=size.small)


// ============================================================================
// MODULE: info_table
// ============================================================================

// ============================================================================
// INFO TABLE
// ============================================================================

// Create info table
var table infoTable = table.new(position.top_right, 3, 10, bgcolor=color.new(color.black, 85), border_width=1)

if barstate.islast
    // Header
    table.cell(infoTable, 0, 0, "Pattern-Pilot", text_color=color.white, text_size=size.normal, bgcolor=color.new(color.blue, 70))
    table.merge_cells(infoTable, 0, 0, 2, 0)

    // Pattern counts
    int triangleCount = array.size(trianglePatterns)
    int wedgeCount = array.size(wedgePatterns)
    int channelCount = array.size(channelPatterns)

    table.cell(infoTable, 0, 1, "Triangles:", text_color=color.gray, text_size=size.small)
    table.cell(infoTable, 1, 1, str.tostring(triangleCount), text_color=color.white, text_size=size.small)

    table.cell(infoTable, 0, 2, "Wedges:", text_color=color.gray, text_size=size.small)
    table.cell(infoTable, 1, 2, str.tostring(wedgeCount), text_color=color.white, text_size=size.small)

    table.cell(infoTable, 0, 3, "Channels:", text_color=color.gray, text_size=size.small)
    table.cell(infoTable, 1, 3, str.tostring(channelCount), text_color=color.white, text_size=size.small)

    // Separator
    table.cell(infoTable, 0, 4, "─────────────", text_color=color.gray, text_size=size.tiny)
    table.merge_cells(infoTable, 0, 4, 2, 4)

    // Technical Indicators
    table.cell(infoTable, 0, 5, "RSI:", text_color=color.gray, text_size=size.small)
    color rsiColor = tech.rsi > rsiOverbought ? color.red : tech.rsi < rsiOversold ? color.green : color.gray
    table.cell(infoTable, 1, 5, str.tostring(tech.rsi, "#.#"), text_color=rsiColor, text_size=size.small)

    table.cell(infoTable, 0, 6, "Stoch RSI:", text_color=color.gray, text_size=size.small)
    color stochColor = tech.stochK > stochRsiOverbought ? color.red : tech.stochK < stochRsiOversold ? color.green : color.gray
    table.cell(infoTable, 1, 6, str.tostring(tech.stochK, "#.#"), text_color=stochColor, text_size=size.small)

    // Stoch RSI Cross
    string crossStatus = tech.stochCrossUp ? "⬆ Bullish" : tech.stochCrossDown ? "⬇ Bearish" : "─"
    color crossColor = tech.stochCrossUp ? color.green : tech.stochCrossDown ? color.red : color.gray
    table.cell(infoTable, 0, 7, "K/D Cross:", text_color=color.gray, text_size=size.small)
    table.cell(infoTable, 1, 7, crossStatus, text_color=crossColor, text_size=size.small)

    // Volume Ratio
    table.cell(infoTable, 0, 8, "Volume:", text_color=color.gray, text_size=size.small)
    color volColor = tech.volumeRatio >= volumeMultiplier ? color.yellow : color.gray
    table.cell(infoTable, 1, 8, str.tostring(tech.volumeRatio, "#.#x"), text_color=volColor, text_size=size.small)

    // Active patterns with highest confidence
    if triangleCount > 0 or wedgeCount > 0 or channelCount > 0
        float highestConf = 0
        string bestPattern = ""

        for i = 0 to triangleCount - 1
            TrianglePattern tri = array.get(trianglePatterns, i)
            if tri.confidence > highestConf
                highestConf := tri.confidence
                bestPattern := tri.triangleType + " △"

        for i = 0 to wedgeCount - 1
            WedgePattern wedge = array.get(wedgePatterns, i)
            if wedge.confidence > highestConf
                highestConf := wedge.confidence
                bestPattern := wedge.wedgeType + " ◆"

        for i = 0 to channelCount - 1
            ChannelPattern chan = array.get(channelPatterns, i)
            if chan.confidence > highestConf
                highestConf := chan.confidence
                bestPattern := chan.channelType + " ▭"

        table.cell(infoTable, 0, 9, "Best:", text_color=color.gray, text_size=size.small)
        string bestText = bestPattern + " " + str.tostring(highestConf, "#") + "%"
        table.cell(infoTable, 1, 9, bestText, text_color=color.yellow, text_size=size.small)


if enableTriangles and array.size(trianglePatterns) > 0
    for i = 0 to array.size(trianglePatterns) - 1
        TrianglePattern pattern = array.get(trianglePatterns, i)
        if pattern.active
            bool breakoutUp = close > pattern.upperLine_y2
            bool breakoutDown = close < pattern.lowerLine_y2

            if breakoutUp
                alert("Triangle Breakout UP: " + pattern.triangleType, alert.freq_once_per_bar_close)

            if breakoutDown
                alert("Triangle Breakout DOWN: " + pattern.triangleType, alert.freq_once_per_bar_close)

// Wedge Breakouts
if enableWedges and array.size(wedgePatterns) > 0
    for i = 0 to array.size(wedgePatterns) - 1
        WedgePattern pattern = array.get(wedgePatterns, i)
        if pattern.active
            bool breakoutUp = close > pattern.upperLine_y2
            bool breakoutDown = close < pattern.lowerLine_y2

            if breakoutUp
                alert("Wedge Breakout UP: " + pattern.wedgeType +
                      (pattern.hasRSIDivergence ? " [RSI Div]" : ""),
                      alert.freq_once_per_bar_close)

            if breakoutDown
                alert("Wedge Breakout DOWN: " + pattern.wedgeType +
                      (pattern.hasRSIDivergence ? " [RSI Div]" : ""),
                      alert.freq_once_per_bar_close)

// Channel Breakouts
if enableChannels and array.size(channelPatterns) > 0
    for i = 0 to array.size(channelPatterns) - 1
        ChannelPattern pattern = array.get(channelPatterns, i)
        if pattern.active
            bool breakoutUp = close > pattern.upperLine_y2
            bool breakoutDown = close < pattern.lowerLine_y2

            if breakoutUp
                alert("Channel Breakout UP: " + pattern.channelType, alert.freq_once_per_bar_close)

            if breakoutDown
                alert("Channel Breakout DOWN: " + pattern.channelType, alert.freq_once_per_bar_close)

// ============================================================================
// EXPORTS (for use in other indicators via request.security())
// ============================================================================

int triangleCount = array.size(trianglePatterns)
int wedgeCount = array.size(wedgePatterns)
int channelCount = array.size(channelPatterns)
int totalPatterns = triangleCount + wedgeCount + channelCount

plot(totalPatterns, "Total Patterns", display=display.none)
plot(tech.rsi, "RSI", display=display.none)
plot(tech.stochK, "Stoch K", display=display.none)
plot(tech.volumeRatio, "Volume Ratio", display=display.none)

Overview

Pattern Recognition [WavesUnchained] validates formations through multi-point pivot analysis, volume spike verification, RSI and Stochastic RSI divergence filters, and price closes outside pattern bounds. The indicator overlays trend lines, pattern labels, Fibonacci-based target projections, and confidence ratings directly on the price chart alongside an information panel. Note that pivot-based pattern detection evaluates historical swing points, which update as new bar data becomes available.

Details

Published by

casoon on GitHub

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Breakout, Support & resistance, Oscillators & momentum, Screeners & dashboards

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

  • Signals can change on the live bar or after it closes (repainting)

Topics

pine-scriptpinescripttechnical-analysistrading-indicatorstrading-strategiestradingview

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Enable TrianglesbooltrueenableTriangles
Enable WedgesbooltrueenableWedges
Enable ChannelsbooltrueenableChannels
Minimum Touch Pointsint2minTouchPoints
Minimum Pattern Barsint15minPatternBars
Touch Tolerance %float0.5touchTolerance
Require Volume ConfirmationbooltruerequireVolumeConfirm
Volume Spike Multiplierfloat1.5volumeMultiplier
Require Close Outside PatternbooltruerequireCloseOutside
Use RSI FilterbooltrueuseRSI
RSI Lengthint14rsiLength
RSI Overboughtint70rsiOverbought
Show all 31 settings
SettingTypeDefaultVariable
RSI Oversoldint30rsiOversold
Use Stochastic RSIbooltrueuseStochRSI
Stoch RSI Lengthint14stochRsiLength
Stoch RSI %K Smoothingint3stochRsiSmoothK
Stoch RSI %D Smoothingint3stochRsiSmoothD
Stoch RSI Overboughtint80stochRsiOverbought
Stoch RSI Oversoldint20stochRsiOversold
Detect DivergencebooltruedetectDivergence
Divergence Lookbackint5divergenceLookback
Use Volume FilterbooltrueuseVolume
Volume MA Lengthint20volumeMA
Show Pattern LabelsbooltrueshowPatternLabels
Show Trend LinesbooltrueshowTrendLines
Show Price TargetsbooltrueshowTargets
Show Confidence %booltrueshowConfidence
Ascending/Bullishcolorcolor.new(color.green, 0)colorAscending
Descending/Bearishcolorcolor.new(color.red, 0)colorDescending
Neutral/Symmetriccolorcolor.new(color.gray, 0)colorNeutral
Breakout Signalcolorcolor.new(color.yellow, 0)colorBreakout
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

ORB Pro - Research-Based ORB

by Mrshahidali420
16 stars

The ORB Pro indicator calculates and plots Opening Range Breakout levels, stop-loss and take-profit zones, signal labels, and an status dashboard on the price chart. Built on academic research, it evaluates trades using an opening-candle direction filter, open

Indicator v6 BreakoutSessions & time Repaints
Updated 3 months ago · None stated GitHub repository of ORB Pro - Research-Based ORB

This indicator implements a full Chanlun pipeline on the price chart. It merges contained bars, marks top and bottom fractals, and connects alternating fractals into strokes. Overlapping strokes form pivot boxes, and MACD area ratios flag trend or consolidatio

Indicator v6 Support & resistanceDivergence
Updated 2 months ago · GPL-3.0 GitHub repository of 缠论 - 分型与笔 [Chanlun Core]

Target Radar

by eddiebelaval
7 stars

Target Radar is a multi-factor forecasting indicator that plots projection bands, forward boxes, and dynamic target zones directly on the main price chart. It calculates a forecast score by combining z-scores for trend, momentum, volume pressure, and volatilit

Indicator v5 TrendOscillators & momentum
Updated 3 months ago · None stated GitHub repository of Target Radar

ORB Algo | Flux Charts

by vaskosmihaylov
5 stars

This indicator calculates and visualizes Opening Range Breakout (ORB) zones directly on the price chart. Breakout signals are identified when the close or an EMA crosses the opening range boundary, with entry sensitivity managed by retest requirements. It mode

Indicator v6 BreakoutSessions & time
Updated 2 months ago · GPL-3.0 GitHub repository of ORB Algo | Flux Charts

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers