Each TradingView strategy implicitly uses a certain number of price bars to base its calculations on. Almost always, TradingView can figure out itself how many price bars a strategy needs for its computations. But what if that value cannot be determined automatically?

In this article:

Setting the number of bars a TradingView strategy calculates on

We configure a TradingView strategy’s settings either by hand or with the strategy() function. This function needs to be added to every strategy (Pine Script Language Tutorial, n.d.) and its title argument, which specifies the strategy’s name, always needs to be set (TradingView, n.d.).

Another argument of the strategy() function is max_bars_back. This sparsely documented argument sets the maximum number of historical bars that the strategy can reference (TradingView, n.d.). TradingView automatically calculates how many price bars a strategy needs for its calculations (TradingView, n.d.), but in rare circumstances TradingView has trouble determining this value (Vitvlkv, 2016). In that case, TradingView generates the ‘out of depth at index’ error and we fix that error by setting the max_bars_back argument to the number of bars our strategy uses with its calculations.

To understand what this ‘number of historical bars’ means, let’s say we create a trading strategy that uses a 12-bar RSI (Relative Strength Index). Now before this script can plot and use that RSI value, it needs to have 12 price bars to calculate with. So if we would scroll our chart all the way back to the first few price bars, we’ll see our script ‘waiting’ till there are 12 bars before plotting the RSI.

We set the number of historical bars a script uses with its calculations with max_bars_back like this:

strategy(title="My example strategy", max_bars_back=12)
Tip: TradingView automatically determines how many price bars a script needs for its calculations. This means we only need to set max_bars_back when TradingView cannot figure out that number of bars itself. This happens with less than 5 percent of scripts (estimation), and so we don’t use the max_bars_back argument with the large majority of TradingView strategies.

Now let’s look at a script example to see how a strategy’s code affects the number of historical bars a strategy bases its calculations on. After that we’ll continue our discussion of max_bars_back with some insights and considerations.

Example: seeing how many historical bars a strategy needs

The value we give the max_bars_back argument is closely related to how far ago we reference historical price bars (see Vitvlkv, 2016). This referencing of historical price bars is done with functions that calculate on a series of values (like ema() and lowest()) or with the history referencing operator ([]). That operator returns a previous value from some number of bars ago (Pine Script Language Tutorial, n.d.).

For example, close returns the closing price of the current bar but close[12] uses the history referencing operator to get the close price of 12 bars ago. Likewise, volume[7] returns the volume of 7 bars ago. Now before our script can get both of these values, it needs to have access to at least 12 historical price bars.

For a more visual explanation, let’s consider the following example:

strategy(title="Strategy example - max_bars_back", overlay=true)

plot(series=highest(high, 20), color=green, linewidth=2)
plot(series=lowest(low, 20), color=red, linewidth=2)

We start here with the strategy() function to configure the strategy’s properties. With the title argument we specify the script’s name and with overlay we display the strategy on the chart’s instrument. Since we don’t set the max_bars_back argument, we let TradingView automatically calculate how many bars the script needs.

Then we plot a series of data on the chart with plot(), and this function plots a line by default (TradingView, n.d.). By setting the linewidth argument of both plot() statements to 2, we create plots that are one step bigger than normal (TradingView, n.d.).

The first plot() function call plots a 20-bar highest high with the green basic TradingView colour. We use the highest() function to get that extreme price. That function accepts two arguments: a series of values and the number of bars to get the highest value of (TradingView, n.d.). Here we set those arguments to the bar’s high prices (high) and 20. This also means there need to be 20 price bars before the script can calculate that value.

With the second plot() statement we display the 20-bar lowest low in red. We retrieve that value with lowest(), a function that also accepts two arguments: a series of values alongside an integer that sets the number of bars to calculate on (TradingView, n.d.). We set those arguments to low and 20, and so here we also require 20 price bars to compute on (which are the same bars that we calculate the highest high value on).

When we add this strategy to the chart and scroll all the way back to the first bars (or use the ‘Go To…’ option that’s placed just below the chart), we see the following:

Visual explanation of the max_bars_back argument in TradingView

Because our strategy needs price data of 20 price bars, its holds off plotting till the 20th bar. On any bar before that, the highest() and lowest() function can’t calculate their extreme prices. And so the number of historical bars that our script references is 20. Here we let TradingView determine that value, but to set it ourselves we would add max_bars_back=20 to the strategy() function.

