From f4364ca7c466a6105a97178b6350a123b4df2876 Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Sat, 23 Apr 2022 11:23:09 -0700 Subject: [PATCH] KAMA --- Quantower/Indicators/KAMA_chart.cs | 52 +++++++++++++++++++++++++++++ Source/Indicators/KAMA_Series.cs | 53 ++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 Quantower/Indicators/KAMA_chart.cs create mode 100644 Source/Indicators/KAMA_Series.cs diff --git a/Quantower/Indicators/KAMA_chart.cs b/Quantower/Indicators/KAMA_chart.cs new file mode 100644 index 00000000..3e497191 --- /dev/null +++ b/Quantower/Indicators/KAMA_chart.cs @@ -0,0 +1,52 @@ +using System.Diagnostics; +using System.Drawing; +using TradingPlatform.BusinessLayer; +namespace QuanTAlib; + +public class KAMA_chart : Indicator +{ + #region Parameters + + [InputParameter("Smoothing period", 0, 1, 999, 1, 1)] + private int Period = 10; + + [InputParameter("Data source", 1, variants: new object[] + { "Open", 0, "High", 1, "Low", 2, "Close", 3, "HL2", 4, "OC2", 5, + "OHL3", 6, "HLC3", 7, "OHLC4", 8, "Weighted (HLCC4)", 9 })] + private int DataSource = 3; + + #endregion Parameters + + private readonly TBars bars = new(); + + /////// + private KAMA_Series indicator; + /////// + + public KAMA_chart() + { + this.SeparateWindow = false; + this.Name = "KAMA - Kaufman's Adaptive Moving Average"; + this.Description = "Kaufman's Adaptive Moving Average description"; + this.AddLineSeries("KAMA", Color.RoyalBlue, 3, LineStyle.Solid); + } + + protected override void OnInit() + { + this.ShortName = + "KAMA (" + TBars.SelectStr(this.DataSource) + ", " + this.Period + ")"; + this.indicator = new(source: bars.Select(this.DataSource), period: this.Period, useNaN: false); + Debug.WriteLine($"KAMA on-init. indicator.Count: {indicator.Count}"); + } + + protected override void OnUpdate(UpdateArgs args) + { + bool update = !(args.Reason == UpdateReason.NewBar || + args.Reason == UpdateReason.HistoricalBar); + this.bars.Add(this.Time(), this.GetPrice(PriceType.Open), + this.GetPrice(PriceType.High), this.GetPrice(PriceType.Low), + this.GetPrice(PriceType.Close), this.GetPrice(PriceType.Volume), update); + double result = this.indicator[this.indicator.Count - 1].v; + this.SetValue(result); + } +} diff --git a/Source/Indicators/KAMA_Series.cs b/Source/Indicators/KAMA_Series.cs new file mode 100644 index 00000000..ed7ebc7b --- /dev/null +++ b/Source/Indicators/KAMA_Series.cs @@ -0,0 +1,53 @@ +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; + 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) { } else { } + double _change = Math.Abs( TValue.v - _data[(this.Count>_p)?this.Count-_p : 0].v); + double _sumpv = 0; + double _kama = TValue.v; + for (int i = (this.Count-_p+1>0)?this.Count-_p+1:0; i <= this.Count; i++) { + _sumpv += Math.Abs(_data[(i>0)?i:0].v- _data[(i>1)?i-1:0].v); + } + if (_sumpv != 0) { + double _er = _change/_sumpv; + double _sc = (_er * (_scFast - _scSlow)) + _scSlow; + double _prevKama = (this.Count > 0) ? this[this.Count-1].v : TValue.v; + if (double.IsNaN(_prevKama)) { _prevKama = TValue.v; } + _kama = (_prevKama + (_sc * _sc * (TValue.v - _prevKama))); + } + + var result = (TValue.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _kama); + base.Add(result, update); + } +} \ No newline at end of file