TradingView Backtesting

Kappa Ratios: Lower-Partial-Moment Order and Target Sensitivity

The Kappa ratio divides mean return above a target by the root of a lower partial moment: Kq(tau) = (mean return - tau) / LPMq(tau)^(1/q). The order q controls how shortfall size enters the denominator. Kappa 1, 2 and 3 can rank the same strategies differently, and changing the target can reverse that ranking. State the target, return frequency and order with every result.

See the decision guide

Specify the return sample before the order

Kaplan and Knowles define Kappa through a lower partial moment. For N equally weighted returns, set shortfall di = max(tau - ri, 0), LPMq = sum(di^q)/N, and Kq = (mean(r) - tau)/LPMq^(1/q). Here q is 1, 2 or 3; N is the full sample size. The mean is arithmetic. A positive denominator is required for a finite ratio.

  • This page uses consecutive, non-overlapping monthly simple account returns, net of the stated costs, with no external cash flows. Include month-end valuations of open positions. A trade return or cash profit is not a monthly account return.
  • Use the same dates, units, observation weights and target for both strategies. In decimal inputs, 1% is 0.01. A positive return below a positive target still has a shortfall.
  • If a hypothetical annual effective target is 12%, a constant compounded monthly target is (1.12)^(1/12) - 1 = 0.948879%. This converts a target; it does not annualize a Kappa ratio.
  • The qth root restores return units to the denominator, leaving Kappa dimensionless. Converting both returns and the target from decimals to percentage points leaves the ratio unchanged.
  • Keep these results monthly. Multiplying Kappa by 12 or sqrt(12) has no general justification. To evaluate annual returns, construct that return series and use an annual target; specify any model used to justify an alternative scaling.

One sample window, two strategies, three orders

Assume six hypothetical months. A returns -6%, 0%, 1%, 4%, 4%, 5%; B returns -2%, -2%, -2%, 4%, 4%, 5%. Their arithmetic means are 1.333333% and 1.166667% monthly. At tau = 0%, both have a total shortfall of 6 percentage points, but A concentrates it in one month. These are constructed examples, not measured trading results.

  • Using percentage-point inputs at tau = 0%, A has LPM1 = 6/6 = 1, LPM2 = 36/6 = 6, LPM3 = 216/6 = 36. Thus Kappa 3 = (8/6)/cube_root(36) = 0.403805.
  • B has LPM1 = 6/6 = 1, LPM2 = 12/6 = 2, LPM3 = 24/6 = 4. Its smaller mean wins under orders 2 and 3 because the rooted downside moments are smaller.
  • At tau = 1%, A shortfalls are 7, 1, 0, 0, 0, 0 percentage points; B shortfalls are 3, 3, 3, 0, 0, 0. A has LPM2 = 50/6 and excess mean = 1/3, giving Kappa 2 = 0.115470.
  • The stress row changes only A's first month from -6% to -12%. Its mean falls to 0.333333%; LPM1 doubles, LPM2 quadruples and LPM3 increases eightfold. Their roots all double in this single-shortfall case. The stress changes both numerator and denominator.
Target / scenarioStrategyKappa 1Kappa 2Kappa 3
0% monthlyA1.33330.54430.4038
0% monthlyB1.16670.82500.7350
1% monthlyA0.25000.11550.0864
1% monthlyB0.11110.07860.0700
0%; A loss changed to -12%A stressed0.16670.06800.0505

A decision table for the ranking changes

Compare strategies within a column at a fixed target. Comparing A's Kappa 1 directly with B's Kappa 3 mixes objectives. The numerical gap is not a percentage improvement in performance, and a larger order is not automatically a better statistic.

  • Choose the target and order before selecting a strategy. Report a target sensitivity table when the intended minimum acceptable return is uncertain; choosing the best-looking cell after testing adds selection bias.
  • Inspect shortfall counts and magnitudes alongside the ratio. Six months with one large loss cannot establish a stable tail-loss frequency.
  • Increasing q gives relatively larger shortfalls more weight before taking the root. Do not infer that the rooted denominator must double more than once just because the unrooted third moment increases eightfold.
ComparisonHigher Kappa 1Higher Kappa 2 / 3Interpretation
Original A and B, tau = 0%AB / BThe concentrated loss changes the ranking when shortfall powers increase.
Original A and B, tau = 1%AA / AA's larger excess mean now outweighs its downside denominator.
Stressed A and original B, tau = 0%BB / BOne worse observed loss removes A's Kappa 1 lead.

When Omega and Sortino are the same calculation

With the same sample, constant minimum acceptable return tau, weights and positive downside denominator, Kappa 1 = Omega - 1. Define U = mean(max(r - tau, 0)) and D = mean(max(tau - r, 0)). Since mean(r) - tau = U - D, Kappa 1 = (U - D)/D = U/D - 1. In the original A example at tau = 0%, Omega is 14/6 = 2.333333 and Kappa 1 is 1.333333.

  • Kappa 2 equals Sortino when Sortino uses the same arithmetic excess mean and downside deviation sqrt(mean(max(tau - r, 0)^2)), averaged over all N observations without annualization.
  • A downside average over only below-target observations is a different denominator. For monthly returns -2%, 2%, 2%, 2% at tau = 0%, the mean is 1%; full-sample downside deviation is 1%, giving Kappa 2 = 1. Using the single losing month as the divisor gives downside deviation 2% and a ratio of 0.5.
  • A Sortino output with an annualized numerator, a geometric mean, a different target or a conditional downside average need not equal this Kappa 2. Reconcile definitions before comparing labels.

Negative values and missing downside observations