Now let’s change the lookback of our script to 35:

strategy(title="Strategy example - max_bars_back", overlay=true)

plot(series=highest(high, 35), color=green, linewidth=2)
plot(series=lowest(low, 35), color=red, linewidth=2)

The only change here is that the highest() and lowest() functions now calculate on 35 bars. After saving the code, we see the script delaying an additional 15 bars:

Changing the number of referenced historical bars of our TradingView example

While we see the script wait with plotting until there are enough bars, this also affects other aspects of the strategy. For example, if we submit orders based on the 35-bar highest high and lowest low, then those orders cannot be generated before the 35th bar.

While that’s only a small delay here, if we would reference a lot of bars (like when calculating a 200-bar moving average or the weekly high from a 1-minute chart), then some historical data is ‘lost’ when backtesting since the strategy has to ‘wait’ till there are enough price bars to calculate on.

Tips for setting a strategy’s max_bars_back argument

Some insights for setting the max_bars_back argument of the strategy() function are:

  • First, we need to set max_bars_back when TradingView cannot figure out the number of historical bars a strategy references. Unfortunately, this is a black box operation since we neither know how TradingView estimates this value nor can we see it somewhere. So currently the only way to know that we need to set max_bars_back is with the ‘out of depth at index’ error message: that error appears when TradingView cannot determine how many historical bars our script uses.
Example of the 'out of depth at index' error message in TradingView
  • There are two functions that, when we compute them on a series of values, make it impossible for TradingView to figure out how many bars our strategy references: pivothigh() and pivotlow() (TradingView, n.d.). But besides this, there is no way to know in advance (before the error message appears) whether we need to set the max_bars_back argument or not. Generally speaking, scripts that repeatedly use values from previous bars with calculations (that is, use the history referencing operator) are harder for TradingView to get right.
  • As preparation, we can set max_bars_back before the ‘out of depth at index’ error happens. If we do, this argument’s value has to be high enough for the script’s calculations – even when we change the values our script calculates with through the input options. Because when the manually specified value of max_bars_back is too low, TradingView still attempts to calculate this value automatically (TradingView, n.d.). And then the script can still run into the error message even though we’ve set the max_bars_back argument.

Now which value do we need to set the max_bars_back argument to? Some tips and considerations for that are:

  • First, we don’t want to set max_bars_back too low. Otherwise the strategy still runs into the ‘out of depth at index’ error message when we change an input option, for example. And so the value of max_bars_back needs to be generous enough to allow us to change the script’s inputs to double or triple their length.
  • However, there are two reasons why we don’t want to set max_bars_back too high. First, if we set this argument to higher than the number of bars on the chart, TradingView generates the ‘too large lookback’ error message:
TradingView error message: 'too large lookback'
  • The second reason why a high max_bars_back value isn’t helpful is because it makes the script ‘wait’ before calculating, and that artificially shortens our backtest period. For example, if we set the argument to 1000, then the strategy won’t generate any orders or take other actions during the first thousand bars on the chart. And so we’ll be wasting a lot of historical data instead of using that data in our backtest.
  • As a rule of thumb, an approximate max_bars_back value for short-term strategies is 50. A value of 100 is more suitable for medium-term strategies, whereas a long-term strategy can require 200 bars back or more.
Note: There is no manual option that sets the number of historical bars the strategy uses with its calculations; we can only specify that value with the max_bars_back argument of the strategy() function.

To learn more about working with the max_bars_back argument, let’s look at a programming example.

Example: setting the number of bars a strategy uses

The example strategy that’s shown below only works when we set the max_bars_back argument of the strategy() function. Because TradingView is quite good in determining how many historical bars a strategy calculates on, we have to employ some ‘tricks’ in our code to prevent TradingView from determining how many historical bars our strategy references. As a consequence, this strategy isn’t a good example of a clear, well-written TradingView script.

The example strategy plots and trades the swing pivot high (see next image). We go long on the first bar of the day, provided the bar’s close is below the swing pivot. Once we’re long, we submit a limit sell order for the then-current pivot swing high. How we calculate the pivot depends on the strategy’s market position. This way we require a ‘stronger’ pivot high to open a position, but once we’re in a position, we use a less pronounced swing pivot to exit the position. That way our take profit order is executed sooner. After discussing the code, we’ll look at how the strategy behaves and the impact of the max_bars_back argument.

