TradingView Backtesting

Kendall Tau for Signal Research: Concordance and Ties

Kendall tau measures whether higher signal scores tend to precede higher returns by comparing pairs of observations. Tau-b adjusts the denominator for tied scores and tied outcomes. In the six-observation example below, 11 pairs agree, one disagrees and three contain ties, giving tau-b = 0.769231. That describes historical ordering; it does not measure win rate, return size or profit after costs.

See the decision guide

Build a signal ranking validation table

These are hypothetical observations for one instrument over six successive sessions, not TradingView test results. The score is fixed at the preceding session close. The outcome is the following session open-to-close return: y = 100 × (close / open - 1), in percent. Higher scores predict higher returns. All prices use the same arbitrary currency unit, and costs are excluded. Session 0 precedes session 1; observation A is only complete after session 1 closes.

  • The values 1%, 3%, 2%, 2%, 4% and 4% are exact scenario inputs. In real exports, keep full precision and define any rounding or score buckets before testing; rounding can manufacture ties.
  • The score at session 1 close cannot predict the return that ended at that same close. It belongs with session 2. Discard the final score if its future outcome is not yet observable.
  • Use every observation allowed by the sampling rule, including weak scores and losses. Selecting only executed or winning trades tests a different, selected population.
Observation / score timestampScore xNext session openNext session closeFuture return y
A / session 0 close11001011%
B / session 1 close21001033%
C / session 2 close21001022%
D / session 3 close31001022%
E / session 4 close41001044%
F / session 5 close41001044%

Classify each of the 15 unordered pairs once

For rows i and j, compare the signs of x_i - x_j and y_i - y_j. Equal nonzero signs are concordant: the higher score has the higher outcome. Opposite signs are discordant. If either difference is zero, assign the pair to exactly one tie category. Iterate i < j so neither a row paired with itself nor a reversed duplicate enters the count.

  • AB agrees: score 2 exceeds 1, and return 3% exceeds 1%. BD disagrees: score 3 exceeds 2, but return 2% is below 3%.
  • BC is tied only in the score. CD is tied only in the return. EF is tied in both, so it must not also enter Tx or Ty.
  • The audit identity is C + D + Tx + Ty + Txy = n(n - 1)/2. Here 11 + 1 + 1 + 1 + 1 = 15 = 6 × 5 / 2.
CategoryConditionPairsCount
Concordant CScore order and return order agreeAB, AC, AD, AE, AF, BE, BF, CE, CF, DE, DF11
Discordant DScore order and return order disagreeBD1
Only X tie TxEqual score; different returnBC1
Only Y tie TyDifferent score; equal returnCD1
Both tie TxyEqual score and equal returnEF1

Calculate tau-b without double-counting ties

Using the exclusive tie counts above, tau-b = (C - D) / sqrt((C + D + Tx) × (C + D + Ty)). The numerator is 11 - 1 = 10. The denominator is sqrt(13 × 13) = 13, so tau-b = 10 / 13 = 0.769231. Ties in both variables contribute to the total pair audit but not to either factor in this form of the denominator.

  • An equivalent check uses N = 15 total pairs, all X ties = Tx + Txy = 2 and all Y ties = Ty + Txy = 2. The denominator is sqrt((N - all X ties) × (N - all Y ties)) = 13.
  • Without ties, tau-b reduces to (C - D) / N. Dividing this tied example by all 15 pairs instead gives tau-a = 0.666667, a different normalization.
  • Among the 12 pairs with no tie, 11 / 12 = 91.67% agree. Tau-b is 0.769231, not that agreement percentage and not the probability that a trade wins.
  • If either score or return is constant, one denominator factor is zero and tau-b is undefined. Report that condition; replacing it with zero would falsely imply a measured absence of rank association.

Recompute the pair counts outside the report

This Python example uses only the standard library. It accepts complete finite numeric pairs, rejects samples with fewer than two rows and returns None for an undefined coefficient. The explicit pair loop is intended for a small audit sample; its work grows quadratically with the number of rows.

  • For an independent implementation check, scipy.stats.kendalltau([1, 2, 2, 3, 4, 4], [1, 3, 2, 2, 4, 4], variant="b").statistic should match 10 / 13. The statistic and the p-value are separate outputs.
  • Test increasing distinct values against themselves for +1, against reversed values for -1, and a constant series for an undefined result. Reject missing or infinite values rather than silently turning them into zero returns.
  • Decide how to handle missing paired observations before estimating tau. Keep a count and reason for excluded rows; never drop a score and its return independently.
Expected counts: concordant 11, discordant 1, onlyXtie 1, onlyYtie 1, bothtie 1. Denominator 13.0; tau-b 0.7692307692307693.python
from itertools import combinations
from math import isfinite, sqrt

