JapaneseCandlestickPatterns
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Marceeelll

//@version=5

// @description Japanese Candlestick Patterns is a library of functions that enables the detection of popular Japanese candlestick patterns such as Doji, Hammer, and Engulfing, among others. The library provides a simple yet powerful way to analyze financial markets and make informed trading decisions. Japanese Candlestick Patterns library can help you identify potential trading opportunities.
library("JapaneseCandlestickPatterns", overlay = true)


///////////////////////
// --- CONSTANTS --- //
///////////////////////

C_BodyHigh = math.max(close, open)
C_BodyLow = math.min(close, open)
C_Body = C_BodyHigh - C_BodyLow
C_BodyAvg = ta.ema(C_Body, 14)
C_CandleAvg = ta.ema(high - low, 14)
C_IsSmallBody = C_Body < C_BodyAvg
C_IsLongBody = C_Body > C_BodyAvg
C_IsWhiteBody = open < close
C_IsBlackBody = open > close
C_Range = high - low
C_IsInsideBar = C_BodyHigh[1] > C_BodyHigh and C_BodyLow[1] < C_BodyLow
C_BodyMiddle = C_Body / 2 + C_BodyLow
C_UpShadow = high - C_BodyHigh
C_DnShadow = C_BodyLow - low
// Returns in percent (1-100) how much the upper shadow takes of the high and lows
C_UpShadowPercentageOfBody = (C_UpShadow / C_Range) * 100
// Returns in percent (1-100) how much the down shadow takes of the high and lows
C_DnShadowPercentageOfBody = (C_DnShadow / C_Range) * 100


////////////////////////////
// --- HELPER METHODS --- //
////////////////////////////

str_array_to_string(array, separator) =>
    result = ""
    lastIndex = array.size(array) - 1
    for i = 0 to lastIndex
        result := result + array.get(array, lastIndex - i) + (i == lastIndex ? "" : "\n")
    result

isInDeviationRange(float price1, price2, float deviation) =>
    price1 - price1 * deviation < price2 and price1 + price1 * deviation > price2


///////////////////////////
// --- DOJI PATTERNS --- //
///////////////////////////

//
// --- DOJI PATTERN ---
//

export isDojiCandle() =>
    // 1. Has Doji Body
    C_DojiBodyPercent = 5.0
    if C_Range > 0 and C_Body <= C_Range * C_DojiBodyPercent / 100
        true


//
// --- GRAVESTONE DOJI PATTERN ---
//

export isGravestoneDojiCandle() =>
    // 1. Is a doji candle
    if isDojiCandle()
        // 2. When the opening and closing prices are at 15% of the low of the day
        if C_DnShadowPercentageOfBody  < 15
            true


//
// --- DRAGONFLY DOJI PATTERN ---
//

export isDragonflyDojiCandle() =>
    // 1. Is a doji candle
    if isDojiCandle()
        // 2. When the opening and closing prices are at 15% of the high of the day
        if C_UpShadowPercentageOfBody  < 15
            true


//
// --- EVENING DOJI STAR PATTERN ---
//

export isEveningDojiStarCandle(bool isUpTrend) =>
    // 1. The market is in a uptrend
    if isUpTrend
        // 2. Long, doji, long body
        if C_IsLongBody[2] and isDojiCandle()[1] and C_IsLongBody
            // 3. First is a white
            if C_IsWhiteBody[2]
                // 4. Followed by a small body which gaps higher
                if C_BodyHigh[2] < C_BodyLow[1]
                    // 5. Second gap
                    if C_BodyHigh < C_BodyLow[1]
                        // 6. Black body that moves well within the first period white body
                        if C_IsBlackBody and C_BodyLow <= C_BodyMiddle[2]
                            true


//
// --- LONG LEGGED DOJI PATTERN ---
//

export isLongLeggedDojiCandle() =>
    // 1. Is doji candle with long upper and down wicks
    if C_DnShadowPercentageOfBody > 45 and C_UpShadowPercentageOfBody > 45
        // 2. Candle range is higher thatn the previous candle range
        if C_Range > C_CandleAvg
            true


//
// --- MORNING DOJI STAR PATTERN ---
//