Example of the TradingView strategy with the swing pivots plotted on the chart
strategy(title="Example - max_bars_back", overlay=true)

// Input options
pivOffsetLong = input(title="Pivot Offset (Long)", type=integer, defval=7)
pivOffsetFlat = input(title="Pivot Offset (Flat)", type=integer, defval=15)

// Determine pivot
pivHighValue = (strategy.position_size == 0) ? pivOffsetFlat :
    pivOffsetLong

pivHigh = pivothigh(pivHighValue, pivHighValue[1])

// Plot pivot on the chart
plot(series=strategy.position_size == 0 ? pivHigh : na,
     style=circles, linewidth=5, color=green, offset=-pivOffsetFlat)

plot(series=strategy.position_size == 0 ? na : pivHigh,
     style=circles, linewidth=5, color=green, offset=-pivOffsetLong)

// Determine trading conditions
recentPivot = fixnan(pivHigh)
enterLong   = (close < recentPivot) and (dayofmonth != dayofmonth[1])

// Submit orders
strategy.entry(id="Enter Long", long=true, when=enterLong)
strategy.exit(id="Exit Long", from_entry="Enter Long", limit=recentPivot)

We first specify the strategy’s settings with strategy(). With this function’s title argument we name the strategy and with overlay set to true the strategy displays on the chart’s instrument (TradingView, n.d.).

Then we add two input options:

pivOffsetLong = input(title="Pivot Offset (Long)", type=integer, defval=7)
pivOffsetFlat = input(title="Pivot Offset (Flat)", type=integer, defval=15)

User-configurable input options are made with input(), and this function also returns the input’s current value (TradingView, n.d.). We store those values in variables with the assignment operator (=). That way we can refer to the input’s value later on in the code by using the variable.

Both inputs are numerical integer inputs. These accept whole numbers only and are made by setting the type argument of the input() function to integer (Pine Script Language Tutorial, n.d.).

The first input is named “Pivot Offset (Long)” and we use its value when calculating the pivot high values that are used to open a position. We give it a default value of 7 (defval=7) and store its current value in the pivOffsetLong variable. We use that variable later on when computing the swing high pivot.

The second input is called “Pivot Offset (Flat)” and this one specifies the strength of the swing pivots high that we use with the strategy’s exit orders. The input starts with a value of 15 and has its current value tracked in the pivOffsetFlat variable.

Next we use the inputs to calculate the swing pivot high:

pivHighValue = (strategy.position_size == 0) ? pivOffsetFlat :
    pivOffsetLong

pivHigh = pivothigh(pivHighValue, pivHighValue[1])

Since we want to calculate the swing pivot highs differently depending on the strategy’s market position, we first need to figure out whether the script is long or flat. We do that with strategy.position_size, a built-in variable that returns the size and direction of the current market position: when the strategy is long, this variable returns the number of open long contracts; when the script is flat, it returns 0 (TradingView, n.d.).

We use strategy.position_size to conditionally set the value of pivHighValue, our variable that holds the swing pivot length. This makes it possible to calculate the swing pivots later on with a different length depending on whether the strategy is long or flat. To implement this, we use the conditional (ternary) operator (?:).

That operator works on three values: a true/false expression and two other values, of which one is returned based on the true/false expression (Pine Script Language Tutorial, n.d.). Now when that expression is true, the conditional operator returns its second value. And when the expression evaluates to false, the operator returns its third and last value. This makes the conditional operator work like an if/else statement: ‘if this expression is true, then return the second value; otherwise, return the third’.

The expression that we evaluate is whether strategy.position_size equals (==) 0, which it does when the strategy is flat (TradingView, n.d.). When that’s the case, the conditional operator returns the value of the pivOffsetFlat input variable (which has a standard value of 15). That value is then assigned to the pivHighValue variable.

Now when the strategy isn’t flat, our conditional operator returns the pivOffsetLong variable (which has a standard value of 7). That value is then stored in the pivHighValue variable. So depending on whether the strategy is flat or not, the pivHighValue variable either holds a value of 7 or 15. Now since we use this variable to calculate the swing pivot highs, the length of those pivots in effect depend on whether the strategy has a position on or not.

