Tips And Tricks In Pinescript

Conclusion

I’am starting with the conclusion since i don’t plan to be on tradingview for some time. Pinescript is a really easy to use and learn language and i had a lot of fun writing all my indicators, i remember the first time i used it, it was a really nice day and i opened the script editor to edit the awesome oscillator indicator, i changed the sma to an ema, how cute, changing a letter, i was 16.

However we made a lot of progress in three years, i have often heard that i was a prolific, creative and talented indicator maker, and since my only goal in tradingview was to share and help people, i will leave all the tips and tricks related to pinescript i have to help you make great indicators.


Show the code

When publishing you can hide the code of your indicator, my first tips is to always show the code of your indicator/strategy except if you have a really good reason to hide it. Showing your code can help other people spot errors thus making you improve. If you think that your code is the holy grail and this is why you have to hide it i must tell you that there are a lot of researchers publishing papers that you can read about machine learning and other mega complex stuff applied to financial markets. At the end sharing is caring so share everything from your indicator !


Structure Your Code

Its common to make an indicator, save it, and start a new project from scratch, then if you come back to the saved indicator you can get lost, this happened with me several times. First create sections in your code, then delimit each section with a commentary, commentaries can be made in pine with //, i personally use //---- but you can use whatever you want like //ma section or //plots etc. The name of your variables can also be important, i’am conformable with an alphabet like variable declaration. Therefore you could structure you code like this :

study(…..)
length = input(14)
//----
a = …
b = …
c = …
//----
plot(…)


So try to find a structure style such that you will be confortable with :)


Making Different Types Of Filters in Pine

There are different types of filters, i explain each ones in my post Digital Filters And DSP . Here is the code to compute all common types of filters :

low-pass (lp) = lp(x)
high-pass (hp) = x - lp(x)
bandpass (bp) = lp(x - lp(x))
band-reject (br) = x - lp(x - lp(x))


with a simple moving average this give you :

low-pass moving average = sma(close, length)
high-pass moving average = x - sma(x, length)
bandpass moving average = sma(x - sma(x,length),length)
bandreject moving average = x - sma(x - sma(x,length),length)



This is a easy way to compute each filters types. You can use other filters types instead of the simple moving average.

Understand The Exponential Window Function

The exponential window function is my favorite equation and in my opinion one of the most elegant thing i had the chance to use. This window function is widely used and have the following form :

alpha*x + (1-alpha)*y

or

y + alpha*(x - y)

alpha is called a smoothing constant, when a = 0.5 you are just computing the average between x and y because a = 0.5 and 1 - a = 0.5, therefore you end up with : 0.5*x + 0.5*y = (x + y)/2. But the beauty of this function is that as long as a is greater than 0 and lower than 1 you will give more weight to x or y depending of a. There are a lot of indicators using this, the most well known being the exponential moving average.

Rescaling Inputs in A Certain Range

Scaling involve putting the input in a certain range such that the output is always equal or lower than a certain value and always equal or higher than another value, there are several ways to rescale values in a certain range, here are some ways to rescale values in Pine :

rsi(x, length) - rescale values in range of (100,0), higher length will converge the results toward 50, if you don’t want that use smooth values for x.

stoch(x,x,x,length) - rescale values in range of (100,0)

correlation(x,n,length) - rescale values in range of (1,-1)

sign(x) - rescale values in a range of (1,-1), 1 when x > 0 and -1 when x < 0.

x/highest(x,length) - values will always be equal or greater than 0 and equal or lower than 1, when x is high the output will be closer to 1.

lowest(x,length)/x - values will always be equal or greater than 0 and equal or lower than 1, when x is low the output will be closer to 1.

a = change(src,length)
b = abs(a)/highest(abs(a),length) * sign(a)
- rescale values in a range of (1,-1)

a/(a + b) - rescale values in a range of (1,0) if a > 0 or (1,-1) if a < 0 as long as a < (a + b)

(x - b)/(a - b) - rescale in a range of (1,0) if x > b and a > x.

see also :

Smoothed Delta's Ratio Oscillator


Japanese Correlation Coefficient


Karobein Oscillator


Use operations to scale in a desired range, when the rescaling is in a range (1,-1) use the following operations :

0.5 * rescaling + 0.5 if you want to rescale in a range of

(1 + rescaling) * (maximum/2) if you need a custom range


Use Recursion With The Exponential Window Function

Recursion is the process of using outputs as inputs for a function. Pine allow you to use outputs as inputs thanks to the nz() function. Lets use an example, imagine the following code with the version 2 of pinescript :

a = change(nz(a[1],close),9)

This code compute the difference between a and a 9 bars back, now when the code will make the first calculation you want a numerical value for a because a will be equal to na (non attributed), this is why you need to have an initial value (also called a seed) for a and this is what nz() do , attribute a value to a when a = na. By running the code above you will end up with a periodic results, so how can you use recursion with your indicators ? Here the following code example show you how :

alpha = 0.5
a = scale(close, length)
b = function(alpha*a+(1-alpha)*nz(b[1],a))


