Fisher Transform
I. What is the Fisher Transform Indicator
The Fisher Transform is a mathematical transformation indicator that converts price distributions into an approximately normal (Gaussian) distribution. Through a nonlinear transformation, it maps prices into an unbounded space, greatly stretching the distribution tails and producing sharper turning signals. This makes price reversal points much easier to identify.
Historical Background
The Fisher Transform was invented by John Ehlers and published in his 2002 book Cybernetic Analysis for Stocks and Futures as well as in Technical Analysis of Stocks and Commodities magazine. Ehlers is a pioneer in applying signal processing techniques to financial analysis, bringing Digital Signal Processing (DSP) concepts into technical analysis. The indicator is named after statistician Ronald Fisher, who developed the Fisher z-transformation.
Indicator Classification
- Type: Oscillator, displayed in a separate panel
- Category: Momentum / Statistical Transformation
- Default Parameter: Period
- Value Range: Unbounded, but typically stays between -3 and +3
Traditional oscillators (such as RSI, Stochastic) have skewed distributions between 0–100 — values cluster in the middle region most of the time, with extreme values appearing less frequently. Fisher Transform stretches this skewed distribution into a normal distribution, making extreme values easier to identify and turning points sharper.
II. Mathematical Principles and Calculation
Core Formula
Step 1: Normalize price to [-1, 1] range
Where:
- = current closing price (or median price )
- = lowest low over the past periods
- = highest high over the past periods
Step 2: Clamp and smooth
Clamp to to prevent logarithm overflow, and apply EMA smoothing:
Take the clamped value:
Step 3: Apply Fisher Transform
This formula is the inverse hyperbolic tangent function: .
Step 4: Signal line
The previous period’s Fisher value serves as the signal line.
Mathematical Meaning
The Fisher Transform has these properties:
- When is near 0: (linear)
- When approaches ±1: (extreme amplification)
This means when price is in the middle of the range, the Fisher value changes gradually; but when price approaches extreme positions, the Fisher value changes dramatically, producing sharp peaks and troughs.
Step-by-Step Calculation Logic
- Find range extremes: Highest high and lowest low over the past periods
- Normalize: Map price to [-1, 1]
- Smooth: Use simple recursive smoothing ()
- Clamp: Restrict values to ±0.999
- Fisher Transform: Apply
- Signal line: 1-period lag of Fisher
Ehlers uses smoothing (equivalent to ), which is simpler than standard EMA. Some implementations skip this step and apply the Fisher Transform directly to the normalized value.
III. Python Implementation
import numpy as np
import pandas as pd
def fisher_transform(high: pd.Series,
low: pd.Series,
period: int = 9) -> pd.DataFrame:
"""
Calculate Fisher Transform
Parameters
----------
high : pd.Series
High price series
low : pd.Series
Low price series
period : int
Lookback period, default 9
Returns
-------
pd.DataFrame
DataFrame with Fisher and Signal columns
"""
# Use median price
median_price = (high + low) / 2
highest = high.rolling(window=period, min_periods=period).max()
lowest = low.rolling(window=period, min_periods=period).min()
# Normalize to [-1, 1]
raw = 2 * (median_price - lowest) / (highest - lowest) - 1
# Smooth and clamp
value = pd.Series(np.nan, index=high.index)
fisher = pd.Series(np.nan, index=high.index)
first_valid = raw.first_valid_index()
if first_valid is None:
return pd.DataFrame({"Fisher": fisher, "Signal": fisher}, index=high.index)
start_idx = high.index.get_loc(first_valid)
value.iloc[start_idx] = 0.0
fisher.iloc[start_idx] = 0.0
for i in range(start_idx, len(high)):
if np.isnan(raw.iloc[i]):
continue
# Smooth
prev_val = value.iloc[i - 1] if i > start_idx and not np.isnan(value.iloc[i - 1]) else 0.0
v = 0.5 * raw.iloc[i] + 0.5 * prev_val
# Clamp
v = max(min(v, 0.999), -0.999)
value.iloc[i] = v
# Fisher Transform
fisher.iloc[i] = 0.5 * np.log((1 + v) / (1 - v))
signal = fisher.shift(1)
return pd.DataFrame({
"Fisher": fisher,
"Signal": signal
}, index=high.index)
# ========== Usage Example ==========
if __name__ == "__main__":
np.random.seed(42)
dates = pd.date_range("2024-01-01", periods=120, freq="D")
base = 100 + np.cumsum(np.random.randn(120) * 1.0)
df = pd.DataFrame({
"date": dates,
"open": base + np.random.randn(120) * 0.3,
"high": base + np.abs(np.random.randn(120) * 0.8),
"low": base - np.abs(np.random.randn(120) * 0.8),
"close": base,
"volume": np.random.randint(1000, 10000, size=120),
})
df.set_index("date", inplace=True)
# Calculate Fisher Transform
ft = fisher_transform(df["high"], df["low"], period=9)
df = pd.concat([df, ft], axis=1)
print("=== Fisher Transform Results (Last 15 Rows) ===")
print(df[["close", "Fisher", "Signal"]].tail(15).to_string())
# Crossover signals
df["buy"] = (df["Fisher"] > df["Signal"]) & (df["Fisher"].shift(1) <= df["Signal"].shift(1))
df["sell"] = (df["Fisher"] < df["Signal"]) & (df["Fisher"].shift(1) >= df["Signal"].shift(1))
print("\n=== Buy Signals (Fisher Crosses Above Signal) ===")
print(df[df["buy"]][["close", "Fisher", "Signal"]].head(10).to_string())
print("\n=== Sell Signals (Fisher Crosses Below Signal) ===")
print(df[df["sell"]][["close", "Fisher", "Signal"]].head(10).to_string())
IV. Problems the Indicator Solves
1. Producing Sharp Turning Signals
Fisher Transform’s greatest advantage is making price reversal points very sharp and obvious. Traditional oscillators show relatively smooth changes in extreme areas, while Fisher Transform produces very sharp peaks and troughs that are easy to identify.
2. Crossover Signals
| Signal | Condition | Meaning |
|---|---|---|
| Buy | Fisher crosses above Signal | Momentum shifts from bearish to bullish |
| Sell | Fisher crosses below Signal | Momentum shifts from bullish to bearish |
3. Extreme Value Reversals
- Fisher > 1.5 ~ 2.0 -> Overbought zone; watch for Signal cross-under as sell signal
- Fisher < -1.5 ~ -2.0 -> Oversold zone; watch for Signal cross-over as buy signal
4. Approximately Normal Distribution
Since Fisher-transformed values are approximately normally distributed, standard deviation (e.g., ±2σ) can be used to define statistically-based overbought/oversold thresholds — more theoretically grounded than RSI’s empirical thresholds.
Fisher Transform signals are very sensitive with high crossover frequency. In ranging markets, it may produce many short-term signals, requiring trend filtering to control the number of trades.
V. Advantages, Disadvantages, and Use Cases
Advantages
| Advantage | Description |
|---|---|
| Sharp signals | Reversal points are very clear and hard to miss |
| Statistical foundation | Based on probability distribution transformation with mathematical theory support |
| Unbounded distribution | Not limited to a fixed range; extreme values are fully expressed |
| Fast response | Responds quickly to price changes |
Disadvantages
| Disadvantage | Description |
|---|---|
| Noise sensitive | High sensitivity brings more false signals |
| Few parameters | Only one period parameter, limited tuning space |
| Abstract concept | Fisher Transform’s mathematical meaning is less intuitive than RSI or MACD |
| Tail risk | Extreme values can be very large, affecting chart readability |
Use Cases
- Short-term trading: Fisher’s sensitivity is well-suited for short-term and intraday trading
- Reversal trading: Capturing reversals within well-defined support/resistance ranges
- Pairing with other Ehlers indicators: Such as MAMA, Instantaneous Trendline, etc.
Comparison with Related Indicators
| Comparison | Fisher | RSI | Stochastic |
|---|---|---|---|
| Distribution | Approx. normal | Skewed | Skewed |
| Value range | Unbounded | 0–100 | 0–100 |
| Signal sharpness | Very high | Medium | Medium |
| Response speed | Fast | Medium | Fast |
| False signal rate | High | Medium | High |
- Focus on large crossovers: When the difference between Fisher and Signal is significant (e.g., > 0.5), the crossover signal is more reliable than a tiny crossover.
- Combine with trend filtering: Use EMA(50) or ADX to filter trend direction and reduce counter-trend false signals.
- Watch peak levels: Record historical Fisher peak levels; when the current peak fails to exceed the previous high, it may indicate weakening momentum.
- Moderate smoothing: If signals are too frequent, increase the period parameter (e.g., to 13 or 21).