Pine Script – Lesson 9: How To Eliminate Repainting
Table of Contents
How to Deal With Security() Repainting
Repainting. It’s the rookie Pine Script coder’s arch nemesis.
If your script repaints, it can render it completely useless, or worse – if you create strategy scripts that repaint, then they can give false results during the automatic backtesting process and give you inaccurate (and often overestimated) results.
Repainting most often occurs when your script references higher timeframe data. So in this lesson I will be using a HTF High / Low indicator script to demonstrate how to avoid this very serious issue.
Video Lesson
Here’s the video version that compliments this lesson:
What Is Repainting?
Before I begin I should probably explain what this repainting thing is for anyone who is unfamiliar.
First of all, there’s a fantastic script and blog post created by the official Pine Script development team that explains this issue in detail (and how to circumvent it if desired).
You can access it by clicking here.
I should start by pointing out that repainting isn’t a bug. It may seem like a bug, and when I first began coding in Pine Script I had the impression that it was an unintended glitch in the Pine runtime engine.
But it turns out that the repainting feature is an intended design by the Pine Script development team, and it’s up to us as Pine coders to “turn it off” if we don’t want it to occur with our scripts.
What Do Pine Coders Mean By Repainting?
To paraphrase the authors of the script and blog post that I used for reference material for this lesson:
Repainting is used to describe three different behaviors.
- An indicator showing results that change during the realtime bar, whether the script is using the security() function or not
- An indicator that uses future data not yet available on historical bars
- An indicator that uses a negative offset parameter in order to plot information on past bars
The repainting types that this lesson will address are the first two – results that change during the realtime bar while using the security() function, and accidentally accessing future data not yet available on historical bars.
Why Should Repainting Be Avoided?
There are two main reasons why repainting can be a problem.
The first is because it can affect the behavior of scripts being calculated on the realtime bar, which is where we make our trading decisions. So if a script is giving us bad information on the current bar, then it’s obviously going to negatively affect the results of our trades.
The second reason is because if you’re creating a strategy script (something I haven’t yet covered in these lessons), then a repainting script can give false backtesting results.
This could lead you to overestimating the efficacy of your strategy, and when you go to trade it on live markets you will not be getting anywhere close to the same results as you got during your backtesting.
Avoiding repainting depends on you – how you code your script, and how you set your alerts. To avoid repainting related to alerts, you simply need to set your alert to trigger once per bar close.
But to avoid repainting on your charts, it’s a little more complicated. And so in today’s lesson I’m going to demonstrate how to appropriately use the security() function to eliminate repainting.
Step 1: Get User Input
As always, this lesson begins by getting our user input.
In this case we need to know which higher timeframe our user wants to reference – and so we need to create an input variable that allows them to select a time resolution.
res = input(title="Timeframe", type=input.resolution, defval="D")
This line of code will create an option in the settings menu that will allow the user to select which higher timeframe they want to grab the highs and lows from.
That’s the only information we need our user to give us – so the start of our script should look something like this:
// @version=4 study("HTF High/Low", overlay=true) // Get user input res = input(title="Timeframe", type=input.resolution, defval="D")
Step 2: Create A Non-Repainting
Security() Function
The next step is to customize the security() function in such a way as to eliminate repainting.
To do this we’re going to create our own custom function. We haven’t done this yet in any of the previous lessons, but don’t worry – as usual when it comes to Pine Script, the TradingView developers have made it extremely simple.
All we need to do is declare our function name, followed by the operator “=>”, and then define what the function does.
So our next line of code will look something like this:
rp_security(_symbol, _res, _src) => security(_symbol, _res, _src)
Now we’ve created our own custom function called “rp_security”. So far, all this function does is mimic the exact same functionality as the standard inbuilt security() function (which repaints).
The 3 variables within rp_security()’s brackets are input variables. When we call this function in future lines of code, we need to give it those 3 variables so that it can calculate its result.
In this particular case, we’ve specified that our custom function requires a symbol input (_symbol), a time resolution input (_res) and a source input (_src).
The symbol input is the chart ticker ID (syminfo.tickerid), the timeframe resolution will be referencing our user-defined resolution variable (res), and the source input simply refers to a price series – open, high, low or close.
Eliminating Repainting
Now that we have our custom function defined, in order to make this new function prevent the chart from repainting, we need to adjust how it accesses the “source” input.
Instead of accessing the current bar’s source input (in our case, high or low), we want it to access the previous bar’s high or low. To achieve this we use a conditional operator that asks whether or not the current higher timeframe bar is a realtime bar (ie. has it closed yet?), and if it hasn’t, then we reference the previous bar instead of the current bar.
In code, that looks something like this:
// Create non-repainting security function rp_security(_symbol, _res, _src) => security(_symbol, _res, _src[barstate.isrealtime ? 1 : 0])
What this line of code is saying is: if the current bar we’re referencing is active (isrealtime), and has not yet closed, then reference the previous bar. Otherwise, reference the current bar.
And in case you’re wondering – yes, this could also be achieved by saying “[barstate.ishistory ? 0 : 1]”.
Now it’s important to remember that the security() function has “lookahead” set to false by default. So by default, on historical bars it will already be referencing the previous bar – it’s only on active HTF bars (ones that haven’t yet closed) that the script will “repaint”.
In the previous lesson I explained what the security() input variables “lookahead” and “gaps” do, so if you’re unsure what I mean by all of this, please go back and check that lesson out. It will make this lesson a lot easier to understand.
And again – if you’re still unsure what all this means, I encourage you to read through this official documentation and this example created by the Pine Script development team.
Step 3: Get The HTF High & Low
Now that we’ve created our “secure” non-repainting security() function, it’s time to put it to use!
We need to write two more lines of code – one that gets the higher timeframe high, and one that gets the higher timeframe low:
// Get HTF price data htfHigh = rp_security(syminfo.tickerid, res, high) htfLow = rp_security(syminfo.tickerid, res, low)
These two lines are identical except for the final parameter – this final parameter is the _src input variable (which is short for “source” – which is a candle’s open, high, low or close price).
For our purposes in today’s lesson we’re referencing the high and the low – so these two lines of code are saying “use our rp_security() function to retrieve the current symbol’s high and low from the specified timeframe (res)”.
And there we go!
We now have access to the Daily chart’s high and low in such a way that avoids repainting issues. Now, all that’s left is to actually draw them onto our chart.
Step 4: Draw Data
The final step is the easiest of all – we just need to plot the htfHigh and htfLow variables onto our charts.
I’ll leave it up to you to customize the visual elements of these drawings – for this lesson I’m going to leave it as simple as possible. All we’re going to do is color the lines red & blue and name them for the options menu:
// Plot data to chart plot(htfHigh, color=color.red, title="HTF High") plot(htfLow, color=color.blue, title="HTF Low")
And there we have it! We’re all done.
Now (hopefully) you understand what repainting is in relation to the security() function, and more importantly – how to avoid it.
Bonus Step: Create Alerts!
After completing this lesson it occurred to me that this indicator is perfect for using it to set alerts, so why don’t we add that functionality while we’re at it.
All we need to do is add an alertcondition() function to the end of the script and detect when the current intraday closing price exceeds the higher timeframe high or low.
We can do that like so:
// Trigger breakout alerts alertcondition(close > htfHigh or close < htfLow, title="HTF Breakout Alert!", message="HTF Breakout Alert For: {{ticker}}")
If you are going to use this script for its alert functionality, it would be best to set the alert to trigger only once.
And that brings me to the end of this lesson – see you in the next one!
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
// Created by Matthew J. Slabosz // www.zenandtheartoftrading.com // @version=4 study("HTF High/Low", overlay=true) // Get user input res = input(title="Timeframe", type=input.resolution, defval="D") // Create non-repainting security function rp_security(_symbol, _res, _src) => security(_symbol, _res, _src[barstate.isrealtime ? 1 : 0]) // Get HTF price data htfHigh = rp_security(syminfo.tickerid, res, high) htfLow = rp_security(syminfo.tickerid, res, low) // Plot data to chart plot(htfHigh, color=color.red, title="HTF High") plot(htfLow, color=color.blue, title="HTF Low") // Trigger breakout alerts alertcondition(close > htfHigh or close < htfLow, title="HTF Breakout Alert!", message="HTF Breakout Alert For: {{ticker}}")
Explained very well!!
Thanks, very clear to start coding!
Hi, you have explained it very well.
I am stuck in one problem. I want to read only one candle(9.45 am to 9.15 am) and create alert on high or below breakout of that candle. Any help will be very much appreciated. thanks
//@version=4 strategy(“P&F Scalper”, shorttitle=”P&F”, overlay=true) timeframe = input(’11’) box = input(‘Traditional’) boxsize = input(title=”% Boxsize”, type=input.float, defval= 2) reversal = input(7) pnf = pointfigure(syminfo.tickerid, ‘close’, box, boxsize, reversal) // pnf_open= security(pnf, timeframe , open, barmerge.gaps_off, barmerge.lookahead_on) // pnf_close= security(pnf, timeframe , close, barmerge.gaps_off, barmerge.lookahead_on) // Create non-repainting security function rp_security(_symbol, _res, _src, _boxsize, _reversal) => security(_symbol, _res, _src[barstate.isrealtime ? 1 : 0], _boxsize, _reversal) pnf_open= rp_security(pnf, timeframe , open, barmerge.gaps_off, barmerge.lookahead_on) pnf_close= rp_security(pnf, timeframe , close, barmerge.gaps_off, barmerge.lookahead_on) p1 = plot(pnf_open, title=”pnf_open”, color=color.green) p2 = plot(pnf_close, title=”pnf_close”,color=color.maroon) base = pnf_close> pnf_open? pnf_close: pnf_open p0 = plot(base, title=”base”, color=color.gray) fill(p0, p1,… Read more »
I used your logic. It still repaints can you help //@version=3 strategy(“P&F Scalp Strat”, shorttitle=”1m Scalp Strat”, overlay=true) timeframe = input(‘3’) box = input(‘Traditional’) boxsize = input(6, type=float) reversal = input(2) pnf_o = pointfigure(tickerid, ‘open’, box, boxsize, reversal) pnf_c = pointfigure(tickerid, ‘close’, box, boxsize, reversal) // Create non-repainting security function nrp_security(_symbol, _timeframe, _src) => security(_symbol, _timeframe, _src[barstate.isrealtime ? 1 : 0]) pnf_open= nrp_security(pnf_o, timeframe , open) pnf_close= nrp_security(pnf_c, timeframe , close) p1 = plot(pnf_open, title=”pnf_open”, color=green) p2 = plot(pnf_close, title=”pnf_close”,color=maroon) base = pnf_close> pnf_open? pnf_close: pnf_open p0 = plot(base, title=”base”, color=gray) fill(p0, p2, color=green, transp=80) fill(p0, p1, color=maroon, transp=80) entry() =>… Read more »
how to get this to not repainting
strategy(“P&F scalp strat V3″, shorttitle=”MEXlongOnly_strat V3″, overlay=true)
timeframe = input(‘3’)
box = input(‘Traditional’)
boxsize = input(5, type=float)
reversal = input(1)
pnf = pointfigure(tickerid, ‘close’, box, boxsize, reversal)
pnf_open= security(pnf, timeframe , open)
pnf_close= security(pnf, timeframe , close)
p1 = plot(pnf_open, title=”pnf_open”, color=green)
p2 = plot(pnf_close, title=”pnf_close”,color=maroon)
base = pnf_close> pnf_open? pnf_close: pnf_open
p0 = plot(base, title=”base”, color=gray)
fill(p0, p2, color=green, transp=70)
fill(p0, p1, color=maroon, transp=70)
entry() => (base > pnf_close)
exit() => (base > pnf_open)
alertcondition(entry(), title=’buy’, message=’buy!’)
alertcondition(exit(), title=’sell’, message=’sell!’)
strategy.risk.allow_entry_in(strategy.direction.long)
strategy.entry(“Long”, long=true, when=entry())
strategy.entry(“close”, false, when=exit())
I used this technic but my code is still repainting.