Anyone who has coded in pine-script will no doubt agree that debugging can be a pain in the backside. Without the ability to print to the terminal, we are forced to plot anything and everything we wish to inspect. This process can be even more laborious if the variables that you are plotting work on different scales. The result is often a long exercise of commenting and uncommenting different plots.
With that in mind, this article aims to cover the basics of debugging along with some practical examples and a few tips that might just make your life a little easier.
The Basics
Since we are not able to print to the terminal, our only option for inspecting variables is to plot everything we need to check. Over the next few paragraphs, we will cover the basics of plotting each type of variable and then move onto some tips and general advice.
Floats and Ints
Floats and integers probably need no introduction. They are the bread and butter of plotting. However, in order to cover the debugging topic completely, at least a couple of sentences should be written.
To plot a float or integer, just stick it inside a plot()
call as the first parameter and you are good to go. e.g. plot(myint)
.
Note that plotting floats and integers is not completely plain sailing. Although any float or integer value can be plotted to the chart, you might run into scaling issues. What do I mean by “scaling issues”? When you plot an integer that has a value of 8000 alongside a float that has a value of 0.5, you are unlikely to be able to see clearly what either of them are doing. This is something we will look at in a bit more detail later.
Boolean Values
Boolean variables that are variables that are either true
or false
. If you try to plot a boolean value directly in the plot()
function you will receive the following error:
Add to Chart operation failed, reason: line 130: root_data expression of plot_11 has invalid type: series__bool expected series
In order to debug a boolean value, we need to first convert it to a valid type. One method of doing this is to use a ternary conditional operator to convert it to a float for plotting. Take the following example where we assume x
is a variable that is storing a true
orfalse
value. (e.g x = false
)
x_int = x ? 1 : 0
This line creates a new variable calledx_int
checks whether it is true. If it is, 1 is stored to x_int
. If not, then the value must be false (because it certainly is not true!) and so 0 is stored to x_int
. Now when we plot x_int
usingplot(x_int)
we are then easily able to see every time x is true in the time series.
Alternatively, you can also do this inside directly the plot without creating a x_int
variable: plot(x ? 1 : 0)
Strings
Strings should largely be debugged in the same way as boolean values. However, before we move onto some debugging examples, allow me to take you down a side track on a loosely related topic.
If you have ever tried to plot a string using the text
variable in one of the plots which supports it, (e.g plotshape()
, plotchar()
etc) you might have spent a lot of time “debugging” strings. Tradingview provides a tostring()
function that makes it look like you could convert the most recent series value to a string and plot it only when certain conditions are met. That would be incredibly useful! For example, let’s say you are plotting on an intra-day chart but would like to know what most recent daily ATR value is. If you directly plot the complete ATR values on the main chart, it could end up being cluttered and run into scaling issues. In my opinion, it would be cleaner to just plot the values above the bar at regular intervals. Unfortunately, at the time of writing, you cannot do this! Or, I have not found the magic source at least. The plotting functions only allow constant strings (strings that never change / or are not dynamically created).
To help visualize what I am talking/ranting about, take a look at the following chart:
Wouldn’t it be nice and clean to plot 0.0059
below the words ATR? For those interested, the chart above was created with the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//@version=3 study("Daily ATR", overlay=true) atr = security(tickerid, "D", atr(7)) is_newbar(res) => t = time(res) change(t) != 0 // Track new bar days nb = is_newbar("D") plotchar(nb, location=location.abovebar, text="ATR") //plotchar(nb, location=location.abovebar, text=tostring(atr)) |
If take the code above and uncomment the last plotline, you will receive the following error:
Add to Chart operation failed, reason: argument "text" accepts only constant string values
If any readers out there know a workaround or something I have missed, I would LOVE to know. Get in touch!
Moving off the beaten track….
Admittedly, the little detour above might not tell you how to debug a string but I hope it might just save you some head-scratching time. Now let’s get back to the main topic.
As mentioned above, debugging strings is very similar to debugging Boolean values. We must first convert the string to a value which can be plotted. Again you can use a ternary conditional operator to create a plot-able value. In general, you will not run into many scenarios where debugging strings is needed. However, it can be useful if you use the options parameter in theinput()
function. This is especially the case if your code following needs to make decisions based on the selected option.
The following code will perform a check on which option has been selected then change the plot value and colour accordingly:
1 2 3 4 5 6 7 8 |
//@version=3 study("Daily ATR", overlay=false) opts = input(defval='Hammer', title='Operation Mode', options=["Hammer", "Hanging", "Engulfing"]) operation_mode = opts == 'Hammer' ? 1 : opts == 'Hanging' ? 2 : opts == 'Engulfing' ? 3 : 0 plot_color = opts == 'Hammer' ? green : opts == 'Hanging' ? red : opts == 'Engulfing' ? purple : na plot(operation_mode, color=plot_color, linewidth=2) |
Making life a little easier
Now we have the basics down, let’s move onto some topics and general plotting advice that can make your life a little easier.
Plotting Styles
When debugging lots of different values, it can be difficult to easily recognise which line represents which variable. Especially if they are all plotted as simple blue line charts (the default plot values). Using different plotting styles and options can help some of your results stand out from the crowd.
- Plotshape() and Plotchar(): Are a good option when you have events that happen infrequently. Like in the ATR example above, they help mark events in a clutter-free way.
- Background Coloring: Often overlooked, colouring the background can be a great way to indicate a current state. For example, if you are plotting to the main chart but want to know when we are trading in overbought or oversold conditions.
Background Example
Let’s take the example above and turn in into code. The code below will change the background colour of the main chart when the RSI indicator is overbought or oversold.
1 2 3 4 5 6 7 8 |
//@version=3 study("RSI Backgound", overlay=true) rsi = rsi(close, 14) bcol = rsi > 70 ? red : rsi < 30 ? green : na bgcolor(bcol) |
And the result on the charts:
Traffic Lights
If you have complex entry/exit criteria filled with different filters and confirmations, it can be difficult to identify why your script did or didn’t do something. For such strategies, I tend to employ a traffic lights system that provides the status of each check. When your script doesn’t enter, it becomes immediately obvious which entry check or filter is holding it back.
Here is a simple example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
//@version=3 strategy("Traffic Lights", overlay=false) sma_check = close > sma(close, 14) vol_check = volume > (volume[1] * 2) close_check = close > open sma_plot_col = sma_check ? green : red vol_plot_col = vol_check ? green : red close_plot_col = close_check ? green : red longCondition = sma_check and vol_check and close_check if(longCondition) strategy.entry("Long", strategy.long) shortCondition = not sma_check and not vol_check and not close_check if(shortCondition) strategy.entry("Short", strategy.short) plotshape(2, title='SMA Check', color=sma_plot_col, style=shape.circle, location=location.absolute) plotshape(4, title='Vol Check', color=vol_plot_col, style=shape.circle, location=location.absolute) plotshape(6, title='Close Check',color=close_plot_col, style=shape.circle, location=location.absolute) hline(1, linestyle=solid) hline(3, linestyle=dashed) hline(5, linestyle=dashed) hline(7, linestyle=solid) |
The code above has three simple checks for the purposes of the example. It will check if we have closed above an SMA value, whether the volume has increased and whether we have closed up or down. The following image shows how it looks on a chart.
Now you can easily see on each bar which test passed or failed. You might argue that it is still hard to tell without memorizing which light is for which test. That is true. However, remember that this is just an example, you can change the shapes, colors, styles etc to make each test more memorable. In addition, even if you do not know which line is which, you can easily see that the first line failed and take a look at the line title.
I often find that when I use this method, I avoid debugging completely. This is because we may often think the code “should” have done something but after reviewing the check that failed, we quickly realize it is correct and move on.
One Keyboard Shortcut To Rule Them All
If you have lots of test plots that you do not want to display all of the time, then knowing this simple keyboard shortcut can significantly speed up the time spent commenting and uncommenting. It is the multiline comment! Highlight a large chunk of code then press CTRL+/
(or Option+/
for mac users). This will comment out the whole block of highlighted text. A real time saver when switching from test plots to your indicator plots.
Scalability
As alluded to throughout the article, the final thing we should be wary of when debugging is the scale that each line takes. We should adjust our plotting accordingly. For example, if you are plotting the results of a boolean test (checking if the result was true or false) with 0
and 1
and then plot it on the same main chart as a bitcoin, you are going to have a visibility issue.
In such scenarios there a few options we can take:
- Rescale the boolean to something more appropriate. For example, you could use the
high
andlow
values so that it moves from high to low in line with price. (e.g.x_int = x ? high : low
) - Plot using a different scale. (Using the scale left, right or no scale options). This can unlink the indicator from the price scale.
- Uncomment plots (if any) designed for the main chart and then plot the text plots to a subplot (
overlay=false
) for the purpose of testing.
The image above shows an example of plotting an RSI over the main chart using scale.none
. The full code follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//@version=3 study(title="Stochastic RSI", shorttitle="Stoch RSI", overlay=true, scale=scale.none) smoothK = input(3, minval=1) smoothD = input(3, minval=1) lengthRSI = input(14, minval=1) lengthStoch = input(14, minval=1) src = input(close, title="RSI Source") rsi1 = rsi(src, lengthRSI) k = sma(stoch(rsi1, rsi1, rsi1, lengthStoch), smoothK) d = sma(k, smoothD) plot(k, color=blue) plot(d, color=orange) h0 = hline(80) h1 = hline(20) fill(h0, h1, color=purple, transp=80) |
Enough for Today
This article was originally intended to only cover debugging but grew in something slightly different as it was being crafted. Once you become familiar with the basics of converting everything to a float or integer, debugging becomes largely a question of taste, preferences and plotting. I think that because of this, half the article covers the various plotting options.
At any rate, I hope the article is useful. Happy debugging!
Dear ???,
I am trying to build a dynamic trend line indicator using ONLY the two most recent pivot points. Currently i am working with pivotpoint[1] and pivotpoint[0], but the program does not discard the third most recent pivotpoint once a new pivotpoint is formed. Can you help me?
Hi Mats,
Thanks for getting in touch. It is a bit hard to say without seeing the code. I am having trouble visualizing the issue where the program is not discarding the third most recent pivot point.
Thanks a ton, that really helps me a lot. I really hope they make debugging easier soon, with console output per bar, plotting dynamic text values, etc. Your solutions will greatly help in the mean time.
Hello, I am trying to plotchar but it is not letting me put the plotchar inside an if statement so what kind of scripting language this is? and also I mad a coy of plotchar in many lines with all the possible conditionals and it worked but I got tones of 0.00000 0.00000 on top where the description is. any ideas of how to get rid of the 0.0000? Thanks
Hi,
Do you have any tips on how to select prices only for X bars back in time? I can’t find a way to describe a window of interrest.
Btw, nice descriptions!
Hey; thanks for the info, this was really helpful! I got several ideas from this article.
Another method I use for making it easy to work with debugging plots is to use an input statement to enable turning them on and off in the settings window, instead of commenting the lines.
debug=input(false,title=”Show Debug Plots?”)
…
plot(debug ? var_1 : na, color=red, linewidth=3, title=”var_1 (plus any descriptive text)”)
…
Cheers !
Pinescript and trading concepts explained really well. I truly appreciate it. You have a gift.
I too was trying to find a work around to plot the ATR values but could not overcome the same limitations you stated above. Instead I went to plot lines (hline) for the high and low of the ATR. This does the job kind of nicely.
BTW, what is your TradingView handle? I would very much like to see your scripts, I learnt plenty of tricks and good coding practice from your examples. Thanks.
Version 4 of the compiler provides a way to print some text as a label:
//@version=4
study(“Print Text”, overlay=true)
x = bar_index
y = close
txt = tostring(close)
label.new(x, y, txt)
Yeah, absolutely! Nice comment.
This post was created some time ago before v4. It looks like a follow up is needed in the not too distant future.
Hi there,
Any ideas how to run this code in Pine Script v4? I especially have the problem to plot a shape at a certain position in another script of mine. I got the same error message as with your code.
line 27: Undeclared identifier ‘dashed’;
line 28: Undeclared identifier ‘dashed’;
line 29: Undeclared identifier ‘solid’;
line 300: Cannot call ‘plotshape’ with arguments (style=const string, size=const string, location=const string); available overloads: plotshape(series[bool], const string, input string, input string, series[color], input integer, series[integer], const string, series[color], const bool, const string, input integer, const integer, string) => void; plotshape(fun_arg__, const string, input string, input string, fun_arg__, input integer, series[integer], const string, fun_arg__, const bool, const string, input integer, const integer, string) => void;
line 302: Cannot call ‘plotshape’ with arguments (text=string); available overloads: plotshape(series[bool], const string, input string, input string, series[color], input integer, series[integer], const string, series[color], const bool, const string, input integer, const integer, string) => void; plotshape(fun_arg__, const string, input string, input string, fun_arg__, input integer, series[integer], const string, fun_arg__, const bool, const string, input integer, const integer, string) => void
Here there. In v4, you need to change the values used in plotting.
Take a look at the docs by CTRL + Clicking on a plot type to see them all.
For example,
green
would becomecolor.green
in v4.dashed
becomeshline.style_dashed
and so on.
is it also possible to use scale.none on only one element of a larger script? Because I have a line I would print under the chart (sticky to bottom maybe) while having some other part of the same script as background for the chart.