mirror of
https://github.com/wassname/pandas-ta.git
synced 2026-09-09 11:28:26 +08:00
added Elders Thermometer 90%correlation
This commit is contained in:
@@ -51,7 +51,7 @@ Category = {
|
||||
"trend": ["adx", "amat", "aroon", "chop", "cksp", "decay", "decreasing", "dpo", "increasing", "long_run", "psar", "qstick", "short_run", "ttm_trend", "vortex"],
|
||||
|
||||
# Volatility
|
||||
"volatility": ["aberration", "accbands", "atr", "bbands", "donchian", "kc", "massi", "natr", "pdist", "rvi", "true_range", "ui"],
|
||||
"volatility": ["aberration", "accbands", "atr", "bbands", "donchian", "kc", "massi", "natr", "pdist", "rvi", "thermo", "true_range", "ui"],
|
||||
|
||||
# Volume, "vp" or "Volume Profile" is unique
|
||||
"volume": ["ad", "adosc", "aobv", "cmf", "efi", "eom", "mfi", "nvi", "obv", "pvi", "pvol", "pvt"],
|
||||
|
||||
@@ -1366,6 +1366,13 @@ class AnalysisIndicators(BasePandasObject):
|
||||
result = rvi(high=high, low=low, close=close, length=length, scalar=scalar, refined=refined, thirds=thirds, mamode=mamode, drift=drift, offset=offset, **kwargs)
|
||||
return self._post_process(result, **kwargs)
|
||||
|
||||
def thermo(self, long=None, short= None, length=None, mamode=None, drift=None, offset=None, **kwargs):
|
||||
high = self._get_column(kwargs.pop("high", "high"))
|
||||
low = self._get_column(kwargs.pop("low", "low"))
|
||||
|
||||
result = thermo(high=high, low=low, long=long, short=short, length=length, mamode=mamode, drift=drift, offset=offset, **kwargs)
|
||||
return self._post_process(result, **kwargs)
|
||||
|
||||
def true_range(self, drift=None, offset=None, **kwargs):
|
||||
high = self._get_column(kwargs.pop("high", "high"))
|
||||
low = self._get_column(kwargs.pop("low", "low"))
|
||||
|
||||
@@ -9,5 +9,6 @@ from .massi import massi
|
||||
from .pdist import pdist
|
||||
from .natr import natr
|
||||
from .rvi import rvi
|
||||
from .thermo import thermo
|
||||
from .true_range import true_range
|
||||
from .ui import ui
|
||||
@@ -0,0 +1,126 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import numpy as np
|
||||
from pandas import DataFrame, Series
|
||||
from pandas_ta.overlap import ema
|
||||
from pandas_ta.utils import get_offset, verify_series, get_drift
|
||||
|
||||
def thermo(high, low, long=None, short=None, length=None, mamode=None, drift=None, offset=None, **kwargs):
|
||||
"""Indicator: Elders Thermometer (THERMO)"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
drift = get_drift(drift)
|
||||
offset = get_offset(offset)
|
||||
|
||||
length = int(length) if length and length > 0 else 20
|
||||
long = float(long) if long and long > 0 else 2
|
||||
short = float(short) if short and short > 0 else 0.5
|
||||
mamode = mamode.lower() if mamode else "ema"
|
||||
|
||||
asint = kwargs.pop("asint", True)
|
||||
lazybear = kwargs.pop("lazybear", False)
|
||||
|
||||
|
||||
# Calculate Result
|
||||
thermoL = (low.shift(drift) - low).abs()
|
||||
thermoH = (high - high.shift(drift)).abs()
|
||||
if lazybear:
|
||||
thermo = (high < high.shift(drift)) & (low > low.shift(drift))
|
||||
if thermo.any():
|
||||
thermo = thermoL
|
||||
thermo = thermo.where(thermoH < thermoL, thermoH)
|
||||
thermo.index = high.index
|
||||
|
||||
thermoma = ema(thermo,length)
|
||||
|
||||
else:
|
||||
thermo = thermoL
|
||||
thermo = thermo.where(thermoH < thermoL, thermoH)
|
||||
thermo.index = high.index
|
||||
|
||||
thermoma = ema(thermo, length)
|
||||
|
||||
# Create signals
|
||||
thermo_long = thermo < (thermoma * long) # Returns T/F
|
||||
thermo_short = thermo > (thermoma * short) # Returns T/F
|
||||
|
||||
# Binary output, useful for signals
|
||||
if asint:
|
||||
thermo_long = thermo_long.astype(int)
|
||||
thermo_short = thermo_short.astype(int)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
thermo = thermo.shift(offset)
|
||||
thermoma = thermoma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if "fillna" in kwargs:
|
||||
thermo.fillna(kwargs["fillna"], inplace=True)
|
||||
thermoma.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
thermo.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
thermoma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
_props = f"_{length}_{long}_{short}"
|
||||
thermo.name = f"THERMO{_props}"
|
||||
thermoma.name = f"THERMOma{_props}"
|
||||
thermo_long.name = f"THERMOl{_props}"
|
||||
thermo_short.name = f"THERMOs{_props}"
|
||||
|
||||
thermo.category = thermo_long.category = thermo_short.category = thermoma.category = "volatility"
|
||||
|
||||
# Prepare Dataframe to return
|
||||
data = {thermo.name: thermo, thermoma.name: thermoma, thermo_long.name: thermo_long, thermo_short.name: thermo_short}
|
||||
df = DataFrame(data)
|
||||
df.name = f"THERMO_{length}"
|
||||
df.category = thermo.category
|
||||
|
||||
return df
|
||||
|
||||
thermo.__doc__ = \
|
||||
"""Elders Thermometer (THERMO)
|
||||
|
||||
Elder's Thermometer measures price volatility.
|
||||
|
||||
Sources:
|
||||
https://www.motivewave.com/studies/elders_thermometer.htm
|
||||
https://www.tradingview.com/script/HqvTuEMW-Elder-s-Market-Thermometer-LazyBear/
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=20, drift=1, mamode=EMA, long=2 short=0.5
|
||||
|
||||
EMA = Exponential Moving Average
|
||||
|
||||
thermoL = (low.shift(drift) - low).abs()
|
||||
thermoH = (high - high.shift(drift)).abs()
|
||||
thermo = np.where(thermoH > thermoL, thermoH, thermoL)
|
||||
|
||||
thermoma = ema(thermo, length)
|
||||
|
||||
thermo_long = thermo < (thermoma * long) # Returns T/F
|
||||
thermo_short = thermo > (thermoma * short) # Returns T/F
|
||||
|
||||
Binary output
|
||||
thermo_long = thermo_long.astype(int)
|
||||
thermo_short = thermo_short.astype(int)
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
long(int): The buy factor
|
||||
short(float): The sell factor
|
||||
length (int): The period. Default: 20
|
||||
drift (int): The diff period. Default: 1
|
||||
mamode (str): Two options: None or "sma". Default: ema
|
||||
offset (int): How many periods to offset the result. Default: 0
|
||||
|
||||
Kwargs:
|
||||
fillna (value, optional): pd.DataFrame.fillna(value)
|
||||
fill_method (value, optional): Type of fill method
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: thermo, thermoma, thermo_long, thermo_short columns.
|
||||
"""
|
||||
@@ -79,6 +79,11 @@ class TestVolatilityExtension(TestCase):
|
||||
self.assertIsInstance(self.data, DataFrame)
|
||||
self.assertEqual(self.data.columns[-1], "RVIt_14")
|
||||
|
||||
def test_thermo_ext(self):
|
||||
self.data.ta.thermo(append=True)
|
||||
self.assertIsInstance(self.data, DataFrame)
|
||||
self.assertEqual(list(self.data.columns[-4:]), ["THERMO_20_2_0.5", "THERMOma_20_2_0.5", "THERMOl_20_2_0.5", "THERMOs_20_2_0.5"])
|
||||
|
||||
def test_true_range_ext(self):
|
||||
self.data.ta.true_range(append=True)
|
||||
self.assertIsInstance(self.data, DataFrame)
|
||||
|
||||
@@ -144,6 +144,11 @@ class TestVolatility(TestCase):
|
||||
self.assertIsInstance(result, Series)
|
||||
self.assertEqual(result.name, "RVIt_14")
|
||||
|
||||
def test_thermo(self):
|
||||
result = pandas_ta.thermo(self.high, self.low)
|
||||
self.assertIsInstance(result, DataFrame)
|
||||
self.assertEqual(result.name, "THERMO_20_2_0.5")
|
||||
|
||||
def test_true_range(self):
|
||||
result = pandas_ta.true_range(self.high, self.low, self.close)
|
||||
self.assertIsInstance(result, Series)
|
||||
|
||||
Reference in New Issue
Block a user