From 8093e35badc76b7cca2311eaef83abc1b1908a10 Mon Sep 17 00:00:00 2001 From: nardew <28791551+nardew@users.noreply.github.com> Date: Sun, 16 Mar 2025 23:02:02 +0100 Subject: [PATCH] Normalized Average True Range --- README.md | 6 ++-- docs/indicator-catalogue.md | 23 +++++++------- examples/binance_online.py | 3 +- examples/indicators.py | 5 +-- talipp/indicators/NATR.py | 47 +++++++++++++++++++++++++++++ talipp/indicators/RogersSatchell.py | 2 +- talipp/indicators/__init__.py | 2 ++ test/test_NATR.py | 32 ++++++++++++++++++++ 8 files changed, 102 insertions(+), 18 deletions(-) create mode 100644 talipp/indicators/NATR.py create mode 100644 test/test_NATR.py diff --git a/README.md b/README.md index 0ed5bf18..139bb096 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,11 @@ Last but not least, `talipp` is a community project and therefore open to any su ### What's new in the recent versions +- Normalized Average True Range indicator - Rogers-Satchell volatility indicator - auto-sampling of input values -- [v2.0.0 scope](https://github.com/nardew/talipp/issues/111) -For the full history of changes see [CHANGELOG](https://github.com/nardew/talipp/releases). +For the full history of changes see [Release Notes](https://github.com/nardew/talipp/releases). --- @@ -45,7 +45,7 @@ For the full history of changes see [CHANGELOG](https://github.com/nardew/talipp - Accumulation/Distribution (ADL) - Aroon - Average Directional Index (ADX) -- Average True Range (ATR) +- Average True Range (ATR), Normalized Average True Range (NATR) - Awesome Oscillator (AO) - Balance of Power (BOP) - Bollinger Bands (BB) diff --git a/docs/indicator-catalogue.md b/docs/indicator-catalogue.md index 4dce2275..94ffc0fe 100644 --- a/docs/indicator-catalogue.md +++ b/docs/indicator-catalogue.md @@ -1,10 +1,10 @@ # Indicator catalogue -| Name | Full name | Description | -|-------------------------------------------------------------------------|---------------------------------------|----------------------------------------------------------------------------------------------------------------------| +| Name | Full name | Description | +|-------------------------------------------------------------------------|---------------------------------------|----------------------------------------------------------------------------------------------------------------| | [ADL][talipp.indicators.AccuDist.AccuDist] | Accumulation Distribution Line | [](https://school.stockcharts.com/doku.php?id=technical_indicators:accumulation_distribution_line) | | [ADX][talipp.indicators.ADX.ADX] | Average Directional Index | [](https://school.stockcharts.com/doku.php?id=technical_indicators:average_directional_index_adx) | -| [ALMA][talipp.indicators.ALMA.ALMA] | Arnaud Legoux Moving Average | | +| [ALMA][talipp.indicators.ALMA.ALMA] | Arnaud Legoux Moving Average | | | [AO][talipp.indicators.AO.AO] | Awesome Oscillator | [](https://www.babypips.com/forexpedia/awesome-oscillator) | | [Aroon][talipp.indicators.Aroon.Aroon] | Aroon Indicator | [](https://www.investopedia.com/terms/a/aroon.asp) | | [ATR][talipp.indicators.ATR.ATR] | Average True Range | [](https://school.stockcharts.com/doku.php?id=technical_indicators:average_true_range_atr) | @@ -22,7 +22,7 @@ | [EMV][talipp.indicators.EMV.EMV] | Ease of Movement | [](https://school.stockcharts.com/doku.php?id=technical_indicators:ease_of_movement_emv) | | [ForceIndex][talipp.indicators.ForceIndex.ForceIndex] | Force Index | [](https://school.stockcharts.com/doku.php?id=technical_indicators:force_index) | | [HMA][talipp.indicators.HMA.HMA] | Hull Moving Average | [](https://school.stockcharts.com/doku.php?id=technical_indicators:hull_moving_average) | -| [IBS][talipp.indicators.IBS.IBS] | Internal Bar Strength | [](https://www.coingecko.com/learn/internal-bar-strength-ibs) | +| [IBS][talipp.indicators.IBS.IBS] | Internal Bar Strength | [](https://www.coingecko.com/learn/internal-bar-strength-ibs) | | [IchimokuCloud][talipp.indicators.Ichimoku.Ichimoku] | Ichimoku Cloud | [](https://school.stockcharts.com/doku.php?id=technical_indicators:ichimoku_cloud) | | [KAMA][talipp.indicators.KAMA.KAMA] | Kauffman's Adaptive Moving Average | [](https://school.stockcharts.com/doku.php?id=technical_indicators:kaufman_s_adaptive_moving_average) | | [KeltnerChannels][talipp.indicators.KeltnerChannels.KeltnerChannels] | Keltner Channels | [](https://school.stockcharts.com/doku.php?id=technical_indicators:keltner_channels) | @@ -32,18 +32,19 @@ | [MassIndex][talipp.indicators.MassIndex.MassIndex] | Mass Index | [](https://school.stockcharts.com/doku.php?id=technical_indicators:mass_index) | | [McGinleyDynamic][talipp.indicators.McGinleyDynamic.McGinleyDynamic] | McGinley Dynamic | [](https://www.investopedia.com/terms/m/mcginley-dynamic.asp) | | [MeanDev][talipp.indicators.MeanDev.MeanDev] | Mean Deviation | [](https://school.stockcharts.com/doku.php?id=technical_indicators:accumulation_distribution_line) | +| [NATR][talipp.indicators.NATR.NATR] | Normalized Average True Range | [](https://www.macroption.com/normalized-atr/) | | [OBV][talipp.indicators.OBV.OBV] | On-balacen Volume | [](https://school.stockcharts.com/doku.php?id=technical_indicators:on_balance_volume_obv) | | [ParabolicSAR][talipp.indicators.ParabolicSAR.ParabolicSAR] | Parabolic Stop and Reverse | [](https://school.stockcharts.com/doku.php?id=technical_indicators:parabolic_sar) | -| [PivotsHL][talipp.indicators.PivotsHL.PivotsHL][^1][^2] | Pivot High Low Points | | +| [PivotsHL][talipp.indicators.PivotsHL.PivotsHL][^1][^2] | Pivot High Low Points | | | [ROC][talipp.indicators.ROC.ROC] | Rate of Change | [](https://school.stockcharts.com/doku.php?id=technical_indicators:rate_of_change_roc_and_momentum) | -| [RogersSatchell][talipp.indicators.RogersSatchell.RogersSatchell] | Rogers-Satchell volatility indicator | [](https://portfolioslab.com/tools/rogers-satchell) | +| [RogersSatchell][talipp.indicators.RogersSatchell.RogersSatchell] | Rogers-Satchell volatility indicator | [](https://portfolioslab.com/tools/rogers-satchell) | | [RSI][talipp.indicators.RSI.RSI] | Relative Strength Index | [](https://school.stockcharts.com/doku.php?id=technical_indicators:relative_strength_index_rsi) | -| [SFX][talipp.indicators.SFX.SFX] | | | +| [SFX][talipp.indicators.SFX.SFX] | | | | [SMA][talipp.indicators.SMA.SMA] | Simple Moving Average | [](https://www.investopedia.com/terms/s/sma.asp) | -| [SMMA][talipp.indicators.SMMA.SMMA] | Smoothed Simple Moving Average | | -| [SOBV][talipp.indicators.SOBV.SOBV] | Smoothed On-Balance Volume | | +| [SMMA][talipp.indicators.SMMA.SMMA] | Smoothed Simple Moving Average | | +| [SOBV][talipp.indicators.SOBV.SOBV] | Smoothed On-Balance Volume | | | [STC][talipp.indicators.STC.STC] | Schaff Trend Cycle | [](https://howtotrade.com/indicators/schaff-trend-cycle/) | -| [StdDev][talipp.indicators.StdDev.StdDev] | Standard Deviation | | +| [StdDev][talipp.indicators.StdDev.StdDev] | Standard Deviation | | | [Stoch][talipp.indicators.Stoch.Stoch] | Stochastic Oscillator | [](https://school.stockcharts.com/doku.php?id=technical_indicators:stochastic_oscillator_fast_slow_and_full) | | [StochRSI][talipp.indicators.StochRSI.StochRSI] | Stochastic Relative Strength Index | [](https://school.stockcharts.com/doku.php?id=technical_indicators:stochrsi) | | [SuperTrend][talipp.indicators.SuperTrend.SuperTrend] | Super Trend | [](https://www.investopedia.com/supertrend-indicator-7976167) | @@ -57,7 +58,7 @@ | [VWAP][talipp.indicators.VWAP.VWAP] | Volume-weighted Average Price | [](https://school.stockcharts.com/doku.php?id=technical_indicators:vwap_intraday) | | [VWMA][talipp.indicators.VWMA.VWMA] | Volume-weighted Moving Average | [](https://www.tradingsetupsreview.com/volume-weighted-moving-average-vwma/) | | [WMA][talipp.indicators.WMA.WMA] | Weighted Moving Average | [](https://fxopen.com/blog/en/what-is-a-weighted-moving-average-and-how-do-you-calculate-it/) | -| [ZigZag][talipp.indicators.ZigZag.ZigZag][^1] | ZigZag | [](https://school.stockcharts.com/doku.php?id=technical_indicators:zigzag) | +| [ZigZag][talipp.indicators.ZigZag.ZigZag][^1] | ZigZag | [](https://school.stockcharts.com/doku.php?id=technical_indicators:zigzag) | | [ZLEMA][talipp.indicators.ZLEMA.ZLEMA] | Zero-lag Exponential Moving Average | [](https://en.wikipedia.org/wiki/Zero_lag_exponential_moving_average) | Request a new indicator via [GitHub Issues](https://github.com/nardew/talipp/issues/new). diff --git a/examples/binance_online.py b/examples/binance_online.py index b28fcc21..75e6e84b 100644 --- a/examples/binance_online.py +++ b/examples/binance_online.py @@ -8,7 +8,7 @@ from talipp.indicators import AccuDist, ADX, ALMA, AO, Aroon, ATR, BB, BOP, CCI, ChaikinOsc, ChandeKrollStop, CHOP, \ CoppockCurve, DEMA, DonchianChannels, DPO, EMA, EMV, ForceIndex, HMA, IBS, Ichimoku, \ - KAMA, KeltnerChannels, KST, KVO, MACD, MassIndex, McGinleyDynamic, MeanDev, OBV, ROC, RogersSatchell, RSI, \ + KAMA, KeltnerChannels, KST, KVO, MACD, MassIndex, McGinleyDynamic, MeanDev, NATR, OBV, ROC, RogersSatchell, RSI, \ ParabolicSAR, \ SFX, SMA, SMMA, SOBV, STC, StdDev, \ Stoch, StochRSI, SuperTrend, T3, TEMA, TRIX, TSI, TTM, UO, VTX, VWAP, VWMA, WMA, ZigZag, ZLEMA @@ -61,6 +61,7 @@ async def run(): print(f'MassIndex: {MassIndex(9, 9, 10, ohlcv)[-1]}') print(f'McGinleyDynamic: {McGinleyDynamic(14, close)[-1]}') print(f'MeanDev: {MeanDev(10, close)[-1]}') + print(f'NATR: {NATR(14, ohlcv)[-5:]}') print(f'OBV: {OBV(ohlcv)[-1]}') print(f'ROC: {ROC(9, close)[-1]}') print(f'RogersSatchell: {RogersSatchell(9, ohlcv)[-5:]}') diff --git a/examples/indicators.py b/examples/indicators.py index c778d5b6..c4050810 100644 --- a/examples/indicators.py +++ b/examples/indicators.py @@ -2,7 +2,7 @@ from talipp.indicators import AccuDist, ADX, ALMA, AO, Aroon, ATR, BB, BOP, CCI, ChaikinOsc, ChandeKrollStop, CHOP, \ CoppockCurve, DEMA, DonchianChannels, DPO, EMA, EMV, ForceIndex, HMA, IBS, Ichimoku, KAMA, KeltnerChannels, KST, KVO, \ - MACD, MassIndex, MeanDev, OBV, ROC, RogersSatchell, RSI, ParabolicSAR, SFX, SMA, SMMA, SOBV, STC, StdDev, Stoch, StochRSI, \ + MACD, MassIndex, MeanDev, NATR, OBV, ROC, RogersSatchell, RSI, ParabolicSAR, SFX, SMA, SMMA, SOBV, STC, StdDev, Stoch, StochRSI, \ SuperTrend, T3, TEMA, TRIX, TSI, TTM, UO, VTX, VWAP, VWMA, WMA, ZigZag, ZLEMA from talipp.ohlcv import OHLCVFactory @@ -47,9 +47,10 @@ print(f'MACD: {MACD(12, 26, 9, close)[-1]}') print(f'MassIndex: {MassIndex(9, 9, 10, ohlcv)[-1]}') print(f'MeanDev: {MeanDev(10, close)[-1]}') + print(f'NATR: {NATR(14, ohlcv)[-1]}') print(f'OBV: {OBV(ohlcv)[-1]}') print(f'ROC: {ROC(9, close)[-1]}') - #print(f'RogersSatchell: {RogersSatchell(9, ohlcv)[-1]}') + print(f'RogersSatchell: {RogersSatchell(9, ohlcv)[-1]}') print(f'RSI: {RSI(14, close)[-1]}') print(f"SAR: {ParabolicSAR(0.02, 0.02, 0.2, ohlcv)[-20:]}") print(f'SFX: {SFX(12, 12, 3, ohlcv)[-1]}') diff --git a/talipp/indicators/NATR.py b/talipp/indicators/NATR.py new file mode 100644 index 00000000..68ee3f95 --- /dev/null +++ b/talipp/indicators/NATR.py @@ -0,0 +1,47 @@ +from typing import List, Any + +from talipp.indicator_util import has_valid_values +from talipp.indicators import ATR +from talipp.indicators.Indicator import Indicator, InputModifierType +from talipp.input import SamplingPeriodType +from talipp.ohlcv import OHLCV + + +class NATR(Indicator): + """Normalized Average True Range + + Input type: [OHLCV][talipp.ohlcv.OHLCV] + + Output type: `float` + + Args: + period: Period. + input_values: List of input values. + input_indicator: Input indicator. + input_modifier: Input modifier. + input_sampling: Input sampling type. + """ + + def __init__(self, period: int, + input_values: List[OHLCV] = None, + input_indicator: Indicator = None, + input_modifier: InputModifierType = None, + input_sampling: SamplingPeriodType = None): + super(NATR, self).__init__(input_modifier=input_modifier, + input_sampling=input_sampling) + + self.period = period + self.atr = ATR(period) + + self.add_sub_indicator(self.atr) + + self.initialize(input_values, input_indicator) + + def _calculate_new_value(self) -> Any: + if not has_valid_values(self.atr, 1): + return None + + if self.input_values[-1].close == 0: + return None + + return 100.0 * self.atr[-1] / self.input_values[-1].close diff --git a/talipp/indicators/RogersSatchell.py b/talipp/indicators/RogersSatchell.py index 2771e68b..73e3ff5a 100644 --- a/talipp/indicators/RogersSatchell.py +++ b/talipp/indicators/RogersSatchell.py @@ -42,4 +42,4 @@ def _calculate_new_value(self) -> Any: for ohlcv in self.input_values[-self.period:]: s += log(float(ohlcv.high) / ohlcv.close) * log(float(ohlcv.high) / ohlcv.open) + log(float(ohlcv.low) / ohlcv.close) * log(float(ohlcv.low) / ohlcv.open) - return sqrt(s / self.period) + return sqrt(s / self.period) if s > 0 else None diff --git a/talipp/indicators/__init__.py b/talipp/indicators/__init__.py index e10ffd29..88383fcc 100644 --- a/talipp/indicators/__init__.py +++ b/talipp/indicators/__init__.py @@ -29,6 +29,7 @@ from .MassIndex import MassIndex as MassIndex from .McGinleyDynamic import McGinleyDynamic as McGinleyDynamic from .MeanDev import MeanDev as MeanDev +from .NATR import NATR as NATR from .OBV import OBV as OBV from .PivotsHL import PivotsHL as PivotsHL from .ROC import ROC as ROC @@ -89,6 +90,7 @@ "MassIndex", "McGinleyDynamic", "MeanDev", + "NATR", "OBV", "ParabolicSAR", "PivotsHL", diff --git a/test/test_NATR.py b/test/test_NATR.py new file mode 100644 index 00000000..80edd514 --- /dev/null +++ b/test/test_NATR.py @@ -0,0 +1,32 @@ +import unittest + +from talipp.indicators import NATR + +from TalippTest import TalippTest + + +class TestATR(TalippTest): + def setUp(self) -> None: + self.input_values = list(TalippTest.OHLCV_TMPL) + + def test_init(self): + ind = NATR(5, self.input_values) + + print(ind) + + self.assertAlmostEqual(ind[-3], 6.387410, places = 5) + self.assertAlmostEqual(ind[-2], 6.501871, places = 5) + self.assertAlmostEqual(ind[-1], 6.861131, places = 5) + + def test_update(self): + self.assertIndicatorUpdate(NATR(5, self.input_values)) + + def test_delete(self): + self.assertIndicatorDelete(NATR(5, self.input_values)) + + def test_purge_oldest(self): + self.assertIndicatorPurgeOldest(NATR(5, self.input_values)) + + +if __name__ == '__main__': + unittest.main()