When you want to use recursion in a function you must first declare alpha, alpha can be any value as long as alpha is greater than 0 and lower than 1, then you must scale your input (here the closing price), this is done because depending on the price scale you could get different results when using recursion, when your rescaling range is low (1,0) your results could be more periodic than in a range like (100,0).

snapshot

function is the function you want to use, if you rescaled the input you should use a function that don’t plot in the chart, if your function is a simple moving average sma or another filter you won’t need to scale the input, however if you use function such as change, rsi, stochastic…or any other oscillator you will need to rescale the values in a certain range. Recursion can help you get smooth, predictive, periodic and reactive results. If you need adaptive results you can even make alpha a variable by rescaling something you want in a range of (1,0), for example :

alpha = scale(tr,length)
a = scale(close)
b = function(alpha*a+(1-alpha)*nz(b[1],a))


Create Adaptive Exponential Filters Easily

Adaptive moving averages are greats because market exhibit a dynamical behavior, so adaptivity is a good way to have really great filters. An easy way to have adaptive filters is by using exponential averaging, in short using the structure of an exponential moving average, the ema of x is calculated as follow in pine :

y = alpha*x + (1-alpha)*nz(y[1],x)

which can derived to :

y = nz(y[1],x) + alpha*(x - nz(y[1],x))

where alpha = 2/(length + 1)

As you can see we go back to the exponential window function and thanks to this function the ema is really efficient to compute. An adaptive filter will use a variable alpha instead of a constant one, so as long as 1 >= alpha >= 0 you will get an adaptive filter. If you want to adapt the filter to market volatility just rescale a volatility indicator like the atr in a range of (1,0), in pine this would look like :

alpha = scale(tr,length)
y = alpha*close + (1-alpha)*nz(y[1],close)



If you need alpha to decrease when length increase you can divide your rescaling by length, for example if you use the stochastic of the true range as alpha you can make :

alpha = stoch(tr,tr,tr,length)/100 / sqrt(length)
y = alpha*close + (1-alpha)*nz(y[1],close)



Create An Adaptive Simple Moving Average

In pine you can compute a simple moving average by using the sma() function :

sma = sma(x,length)

now imagine you want to use a non constant length, like for example the barssince() function, well you can’t because the sms function only work with constant integers, so how can you use non constant integers for calculating an adaptive sma ? Well you can derive the sma formula like this :

length = barssince(cross(close,vwap)) + 1
rsum = cum(close)
asma = (rsum - rsum[length])/length


Make sure that length is always greater or equal to 1, if length is a float use the round() function :

length = round(barssince(cross(close,vwap)) + 1)


Use Fixnan For Colors

When plotting an indicator with a plot fonction using a condition to change colors, you will have a different color if the indicator value is equal to its previous value, this image describe this case :

snapshot

with :

a = highest(50)
c = if a > a[1]
lime
else
if a < a[1]
red
plot(a,color=c)


Instead you can use a ternary operator and use fixnan to fix this issue, the plot code look like this :

plot(a,color=fixnan(a>a[1]?lime:a<a[1]?red:na))

snapshot

Its an efficient way to keep the previous color when the indicator is equal to its previous value.

Get Color Codes Easily

Having custom colors is great, those custom colors can be plated by using a color, those codes start with # and follow with letters and/or numbers, for example #006dff. You can go to websites showing the color codes for different colors but you can also use tradingview to get the color codes. In order to do that open the settings of an indicator and go to the plot section, then change the color of the plot by clicking on the color box, click on the color you want in the palette, if you need a custom color skip this step, after this you will see a + symbol above opacity, click on it, you will then see the code of your color at the top, if you need a custom color adjust the fader and the white circle position in order to select your color. Then copy the color code.

You can than then create a plot function and paste your color code, however you don’t copy the # so make sure to add it before pasting the code.

Create Zero-Lag Filters

When filtering you are loosing frequency information, in order to compensate for this loss the filter will have phase (lag). A simple way to reduce lag is to boost certain frequencies in the input before filtering, this can be done in pine in a lot of ways :

a = lp(x + change(x,length/2))
b = lp(x + hp(x)) which can be derived to lp(x) + bp(x)


where x is what you want to filter, with a simple moving average this give you :

a = sma(close + change(close,length/2),length)
b = sma(close + (close - sma(close,length)),length)


which can be derived to :

sma(close,length) + sma(close - sma(close,length),length)

If you want to reduce overshoots you can divide length by 2 for the boosting part

b = sma(close + (close - sma(close,length/2)),length)

You can also reduce lag with recursive filters with :

alpha = 2/(length + 1)
a = alpha*close+(1-alpha)*nz(a[1],close) + change(nz(a[1],close),length)/length


or

alpha = 2/(length + 1)
a = alpha*close+(1-alpha)*nz(a[1],close) + change(nz(a[1],close))/2


or even with the common double exponential smoothing method :

