Fisher Transform on TradingView: formula and limits
The Fisher Transform indicator maps a price's position within a recent range through a nonlinear logarithmic function. Recursive smoothing produces the Fisher line; its previous value supplies the trigger in this implementation. The transform amplifies changes near its input limits, which makes clipping and initialization part of the calculation, not optional details.
This page covers the TradingView oscillator associated with John F. Ehlers. It does not cover statistical tests of correlations using Fisher z. A large reading is neither a normal-distribution probability nor a promise that price will reverse.
The calculation has two recursive states
Use the highest and lowest hl2 values over the lookback, including the current bar. These are extrema of the midpoint source, not the highest candle high and lowest candle low. For a nonzero range, the position q lies between 0 and 1.
p = (high + low) / 2; H = highest(p, N); L = lowest(p, N)
q = (p - L) / (H - L); r = 2q - 1
u[t] = 0.33 × r[t] + 0.67 × x[t-1]
x[t] = clip(u[t])
F[t] = 0.5 × ln((1 + x[t]) / (1 - x[t])) + 0.5 × F[t-1]
Trigger[t] = F[t-1]
The code writes the first recursion as 0.66 × (q - 0.5), which equals 0.33 × r. It smooths the normalized input before the logarithm, then adds half of the previous Fisher output afterward. The trigger is a one-bar delay, not an independently calculated moving average.
Clipping changes the output
The retained Pineify rule maps u > 0.99 to 0.999 and u < -0.99 to -0.999. Values exactly at ±0.99 remain unchanged. This threshold rule has a small discontinuity; it is different from simply bounding u with min/max at ±0.999. Both keep the logarithm away from ±1, where it is undefined or divergent.
For the logarithmic term alone, x = 0.9 gives 1.4722; x = 0.99 gives 2.6467; x = 0.999 gives 3.8002. Its slope is 1 / (1 - x²), so small input changes have a much larger effect near either tail. Recursive memory also matters: with x held at 0.999, F approaches 7.6004. The theoretical transform is unbounded as x approaches 1, but this clipped implementation has a finite limit from a zero seed.
A four-bar calculation you can reproduce
Hypothetical arithmetic example: assume a complete lookback with H = 110 and L = 90 on each row, and zero prior x and F before row A. The extrema must remain in the window. Values below are rounded only for display; each recursion uses the unrounded prior value. This is not a market backtest.
| Bar / p | q | x after clipping | Fisher F | Trigger |
|---|---|---|---|---|
| A / 105 | 0.7500 | 0.165000 | 0.166522 | 0.000000 seed |
| B / 110 | 1.0000 | 0.440550 | 0.556174 | 0.166522 |
| C / 110 | 1.0000 | 0.625169 | 1.011532 | 0.556174 |
| D / 100 | 0.5000 | 0.418863 | 0.952078 | 1.011532 |
Row A: 0.66 × (0.75 - 0.5) = 0.165, then 0.5 × ln(1.165 / 0.835) = 0.166522. At row D, price is at the range midpoint but Fisher is still positive because both states retain earlier values. Fisher falls below its trigger on D; a zero-line crossing has not happened.
If H = L, q cannot be calculated by division. The example code assigns q = 0.5. Then x decays through 0.67 × x[t-1], and Fisher decays through both its transform input and its previous output. A flat range does not force an immediate zero after a nonzero history.
Pine Script with explicit warm-up and flat-range handling
Paste this complete indicator into TradingView's Pine Editor and add it to a chart. It retains the original page's Fisher and trigger plots and reference levels, adds an adjustable length, and exposes the normalized state and source range in the Data Window.
// 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("Fisher Transform: explicit boundaries", overlay = false)
length = input.int(9, "Length", minval = 2)
source = hl2
highest = ta.highest(source, length)
lowest = ta.lowest(source, length)
priceRange = highest - lowest
ready = bar_index >= length - 1
// Preserve the threshold clipping rule used by Pineify's Fisher helper.
clip(value) => value > 0.99 ? 0.999 : value < -0.99 ? -0.999 : value
// A flat range contributes a neutral input; prior state decays gradually.
position = priceRange > 0 ? (source - lowest) / priceRange : 0.5
float value = 0.0
value := ready ? clip(0.66 * (position - 0.5) + 0.67 * nz(value[1])) : 0.0
float fisher = 0.0
fisher := ready ? 0.5 * math.log((1 + value) / (1 - value)) + 0.5 * nz(fisher[1]) : 0.0
trigger = fisher[1]
plot(ready ? fisher : na, "Fisher", color = color.blue, linewidth = 2)
plot(ready and ready[1] ? trigger : na, "Trigger", color = color.orange)
plot(ready ? value : na, "Normalized state", display = display.data_window)
plot(ready ? priceRange : na, "Source range", display = display.data_window)
hline(1.5, "Upper reference", color = color.gray)
hline(0.75, "Upper inner reference", color = color.gray)
hline(0, "Zero", color = color.gray)
hline(-0.75, "Lower inner reference", color = color.gray)
hline(-1.5, "Lower reference", color = color.gray)
bgcolor(not ready ? color.new(color.gray, 90) : na, title = "Warm-up")
crossUp = ta.crossover(fisher, trigger)
crossDown = ta.crossunder(fisher, trigger)
validSignal = ready and ready[1] and ready[2] and priceRange > 0 and barstate.isconfirmed
confirmedUp = validSignal and crossUp
confirmedDown = validSignal and crossDown
plotshape(confirmedUp, "Confirmed up cross", shape.triangleup, location.bottom, color.blue, size = size.tiny)
plotshape(confirmedDown, "Confirmed down cross", shape.triangledown, location.top, color.orange, size = size.tiny)
alertcondition(confirmedUp, "Fisher above trigger", "Fisher crossed above its prior-bar trigger on a closed bar.")
alertcondition(confirmedDown, "Fisher below trigger", "Fisher crossed below its prior-bar trigger on a closed bar.")| Output | Meaning | Boundary |
|---|---|---|
| Blue Fisher | The recursive transformed state F | Changes while a bar is open |
| Orange trigger | F from the previous bar | Hidden until a previous complete-window Fisher exists |
| Gray warm-up background | Fewer than N available chart bars | State stays at zero and plots are hidden |
| Triangle / alert | A confirmed Fisher-trigger crossover | Requires three complete-window Fisher observations and a nonzero current range |
| ±0.75 and ±1.5 lines | Reference levels retained from the original example | Not statistical confidence levels or universal entry thresholds |
This is a teaching variant of the original helper. It starts the recursion at the first complete lookback, substitutes neutral input for a flat range, and filters alert timing. Those choices can produce different early or flat-range values from TradingView's built-in indicator or another Fisher script. Syntax validation does not establish numerical parity.
Settings and a bar-by-bar comparison procedure
| Setting | Example | What to check |
|---|---|---|
| Length | 9; minimum 2 | The window counts bars, not calendar days. Changing it can change both extrema. |
| Source | hl2, fixed in the code | A close-based implementation can produce different normalized values. |
| Input memory | 0.33 new r + 0.67 prior x | Keep coefficients fixed when comparing only length. |
| Output memory | 0.5 prior F | Do not replace this recurrence with an EMA without treating it as a different indicator. |
| Initialization | Zero state until a full window exists | The first displayed value still contains seed effects; one full lookback does not mean full convergence. |
- Match symbol, session, chart type, timeframe, length, and source before comparing two scripts.
- Inspect H, L, q, x, F, and trigger on the same closed bars. Check a rising sequence, a falling sequence, a flat range, and a bar where an old extreme leaves the lookback.
- Load more earlier history and compare the same later bars. Recursive state can depend on where calculation begins; document the remaining difference instead of assuming N bars eliminates it.
- Check ±0.99 boundary inputs separately. A continuous clamp and the retained threshold rule do not have identical output.
- Only after values agree, compare alerts and any strategy rules. Record confirmation timing, exits, position sizing, fees, and slippage.
Reading signals without treating them as forecasts
| Observation | What it establishes | What it does not establish |
|---|---|---|
| Fisher crosses above trigger | F changed from not rising to rising relative to its prior value | That price has bottomed or a long trade will profit |
| Fisher crosses below trigger | F changed from not falling to falling relative to its prior value | That price has topped or a short trade will profit |
| Fisher crosses zero | The recursively transformed state changed sign | That the current source just crossed its range midpoint |
| Large absolute Fisher | A large transformed state relative to zero | A standard deviation, tail probability, or immediate reversal |
| Price and Fisher diverge | The selected price and oscillator swings differ | An entry time without a defined swing-confirmation rule |
A narrow price range can still produce normalized values near ±1. The denominator rescales the movement, so a small percentage price move does not imply a small Fisher reading. New rolling extrema and expiring old extrema can also change the reading without a proportionate price move.
Fisher, RSI, and Stochastic answer different calculations. This Fisher implementation uses the midpoint source's rolling range and a logarithm. RSI measures smoothed gains relative to losses; Stochastic places a source within a high-low window. Shared oscillator terminology does not establish equal levels, equal signal timing, or a performance ranking.
Repeated crossovers can occur in sideways data; a strong trend can keep Fisher on one side of zero. Adding a trend filter creates a new rule to test. Educational indicator calculations are not investment advice or evidence of trading returns.
Ask Pineify for a specification you can check
The Pine Script AI Coding Agent accepts a description of your indicator requirements. Include the calculation and boundary rules so the resulting script can be compared bar by bar before you add trading orders.
Create a Pine Script v6 Fisher indicator using hl2, length 9, the 0.66/0.67 input recurrence, clipping above 0.99 to 0.999 and below -0.99 to -0.999, and 0.5 prior Fisher memory. Seed both states at zero after a full lookback; use midpoint input when the range is zero. Plot Fisher and its prior-bar trigger. Mark crossovers only on confirmed bars after enough valid history, and suppress flat-range alerts. Expose intermediate values for comparison. Keep order entries and exits separate.
Review the generated source, run syntax validation, then compare its values with the four-bar example and the chart Data Window. If you later request a strategy, define exits and costs explicitly; an indicator crossover alone is not a complete trading system.
Customize Fisher with PineifyFisher Transform FAQ
Sources and related indicators
Sources checked October 17, 2026. The coefficient and clipping examples describe the code on this page; the calculation table is a stated hypothetical example.
True Strength Index
Compare with double-smoothed price-change momentum.
Awesome Oscillator
Compare with a difference between midpoint-price averages.
Relative Strength Index
Review a gain/loss oscillator and its separate settings.
Schaff Trend Cycle
Review a different oscillator calculation.