Volume Impulse & Candlestick Patterns - Fontiramisu
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// Author : @Fontiramisu
// Indicator showing volume impulse & candlestick pattern.
//@version=5
indicator("Candlestick Patterns & Volume Signal - Fontiramisu", shorttitle = "Pattern & Volume Sign", overlay=true)

// ----------------- Candlestick Patterns ----------------- [
tfInput = input.timeframe("")
tfClose = request.security(syminfo.tickerid, tfInput, close)
tfHigh = request.security(syminfo.tickerid, tfInput, high)
tfLow = request.security(syminfo.tickerid, tfInput, low)
tfOpen = request.security(syminfo.tickerid, tfInput, open)
//bar_index request.security(syminfo.tickerid, tfInput, bar_index)

// Calculate Input Parameters.
lenghtSizeAvgBody = input.int(14, title="Lenght Size Avg Body", tooltip="Lenght used to calculate the average body size (ema method is used)", group="Calculate Parameters")

// Calculate Variables.
C_DownTrend = true
C_UpTrend = true
var trendRule1 = "SMA50"
var trendRule2 = "SMA50, SMA200"
var trendRule = input.string(trendRule1, "Detect Trend Based On", options=[trendRule1, trendRule2, "No detection"], group="Calculate Parameters")

if trendRule == trendRule1
    priceAvg = ta.sma(tfClose, 50)
    C_DownTrend := tfClose < priceAvg
    C_UpTrend := tfClose > priceAvg

if trendRule == trendRule2
    sma200 = ta.sma(tfClose, 200)
    sma50 = ta.sma(tfClose, 50)
    C_DownTrend := tfClose < sma50 and sma50 < sma200
    C_UpTrend := tfClose > sma50 and sma50 > sma200
C_ShadowPercent = 5.0 // size of shadows
C_ShadowEqualsPercent = 100.0
C_DojiBodyPercent = 5.0
C_Factor = 2.0 // shows the number of times the shadow dominates the candlestick body

C_BodyHi = math.max(tfClose, tfOpen)
C_BodyLo = math.min(tfClose, tfOpen)
C_Body = C_BodyHi - C_BodyLo
C_BodyAvg = ta.ema(C_Body, lenghtSizeAvgBody)
C_SmallBody = C_Body < C_BodyAvg
C_LongBody = C_Body > C_BodyAvg
C_UpShadow = tfHigh - C_BodyHi
C_DnShadow = C_BodyLo - tfLow
C_HasUpShadow = C_UpShadow > C_ShadowPercent / 100 * C_Body
C_HasDnShadow = C_DnShadow > C_ShadowPercent / 100 * C_Body
C_WhiteBody = tfOpen < tfClose
C_BlackBody = tfOpen > tfClose
C_Range = tfHigh-tfLow
C_IsInsideBar = C_BodyHi[1] > C_BodyHi and C_BodyLo[1] < C_BodyLo
C_BodyMiddle = C_Body / 2 + C_BodyLo
C_ShadowEquals = C_UpShadow == C_DnShadow or (math.abs(C_UpShadow - C_DnShadow) / C_DnShadow * 100) < C_ShadowEqualsPercent and (math.abs(C_DnShadow - C_UpShadow) / C_UpShadow * 100) < C_ShadowEqualsPercent
C_IsDojiBody = C_Range > 0 and C_Body <= C_Range * C_DojiBodyPercent / 100
C_Doji = C_IsDojiBody and C_ShadowEquals

// Y Pos for label.
patternLabelPostfLow = tfLow - (ta.atr(30) * 0.7)
patternLabelPostfHigh = tfHigh + (ta.atr(30) * 0.7)

// Show Parameter.
label_color_bullish = input(color.blue, "Label Color Bullish", group="show parameters")
label_color_bearish = input(color.red, "Label Color Bearish", group="show parameters")
label_color_neutral = input(color.gray, "Label Color Neutral", group="show parameters")
CandleType = input.string(title = "Pattern Type", defval="Both", options=["Bullish", "Bearish", "Both"], group="show parameters")


// Engulfing.
EngulfingInput = input(title = "Show Engulfing" ,defval=true, group="Engulfing Pattern") 
isCompareAvgBodyEngulfing = input(title = "VS avg body" ,defval=false, group="Engulfing Pattern", tooltip="Check the box if you want to compare body candle to average of the last few ones, otherwise it will be compare to the last one") 
engulfingMultiplier = input.float(defval=3, title = "Engulfing Multiplier", group="Engulfing Pattern", tooltip="Minimum multiplier to validate the pattern")

