Source code for backtrader.indicators.contrib.fisher_org_v1

#!/usr/bin/env python
"""Functional-test indicators migrated to contrib.

Generated from a single functional strategy module to preserve file-local
helper functions and constants without cross-test name collisions.
"""

import math

from .. import Indicator

__all__ = [
    "FisherOrgV1",
]


def _price(data, mode, ago=0):
    """Return a price value from OHLC data based on a mode selector.

    Parameters
    ----------
    data : DataFeed
        Data feed with open/high/low/close lines.
    mode : int
        Price mode: 2=open, 3=high, 4=low, 5=(h+l)/2, 6=(h+l+c)/3,
        7=(2c+h+l)/4, 8=(o+c)/2, 9=(o+h+l+c)/4; default returns close.
    ago : int
        Bar offset.

    Returns
    -------
    float
        Selected price value.
    """
    o = float(data.open[-ago])
    h = float(data.high[-ago])
    low_price = float(data.low[-ago])
    c = float(data.close[-ago])
    if mode == 2:
        return o
    if mode == 3:
        return h
    if mode == 4:
        return low_price
    if mode == 5:
        return (h + low_price) / 2.0
    if mode == 6:
        return (h + low_price + c) / 3.0
    if mode == 7:
        return (2.0 * c + h + low_price) / 4.0
    if mode == 8:
        return (o + c) / 2.0
    if mode == 9:
        return (o + h + low_price + c) / 4.0
    return c


[docs] class FisherOrgV1(Indicator): """Fisher Transform indicator producing a Gaussian-like signal line and its lagged trigger. Lines ----- fisher : float Current smoothed Fisher Transform value. trigger : float Previous bar's fisher value, used for crossover detection. """ lines = ("fisher", "trigger") params = ( ("length", 7), ("ipc", 1), ) def __init__(self): """Initialise indicator state and smoothing recursive value.""" self.addminperiod(int(self.p.length) + 2) self._value1 = 0.0
[docs] def next(self): """Compute Fisher Transform value and set fisher/trigger lines.""" length = int(self.p.length) highs = [float(self.data.high[-i]) for i in range(length)] lows = [float(self.data.low[-i]) for i in range(length)] smax = max(highs) smin = min(lows) if smax == smin: smax += 1e-12 price = _price(self.data, int(self.p.ipc), 0) wpr = (price - smin) / (smax - smin) value0 = (wpr - 0.5) + 0.67 * self._value1 value0 = min(max(value0, -0.999), 0.999) fisher_prev = float(self.lines.fisher[-1]) if len(self) > 1 else 0.0 if not math.isfinite(fisher_prev): fisher_prev = 0.0 res2 = (1.0 + value0) / (1.0 - value0) if res2 < 1e-7: res2 = 1.0 fisher = 0.5 * math.log(res2) + 0.5 * fisher_prev self.lines.fisher[0] = fisher self.lines.trigger[0] = fisher_prev self._value1 = value0