PineCoders Squawk Box
3.85K subscribers
60 photos
166 links
News & Tips on TradingView's Pine programming language
Download Telegram
💪 #tip
Functions and built-ins used in `if` or ternary (`?`) blocks

An important change to the way conditional statement blocks are evaluated was introduced with v4 of Pine. Many coders are not aware of it or do not understand its implications. This User Manual section explains the change and provides a list of exceptions for functions/built-ins which are NOT affected by the constraints. We'll explain what's happening here, and how to avoid the problems caused by code that does not take the change into account.

This is what's happening:
🔷 1. Starting in Pine v4, both blocks of conditional statements are no longer executed on every bar. By "both blocks", we mean the part executed when the conditional expression evaluates to true, and the one (if it exists) to be executed when the expression evaluates to false.
🔷 2. Many functions/built-ins need to execute on every bar to return correct results. Think of a rolling average like sma() or a function like highest(). If they miss values along the way, it's easy to see how they won't calculate properly.

This is the PineCoders "If Law":
🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸 🔸🔸🔸
Whenever an if or ternary's (?) conditional expression can be evaluated differently bar to bar, all functions used in the conditional statement's blocks not in the list of exceptions need to be pre-evaluated prior to entry in the if statement, to ensure they are executed on each bar.
🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸

While this can easily be forgotten in the creative excitement of coding your latest idea, you will save yourself lots of pain by understanding and remembering this. This is a major change from previous versions of Pine. It has far-reaching consequences and not structuring code along these lines can have particularly pernicious consequences because the resulting incorrect behavior is sometimes discrete (appearing only here and there) and random.

To avoid problems, you need to be on the lookout for 2 conditions:
🔷 Condition A)
A conditional expression that can only be evaluated with incoming, new bar information (i.e., using series variables like close). This excludes expressions using values of literal, const, input or simple forms because they do not change during the script's execution, and so when you use them, the same block in the if statement is guaranteed to execute on every bar. [Read this if you are not familiar with Pine forms and types.]
🔷 Condition B)
When condition A is met, and the if block(s) contain(s) functions or built-ins NOT in the list of exceptions, i.e., which require evaluation on every bar to return a correct result, then condition B is also met.

In this FAQ & Code entry you will see an example where an apparently inoffensive built-in like vwap is used in a ternary. vwap is not in the list of exceptions, and so when condition A is realized, it will require evaluation prior to entry in the if block. You can flip between 3 modes: #1 where condition A is fulfilled and #2 and #3 where it is not. You will see how the unshielded value ("upVwap2" in the thick line) will produce incorrect results when mode 1 is used.
💪 #tip
Books
We have a short list of books on trading systems and techniques here:
https://www.pinecoders.com/books/
💪 #tip
How to format values to tick precision with `tostring()`
We have a very useful new f_tickFormat() function that does this in our FAQ. Kudos to DayTradingOil for the original idea.
🐞⚔️ #bugfix
fill()
There was a bug with the fill() function where if you plotted a single value surrounded by na values, it could not be filled. The fill shown here would not appear before. Now it does.
https://www.tradingview.com/x/wxstnMDz/

💪 #tip
Keep in mind that to prevent fills between plots you have 2 options:
1. Plot na on one of the 2 plots or both.
2. Set the fill's color to na conditionally.
💪 #tip
Ever wonder how the Strategy Tester values are calculated? This doc explains it all.
💪 #tip
When using security() and you want to avoid repainting, remember that it's critical to use two techniques TOGETHER:
1. Offset the series with [1]
2. Use lookahead = barmerge.lookahead_on

noRepaintAndNoFutureData  = security(syminfo.tickerid, "D", close[1], lookahead = barmerge.lookahead_on)

We see code in scripts that uses lookahead on but doesn't offset the series, which produces 2 adverse effects:
a) Future data is used on historical bars, which let's your script cheat and misleads traders, as there is no way your code can reproduce that behavior in realtime, because there are no future bars to cheat with then.
b) The value will repaint in realtime.

//@version=4
study("", "", true)
noRepaintAndNoFutureData = security(syminfo.tickerid, "D", close[1], lookahead = barmerge.lookahead_on)
repaintsAndUsesFutureData = security(syminfo.tickerid, "D", close, lookahead = barmerge.lookahead_on)
plot(noRepaintAndNoFutureData, "Good", color.green, 2)
plot(repaintsAndUsesFutureData, "Bad", color.red, 2)

https://www.tradingview.com/x/hkfEyExW/
💪 #tip
To repaint or not to repaint?
Traders ask script authors all the time if our scripts repaint, as if it was something inherently bad, which it isn't. The decision to code a repainting or non-repainting signal is yours to make, and should be based on your design, your calculations, what features you want to offer users of your script, and how you intend alerts to be used with your script. The best option is often to provide users with a Repaint/No Repaint choice, but that is not always possible.

Note that a simple RSI like:
r = rsi(close, 14)
plot(r)

is a repainting signal, as the value of r will vary during the realtime bar.

Also note that if your users ask for a non-repainting version of that plot, then you will need to plot a late signal, and so users should realize this. They can't have their cake and eat it too: non-repainting signals are by definition late, like this:
r = rsi(close, 14)[1]
plot(r)

or this equivalent code:
r = rsi(close[1], 14)
plot(r)

or this one:
r = rsi(close, 14)
plot(r[1])

Which version you will use will depend on your needs and the code's context.
💪 #tip
When using security(), the way we achieve repainting or non-repainting needs to take into account the context of that function's call. These are your 2 options:

//@version=4
study("", "", true)
repaint = security(syminfo.tickerid, "D", close)
noRepaint = security(syminfo.tickerid, "D", close[1], lookahead = barmerge.lookahead_on)
plot(repaint, "repaint", color.green, 8, transp = 70)
plot(noRepaint, "noRepaint", color.green, 2)


Again, it is critical NOT to mix up using no offset to the series as in the repaint case AND using lookahead = barmerge.lookahead_on, as this will produce lookahead bias by using future data.

https://www.tradingview.com/x/H0gjYIDP/
💪 #tip
Why do the OHLC built-ins sometimes return different values than the ones shown on the chart?
See the answer in our new FAQ entry.
💪 #tip
See our FAQ entry on how to split a string into characters:
https://www.pinecoders.com/faq_and_code/#how-can-i-split-a-string-into-characters
💪 #tip
Reminder
This is the complete list of functions that support a "series int", so a dynamic value, as the argument to their length parameter:
https://www.pinecoders.com/faq_and_code/#can-i-use-a-variable-length-in-functions
💪 #tip
You can access the Pine Script Editor from anywhere, including on phones, by using this link:
https://www.tradingview.com/pine/
💪 #tip
The "Strategy" section of the User Manual was vastly expanded. It contains much more details and code examples:
https://www.tradingview.com/pine-script-docs/en/v5/concepts/Strategies.html
💪 #tip
Better late than never ) We added a usrman page on matrices:
https://www.tradingview.com/pine-script-docs/en/v5/language/Matrices.html