What do we mean by repainting?
Repainting is used to describe three different things, in what we’ve seen in TV members comments on indicators:
1. An indicator showing results that change during the realtime bar, whether the script is using the security() function or not, e.g., a Buy signal that goes on and then off, or a plot that changes values.
2. An indicator that uses future data not yet available on historical bars.
3. An indicator that uses a negative offset= parameter when plotting in order to plot information on past bars.
The repainting types we will be discussing here are the first two types, as the third one is intentional—sometimes even intentionally misleading when unscrupulous script writers want their strategy to look better than it is.
Let’s be clear about one thing: repainting is not caused by a bug; it is caused by the different context between historical bars and the realtime bar, and script coders or users not taking the necessary precautions to prevent it.
Why should repainting be avoided?
Repainting matters because it affects the behavior of Pine scripts in the realtime bar, where the action happens and counts, because that is when traders (or our systems) take decisions where odds must be in our favor.
Repainting also matters because if you test a strategy on historical bars using only OHLC values, and then run that same code on the realtime bar with more than OHLC information, scripts not properly written or misconfigured alerts will alter the strategy’s behavior. At that point, you will not be running the same strategy you tested, and this invalidates your test results, which were run while not having the additional price information that is available in the realtime bar.
The realtime bar on your charts is only one bar, but it is a very important bar. Coding proper strategies and indicators on TV requires that you understand the variations in script behavior and how information available to the script varies between when the script is running on historical and realtime bars.
How does repainting occur?
Repainting happens because of something all traders instinctively crave: more information. Contrary to trader lure, more information is not always better. In the realtime bar, all TV indicators (a.k.a. studies) execute every time price changes (i.e. every tick). TV strategies will also behave the same way if they use the calc_on_every_tick = true parameter in their strategy() declaration statement (the parameter’s default value is false). Pine coders must decide if they want their code to use the realtime price information as it comes in, or wait for the realtime bar to close before using the same OHLC values for that bar that would be used on historical bars.
Strategy modelers often assume that using realtime price information as it comes in the realtime bar will always improve their results. This is incorrect. More information does not necessarily improve performance because it almost always entails more noise. The extra information may or may not improve results; one cannot know until the code is run in realtime for enough time to provide data that can be analyzed and from which somewhat reliable conclusions can be derived. In any case, as was stated before, it is critical to understand that if your strategy is taking decisions on realtime tick data, you are NOT running the same strategy you tested on historical bars with OHLC values only.
How do we avoid repainting?
It comes down to using reliable information and properly configuring alerts, if you use them. Here are the main considerations:
1. If your code is using security() calls, use the syntax we propose to obtain reliable data from higher timeframes.
2. If your script is a strategy, do not use the calc_on_every_tick = true parameter unless your strategy uses previous bar information to calculate.
3. If your script is a study and is using current timeframe information that is compared to values obtained from a higher timeframe, even if you can rely on reliable higher timeframe information because you are correctly using the security() function, you still need to ensure the realtime bar’s information you use (a cross of current close over a higher timeframe MA, for example) is consistent with your backtest methodology, i.e. that your script calculates on the close of the realtime bar. If your system is using alerts, the simplest solution is to configure alerts to trigger Once Per Bar Close. If you are not using alerts, the best solution is to use information from the preceding bar. When using previous bar information, alerts can be configured to trigger Once Per Bar safely.
What does this indicator do?
It shows results for 9 different ways of using the security() function and illustrates the simplest and most effective way to avoid repainting, i.e. using security() as in the example above. To show the indicator’s lines the most clearly, price on the chart is shown with a black line rather than . This indicator also shows how misusing security() produces repainting. All combinations of using a 0 or 1 offset to reference the series used in the security(), as well as all combinations of values for the gaps= and lookahead= parameters are shown.
The close in the call labeled “BEST” means that once security has reached the upper timeframe (1 day in our case), it will fetch the previous day’s value.
The gaps= parameter is not specified as it is off by default and that is what we need. This ensures that the value returned by security() will not contain na values on any of our chart’s bars.
The lookaheadsecurity() to use the last available value for the higher timeframe bar we are using (the previous day, in our case). This ensures that security() will return the value at the end of the higher timeframe, even if it has not occurred yet. In our case, this has no negative impact since we are requesting the previous day’s value, with has already closed.
The indicator’s Settings/Inputs allow you to set:
- The higher timeframe security() calls will use
- The source security() calls will use
- If you want identifying labels printed on the lines that have no gaps (the lines containing gaps are plotted using very thick lines that appear as horizontal blocks of one bar in length)
For the lines to be plotted, you need to be on a smaller timeframe than the one used for the security() calls.
Comments in the code explain what’s going on.
I am not 100% sure, but it appears in my case the alternate version works well even with lookahead_on, at least on the realtime bar I cannot see any repaint.
I can imagine this is because on realtime bars, the alternate version is basically equivalent to your preferred method?
Can you pl clarify the foll observations w.r.t higher time frame data values ?
Eg: Chart time frame is 30m ; Higher Time frame is 120m.
tf_High = security(syminfo.tickerid, "120", high, barmerge.lookahead_on, barmerge.gaps_off)
We get "na" values for tf_High, and every 120m, we get a valid value.
// Use barmerge OFF
tf_High = security(syminfo.tickerid, "120", high, barmerge.lookahead_off, barmerge.gaps_off)
No na values. All values are valid.
gaps off/on has no affect.
Not clear why this behavior. Can you pl clarify the above behavior and how one could reliably use higher time frame data - given that in the recommended way of use, NA is returned?
tf_High = security(syminfo.tickerid, "120", high, barmerge.lookahead_on, barmerge.gaps_off) plot(tf_High) // Use barmerge OFF tf_High = security(syminfo.tickerid, "120", high, barmerge.lookahead_off, barmerge.gaps_off) // No na values. All values are valid.
Yes. The code is just that. The full code for the study is below.
//@version=4 study(title="HTF-Check") // tf_High = security(syminfo.tickerid, "120", high, barmerge.lookahead_on, barmerge.gaps_off) plot(tf_High) tf_Low = security(syminfo.tickerid, "120", low, barmerge.lookahead_off, barmerge.gaps_off) plot(tf_Low)
//@version=4 study("HTF-Check2", "", true) tf_High = security(syminfo.tickerid, "120", high, barmerge.gaps_off, barmerge.lookahead_on) plot(tf_High) tf_Low = security(syminfo.tickerid, "120", low, barmerge.gaps_off, barmerge.lookahead_off) plot(tf_Low)