diff --git a/Source/Indicators/KAMA_Series.cs b/Source/Indicators/KAMA_Series.cs index c9360ae5..eda39bc4 100644 --- a/Source/Indicators/KAMA_Series.cs +++ b/Source/Indicators/KAMA_Series.cs @@ -1,63 +1,67 @@ -namespace QuanTAlib; -using System; - -/* -KAMA: Kaufman's Adaptive Moving Average - Created in 1988 by American quantitative finance theorist Perry J. Kaufman and is known as - Kaufman's Adaptive Moving Average (KAMA). Even though the method was developed as early as 1972, - it was not until the popular book titled "Trading Systems and Methods" that it was made widely - available to the public. Unlike other conventional moving averages systems, the Kaufman's Adaptive - Moving Average, considers market volatility apart from price fluctuations. - - KAMAi = KAMAi - 1 + SC * ( price - KAMAi-1 ) - -Sources: - https://www.tutorialspoint.com/kaufman-s-adaptive-moving-average-kama-formula-and-how-does-it-work - https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/kaufmans-adaptive-moving-average-kama/ - https://www.technicalindicators.net/indicators-technical-analysis/152-kama-kaufman-adaptive-moving-average - -Remark: - If useNaN:true argument is provided, KAMA starts calculating values from [period] bar onwards. - Without useNaN argument (default setting), KAMA starts calculating values from bar 1 - and yields - slightly different results for the first 50 bars - and then converges with the other one. - - */ - -public class KAMA_Series : Single_TSeries_Indicator -{ - private static double _scFast, _scSlow; - private readonly System.Collections.Generic.List _buffer = new(); - private double _lastkama = double.NaN; - private double _lastlastkama; - - public KAMA_Series(TSeries source, int period, int fast = 2, int slow= 30, bool useNaN = false) : base(source, period, useNaN) { - _scFast = 2.0 / (fast+1); - _scSlow = 2.0 / (slow+1); - if (base._data.Count > 0) { base.Add(base._data); } - } - public override void Add((System.DateTime t, double v) TValue, bool update) { - if (update) { - _buffer[_buffer.Count - 1] = TValue.v; - this._lastkama = this._lastlastkama; - } - else { - _buffer.Add(TValue.v); - } - if (_buffer.Count>_p+1) { _buffer.RemoveAt(0); } - double _kama = TValue.v; - double _change = Math.Abs( _buffer[_buffer.Count-1] - _buffer[(_buffer.Count>_p+1)?1:0]); - double _sumpv = 0; - for (int i = 1; i < _buffer.Count; i++) { - _sumpv += Math.Abs(_buffer[(_buffer.Count>0)?i:0]- _buffer[i-1]); - } - double _er = (_sumpv==0)?0:_change/_sumpv; - double _sc = (_er * (_scFast - _scSlow)) + _scSlow; - if (_buffer.Count==1) { _lastkama = _buffer[0]; } - - _kama = (_lastkama + (_sc * _sc * (TValue.v - _lastkama))); - _lastlastkama = _lastkama; - _lastkama = _kama; - var result = (TValue.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _kama); - base.Add(result, update); - } +namespace QuanTAlib; +using System; + +/* +KAMA: Kaufman's Adaptive Moving Average + Created in 1988 by American quantitative finance theorist Perry J. Kaufman and is known as + Kaufman's Adaptive Moving Average (KAMA). Even though the method was developed as early as 1972, + it was not until the popular book titled "Trading Systems and Methods" that it was made widely + available to the public. Unlike other conventional moving averages systems, the Kaufman's Adaptive + Moving Average, considers market volatility apart from price fluctuations. + + KAMAi = KAMAi - 1 + SC * ( price - KAMAi-1 ) + +Sources: + https://www.tutorialspoint.com/kaufman-s-adaptive-moving-average-kama-formula-and-how-does-it-work + https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/kaufmans-adaptive-moving-average-kama/ + https://www.technicalindicators.net/indicators-technical-analysis/152-kama-kaufman-adaptive-moving-average + +Remark: + If useNaN:true argument is provided, KAMA starts calculating values from [period] bar onwards. + Without useNaN argument (default setting), KAMA starts calculating values from bar 1 - and yields + slightly different results for the first 50 bars - and then converges with the other one. + + */ + +public class KAMA_Series : Single_TSeries_Indicator +{ + private static double _scFast, _scSlow; + private readonly System.Collections.Generic.List _buffer = new(); + private double _lastkama = double.NaN; + private double _lastlastkama; + + public KAMA_Series(TSeries source, int period, int fast = 2, int slow= 30, bool useNaN = false) : base(source, period, useNaN) { + _scFast = 2.0 / (fast+1); + _scSlow = 2.0 / (slow+1); + if (base._data.Count > 0) { base.Add(base._data); } + } + public override void Add((System.DateTime t, double v) TValue, bool update) + { + if (update){ + _buffer[_buffer.Count - 1] = TValue.v; + this._lastkama = this._lastlastkama; + } else { + _buffer.Add(TValue.v); + } + if (_buffer.Count > _p + 1) { _buffer.RemoveAt(0); } + double _kama = TValue.v; + if (this.Count < this._p) { + for (int i = 0; i < this._buffer.Count; i++) { _kama += this._buffer[i]; } + _kama /= this._buffer.Count; + } else { + double _change = Math.Abs(_buffer[_buffer.Count - 1] - _buffer[(_buffer.Count > _p + 1) ? 1 : 0]); + double _sumpv = 0; + for (int i = 1; i < _buffer.Count; i++) + { + _sumpv += Math.Abs(_buffer[(_buffer.Count > 0) ? i : 0] - _buffer[i - 1]); + } + double _er = (_sumpv == 0) ? 0 : _change / _sumpv; + double _sc = (_er * (_scFast - _scSlow)) + _scSlow; + _kama = (_lastkama + (_sc * _sc * (TValue.v - _lastkama))); + } + _lastlastkama = _lastkama; + _lastkama = _kama; + var result = (TValue.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _kama); + base.Add(result, update); + } } \ No newline at end of file diff --git a/Tests/MovingAvg/KAMA_Test.cs b/Tests/MovingAvg/KAMA_Test.cs new file mode 100644 index 00000000..69e771c6 --- /dev/null +++ b/Tests/MovingAvg/KAMA_Test.cs @@ -0,0 +1,33 @@ +using Xunit; +using System; +using QuanTAlib; + +namespace MovingAvg; +public class KAMA_Test +{ + [Fact] + public void Add_Test() + { + TSeries a = new() { 0, 1, 2, 3, 4, 5 }; + KAMA_Series c = new(a, 3); + Assert.Equal(6, c.Count); + a.Add(5); + Assert.Equal(a.Count, c.Count); + a.Add(0, update: true); + Assert.Equal(a.Count, c.Count); + } + + [Fact] + public void Edge_Test() + { + TSeries a = new() { double.NaN, double.Epsilon, double.PositiveInfinity, double.MaxValue }; + KAMA_Series c = new(a, 3); + Assert.Equal(a.Count, c.Count); + a.Add(double.NaN); + Assert.Equal(a.Count, c.Count); + a.Add(double.PositiveInfinity); + Assert.Equal(a.Count, c.Count); + + } + +} diff --git a/Tests/Validations/Skender_Stock.cs b/Tests/Validations/Skender_Stock.cs index dc2de31d..a1adde8d 100644 --- a/Tests/Validations/Skender_Stock.cs +++ b/Tests/Validations/Skender_Stock.cs @@ -1,7 +1,7 @@ -using Xunit; using System; -using Skender.Stock.Indicators; using QuanTAlib; +using Skender.Stock.Indicators; +using Xunit; namespace Validation; @@ -108,4 +108,13 @@ public class Skender_Stock Assert.Equal(Math.Round((double)SK.Last().Atrp!, 8), Math.Round(QL.Last().v, 8)); } + + [Fact] + public void KAMA() + { + KAMA_Series QL = new(this.bars.Close, this.period, useNaN: false); + var SK = this.quotes.GetKama(this.period); + + Assert.Equal(Math.Round((double)SK.Last().Kama!, 8), Math.Round(QL.Last().v, 8)); + } }