def tau_b(rows):
    if len(rows) < 2:
        raise ValueError("At least two observations are required")
    if any(not isfinite(v) for row in rows for v in row):
        raise ValueError("Use complete finite numeric observations")
    counts = dict(concordant=0, discordant=0,
                  onlyXtie=0, onlyYtie=0, bothtie=0)
    for (xi, yi), (xj, yj) in combinations(rows, 2):
        sx = int(xi > xj) - int(xi < xj)
        sy = int(yi > yj) - int(yi < yj)
        if sx == 0 and sy == 0:
            key = "bothtie"
        elif sx == 0:
            key = "onlyXtie"
        elif sy == 0:
            key = "onlyYtie"
        else:
            key = "concordant" if sx == sy else "discordant"
        counts[key] += 1
    c, d = counts["concordant"], counts["discordant"]
    denominator = sqrt((c + d + counts["onlyXtie"]) *
                       (c + d + counts["onlyYtie"]))
    value = (c - d) / denominator if denominator else None
    return counts, denominator, value

rows = [(1, 1), (2, 3), (2, 2), (3, 2), (4, 4), (4, 4)]
print(tau_b(rows))

Kendall vs Spearman on the same observations

Spearman correlation is Pearson correlation of the two rank series, assigning average ranks to ties. For these rows, the score ranks are [1, 2.5, 2.5, 4, 5.5, 5.5] and return ranks are [1, 4, 2.5, 2.5, 5.5, 5.5]. Each rank mean is 3.5. The centered cross-product sum is 14.25 and each centered sum of squares is 16.5, giving rho = 14.25 / sqrt(16.5 × 16.5) = 0.863636.

  • The larger Spearman number does not mean the signal became better. The two coefficients use different scales and summarize ordering differently; compare the same metric across tests.
  • Use average ranks for Spearman when ties exist. The shortcut 1 - 6 × sum(rank differences squared) / (n × (n² - 1)) assumes no ties and is not the calculation above.
  • Neither statistic uses return magnitude after ordering. A monotone transformation that preserves all ties leaves the coefficient unchanged. A high rank correlation can coexist with returns too small to cover spread, fees or slippage.
ChoiceWhat it calculatesThis sampleResearch use
Kendall tau-bNet agreement across pairs with tie adjustment0.769231Audit which pairs support or contradict a signal ordering
Spearman rhoCorrelation of average ranks0.863636Compare the positions of observations in the two rankings

Small samples and coarse scores limit the evidence

Tau-b handles ties in its calculation; it does not make a six-row sample reliable. With only two distinct observations the coefficient is always +1 or -1. A score with only a few possible levels can produce many ties, so publish the five pair counts and the number of unique scores with the coefficient.

  • SciPy allows method="exact" for its Kendall p-value only when there are no ties. The six-row example contains ties, so do not label an asymptotic p-value as an exact test.
  • A pairing permutation test can provide a small-sample reference distribution when the null permits observations to be exchanged. Repeated daily scores, overlapping forward returns and common shocks can break that assumption; shuffling individual rows is not automatically a valid trading test.
  • Choose a dependence-aware design before making inference. Non-overlapping outcome windows remove mechanical overlap but do not prove independence. With too little data to model dependence, report the coefficient descriptively and collect more observations.
  • Keep model selection separate from evaluation. Freeze the score formula, return horizon, tie policy and sample inclusion rules before testing later data. If many variants were tried, record that search rather than presenting the best tau as a single untouched test.

Keep the signal timestamp earlier than its outcome

TradingView chart exports can include symbol data and indicator values. Save the score with its original timestamp, instrument, timeframe and session settings, then construct the future-return column separately. A historical plot can look correctly ordered even when the value was revised or moved into the past after the decision time.

  • Check that the plotted score was available at the stated close. Unconfirmed higher-timeframe inputs, future-looking calculations and backdated pivot plots require a separate availability audit. A confirmation time is not the earlier bar where a marker is drawn.
  • Record the return start, end, price adjustment and cost convention. This example starts at the next session open. A close-to-close label includes the overnight move and answers a different question.
  • For a contrarian score, specify the intended direction before evaluating. Do not reverse its sign after seeing the holdout result and call that the original hypothesis.
  • For multiple instruments, decide whether to compare signals within each date or across time within one instrument. Pooling every date and symbol can mix market regimes and cross-sectional effects into one number.

Use Pineify for the supported next step

Backtest Deep Report imports TradingView List of Trades CSV files and calculates supported trade metrics such as net profit and drawdown. Its trade records include entry and exit times, prices and P&L. Those records do not supply a numeric score for every eligible observation or the fixed-horizon labels used here, and the current report does not expose Kendall tau-b.

  • Keep the pair-count worksheet or Python output beside the trade report. Audit rank association separately from the trading rules, sizing, costs and resulting P&L.
  • Pine Script AI Coding Agent can help write a script from a description. Specify the score, when it becomes available and which value to export. Review and validate generated Pine code, then reconcile exported observations with a known example before trusting a custom statistic.
Where Pineify fits

Backtest Deep Report

Inspect supported profit and drawdown results from a TradingView trade CSV. Keep the independent tau-b audit with your signal and future-return data.

Also useful: Pine Script AI Coding Agent. Describe a signal and its timing rules, then review and validate the generated Pine Script before exporting observations.

Explore Backtest Deep Report

Frequently asked questions

Educational information, not investment advice. The six observations are hypothetical and exclude trading costs. Historical rank association does not establish causation, executable fills or future profitability.

Sources and verification

Published by Pineify. Pineify is an independent product and is not affiliated with TradingView. Sources support the technical explanations; examples do not establish trading performance.