KDJ indicator on TradingView: formula and limits
KDJ starts with the close's position inside a recent high-low range, recursively smooths it into K and D, then calculates J = 3K - 2D. J can exceed 100 or fall below zero. To reproduce a KDJ chart, match the smoothing, initial values, first calculation bar, and flat-price rule as well as the familiar 9, 3, 3 settings.
A reproducible KDJ calculation
The 9, 3, 3 version uses the highest high H and lowest low L over nine chart bars, including the current bar. For a nonzero range, calculate RSV and update K before D:
RSV = 100 × (close - L) / (H - L)
K = (RSV + 2 × previous K) / 3
D = (current K + 2 × previous D) / 3
J = 3 × K - 2 × D
Moomoo documents these KDJ equations. This is recursive smoothing: a divisor of 3 assigns one third of the weight to the new input and two thirds to the previous result. It is not a three-observation simple average or a bicubic calculation.
Initialization and flat-price policy are part of the formula
The example below waits for N loaded bars, starts the previous K and D at 50, and uses RSV = 50 when H = L. These are explicit implementation choices. A zero seed, a first-RSV seed, a partial lookback window, or carrying the previous RSV on a flat window can all change the early values.
For example, with a first RSV of 100, a 50 seed gives K = 66.67 and D = 55.56. A zero seed gives K = 33.33 and D = 11.11. The same lengths do not imply the same series. With divisor 3 and identical subsequent RSV values, a difference in K decays by a factor of 2/3 per update; D also carries its own initial difference and the differences in K.
Worked example: J crosses 100 without K or D doing so
This is a hypothetical arithmetic fixture, not market data or a backtest. Assume each nine-bar window has H = 110, L = 100, and close = 110. RSV is 100. Start previous K = D = 50, keep both divisors at 3, and retain full precision between updates.
| Valid update | RSV | K | D | J |
|---|---|---|---|---|
| 1 | 100 | 66.6667 | 55.5556 | 88.8889 |
| 2 | 100 | 77.7778 | 62.9630 | 107.4074 |
| 3 | 100 | 85.1852 | 70.3704 | 114.8148 |
| 4 | 100 | 90.1235 | 76.9547 | 116.4609 |
On update 2, J = 3 × 77.7778 - 2 × 62.9630, approximately 107.4074 before display rounding. J adds twice the K-D gap to K. It has no 0-to-100 clamp. Mirroring the fixture with RSV = 0 produces J = -7.4074 on update 2.
K and D stay between 0 and 100 under this script's assumptions: valid OHLC data, a seed in that interval, and smoothing divisors of at least 1. Each update is a weighted average of values in the interval. J uses subtraction, so the same bound does not apply.
Flat-window check
If all nine bars have high = low = close = 100, the script assigns RSV = 50. Starting from K = D = 50 leaves K, D, and J at 50. Following other price history, the same flat input moves K and D toward 50; it does not immediately reset them.
KDJ Pine Script v6 with explicit boundary rules
Open Pine Editor in TradingView, create an indicator, paste the complete source, save it, and select Add to chart. The script plots K in orange, D in green, and J in fuchsia. RSV is available in the Data Window for numerical comparison.
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Pineify
//@version=6
indicator("KDJ - explicit seed and flat-range rule", overlay = false)
length = input.int(9, "RSV lookback", minval = 1)
kLength = input.int(3, "K smoothing divisor", minval = 1)
dLength = input.int(3, "D smoothing divisor", minval = 1)
highestPrice = ta.highest(high, length)
lowestPrice = ta.lowest(low, length)
priceRange = highestPrice - lowestPrice
ready = bar_index >= length - 1 and not na(close) and not na(priceRange)
// Wait for N loaded bars. A flat window uses a neutral RSV of 50.
float rsv = ready ? (priceRange > 0 ? 100 * (close - lowestPrice) / priceRange : 50.0) : na
// Seed the previous K and D at 50 before the first valid update.
var float k = 50.0
var float d = 50.0
if ready
k := (rsv + (kLength - 1) * k) / kLength
d := (k + (dLength - 1) * d) / dLength
kValue = ready ? k : na
dValue = ready ? d : na
jValue = ready ? 3 * k - 2 * d : na
plot(kValue, "K", color.orange, linewidth = 2)
plot(dValue, "D", color.green, linewidth = 2)
plot(jValue, "J", color.fuchsia)
plot(rsv, "RSV", display = display.data_window)
hline(80, "Upper reference", color.gray)
hline(20, "Lower reference", color.gray)
// Evaluate crosses every bar; expose only confirmed chart-bar events.
crossUp = ta.crossover(kValue, dValue)
crossDown = ta.crossunder(kValue, dValue)
confirmedUp = barstate.isconfirmed and crossUp
confirmedDown = barstate.isconfirmed and crossDown
alertcondition(confirmedUp, "K crosses above D at close", "KDJ: K crossed above D on a closed chart bar.")
alertcondition(confirmedDown, "K crosses below D at close", "KDJ: K crossed below D on a closed chart bar.")At length 9, the first eight loaded bars have no plotted KDJ values; the ninth receives the first recursive update. A length of 1 starts immediately, and a smoothing divisor of 1 passes the current input through. The 20 and 80 lines are visual references. They do not create orders.
Cross conditions require two valid plotted values, so the first output bar cannot produce a crossover alert. The code exposes confirmed crossover conditions for alerts; create an alert in TradingView and select the desired condition. It is an indicator, with no position sizing, exits, or performance report.
What each KDJ setting changes
| Input | Example default | Effect and constraint |
|---|---|---|
| RSV lookback | 9 bars | Defines the rolling high-low range. Minimum 1; outputs wait for this many loaded bars. |
| K smoothing divisor | 3 | New RSV weight is 1/divisor. A larger divisor retains more of the previous K. Minimum 1. |
| D smoothing divisor | 3 | New K weight is 1/divisor. A larger divisor retains more of the previous D. Minimum 1. |
The lengths count chart bars, not fixed calendar days. Changing from a daily chart to an hourly chart changes the sampled data even when the inputs remain 9, 3, 3. There is no demonstrated best timeframe or asset-specific setting in this example.
For a controlled comparison, change one input, preserve the seed and zero-range policy, and compare the resulting series on identical bars. A parameter that creates more crossovers has not established better trade outcomes.
KDJ vs the traditional Stochastic oscillator
The shared RSV calculation does not make the smoothed lines interchangeable. TradingView's Stochastic documentation uses an SMA for %K smoothing and another SMA for %D.
| Calculation | This KDJ example | TradingView Stochastic |
|---|---|---|
| Range input | Close within the rolling high-low range | Close within the rolling high-low range |
| K smoothing | Recursive, new input weight 1/kLength | SMA of the raw stochastic value |
| D smoothing | Recursive, new K weight 1/dLength | SMA of %K |
| Third line | J = 3K - 2D | No J line in the documented formula |
| Parity requirement | Match seed, warmup, flat rule, and lengths | Match SMA lengths and input series |
Appending a J calculation to Stochastic creates a third line, but its K and D still follow the Stochastic smoothing rules. If two KDJ scripts disagree, compare their first valid RSV, K, and D in order before changing trading thresholds.
Read the Stochastic Pine Script guideRead line values separately from trading decisions
| Observation | What it establishes | What remains unproven |
|---|---|---|
| K crosses above D | K moved from at or below D to above D | A profitable entry or a lasting price trend |
| J exceeds 100 | The positive K-D gap is large enough to push J above 100 | A reversal date or probability |
| K is above 80 | Smoothed RSV is near the upper end of its scale | That price must fall next |
| A live crossover disappears | The current bar changed before it closed | That a closed-bar alert fired |
The live bar's high, low, and close can change. Pine rolls ordinary persistent variables back to their committed state before each realtime recalculation, so this script recomputes the current update from the previous bar rather than repeatedly smoothing every tick. See the execution model.
The alert conditions require barstate.isconfirmed, which TradingView defines as true on historical bars and the closing realtime update. The plotted live values remain provisional. This example uses only the chart timeframe; bar-state confirmation has separate limitations inside request.security().
- Compare identical symbols, sessions, timeframes, and loaded history. A different starting point changes the recursive seed history.
- Keep J unclipped when auditing calculations. Clamping it to 0 or 100 hides the behavior the third line is meant to display.
- Do not call the recursive smoother
ta.sma. Matching a divisor of 3 alone does not match its weighting or seed. - Separate an indicator check from a strategy test. Any entry rule still needs an exit, size, execution timing, fees, and slippage assumptions.
Educational calculation example, not investment advice. No return, accuracy, or reversal probability is established by these values.
Turn a KDJ specification into a checkable script
Pineify's Pine Script AI Coding Agent can help write or revise the indicator from a plain-language specification. Give it the calculation contract, not only the name KDJ.
Create a Pine Script v6 KDJ indicator with 9-bar RSV and recursive K/D smoothing divisors of 3. Wait for the full lookback, seed previous K and D at 50, and use RSV 50 on a zero-range window. Do not clamp J. Plot RSV in the Data Window and require chart-bar close for crossover alerts.
- Open the Pine Script AI Coding Agent and supply the specification.
- Compare the generated recurrence and boundary choices with the formula.
- Validate the source and add it to a TradingView chart.
- Check individual RSV, K, D, and J values before interpreting signals.
- Define exits, size, costs, and execution timing separately if you request a strategy.
KDJ questions
Sources and calculation scope
Sources checked October 17, 2026. The seed, full-window warmup, zero-range policy, and hypothetical arithmetic fixtures are explicit choices of this example.
- Moomoo: KDJ calculation formula
RSV, the default recursive K/D equations, and J = 3K - 2D.
- TradingView: Stochastic (STOCH)
The built-in Stochastic uses simple moving averages for its smoothing.
- TradingView Pine Script: Bar states
barstate.isconfirmed is true on historical bars and the closing realtime update.
- TradingView Pine Script: Execution model
Open-bar calculations are recalculated from committed state through rollback.