In TradingView, we can programmatically specify how many decimals the plotted values of a strategy should use. However, that setting is not always respected by TradingView. Why is that and how do we prevent that from happening?

In this article:

Setting a TradingView strategy’s precision with code

We can configure a TradingView strategy programmatically with the strategy() function. That function needs to be in the code of every strategy (Pine Script Language Tutorial, n.d.) and its title argument, which sets the strategy’s name, always needs to be given a value (TradingView, n.d.).

Another argument of that function is precision, and this one specifies the number of decimals plotted by the strategy (TradingView, n.d.). This optional argument accepts values of 0 and greater, and defaults to a value of 4 when omitted from strategy() (TradingView, n.d.). Furthermore, when we set precision to 0 then the strategy’s values display with thousands (‘K’) and millions (’M’) suffixes.

An example of setting a strategy’s precision programmatically is:

strategy(name="My example strategy", precision=2)

We take a closer look at the precision argument in setting the precision of a TradingView strategy. But as it turns out, TradingView does not always respect the number of decimals that we’ve configured. This happens whenever we plot the strategy on the chart’s instrument instead of displaying it in a separate subchart. In that case, our strategy will use the same number of decimals as the chart’s instrument – regardless of which precision we’ve configured.

Before we analyse that behaviour and look at two solutions, let’s discuss the programming example that we’re going to use as an exhibit of that odd TradingView behaviour.

Precision price scale: example of TradingView discrepancy

To see how an instrument’s price scale affects how many decimals the strategy uses when plotting its values, we create an example strategy that trades four Exponential Moving Averages (EMAs). This strategy is always in the market and goes long when the moving averages are aligned above each other, and opens a short position when the EMAs are all below each other.

The chart below gives a quick peek at how the strategy looks and the decimals it plots; after discussing the script’s code, we’ll look at the strategy’s behaviour in depth.

Example of the TradingView strategy with precision discrepancy highlighted
//@version=2
strategy(title="Quad EMAs", overlay=true, precision=5, calc_on_every_tick=true)

// Inputs
ema1Len = input(title="EMA 1", type=integer, defval=12)
ema2Len = input(title="EMA 2", type=integer, defval=24)
ema3Len = input(title="EMA 3", type=integer, defval=36)
ema4Len = input(title="EMA 4", type=integer, defval=48)

// Compute values
ema1 = ema(close, ema1Len)
ema2 = ema(close, ema2Len)
ema3 = ema(close, ema3Len)
ema4 = ema(close, ema4Len)

// Plot values
plot(series=ema1, linewidth=2, color=blue)
plot(series=ema2, linewidth=2, color=orange)
plot(series=ema3, linewidth=2, color=purple)
plot(series=ema4, linewidth=2, color=teal)

// Determine trading conditions
longCondition  = (ema1 > ema2) and (ema2 > ema3) and (ema3 > ema4)
shortCondition = (ema1 < ema2) and (ema2 < ema3) and (ema3 < ema4)

// Submit orders
if (longCondition)
    strategy.entry(id="Enter Long", long=true)

if (shortCondition)
    strategy.entry(id="Enter Short", long=false)

We start with the @version=2 comment. This specifies that the script should use the second version of Pine Script, which makes if statements possible (Pine Script Language Tutorial, n.d.) and we’re going to use those later on in the code.

Then we specify the strategy’s properties with the strategy() function. With the required title argument we set the name of the strategy and, with overlay=true, the strategy displays on the chart’s instrument (TradingView, n.d.). And to plot the strategy’s moving averages with 5 decimals, we set the precision argument to 5.

The last argument that we set is calc_on_every_tick. When this argument has a value of true, the strategy calculates on every real-time price update (TradingView, n.d.). That will make our strategy plot and update the EMAs on the last bar of the chart, instead of only updating the plots when the price bar closes.

Next we add several input options:

ema1Len = input(title="EMA 1", type=integer, defval=12)
ema2Len = input(title="EMA 2", type=integer, defval=24)
ema3Len = input(title="EMA 3", type=integer, defval=36)
ema4Len = input(title="EMA 4", type=integer, defval=48)

We create user-configurable input options with input(), and this function also returns the input’s current value (TradingView, n.d.). Here we put each of those returned values in a variable with the assignment operator (=). That makes it possible to refer the input’s current value later on in the script by using the variable.

The four input options are all numerical integer inputs, and these are made by setting the type argument of the input() function to integer (Pine Script Language Tutorial, n.d.). Other arguments that our input() function calls have in common are title and defval. With the first we specify the input’s name, and that name is placed before the input option in the script’s settings (see image further down below). And defval sets the input’s default value; that way the script can start calculating with the inputs’ values without us manually setting them.

We name our inputs “EMA 1” through “EMA 4” and give them default values of 12, 24, 36, and 48. We assign the values returned by input() to the ema1Len, ema2Len, ema3Len, and ema4Len variables. That way we can access their values later on in the script.

Then we calculate the moving averages:

ema1 = ema(close, ema1Len)
ema2 = ema(close, ema2Len)
ema3 = ema(close, ema3Len)
ema4 = ema(close, ema4Len)