export isMorningDojiStarCandle(bool isDownTrend) =>
    // 1. The market is in a downtrend
    if isDownTrend
        // 1. Long, doji, long body
        if C_IsLongBody[2] and isDojiCandle()[1] and C_IsLongBody
            // 2. First is a black body
            if C_IsBlackBody[2]
                // 3. Followed by a small body which gaps lower
                if C_BodyHigh[1] < C_BodyLow[2]
                    // 4. Second gap
                    if C_BodyHigh[1] < C_BodyLow
                        // 5. White body that moves well within the first period black body
                        if C_IsWhiteBody and C_BodyHigh >= C_BodyMiddle[2]
                            true


///////////////////////////////
// --- REVERSAL PATTERNS --- //
///////////////////////////////

//
// --- COUNTERATTACK LINES PATTERN ---
//

export isBullishCounterattackLinesCandle(bool isDownTrend) =>
    // 1. The market is in a downtrend.
    if isDownTrend
        // 2. The first candle is black (down) with a long real body.
        if C_IsBlackBody[1] and C_IsLongBody[1]
            // 3. The second candle gaps down on the open, is white with a real body that's similar in size to the first candle (min 80%)
            if open < close[1] and C_IsWhiteBody and C_Body > C_Body[1] * 0.8
                // 4. The second candle closes near the first candle's close.
                if isInDeviationRange(C_BodyLow[1], C_BodyHigh, 0.003)
                    true

export isBearishCounterattackLinesCandle(bool isUpTrend) =>
    // 1. The market is in a uptrend.
    if isUpTrend
        // 2. The first candle is white (updown) with a long real body.
        if C_IsWhiteBody[1] and C_IsLongBody[1]
            // 3. The second candle gaps up on the open, is black with a real body that's similar in size to the first candle (min 80%)
            if open > close[1] and C_IsBlackBody and C_Body > C_Body[1] * 0.8
                // 4. The second candle closes near the first candle's close.
                if isInDeviationRange(C_BodyHigh[1], C_BodyLow, 0.003)
                    true


//
// --- DARK-CLOUD COVER PATTERN ---
//

export isDarkCloudCoverCandle(bool isUpTrend) =>
    // 1. The market is in a uptrend.
    if isUpTrend
        // 1. The second day's price opens above the prior session's high (that is, above the top of the upper shadow)
        if open > high[1]
            // 2. By the end of the second day's session, the market closes near the low of the day and well within the priors day's white body
            darkCloudCoverPatternPenetrationInPercentage = (close[1] - close) / (close[1] - open[1]) * 100
            // 3. The greater the degree of penetration into the white real body the more likely a top will occur (more than 50%)
            if darkCloudCoverPatternPenetrationInPercentage > 50 and darkCloudCoverPatternPenetrationInPercentage < 100
                [C_IsWhiteBody[1], darkCloudCoverPatternPenetrationInPercentage]


//
// --- ENGULFING PATTERN ---
//

export isBullishEngulfingCandle() =>
    // Two candlesticks comprise the engulfing pattern.
    // 1.1 The second real body must engluf the prior real body (it need not engluf the shadows)
    //     Check for a bullish engulfing pattern
    if C_BodyHigh > C_BodyHigh[1] and C_BodyLow < C_BodyLow[1]
        // 2. The second real body should be the opposite color of the first real body.
        // The exception to this rule is if the first real body of the engulfing pattern is so small it is almost a doji
        if isDojiCandle()[1] ? true : C_IsWhiteBody != C_IsWhiteBody[1]
            // 3. Is bullish
            if close > open
                true

export isBearishEngulfingCandle() =>
    // Two candlesticks comprise the engulfing pattern.
    // 1.1 The second real body must engluf the prior real body (it need not engluf the shadows)
    //     Check for a bullish engulfing pattern
    if C_BodyHigh > C_BodyHigh[1] and C_BodyLow < C_BodyLow[1]
        // 2. The second real body should be the opposite color of the first real body.
        // The exception to this rule is if the first real body of the engulfing pattern is so small it is almost a doji
        if isDojiCandle()[1] ? true : C_IsWhiteBody != C_IsWhiteBody[1]
            // 3. Is bearish
            if close < open
                true


//
// --- HAMMER & HANGING MAN PATTERN ---
//