A negative Kappa means the sample arithmetic mean is below tau when the denominator is positive. More negative is not a complete risk ranking: increasing a positive denominator makes a fixed negative numerator closer to zero. This can make a riskier sample look better under a naive descending sort.

  • D has a higher Kappa 2 and 3 than C because the negative numerator is divided by a larger risk measure. Report the mean shortfall and loss pattern instead of declaring D safer.
  • With a positive denominator and mean exactly equal to tau, Kappa is zero. With a zero denominator, this page returns no ratio and distinguishes no observed shortfalls from an all-at-target sample.
  • A tiny positive denominator can create a very large value. Keep full precision until display and report N, the shortfall count and the rooted moment.
  • An unobserved crash contributes nothing to empirical LPM. Higher orders can depend heavily on a few observations; a finite sample does not establish that the population qth lower moment exists or is estimated accurately.
  • Reordering the same returns leaves Kappa unchanged but can change the drawdown path. Keep drawdown, dates and exposure with the ratio. Compare an untouched period and realistic cost assumptions before interpreting a ranking as persistent.
Independent example at tau = 0%Kappa 1Kappa 2Kappa 3What to retain
C: -1%, -1%, -1%, -1%-1.0000-1.0000-1.0000Mean -1%; four equal losses
D: -4%, 0%, 0%, 0%-1.0000-0.5000-0.3969Same mean; one concentrated loss
1%, 2%, 3%No finite ratioNo finite ratioNo finite ratioPositive numerator, zero downside denominator
0%, 0%, 0%UndefinedUndefinedUndefined0/0; every return equals the target

Reproduce the tables in Python

Run this standard-library Python code with decimal monthly returns. It prints all ranking rows and the independent counterexamples. The function rejects empty or non-finite inputs, uses all observations in every moment, and returns None with a reason when the ratio has no finite value. Missing calendar periods must be resolved before calling it; the function cannot infer dates or reconstruct equity.

Unannualized empirical Kappa; hypothetical monthly account returnspython
from math import fsum, isfinite, isclose

def kappa(returns, tau=0.0, order=3):
    r = tuple(returns)
    if (not r or order not in (1, 2, 3)
            or not isfinite(tau) or not all(isfinite(x) for x in r)):
        raise ValueError("Use finite returns, a finite target, and order 1, 2 or 3")
    shortfalls = [max(tau - x, 0.0) for x in r]
    excess = fsum(x - tau for x in r) / len(r)
    lpm = fsum(d ** order for d in shortfalls) / len(r)
    downside = lpm ** (1.0 / order)
    reason = "finite"
    if downside == 0:
        reason = "all at target" if all(x == tau for x in r) else "no observed shortfall"
    return {
        "n": len(r), "below_target": sum(d > 0 for d in shortfalls),
        "excess": excess, "lpm": lpm, "downside": downside,
        "ratio": excess / downside if downside > 0 else None,
        "reason": reason,
    }

A = [-.06, 0, .01, .04, .04, .05]
B = [-.02, -.02, -.02, .04, .04, .05]
stressed_A = [-.12, 0, .01, .04, .04, .05]
cases = [
    ("A at 0%", A, 0), ("B at 0%", B, 0),
    ("A at 1%", A, .01), ("B at 1%", B, .01),
    ("Stressed A", stressed_A, 0),
    ("C", [-.01] * 4, 0), ("D", [-.04, 0, 0, 0], 0),
    ("No shortfalls", [.01, .02, .03], 0),
    ("All at target", [0, 0, 0], 0),
]
for name, sample, tau in cases:
    results = [kappa(sample, tau, q) for q in (1, 2, 3)]
    print(name, [None if x["ratio"] is None else round(x["ratio"], 4)
                 for x in results], results[0]["reason"])

assert isclose(kappa([-.02, .02, .02, .02], 0, 2)["ratio"], 1.0)
assert kappa(A, 0, 1)["ratio"] > kappa(B, 0, 1)["ratio"]
assert kappa(A, 0, 2)["ratio"] < kappa(B, 0, 2)["ratio"]
assert kappa(A, .01, 2)["ratio"] > kappa(B, .01, 2)["ratio"]
print("Monthly target for assumed 12% annual target:", (1.12 ** (1/12) - 1))

Use TradingView exports without changing the sample definition

TradingView exports each report tab separately. Its List of Trades CSV provides trade records, not a complete monthly mark-to-market account series. Record the strategy settings, costs and initial capital; reconcile exported trades before building monthly returns. Include idle months and open-position valuations, and document any cash-flow adjustment.

  • Backtest Deep Report accepts supported TradingView trade CSV data and calculates trade-based results such as net profit and profit factor. Its current metrics do not include Kappa. Run the calculation above separately on the required periodic return series.
  • The report's current Sortino calculation uses trade percentage returns, a downside average over negative trades and a trades-per-year scaling. It does not reproduce the full-sample monthly Kappa 2 defined here. A matching label is insufficient evidence of matching inputs or formulas.
  • Pine Script AI Coding Agent can help draft custom strategy or calculation code. Specify the sampling boundaries, target units, all-N divisor and missing-denominator behavior; review the result and reconcile it against this independent example.
Where Pineify fits

Backtest Deep Report

Inspect supported results from exported TradingView trades before calculating Kappa separately on a documented periodic return series.

Also useful: Pine Script AI Coding Agent. Draft custom strategy code with explicit sampling and target rules, then review and validate it against an independent calculation.

Review the trade-report workflow

Frequently asked questions

Educational information, not investment advice. Every numerical example is hypothetical. Kappa summarizes a stated historical sample and target; it does not guarantee returns or measure every trading risk.

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.