bodyCompareEngulfing = isCompareAvgBodyEngulfing ? C_BodyAvg : C_Body[1]
C_EngulfingBullish = C_DownTrend and C_WhiteBody and C_LongBody and C_BlackBody[1] and tfOpen <= tfClose[1] and tfClose >= tfOpen[1] and C_Body > bodyCompareEngulfing * engulfingMultiplier
alertcondition(C_EngulfingBullish, title = "Engulfing – Bullish", message = "New Engulfing – Bullish pattern detected")
if C_EngulfingBullish  and  EngulfingInput and (("Bullish" == CandleType) or CandleType == "Both")
    var ttBullishEngulfing = "Engulfing\nAt the end of a given downward trend, there will most likely be a reversal pattern. To distinguish the first day, this candlestick pattern uses a small body, foltfLowed by a day where the candle body fully overtakes the body from the day before, and tfCloses in the trend’s opposite direction. Although similar to the outside reversal chart pattern, it is not essential for this pattern to completely overtake the range (tfHigh to tfLow), rather only the tfOpen and the tfClose."
    label.new(bar_index, patternLabelPostfLow, text="BE", style=label.style_label_up, color = label_color_bullish, textcolor=color.white, tooltip = ttBullishEngulfing)

C_EngulfingBearish = C_UpTrend and C_BlackBody and C_LongBody and C_WhiteBody[1] and tfOpen >= tfClose[1] and tfClose < tfOpen[1] and C_Body > bodyCompareEngulfing * engulfingMultiplier
alertcondition(C_EngulfingBearish, title = "Engulfing – Bearish", message = "New Engulfing – Bearish pattern detected")
if C_EngulfingBearish  and  EngulfingInput and (("Bearish" == CandleType) or CandleType == "Both")
    var ttBearishEngulfing = "Engulfing\nAt the end of a given uptrend, a reversal pattern will most likely appear. During the first day, this candlestick pattern uses a small body. It is then foltfLowed by a day where the candle body fully overtakes the body from the day before it and tfCloses in the trend’s opposite direction. Although similar to the outside reversal chart pattern, it is not essential for this pattern to fully overtake the range (tfHigh to tfLow), rather only the tfOpen and the tfClose."
    label.new(bar_index, patternLabelPostfHigh, text="BE", style=label.style_label_down, color = label_color_bearish, textcolor=color.white, tooltip = ttBearishEngulfing)



// ]----------------- Volume Signals ----------------- [
// Inputs.
showVolumeImpulseInput  = input.bool(true,"Show Volume Impulse", group="Volume")
lengthVolume            = input.int(20,"Volume Period", group="Volume", tooltip="Lenght of the average volume calculated")
factor                  = input.float(3.5,"Volume Multiplier", minval=0, group="Volume", tooltip="Factor you want to set to compare actual volume with average volume")

// Helping vars.
smaVolume       = ta.sma(volume, lengthVolume)
isVolUp         = close > open 
isVolDown       = open > close
isVolImpulse    = volume > smaVolume * factor

// Cond + label
if showVolumeImpulseInput
    if isVolImpulse and isVolUp
        label.new(bar_index, patternLabelPostfLow, text="VI", style=label.style_label_up, color = label_color_bullish, textcolor=color.white, tooltip = "Volume Impulse Up")
    
    else if isVolImpulse and isVolDown
        label.new(bar_index, patternLabelPostfHigh, text="VI", style=label.style_label_down, color = label_color_bearish, textcolor=color.white, tooltip = "Volume Impulse Down")


// ] DEBUG -- Show Table -- [
// show_table = input(true,  "Show table with stats", group = "Drawings")

// if show_table
//     var table ATHtable = table.new(position.bottom_right, 10, 10, frame_color = color.gray, bgcolor = color.gray, border_width = 1, frame_width = 1, border_color = color.white)
    
//     table.cell(ATHtable, 0, 0, text="C_LongBody", bgcolor=color.silver, tooltip="")
//     // table.cell(ATHtable, 1, 0, text="na", bgcolor=color.green, tooltip="")
//     table.cell(ATHtable, 1, 0, text=str.tostring(C_LongBody), bgcolor=color.green, tooltip="")
    
//     table.cell(ATHtable, 0, 1, text="C_UpTrend", bgcolor=color.silver, tooltip="")
//     table.cell(ATHtable, 1, 1, text=str.tostring(C_UpTrend), bgcolor=color.green, tooltip="")
// // ]
