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:
- Theory: setting the strategy’s calculation frequency with
- Tips for strategies that calculate with every real-time tick
- Difference between calculate after an order fills versus calculating with every tick
- Setting the calculation frequency of a TradingView strategy by hand
- Example strategy that calculates with every real-time price update
- Example charts: the strategy’s behaviour on real-time and historical data
Setting a strategy’s calculation frequency with code
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
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.
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_tickis enabled or disabled. (We can, however, have a strategy calculate more than once on historical bars with the
calc_on_order_fillsargument, but that feature is distinct from
- 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_tickis set to
falseor missing from the
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
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=truedo 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=truestrategies 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_tickenabled 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.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_sizevariable (TradingView, n.d.) – before submitting orders with
- 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
trueto 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=truegives us a quicker strategy, in this case the script is too quick and we end up with a bigger position than intended.
- Positions in TradingView are opened with the
calc_on_every_tickis enabled, the strategy evaluates real-time price bars as if they were already closed. This means that built-in variables like
closerefer 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.):
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.
calc_on_every_tickis 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:
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:
calc_on_every_tickargument. 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
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.
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 > open) enterShort = timeFilter and (priceData < emaValue) and (close < open) and (close < open) // 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)
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
We name the other input option “EMA Length” with the
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
Then we compute our moving average:
emaValue = ema(priceData, emaLen)
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
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.
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:
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 > open) enterShort = timeFilter and (priceData < emaValue) and (close < open) and (close < open)
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 > open).
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 < open). When all four of these expressions evaluate to
enterShort variable is assigned
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)
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 (
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 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)
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.
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
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.
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:
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:
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):
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:
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.
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:
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.
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:
Then after 10 seconds, we see the visual confirmation that several orders were filled on this bar:
After a while of calculating on every real-time tick, the chart shows how active our strategy trades with
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:
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.
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
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.
- Besides calculating a TradingView strategy with every real-time tick, we can also have it perform an additional one-time intra-bar calculation when an order fills. Such an additional calculation is done on historical and real-time price bars.
- We use the
strategy()function to programmatically configure a strategy’s properties. Other settings that affect how the script trades when
calc_on_every_tickis enabled are the strategy’s default order size and the pyramiding limit setting.
- For a better understanding of the
calc_on_every_tickargument and how it affects the strategy’s backtest results, see TradingView tip:
calc_on_every_tickgives inaccurate backtest performance.
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/