By configuring a strategy’s settings with code we can be sure that it’s always added to the chart with the right settings. One of those settings that we can define programmatically is the default order size. How do we do that in TradingView?

In this article:

Setting the default order size in TradingView programmatically

The strategy() function programmatically configures a strategy’s settings in TradingView. We have to add this function to every strategy script (Pine Script Language Tutorial, n.d.) and its title argument, which specifies the strategy’s name, always needs to be set too (TradingView, n.d.). By configuring our strategy with strategy(), the script always has the right settings and we don’t need to spend time manually configuring it.

A setting that we can configure with strategy() is the default order size, which we set with two arguments: default_qty_value and default_qty_type. The first argument specifies the default order size while the other defines how that default order size is calculated (TradingView, n.d.).

Note: A strategy’s default order size sets the number of contracts, shares, units, lots, or whichever quantity an instrument trades in (TradingView, n.d.). In this article we simply use the term “contracts”, but know that this can also refer to shares, units, or lots (depending on the instrument you’re trading).

There are three ways to set a strategy’s default order size, depending on how the default_qty_value and default_qty_type arguments are combined (TradingView, n.d.):

  • When default_qty_type is set to strategy.fixed or not set at all, then default_qty_value specifies the number of contracts to trade with each order. This approach is what we discuss in this article.
  • When default_qty_type is set to strategy.cash, then default_qty_value specifies how much of the strategy’s cash needs to be invested in each order. We examine this in setting a strategy’s order size to fixed amount of cash.
  • And when default_qty_type is set to strategy.percent_of_equity, then default_qty_value specifies which percentage of the strategy’s equity should be invested with each order. We discuss this approach in setting order size based on a percentage of equity.

So to set the strategy’s default order size to a fixed number of contracts, we use the strategy() function like this:

strategy(title="Example - fixed number of contracts", 
     default_qty_type=strategy.fixed, default_qty_value=3)

Since the default_qty_type argument defaults to strategy.fixed when omitted (TradingView, n.d.), the default order size of 3 contracts can also be set like this:

strategy(title="Example - fixed number of contracts", default_qty_value=3)

The default_qty_value argument has a standard value of 1. Meaning, when that argument and default_qty_type aren’t set, then each order has a default order size of 1 contract.

