דלגו לתוכן
פתיחת RaamTrade

FisherTransform — Fisher Transform

תוכן זה אינו זמין עדיין בשפה שלך.

Ehlers’ transform of the normalised range position: prices are not Gaussian, but pushing the range position through the Fisher function makes the extremes behave more like a normal distribution’s tails. The effect is that turning points read as sharp spikes rather than gentle rolls.

Signature

FisherTransform(period, on=<source>)
Argument Type Required Notes
period int Param or literal Yes Range lookback. Default 9, bounds 2–400.
on= symbol No Source symbol. Defaults to the trade symbol. Reads high and low.

Formula

$$ x_t = 0.66\left(2\frac{\text{mid} - LL}{HH - LL} - 1\right) + 0.67,x_{t-1}, \quad \text{Fisher}t = 0.5\ln\frac{1+x_t}{1-x_t} + 0.5,\text{Fisher}{t-1} $$

x is clamped to ±0.999 — the log diverges at ±1, and without the clamp a bar closing exactly at the range extreme would blow the indicator up.

Warmup

period bars; the recursion converges within a few more.

Reading the value

ft.fisher
ft.trigger
ft.fisher > ft.trigger

trigger is the previous bar’s fisher value — the standard crossover partner. Multi-value: cannot be chained onto or crossed.

Example

class FisherTurn(Strategy):
"""Enter on a Fisher turn out of an extreme."""
trade = Symbol()
sizing = FixedCapital()
def execute(self):
ft = FisherTransform(9, on=self.trade)
if ft.fisher > ft.trigger and ft.fisher < -1 and self.trade.is_flat:
self.buy(self.trade, reason=f"Fisher turning at {ft.fisher:.2f}")
if ft.fisher < ft.trigger and ft.fisher > 1 and self.trade.is_long:
self.sell(self.trade, self.trade.position, reason="Fisher rolling over")

Notes

  • John Ehlers, Cybernetic Analysis for Stocks and Futures (2004).
  • Proven by an assert-via-trade run on SPY daily (stayed inside a sane bound on every bar, including at range extremes where the clamp is what prevents a divergence; zero alarm trades).
  • Unbounded in principle: the value grows without limit if price pins to one end of its range.