We calculate that swing pivot high with pivothigh(). That function returns the price of the swing pivot (if there’s currently one) or ‘NaN’ (a “not a number” value) for when there’s no new pivot price point (TradingView, n.d.). With the pivHighValue variable inside this function’s parentheses we specify the pivot’s left and right strength. These ‘strength’ values indicate how many bars there need to be to the left and to the right of a high before TradingView considers that point to be a ‘swing pivot high’.

We offset the second pivHighValue variable inside the pivothigh() function’s parentheses with the history referencing operator ([]). With help of this operator we don’t use this variable’s current bar value but work with the previous bar value of pivHighValue instead. (The only reason for this part of the code example is to prevent TradingView from correctly guessing the number of historical bars that the script calculates on.)

The value that’s returned by pivothigh() – which is either the swing pivot high or ‘NaN’ – is assigned to the pivHigh variable. Then we plot that value on the chart:

plot(series=strategy.position_size == 0 ? pivHigh : na,
     style=circles, linewidth=5, color=green, offset=-pivOffsetFlat)

plot(series=strategy.position_size == 0 ? na : pivHigh,
     style=circles, linewidth=5, color=green, offset=-pivOffsetLong)

Even though we have a single value to plot (the swing pivot high), we call the plot() function twice. We need to do that because the pivothigh() function determines a swing pivot high after it occurred. To display that value correctly on the chart, we need to shift the pivot high value a certain number of bars to the left. Since we cannot set the offset argument of the plot() function conditionally (TradingView, n.d.), we use two plot() statements with different offset values for our swing pivot strengths of 7 and 15 bars.

The first plot() statement displays the swing pivot values for when our strategy is flat. To plot those values conditionally (based on whether there’s a market position), we use the conditional (ternary) operator (?:) again. With this operator we set the series argument of the plot() function either to the pivot high value (which we stored in the pivHigh variable) or na. That built-in variable represents a “not a number” value (TradingView, n.d.) which, when used with plotting, has the effect of turning off the plot (see Pine Script Language Tutorial, n.d.). The true/false expression of the conditional operator is whether the strategy’s market position (strategy.position_size) equals (==) 0. When the strategy is indeed flat, we set the series argument of the plot() function to the pivHigh variable.

With the other arguments of this first plot() statement we display the swing pivot values as a small circle (style=circles) in the green basic TradingView colour. And the linewidth argument set to 5 makes our circles appear with a large size. Now to plot this swing pivot value on the correct bar, we set the offset argument of the plot() function to -pivOffsetFlat (the input variable that holds the pivot strength for when the strategy is flat).

The second plot() function call is similar. But now we plot the pivHigh value on the chart whenever the strategy is not flat. We offset those plotted values to the left with the pivOffsetLong input variable that has a standard value of 15. Since both plot() statements cancel each other out with our use of the conditional operator, the swing pivot high value either displays with 7 or 15 bars to the left depending on whether the strategy currently has an open position or not.

Then we determine the trading conditions:

recentPivot = fixnan(pivHigh)
enterLong   = (close < recentPivot) and (dayofmonth != dayofmonth[1])

As discussed above, the pivothigh() function either returns the swing pivot high price or NaN when there isn’t a new pivot high. The problem with those non-numerical NaN values is that we cannot use them with comparisons. And so we first need to convert them to actual numbers. We do that here with the fixnan() function, which replaces any NaN value with the nearest non-NaN value (TradingView, n.d.).

The fixnan() function works here with the pivHigh variable that holds the value returned by pivothigh(). This way we replace any NaN value with the nearest swing pivot price, and we assign those corrected values to the recentPivot variable. This makes the recentPivot variable hold the recent pivot swing price on every bar (without NaN values).

Then we create the true/false enterLong variable. We set this variable’s value with two expressions that are combined with the and logical operator. This operator only returns true when the value on its left and the value on its right are true too (Pine Script Language Tutorial, n.d.). When one or both values are false, then combing them with and returns false too.

The first expression checks whether the bar’s closing price (close) is less than (<) the recent swing pivot high (recentPivot). Since we’re going to use the recentPivot value as our profit target, we want to make sure we don’t open a position above our profit target (since that position will then be immediately closed by the strategy).

