mirror of
https://github.com/wassname/pandas-ta.git
synced 2026-08-02 12:50:22 +08:00
Update ebsw.py
Updated EBSW indicator as suggested by Squigglez2. The initial version can still be used via boolean argument.
This commit is contained in:
+83
-39
@@ -5,58 +5,94 @@ from numpy import nan as npNaN
|
||||
from numpy import pi as npPi
|
||||
from numpy import sin as npSin
|
||||
from numpy import sqrt as npSqrt
|
||||
from numpy import zeros as npZeros
|
||||
from numpy import roll as npRoll
|
||||
from numpy import mean as npMean
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def ebsw(close, length=None, bars=None, offset=None, **kwargs):
|
||||
def ebsw(close, length=None, bars=None, offset=None, initial_version=False, **kwargs):
|
||||
"""Indicator: Even Better SineWave (EBSW)"""
|
||||
# Validate arguments
|
||||
length = int(length) if length and length > 38 else 40
|
||||
length = int(length) if length and length > 10 else 40
|
||||
bars = int(bars) if bars and bars > 0 else 10
|
||||
close = verify_series(close, length)
|
||||
initial_version = bool(initial_version) # allow initial version to be used (more responsive/caution!)
|
||||
offset = get_offset(offset)
|
||||
|
||||
if close is None: return
|
||||
|
||||
# variables
|
||||
alpha1 = HP = 0 # alpha and HighPass
|
||||
a1 = b1 = c1 = c2 = c3 = 0
|
||||
Filt = Pwr = Wave = 0
|
||||
if initial_version: # not the default version that is active
|
||||
# variables
|
||||
alpha1 = HP = 0 # alpha and HighPass
|
||||
a1 = b1 = c1 = c2 = c3 = 0
|
||||
Filt = Pwr = Wave = 0
|
||||
|
||||
lastClose = lastHP = 0
|
||||
FilterHist = [0, 0] # Filter history
|
||||
lastClose = lastHP = 0
|
||||
FilterHist = [0, 0] # Filter history
|
||||
|
||||
# Calculate Result
|
||||
m = close.size
|
||||
result = [npNaN for _ in range(0, length - 1)] + [0]
|
||||
for i in range(length, m):
|
||||
# HighPass filter cyclic components whose periods are shorter than Duration input
|
||||
alpha1 = (1 - npSin(360 / length)) / npCos(360 / length)
|
||||
HP = 0.5 * (1 + alpha1) * (close[i] - lastClose) + alpha1 * lastHP
|
||||
# Calculate Result
|
||||
m = close.size
|
||||
result = [npNaN for _ in range(0, length - 1)] + [0]
|
||||
for i in range(length, m):
|
||||
# HighPass filter cyclic components whose periods are shorter than Duration input
|
||||
alpha1 = (1 - npSin(360 / length)) / npCos(360 / length)
|
||||
HP = 0.5 * (1 + alpha1) * (close[i] - lastClose) + alpha1 * lastHP
|
||||
|
||||
# Smooth with a Super Smoother Filter from equation 3-3
|
||||
a1 = npExp(-npSqrt(2) * npPi / bars)
|
||||
b1 = 2 * a1 * npCos(npSqrt(2) * 180 / bars)
|
||||
c2 = b1
|
||||
c3 = -1 * a1 * a1
|
||||
# Smooth with a Super Smoother Filter from equation 3-3
|
||||
a1 = npExp(-npSqrt(2) * npPi / bars)
|
||||
b1 = 2 * a1 * npCos(npSqrt(2) * 180 / bars)
|
||||
c2 = b1
|
||||
c3 = -1 * a1 * a1
|
||||
c1 = 1 - c2 - c3
|
||||
Filt = c1 * (HP + lastHP) / 2 + c2 * FilterHist[1] + c3 * FilterHist[0]
|
||||
# Filt = float("{:.8f}".format(float(Filt))) # to fix for small scientific notations, the big ones fail
|
||||
|
||||
# 3 Bar average of Wave amplitude and power
|
||||
Wave = (Filt + FilterHist[1] + FilterHist[0]) / 3
|
||||
Pwr = (Filt * Filt + FilterHist[1] * FilterHist[1] + FilterHist[0] * FilterHist[0]) / 3
|
||||
|
||||
# Normalize the Average Wave to Square Root of the Average Power
|
||||
Wave = Wave / npSqrt(Pwr)
|
||||
|
||||
# update storage, result
|
||||
FilterHist.append(Filt) # append new Filt value
|
||||
FilterHist.pop(0) # remove first element of list (left) -> updating/trim
|
||||
lastHP = HP
|
||||
lastClose = close[i]
|
||||
result.append(Wave)
|
||||
else: # this version is the default version
|
||||
# Instance Variables
|
||||
lastHP = lastClose = 0
|
||||
filtHist = npZeros(3)
|
||||
result = [npNaN] * (length - 1) + [0]
|
||||
|
||||
# Calculate constants
|
||||
angle = 2 * npPi / length
|
||||
alpha1 = (1 - npSin(angle)) / npCos(angle)
|
||||
ang = 2 ** .5 * npPi / bars
|
||||
a1 = npExp(-ang)
|
||||
c2 = 2 * a1 * npCos(ang)
|
||||
c3 = -a1 ** 2
|
||||
c1 = 1 - c2 - c3
|
||||
Filt = c1 * (HP + lastHP) / 2 + c2 * FilterHist[1] + c3 * FilterHist[0]
|
||||
# Filt = float("{:.8f}".format(float(Filt))) # to fix for small scientific notations, the big ones fail
|
||||
|
||||
# 3 Bar average of Wave amplitude and power
|
||||
Wave = (Filt + FilterHist[1] + FilterHist[0]) / 3
|
||||
Pwr = (Filt * Filt + FilterHist[1] * FilterHist[1] + FilterHist[0] * FilterHist[0]) / 3
|
||||
for i in range(length, close.size):
|
||||
HP = 0.5 * (1 + alpha1) * (close[i] - lastClose) + alpha1 * lastHP
|
||||
|
||||
# Normalize the Average Wave to Square Root of the Average Power
|
||||
Wave = Wave / npSqrt(Pwr)
|
||||
# Rotate filters to overwrite oldest value
|
||||
filtHist = npRoll(filtHist, -1)
|
||||
filtHist[-1] = c1 * (HP + lastHP) / 2 + c2 * filtHist[1] + c3 * filtHist[0]
|
||||
|
||||
# update storage, result
|
||||
FilterHist.append(Filt) # append new Filt value
|
||||
FilterHist.pop(0) # remove first element of list (left) -> updating/trim
|
||||
lastHP = HP
|
||||
lastClose = close[i]
|
||||
result.append(Wave)
|
||||
# Wave calculation
|
||||
wave = npMean(filtHist)
|
||||
rms = npSqrt(npMean(filtHist ** 2))
|
||||
wave = wave / rms
|
||||
|
||||
# Update past values
|
||||
lastHP = HP
|
||||
lastClose = close[i]
|
||||
result.append(wave)
|
||||
|
||||
ebsw = Series(result, index=close.index)
|
||||
|
||||
@@ -78,22 +114,30 @@ def ebsw(close, length=None, bars=None, offset=None, **kwargs):
|
||||
|
||||
|
||||
ebsw.__doc__ = \
|
||||
"""Even Better SineWave (EBSW) *beta*
|
||||
"""Even Better SineWave (EBSW)
|
||||
|
||||
This indicator measures market cycles and uses a low pass filter to remove noise.
|
||||
Its output is bound signal between -1 and 1 and the maximum length of a detected
|
||||
trend is limited by its length input.
|
||||
|
||||
Written by rengel8 for Pandas TA based on a publication at 'prorealcode.com' and
|
||||
a book by J.F.Ehlers.
|
||||
a book by J.F.Ehlers. According to the suggestion by Squigglez2* and major differences between
|
||||
the initial version's output close to the implementation from Ehler's, the default version is now
|
||||
more closely related to the code from pro-realcode.
|
||||
|
||||
* This implementation seems to be logically limited. It would make sense to
|
||||
implement exactly the version from prorealcode and compare the behaviour.
|
||||
Remark:
|
||||
The default version is now more cycle oriented and tends to be less whipsaw-prune. Thus the older version
|
||||
might offer earlier signals at medium and stronger reversals.
|
||||
A test against the version at TradingView showed very close results with the advantage to be one bar/candle
|
||||
faster, than the corresponding reference value. This might be pre-roll related and was not further investigated.
|
||||
|
||||
|
||||
* https://github.com/twopirllc/pandas-ta/issues/350
|
||||
|
||||
|
||||
Sources:
|
||||
https://www.prorealcode.com/prorealtime-indicators/even-better-sinewave/
|
||||
J.F.Ehlers 'Cycle Analytics for Traders', 2014
|
||||
- https://www.prorealcode.com/prorealtime-indicators/even-better-sinewave/
|
||||
- J.F.Ehlers 'Cycle Analytics for Traders', 2014
|
||||
|
||||
Calculation:
|
||||
refer to 'sources' or implementation
|
||||
|
||||
Reference in New Issue
Block a user