Tip: TradingView uses the default order size that we’ve set with the strategy() function as long as we don’t set the order size with the qty argument of the strategy.entry(), strategy.exit(), or strategy.order() functions. So depending on how we use those functions in our code, we can submit orders with a different size than set by `strategy().

Before we look at a programming example that uses default_qty_value, let’s see how we can set a strategy’s default order size by hand.

Manually configuring the default order size in TradingView

By setting the default order size by hand we override whichever value strategy() sets. That way we can quickly change a strategy’s settings without having to edit the code, and can also configure the same strategy differently on multiple charts.

To change the strategy’s default order size, we 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 brings up a window with the strategy’s settings. Here we select the ‘Properties’ tab where we specify the default order size with the ‘Order Size’ input:

Configuring a strategy's order size by hand in TradingView
Note: The manual order size setting takes precedence over the default_qty_value argument of the strategy() function. This way we can configure the same script differently on multiple charts, regardless of how we’ve set its values programmatically.

The ‘Order Size’ numerical option and its pull-down menu correspond to the default_qty_type and default_qty_value arguments as follows:

Comparing the manual TradingView settings with the strategy() function

To better understand a TradingView strategy’s default order size, let’s examine a programming example.

Specifying the order size of a TradingView strategy programmatically

In the programming example below we set the default order size with the strategy() function’s default_qty_value argument. The trading strategy trades three pivot points: the main, or central, pivot point and the first resistance (R1) and support (S1) levels. These are calculated as follows (Lien, n.d.):

  • Central Pivot Point = (High + Low + Close) / 3
  • First Resistance (R1) = (2 * Pivot Point) - Low
  • First Support (S1) = (2 * Pivot Point) - High

When a bar touches the central pivot point, our strategy checks the direction of the Exponential Moving Average (EMA): if the EMA rises, the strategy goes long; when the moving average drops, it goes short. The strategy can close a long position in three ways: with a stop-loss order at S1, a profit target at R1, or when a short signal happens. Likewise, shorts are exited with a stop-loss at R1, a take profit at S1, or with a long signal. After discussing the code, we’ll look at the strategy’s behaviour and the impact of changing the default_qty_value argument.

Example of a TradingView strategy that trades pivot points
strategy(title="Example - Trading Daily Pivots", 
     default_qty_value=3, overlay=true)

// Input option
emaLen = input(title="EMA Length", type=integer, defval=12)

// Get daily price data
dayHigh  = security(tickerid, "D", high[1])
dayLow   = security(tickerid, "D", low[1])
dayClose = security(tickerid, "D", close[1])

// Compute values
pivotPoint = (dayHigh + dayLow + dayClose) / 3

pivR1 = (2 * pivotPoint) - dayLow
pivS1 = (2 * pivotPoint) - dayHigh

emaValue = ema(close, emaLen)

// Plot values
plot(series=pivotPoint, style=cross, linewidth=2, color=orange)
plot(series=pivR1, style=circles, linewidth=3, color=#9932CC)
plot(series=pivS1, style=circles, linewidth=3, color=#9932CC)

plot(series=emaValue, color=#3CB371, linewidth=2)

// Determine order conditions
pivotTouched = (low < pivotPoint) and (high > pivotPoint)
timeFilter   = (year == 2016) and (month > 2)

enterLong    = pivotTouched and timeFilter and rising(emaValue, 1)
enterShort   = pivotTouched and timeFilter and falling(emaValue, 1)

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

strategy.exit(id="Exit Long", from_entry="Enter Long", 
     limit=pivR1, stop=pivS1)
strategy.exit(id="Exit Short", from_entry="Enter Short", 
     limit=pivS1, stop=pivR1)

We start with the strategy() function to configure strategy properties programmatically. With the title argument we set the strategy’s name and we overlay the strategy on the chart’s instrument with overlay=true (TradingView, n.d.). By setting the default_qty_value argument to 3, our strategy’s default order size are three contracts.

Then we create an input option:

emaLen = input(title="EMA Length", type=integer, defval=12)

Adding numerical input options to a script is done with input(), and this function also returns the input’s current value (TradingView, n.d.). Here we store that returned value in the emaLen variable with the assignment operator (=). That way we can refer to the input’s current value later on in the code by using the variable.

The kind of input that we make is a numerical integer input. Such an input only accepts whole numbers and is made by setting the type argument of the input() function to integer (Pine Script Language Tutorial, n.d.). We name the input “EMA Length” with the title argument, and this name is placed before the input in the script’s settings (see image further down below). With the input() function’s defval argument we give the input a default value of 12.

Before computing the daily pivots, we retrieve the data that we’re going to calculate with:

dayHigh  = security(tickerid, "D", high[1])
dayLow   = security(tickerid, "D", low[1])
dayClose = security(tickerid, "D", close[1])

The security() function requests data from other symbols and/or resolutions than the one the script currently is applied to (Pine Script Language Tutorial, n.d.). For this ‘data loading in the background’ we need to specify inside this function’s parentheses the symbol’s name, the resolution, and the kind of data that security() should return from that symbol and resolution (TradingView, n.d.).

We assign each of the three variables here (dayHigh, dayLow, and dayClose) the values returned by security(). Those function calls are much alike. Their first argument is tickerid, a built-in variable with the symbol and exchange prefix of the instrument that the script currently calculates on (TradingView, n.d.). With that variable each security() function loads data from the exact same symbol and exchange as the one that the script already calculates on.

The next shared feature is the second argument, "D". This argument specifies the data resolution to load, and with "D" security() loads daily prices (Pine Script Language Tutorial, n.d.).

The third argument of the security() function calls are the high, low, and close built-in variables to make the function return the daily high, low, and closing prices. The problem, however, is that this makes security() return the current day’s data while we need the previous day’s data to calculate the pivots on.

To fix that, we place the history referencing operator ([]) with a value of 1 just after each variable. This way the previous bar value is returned (see Pine Script Language Tutorial, n.d.), and so the dayHigh, dayLow, and dayClose variables don’t end up with the current day’s data but with that of the previous trading day.

With the data loaded, we calculate the pivot points and EMA:

pivotPoint = (dayHigh + dayLow + dayClose) / 3

pivR1 = (2 * pivotPoint) - dayLow
pivS1 = (2 * pivotPoint) - dayHigh

emaValue = ema(close, emaLen)

To compute the main pivot point we add the high, low, and close of the previous day together and divide that sum by 3. That value is then assigned to the pivotPoint variable. The pivR1 and pivS1 variables are calculated next, and for these we multiply the main pivot point with 2 and then either subtract the previous day’s low (dayLow) or high (dayHigh).

We calculate the exponential moving average with ema(). That function requires two arguments: a series of values as well as the number of bars to calculate on (TradingView, n.d.). Here we have the function compute on the instrument’s closing prices (close) with a length set by the emaLen input variable, which we gave a default value of 12 earlier. That 12-bar EMA of closing prices is stored in the emaValue variable for use later on.

Next we plot the computed values on the chart:

plot(series=pivotPoint, style=cross, linewidth=2, color=orange)
plot(series=pivR1, style=circles, linewidth=3, color=#9932CC)
plot(series=pivS1, style=circles, linewidth=3, color=#9932CC)

plot(series=emaValue, color=#3CB371, linewidth=2)

The plot() function plots the data from its series argument on the chart (TradingView, n.d.). Here that argument of the first plot() function call is set to the pivotPoint variable to display the main pivot. The style argument specifies the plot style (TradingView, n.d.) and, with a value of cross, displays the plot’s values in small ‘+’ signs.

The size of a plot is set with linewidth, and this argument starts at 1 for the default plot size (TradingView, n.d.). And so setting this argument to 2 makes the crosses appear a bit bigger than default. Finally, with the color argument we display the main pivot in the orange basic TradingView colour.

The second and third plot() function call display the R1 and S1 pivots. These plots show up as little dots (style=circles) in the #9932CC hexadecimal colour value of dark orchid. The last plot() statement plots the 12-bar EMA (series=emaValue). Given that we don’t specify the plot’s style argument here, it defaults to a line plot (TradingView, n.d.). We set the moving average plot colour to #3CB371, the hexadecimal colour value of medium sea green.

With the values computed and plotted on the chart, we determine two of the strategy’s trading conditions:

pivotTouched = (low < pivotPoint) and (high > pivotPoint)
timeFilter   = (year == 2016) and (month > 2)

We assign these variables the result of combining two true/false expressions with the and logical operator. This operator returns true when the value on its left and the value on its right are both true. When one or both values evaluate to false, then and returns false too (Pine Script Language Tutorial, n.d.). This way we can use and to check whether two situations happen at the same time.

The first true/false expression that affects the value of the pivotTouched variable is whether the low of the current bar (low) is less than (<) the main pivot (pivotPoint). The other expression evaluates if the bar’s high (high) is greater than (>) the main pivot. When both situations happen, the price bar’s range has touched the main pivot point and the pivotTouched variable is then assigned true. We use that variable later as a condition for opening an order.

The value of the timeFilter variable depends on two situations: whether the current bar’s year (year) equals (==) 2016 and whether that bar’s month (month) is greater than (>) 2. By combining these expressions, timeFilter is assigned true for all bars that happen in March 2016 and later. Since we didn’t assume any calendar influence on the strategy, what’s the point of this variable?

We’ll use the timeFilter variable later to limit the strategy’s orders to a recent time period. Unfortunately, this is currently needed because TradingView allows up to 2,000 orders. Without this time filter, the strategy runs into the ‘order limit was reached’ error message:

TradingView error: running into the order limit

We prevent that error from happening by using timeFilter to limit the number of orders. (Depending on when you run the example strategy and which time frame you use, you might need to update the values that set the timeFilter variable.)

Next we determine the actual trading conditions:

enterLong    = pivotTouched and timeFilter and rising(emaValue, 1)
enterShort   = pivotTouched and timeFilter and falling(emaValue, 1)

The enterLong and enterShort true/false variables hold our conditions for entering a position. Their value is set with three true/false expressions that are joined together with the and logical operator. As such, each needs to be true before the combined result of all three is true too (Pine Script Language Tutorial, n.d.).

The first expression that affects the value of the enterLong variable is pivotTouched, our true/false variable that we assigned true when the price bar touched the main pivot. We also require timeFilter to be true; that limits the number of long positions opened by the strategy. The last expression is the rising() function with the values of emaValue and 1 passed in as arguments.

That rising() function returns 1 – which TradingView considers the equivalent of true – when the value of its first argument has increased for the number of bars set by the second argument (TradingView, n.d.). And so rising(emaValue, 1) returns true when our 12-bar EMA rises for at least one bar (that is, from the previous bar to the current). And when the moving average isn’t rising, then rising() returns the equivalent of false (TradingView, n.d.).

By combining these three true/false expressions with the and logical operator, the enterLong variable only holds true when the current bar touched the pivot, the bar’s date is in March 2016 or later, and the 12-bar moving average has been rising for at least one bar.

The enterShort variable is assigned its true/false value in much the same way. Here we also require that pivotTouched and timeFilter are true, and we use the falling() function to check whether the EMA (emaValue) has been dropping from the previous bar to the current. When the three true/false expressions all evaluate to true, enterShort is set to true too; otherwise, this variable holds false.

Then we use the order conditions to enter long and short positions:

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

The strategy.entry() function enters a position with a market order by default and, when there’s already an open position in the other direction, reverses that position (TradingView, n.d.). We use three arguments of this function here.

The order identifier is specified with the id argument. That name displays on the chart and in the ‘Strategy Tester’ window, but other functions also use this identifier to reference the order (as we’ll see below). With the long argument set to true, strategy.entry() opens a long position while, with long=false, it enters a short position (TradingView, n.d.). And we use the when argument to specify when to send the order: if this argument’s value is true, then the order is submitted; if when is false, then the order isn’t send (TradingView, n.d.).

Note: We don’t specify the order size here with the qty argument of the strategy.entry() function. If we did, we would override the strategy’s default order size that we set with the default_qty_value argument of the strategy() function in the beginning of the programming example.

The first strategy.entry() statement opens a long position (long=true) that’s named “Enter Long”. TradingView submits this order when the current bar value of enterLong is true. The other strategy.entry() function call opens a short position (long=false) when our enterShort variable holds true. “Enter Short” is the appropriate name for that order.

We conclude the programming example with the stop-loss and take profit orders:

strategy.exit(id="Exit Long", from_entry="Enter Long", 
     limit=pivR1, stop=pivS1)
strategy.exit(id="Exit Short", from_entry="Enter Short", 
     limit=pivS1, stop=pivR1)

We close the strategy’s positions with strategy.exit(), a function with different ways to exit a market position depending on which arguments we set (TradingView, n.d.). In addition, this function is smart enough to only submit exit orders when there’s an open position (Pine Script Language Tutorial, n.d.). That means we can call this function unconditionally without having to worry that it submits an order that opens a new position.

Both strategy.exit() statements use the same four arguments. We specify the order name with id. With from_entry we set which entry order strategy.exit() exits from (TradingView, n.d.). By specifying the limit argument we have the function submit a limit order for that argument’s value. Likewise, setting the stop argument makes strategy.exit() submit a stop-loss order for that value (TradingView, n.d.). This shows how one strategy.exit() function call can submit both a stop-loss and take profit order for the same position.

Note that here we also don’t set the qty argument of the strategy.exit() function. Instead, we rely on the default order size (default_qty_value=3) that we specified with the strategy() function earlier.

Our first strategy.exit() function call submits an order named “Exit Long” and that exits from our “Enter Long” order. This shows how the exit order ‘connects’ with a particular entry by referencing that entry order’s identifier. With the limit argument set to the pivR1 variable we submit a take profit order for the R1 pivot level. And we have strategy.exit() submit a stop-loss order for our long position at the S1 pivot (stop=pivS1).

The second strategy.exit() function call is much the same. We appropriately name this order “Exit Short” and have it close the “Enter Short” trade. We submit a limit order with our take profit target at S1 (limit=pivS1) and use the R1 pivot level as a stop-loss (stop=pivR1).

This ends the discussion of the strategy’s code; now let’s see how the script behaves on the chart.

Trading pivots with stop and limit orders in TradingView

The above programming example has the following input option:

Example of the input option of our TradingView strategy example

When we add the script to a DAX Index CFD chart, it behaves like this:

Example chart: trading pivot points in TradingView programmatically

Here the strategy opens a long position at the start of the trading day. That position is later reversed into a short trade, which reaches the profit target on its entry bar. Later in the day the strategy enters another long position, and this trade also reaches its profit target.

Another chart example of our trading strategy is:

Example of our TradingView strategy that trades pivot points

As these charts show, the strategy trades 3 contracts per order (trades that reverse a position are for 6 contracts: 3 that close the position and 3 contracts opened in the other direction). Since we haven’t enabled the strategy’s pyramiding, the strategy is only allowed to trade one entry in the same direction. And so our maximum position size is also 3 contracts. We also see that with the ‘Max Contracts Held’ metric of the ‘Strategy Tester’ window:

Overview of the strategy performance in TradingView

Now let’s look at how adjusting the default order size changes the strategy’s behaviour and performance.

Changing a TradingView’s strategy default order size programmatically

In the above programming example, we configured the script with the strategy() function as follows:

strategy(title="Example - Trading Daily Pivots", 
     default_qty_value=3, overlay=true)

With the default_qty_value argument set to 3, each trade is sized with 3 contracts by default. To triple that order size, we set default_qty_value to 9:

strategy(title="Example - Trading Daily Pivots", 
     default_qty_value=9, overlay=true)

After we save the script, TradingView reloads the strategy and each order now trades 9 contracts at a time:

TradingView example strategy trading more contracts per trade

Likewise, in the ‘Strategy Tester’ window we also now see a maximum position size of 9 contracts:

Overview of the performance summary window in TradingView with the trade size highlighted

Summary

The strategy() function configures a strategy’s properties and needs to be added to the code of every strategy. With this function’s default_qty_value and default_qty_type arguments we can set the default order size to a certain number of contracts, shares, units, or lots. To do that we set default_qty_type to strategy.fixed (or don’t specify this argument since it already defaults to strategy.fixed). Then we set the default size of each order with default_qty_value. Since that latter argument defaults to 1, when we omit both arguments the strategy submits orders for 1 contract. We can override whichever order sizing that default_qty_type and default_qty_value set in two ways. First, by manually changing the ‘Order Size’ option in the strategy properties window. Second, by setting the qty argument of the strategy.entry(), strategy.exit(), and strategy.order() functions to a custom order size when we programmatically submit an order.

Learn more:


References

Lien, K. (n.d.). Pivot Strategies: A Handy Tool For Forex Traders. Retrieved on April 16, 2016, from http://www.investopedia.com/articles/forex/05/fxpivots.asp

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