Pine Script – Lesson 6: How To Detect Engulfing Candles

Pine Script Basics Course Pine Script Mastery Course

How To Identify Candle Patterns Using Pine Script

In this lesson I’ll show you how to detect basic candlestick patterns using Pine Script.

We’ll focus solely on Engulfing Candles for now, but the process involved in identifying them is similar for all other candle patterns such as pinbars, shooting stars and hammers, dojis, higher-high higher-close and lower-low lower-close candles.

Today we’ll be expanding upon the script that we made in Lesson 4: Generating RSI Signals. This way the lesson will produce a practical and useful outcome – an RSI oscillator that you can modify to detect whatever kinds of candle patterns you want based on the RSI conditions.


Video Lesson

If you prefer to learn in a visual/audio manner, then here’s a video version of this lesson:


Working With the RSI Indicator

This script will essentially be a basic remake of my RSI Swing Signals indicator. I hope to demonstrate how you can create your own custom indicators similar to this:

If you’re inexperienced with Pine Script and you haven’t gone through the Basics section of my Pine Script lessons then I highly recommend that you do that first. Otherwise… let’s get started.

Here’s the source code from the final lesson of the Basics section which we will be working with again today. I recommend starting a new script and pasting this code into the Pine Script Editor before continuing:


RSI Signal Source Code

//@version=4
study(title="Lesson 5", shorttitle="RSI Signal", overlay=true)

// Get user input
rsiSource = input(title="RSI Source", type=input.source, defval=close)
rsiLength = input(title="RSI Length", type=input.integer, defval=14)
rsiOverbought = input(title="RSI Overbought Level", type=input.integer, defval=70)
rsiOversold = input(title="RSI Oversold Level", type=input.integer, defval=30)

// Get RSI value
rsiValue = rsi(rsiSource, rsiLength)
rsiOB = rsiValue >= rsiOverbought
rsiOS = rsiValue <= rsiOversold

// Plot signals to chart
plotshape(rsiOB, title="Overbought", location=location.abovebar, color=color.red, transp=0, style=shape.triangledown, text="OB")
plotshape(rsiOS, title="Oversold", location=location.belowbar, color=color.green, transp=0, style=shape.triangleup, text="OS")

// Send out an alert if this candle meets our conditions
alertcondition(rsiOB or rsiOS, title="RSI Alert!", message="RSI signal for XXX")


The Four Basic Elements Of A Strategy

All rules-based strategies are comprised of at least four basic elements:

  • Entry Conditions (eg. trending market, test of support/resistance zone)
  • Indicator Conditions (eg. RSI overbought/oversold, price is above/below a moving average)
  • Entry Reasons (eg. a bullish or bearish engulfing candle)
  • Trade Management (eg. stops and targets, position size).

I won’t go into detail about all of these elements in this lesson because that’s outside the scope of what we’re doing. Instead today’s lesson will be focusing on the second and third elements – indicator conditions and entry reasons.


The Anatomy Of Candles

There are four built-in Pine Script variables we have to work with in order to detect candle patterns: the open price, the close price, the high and the low.

Using these four variables we can determine if a candle meets the criteria to be called a certain pattern – such as an “engulfing candle”.

Anatomy of a Candle
CANDLE ANATOMY

The example above is called an engulfing candle. This is because the close of the green candle closes higher than the open of the red candle.

This is a sign of bullish strength – but if this pattern occurs in the opposite direction as a bearish engulfing candle, then it’s a sign of potential bearish strength.

This simple pattern when used in conjunction with market and indicator conditions and filters can make for a high-accuracy entry reason for almost any strategy.


Comparing Candle Prices With Pine Script

So we know which variables we need to work with – open, close, high, low.

In the case of a bullish engulfing candle, the completion candle must close at a higher price than the previous candle’s open price, just like in the picture above.

In Pine Script we could detect this candle condition with the following line of code:

higherClose = close >= open[1]


This variable will turn true only if the current candle’s closing price is greater than or equal to the previous candle’s opening price.

In order to be considered an engulfing candle, the previous candle must have also closed in the opposite direction – for example, in the illustration above the candle preceding the engulfing candle is red.

In order to determine whether the previous candle was red we can add this line of code:

bullishEngulfing = close >= open[1] and close[1] < open[1]


It’s that simple!

Of course here are a few more steps you can add that will dramatically improve the accuracy of this engulfing candle detection (for example – ensuring that it’s a swing low, ignoring setups with large rejection wicks, etc). But this will do the trick for detecting basic engulfing candles. I’ll cover those more advanced techniques in future lessons.

I haven’t covered arrays yet in any of my lessons, but they are very simple to understand. Square brackets [ ] are used to reference an “array”, which can be thought of as a list of values. In Pine Script this is referred to as the Historical Referencing Operator which will perhaps make more sense if you’re new to coding.