export isHammerCandle(bool isDownTrend) =>
    // 1. The market is in a downtrend.
    if isDownTrend
        // 2. Small body
        if C_IsSmallBody
            // 3. The real body is at the upper end of the trading range.
            if C_UpShadowPercentageOfBody < 10
                // 4. A long lower shadow should be twice the height of the real body.
                if C_DnShadow >= 2 * C_Body
                    // 5. It should have no, or a very short, upper shadow (max 10% of body).
                    if C_UpShadow <= 0.1 * C_Body
                        true


//
// --- HAMMER & HANGING MAN PATTERN ---
//

export isHangingManCandle(bool isUpTrend) =>
    // 1. The market is in a downtrend.
    if isUpTrend
        // 2. Small body
        if C_IsSmallBody
            // 3. The real body is at the upper end of the trading range.
            if C_UpShadowPercentageOfBody < 10
                // 4. A long lower shadow should be twice the height of the real body.
                if C_DnShadow >= 2 * C_Body
                    // 5. It should have no, or a very short, upper shadow (max 10% of body).
                    if C_UpShadow <= 0.1 * C_Body
                        true


//
// --- HARAMI PATTERN ---
//

export isHaramiBearishCandle(bool isUpTrend) =>
    // 1. There is an uptrend. The first candle must continue with the trend’s direction (white body). The color of the second candle doesn't matter.
    if isUpTrend[1] and C_IsWhiteBody[1]
    // 2. The first candle will have a long body.
        if C_IsLongBody[1] and C_IsSmallBody
        // 3. The second candle must be contained within the first candle’s body (so it opens and closes within the body of the first candle). The wicks are irrelevant.
            if C_BodyHigh <= C_BodyHigh[1] and C_BodyLow >= C_BodyLow[1]
                true

export isHaramiBullishCandle(bool isDownTrend) =>
    // 1. There is a downtrend. The first candle must continue with the trend’s direction (black body). The color of the second candle doesn't matter.
    if isDownTrend[1] and C_IsBlackBody[1]
    // 2. The first candle will have a long body.
        if C_IsLongBody[1] and C_IsSmallBody
        // 3. The second candle must be contained within the first candle’s body (so it opens and closes within the body of the first candle). The wicks are irrelevant.
            if C_BodyHigh <= C_BodyHigh[1] and C_BodyLow >= C_BodyLow[1]
                true


//
// --- IN-NECK PATTERN ---
//

export isInNeckCandle(bool isDownTrend) =>
    // 1. The market is in a downtrend.
    if isDownTrend
        // 2. First candlestick is a black real body day and the second is a long, white real body day
        if C_IsBlackBody[1] and C_IsWhiteBody
            // 3. This white day opens sharply lower, under the low of the prior black day
            if open < low[1]
                // 4. white candlestick closes slightly into the prior real body (it should also be a small white candlestick)
                if close < close[1] * 1.002 and close > close[1]
                    // 5. a long real bodied down candle is followed by a smaller real bodied up candle
                    if C_Body[1] > C_Body
                        true


//
// --- ON-NECK PATTERN ---
//

export isOnNeckCandle(bool isDownTrend) =>
    // 1. The market is in a downtrend.
    if isDownTrend
        // 2. First candlestick is a black real body day and the second is a long, white real body day
        if C_IsBlackBody[1] and C_IsWhiteBody
            // 3. This white day opens sharply lower, under the low of the prior black day
            if open < low[1]
                // 4. white candlestick (usually a small one) closes near the low of the previous session
                if close < low[1] * 1.001 and close > low[1] * 0.999
                    // 5. a long real body down candle is followed by a smaller real bodied up candle
                    if C_Body[1] > C_Body
                        true


//
// --- PIERCING PATTERN ---
//

export isPiercingCandle(bool isDownTrend) =>
    // 1. The market is in a downtrend.
    if isDownTrend
        // 2. First candlestick is a black real body day and the second is a long, white real body day
        if C_IsBlackBody[1] and C_IsWhiteBody
            // 3. This white day opens sharply lower, under the low of the prior black day
            if open < low[1]
                // 4. Closes above the mid-point of the prior day's black real body
                if close > C_BodyMiddle[1] and close < open[1]
                    true


//
// --- THREE BLACK CROWS PATTERN ---
//

