This indicator detects reversal candlestick patterns, including hammer, hanging man, doji family, engulfing, morning and evening star, harami, piercing line, dark cloud, shooting star and inverted hammer, and marks them with signal labels on the price chart.
1Copy the scriptPress Copy script above, or the copy button on the code below.
2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.
Source Code
397 lines of Pine Script v5 from test.pine, published under Apache-2.0. Keep the author's credit and licence lines when you reuse it.
test.pine
//@version=5
indicator("Advanced Candlestick Pattern Indicator", overlay=true, max_lines_count=200, max_labels_count=200)
// Variable to track last signal type (prevents repeating signals)
var string lastSignalType = na
// ============================================
// INPUT PARAMETERS
// ============================================
// Pattern Detection Settings
patternStrength = input.int(3, "Pattern Strength", minval=1, maxval=5, group="Pattern Detection")
bodyRatio = input.float(0.65, "Body/Range Ratio", minval=0.1, maxval=0.9, step=0.05, group="Pattern Detection")
shadowRatio = input.float(2.2, "Shadow/Body Ratio", minval=1.0, maxval=5.0, step=0.1, group="Pattern Detection")
patternLookback = input.int(3, "Pattern Lookback", minval=1, maxval=10, group="Pattern Detection")
minPatternSize = input.float(0.5, "Min Pattern Size (ATR)", minval=0.3, maxval=2.0, step=0.1, group="Pattern Detection")
// Enable/Disable Patterns
enableHammer = input.bool(true, "Hammer & Hanging Man", group="Reversal Patterns")
enableDoji = input.bool(true, "Doji Family", group="Reversal Patterns")
enableEngulfing = input.bool(true, "Engulfing Patterns", group="Reversal Patterns")
enableStar = input.bool(true, "Morning/Evening Star", group="Reversal Patterns")
enableHarami = input.bool(true, "Harami Patterns", group="Reversal Patterns")
enablePiercing = input.bool(true, "Piercing Line/Dark Cloud", group="Reversal Patterns")
enableShootingStar = input.bool(true, "Shooting Star", group="Reversal Patterns")
enableInvertedHammer = input.bool(true, "Inverted Hammer", group="Reversal Patterns")
// Confirmation Settings
useVolumeConfirmation = input.bool(true, "Volume Confirmation", group="Confirmations")
volumeMultiplier = input.float(1.15, "Volume Multiple", minval=1.0, maxval=3.0, step=0.1, group="Confirmations")
useTrendConfirmation = input.bool(true, "Trend Confirmation", group="Confirmations")
trendPeriod = input.int(20, "Trend EMA Period", minval=5, maxval=50, group="Confirmations")
useRSIConfirmation = input.bool(true, "RSI Confirmation", group="Confirmations")
rsiPeriod = input.int(14, "RSI Period", minval=5, maxval=30, group="Confirmations")
rsiOverbought = input.int(70, "RSI Overbought", minval=60, maxval=90, group="Confirmations")
rsiOversold = input.int(30, "RSI Oversold", minval=10, maxval=40, group="Confirmations")
useMAConfirmation = input.bool(false, "Moving Average Filter", group="Confirmations")
maFastPeriod = input.int(9, "Fast MA Period", minval=5, maxval=20, group="Confirmations")
maSlowPeriod = input.int(21, "Slow MA Period", minval=10, maxval=50, group="Confirmations")
// Risk Management & Signal Display
stopLossType = input.string("ATR", "Stop Loss Type", options=["Percentage", "ATR", "Pattern Based"], group="Signal Display")
stopLossPercent = input.float(1.5, "Stop Loss %", minval=0.5, maxval=10, step=0.1, group="Signal Display")
atrPeriod = input.int(14, "ATR Period", minval=5, maxval=30, group="Signal Display")
atrMultiplier = input.float(1.6, "ATR Multiplier", minval=1.0, maxval=5.0, step=0.1, group="Signal Display")
takeProfitRatio = input.float(3.2, "Risk/Reward Ratio", minval=1.0, maxval=10.0, step=0.5, group="Signal Display")
showTPSL = input.bool(true, "Show TP/SL Lines", group="Signal Display")
showLabels = input.bool(true, "Show Signal Labels", group="Signal Display")
maxSignalsDisplay = input.int(10, "Max Signals on Chart", minval=1, maxval=50, group="Signal Display")
// ============================================
// TECHNICAL INDICATORS
// ============================================
// ATR for volatility-based stops
atrValue = ta.atr(atrPeriod)
// Trend confirmation
ema = ta.ema(close, trendPeriod)
uptrend = close > ema
downtrend = close < ema
// Moving Average Filter
maFast = ta.ema(close, maFastPeriod)
maSlow = ta.ema(close, maSlowPeriod)
bullishTrendMA = maFast > maSlow and close > maFast
bearishTrendMA = maFast < maSlow and close < maFast
// RSI for momentum confirmation
rsi = ta.rsi(close, rsiPeriod)
rsiOverboughtCondition = rsi > rsiOverbought
rsiOversoldCondition = rsi < rsiOversold
// Volume confirmation
avgVolume = ta.sma(volume, 20)
highVolume = volume > avgVolume * volumeMultiplier
// ============================================
// CANDLESTICK PATTERN FUNCTIONS
// ============================================
// Basic candle properties
bodySize = math.abs(close - open)
totalRange = high - low
upperShadow = high - math.max(close, open)
lowerShadow = math.min(close, open) - low
bodyRatioActual = totalRange > 0 ? bodySize / totalRange : 0
upperShadowRatio = bodySize > 0 ? upperShadow / bodySize : 0
lowerShadowRatio = bodySize > 0 ? lowerShadow / bodySize : 0
// Color determination
bullishCandle = close > open
bearishCandle = close < open
// ============================================
// PATTERN DETECTION FUNCTIONS
// ============================================
// Doji Pattern
isDoji() =>
bodyRatioActual < 0.1 and totalRange > atrValue * 0.5
// Hammer Pattern (bullish reversal at bottom)
isHammer() =>
lowerShadow >= bodySize * shadowRatio and upperShadow <= bodySize * 0.5 and bodyRatioActual >= bodyRatio * 0.5 and bodyRatioActual <= 0.8
// Shooting Star Pattern (bearish reversal at top)
isShootingStar() =>
upperShadow >= bodySize * shadowRatio and lowerShadow <= bodySize * 0.5 and bodyRatioActual >= bodyRatio * 0.5 and bodyRatioActual <= 0.8
// Inverted Hammer (bullish reversal at bottom)
isInvertedHammer() =>
upperShadow >= bodySize * shadowRatio and lowerShadow <= bodySize * 0.5 and bodyRatioActual >= bodyRatio * 0.5
// Bullish Engulfing
isBullishEngulfing() =>
bearishCandle[1] and bullishCandle and open <= close[1] and close > open[1] and bodySize > bodySize[1] * 1.2
// Bearish Engulfing
isBearishEngulfing() =>
bullishCandle[1] and bearishCandle and open >= close[1] and close < open[1] and bodySize > bodySize[1] * 1.2
// Morning Star (3-candle bullish reversal)
isMorningStar() =>
bearishCandle[2] and bodySize[2] > atrValue and bodyRatioActual[1] < 0.3 and bullishCandle and close > (open[2] + close[2]) / 2
// Evening Star (3-candle bearish reversal)
isEveningStar() =>
bullishCandle[2] and bodySize[2] > atrValue and bodyRatioActual[1] < 0.3 and bearishCandle and close < (open[2] + close[2]) / 2
// Bullish Harami
isBullishHarami() =>
bearishCandle[1] and bullishCandle and open > close[1] and close < open[1] and bodySize < bodySize[1] * 0.7
// Bearish Harami
isBearishHarami() =>
bullishCandle[1] and bearishCandle and open < close[1] and close > open[1] and bodySize < bodySize[1] * 0.7
// Piercing Line (bullish reversal)
isPiercingLine() =>
bearishCandle[1] and bullishCandle and open < low[1] and close > (open[1] + close[1]) / 2 and close < open[1]
// Dark Cloud Cover (bearish reversal)
isDarkCloud() =>
bullishCandle[1] and bearishCandle and open > high[1] and close < (open[1] + close[1]) / 2 and close > open[1]
// ============================================
// PATTERN DETECTION LOGIC
// ============================================
// Bullish patterns
bullishDoji = enableDoji and isDoji() and downtrend
bullishHammer = enableHammer and isHammer() and downtrend
bullishInvertedHammer = enableInvertedHammer and isInvertedHammer() and downtrend
bullishEngulfing = enableEngulfing and isBullishEngulfing()
bullishMorningStar = enableStar and isMorningStar()
bullishHarami = enableHarami and isBullishHarami()
bullishPiercing = enablePiercing and isPiercingLine()
// Bearish patterns
bearishDoji = enableDoji and isDoji() and uptrend
bearishShootingStar = enableShootingStar and isShootingStar() and uptrend
bearishHangingMan = enableHammer and isHammer() and uptrend
bearishEngulfing = enableEngulfing and isBearishEngulfing()
bearishEveningStar = enableStar and isEveningStar()
bearishHarami = enableHarami and isBearishHarami()
bearishDarkCloud = enablePiercing and isDarkCloud()
// Combined pattern signals
bullishPatternDetected = bullishDoji or bullishHammer or bullishInvertedHammer or bullishEngulfing or bullishMorningStar or bullishHarami or bullishPiercing
bearishPatternDetected = bearishDoji or bearishShootingStar or bearishHangingMan or bearishEngulfing or bearishEveningStar or bearishHarami or bearishDarkCloud
// ============================================
// CONFIRMATION CONDITIONS
// ============================================
// Volume confirmation
volumeConfirmed = useVolumeConfirmation ? highVolume : true
// Trend confirmation - more relaxed
trendConfirmedBullish = useTrendConfirmation ? (close > ema * 0.99 or not uptrend) : true
trendConfirmedBearish = useTrendConfirmation ? (close < ema * 1.01 or not downtrend) : true
// MA confirmation
maConfirmedBullish = useMAConfirmation ? bullishTrendMA : true
maConfirmedBearish = useMAConfirmation ? bearishTrendMA : true
// RSI confirmation - more relaxed
rsiConfirmedBullish = useRSIConfirmation ? (rsi < 55 and not rsiOverboughtCondition) : true
rsiConfirmedBearish = useRSIConfirmation ? (rsi > 45 and not rsiOversoldCondition) : true
// ============================================
// SIGNAL CONDITIONS
// ============================================
// Buy/Sell signal conditions with confirmation delay (prevent repainting)
buySignalRaw = bullishPatternDetected[1] and volumeConfirmed[1] and trendConfirmedBullish[1] and rsiConfirmedBullish[1] and maConfirmedBullish[1]
sellSignalRaw = bearishPatternDetected[1] and volumeConfirmed[1] and trendConfirmedBearish[1] and rsiConfirmedBearish[1] and maConfirmedBearish[1]
// Final signals with alternating logic (no repeating signals)
buySignal = buySignalRaw and (lastSignalType != "BUY" or na(lastSignalType))
sellSignal = sellSignalRaw and (lastSignalType != "SELL" or na(lastSignalType))
// ============================================
// TP/SL CALCULATION FUNCTIONS
// ============================================
calculateStopLoss(isLong, entryPrice) =>
if stopLossType == "Percentage"
if isLong
entryPrice * (1 - stopLossPercent / 100)
else
entryPrice * (1 + stopLossPercent / 100)
else if stopLossType == "ATR"
if isLong
entryPrice - atrValue * atrMultiplier
else
entryPrice + atrValue * atrMultiplier
else // Pattern Based
if isLong
math.min(low, low[1]) - atrValue * 0.5
else
math.max(high, high[1]) + atrValue * 0.5
calculateTakeProfit(isLong, entryPrice, stopLoss) =>
stopDistance = math.abs(entryPrice - stopLoss)
if isLong
entryPrice + stopDistance * takeProfitRatio
else
entryPrice - stopDistance * takeProfitRatio
// ============================================
// SIGNAL TRACKING AND DISPLAY
// ============================================
// Variables to track signals
var buySignals = array.new<float>()
var sellSignals = array.new<float>()
var buyStops = array.new<float>()
var sellStops = array.new<float>()
var buyTargets = array.new<float>()
var sellTargets = array.new<float>()
var buyBars = array.new<int>()
var sellBars = array.new<int>()
// Add new signals and update last signal type
if buySignal
entryPrice = close
stopLoss = calculateStopLoss(true, entryPrice)
takeProfit = calculateTakeProfit(true, entryPrice, stopLoss)
// Limit the number of signals displayed
if array.size(buySignals) >= maxSignalsDisplay
array.shift(buySignals)
array.shift(buyStops)
array.shift(buyTargets)
array.shift(buyBars)
array.push(buySignals, entryPrice)
array.push(buyStops, stopLoss)
array.push(buyTargets, takeProfit)
array.push(buyBars, bar_index)
// Update last signal type
lastSignalType := "BUY"
if sellSignal
entryPrice = close
stopLoss = calculateStopLoss(false, entryPrice)
takeProfit = calculateTakeProfit(false, entryPrice, stopLoss)
// Limit the number of signals displayed
if array.size(sellSignals) >= maxSignalsDisplay
array.shift(sellSignals)
array.shift(sellStops)
array.shift(sellTargets)
array.shift(sellBars)
array.push(sellSignals, entryPrice)
array.push(sellStops, stopLoss)
array.push(sellTargets, takeProfit)
array.push(sellBars, bar_index)
// Update last signal type
lastSignalType := "SELL"
// ============================================
// VISUALIZATION
// ============================================
// Plot trend EMA with dynamic color and thickness
emaColor = uptrend ? color.new(color.green, 20) : color.new(color.red, 20)
plot(ema, "Trend EMA", color=emaColor, linewidth=4)
// Plot moving averages if enabled
plot(useMAConfirmation ? maFast : na, "Fast MA", color=color.blue, linewidth=1)
plot(useMAConfirmation ? maSlow : na, "Slow MA", color=color.red, linewidth=1)
// Plot buy/sell signals
plotshape(buySignal, title="🔥 BUY SIGNAL", location=location.belowbar, color=color.new(color.green, 0), style=shape.labelup, size=size.large, text="BUY")
plotshape(sellSignal, title="🔥 SELL SIGNAL", location=location.abovebar, color=color.new(color.red, 0), style=shape.labeldown, size=size.large, text="SELL")
// Draw TP/SL lines for recent signals
if showTPSL
// Draw lines for buy signals
buyArraySize = array.size(buySignals)
if buyArraySize > 0
for i = 0 to buyArraySize - 1
if bar_index - array.get(buyBars, i) < 20 // Show lines for recent signals only
entryBar = array.get(buyBars, i)
stopLevel = array.get(buyStops, i)
targetLevel = array.get(buyTargets, i)
// Stop Loss line
line.new(entryBar, stopLevel, bar_index + 10, stopLevel,
color=color.red, style=line.style_dashed, width=2)
// Take Profit line
line.new(entryBar, targetLevel, bar_index + 10, targetLevel,
color=color.green, style=line.style_dashed, width=2)
// Draw lines for sell signals
sellArraySize = array.size(sellSignals)
if sellArraySize > 0
for i = 0 to sellArraySize - 1
if bar_index - array.get(sellBars, i) < 20 // Show lines for recent signals only
entryBar = array.get(sellBars, i)
stopLevel = array.get(sellStops, i)
targetLevel = array.get(sellTargets, i)
// Stop Loss line
line.new(entryBar, stopLevel, bar_index + 10, stopLevel,
color=color.red, style=line.style_dashed, width=2)
// Take Profit line
line.new(entryBar, targetLevel, bar_index + 10, targetLevel,
color=color.green, style=line.style_dashed, width=2)
// Background color for trend
bgcolor(uptrend ? color.new(color.green, 97) : color.new(color.red, 97), title="Trend Background")
// ============================================
// INFORMATION TABLE
// ============================================
if showLabels and barstate.islast
var table infoTable = table.new(position.top_right, 3, 8, bgcolor=color.white, border_width=2, border_color=color.gray)
// Header
table.cell(infoTable, 0, 0, "Signal Info", text_color=color.white, bgcolor=color.blue, text_size=size.normal)
table.cell(infoTable, 1, 0, "Value", text_color=color.white, bgcolor=color.blue, text_size=size.normal)
table.cell(infoTable, 2, 0, "Status", text_color=color.white, bgcolor=color.blue, text_size=size.normal)
// RSI
table.cell(infoTable, 0, 1, "RSI", text_color=color.black, text_size=size.small)
table.cell(infoTable, 1, 1, str.tostring(rsi, "#.##"), text_color=color.black, text_size=size.small)
rsiColor = rsi > rsiOverbought ? color.red : rsi < rsiOversold ? color.green : color.gray
table.cell(infoTable, 2, 1, rsi > rsiOverbought ? "OB" : rsi < rsiOversold ? "OS" : "Neutral", text_color=rsiColor, text_size=size.small)
// Trend
table.cell(infoTable, 0, 2, "Trend", text_color=color.black, text_size=size.small)
table.cell(infoTable, 1, 2, uptrend ? "UP" : "DOWN", text_color=uptrend ? color.green : color.red, text_size=size.small)
table.cell(infoTable, 2, 2, uptrend ? "✓" : "✗", text_color=uptrend ? color.green : color.red, text_size=size.small)
// Volume
table.cell(infoTable, 0, 3, "Volume", text_color=color.black, text_size=size.small)
volRatio = volume / avgVolume
table.cell(infoTable, 1, 3, str.tostring(volRatio, "#.##") + "x", text_color=color.black, text_size=size.small)
table.cell(infoTable, 2, 3, highVolume ? "HIGH" : "LOW", text_color=highVolume ? color.green : color.red, text_size=size.small)
// ATR
table.cell(infoTable, 0, 4, "ATR", text_color=color.black, text_size=size.small)
table.cell(infoTable, 1, 4, str.tostring(atrValue, "#.####"), text_color=color.black, text_size=size.small)
table.cell(infoTable, 2, 4, "-", text_color=color.black, text_size=size.small)
// Active Signals
table.cell(infoTable, 0, 5, "Buy Signals", text_color=color.black, text_size=size.small)
table.cell(infoTable, 1, 5, str.tostring(array.size(buySignals)), text_color=color.green, text_size=size.small)
table.cell(infoTable, 2, 5, array.size(buySignals) > 0 ? "Active" : "-", text_color=array.size(buySignals) > 0 ? color.green : color.gray, text_size=size.small)
table.cell(infoTable, 0, 6, "Sell Signals", text_color=color.black, text_size=size.small)
table.cell(infoTable, 1, 6, str.tostring(array.size(sellSignals)), text_color=color.red, text_size=size.small)
table.cell(infoTable, 2, 6, array.size(sellSignals) > 0 ? "Active" : "-", text_color=array.size(sellSignals) > 0 ? color.red : color.gray, text_size=size.small)
// Risk Reward
table.cell(infoTable, 0, 7, "R:R Ratio", text_color=color.black, text_size=size.small)
table.cell(infoTable, 1, 7, str.tostring(takeProfitRatio, "#.#") + ":1", text_color=color.purple, text_size=size.small)
table.cell(infoTable, 2, 7, "Target", text_color=color.purple, text_size=size.small)
// ============================================
// ALERTS
// ============================================
alertcondition(buySignal, title="🔥 Bullish Signal", message="BULLISH candlestick pattern detected with confirmations - BUY SIGNAL")
alertcondition(sellSignal, title="🔥 Bearish Signal", message="BEARISH candlestick pattern detected with confirmations - SELL SIGNAL")
alertcondition(bullishPatternDetected, title="Bullish Pattern", message="Bullish pattern detected (pending confirmations)")
alertcondition(bearishPatternDetected, title="Bearish Pattern", message="Bearish pattern detected (pending confirmations)")
Overview
Pattern strength, body and shadow ratios and a minimum size in ATR are adjustable. Optional volume, trend EMA, RSI and moving average confirmations filter signals, and stop loss and take profit lines can be drawn using percentage, ATR or pattern-based stops.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Pattern Strength
int
3
patternStrength
Body/Range Ratio
float
0.65
bodyRatio
Shadow/Body Ratio
float
2.2
shadowRatio
Pattern Lookback
int
3
patternLookback
Min Pattern Size (ATR)
float
0.5
minPatternSize
Hammer & Hanging Man
bool
true
enableHammer
Doji Family
bool
true
enableDoji
Engulfing Patterns
bool
true
enableEngulfing
Morning/Evening Star
bool
true
enableStar
Harami Patterns
bool
true
enableHarami
Piercing Line/Dark Cloud
bool
true
enablePiercing
Shooting Star
bool
true
enableShootingStar
Show all 32 settings
Setting
Type
Default
Variable
Inverted Hammer
bool
true
enableInvertedHammer
Volume Confirmation
bool
true
useVolumeConfirmation
Volume Multiple
float
1.15
volumeMultiplier
Trend Confirmation
bool
true
useTrendConfirmation
Trend EMA Period
int
20
trendPeriod
RSI Confirmation
bool
true
useRSIConfirmation
RSI Period
int
14
rsiPeriod
RSI Overbought
int
70
rsiOverbought
RSI Oversold
int
30
rsiOversold
Moving Average Filter
bool
false
useMAConfirmation
Fast MA Period
int
9
maFastPeriod
Slow MA Period
int
21
maSlowPeriod
Stop Loss Type
string
ATR
stopLossType
Stop Loss %
float
1.5
stopLossPercent
ATR Period
int
14
atrPeriod
ATR Multiplier
float
1.6
atrMultiplier
Risk/Reward Ratio
float
3.2
takeProfitRatio
Show TP/SL Lines
bool
true
showTPSL
Show Signal Labels
bool
true
showLabels
Max Signals on Chart
int
10
maxSignalsDisplay
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 Heikin-Ashi Chart Identifier is an indicator that checks if the current chart uses Heikin-Ashi candles. It tracks matched candles where open equals the average of previous open and close, and verifies no gaps between bars. When conditions match, it display
The indicator plots ICT displacement candles on the price chart and can switch to retracement scanning. Displacement requires a move larger than a set share of the 14-bar average high-low range, lasting at least a chosen number of bars, with optional close, pr
All Candlestick Patterns detects and highlights a wide range of bullish, bearish, and neutral candlestick patterns directly on the price chart. It features optional trend filtering using either a 50-period simple moving average or a combination of 50-period an
Mean Reversion Study is an indicator that identifies trade setups combining exponential moving averages, RSI extremes, and engulfing candlestick patterns. It highlights bullish setups when a bullish engulfing candle forms below the EMA while RSI is oversold, a
This indicator overlays up to five higher-timeframe candlestick groups on the price chart. For each enabled timeframe it requests open, high, low, and close and can show up to five candles, offset by a spacing setting. The main inputs set the candle count, the
This indicator detects a large set of classic candlestick patterns and plots colored labels on the price chart. Trend context uses a 50-period simple moving average, or the 50 and 200 averages stacked together, and can be switched off. Users filter bullish, be