alpha = 2/(length + 1)
beta = a number between 0 and 1
a = alpha*close+(1-alpha)*(nz(a[1],close) + nz(b[1]))
b = beta*change(a) + (1-b)*nz(b[1])


when beta is closer to 1 the lag is reduced, a less common form that i use is :

alpha = 2/(length + 1)
a = nz(a[1],close) + alpha*(close - nz(a[1],x)) + alpha*(close - sma(close,length))


You can have a lot of fun by trying different things, but if you need a fast zero-lag filter just use :

ema(close + change(close, length/2),length)

Make Oscillators Easier To Read

If your you want your oscillator to be more constrained in a specific scale range you can use sigmoid functions to change the shape of the indicator, for example lets take the z-score, the inverse fisher transform allow you to scale the z-score in a range of (1,-1), here the code :

a = (close - sma(close,length))/stdev(close,length)
b = (exp(2*a) - 1)/(exp(2*a) + 1)


The fisher transform make you able to get back your z-score from b with :

c = 0.5*log((1 + b)/(1 - b))

There are tons of those functions, another example is the ArSinH function :

a = (close - sma(close,length))/stdev(close,length)
b = log(a + sqrt(pow(a,2) + 1))


The inverse fisher function work well if applied to oscillators in a range of (5,-5), here is a list of those with their inverse and their output scale en.wikipedia.org/wiki/Activation_function


Use Multiple Weights In Your Weighted Filters

A weighted filter have the form :

sma(close*b,length)/sma(b,length)

b is anything you want. But if you want to increase the effect of the weighting you can use more weights thus ending with :

sma(close*b*c*d*e,length)/sma(b*c*d*e,length)

If you want to increase the effect of one weigth, the volume for example, you can use the pow() function :

sma(close*pow(volume,pow),length)/sma(pow(volume,pow),length)

The higher pow the more effect the weight will have over the output, beware that large pow can create results going nuts. This method is a way to get adaptive filter as well.

Use Sympy Gamma For Formula Simplification

This tool will allow you to simplify your formulas, if i take the rsi formula :

rsi = 100 - 100/(1 + a/b)

Sympy will give you me a simplification :

rsi = 100*a/(a + b)

sympygamma.com/input/?i=1/exp(-x+++1)

Chill Out Your Recursion

When using recursion for making oscillators we have seen that it was important to scale your input first and control the amount of output/input the function will use by using the exponential window function, if you don’t feel like rescaling and controlling alpha for the averaging you can also use a more straightforward method that will allow you to use recursion thanks to smoothing, here the method :

a = sma(change(avg(close,nz(a[1],close)),length),length)

Smoothing allow you to skip the rescaling step, you can still control the output/input ratio with exponential averaging.

Take a Look at The Reactivity, Smoothness and Overshoot of Your Filter

In order to know the some characteristics of your filter you will need to use some tools, the first one is the step response, the step response of a filter is a filter using a step function as input, for example the code to see the step response of a simple moving average is :

a = n > 4000 ? 1 : 0
b = sma(a,length)

plot(a),plot(b)


The time it takes to the filter to get to the maximum value of the step function will tell you about its reactivity while seeing if the filter exceed the maximum value of the step function will tell you about its overshoot response, after an overshoot the filter can also go back under the step function, this is an undershoot, there are also tons of other things about the step response like ringing, but some only happen with recursive filter like the ema.

In order to check for smoothness against another filter you can use this code :

a = abs(change(filter1))
b = abs(change(filter2))


If you see that filter 1 have overall greater values than filter 2 it means that filter 1 smooth less.

Estimate A Gaussian Filter

You don’t really need fancy math’s to make a gaussian filter, the central limit theorem will help you estimate it just fine. A gaussian filter is a filter which impulse response is a gaussian function (bell shaped curve), a gaussian filter can be approximated with this code :

a = sma(sma(sma(sma(close,length/4),length/4),length/4),length/4)

In short the more sma function you add the better the estimation.

Some Other Tips

You can declare a float by using 0. instead 0.0

You can find the average highest frequency of a signal a with :

b = a > a[1] and a[1] < a[2] or a < a[1] and a[1] > a[2] ? 1 : 0
c = 1/(cum(b)/n) * 2


Learn all the shortcuts, some in mac :

cmd + enter = add to chart
cmd + i = new script
cmd + click on a function = display the reference manual of the function


Double click on one indicator line will display its settings window.

Know some maths, its not a requisite but it sure help.

And finally work and share with a group, its not something that i did but the truth is that it help a lot.

Final Words

I can’t cite all the person i want to thanks, there are to many, but without support its hard to improve, so thank you all. If this article helped you in any way then i’am more than satisfied. Oh and if you want to thanks and support me for all those years just use, change, improve and learn from my indicators as well as sharing your work or knowledge and have a lot of fun, that’s all i want.

Lets conclude this adventure with the memorable thanks for reading !
Beyond Technical AnalysispinepinescriptTIPStricks

Check out the indicators we are making at luxalgo: tradingview.com/u/LuxAlgo/
Also on:

Disclaimer