export threeBlackCrowsCandle(bool isUpTrend) =>
    // 1. The market is in an uptrend.
    if isUpTrend
        // 2. The first candlestick is a long bullish candlestick
        if C_IsWhiteBody[3] and C_IsLongBody[3]
            // 3. The second candlestick is a long bearish candlestick that opens above the close of the first candlestick and closes below the midpoint of the first candlestick.
            if C_IsBlackBody[2] and C_IsLongBody[2] and open[2] > close[3] and close[2] < C_BodyMiddle[3]
                // 4. The third candlestick is a long bearish candlestick that opens below the midpoint of the second candlestick and closes below the close of the second candlestick.
                if C_IsBlackBody[1] and C_IsLongBody[1] and open[1] < C_BodyMiddle[2]
                    // 5. The fourth candlestick is a long bearish candlestick that opens below the midpoint of the third candlestick and closes below the close of the third candlestick.
                    if C_IsBlackBody and C_IsLongBody and open < C_BodyMiddle[1] and close < close[1]
                        true


//
// --- THRUSTING-NECK PATTERN ---
//

export isThrustingNeckCandle(bool isDownTrend) =>
    // 1. The market is in a downtrend.
    if isDownTrend
        // 2. First candlestick is a black real body day and the second is a long, white real body day
        if C_IsBlackBody[1] and C_IsWhiteBody
            // 3. This white day opens sharply lower, under the low of the prior black day
            if open < low[1]
                // 4. should be a longer white candlestick that is stronger than the in-neck pattern but still does not close above the middle of the prior black real body
                if close < C_BodyMiddle[1] and close > close[1] * 1.002
                    // 5. a long real bodied down candle is followed by a smaller real bodied up candle
                    if C_Body[1] > C_Body
                        true


//
// --- UPSIDE-GAP TWO CROWS PATTERN ---
//

export isUpsideGapTwoCrowsCandle(bool isUpTrend) =>
    // 1. The upside gap two crows must occur during an uptrend.
    if isUpTrend
        // 2. The first candle is bullish.
        if C_IsWhiteBody[2]
            // 3. The second candle must be bearish with a small real body and closes higher than the first, creating a gap.
            if C_IsBlackBody[1] and C_IsSmallBody[1] and close[1] > close[2]
                // 4. The third candle must be bearish while closing above the first candle’s close.
                if C_IsBlackBody and close > close[2]
                    // 5. The third candle must engulf the second candle's real body
                    if C_Body > C_Body[1] and C_BodyHigh > C_BodyHigh[1] and C_BodyLow < C_BodyLow[1]
                        true        


///////////////////////////
// --- STAR PATTERNS --- //
///////////////////////////

//
// --- ABANDONED BABY TOP (BEARISH) PATTERN ---
//

export isAbandonedBabyTopCandle(bool isUpTrend) =>
    // 1. The market is in a uptrend
    if isUpTrend
        // 2. First is a white long body
        if C_IsWhiteBody[2] and C_IsLongBody[2]
            // 3. Followed by a second small body which gaps higher
            if C_IsSmallBody[1] and high[2] < low[1]
                // 4. Followed by a third body which gaps lower
                if high < low[1]
                    // 5. Black third body that moves well within the first period white body
                    if C_IsBlackBody and C_BodyLow <= C_BodyMiddle[2]
                        true


//
// --- ABANDONED BABY BOTTOM (BULLISH) PATTERN ---
//

export isAbandonedBabyBottomCandle(bool isDownTrend) =>
    // 1. The market is in a downtrend
    if isDownTrend
        // 2. First is a black long body
        if C_IsBlackBody[2] and C_IsLongBody[2]
            // 3. Followed by a second small body which gaps lower
            if C_IsSmallBody[1] and high[1] < low[2]
                // 4. Followed by a third body which gaps higher
                if low > high[1]
                    // 5. White third body that moves well within the first period black body
                    if C_IsWhiteBody and C_BodyHigh >= C_BodyMiddle[2]
                        true


//
// --- EVENING STAR PATTERN ---
//

