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.fisherft.triggerft.fisher > ft.triggertrigger 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.