In the first statement we’re asking for the opening price of the candle with the array index (position) of “1”. In programming, arrays and lists typically always start at 0 (zero) instead of 1. This means that close[0] will give us the current candle closing price, and close[100] will give us the candle closing price from 99 candles in the past.

So in the line above, we are essentially saying “close[0] >= open[1]”.

This can be a bit confusing if you’re new to programming but don’t worry – it’ll make sense in time.

Just know that when you are referencing candles in Pine Script you must count up from 0 as you count backwards – so the closing price of the candle 3 bars ago from the current bar will be referenced as “close[2]”.

There are multiple variations of engulfing candles – such as a higher-high higher-close engulfing candle and a fractal swing-low engulfing candle.

In today’s lesson we won’t go into that much detail, but by comparing these candle values with each other it’s quite easy to detect any variation of these patterns that you desire.

For example, if you wanted to detect a higher-high higher-close engulfing candle (ie. one that closes above the high of the wick and not just the opening price), then you can do so with this line of code:

higherHighHigherClose = close >= high[1]


Detecting Engulfing Candles

Now that we’ve covered the basics of a candle’s anatomy and how to get and compare these variables in Pine Script, let’s implement this knowledge into an actual script.

The first thing we’re going to do is add three new variables above the section of Lesson 5’s script that says “// Plot signals to chart”:

bullishEC = close > open[1] and close[1] < open[1]
bearishEC = close < open[1] and close[1] > open[1]
tradeSignal = ((rsiOS or rsiOS[1]) and bullishEC) or ((rsiOB or rsiOB[1]) and bearishEC)


I’ll walk you through what each of these lines does.

The first variable “bullishEC” will turn true if the current candle’s closing price is higher than the previous candle’s opening price and the previous candle was bearish.

The second variable “bearishEC” will turn true if the current candle’s closing price is lower than the previous candle’s opening price and the previous candle was bullish.

The third variable “tradeSignal” will turn true if a bullish or bearish engulfing candle is detected while the RSI conditions are met.

So if the RSI is currently oversold or it was oversold on the previous bar andbullishEC” is true, then “tradeSignal” will turn true.

Or alternatively, if the RSI is currently overbought or it was overbought on the previous bar and bearishEC” is true, “tradeSignal” will turn true.

If it is false and no signal is detected then we ignore the current candle.

With these three variables we can now detect basic engulfing candles whenever the RSI goes overbought or oversold! Simply change your “plot” code to look like this:

// Plot signals to chart
plotshape(tradeSignal and bullishEC, title="Long", location=location.belowbar, color=color.green, transp=0, style=shape.triangleup, text="Long")
plotshape(tradeSignal and bearishEC, title="Short", location=location.abovebar, color=color.red, transp=0, style=shape.triangledown, text="Short")

And voila!

You have an indicator that will detect counter-trend setups for you while you’re backtesting or even while you’re away from your computer.

Pine Script - Detecting Engulfing Candles With RSI Indicator


If you want to turn this into an oscillator indicator similar to my RSI Swing Signals oscillator then all you need to do is change the parameter “overlay=true” to “overlay=false” in the study() constructor, and then add the line “plot(rsi)” to the end of your script.

You may also need to play around with bgcolor() or with plotshape() in order to see the signals better – but that’s all there is to it!

If you have any questions or suggestions about what you’d like me to cover next, feel free to leave them below. See you next time!

Advanced Course

If you want to take your Pine Script coding to the next level, then I think you’ll be interested in my Pine Script Mastery Course.

If you liked this free content then I promise that you’ll love my premium content where I am able to go into much greater detail and help answer students’ questions!



Source Code

//@version=4
study(title="Lesson 6", shorttitle="RSI Swing Signals", overlay=true)

// Get user input
rsiSource = input(title="RSI Source", type=input.source, defval=close)
rsiLength = input(title="RSI Length", type=input.integer, defval=14)
rsiOverbought = input(title="RSI Overbought Level", type=input.integer, defval=70)
rsiOversold = input(title="RSI Oversold Level", type=input.integer, defval=30)

// Get RSI value
rsiValue = rsi(rsiSource, rsiLength)
rsiOB = rsiValue >= rsiOverbought
rsiOS = rsiValue <= rsiOversold

// Identify engulfing candles
bullishEC = close > open[1] and close[1] < open[1]
bearishEC = close < open[1] and close[1] > open[1]
tradeSignal = ((rsiOS or rsiOS[1]) and bullishEC) or ((rsiOB or rsiOB[1]) and bearishEC)

