In TradingView we can configure several strategy settings with code, including when and how often the script calculates. What are the benefits and drawbacks of calculating with every real-time price update, and how do we enable (or disable) this feature programmatically?

In this article:

Setting a strategy’s calculation frequency with code

The strategy() function programmatically configures a TradingView strategy (TradingView, n.d.). We need to add this function to every strategy script (Pine Script Language Tutorial, n.d.), and its title argument (which sets the strategy’s name) always needs to be set too (TradingView, n.d.).

Another argument of that function is calc_on_every_tick. When this argument is set to true, the strategy calculates itself with every real-time tick (TradingView, n.d.). The default value of calc_on_every_tick is false, and in that case the strategy only calculates on the close of each price bar.

To have the strategy compute on every real-time price update, we set the calc_on_every_tick argument like this:

strategy(title="My example strategy", calc_on_every_tick=true)

The advantage of calculating with every tick is a greater speed. Let’s say we want to scale into a position with 3 orders. This typically requires three closed price bars, since on each bar’s close the strategy calculates and generates one of the orders. But with calc_on_every_tick=true, it might only take seconds to generate 3 orders (depending on how active our instrument trades at that time). There’s also a risk management benefit here; with calc_on_every_tick enabled the strategy doesn’t have to ‘wait’ till the bar closes to take action, but can manage the position with every price update.

How calc_on_every_tick affects the strategy’s calculations depends on the kind of price data (Pine Script Language Tutorial, n.d.; TradingView, n.d.):

  • On historical data, the strategy calculates on the price bar’s close regardless of whether calc_on_every_tick is enabled or disabled. (We can, however, have a strategy calculate more than once on historical bars with the calc_on_order_fills argument, but that feature is distinct from calc_on_every_tick.)
  • On real-time data, the strategy calculates with every price update (when calc_on_every_tick=true) or only on bar close (when calc_on_every_tick is set to false or missing from the strategy() function).

Besides this different behaviour based on the kind of price data, enabling calc_on_every_tick affects the strategy in other ways too. Let’s take a closer look at that.

Tips for strategies that calculate with every real-time tick

So with calc_on_every_tick set to true, the strategy becomes quicker and reacts to price movements happening inside the price bar. This advantage does have several important consequences, but for backtesting and order generation:

  • Most importantly, the backtest results when we use calc_on_every_tick=true do not indicate how the script actually performs during real-time trading. Because with that argument enabled, the strategy calculates with every real-time price update – but on historical bars, it only calculates once per bar, on the bar’s close (Pine Script Language Tutorial, n.d.). This calculation difference can have such a big impact that it seems we’re testing two different strategies when we compare historical performance to real-time results. As a consequence, calc_on_every_tick=true strategies can only be tested on real-time data.
    • This mismatch between real-time and historical performance can happen when, during an intra-bar calculation, our order condition is valid – but when the bar closes, the order condition is invalid. In that case we won’t see the trade on the historical bar, but in real-time the strategy would have generated the order. This can, for instance, happen when we trade when two moving averages cross each other. Such a cross can happen repeatedly during the price bar, but doesn’t have to be the case when the bar closes. (Another example is in the programming example below.)
  • A strategy with calc_on_every_tick enabled can perform hundreds of calculations per real-time bar, and so can submit a lot of orders quickly after each other. Important considerations for this are:
    • Positions in TradingView are opened with the strategy.entry() and strategy.order() functions, but only the first of these takes the strategy’s pyramiding limit into account (TradingView, n.d.). This means we can open a position with strategy.order() that’s much bigger than we intended and, with calc_on_every_tick=true, this can happen in a short period of time. This makes it even more important to always check the strategy’s current position size – which is returned by the strategy.position_size variable (TradingView, n.d.) – before submitting orders with strategy.order().
    • TradingView evaluates order conditions and risk management settings when the order generates, not when it fills (Pine Script Language Tutorial, n.d.). Say our strategy is flat and we use calc_on_every_tick set to true to quickly send 10 orders after each other. Our strategy’s pyramiding limit, however, allows only 6 entries in the same direction. But since the orders generate quickly after each other before the other orders are filled, they all get approved by TradingView (after all, the current open position hasn’t reached the limit of 6 entries yet). And so while calc_on_every_tick=true gives us a quicker strategy, in this case the script is too quick and we end up with a bigger position than intended.