We compute the exponential moving averages with ema(), a function that works on two arguments: a series of values and an integer that sets the moving average length in number of bars (TradingView, n.d.).

We base each EMA on the instrument’s closing prices (close) and their length with the input variables we’ve just made (ema1Len, ema2Len, ema3Len, ema4Len). We store those 12-bar, 24-bar, 36-bar, and 48-bar moving averages in the ema1, ema2, ema3, and ema4 variables to use later in the code.

Next up is plotting the moving averages:

plot(series=ema1, linewidth=2, color=blue)
plot(series=ema2, linewidth=2, color=orange)
plot(series=ema3, linewidth=2, color=purple)
plot(series=ema4, linewidth=2, color=teal)

The plot() function displays the values of its series argument, and does so with a line style by default (TradingView, n.d.). Here we set that argument to the variables that hold the value of the exponential moving averages.

The linewidth argument of plot() specifies the width of the plot, starting with a value of 1 as the default size (TradingView, n.d.). Here we set that argument to 2 so that each line is a bit bigger than the standard size. And since we’ve set the overlay argument of the strategy() function to true, the strategy is overlaid on the chart’s instrument and our EMAs won’t plot in a separate subchart.

After that we determine the strategy’s trading conditions:

longCondition  = (ema1 > ema2) and (ema2 > ema3) and (ema3 > ema4)
shortCondition = (ema1 < ema2) and (ema2 < ema3) and (ema3 < ema4)

We create two true/false variables here: longCondition and shortCondition. Both are set to a combination of three true/false expressions that are combined with the and logical operator. This operator returns true when the value on its left and the value on its right are true too; when one or both values are false, then and returns false too (Pine Script Language Tutorial, n.d.). Since we use that operator twice here to combine three true/false expressions, each expression has to be true before the combined result is true too.

The three expressions that set the value of the longCondition variable all check whether the current bar value of a particular moving average is greater than (>) another average. Specifically, we evaluate whether the 12-bar EMA is above the 24-bar average (ema1 > ema2), if that latter moving average is above the 36-bar EMA (ema2 > ema3), and whether that third moving average is greater than the last (ema3 > ema4).

When all three of those situations are the case, the EMAs nicely line up above each other and the longCondition variable is assigned true. But if even one situation doesn’t happen, then longCondition is set to false due to the use of the and logical operator.

The shortCondition variable is given a value similarly, except that now we check whether the moving averages are aligned below each other. That is, we evaluate whether the first EMA is less than (<) the second (ema1 < ema2), with the second below the third (ema2 < ema3), and that latter below the fourth (ema3 < ema4). When these three situations are indeed the case, shortCondition is set to true; otherwise, this variable is assigned false.

Next we use the longCondition and shortCondition variables to submit the strategy’s orders:

if (longCondition)
    strategy.entry(id="Enter Long", long=true)

if (shortCondition)
    strategy.entry(id="Enter Short", long=false)

Both if statements evaluate one variable and call the strategy.entry() function when that variable is true. That function opens a position with a market order by default and, if there’s already an open position in the other direction, reverse that position (TradingView, n.d.). Both strategy.entry() function calls use two arguments. With id we specify the order identifier, and that name displays on the chart and in the ‘Strategy Tester’ window. The long argument, when set to true, makes the function submit an enter long order while long=false has it open a short position (TradingView, n.d.).

The first if statement checks the value of the longCondition variable. With that variable currently being true, we call strategy.entry() to submit an enter long order (long=true) that’s named “Enter Long”. The other if statement processes shortCondition, and when that variable is true we have strategy.entry() submit an “Enter Short” order (long=false). Since our strategy doesn’t submit exit orders, we’re always in the market with either a long or short position.

Example: TradingView’s price scale and precision differences

The example script we discussed above has the following input options:

Input options of the TradingView example strategy

Before looking at how the strategy plots its values, let’s revisit its settings. We started the programming example with the strategy() function to set the strategy’s number of decimals to 5 (precision=5):

strategy(title="Quad EMAs", overlay=true, precision=5, calc_on_every_tick=true)

When we add the script to the Silver spot CFD from FXCM (which quotes in three decimals), our strategy looks like:

Example of the TradingView strategy with precision discrepancy highlighted

As we can see here, our EMAs display with just 3 decimals – and not the 5 we’ve specified with precision. So despite setting the precision to a specific value, our strategy ended up ‘inheriting’ the number of decimals used by the chart’s instrument. If we add the example strategy to other instruments, we see the same behaviour. For example, our EMAs display with 2 decimals on the chart of a stock that quotes in 2 decimals.

In the ‘Data Window’ of the Silver CFD chart, however, our strategy values do appear with 5 decimals:

TradingView's 'Data Window' with precision highlighted

We can have our example strategy plot its values with the same number of decimals that we set with the precision argument, though. For that we need to set the overlay argument of the strategy() function to false:

strategy(title="Quad EMAs", overlay=false, precision=5, 
     calc_on_every_tick=true)

Then we have to remove the strategy from the chart and then re-add it to the chart. The updated code now plots the EMAs with the right number of decimals:

