Pattern Recognition [WavesUnchained] automatically identifies chart patterns including ascending, descending, and symmetrical triangles, rising and falling wedges, and parallel channels.
//@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.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Enable Triangles
bool
true
enableTriangles
Enable Wedges
bool
true
enableWedges
Enable Channels
bool
true
enableChannels
Minimum Touch Points
int
2
minTouchPoints
Minimum Pattern Bars
int
15
minPatternBars
Touch Tolerance %
float
0.5
touchTolerance
Require Volume Confirmation
bool
true
requireVolumeConfirm
Volume Spike Multiplier
float
1.5
volumeMultiplier
Require Close Outside Pattern
bool
true
requireCloseOutside
Use RSI Filter
bool
true
useRSI
RSI Length
int
14
rsiLength
RSI Overbought
int
70
rsiOverbought
Show all 31 settings
Setting
Type
Default
Variable
RSI Oversold
int
30
rsiOversold
Use Stochastic RSI
bool
true
useStochRSI
Stoch RSI Length
int
14
stochRsiLength
Stoch RSI %K Smoothing
int
3
stochRsiSmoothK
Stoch RSI %D Smoothing
int
3
stochRsiSmoothD
Stoch RSI Overbought
int
80
stochRsiOverbought
Stoch RSI Oversold
int
20
stochRsiOversold
Detect Divergence
bool
true
detectDivergence
Divergence Lookback
int
5
divergenceLookback
Use Volume Filter
bool
true
useVolume
Volume MA Length
int
20
volumeMA
Show Pattern Labels
bool
true
showPatternLabels
Show Trend Lines
bool
true
showTrendLines
Show Price Targets
bool
true
showTargets
Show Confidence %
bool
true
showConfidence
Ascending/Bullish
color
color.new(color.green, 0)
colorAscending
Descending/Bearish
color
color.new(color.red, 0)
colorDescending
Neutral/Symmetric
color
color.new(color.gray, 0)
colorNeutral
Breakout Signal
color
color.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.
The CISD with Fib/Range Extensions and Stats indicator tracks Change in State of Delivery (CISD) level breakouts on the price chart. It detects pullback structures filtered by ATR and volume conditions, then projects future target levels using customizable Fib
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
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
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
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
This indicator tracks intermediate market cycles inspired by Linda Raschke's approach using daily NYSE TICK statistics. It fetches daily high, low, and close values for the TICK index via security requests and computes moving averages of the highs and lows. It