Note: When calc_on_every_tick is enabled, the strategy evaluates real-time price bars as if they were already closed. This means that built-in variables like close refer to the current tick of the real-time bar and so this ‘closing price’ is updated with every tick for the last bar of the chart. This happens because, when a price bar hasn’t closed yet, TradingView doesn’t know whether the recent tick will be the last of the bar, and so each new incoming tick updates the price bar’s close.

Calculate after an order fills versus calculate with every tick

Another argument of the strategy() function is calc_on_order_fills. The difference between that argument and calc_on_every_tick is (Pine Script Language Tutorial, n.d.):

  • With calc_on_order_fills set to true, the strategy performs an additional intra-bar calculation when an order fills. That single intra-bar calculation affects the strategy’s real-time behaviour as well as the historical backtest: on real-time data that calculation happens on the next price update, whereas with historical data it happens on the bar’s open, high, low, or close.
  • When calc_on_every_tick is set to true, the strategy calculates on every real-time tick instead of just on bar close. This, however, only changes how the strategy behaves with real-time data and has no effect on historical data.

Now let’s continue discussing the calc_on_every_tick argument by seeing how we can enable this setting by hand. After that we’ll look at a full programming example.

Configuring a strategy’s real-time tick-by-tick calculation by hand

Configuring TradingView strategy settings programmatically is convenient since then our strategy is added to the chart with the correct, predefined settings. But manually changing a setting gives more flexibility since multiple instances of the same strategy can use different settings.

To manually change whether a strategy calculates repeatedly on real-time data, we first click on the gear icon ( ) that’s displayed to the right of the strategy’s name on the chart:

Opening the strategy settings window in TradingView

This opens a window with the different strategy settings. Here we select the ‘Properties’ tab and there we can enable whether the strategy should calculate with every real-time tick or not with the ‘Recalculate On Every Tick’ setting:

Manually configuring how often a TradingView strategy calculates
Note: The ‘Recalculate On Every Tick’ setting overrides the value of the calc_on_every_tick argument. This way we can change the strategy’s behaviour without having to edit the script’s code.

Now let’s examine a full programming example to better understand calc_on_every_tick and its accompanying ‘Recalculate On Every Tick’ setting.

Example: submitting TradingView orders with every real-time tick

The example strategy below generates a long signal whenever two consecutive price bars close higher and a short signal generates when two bars in a row close lower. We filter these signals with a 20-bar Exponential Moving Average (EMA). The motivation behind this simple logic is to generate a reasonable amount of trades so that, on real-time data, we can easily experiment with enabling and disabling the calc_on_every_tick argument.

The image below gives an idea of how the finished script. After discussing the code, we look at how the strategy behaves on the chart and then change that argument’s value.

Example of the TradingView strategy that uses the calc_on_every_tick argument of the strategy() function
strategy(title="Example - calc_on_every_tick", overlay=true, 
     pyramiding=5, calc_on_every_tick=true)

// Inputs
priceData = input(title="Price Data", type=source, defval=close)
emaLen    = input(title="EMA Length", type=integer, defval=20)

// Calculate and determine conditions
emaValue = ema(priceData, emaLen)

timeFilter = (year == 2016) and (month > 3)

enterLong = timeFilter and (priceData > emaValue) and 
     (close > open) and (close[1] > open[1])
enterShort = timeFilter and (priceData < emaValue) and
     (close < open) and (close[1] < open[1])
     