Correct precision when plotting the TradingView strategy in a subchart

But this is of course not a real solution since we specifically wanted our EMAs to be overlaid on the instrument’s price chart and not below it. Especially with multiple scripts on the chart, there’s hardly room to plot each in its own subchart.

Luckily, there are two ways to have a strategy’s values plot with the correct number of decimals specified by the precision argument – and these don’t involve plotting in a subchart:

  • By changing the number of decimals shown on the instrument’s price scale, the plotted strategy values also change with it.
  • And if we attach the strategy to a different price scale, then the script plots its values with its own number of decimals.

Let’s take a look at both approaches below.

Workaround 1: Changing the precision of the chart’s price scale

The first approach that makes an overlaid strategy plot with the right amount of decimals is to change the instrument’s price scale. Since a strategy plotted on the instrument’s chart inherits the number of decimals used by that instrument, changing the instrument’s price scale affects how the strategy’s plots too.

If we look at the Silver CFD chart, the instrument’s values display with three decimals on the price axis and in the ‘Data Window’:

Precision difference when plotting in TradingView

To adjust the precision of those values, we need to change a setting called ‘Override Min Tick’. For that we first open the instrument’s settings by clicking on the gear icon ( ) that’s displayed to the right of the instrument’s name:

Opening the strategy settings window in TradingView

This brings up a window with several instrument settings. Here we select the ‘Style’ tab and there we can set the ‘Override Min Tick’ option to several values:

Changing the 'Override Min Tick' setting in TradingView

This ‘Override Min Tick’ setting changes the instrument’s default size (as in, the minimum price change). This way we can display the instrument’s prices with more (or less) decimals. And so, for instance, we can set the precision of the Silver CFD to 1/100,000 (from 1/1,000):

The TradingView 'Override Min Tick' setting changed

Since a strategy overlaid on the chart instrument uses the same precision as the instrument, an ‘Override Min Tick’ setting of 1/100,000 changes both the instrument’s precision and that of the strategy:

Correct precision of the TradingView strategy

This change also happens in the ‘Data Window’:

Correct precision in the TradingView strategy's 'Data Window'

This approach has a disadvantage too. When we want to plot the strategy’s values with a high precision, the instrument’s values also need to be set to a high precision – and in that case, the right price axis begins to take up a lot of room. For instance, here we’ve set the ‘Override Min Tick’ setting to a low value of 1/1,000,000:

Precision of the TradingView strategy set to a low value by hand

This may also make the instrument’s values itself harder to read. So let’s look at the second work around, which doesn’t intervene with the right price axis.

Workaround 2: Plotting the strategy’s values on a different price scale

Another approach that displays the plotted values of an overlaid strategy with the right amount of decimals is to make the strategy use a different price axis. With its own price axis, the strategy won’t ‘inherit’ the amount of decimals that the instrument uses.

So if we go back to our original programming example (which had a precision of 5), then displaying the strategy on the chart’s instrument didn’t show the EMAs with 5 decimals:

Example of the difference between specified and actual precision in TradingView

To have the strategy plot with its own number of decimals, it has to use its own price scale. To configure that, we first right-click on the strategy name on the chart and select ‘Scale Left’:

Having a TradingView strategy plot on a different price scale

Now the strategy’s values already show with a higher precision than the instrument, as long as we have its plots selected:

TradingView strategy plotted on the left price scale

But reading the strategy’s values well requires that we display the price axis that the strategy attached to. We do that with a right-click on the price scale and select ‘Left Axis’:

Enabling the chart's left price axis in TradingView

Now the strategy values plot clearly, and they show with the right amount of decimals that we configured with the precision argument of the strategy() function:

TradingView strategy plotting with the right number of decimals

With a separate price axis, the strategy’s values always display with the number of decimals that we’ve specified. That means when we change the chart to the US Dollar index (which doesn’t quote in fractional values), our EMA strategy still displays with 5 decimals:

TradingView strategy plotting with the correct amount of decimals

Summary

Each TradingView strategy needs to use strategy(), a function that configures several strategy properties. precision is one argument of that function, and specifies how many values display after the decimal point of plotted strategy values. This argument accepts values of 0 and greater, and with 0 it uses the thousands (‘K’) and millions (’M’) notation for large numbers. But when we plot the strategy’s values in the same area as where the chart’s instrument displays, then the strategy plots with the same number of decimals as the instrument. That can give the impression that the precision setting doesn’t work or is configured incorrectly. However, this is standard TradingView behaviour and can be avoided in two ways: by changing the precision of the chart’s instrument (so that the plotted strategy values change with it). Or by attaching the overlaid strategy to another price scale so that it’s free to use its own number of decimals.

Learn more:


References

Pine Script Language Tutorial (n.d.). Retrieved on February 24, 2016, from https://docs.google.com/document/d/1sCfC873xJEMV7MGzt1L70JTStTE9kcG2q-LDuWWkBeY/

TradingView (n.d.). Script Language Reference Manual. Retrieved on May 16, 2016, from https://www.tradingview.com/study-script-reference/