Skip to content

Commit

Permalink
[GH-158] Gaussian Fisher Transform Price Reversals (#159)
Browse files Browse the repository at this point in the history
The Gaussian Fisher Transform Price Reversals indicator, dubbed
FTR for short, is a stat based price reversal detection indicator
inspired by and based on the work of the electrical engineer
now private trader John F. Ehlers.

https://www.tradingview.com/script/ajZT2tZo-Gaussian-Fisher-Transform-Price-Reversals-FTR/

Implementation reference:

https://github.com/twopirllc/pandas-ta/blob/084dbe1c4b76082f383fa3029270ea9ac35e4dc7/pandas_ta/momentum/fisher.py#L9

Formular:
* Fisher Transform = 0.5 * ln((1 + X) / (1 - X))
* X is a series whose values are between -1 to 1

Examples:
* `df['ftr']` returns the FTR with window 9
* `df['ftr_20']` returns the FTR with window 20
  • Loading branch information
jealous committed Jun 24, 2023
1 parent 7a76c01 commit abe781d
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 6 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Supported statistics/indicators are:
* CTI: Correlation Trend Indicator
* LRMA: Linear Regression Moving Average
* ERI: Elder-Ray Index
* FTR: the Gaussian Fisher Transform Price Reversals indicator

## Installation

Expand Down Expand Up @@ -901,6 +902,27 @@ Examples:
* `df['cti']` returns the CTI of close price with window 12
* `df['high_5_cti']` returns the CTI of high price with window 5

#### [the Gaussian Fisher Transform Price Reversals indicator](https://www.tradingview.com/script/ajZT2tZo-Gaussian-Fisher-Transform-Price-Reversals-FTR/)

The Gaussian Fisher Transform Price Reversals indicator, dubbed
FTR for short, is a stat based price reversal detection indicator
inspired by and based on the work of the electrical engineer
now private trader John F. Ehlers.

https://www.tradingview.com/script/ajZT2tZo-Gaussian-Fisher-Transform-Price-Reversals-FTR/

Implementation reference:

https://github.com/twopirllc/pandas-ta/blob/084dbe1c4b76082f383fa3029270ea9ac35e4dc7/pandas_ta/momentum/fisher.py#L9

Formular:
* Fisher Transform = 0.5 * ln((1 + X) / (1 - X))
* X is a series whose values are between -1 to 1

Examples:
* `df['ftr']` returns the FTR with window 9
* `df['ftr_20']` returns the FTR with window 20

## Issues

We use [Github Issues](https://github.com/jealous/stockstats/issues) to track
Expand Down
71 changes: 65 additions & 6 deletions stockstats.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class StockStatsError(Exception):
'eribear': 13,
'eribull': 13,
'ichimoku': (9, 26, 52),
'ftr': 9,
'kama': (10, 5, 34), # window, fast, slow
'kdjd': 9,
'kdjj': 9,
Expand Down Expand Up @@ -193,7 +194,7 @@ def column(self):
def name(self):
if self._windows is None and self._column is None:
return self._name
if self.column is None:
if self._column is None:
return f'{self._name}_{self._windows}'
return f'{self.column}_{self.windows}_{self._name}'

Expand Down Expand Up @@ -240,6 +241,22 @@ class StockDataFrame(pd.DataFrame):

# End of options

@property
def high(self) -> pd.Series:
return self['high']

@property
def low(self) -> pd.Series:
return self['low']

@property
def close(self) -> pd.Series:
return self['close']

@property
def open(self) -> pd.Series:
return self['open']

def _get_change(self, meta: _Meta):
""" Get the percentage change column
Expand Down Expand Up @@ -299,17 +316,17 @@ def _process_shifts_segment(shift_segment):
shifts = [int(shift_segment)]
return shifts

@staticmethod
def set_nan(pd_obj, shift):
@classmethod
def set_nan(cls, pd_obj, shift):
try:
iter(shift)
max_shift = max(shift)
min_shift = min(shift)
StockDataFrame._set_nan_of_single_shift(pd_obj, max_shift)
StockDataFrame._set_nan_of_single_shift(pd_obj, min_shift)
cls._set_nan_of_single_shift(pd_obj, max_shift)
cls._set_nan_of_single_shift(pd_obj, min_shift)
except TypeError:
# shift is not iterable
StockDataFrame._set_nan_of_single_shift(pd_obj, shift)
cls._set_nan_of_single_shift(pd_obj, shift)

@staticmethod
def _set_nan_of_single_shift(pd_obj, shift):
Expand Down Expand Up @@ -1412,6 +1429,47 @@ def _get_kama(self, meta: _Meta):
last_kama = cur
self[meta.name] = kama

def _ftr(self, window: int):
mp = (self.high + self.low) * 0.5
highest = mp.rolling(window).max()
lowest = mp.rolling(window).min()
width = highest - lowest
width[width < 0.001] = 0.001
position = list(((mp - lowest) / width) - 0.5)

v = 0
size = self.high.size
result = np.zeros(size)
for i in range(window, size):
v = 0.66 * position[i] + 0.67 * v
if v < -0.99:
v = -0.999
if v > 0.99:
v = 0.999
r = 0.5 * (np.log((1 + v) / (1 - v)) + result[i - 1])
result[i] = r
return pd.Series(result, index=self.index)

def _get_ftr(self, meta: _Meta):
""" the Gaussian Fisher Transform Price Reversals indicator
The Gaussian Fisher Transform Price Reversals indicator, dubbed
FTR for short, is a stat based price reversal detection indicator
inspired by and based on the work of the electrical engineer
now private trader John F. Ehlers.
https://www.tradingview.com/script/ajZT2tZo-Gaussian-Fisher-Transform-Price-Reversals-FTR/
Implementation reference:
https://github.com/twopirllc/pandas-ta/blob/084dbe1c4b76082f383fa3029270ea9ac35e4dc7/pandas_ta/momentum/fisher.py#L9
Formular:
* Fisher Transform = 0.5 * ln((1 + X) / (1 - X))
* X is a series whose values are between -1 to 1
"""
self[meta.name] = self._ftr(meta.int)

@staticmethod
def parse_column_name(name):
m = re.match(r'(.*)_([\d\-+~,.]+)_(\w+)', name)
Expand Down Expand Up @@ -1554,6 +1612,7 @@ def handler(self):
('cti',): self._get_cti,
('ker',): self._get_ker,
('eribull', 'eribear'): self._get_eri,
('ftr',): self._get_ftr,
}

def __init_not_exist_column(self, key):
Expand Down
11 changes: 11 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,17 @@ def test_cti(self):
assert_that(cti[20110131], near_to(-0.043))
assert_that(cti[20110215], near_to(0.5006))

def test_ftr(self):
stock = self.get_stock_90days()
f = stock['ftr']
f9 = stock['ftr_9']
assert_that(f[20110114], equal_to(0))
assert_that(f[20110128], near_to(-1.135))
assert_that(f9[20110128], equal_to(f[20110128]))

f = stock['ftr_15']
assert_that(f[20110128], near_to(-1.005))

def test_change_group_window_defaults(self):
stock = self.get_stock_90days()
macd = stock['macd']
Expand Down

0 comments on commit abe781d

Please sign in to comment.