At Backtest Rookies, we love to hear from our readers. Some time ago we received an article request from a reader who was having trouble interpreting some of the key statistics in the strategy testers performance summary tab.
I love TradingView’s performance stats in the strategy tester, but i don’t understand half of them. Can you add a post on what these stats mean and how to use them to determine the performance of your strategy?
Today we finally get around to addressing this topic! So without further ado, let’s get to it. In the interests of completeness, every metric will be discussed and a brief commentary shall be given, even if the metric does seem self-explanatory.
For further tutorials and content aimed at pine script beginners see our Getting Started page.
Note: If you like this article and are thinking to become a Tradingview subscriber, you can support the site by clicking on the affiliate link below before signing up!
Performance Summary – Testing Code
Seeing is believing and as such, testing with code and seeing how it affects each metric will really help with understanding the performance summary. Therefore, we will use the following test code during some of the discussion and examples below. The code shall simply open and close a trade on the exact dates we give it. Since we can control the entry and exit dates, this will allow us to measure, control and verify the results.
strategy("Metrics Investigation", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=20)
T1_On = input(true, title='Trade 1')
T1_Enter_Year = input(defval = 2018, title = "Trade 1 Entry Year")
T1_Enter_Month = input(defval = 1, title = "Trade 1 Entry Month", minval = 1, maxval = 12)
T1_Enter_Day = input(defval = 1, title = "Trade 1 Entry Day", minval = 1, maxval = 31)
T1_Exit_Year = input(defval = 2018, title = "Trade 1 Exit Year")
T1_Exit_Month = input(defval = 2, title = "Trade 1 Exit Month", minval = 1, maxval = 12)
T1_Exit_Day = input(defval = 1, title = "Trade 1 Exit Day", minval = 1, maxval = 31)
T1_Enter = timestamp(T1_Enter_Year, T1_Enter_Month, T1_Enter_Day, 00, 00) // backtest start window
T1_Exit = timestamp(T1_Exit_Year, T1_Exit_Month, T1_Exit_Day, 00, 00) // backtest finish window
T2_On = input(false, title='Trade 2')
T2_Enter_Year = input(defval = 2018, title = "Trade 2 Entry Year")
T2_Enter_Month = input(defval = 3, title = "Trade 2 Entry Month", minval = 1, maxval = 12)
T2_Enter_Day = input(defval = 1, title = "Trade 2 Entry Day", minval = 1, maxval = 31)
T2_Exit_Year = input(defval = 2018, title = "Trade 2 Exit Year")
T2_Exit_Month = input(defval = 4, title = "Trade 2 Exit Month", minval = 1, maxval = 12)
T2_Exit_Day = input(defval = 1, title = "Trade 2 Exit Day", minval = 1, maxval = 31)
T2_Enter = timestamp(T2_Enter_Year, T2_Enter_Month, T2_Enter_Day, 00, 00) // backtest start window
T2_Exit = timestamp(T2_Exit_Year, T2_Exit_Month, T2_Exit_Day, 00, 00) // backtest finish window
T3_On = input(false, title='Trade 3')
T3_Enter_Year = input(defval = 2018, title = "Trade 3 Entry Year")
T3_Enter_Month = input(defval = 5, title = "Trade 3 Entry Month", minval = 1, maxval = 12)
T3_Enter_Day = input(defval = 1, title = "Trade 3 Entry Day", minval = 1, maxval = 31)
T3_Exit_Year = input(defval = 2018, title = "Trade 3 Exit Year")
T3_Exit_Month = input(defval = 6, title = "Trade 3 Exit Month", minval = 1, maxval = 12)
T3_Exit_Day = input(defval = 1, title = "Trade 3 Exit Day", minval = 1, maxval = 31)
T3_Enter = timestamp(T3_Enter_Year, T3_Enter_Month, T3_Enter_Day, 00, 00) // backtest start window
T3_Exit = timestamp(T3_Exit_Year, T3_Exit_Month, T3_Exit_Day, 00, 00) // backtest finish window
T1 = T1_On ? time >= T1_Enter and time <= T1_Exit : false
T2 = T2_On ? time >= T2_Enter and time <= T2_Exit : false
T3 = T3_On ? time >= T3_Enter and time <= T3_Exit : false
bgcolor(T1 or T2 or T3 ? green : na, transp=75)
strategy.entry("Trade 1", strategy.long, when=T1)
strategy.close("Trade 1", when=not T1)
strategy.entry("Trade 2", strategy.long, when=T2)
strategy.close("Trade 2", when=not T2)
strategy.entry("Trade 3", strategy.long, when=T3)
strategy.close("Trade 3", when=not T3)
The formal explanation from the internet is:
The actual profit after working expenses not included in the calculation of gross profit have been paid
However, we can just think of this as our final profit (or loss) after paying commission. The commission is our only working expense when backesting.
You can see this change by:
- Placing the test code on the gold chart
- Opening the strategy properties tab
- Adding a commission
This is a confusing one so beware!!! To most people, the term gross profit would be the opposite of Net Profit. I.e the actual profit achieved before deducting commissions. This is also implied from the formal explanation of
Net Profit. However, in Tradingview, it is actually the SUM of profit from all winning trades. Furthermore, this is the SUM of
Net Profit from all winning trades!
So let’s take a look. Using our example code load it up and add a commission as we did in the first example. We will see that although a commission was paid, the gross profit and net profit are the same!
In the second example, we activate our second trade. Due to the high commission, we added (1%), the second trade results in a loss. It is here we can see how Gross Profit is actually calculated on Tradingview.
If you are just skipping straight to the section(s) you are interested in, make sure that you read
Gross Profitfirst. This is because
Gross loss is just the opposite. It is the SUM of the PNL or all losing trades including the commission cost.
Drawdown is generally understood to be:
A drawdown is the peak-to-trough decline during a specific recorded period of an investment, fund or commodity security. A drawdown is usually quoted as the percentage between the peak and the subsequent trough.
So a drawdown is a difference between your account value before your drawdown starts (i.e you lose a trade or multiple trades) and your account value at the end of your last losing trade. We can show this by plotting the built-in
strategy.max_drawdownparameter from Tradingview on the chart and comparing it to our list of trades.
To demonstrate how this works, we will deviate from the example code above. It does not have enough trades to provide a good overview. Instead, we use the built-in simple moving average strategy from the template that appears anytime you click on “new blank strategy script”:
We will then will simply plot
strategy.max_drawdown and limit the start date for trading to help demonstrate the logic. This will result in the following chart:
As we can see from the chart
Max Drawdownwill only give you the figure from your single largest drawdown period. Other smaller drawdown periods are ignored.
It is worth noting that Tradingview changed the way they calculate the maximum drawdown in 2018.
From now on, strategy.max_drawdown variable as well as Max Drawdown fields in the report are going to be calculated using a new formula that is based on NetProfit.
Note that we also used to include OpenProfit in the past.
Finally, you should also be aware that if you scale into positions (make multiple entries), it can be a bit harder to make a nice link between your trade list and the
Max Drawdown. This is because each entry is listed separately in the list of trades. Furthermore, each entry is made at different price levels. Some might be in profit whilst the others are not. However, the general principle remains the same.
Buy and Hold Return
Buy and hold return is a very useful benchmark to help you decide if all your effort is really worth it. It measures what your PNL would be if you just bought an asset and held it until the end of the test. If you cannot beat the buy and hold return then deploying an active trading strategy makes no sense. Of course, as with anything, there are some caveats to this. The first is that if your strategy spends very little time in the market, you might see the reduced risk of getting caught in a large downturn as a benefit. Additionally, depending on the length of your test, you may choose to ignore the buy and hold returns as it may not represent a clear comparison of the performance through different market regimes/cycles.
In order to more accurately compare your strategy to a buy and hold strategy, Tradingview will measure the buy and hold from the first time your strategy buys and it will hold it until the end of the dataset (irrespective of when your last trade finishes). It would be an unfair comparison if it bought at the start of the data feed and held until the end. This is especially so if you are on the higher timeframes where data may stretch back a decade or more.
You can test this with the example code:
- Place the code on the charts.
- Move the start/end dates of the first trade backwards and forwards – You will notice the buy and hold returns change.
Sharpe Factor is more commonly knowns as the Sharpe Ratio. It compares the returns you generate to the returns you could get from a risk-free asset. A risk-free asset is an asset which has a guaranteed future return or virtually-guaranteed return (like US government bonds – No one expects them to default). The Sharpe Ratio also takes the volatility of the returns you generate into account. It specifically looks at volatility as a way to measure risk. The reason for this is that if you are taking on extra risk, you should be receiving extra rewards! As such, the Sharpe ratio can tell you if your extra risk is worth the reward. The buzzword for this is “risk-adjusted” returns.
To interpret the result, this nice little rule of thumb from Investopedia is excellent:
a ratio of 1 or better is considered good; 2 or better is very good; and 3 or better is considered excellent.
The profit factor is a simple ratio between the Gross Profit and Gross Loss values.
To test this you can place the example code on the XAUUSD daily chart and then enable Trades 1 and 3. This will give you one winning and one losing trade. Using the Gross Profit and Gross Loss values will enable you to easily verify this ratio:
- $482.17 / $156.31 = 3.0847 which is then rounded up to 3.085
Max Contracts Held
This is the largest single position you have held at a time. You can verify this by scrolling through your list of trades and comparing it to the contracts column of each trade.
Note: If you have pyramiding turned on and make multiple long or short entries, your
Max Contracts Held figure will be the sum of all the individual entries on your largest trade. In this case, you cannot simply compare the contracts column as it lists each entry separately.
If you still have an open position at the end of your backtest, the current PnL for that position will be listed here.
This is a simple sum of all the commissions paid. Unless you add a commission during backtesting, this value will be $0. To test this metric, place the example strategy on a chart, open the strategy properties tab and add a commission. To make things even easier to verify, place a fixed commission of x dollars per order. You will then be able to clearly see a link between the $ value and the number of trades in your list.
It is worth noting that a trade consists of 2 orders. One to open it and one to close it!
Total Closed Trades
This is the total number of trades closed. Instead of providing a single “total trades” metric, Tradingview breaks it down into closed and open trades as by definition, an open trade is not completed yet and we do not know how it will affect the rest of our statistics.
Total Open Trades
This is the number of open positions you have at the end of the backtest. If you have closed everything out, this value will be 0. If not, it will consider each entry as an open trade. This is only applicable if you have pyramiding turned on and have made multiple entries. Otherwise, the value will be 1.
Number Winning Trades
As described on the tin! It is the total number of winning trades!
Number Losing Trades
No change to the format here either… Again it is as described. The total number of losing trades.
This is the sum of all your trades and then divided by the number of trades. To verify this place the example code on the chart, load up XAUUSD on the daily chart and enable all 3 trades. The
Avg Trade is reported as $231.25 so let’s verify it.
Trade 1 ($482.18) + Trade 2 ($367.88) + Trade3 ($-156.32) = $693.74
$693.74/3 = $231.2466
And that rounds up to $231.25
Avg Win Trade
As with the
Avg Trade, this only considers winning trades when summing and dividing. So taking the figures from above we can easily verify that:
Trade 1 ($482.18) + Trade 2 ($367.88) / 2 = $425.03
Avg Los Trade
Similarly, if we use the same example as
Avg Trade and
Avg Win Trade, then we know that we have only had one losing trade. Therefore the
Avg Los trade is the same as the losing trade at $156.32
Ratio Avg Win / Avg Loss
Profit Factor provides the ratio of the
Gross Profit and
Gross Loss values,
Ratio Avg Win / Avg Loss does the same thing for its respective metrics. We can see this by dividing the
Avg Win by the
Avg Los. Doing this results in:
$425.03 / $156.32 = 2.71897
Largest Win Trade
This probably needs no introduction but it returns the largest winning trade from your whole list of trades. You can verify this by scrolling through the list of trades and checking that the number matches the largest. In the case of our example code, the largest winning trade on XAUUSD daily was $482.17.
Note: The eagle-eyed may notice that in our example the performance summary states $482.17 and the list of trades states $482.18. I can only assume this is a rounding bug on Tradingviews end. If you are reading this in the future and they match, it is probably fixed!
Largest Losing Trade
As a counter to the
Largest Win Trade, this metric provides the polar opposite. The largest losing trade from our list of trades.
Avg # Bars in Trades
This metric tracks the average number of bars we are in the market for. This is measured from the bar we enter to the bar we exit. Don’t just take my word for it though, verify! We can do this by using the date range drawing tool and placing our example code on the XAUUSD daily chart. Measure up the trades and you will have something that looks like this:
As we can see we have 22, 21 and 23 bars in trades. That makes a final calculation of 66/3 = 22…
Uh oh… Something doesn’t seem right. So what is going on here? If you zoom in and focus on the last trade. Then count each candle in the range, you will count 24 candles. That means 2 things.
- The exit bar is considered a bar in the trade even though we exit at the open.
- The date range tool gives you the number of bars to the end of the range excluding the starting bar.
Avg # Bars in Winning Trades
The same formula applies to
Avg # in Winning Trades. It simply just filters out all losing trades from the calculation.
Avg # Bars in Losing Trades
Avg # Bars in Losing Tradesprovides the last counter metric and filters out all the winning trades from the average calculation.