export isEveningStarCandle(bool isUpTrend) =>
    // 1. The market is in a uptrend
    if isUpTrend
        C_EveningStarPatternIdeal = false
        // 2. Long, small, long body
        if C_IsLongBody[2] and C_IsSmallBody[1] and C_IsLongBody
            // 3. First is a white body
            if C_IsWhiteBody[2]
                // 4.1 Followed by a small body which gaps higher
                if C_BodyHigh[2] < C_BodyLow[1]
                    // 4.2 [Optional] The second gap is rare, but lack of it does not seem to vitiate the power of this formation
                    C_EveningStarPatternIdeal := C_BodyHigh < C_BodyLow[1]
                    // 5. Black body that moves well within the first period white body
                    if C_IsBlackBody and C_BodyLow <= C_BodyMiddle[2]
                        [true, C_EveningStarPatternIdeal]


//
// --- INVERTED HAMMER PATTERN ---
//

export isInvertedHammerCandle(bool isDownTrend) =>
    // 1. The market is in a downtrend
    if isDownTrend
        // 2. Small body
        if C_IsSmallBody
            // 3. The real body is at the upper end of the trading range.
            if C_DnShadowPercentageOfBody < 10
                // 4. A long upper shadow should be twice the height of the real body.
                if C_UpShadow >= 2 * C_Body
                    true


//
// --- MORNING STAR PATTERN ---
//

export isMorningStarCandle(bool isDownTrend) =>
    // 1. The market is in a downtrend
    if isDownTrend
        C_MorningStarPatternIdeal = false
        // 1. Long, small, long body
        if C_IsLongBody[2] and C_IsSmallBody[1] and C_IsLongBody
            // 2. First is a black body
            if C_IsBlackBody[2]
                // 3.1 Followed by a small body which gaps lower
                if C_BodyHigh[1] < C_BodyLow[2]
                    // 3.2 [Optional] The second gap is rare, but lack of it does not seem to vitiate the power of this formation
                    C_MorningStarPatternIdeal := C_BodyHigh[1] < C_BodyLow
                    // 4. White body that moves well within the first period black body
                    if C_IsWhiteBody and C_BodyHigh >= C_BodyMiddle[2]
                        [true, C_MorningStarPatternIdeal]


//
// --- SHOOTING STAR ---
//

export isShootingStarCandle(bool isUpTrend) =>
    // 1. The market is in a uptrend
    if isUpTrend
        // 2. Small body
        if C_IsSmallBody
            // 3. The real body is at the upper end of the trading range.
            if C_DnShadowPercentageOfBody < 10
                // 4. A long upper shadow should be twice the height of the real body.
                if C_UpShadow >= 2 * C_Body
                    true


///////////////////////////////////
// --- CONTINUATION PATTERNS --- //
///////////////////////////////////

//
// --- RISING THREE METHODS PATTERN ---
//

export isRisingThreeMethodsCandle(bool isUpTrend) =>
    // 1. The market is in a uptrend
    if isUpTrend
        // 2. The first candlestick is a long white candlestick
        if C_IsLongBody[4] and C_IsWhiteBody[4]
            // 3. Follow by three consecutive smaller black candles
            if C_IsBlackBody[3] and C_IsBlackBody[2] and C_IsBlackBody[1] and C_IsSmallBody[3] and C_IsSmallBody[2] and C_IsSmallBody[1]
                // 4. Those three bearish candles trade above the low and below the high of the first candlestick
                if open[3] < high[4] and close[3] > low[4] and close[2] > low[4]
                    // 5. The fifth candlestick is a white long candlestick that closes above the high of the fourth candlestick and confirms the continuation of the uptrend.
                    if close > close[4] and C_IsLongBody
                        true


//
// --- FALLING THREE METHODS PATTERN ---
//

export isFallingThreeMethodsCandle(bool isDownTrend) =>
    // 1. The market is in a downtrend
    if isDownTrend
        // 2. The first candlestick is a long black candlestick
        if C_IsLongBody[4] and C_IsBlackBody[4]
            // 3. Follow by three consecutive smaller white candles
            if C_IsWhiteBody[3] and C_IsWhiteBody[2] and C_IsWhiteBody[1] and C_IsSmallBody[3] and C_IsSmallBody[2] and C_IsSmallBody[1]
                // 4. Those three bullish candles trade above the low and below the high of the first candlestick
                // if open[3] < high[4] and open[2] > open[3] and open[1] > open[2] and close[3] > low[4] and close[2] > low[4]
                if open[3] > low[4] and close < high[4] and close[2] < high[4] and close[1] < high[4]
                    // 5. The fifth candlestick is a black long candlestick that closes below the low of the fourth candlestick and confirms the continuation of the downtrend.
                    if close < close[4] and C_IsLongBody
                        true