// Plot signals to chart
plotshape(tradeSignal and bullishEC, title="Long", location=location.belowbar, color=color.green, transp=0, style=shape.triangleup, text="Long")
plotshape(tradeSignal and bearishEC, title="Short", location=location.abovebar, color=color.red, transp=0, style=shape.triangledown, text="Short")

// Send out an alert if this candle meets our conditions
alertcondition(tradeSignal, title="RSI Trade Alert!", message="RSI Swing Signal for XXX")

OVERVIEW


Free Premium Charts!

5 2 votes
Article Rating
Subscribe
Notify of
guest
13 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Tom
Tom
3 years ago

Hi, could you help me with writing my own script for kind of zig zag indicator? I want to write script that will draw trend line based on candles max and min. If next candle ends higher then previous one then it will be up trend, but when next candle ends on the same level or lower then script should check minimum of candle, and if the min of next candle is lower than min of prev candle than trend should change to downtrend. I’ve searched internet but I can’t find similiar script

Anonymous
Anonymous
3 years ago

Great content!

polybait
polybait
2 years ago

Hi,
Excellent content! I am just starting to study pine and your lessons help a lot.
One thing I noticed in this lesson: it seems your definition of engulfing candles is incomplete. I believe it should be more like this

// Identify engulfing candles
bullishEC = close >= open[1] and close[1] <= open[1] and open <= close[1]
bearishEC = close <= open[1] and close[1] >= open[1] and open >= close[1]

Jesper
Jesper
Reply to  polybait
2 years ago

This makes it complete ! Perfect addition.

RRR
RRR
Reply to  polybait
2 years ago

That’s funny I actually came here to post the exact same comment and code modification. It’s weird that sometimes the engulfing’s entry price gaps. But what’s even weirder is that the original code gets more wins on my 50 (so far) back test on binance futures than real engulfing setups, lol!

Mbuk2k
Mbuk2k
2 years ago

Great article and love your video/course – thank you! just wondered if you had used arrays or something similar to only display labels if they interact with a plotted horizontal line (through pine script). Image attached but no idea if it’s possible and figure if anyone knows if it is…it’d be you :D cheers!

chart.png
mbuk2k
mbuk2k
Reply to  mjslabosz
2 years ago

Bro you’re a bloody legend! thank you!! keep up the great work matey

Raj
Raj
2 years ago

Hi!
I’m trying to create an array which can hold boolean values for the last 5 instances of Bullish engulfing (1) & Bearish engulfing (0) patterns. The idea is to generate a buy signal when there are 2 consecutive bullish engulfing patterns.. Can you help with the code please?
Thanks..

Last edited 2 years ago by Raj
Milonashis Das
Milonashis Das
1 year ago

Hi Matthew, it’s really helpful. Can you please write a code to detect a DOUBLE TOP AND DOUBLE BOTTOM instead of just engulfing candle ON THIS? Please..

Robert
Robert
1 year ago

Hi Mat, I’ve combined some of your basic lessons with ema-crossover. For some reason it does’nt work. what have I done wrong? Would you help me out? Best regards, Robert here’s the code: //@version=4 study(title=”RSI EMA-Crossings Swing”, overlay=true) // Get user input RSI rsiSource = input(title=”RSI Source”, type=input.source, defval=close) rsiLength = input(title=”RSI Length”, type=input.integer, defval=14) rsiOverbought = input(title=”RSI Overbought Level”, type=input.integer, defval=70) rsiOversold = input(title=”RSI Oversold Level”, type=input.integer, defval=30) // Get user input Ema  short = ema(close, 9) long = ema(close, 21) initialcrossover = crossover(short,long) initialcrossunder = crossunder(short,long) // Get RSI value rsiValue = rsi(rsiSource, rsiLength) rsiOB = rsiValue >=… Read more »

ovi
ovi
Reply to  Robert
10 months ago

//@version=4 study(title = “RSI EMA-Crossings Swing”, overlay=true) // Get user input RSI rsiSource = input(title=“RSI Source”, type=input.source, defval=close) rsiLength = input(title=“RSI Length”, type=input.integer, defval=14) rsiOverbought = input(title=“RSI Overbought Level”, type=input.integer, defval=75) rsiOversold = input(title=“RSI Oversold Level”, type=input.integer, defval=30) // Get user input Ema short = ema(close, 9) long = ema(close, 21) initialcrossover = crossover(short,long) initialcrossunder = crossunder(short,long) // Get RSI value rsiValue = rsi(rsiSource, rsiLength) rsiOB = rsiValue >= rsiOverbought rsiOS = rsiValue <= rsiOversold // Identify engulfing candles bullishEC = close >= open[1] and close[1] <= open[1] bearishEC = close < open[1] and close[1] > open[1] tradeSignallong =(initialcrossover and… Read more »