// Perform plotting and colouring
plot(series=emaValue, color=#FFA07A, linewidth=2)

backgroundColour = enterLong ? green :
     enterShort ? red :
     na

bgcolor(color=backgroundColour, transp=90)

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

strategy.entry(id="Enter Short", long=false, when=enterShort)

We start with the strategy() function and use this function’s title argument to set the strategy’s name. With the overlay argument given a value of true the strategy displays on the chart’s instrument and pyramiding set to 5 allows up to 5 entries in the same direction (TradingView, n.d.). Then, with calc_on_every_tick set to true, we make the strategy calculate with every real-time tick instead of just computing on the bar’s close (Pine Script Language Tutorial, n.d.).

After configuring the strategy, we add two input options:

priceData = input(title="Price Data", type=source, defval=close)
emaLen    = input(title="EMA Length", type=integer, defval=20)

The input() function adds input options to a script. It not only creates the option in the ‘Inputs’ tab of the script’s settings but also returns the input’s current value (TradingView, n.d.). Here we store those returned values in a variable with the assignment operator (=). That way we can refer to the input’s current value by simply using the variable.

The first input is a so-called ‘source’ data input option. Such an input adds a pull-down menu to the script’s settings, and the items in that menu correspond to the instrument’s prices (like its high or close data). We make this input by setting the type argument of the input() function to source (Pine Script Language Tutorial, n.d.). Here we give it the instrument’s closing prices by default (defval=close) and store its current setting in the priceData variable.

We name the other input option “EMA Length” with the input() function’s title argument (the image further down below show how the inputs look like). We set its type argument to integer to create a numerical integer input that accepts whole numbers only (Pine Script Language Tutorial, n.d.). Twenty is the default value for this input (defval=20) and we track its current value with the emaLen variable.

Then we compute our moving average:

emaValue = ema(priceData, emaLen)

The ema() function calculates an Exponential Moving Average with two arguments: a series of values to process and an integer that sets the average’s length in number of bars (TradingView, n.d.). We have the function calculate on the value of the priceData input variable (which holds the instrument’s closing prices by default) and emaLen, the input variable that we gave a standard value of 20. We store the value returned by ema() in the emaValue variable for use later on.

Next we create a time-based filter:

timeFilter = (year == 2016) and (month > 3)

Here we assign the timeFilter true/false variable a value based on two expressions that we combine in one condition with the and logical operator. Given how this operator works, combing two expressions with and returns true when the value on this operator’s left and the value on its right are both true too. Otherwise, when one or both expressions are false, then the combined result is false too (Pine Script Language Tutorial, n.d.).

The first expression checks whether the year built-in variable (which returns the current bar’s year; TradingView, n.d.) equals (==) 2016. With the second expression we evaluate whether the month of the current bar (month) is greater than (>) 3. Combining these two expressions means that the timeFilter variable holds true whenever the bar happens in April 2016 or later.

The timeFilter variable isn’t related to our strategy’s logic. Instead, its purpose is to artificially limit the number of orders the strategy generates. This is currently unfortunately needed because TradingView can generate up to 2,000 orders, and the ‘Order’s limit (2000) was reached’ error message appears when we generate more orders than that:

Example of the order limit error message in TradingView

So by having the strategy only take trades that happen in April 2016 or later, we limit the number of trades that the script makes and prevent that error from happening. Note that you need to change this timeFilter variable depending on which time frame you use and when you test the above example.

The next part of the example is figuring out the strategy’s trading conditions:

enterLong = timeFilter and (priceData > emaValue) and 
     (close > open) and (close[1] > open[1])
enterShort = timeFilter and (priceData < emaValue) and
     (close < open) and (close[1] < open[1])

We make two true/false variables here. Their values are set with 4 expressions that we combine with the and logical operator. That way each expression needs to be true before the variable is assigned true also. And when one or more expressions are false, then the variable is given a value of false too (Pine Script Language Tutorial, n.d.).

The first expression that affects the value of the enterLong variable is the timeFilter true/false variable that we’ve just created. The second expression is whether the priceData input variable (which holds the instrument’s closing prices by default) is greater than (>) the 20-bar EMA (emaValue). This is our up trend filter.

The other two expressions check recent price action. The first of these evaluates whether the current bar closed higher (close > open) while the second checks if the previous bar closed higher too. For that we use the history referencing operator ([]) to retrieve the previous bar’s data (close[1] > open[1]).

We set the value of the enterShort true/false variable in the same way. Here we also evaluate the timeFilter variable, whereas the second expression requires that the value of the “Price Data” input option is less than (<) its 20-bar EMA (priceData < emaValue). We also check if the current bar closed lower (close < open) and if the previous bar had a lower close too (close[1] < open[1]). When all four of these expressions evaluate to true, the enterShort variable is assigned true too.

Next we display the EMA and the trading signals on the chart:

plot(series=emaValue, color=#FFA07A, linewidth=2)

backgroundColour = enterLong ? green :
     enterShort ? red :
     na

bgcolor(color=backgroundColour, transp=90)

The plot() function displays the values of its series argument on the chart, and plots them with a continuous line by default (TradingView, n.d.). Here we set the series argument to the emaValue variable. Those 20-bar EMA values display with the #FFA07A hexadecimal colour of light salmon. We use the linewidth argument, which specifies the plot’s size starting from 1 as the default size (TradingView, n.d.), to make the line a bit bigger than default (linewidth=2).

The next step is creating a visual confirmation of the long and short signals by changing the chart’s background colour. To do this, we first create the backgroundColour variable and assign it a conditional colour with two conditional (ternary) operators (?:).

A conditional operator works on three values: a true/false condition and two other values. The first of those two values is returned when the true/false conditions is true, while the operator returns the second when that condition evaluates to false (Pine Script Language Tutorial, n.d.). This makes the conditional operator work like an if/else statement: ‘if the condition is true, then return this value; otherwise, return the other value’.

Our first condition is the enterLong true/false variable. Now when this variable is true, the first conditional operator returns the green basic TradingView colour. We then store that returned colour in the backgroundColour variable for use later on in the script. But when that first condition is false, TradingView processes the second conditional operator.

That operator evaluates enterShort. When that true/false variable is true, this conditional operator returns the red standard TradingView colour and we then assign that colour to the backgroundColour variable. Now when the condition of the second conditional operator is also false, then this operator returns na.

That na built-in variable represents a “not a number” value (TradingView, n.d.). When we use such a value with colouring, it acts as a conditional colour and ‘turns off’ colouring (Pine Script Language Tutorial, n.d.). So when we set a plot’s colour to na on a certain bar, then on that bar the plot won’t appear. But when we set the colour back to a basic colour or hexadecimal colour, then the plot reappears.

We use the colour that’s stored in the backgroundColour variable with bgcolor(). That function colours the background of price bars with a specific colour (TradingView, n.d.). We set its color argument to the backgroundColour variable to either get a green, red, or invisible (na) background colour. With the transp argument we set this background’s transparency. That argument accepts values that range from 0 (no transparency) to 100 for full transparency (TradingView, n.d.), and since we set it to 90 we get a highly transparent background.

The programming example ends with submitting the strategy’s orders:

strategy.entry(id="Enter Long", long=true, when=enterLong)

strategy.entry(id="Enter Short", long=false, when=enterShort)

The strategy.entry() function opens positions with a market order by default and, when there’s already a position in the other direction, reverses that position (TradingView, n.d.). Since we set the strategy’s pyramiding setting to 5 in the beginning of the example, our strategy is allowed to enter up to 5 times in the same direction.

Both strategy.entry() function calls use the same arguments. With id we specify the order identifier (TradingView, n.d.), and that name appears on the chart and in the ‘Strategy Tester’ window. The long argument, when set to true, makes strategy.entry() submit an enter long order while setting this argument to false makes the function generate an enter short order (TradingView, n.d.). And the when argument accepts a true/false value to active the order on the current bar (when true) or hold off submitting it (false) (TradingView, n.d.).

In the first strategy.entry() statement we set the id argument to “Enter Long” and, with long=true, have the function generate an enter long order. That order generates when the enterLong variable holds a value of true, meaning TradingView submits this order when there’s a close above the 20-bar EMA and there are two price bars in a row that close higher.

The second strategy.entry() statement has its id argument set to “Enter Short”. With the long argument set to false the function creates an enter short order. When that order submits is specified with the enterShort variable, which holds true whenever the bar close below the 20-bar EMA and there are two lower closes in a row.

Calculating on every tick: historical data versus real-time data

Our example strategy has the following input options:

Example of the strategy's input options

To recap, in the example strategy we used the strategy() function with the calc_on_every_tick argument enabled:

strategy(title="Example - calc_on_every_tick", overlay=true, 
     pyramiding=5, calc_on_every_tick=true)

Now when we add our strategy to the chart, it looks like this:

Example chart of the TradingView trading strategy

This screenshot was taken immediately after the strategy was added to the chart, and so all bars (except the last bar) are historical bars. On those historical bars the strategy’s orders fill on the bar after the one on which the long or short signal happened. This occurs because, even when calc_on_every_tick is set to true, the strategy still only calculates at the close of historical price bars (Pine Script Language Tutorial, n.d.). And so when there’s a long or short signal, that order generates on the bar’s close and fills on the next bar’s open.

However, that’s the case with historical price bars. Let’s see what happens when our strategy calculates on real-time data. An implication of calc_on_every_tick=true coupled with real-time price data is that orders can generate with any price update, long before the bar closes. But when a buy or sell condition triggers inside the price bar and the script submits an order, that doesn’t mean that on the bar’s close the long or short signal is still present.

This can be confusing: a strategy can fill several orders on a real-time bar, but if we look 5 seconds later the entry condition is invalided and we’re left wondering why the strategy submitted those orders. We see this happening on our real-time GBP/USD chart as well. With still 15 seconds to go before the current bar closes, the strategy had already filled its first order on that bar even though at the current real-time tick the entry condition isn’t valid anymore (otherwise the chart’s background would have been green):

Example chart of the TradingView strategy calculating in real time

A few seconds later the bar closes and the strategy fills several long orders – even though on the bar’s close there’s no long entry signal present. We also see another feature of calc_on_every_tick here; the ability to generate and fill a lot of orders quickly after each other:

Example of the TradingView trading strategy running on real-time data

The next chart shows another situation that can happen with calc_on_every_tick enabled. After our strategy filled 5 long entries on a bar that didn’t have a long signal when it closes, two price bars occurred that did had a long entry signal on bar close. However, due to the strategy’s pyramiding setting it couldn’t trade those valid long signals since it already had reached its maximum number of entries in the same direction.

This illustrates how calc_on_every_tick=true makes backtesting results unreliable: on historical data, the script only calculates on bar close and so only trades bars with a green or red background (see the left part of the chart examples). But on real-time data the strategy trades price bars that don’t have a signal on bar close and ‘ignores’ price bars that do have a valid signal when the bar closed.

Another example of the TradingView strategy running on real-time data

After a while the strategy generates a short signal and fills several short orders during the price bar. This time, when the price bar closes, the short signal is still valid:

Example of the TradingView strategy filling more short trades in real time

Then an enter long order is triggered when the strategy is already short. The script submits and fills that long order on the same bar on which the signal happened, and this order isn’t filled on the next bar (as would happen on historical data). This shows how the calc_on_every_tick argument not only makes our strategy much quicker with entries, but with exits too. Also note that, on this chart, the strategy didn’t reach it maximum of 5 entries in the same direction since there weren’t that many long signals during the bar.

Example of the strategy performing with real-time data

Even though calc_on_every_tick=true makes our strategy quicker, orders are still not filled immediately. For instance, on this real-time bar with a red background a short signal generates:

Short signal in real time of our TradingView example strategy

Then after 10 seconds, we see the visual confirmation that several orders were filled on this bar:

Visual confirmation that the orders of our TradingView strategy filled

After a while of calculating on every real-time tick, the chart shows how active our strategy trades with calc_on_every_tick=true:

Example of the TradingView strategy running on real-time data with calc_on_every_tick

Now let’s see how real-time tick-by-tick calculations affect the strategy’s behaviour compared to once per bar calculations on historical data. For this we remove the script from the chart and then re-add it. That causes the strategy to reprocess all price bars (except the last bar) as historical bars, and this way it treats the bars on which it just performed real-time calculations as historical bars.

So on the previous chart we see the strategy’s behaviour with calculating on every real-time price update. On this chart we see how the strategy performs on those same bars, but now with just only one calculation per price bar:

Example of the TradingView strategy calculating once per historical price bar

As this shows, there’s a considerable difference between the strategy’s behaviour with a tick-by-tick calculation compared with a once per bar calculation. For more on this, see TradingView tip: calc_on_every_tick gives unreliable backtest results.

Summary

The strategy() function configures a strategy’s settings programmatically, and we need to add this function to every TradingView strategy. One argument of this function is calc_on_every_tick. When this optional argument is set to true, the strategy calculates on every real-time tick. Those additional calculations only happen on real-time data though; on historical data, the strategy still calculates once per bar, on bar close. The default value of calc_on_every_tick is false, and so if we don’t specify it in the strategy() function, the script only calculates on the close of each real-time and historical price bar. When we have a strategy calculate with every real-time price update, it responds quicker to price action. This way the script can open positions sooner, but can also reduce risks quicker. However, because order conditions can be valid inside a bar but invalid when the bar closes, there can be a considerable difference between a strategy’s real-time and backtested results. Because of that, a strategy with calc_on_every_tick set to true can currently only be forward-tested on real-time data.

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 April 11, 2016, from https://www.tradingview.com/study-script-reference/