The second expression evaluates if the current bar is the first of the trading day. We use dayofmonth here, a built-in variable that returns the bar’s day of month (TradingView, n.d.). The evaluation checks whether this variable’s current value is unequal to (!=) its value on the previous bar (dayofmonth[1]). This makes our strategy only open positions at the start of the trading day.

In the last part of the script we submit the strategy’s orders:

strategy.entry(id="Enter Long", long=true, when=enterLong)
strategy.exit(id="Exit Long", from_entry="Enter Long", limit=recentPivot)

The strategy.entry() function opens a position with a market order by default (TradingView, n.d.). We use three of this function’s arguments: with id we set the order identifier to “Enter Long”. That name displays on the chart and in the ‘Strategy Tester’ window, and other order functions use this name to reference this order. The long argument, when set to true, makes strategy.entry() submit an enter long order (TradingView, n.d.). And the when argument requires a true/false expression to specify when the order should be submitted. This argument is set to the enterLong variable we declared a moment ago.

We end the example with strategy.exit(), a function that closes an open position (TradingView, n.d.). We configure this exit order with several arguments. The order identifier is set with id and from_entry specifies which entry order our exit order should apply to. With the limit argument we set the profit target of this exit order to a specific price (TradingView, n.d.). That argument is set here to the recentPivot variable , which holds the 7-bar and 15-bar swing pivot high.

There are three noteworthy features of our exit order. First, we only use strategy.exit() to submit a take profit order but not a stop-loss order. This means there’s no limit on the maximum loss that each position can occur. Second, we submit the take profit order based on a variable’s value, and that variable updates each time a new swing high happens. That means our profit target isn’t fixed but can change while the position is open.

The third observation is that we call strategy.exit() unconditionally; this function even executes when there’s no open position. This isn’t a problem because, when the entry order isn’t filled but we still execute strategy.exit(), our exit order waits till the entry order places before being submitted (TradingView, n.d.). This means we don’t need to worry that our profit target acts as a limit order that opens a short position.

Example charts: trading pivot high prices with a strategy

Now let’s see how our example strategy behaves. First, the script’s input options are:

The input options of our TradingView example strategy

When we add our strategy to the chart, it doesn’t do anything except triggering the ‘out of depth at index’ error message:

The 'out of depth at index' error message in TradingView

This error message happens when TradingView cannot automatically determine how many historical price bars the strategy bases its calculations on. Luckily, we can fix this error with the max_bars_back argument. We initially used the strategy() function in the above example like this:

strategy(title="Example - max_bars_back", overlay=true)

But since we didn’t specify the max_bars_back argument, TradingView automatically guessed the number of bars our strategy calculates with (TradingView, n.d.). But as the error message shows, that estimation didn’t went well.

So let’s help TradingView and set the number of bars our strategy references ourselves. To do so, we change the strategy() function to:

strategy(title="Example - max_bars_back", overlay=true, max_bars_back=50)

Here we add the max_bars_back argument with a value of 50. This makes our strategy wait with calculating its values until there are at least 50 price bars to work with. With our input options set to 7 and 15, this value is high enough to prevent running into the ‘out of depth at index’ error message again.

After making that change, we save the script. The strategy then reloads on the chart and works correctly:

Example of the TradingView strategy with the swing pivot high plotted Example of the TradingView strategy with the max_bars_back argument set correctly

Now our strategy opens long positions when the first bar of the day closes below the swing pivot high, and exits that position at the pivot high with a limit order.

Summary

Each TradingView strategy needs to include strategy(), a function that uses several arguments to configure a strategy’s settings. One of those arguments is max_bars_back which sets the number of bars a strategy uses with its calculations. By default, TradingView estimates this number of bars automatically. But in rare cases (especially when we use the pivothigh() or pivotlow() functions), TradingView cannot figure out how many bars a strategy needs for its calculations. In that case, TradingView generates the ‘out of depth at index’ error message when we add the strategy to the chart. To fix that error, we need to set the max_bars_back argument to a manual value that’s high enough that the script has enough price bars to calculate on. Typical values for this argument range from 50 for short-term strategies to over 200 for long-term strategies that reference a lot of historical price bars.

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

Vitvlkv (2016, February 25). Pine script documentation issue - forum discussion. Retrieved on March 11, 2016, from https://getsatisfaction.com/tradingview/topics/pine-script-documentation-issue#reply_16650071