//
// --- UPSIDE TASUKI GAP PATTERN ---
//

export isUpsideTasukiGapCandle(bool isUpTrend) =>
    // 1. The market is in a uptrend
    if isUpTrend
        // 2. First white long body followed by a smal white body
        if C_IsLongBody[2] and C_IsSmallBody[1] and C_IsWhiteBody[2] and C_IsWhiteBody[1]
            // 3. First two candles create a gap
            if C_BodyHigh[2] < C_BodyLow[1] 
                // 4. The third candle is black and closes inside the gap created by the first two candles
                if C_IsBlackBody and C_BodyLow > C_BodyHigh[2] and C_BodyLow < C_BodyLow[1]
                    true


//
// --- DOWNSIDE TASUKI GAP PATTERN ---
//

export isDownsideGapTasukiCandle(bool isDownTrend) =>
    // 1. The market is in a downtrend
    if isDownTrend
        // 2. First black long body followed by a smal black body
        if C_IsLongBody[2] and C_IsSmallBody[1] and C_IsBlackBody[2] and C_IsBlackBody[1]
            // 3. First two candles create a gap
            if C_BodyLow[2] > C_BodyHigh[1]
            // if C_BodyHigh[1] < C_BodyLow[2]
                // 4. The third candle is white and closes inside the gap created by the first two candles
                if C_IsWhiteBody and C_BodyHigh < C_BodyLow[2] and C_BodyHigh > C_BodyHigh[1]
                    true


///////////////////////
// --- UTILITIES --- //
///////////////////////

//
// --- LONG LOWER SHADDOW ---
//

export isLongLowerShadowCandle() =>
    if C_DnShadowPercentageOfBody > 75
        true


//
// --- LONG UPPER SHADDOW ---
//

export isLongUpperShadowCandle() =>
    if C_UpShadowPercentageOfBody > 75
        true


///////////////////
// --- DEBUG --- //
///////////////////

// plotchar(C_IsUpTrend, "C_IsUpTrend", " ", display = display.all - display.status_line)
// plotchar(C_IsDownTrend, "C_IsDownTrend", " ", display = display.all - display.status_line)
// plotchar(C_BodyHigh, "C_BodyHigh", " ", display = display.all - display.status_line)
// plotchar(C_BodyLow, "C_BodyLow", " ", display = display.all - display.status_line)
// plotchar(C_Body, "C_Body", " ", display = display.all - display.status_line)
// plotchar(C_BodyAvg, "C_BodyAvg", " ", display = display.all - display.status_line)
// plotchar(C_CandleAvg, "C_CandleAvg", " ", display = display.all - display.status_line)
// plotchar(C_IsSmallBody, "C_IsSmallBody", " ", display = display.all - display.status_line)
// plotchar(C_IsLongBody, "C_IsLongBody", " ", display = display.all - display.status_line)
// plotchar(C_IsWhiteBody, "C_IsWhiteBody", " ", display = display.all - display.status_line)
// plotchar(C_IsBlackBody, "C_IsBlackBody", " ", display = display.all - display.status_line)
// plotchar(C_Range, "C_Range", " ", display = display.all - display.status_line)
// plotchar(C_IsInsideBar, "C_IsInsideBar", " ", display = display.all - display.status_line)
// plotchar(C_BodyMiddle, "C_BodyMiddle", " ", display = display.all - display.status_line)
// plotchar(C_UpShadow, "C_UpShadow", " ", display = display.all - display.status_line)
// plotchar(C_DnShadow, "C_DnShadow", " ", display = display.all - display.status_line)
// plotchar(C_UpShadowPercentageOfBody, "C_UpShadowPercentageOfBody", " ", display = display.all - display.status_line)
// plotchar(C_DnShadowPercentageOfBody, "C_DnShadowPercentageOfBody", " ", display = display.all - display.status_line)