TradingView indicators

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.

Source: hl2Example length: 9 barsTrigger: prior FisherPine Script v6

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 / pqx after clippingFisher FTrigger
A / 1050.75000.1650000.1665220.000000 seed
B / 1101.00000.4405500.5561740.166522
C / 1101.00000.6251691.0115320.556174
D / 1000.50000.4188630.9520781.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.

Pine Script v6Copy code
// 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.")
OutputMeaningBoundary
Blue FisherThe recursive transformed state FChanges while a bar is open
Orange triggerF from the previous barHidden until a previous complete-window Fisher exists
Gray warm-up backgroundFewer than N available chart barsState stays at zero and plots are hidden
Triangle / alertA confirmed Fisher-trigger crossoverRequires three complete-window Fisher observations and a nonzero current range
±0.75 and ±1.5 linesReference levels retained from the original exampleNot 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

SettingExampleWhat to check
Length9; minimum 2The window counts bars, not calendar days. Changing it can change both extrema.
Sourcehl2, fixed in the codeA close-based implementation can produce different normalized values.
Input memory0.33 new r + 0.67 prior xKeep coefficients fixed when comparing only length.
Output memory0.5 prior FDo not replace this recurrence with an EMA without treating it as a different indicator.
InitializationZero state until a full window existsThe first displayed value still contains seed effects; one full lookback does not mean full convergence.
  1. Match symbol, session, chart type, timeframe, length, and source before comparing two scripts.
  2. 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.
  3. 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.
  4. Check ±0.99 boundary inputs separately. A continuous clamp and the retained threshold rule do not have identical output.
  5. 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

ObservationWhat it establishesWhat it does not establish
Fisher crosses above triggerF changed from not rising to rising relative to its prior valueThat price has bottomed or a long trade will profit
Fisher crosses below triggerF changed from not falling to falling relative to its prior valueThat price has topped or a short trade will profit
Fisher crosses zeroThe recursively transformed state changed signThat the current source just crossed its range midpoint
Large absolute FisherA large transformed state relative to zeroA standard deviation, tail probability, or immediate reversal
Price and Fisher divergeThe selected price and oscillator swings differAn 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 Pineify

Fisher Transform FAQ

It is an oscillator associated with John F. Ehlers that applies a logarithmic transform to a normalized price state. This implementation uses hl2 within its rolling range, recursive smoothing, and a prior-bar trigger. It describes transformed price position; it does not calculate a probability of reversal.

The logarithmic function has the same form, but the input and interpretation differ. This trading indicator transforms a smoothed price-range position. It is not a correlation significance test, and its readings are not p-values, confidence intervals, or standard-normal z-scores.

The nonlinear function alone does not guarantee a normal output distribution. The result depends on the input distribution, rolling normalization, clipping, and recursive terms. For example, a constant price series under this page's flat-range rule stays at zero after a zero seed, rather than becoming a normal distribution.

There is no universal best length or threshold. The example starts at 9 bars and fixes the source at hl2. Compare lengths on the same symbol, timeframe, history, and signal definition. A change in length changes the rolling extrema, so its effect on crossings depends on the price sequence.

The trigger is the previous Fisher value. Fisher above trigger means the oscillator rose from the previous bar; below means it fell. A crossover changes that relationship. It can occur while Fisher remains above or below zero, and does not prove that the price trend reversed.

The normalization denominator is zero. This example substitutes a midpoint position of 0.5, so the new directional contribution is zero while prior state decays. It suppresses cross alerts on flat-range bars. A script that resets, holds its last value, or outputs na instead can produce different results.

Compare the source, rolling high and low, coefficients, clipping rule, initial state, available history, and zero-range behavior. This example intentionally starts its recursive state only after a full lookback and adds a flat-range guard. It is a documented teaching variant, not a claim of exact built-in parity.

The calculation accepts any chart with suitable high and low data. A 9-bar window measures different elapsed time on a 5-minute, daily, or weekly chart. There is no supported universal accuracy claim for any asset or timeframe. Check the values first; test trading rules separately with costs and untouched data.

The plotted Fisher value can change while the current bar is open because high and low can change. The example requires barstate.isconfirmed for crossover markers and alerts. That confines these signals to closed bars; it does not prevent differences caused by revised source data, changed settings, or a different history start.