diff --git a/Calculations/Basics/MAX_Series.cs b/Calculations/Basics/MAX_Series.cs
deleted file mode 100644
index 109461a9..00000000
--- a/Calculations/Basics/MAX_Series.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-MAX - Maximum value in the given period in the series.
- If period = 0 => period = full length of the series
- */
-
-public class MAX_Series : Single_TSeries_Indicator
-{
- public MAX_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- double _max = _buffer.Max();
-
- base.Add((TValue.t, _max), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Basics/MIDPOINT_Series.cs b/Calculations/Basics/MIDPOINT_Series.cs
deleted file mode 100644
index d96a39a6..00000000
--- a/Calculations/Basics/MIDPOINT_Series.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-namespace QuanTAlib;
-using System;
-
-/*
-MIDPOINT: Midpoint value (max+min)/2 in the given period in the series.
- If period = 0 => period = full length of the series
-
-Sources:
- https://thefaqblog.com/what-is-the-midpoint-in-statistics/
-
- */
-
-public class MIDPOINT_Series : Single_TSeries_Indicator
-{
- public MIDPOINT_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0)
- { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
-
- double _max = TValue.v;
- double _min = TValue.v;
- for (int i = 0; i < this._buffer.Count; i++)
- {
- _max = Math.Max(this._buffer[i], _max);
- _min = Math.Min(this._buffer[i], _min);
- }
- double _mid = (_max + _min) * 0.5;
-
- base.Add((TValue.t, _mid), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Basics/MIN_Series.cs b/Calculations/Basics/MIN_Series.cs
deleted file mode 100644
index e41a0ba4..00000000
--- a/Calculations/Basics/MIN_Series.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-MIN - Minimum value in the given period in the series.
- If period = 0 => period = full length of the series
- */
-
-public class MIN_Series : Single_TSeries_Indicator
-{
- public MIN_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
-
- double _min = _buffer.Min();
- base.Add((TValue.t, _min), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Basics/SUM_Series.cs b/Calculations/Basics/SUM_Series.cs
deleted file mode 100644
index bb98134d..00000000
--- a/Calculations/Basics/SUM_Series.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-namespace QuanTAlib;
-using System;
-
-/*
-SUM: Cumulative Sum (aka Running Total)
- SUM across a period provides a rolling sum of all values across the period.
- If SUM values would be divided with period, the output would be SMA()
-
-Sources:
- https://en.wikipedia.org/wiki/CUSUM
-
- */
-
-public class SUM_Series : Single_TSeries_Indicator
-{
- public SUM_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- if (update) { _buffer[_buffer.Count - 1] = TValue.v; }
- else { _buffer.Add(TValue.v); }
- if (_buffer.Count > this._p && this._p != 0) { _buffer.RemoveAt(0); }
-
- double _sum = 0;
- for (int i = 0; i < _buffer.Count; i++) { _sum += _buffer[i]; }
-
- var result = (TValue.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _sum);
-
- base.Add(result, update);
- }
-}
diff --git a/Calculations/Basics/ZL_Series.cs b/Calculations/Basics/ZL_Series.cs
deleted file mode 100644
index 0a85f65b..00000000
--- a/Calculations/Basics/ZL_Series.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-namespace QuanTAlib;
-using System;
-
-/*
-ZL: Zero Lag
- Data is de-lagged by removing the data from “lag” days ago, thus removing
- (or attempting to) the cumulative effect of the moving average.
-
-Calculation:
- Lag = (Period-1)/2
- ZL = Data + (Data - Data(Lag days ago) )
-
-Sources:
- https://mudrex.com/blog/zero-lag-ema-trading-strategy/
-
- */
-
-public class ZL_Series : Single_TSeries_Indicator
-{
- public ZL_Series(TSeries source, int period, bool useNaN = false) : base(source, period:period, useNaN:useNaN) {
- if (this._data.Count > 0) { base.Add(this._data); }
- }
-
- public override void Add((DateTime t, double v) TValue, bool update)
- {
- int _lag = (int)((_p-1) * 0.5);
- _lag = (this.Count-_lag < 0) ? 0 : this.Count-_lag;
-
- double _zl = TValue.v + (TValue.v - _data[_lag].v);
-
- var ret = (TValue.t, (base.Count==0 && base._NaN) ? double.NaN : _zl );
- base.Add(ret, update);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Calculations.csproj b/Calculations/Calculations.csproj
index 4140e36e..466a0ced 100644
--- a/Calculations/Calculations.csproj
+++ b/Calculations/Calculations.csproj
@@ -1,74 +1,82 @@
-
-
- QuanTAlib
- 0.2.0
- Library of TA Calculations, Charts and Strategies for Quantower
- Quantitative Technical Analysis Library in C# for Quantower
- git
- https://github.com/mihakralj/QuanTAlib
- true
- Miha Kralj
- Miha Kralj
- readme.md
- net8.0;net7.0;net6.0
- disable
- preview
- disable
- true
- en-US
- QuanTAlib
- QuanTAlib
- True
- AnyCPU
- False
- embedded
- True
- True
-
+
+
+
+ QuanTAlib
+ 0.2.0
+ Library of TA Calculations, Charts and Strategies for Quantower
+ Quantitative Technical Analysis Library in C# for Quantower
+ git
+ https://github.com/mihakralj/QuanTAlib
+ true
+ Miha Kralj
+ Miha Kralj
+ readme.md
+ net8.0;net7.0;net6.0
+ disable
+ preview
+ disable
+ true
+ en-US
+ QuanTAlib
+ QuanTAlib
+ True
+ AnyCPU
+ False
+ embedded
+ True
+ True
+
Indicators;Stock;Market;Technical;Analysis;Algorithmic;Trading;Trade;Trend;Momentum;Finance;Algorithm;Algo;
AlgoTrading;Financial;Strategy;Chart;Charting;Oscillator;Overlay;Equity;Bitcoin;Crypto;Cryptocurrency;Forex;
Quantitative;Historical;Quotes;
-
- Apache-2.0
-
- 0.2.1.0
- 0.2.1.0
- 0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d
- NETSDK1057
-
-
- full
- True
- 7
- True
- anycpu
-
-
-
- True
- 7
- True
- anycpu
-
-
- QuanTAlib2.png
- https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png
- True
- ..\.sonarlint\mihakralj_quantalibcsharp.ruleset
- 0.2.1-dev.2
-
-
-
-
-
-
- True
-
-
-
- True
- False
-
-
-
+
+ Apache-2.0
+
+
+ 0.2.1.0
+ 0.2.1.0
+ 0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d
+ NETSDK1057
+ IDE1006
+ true
+ $(NoWarn);NETSDK1057
+
+
+ full
+ True
+ 7
+ True
+ anycpu
+
+
+
+
+ True
+ 7
+ True
+ anycpu
+
+
+ QuanTAlib2.png
+ https://raw.githubusercontent.com/mihakralj/QuanTAlib/main/.github/QuanTAlib2.png
+ True
+ ..\.sonarlint\mihakralj_quantalibcsharp.ruleset
+ 0.2.1-dev.2
+
+
+
+
+
+
+ True
+
+
+
+
+ True
+ False
+
+
+
+
\ No newline at end of file
diff --git a/Calculations/ClassStructures/Single_TBars_Abstract.cs b/Calculations/ClassStructures/Single_TBars_Abstract.cs
index 4d5e3170..1d3c8ac8 100644
--- a/Calculations/ClassStructures/Single_TBars_Abstract.cs
+++ b/Calculations/ClassStructures/Single_TBars_Abstract.cs
@@ -44,7 +44,7 @@ public abstract class Single_TBars_Indicator : TSeries
// potentially overridable Add() method for the whole bars or series (could be replaced with faster bulk algo)
public virtual void Add(TBars bars) { for (int i = 0; i < bars.Count; i++) { this.Add(TBar: bars[i], update: false); } }
- public virtual void Add(TSeries data) { for (int i = 0; i < data.Count; i++) { base.Add(TValue: data[i], update: false); } }
+ public virtual new void Add(TSeries data) { for (int i = 0; i < data.Count; i++) { base.Add(TValue: data[i], update: false); } }
public void Add((System.DateTime t, double o, double h, double l, double c, double v) TBar) => this.Add(TBar: TBar, update: false);
public void Add(bool update) => this.Add(TBar: this._bars[this._bars.Count - 1], update: update);
public void Add() => this.Add(TBar: this._bars[this._bars.Count - 1], update: false);
diff --git a/Calculations/ClassStructures/Single_TSeries_Abstract.cs b/Calculations/ClassStructures/Single_TSeries_Abstract.cs
index d52ad80d..ca8d3d89 100644
--- a/Calculations/ClassStructures/Single_TSeries_Abstract.cs
+++ b/Calculations/ClassStructures/Single_TSeries_Abstract.cs
@@ -23,29 +23,32 @@ public abstract class Single_TSeries_Indicator : TSeries
protected readonly TSeries _data;
protected int _p;
- // Chainable Constructor - add it at the end of primary constructor :base(source: source, period: period, useNaN: useNaN)
- protected Single_TSeries_Indicator(TSeries source, int period, bool useNaN) {
- _data = source;
- _period = period;
- _p = _period;
- _NaN = useNaN;
- _data.Pub += Sub;
- }
+ // Chainable Constructor - add it at the end of primary constructor :base(source: source, period: period, useNaN: useNaN)
+ protected Single_TSeries_Indicator(TSeries source, int period, bool useNaN)
+ {
+ _data = source;
+ _period = period;
+ _p = _period;
+ _NaN = useNaN;
+ _data.Pub += Sub;
+ }
- // overridable Add() method to add/update a single item at the end of the list
+ // overridable Add() method to add/update a single item at the end of the list
- public virtual void Add((DateTime t, double v) TValue, bool update, bool useNaN) {
- if (_period == 0) { _p = Length; }
- var res = (TValue.t, Count < _p - 1 && _NaN ? double.NaN : TValue.v);
- base.Add(res, update);
- }
- public new virtual void Add((DateTime t, double v) TValue, bool update) => base.Add(TValue, update);
+ public virtual void Add((DateTime t, double v) TValue, bool update, bool useNaN)
+ {
+ if (_period == 0) { _p = Length; }
+ var res = (TValue.t, Count < _p - 1 && _NaN ? double.NaN : TValue.v);
+ base.Add(res, update);
+ }
+ public new virtual void Add((DateTime t, double v) TValue, bool update) => base.Add(TValue, update);
- // potentially overridable Add() method for the whole series (could be replaced with faster bulk algo)
- public virtual void Add(TSeries data) {
- foreach (var item in data) { Add(TValue: item, update: false); }
- }
- public new void Add((System.DateTime t, double v) TValue) => this.Add(TValue: TValue, update: false);
+ // potentially overridable Add() method for the whole series (could be replaced with faster bulk algo)
+ public virtual new void Add(TSeries data)
+ {
+ foreach (var item in data) { Add(TValue: item, update: false); }
+ }
+ public new void Add((System.DateTime t, double v) TValue) => this.Add(TValue: TValue, update: false);
public void Add(bool update) => this.Add(TValue: this._data[this._data.Count - 1], update: update);
public void Add() => this.Add(TValue: this._data[this._data.Count - 1], update: false);
public new void Sub(object source, TSeriesEventArgs e) => this.Add(TValue: this._data[this._data.Count - 1], update: e.update);
diff --git a/Calculations/ClassStructures/TBars.cs b/Calculations/ClassStructures/TBars.cs
deleted file mode 100644
index 562cc968..00000000
--- a/Calculations/ClassStructures/TBars.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-namespace QuanTAlib;
-using System;
-
-/*
-TBars class - includes all series for common data used in indicators and other calculations.
- Has a bit limited overloading and casting (compared to TSeries)
- Includes Select(int) method to simplify choosing the most optimal data source for indicators
- Includes the most basic pricing calcs: HL2, OC2, OHL3, HLC3, OHLC4, HLCC4
- (it is 'cheaper' to calculate them once during data capture than each time during data analysis)
-
- */
-
-public class TBars : System.Collections.Generic.List<(DateTime t, double o, double h, double l, double c, double v)>
-{
- private readonly TSeries _open = new();
- private readonly TSeries _high = new();
- private readonly TSeries _low = new();
- private readonly TSeries _close = new();
- private readonly TSeries _volume = new();
- private readonly TSeries _hl2 = new();
- private readonly TSeries _oc2 = new();
- private readonly TSeries _ohl3 = new();
- private readonly TSeries _hlc3 = new();
- private readonly TSeries _ohlc4 = new();
- private readonly TSeries _hlcc4 = new();
-
- public TSeries Open => this._open;
- public TSeries High => this._high;
- public TSeries Low => this._low;
- public TSeries Close => this._close;
- public TSeries Volume => this._volume;
- public TSeries HL2 => this._hl2;
- public TSeries OC2 => this._oc2;
- public TSeries OHL3 => this._ohl3;
- public TSeries HLC3 => this._hlc3;
- public TSeries OHLC4 => this._ohlc4;
- public TSeries HLCC4 => this._hlcc4;
-
- public TBars Tail(int count = 10)
- {
- TBars outBars = new();
- if (count > this.Count) { count = this.Count; }
- for (int i = this.Count - count; i < this.Count; i++) { outBars.Add(this[i]); }
- return outBars;
- }
- public TSeries Select(int source)
- {
- return source switch
- {
- 0 => _open,
- 1 => _high,
- 2 => _low,
- 3 => _close,
- 4 => _hl2,
- 5 => _oc2,
- 6 => _ohl3,
- 7 => _hlc3,
- 8 => _ohlc4,
- _ => _hlcc4,
- };
- }
- public static string SelectStr(int source)
- {
- return source switch
- {
- 0 => "Open",
- 1 => "High",
- 2 => "Low",
- 3 => "Close",
- 4 => "HL2",
- 5 => "OC2",
- 6 => "OHL3",
- 7 => "HLC3",
- 8 => "OHLC4",
- _ => "HLCC4",
- };
- }
-
- public void Add((DateTime t, double o, double h, double l, double c, double v) i, bool update = false)
- => Add(i.t, i.o, i.h, i.l, i.c, i.v, update);
-
- public void Add(DateTime t, decimal o, decimal h, decimal l, decimal c, decimal v, bool update = false)
- => Add(t, (double)o, (double)h, (double)l, (double)c, (double)v, update);
-
- public void Add(DateTime t, double o, double h, double l, double c, double v, bool update = false)
- {
- if (update) {
- this[this.Count - 1] = (t, o, h, l, c, v);
- }
- else {
- base.Add((t, o, h, l, c, v));
- }
- _open.Add((t, o),update);
- _high.Add((t, h), update);
- _low.Add((t, l), update);
- _close.Add((t, c), update);
- _volume.Add((t, v), update);
- _hl2.Add((t, (h + l) * 0.5), update);
- _oc2.Add((t, (o + c) * 0.5), update);
- _ohl3.Add((t, (o + h + l) * 0.333333333333333), update);
- _hlc3.Add((t, (h + l + c) * 0.333333333333333), update);
- _ohlc4.Add((t, (o + h + l + c) * 0.25), update);
- _hlcc4.Add((t, (h + l + c + c) * 0.25), update);
-
- this.OnEvent(update);
- }
-
- // delegate used by event handler + event handler (Pub == publisher)
- public delegate void NewDataEventHandler(object source, TSeriesEventArgs args);
- public event NewDataEventHandler Pub;
-
- // Broadcast handler - only to valid targets
- protected virtual void OnEvent(bool update = false)
- {
- if (Pub != null && Pub.Target != this)
- {
- Pub(this, new TSeriesEventArgs { update = update });
- }
- }
-
- public void Sub(object source, TSeriesEventArgs e)
- {
- TBars ss = (TBars)source;
- if (ss.Count > 1)
- {
- for (int i = 0; i < ss.Count; i++)
- {
- this.Add(ss[i]);
- }
- }
- else
- {
- this.Add(ss[ss.Count - 1], e.update);
- }
- }
-}
diff --git a/Calculations/ClassStructures/TSeries.cs b/Calculations/ClassStructures/TSeries.cs
deleted file mode 100644
index eafec24d..00000000
--- a/Calculations/ClassStructures/TSeries.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Data;
-using System.Linq;
-
-/*
-TSeries is the cornerstone of all QuanTAlib classes.
- TSeries is a single List of tuples (time, value) and contains several operators, casts, overloads
- and other helpers that simplify usage of library.
- Think of TSeries as an equivalent of Numpy array.
-
- - includes Length property (to mimic array's method)
- - includes publishing and subscribing methods that attach to events
-
- */
-
-
-public class TSeriesEventArgs : EventArgs{
- public bool update { get; set; }
-}
-
-public class TSeries : List<(DateTime t, double v)> {
-
- public static implicit operator (DateTime t, double v)(TSeries l) => l[^1];
- public static implicit operator double(TSeries l) => l[^1].v;
- public static implicit operator DateTime(TSeries l) => l[^1].t;
- public List t => this.Select(item => item.t).ToList();
- public List v => this.Select(item => item.v).ToList();
- public int Length => this.Count;
-
- public TSeries Tail(int count = 10) {
- var tailSeries = new TSeries();
- tailSeries.AddRange(this.Skip(Math.Max(0, this.Count - count)).Take(count));
- return tailSeries;
- }
- public (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
- if (update) { this[^1] = TValue; }
- else { base.Add(TValue); }
- OnEvent(update);
- return TValue;
- }
-
- public void Add(DateTime t, double v, bool update = false) => this.Add((t, v), update);
- public void Add(double v, bool update = false) => this.Add((DateTime.Now, v), update);
- protected virtual void OnEvent(bool update = false) {
- Pub?.Invoke(this, new TSeriesEventArgs { update = update });
- }
-
- public delegate void NewDataEventHandler(object source, TSeriesEventArgs args);
- public event NewDataEventHandler Pub;
-
- public void Sub(object source, TSeriesEventArgs e) {
- TSeries ss = (TSeries)source;
- if (ss.Count > 0) {
- this.AddRange(ss);
- }
- else {
- this.Add(ss[^1], e.update);
- }
- }
-}
diff --git a/Calculations/Logic/EQUITY_Series.cs b/Calculations/Logic/EQUITY_Series.cs
index 1257352d..50313152 100644
--- a/Calculations/Logic/EQUITY_Series.cs
+++ b/Calculations/Logic/EQUITY_Series.cs
@@ -81,7 +81,7 @@ public class EQUITY_Series : Single_TSeries_Indicator {
//Console.WriteLine($"{TValue.v,3}\t {(_inmarket)} : {_cash,10:f2} + {_units*_price[this.Count-1].v,7:f2} = {_equity-_capital:f2}");
}
- inmarket.Add(TValue.t, (double)_inmarket);
+ inmarket.Add((TValue.t, (double)_inmarket));
base.Add((TValue.t, _equity), update, _NaN);
}
}
\ No newline at end of file
diff --git a/Calculations/Statistics/BIAS_Series.cs b/Calculations/Statistics/BIAS_Series.cs
deleted file mode 100644
index 7440a5f3..00000000
--- a/Calculations/Statistics/BIAS_Series.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-BIAS: Rate of change between the source and a moving average.
- Bias is a statistical term which means a systematic deviation from the actual value.
-
-BIAS = (close - SMA) / SMA
- = (close / SMA) - 1
-
-Sources:
- https://en.wikipedia.org/wiki/Bias_of_an_estimator
-
- */
-
-public class BIAS_Series : Single_TSeries_Indicator
-{
- public BIAS_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
-
- double _sma = _buffer.Average();
- double _bias = (_buffer[_buffer.Count - 1] / ((_sma != 0) ? _sma : 1)) - 1;
-
- base.Add((TValue.t, _bias), update, _NaN);
- }
-}
diff --git a/Calculations/Statistics/DECAY_Series.cs b/Calculations/Statistics/DECAY_Series.cs
deleted file mode 100644
index 2084c95a..00000000
--- a/Calculations/Statistics/DECAY_Series.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Collections.Generic;
-
-
-/*
-DECAY:
- Linear decay can be modeled by a straight line with a negative slope of 1/period.
- The value decreases in a straight line from the last maximum to 0.
- Decay = Last Max - distance/period
-
- Exponential decay is modeled as an exponential curve with diminishing factor of
- 1-1/p
-
- */
-
-public class DECAY_Series : Single_TSeries_Indicator {
- private readonly bool _exp;
- private double _pdecay, _ppdecay;
- private readonly double _dfactor;
-
- public DECAY_Series(TSeries source, int period = 10, bool exponential= false, bool useNaN = false) : base(source, period, false) {
- _exp = exponential;
- _dfactor = (_exp)? 1.0 - 1.0 / (double)_p : 1/(double)_p;
- _pdecay = _ppdecay = 0;
- if (source.Count > 0) { base.Add(this._data); }
- }
-
- public override void Add((DateTime t, double v) TValue, bool update) {
- if (update) { _pdecay = _ppdecay; }
- else { _ppdecay = _pdecay; }
-
- if (this.Count == 0) { _pdecay = TValue.v; }
- double _decay = Math.Max(TValue.v, Math.Max((_exp)?_pdecay*_dfactor:_pdecay-_dfactor, 0));
- _pdecay = _decay;
-
- base.Add((TValue.t, _decay), update, _NaN);
- }
-}
diff --git a/Calculations/Statistics/ENTROPY_Series.cs b/Calculations/Statistics/ENTROPY_Series.cs
deleted file mode 100644
index f27ea6c7..00000000
--- a/Calculations/Statistics/ENTROPY_Series.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-ENTP: Entropy
- Introduced by Claude Shannon in 1948, entropy measures the unpredictability
- of the data, or equivalently, of its average information.
-
-Calculation:
- P = close / Σ(close)
- ENTP = Σ(-P * Log(P) / Log(base))
-
-Sources:
- https://en.wikipedia.org/wiki/Entropy_(information_theory)
- https://math.stackexchange.com/questions/3428693/how-to-calculate-entropy-from-a-set-of-correlated-samples
-
- */
-
-public class ENTROPY_Series : Single_TSeries_Indicator
-{
- public ENTROPY_Series(TSeries source, int period, double logbase = 2.0, bool useNaN = false) : base(source, period, useNaN)
- {
- this._logbase = logbase;
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly double _logbase;
- private readonly System.Collections.Generic.List _buffer = new();
- private readonly System.Collections.Generic.List _buff2 = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- double _sum = _buffer.Sum();
-
- double _pp = this._buffer[this._buffer.Count - 1] / _sum;
- double _ppp = -_pp * Math.Log(_pp) / Math.Log(this._logbase);
-
- Add_Replace_Trim(_buff2, _ppp, _p, update);
- double _entp = _buff2.Sum();
-
- base.Add((TValue.t, _entp), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Statistics/KURTOSIS_Series.cs b/Calculations/Statistics/KURTOSIS_Series.cs
deleted file mode 100644
index ae1c93b2..00000000
--- a/Calculations/Statistics/KURTOSIS_Series.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-KURT: Kurtosis of population
- Kurtosis characterizes the relative peakedness or flatness of a distribution
- compared with the normal distribution. Positive kurtosis indicates a relatively
- peaked distribution. Negative kurtosis indicates a relatively flat distribution.
-
- The normal curve is called Mesokurtic curve. If the curve of a distribution is
- more outlier prone (or heavier-tailed) than a normal or mesokurtic curve then
- it is referred to as a Leptokurtic curve. If a curve is less outlier prone (or
- lighter-tailed) than a normal curve, it is called as a platykurtic curve.
-
-Calculation:
- sum4 = Σ(close-SMA)^4
- sum2 = (Σ(close-SMA)^2)^2
- KURT = length * (sum4/sum2)
-
-Sources:
- https://en.wikipedia.org/wiki/Kurtosis
- https://stats.oarc.ucla.edu/other/mult-pkg/faq/general/faq-whats-with-the-different-formulas-for-kurtosis/
-
- */
-
-public class KURTOSIS_Series : Single_TSeries_Indicator
-{
- public KURTOSIS_Series(TSeries source, int period, double logbase = 2.0, bool useNaN = false) : base(source, period, useNaN)
- {
- this._logbase = logbase;
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- protected double _logbase;
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- double _n = this._buffer.Count;
- double _avg = _buffer.Average();
-
- double _s2 = 0;
- double _s4 = 0;
- for (int i = 0; i < this._buffer.Count; i++)
- {
- _s2 += (_buffer[i] - _avg) * (_buffer[i] - _avg);
- _s4 += (_buffer[i] - _avg) * (_buffer[i] - _avg) * (_buffer[i] - _avg) * (_buffer[i] - _avg);
- }
-
- double _Vx = _s2 / (_n - 1);
- double _kurt = (_n > 3) ? ((((_n * (_n + 1)) / (((_n - 1) * (_n - 2)) * (_n - 3))) * (_s4 / (_Vx * _Vx))) - (3 * (((_n - 1) * (_n - 1)) / ((_n - 2) * (_n - 3))))) : Double.NaN;
-
- var result = (TValue.t, (this.Count < this._p - 1 && this._NaN) ? Double.NaN : _kurt);
- base.Add(result, update);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Statistics/MAD_Series.cs b/Calculations/Statistics/MAD_Series.cs
deleted file mode 100644
index 8a032e4a..00000000
--- a/Calculations/Statistics/MAD_Series.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-MAD: Mean Absolute Deviation
- Also known as AAD - Average Absolute Deviation, to differentiate it from Median Absolute Deviation
- MAD defines the degree of variation across the series.
-
-Calculation:
- MAD = Σ(|close-SMA|) / period
-
-Sources:
- https://en.wikipedia.org/wiki/Average_absolute_deviation
-
- */
-
-public class MAD_Series : Single_TSeries_Indicator
-{
- public MAD_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
-
- double _sma = _buffer.Average();
-
- double _mad = 0;
- for (int i = 0; i < _buffer.Count; i++) { _mad += Math.Abs(_buffer[i] - _sma); }
- _mad /= this._buffer.Count;
-
- base.Add((TValue.t, _mad), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Statistics/MAPE_Series.cs b/Calculations/Statistics/MAPE_Series.cs
deleted file mode 100644
index ca737cf1..00000000
--- a/Calculations/Statistics/MAPE_Series.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-MAPE: Mean Absolute Percentage Error
- Measures the size of the error in percentage terms
-
-Calculation:
- MAPE = Σ(|close – SMA| / |close|) / n
-
-Sources:
- https://en.wikipedia.org/wiki/Mean_absolute_percentage_error
-
-Remark:
- returns infinity if any of observations is 0.
- Use SMAPE or WMAPE instead to avoid division-by-zero in MAPE
-
- */
-
-public class MAPE_Series : Single_TSeries_Indicator
-{
- public MAPE_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- double _sma = _buffer.Average();
-
- double _mape = 0;
- for (int i = 0; i < _buffer.Count; i++) {
- _mape += (_buffer[i] != 0) ? Math.Abs(_buffer[i] - _sma) / Math.Abs(_buffer[i]) : double.PositiveInfinity;
- }
- _mape /= (_buffer.Count>0) ? _buffer.Count : 1;
-
- base.Add((TValue.t, _mape), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Statistics/MEDIAN_Series.cs b/Calculations/Statistics/MEDIAN_Series.cs
deleted file mode 100644
index 0bbd6948..00000000
--- a/Calculations/Statistics/MEDIAN_Series.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-namespace QuanTAlib;
-using System;
-using static System.Net.Mime.MediaTypeNames;
-
-/*
-MED - Median value
- Median of numbers is the middlemost value of the given set of numbers.
- It separates the higher half and the lower half of a given data sample.
- At least half of the observations are smaller than or equal to median
- and at least half of the observations are greater than or equal to the median.
-
- If the number of values is odd, the middlemost observation of the sorted
- list is the median of the given data. If the number of values is even,
- median is the average of (n/2)th and [(n/2) + 1]th values of the sorted list.
-
- If period = 0 => period is max
-
-Sources:
- https://corporatefinanceinstitute.com/resources/knowledge/other/median/
- https://en.wikipedia.org/wiki/Median
-
- */
-
-public class MEDIAN_Series : Single_TSeries_Indicator
-{
- public MEDIAN_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
-
- System.Collections.Generic.List _s = new(this._buffer);
- _s.Sort();
- int _p1 = _s.Count / 2;
- int _p2 = Math.Max(0, (_s.Count / 2) - 1);
- double _med = (_s.Count % 2 != 0) ? _s[_p1] : (_s[_p1] + _s[_p2]) / 2;
-
- base.Add((TValue.t, _med), update, _NaN);
- }
-}
diff --git a/Calculations/Statistics/MSE_Series.cs b/Calculations/Statistics/MSE_Series.cs
deleted file mode 100644
index 1dcdd6a9..00000000
--- a/Calculations/Statistics/MSE_Series.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-MSE: Mean Square Error
- Defined as a Mean (Average) of the Square of the difference between actual and estimated values.
-
-Sources:
- https://en.wikipedia.org/wiki/Mean_squared_error
-
- */
-
-public class MSE_Series : Single_TSeries_Indicator
-{
- public MSE_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- double _sma = _buffer.Average();
-
- double _mse = 0;
- for (int i = 0; i < _buffer.Count; i++) { _mse += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
- _mse /= this._buffer.Count;
-
- base.Add((TValue.t, _mse), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Statistics/SDEV_Series.cs b/Calculations/Statistics/SDEV_Series.cs
deleted file mode 100644
index b85c3a16..00000000
--- a/Calculations/Statistics/SDEV_Series.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-SDEV: Population Standard Deviation
- Population Standard Deviation is the square root of the biased variance, also knons as
- Uncorrected Sample Standard Deviation
-
-Sources:
- https://en.wikipedia.org/wiki/Standard_deviation#Uncorrected_sample_standard_deviation
-
-Remark:
- SDEV (Population Standard Deviation) is also known as a biased/uncorrected Standard Deviation.
- For unbiased version that uses Bessel's correction, use SDEV instead.
-
- */
-
-public class SDEV_Series : Single_TSeries_Indicator
-{
- public SDEV_Series(TSeries source, int period=0, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- double _sma = _buffer.Average();
-
- double _pvar = 0;
- for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
- _pvar /= this._buffer.Count;
- double _psdev = Math.Sqrt(_pvar);
-
- base.Add((TValue.t, _psdev), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Statistics/SMAPE_Series.cs b/Calculations/Statistics/SMAPE_Series.cs
deleted file mode 100644
index dc0eaaa5..00000000
--- a/Calculations/Statistics/SMAPE_Series.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-SMAPE: Symmetric Mean Absolute Percentage Error
- Measures the size of the error in percentage terms
-
-Sources:
- https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error
-
- */
-
-public class SMAPE_Series : Single_TSeries_Indicator
-{
- public SMAPE_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- double _sma = _buffer.Average();
-
- double _smape = 0;
- for (int i = 0; i < _buffer.Count; i++) { _smape += Math.Abs(_buffer[i] - _sma) / (Math.Abs(_buffer[i]) + Math.Abs(_sma)); }
- _smape /= this._buffer.Count;
-
- base.Add((TValue.t, _smape), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Statistics/SSDEV_Series.cs b/Calculations/Statistics/SSDEV_Series.cs
deleted file mode 100644
index ab882476..00000000
--- a/Calculations/Statistics/SSDEV_Series.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-SSDEV: (Corrected) Sample Standard Deviation
- Sample Standard Deviaton uses Bessel's correction to correct the bias in the variance.
-
-Sources:
- https://en.wikipedia.org/wiki/Standard_deviation#Corrected_sample_standard_deviation
- Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction
-
-Remark:
- SSDEV (Sample Standard Deviation) is also known as a unbiased/corrected Standard Deviation.
- For a population/biased/uncorrected Standard Deviation, use PSDEV instead
-
- */
-
-public class SSDEV_Series : Single_TSeries_Indicator
-{
- public SSDEV_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- double _sma = _buffer.Average();
-
- double _svar = 0;
- for (int i = 0; i < this._buffer.Count; i++) { _svar += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
- _svar /= (_buffer.Count > 1) ? _buffer.Count - 1 : 1; // Bessel's correction
- double _ssdev = Math.Sqrt(_svar);
-
- base.Add((TValue.t, _ssdev), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Statistics/SVAR_Series.cs b/Calculations/Statistics/SVAR_Series.cs
deleted file mode 100644
index a83d5deb..00000000
--- a/Calculations/Statistics/SVAR_Series.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-SVAR: Sample Variance
- Sample variance uses Bessel's correction to correct the bias in the estimation of population variance.
-
-Sources:
- https://en.wikipedia.org/wiki/Variance
- Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction
-
-Remark:
- SVAR is also known as the Unbiased Sample Variance, while VAR (Population Variance) is known as
- the Biased Sample Variance.
-
- */
-
-public class SVAR_Series : Single_TSeries_Indicator
-{
- public SVAR_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- double _sma = _buffer.Average();
-
- double _svar = 0;
- for (int i = 0; i < this._buffer.Count; i++) { _svar += (this._buffer[i] - _sma) * (this._buffer[i] - _sma); }
- _svar /= (this._buffer.Count > 1) ? this._buffer.Count - 1 : 1; // Bessel's correction
-
- base.Add((TValue.t, _svar), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Statistics/VAR_Series.cs b/Calculations/Statistics/VAR_Series.cs
deleted file mode 100644
index 447ae16f..00000000
--- a/Calculations/Statistics/VAR_Series.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-VAR: Population Variance
- Population variance without Bessel's correction
-
-Sources:
- https://en.wikipedia.org/wiki/Variance
- Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction
-
-Remark:
- VAR (Population Variance) is also known as a biased Sample Variance. For unbiased
- sample variance use SVAR instead.
-
- */
-
-public class VAR_Series : Single_TSeries_Indicator
-{
- public VAR_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- double _sma = _buffer.Average();
-
- double _pvar = 0;
- for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
- _pvar /= this._buffer.Count;
-
- base.Add((TValue.t, _pvar), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Statistics/WMAPE_Series.cs b/Calculations/Statistics/WMAPE_Series.cs
deleted file mode 100644
index ff2a112a..00000000
--- a/Calculations/Statistics/WMAPE_Series.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-WMAPE: Weighted Mean Absolute Percentage Error
- Measures the size of the error in percentage terms. Improves problems with MAPE
- when there are zero or close-to-zero values because there would be a division by zero
- or values of MAPE tending to infinity.
-
-Sources:
- https://en.wikipedia.org/wiki/WMAPE
-
- */
-
-public class WMAPE_Series : Single_TSeries_Indicator
-{
- public WMAPE_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- double _sma = _buffer.Average();
-
- double _div = 0;
- double _wmape = 0;
- for (int i = 0; i < _buffer.Count; i++)
- {
- _wmape += Math.Abs(_buffer[i] - _sma);
- _div += Math.Abs(_buffer[i]);
- }
- _wmape = (_div!=0) ? _wmape/_div : double.PositiveInfinity;
-
- base.Add((TValue.t, _wmape), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Statistics/ZSCORE_Series.cs b/Calculations/Statistics/ZSCORE_Series.cs
deleted file mode 100644
index fbaf02b9..00000000
--- a/Calculations/Statistics/ZSCORE_Series.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-ZSCORE: number of standard deviations from SMA
- Z-score describes a value's relationship to the mean of a series, as measured in
- terms of standard deviations from the mean. If a Z-score is 0, it indicates that
- the data point's score is identical to the mean score. A Z-score of 1.0 would
- indicate a value that is one standard deviation from the mean. Z-scores may be
- positive or negative, with a positive value indicating the score is above the
- mean and a negative score indicating it is below the mean.
-
-Sources:
- https://en.wikipedia.org/wiki/Z-score
- https://www.investopedia.com/terms/z/zscore.asp
-
-Calculation:
- std = std * STDEV(close, length)
- mean = SMA(close, length)
- ZSCORE = (close - mean) / std
-
- */
-
-public class ZSCORE_Series : Single_TSeries_Indicator
-{
- public ZSCORE_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- double _sma = _buffer.Average();
-
- double _pvar = 0;
- for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
- _pvar /= this._buffer.Count;
- double _psdev = Math.Sqrt(_pvar);
- double _zscore = (_psdev == 0) ? double.NaN : (TValue.v - _sma) / _psdev;
-
- base.Add((TValue.t, _zscore), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Trends/ALMA_Series.cs b/Calculations/Trends/ALMA_Series.cs
deleted file mode 100644
index 30d755f5..00000000
--- a/Calculations/Trends/ALMA_Series.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-namespace QuanTAlib;
-using System;
-
-/*
-ALMA: Arnaud Legoux Moving Average
- The ALMA moving average uses the curve of the Normal (Gauss) distribution, which
- can be shifted from 0 to 1. This allows regulating the smoothness and high
- sensitivity of the indicator. Sigma is another parameter that is responsible for
- the shape of the curve coefficients. This moving average reduces lag of the data
- in conjunction with smoothing to reduce noise.
-
-
-Sources:
- https://phemex.com/academy/what-is-arnaud-legoux-moving-averages
- https://www.prorealcode.com/prorealtime-indicators/alma-arnaud-legoux-moving-average/
-
- Discrepancy with Pandas-TA (but passes the validation with Skender.GetAlma)
-
- */
-
-public class ALMA_Series : Single_TSeries_Indicator
-{
- private readonly System.Collections.Generic.List _buffer = new();
- private readonly double[] _weight;
- private double _norm;
- private readonly double _offset, _sigma;
-
- public ALMA_Series(TSeries source, int period, double offset = 0.85, double sigma = 6.0, bool useNaN = false)
- : base(source, period, useNaN)
- {
- _offset = offset;
- _sigma = sigma;
- _weight = new double[period];
-
- if (this._data.Count > 0) { base.Add(this._data); }
- }
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
-
- if (this._buffer.Count <= _p)
- {
- int _len = this._buffer.Count;
- _norm = 0;
- double _m = _offset * (_len - 1);
- double _s = _len / _sigma;
- for (int i = 0; i < _len; i++)
- {
- double _wt = Math.Exp(-((i - _m) * (i - _m)) / (2 * _s * _s));
- _weight[i] = _wt;
- _norm += _wt;
- }
- }
-
- double _weightedSum = 0;
- for (int i = 0; i < this._buffer.Count; i++)
- { _weightedSum += _weight[i] * _buffer[i]; }
- double _alma = _weightedSum / _norm;
-
- base.Add((TValue.t, _alma), update, _NaN);
- }
-}
diff --git a/Calculations/Momentum/CCI_Series.cs b/Calculations/Trends/CCI_Series.cs
similarity index 100%
rename from Calculations/Momentum/CCI_Series.cs
rename to Calculations/Trends/CCI_Series.cs
diff --git a/Calculations/Trends/DEMA_Series.cs b/Calculations/Trends/DEMA_Series.cs
deleted file mode 100644
index 6dea06e5..00000000
--- a/Calculations/Trends/DEMA_Series.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-using System.Runtime.CompilerServices;
-
-/*
-DEMA: Double Exponential Moving Average
- DEMA uses EMA(EMA()) to calculate smoother Exponential moving average.
-
-Sources:
- https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/double-exponential-moving-average-dema/
-
-Remark:
- ema1 = EMA(close, length)
- ema2 = EMA(ema1, length)
- DEMA = 2 * ema1 - ema2
-
- */
-
-public class DEMA_Series : Single_TSeries_Indicator
-{
- private readonly double _k;
- private int _len;
- private readonly bool _useSMA;
- private double _sum, _lastsum, _lastlastsum;
- private double _lastema1, _lastlastema1;
- private double _lastema2, _lastlastema2;
-
- public DEMA_Series(TSeries source, int period, bool useNaN = false, bool useSMA = true) : base(source, period, useNaN)
- {
- _k = 2.0 / (_p + 1);
- _len = 0;
- _useSMA = useSMA;
- _sum = _lastema1 = _lastema2 =0;
- if (_data.Count > 0) { base.Add(_data); }
- }
-
- public override void Add((DateTime t, double v) TValue, bool update)
- {
- if (update) {
- _lastsum = _lastlastsum;
- _lastema1 = _lastlastema1;
- _lastema2 = _lastlastema2;
- }
- else {
- _lastlastsum = _lastsum;
- _lastlastema1 = _lastema1;
- _lastlastema2 = _lastema2;
- _len++;
- }
-
- double _ema1, _ema2, _dema;
- if (this.Count == 0) {
- _ema1 = _ema2 = _sum = TValue.v;
- }
- else if (_len <= _period && _useSMA && _period != 0) {
- _sum += TValue.v;
- _ema1 = _sum / Math.Min(_len, _period);
- _ema2 = _ema1;
- }
- else {
- _ema1 = (TValue.v - _lastema1) * _k + _lastema1;
- _ema2 = (_ema1 - _lastema2) * _k + _lastema2;
- }
- _dema = 2*_ema1 - _ema2;
-
- _lastema1 = Double.IsNaN(_ema1)?_lastema1:_ema1;
- _lastema2 = Double.IsNaN(_ema2)?_lastema2:_ema2;
-
- base.Add((TValue.t, _dema), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Trends/DWMA_Series.cs b/Calculations/Trends/DWMA_Series.cs
deleted file mode 100644
index 08d2ecb9..00000000
--- a/Calculations/Trends/DWMA_Series.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-namespace QuanTAlib;
-using System;
-
-/*
-DWMA: Double Weighted Moving Average
- The weights are decreasing over the period with p^2 decay
- and the most recent data has the heaviest weight.
-
- */
-
-public class DWMA_Series : Single_TSeries_Indicator {
- private readonly System.Collections.Generic.List _buffer1 = new();
- private readonly System.Collections.Generic.List _weights = new();
- public DWMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN) {
- for (int i = 0; i < this._p; i++) {
- double _weight = (i + 1) * (i + 1);
- this._weights.Add(_weight);
- }
- if (base._data.Count > 0) { base.Add(base._data); }
- }
-
- public override void Add((System.DateTime t, double v) TValue, bool update) {
- Add_Replace_Trim(_buffer1, TValue.v, _p, update);
- double _wma1 = 0, _wsum = 0;
- for (int i = 0; i < _buffer1.Count; i++) {
- _wma1 += _buffer1[i] * _weights[i];
- _wsum += _weights[i];
- }
- _wma1 /= _wsum;
-
- base.Add((TValue.t, _wma1), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Trends/EMA_Series.cs b/Calculations/Trends/EMA_Series.cs
deleted file mode 100644
index 6ce1d3a1..00000000
--- a/Calculations/Trends/EMA_Series.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-EMA: Exponential Moving Average
- EMA needs very short history buffer and calculates the EMA value using just the
- previous EMA value. The weight of the new datapoint (k) is k = 2 / (period-1)
-
-Sources:
- https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages
- https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp
- https://blog.fugue88.ws/archives/2017-01/The-correct-way-to-start-an-Exponential-Moving-Average-EMA
-
-Issues:
- There is no consensus what the first EMA value should be - a zero, a first
- datapoint, or an average of the initial Period bars. All three starting methods
- converge within 20+ bars to the same moving average. Most implementations (including this one)
- use SMA() for the first Period bars as a seeding value for EMA.
-
- */
-
-public class EMA_Series : Single_TSeries_Indicator {
- private double _k;
- private double _lastema, _lastlastema;
- private double _sum, _oldsum;
- private int _len;
- private readonly bool _useSMA;
-
- public EMA_Series(TSeries source, int period, bool useNaN = false, bool useSMA = true) : base(source, period, useNaN) {
- _k = 2.0 / (_p + 1);
- _sum = _oldsum = _lastema = _lastlastema = 0;
- _len = 0;
- _useSMA = useSMA;
- if (this._data.Count > 0) { base.Add(this._data); }
- }
-
- public override void Add((DateTime t, double v) TValue, bool update) {
-
- if (update) { _lastema = _lastlastema; _sum = _oldsum; }
- else { _lastlastema = _lastema; _oldsum = _sum; _len++; }
-
- double _ema = 0;
- // when period = 0, create cumulative/additive series where _k is progressively larger
- if (_period == 0) { _k = 2.0 / (_len + 1); }
-
- // the first value of the series
- if (this.Count == 0) {
- _ema = _sum = TValue.v;
- }
- // if SMA is used for seeding, calculate SMA within period
- else if (_len <= _period && _useSMA && _period != 0) {
- _sum += TValue.v;
- if (_period != 0 && _len > _period) {
- _sum -= (_data[base.Count - _period - (update ? 1 : 0)].v);
- }
- _ema = _sum / Math.Min(_len, _period);
- }
- // calculate EMA out from last EMA and factor k
- else {
- _ema = _k * (TValue.v - _lastema) + _lastema;
- }
- _lastema = Double.IsNaN(_ema)?_lastema:_ema;
-
- base.Add((TValue.t, _ema), update, _NaN);
- }
- public void Reset() {
- _sum = _oldsum = _lastema = _lastlastema = 0;
- _len = 0;
- }
-}
\ No newline at end of file
diff --git a/Calculations/Trends/FMA_Series.cs b/Calculations/Trends/FMA_Series.cs
deleted file mode 100644
index c43a6679..00000000
--- a/Calculations/Trends/FMA_Series.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-namespace QuanTAlib;
-using System;
-
-/*
-FMA: Fibonacci Moving Average
- FMA calculates the average across multiple EMAs with periods following Fibonacci sequence
- (skipping initial Fibonacci numbers of 1, 1, 2) 3, 5, 8, 13, 21, 34...
-
- FMA(n) = Average(EMA(3), EMA(5), EMA(8), ema(13), ... EMA(n-th Fib))
-
-Sources:
- https://kaabar-sofien.medium.com/the-fibonacci-moving-average-the-full-guide-60e718117595
- https://usethinkscript.com/threads/fibonacci-moving-average.8099/
-
- */
-
-public class FMA_Series : Single_TSeries_Indicator {
- readonly double[,] fib;
- double _oldsum;
- readonly int _len;
-
- public FMA_Series(TSeries source, int period) : base(source, period, false) {
- _len = period;
- fib = new double[_len, 4];
- int a = 3;
- int b = 5;
- int f = 0;
- fib[0, 0] = 2 / ((double)a - 1);
- if (_len > 1) { fib[1, 0] = 2 / ((double)b - 1); }
- if (_len > 2) {
- for (int i = 2; i < _len; i++) {
- f = a + b;
- a = b;
- b = f;
- fib[i, 0] = 2 / ((double)f - 1);
- }
- }
- _oldsum = 0;
- if (this._data.Count > 0) { base.Add(this._data); }
- }
-
- public override void Add((DateTime t, double v) TValue, bool update) {
- double _sum = 0;
- for (int i = 0; i < _len; i++) {
- if (update) { fib[i, 1] = fib[i, 3]; _sum = _oldsum; }
- else { fib[i, 3] = fib[i, 1]; _oldsum = _sum; }
-
- if (this.Count == 0) { fib[i, 1] = TValue.v; }
- else {
- fib[i, 2] = fib[i, 0] * (TValue.v - fib[i, 1]) + fib[i, 1];
- fib[i, 1] = fib[i, 2];
- }
- _sum += fib[i, 1];
- }
-
- double _fma = _sum / _len;
- base.Add((TValue.t, _fma), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Trends/HEMA_Series.cs b/Calculations/Trends/HEMA_Series.cs
deleted file mode 100644
index dcb9d560..00000000
--- a/Calculations/Trends/HEMA_Series.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-namespace QuanTAlib;
-using System;
-
-/*
-HEMA: Hull-EMA Moving Average - a hybrid indicator
- Modified HUll Moving Average; instead of using WMA (Weighted MA) for calculation,
- HEMA uses EMA for Hull's formula:
-
-EMA1 = EMA(n/2) of price - where k = 4/(n/2 +1)
-EMA2 = EMA(n) of price - where k = 3/(n+1)
-Raw HMA = (2 * EMA1) - EMA2
-EMA3 = EMA(sqrt(n)) of Raw HMA - where k = 2/(sqrt(n)+1)
-
- */
-
-public class HEMA_Series : Single_TSeries_Indicator
-{
- public HEMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- this._k1 = 4 / ((period * 0.5) + 1);
- this._k2 = 3 / (double)(period + 1);
- this._k3 = 2 / (Math.Sqrt(period) + 1);
- this._lastema1 = this._lastlastema1 = double.NaN;
- this._lastema2 = this._lastlastema2 = double.NaN;
- this._lastema3 = this._lastlastema3 = double.NaN;
-
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly double _k1, _k2, _k3;
- private double _lastema1, _lastlastema1;
- private double _lastema2, _lastlastema2;
- private double _lastema3, _lastlastema3;
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- if (update)
- {
- this._lastema1 = this._lastlastema1;
- this._lastema2 = this._lastlastema2;
- this._lastema3 = this._lastlastema3;
- }
- double _ema1 = System.Double.IsNaN(this._lastema1) ? TValue.v : TValue.v * this._k1 + this._lastema1 * (1 - this._k1);
- double _ema2 = System.Double.IsNaN(this._lastema2) ? TValue.v : TValue.v * this._k2 + this._lastema2 * (1 - this._k2);
-
- double _rawhema = (2 * _ema1) - _ema2;
- double _ema3 = System.Double.IsNaN(this._lastema3) ? _rawhema : _rawhema * this._k3 + this._lastema3 * (1 - this._k3);
-
- this._lastlastema1 = this._lastema1;
- this._lastlastema2 = this._lastema2;
- this._lastlastema3 = this._lastema3;
- this._lastema1 = _ema1;
- this._lastema2 = _ema2;
- this._lastema3 = _ema3;
-
- base.Add((TValue.t, _ema3), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Trends/HMA_Series.cs b/Calculations/Trends/HMA_Series.cs
deleted file mode 100644
index 8a522fba..00000000
--- a/Calculations/Trends/HMA_Series.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-namespace QuanTAlib;
-using System;
-
-/*
-HMA: Hull Moving Average
- Developed by Alan Hull, an extremely fast and smooth moving average; almost
- eliminates lag altogether and manages to improve smoothing at the same time.
-
-Sources:
- https://alanhull.com/hull-moving-average
- https://school.stockcharts.com/doku.php?id=technical_indicators:hull_moving_average
-
-WMA1 = WMA(n/2) of price
-WMA2 = WMA(n) of price
-Raw HMA = (2 * WMA1) - WMA2
-HMA = WMA(sqrt(n)) of Raw HMA
-
- */
-
-public class HMA_Series : TSeries
-{
- private readonly int _p;
- private readonly bool _NaN;
- private readonly TSeries _data;
- private double _wma1, _wma2;
- private readonly System.Collections.Generic.List _buf1 = new();
- private readonly System.Collections.Generic.List _buf2 = new();
- private readonly System.Collections.Generic.List _buf3 = new();
- private readonly System.Collections.Generic.List _weights = new();
-
- public HMA_Series(TSeries source, int period, bool useNaN = false)
- {
- this._p = period;
- this._data = source;
- this._NaN = useNaN;
- for (int i = 0; i < this._p; i++)
- {
- this._weights.Add(i + 1);
- }
-
- source.Pub += this.Sub;
- if (source.Count > 0)
- {
- for (int i = 0; i < source.Count; i++)
- {
- this.Add(source[i], false);
- }
- }
- }
- public new void Add((System.DateTime t, double v) data, bool update = false)
- {
- if (update)
- {
- this._buf1[this._buf1.Count - 1] = data.v;
- this._buf2[this._buf2.Count - 1] = data.v;
- }
- else
- {
- this._buf1.Add(data.v);
- this._buf2.Add(data.v);
- }
- if (this._buf1.Count > (int)((double)this._p / 2))
- {
- this._buf1.RemoveAt(0);
- }
- if (this._buf2.Count > this._p)
- {
- this._buf2.RemoveAt(0);
- }
-
- this._wma1 = 0;
- for (int i = 0; i < this._buf1.Count; i++)
- {
- this._wma1 += this._buf1[i] * this._weights[i];
- }
- this._wma1 /= (this._buf1.Count * (this._buf1.Count + 1)) * 0.5;
-
- this._wma2 = 0;
- for (int i = 0; i < this._buf2.Count; i++)
- {
- this._wma2 += this._buf2[i] * this._weights[i];
- }
- this._wma2 /= (this._buf2.Count * (this._buf2.Count + 1)) * 0.5;
-
- if (update)
- {
- this._buf3[this._buf3.Count - 1] = 2 * this._wma1 - this._wma2;
- }
- else
- {
- this._buf3.Add(2 * this._wma1 - this._wma2);
- }
-
- if (this._buf3.Count > (int)Math.Sqrt(this._p))
- {
- this._buf3.RemoveAt(0);
- }
-
- double _hma = 0;
- for (int i = 0; i < this._buf3.Count; i++)
- {
- _hma += this._buf3[i] * this._weights[i];
- }
-
- _hma /= (this._buf3.Count * (this._buf3.Count + 1)) * 0.5;
-
- (System.DateTime t, double v) result =
- (data.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _hma);
- base.Add(result, update);
- }
- public void Add(bool update = false)
- {
- this.Add(this._data[this._data.Count - 1], update);
- }
- public new void Sub(object source, TSeriesEventArgs e)
- {
- this.Add(this._data[this._data.Count - 1], e.update);
- }
-}
diff --git a/Calculations/Trends/KAMA_Series.cs b/Calculations/Trends/KAMA_Series.cs
deleted file mode 100644
index 808ce8c7..00000000
--- a/Calculations/Trends/KAMA_Series.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-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 readonly 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;
- _lastkama = _lastlastkama;
- }
- else {
- _buffer.Add(TValue.v);
- _lastlastkama = _lastkama;
- }
- if (_buffer.Count > _p + 1) { _buffer.RemoveAt(0); }
-
- double _kama = 0;
- if (this.Count < this._p) { _kama = TValue.v; }
- 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)));
- }
- _lastkama = _kama;
- base.Add((TValue.t, _kama), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Trends/MAMA_Series.cs b/Calculations/Trends/MAMA_Series.cs
index 624321a8..5a04d341 100644
--- a/Calculations/Trends/MAMA_Series.cs
+++ b/Calculations/Trends/MAMA_Series.cs
@@ -15,105 +15,144 @@ Sources:
*/
-public class MAMA_Series : Single_TSeries_Indicator
-{
- public MAMA_Series(TSeries source, double fastlimit = 0.5, double slowlimit = 0.05, bool useNaN = false) : base(source, period: 5, useNaN)
- {
- fastl = fastlimit;
- slowl = slowlimit;
- Fama = new();
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private double sumPr, jI, jQ;
- readonly double fastl, slowl;
- private (double i, double i1, double i2, double i3, double i4, double i5, double i6, double io) pr, i1, q1, sm, dt;
- private (double i, double i1, double io) i2, q2, re, im, pd, ph, mama, fama;
- public TSeries Fama { get; }
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
+public class MAMA_Series : Single_TSeries_Indicator {
+ public MAMA_Series(TSeries source, double fastlimit = 0.5, double slowlimit = 0.05, bool useNaN = false) : base(source, 5, useNaN) {
+ fastl = fastlimit;
+ slowl = slowlimit;
+ Fama = new TSeries();
+ if (_data.Count > 0) {
+ base.Add(_data);
+ }
+ }
- if (!update) {
- // roll forward (oldx = x)
- pr.io = pr.i6; pr.i6 = pr.i5; pr.i5 = pr.i4; pr.i4 = pr.i3; pr.i3 = pr.i2; pr.i2 = pr.i1; pr.i1 = pr.i;
- i1.io = i1.i6; i1.i6 = i1.i5; i1.i5 = i1.i4; i1.i4 = i1.i3; i1.i3 = i1.i2; i1.i2 = i1.i1; i1.i1 = i1.i;
- q1.io = q1.i6; q1.i6 = q1.i5; q1.i5 = q1.i4; q1.i4 = q1.i3; q1.i3 = q1.i2; q1.i2 = q1.i1; q1.i1 = q1.i;
- dt.io = dt.i6; dt.i6 = dt.i5; dt.i5 = dt.i4; dt.i4 = dt.i3; dt.i3 = dt.i2; dt.i2 = dt.i1; dt.i1 = dt.i;
- sm.io = sm.i6; sm.i6 = sm.i5; sm.i5 = sm.i4; sm.i4 = sm.i3; sm.i3 = sm.i2; sm.i2 = sm.i1; sm.i1 = sm.i;
- i2.io = i2.i1; i2.i1 = i2.i;
- q2.io = q2.i1; q2.i1 = q2.i;
- re.io = re.i1; re.i1 = re.i;
- im.io = im.i1; im.i1 = im.i;
- pd.io = pd.i1; pd.i1 = pd.i;
- ph.io = ph.i1; ph.i1 = ph.i;
- mama.io = mama.i1; mama.i1 = mama.i;
- fama.io = fama.i1; fama.i1 = fama.i;
- }
- int i = base.Count;
- pr.i = TValue.v;
- if (i > 5) {
- double adj = (0.075 * pd.i1) + 0.54;
+ private double sumPr, jI, jQ;
+ private readonly double fastl, slowl;
+ private (double i, double i1, double i2, double i3, double i4, double i5, double i6, double io) pr, i1, q1, sm, dt;
+ private (double i, double i1, double io) i2, q2, re, im, pd, ph, mama, fama;
+ public TSeries Fama { get; }
- // smooth and detrender
- sm.i = ((4 * pr.i) + (3 * pr.i1) + (2 * pr.i2) + pr.i3) / 10;
- dt.i = ((0.0962 * sm.i) + (0.5769 * sm.i2) - (0.5769 * sm.i4) - (0.0962 * sm.i6)) * adj;
+ public override void Add((DateTime t, double v) TValue, bool update) {
+ if (!update) {
+ // roll forward (oldx = x)
+ pr.io = pr.i6;
+ pr.i6 = pr.i5;
+ pr.i5 = pr.i4;
+ pr.i4 = pr.i3;
+ pr.i3 = pr.i2;
+ pr.i2 = pr.i1;
+ pr.i1 = pr.i;
+ i1.io = i1.i6;
+ i1.i6 = i1.i5;
+ i1.i5 = i1.i4;
+ i1.i4 = i1.i3;
+ i1.i3 = i1.i2;
+ i1.i2 = i1.i1;
+ i1.i1 = i1.i;
+ q1.io = q1.i6;
+ q1.i6 = q1.i5;
+ q1.i5 = q1.i4;
+ q1.i4 = q1.i3;
+ q1.i3 = q1.i2;
+ q1.i2 = q1.i1;
+ q1.i1 = q1.i;
+ dt.io = dt.i6;
+ dt.i6 = dt.i5;
+ dt.i5 = dt.i4;
+ dt.i4 = dt.i3;
+ dt.i3 = dt.i2;
+ dt.i2 = dt.i1;
+ dt.i1 = dt.i;
+ sm.io = sm.i6;
+ sm.i6 = sm.i5;
+ sm.i5 = sm.i4;
+ sm.i4 = sm.i3;
+ sm.i3 = sm.i2;
+ sm.i2 = sm.i1;
+ sm.i1 = sm.i;
+ i2.io = i2.i1;
+ i2.i1 = i2.i;
+ q2.io = q2.i1;
+ q2.i1 = q2.i;
+ re.io = re.i1;
+ re.i1 = re.i;
+ im.io = im.i1;
+ im.i1 = im.i;
+ pd.io = pd.i1;
+ pd.i1 = pd.i;
+ ph.io = ph.i1;
+ ph.i1 = ph.i;
+ mama.io = mama.i1;
+ mama.i1 = mama.i;
+ fama.io = fama.i1;
+ fama.i1 = fama.i;
+ }
- // in-phase and quadrature
- q1.i = ((0.0962 * dt.i) + (0.5769 * dt.i2) - (0.5769 * dt.i4) - (0.0962 * dt.i6)) * adj;
- i1.i = dt.i3;
+ var i = Count;
+ pr.i = TValue.v;
+ if (i > 5) {
+ var adj = 0.075 * pd.i1 + 0.54;
- // advance the phases by 90 degrees
- jI = ((0.0962 * i1.i) + (0.5769 * i1.i2) - (0.5769 * i1.i4) - (0.0962 * i1.i6)) * adj;
- jQ = ((0.0962 * q1.i) + (0.5769 * q1.i2) - (0.5769 * q1.i4) - (0.0962 * q1.i6)) * adj;
+ // smooth and detrender
+ sm.i = (4 * pr.i + 3 * pr.i1 + 2 * pr.i2 + pr.i3) / 10;
+ dt.i = (0.0962 * sm.i + 0.5769 * sm.i2 - 0.5769 * sm.i4 - 0.0962 * sm.i6) * adj;
- // phasor addition for 3-bar averaging
- i2.i = i1.i - jQ;
- q2.i = q1.i + jI;
+ // in-phase and quadrature
+ q1.i = (0.0962 * dt.i + 0.5769 * dt.i2 - 0.5769 * dt.i4 - 0.0962 * dt.i6) * adj;
+ i1.i = dt.i3;
- i2.i = (0.2 * i2.i) + (0.8 * i2.i1); // smoothing it
- q2.i = (0.2 * q2.i) + (0.8 * q2.i1);
+ // advance the phases by 90 degrees
+ jI = (0.0962 * i1.i + 0.5769 * i1.i2 - 0.5769 * i1.i4 - 0.0962 * i1.i6) * adj;
+ jQ = (0.0962 * q1.i + 0.5769 * q1.i2 - 0.5769 * q1.i4 - 0.0962 * q1.i6) * adj;
- // homodyne discriminator
- re.i = (i2.i * i2.i1) + (q2.i * q2.i1);
- im.i = (i2.i * q2.i1) - (q2.i * i2.i1);
+ // phasor addition for 3-bar averaging
+ i2.i = i1.i - jQ;
+ q2.i = q1.i + jI;
- re.i = (0.2 * re.i) + (0.8 * re.i1); // smoothing it
- im.i = (0.2 * im.i) + (0.8 * im.i1);
+ i2.i = 0.2 * i2.i + 0.8 * i2.i1; // smoothing it
+ q2.i = 0.2 * q2.i + 0.8 * q2.i1;
- // calculate period
- pd.i = (im.i != 0 && re.i != 0) ? (6.283185307179586 / Math.Atan(im.i / re.i)) : 0d;
+ // homodyne discriminator
+ re.i = i2.i * i2.i1 + q2.i * q2.i1;
+ im.i = i2.i * q2.i1 - q2.i * i2.i1;
- // adjust period to thresholds
- pd.i = (pd.i > 1.5 * pd.i1) ? 1.5 * pd.i1 : pd.i;
- pd.i = (pd.i < 0.67 * pd.i1) ? 0.67 * pd.i1 : pd.i;
- pd.i = (pd.i < 6d) ? 6d : pd.i;
- pd.i = (pd.i > 50d) ? 50d : pd.i;
+ re.i = 0.2 * re.i + 0.8 * re.i1; // smoothing it
+ im.i = 0.2 * im.i + 0.8 * im.i1;
- // smooth the period
- pd.i = (0.2 * pd.i) + (0.8 * pd.i1);
+ // calculate period
+ pd.i = im.i != 0 && re.i != 0 ? 6.283185307179586 / Math.Atan(im.i / re.i) : 0d;
- // determine phase position
- ph.i = (i1.i != 0) ? Math.Atan(q1.i / i1.i) * 57.29577951308232 : 0;
+ // adjust period to thresholds
+ pd.i = pd.i > 1.5 * pd.i1 ? 1.5 * pd.i1 : pd.i;
+ pd.i = pd.i < 0.67 * pd.i1 ? 0.67 * pd.i1 : pd.i;
+ pd.i = pd.i < 6d ? 6d : pd.i;
+ pd.i = pd.i > 50d ? 50d : pd.i;
- // change in phase
- double delta = Math.Max(ph.i1 - ph.i, 1d);
+ // smooth the period
+ pd.i = 0.2 * pd.i + 0.8 * pd.i1;
- // adaptive alpha value
- double alpha = Math.Max(fastl / delta, slowl);
+ // determine phase position
+ ph.i = i1.i != 0 ? Math.Atan(q1.i / i1.i) * 57.29577951308232 : 0;
- // final indicators
- mama.i = ((alpha * pr.i) + ((1d - alpha) * mama.i1));
- fama.i = ((0.5d * alpha * mama.i) + ((1d - (0.5d * alpha)) * fama.i1));
- }
- else {
- sumPr += pr.i;
- pd.i = sm.i = dt.i = i1.i = q1.i = i2.i = q2.i = re.i = im.i = ph.i = 0;
- mama.i = fama.i = sumPr / (i+1);
- }
+ // change in phase
+ var delta = Math.Max(ph.i1 - ph.i, 1d);
- base.Add((TValue.t, mama.i), update, _NaN);
- var result = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : fama.i);
- Fama.Add(result, update);
- }
+ // adaptive alpha value
+ var alpha = Math.Max(fastl / delta, slowl);
+
+ // final indicators
+ mama.i = alpha * pr.i + (1d - alpha) * mama.i1;
+ fama.i = 0.5d * alpha * mama.i + (1d - 0.5d * alpha) * fama.i1;
+ }
+ else {
+ sumPr += pr.i;
+ pd.i = sm.i = dt.i = i1.i = q1.i = i2.i = q2.i = re.i = im.i = ph.i = 0;
+ mama.i = fama.i = sumPr / (i + 1);
+ }
+
+ base.Add((TValue.t, mama.i), update, _NaN);
+ var result = (TValue.t, Count < _p - 1 && _NaN ? double.NaN : fama.i);
+ Fama.Add(result, update);
+ }
}
diff --git a/Calculations/Trends/RMA_Series.cs b/Calculations/Trends/RMA_Series.cs
deleted file mode 100644
index f7059c51..00000000
--- a/Calculations/Trends/RMA_Series.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-RMA: wildeR Moving Average
- J. Welles Wilder introduced RMA as an alternative to EMA. RMA's weight (k) is
- set as 1/period, giving less weight to the new data compared to EMA.
-
-Sources:
- https://archive.org/details/newconceptsintec00wild/page/23/mode/2up
- https://tlc.thinkorswim.com/center/reference/Tech-Indicators/studies-library/V-Z/WildersSmoothing
- https://www.incrediblecharts.com/indicators/wilder_moving_average.php
-
-Issues:
- Pandas-TA library calculates RMA using straight Exponential Weighted Mean:
- pandas.ewm().mean() and returns incorrect first (period) of bars compared to
- published formula. This implementation passess the validation test in Wilder's book.
-
- */
-
-public class RMA_Series : Single_TSeries_Indicator
-{
- private readonly System.Collections.Generic.List _buffer = new();
- private readonly double _k, _k1m;
- private double _lastema, _lastlastema;
-
- public RMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- this._k = 1.0 / (double)(this._p);
- this._k1m = 1.0 - this._k;
- this._lastema = this._lastlastema = double.NaN;
- if (_data.Count > 0) { base.Add(_data); }
- }
-
- public override void Add((DateTime t, double v) TValue, bool update)
- {
- double _ema;
- if (update) { this._lastema = this._lastlastema; }
-
- if (this.Count < this._p)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- _ema = _buffer.Average();
- }
- else
- {
- _ema = (TValue.v * _k) + (_lastema * _k1m);
- }
-
- this._lastlastema = this._lastema;
- this._lastema = _ema;
-
- base.Add((TValue.t, _ema), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Trends/SMA_Series.cs b/Calculations/Trends/SMA_Series.cs
deleted file mode 100644
index 84c8395d..00000000
--- a/Calculations/Trends/SMA_Series.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-namespace QuanTAlib;
-using System;
-
-/*
-SMA: Simple Moving Average
- The weights are equally distributed across the period, resulting in a mean() of
- the data within the period
-
-Sources:
- https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/simple-moving-average-sma/
- https://stats.stackexchange.com/a/24739
-
-Remark:
- This calc doesn't use LINQ or SUM() or any of (slow) iterative methods. It is not as fast as TA-LIB
- implementation, but it does allow incremental additions of inputs and real-time calculations of SMA()
-
- */
-
-public class SMA_Series : Single_TSeries_Indicator {
- private double _sum, _oldsum;
- private int _len;
-
- public SMA_Series(TSeries source, int period = 0, bool useNaN = false) : base(source, period, false) {
- _sum = _oldsum = 0;
- _len = 0;
- if (this._data.Count > 0) { base.Add(this._data); }
- }
-
- public override void Add((DateTime t, double v) TValue, bool update) {
- if (update) { _sum = _oldsum; }
- else { _oldsum = _sum; _len++; }
-
- _sum += TValue.v;
- if (_period != 0 && _len > _period) {
- _sum -= (_data[base.Count - _period - (update ? 1 : 0)].v);
- }
- double _div = (_period == 0) ? _len : Math.Min(_len, _period);
- base.Add((TValue.t, _sum / _div), update, _NaN);
- }
- public void Reset() {
- _sum = _oldsum = 0;
- _len = 0;
- }
-}
diff --git a/Calculations/Trends/SMMA_Series.cs b/Calculations/Trends/SMMA_Series.cs
deleted file mode 100644
index 27d3bfcd..00000000
--- a/Calculations/Trends/SMMA_Series.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-SMMA: Smoothed Moving Average
- The Smoothed Moving Average (SMMA) is a combination of a SMA and an EMA. It gives the recent prices
- an equal weighting as the historic prices as it takes all available price data into account.
- The main advantage of a smoothed moving average is that it removes short-term fluctuations.
-
- SMMA(i) = (SMMA-1*(N-1) + CLOSE (i)) / N
-
-Sources:
- https://blog.earn2trade.com/smoothed-moving-average
- https://guide.traderevolution.com/traderevolution/mobile-applications/phone/android/technical-indicators/moving-averages/smma-smoothed-moving-average
- https://www.chartmill.com/documentation/technical-analysis-indicators/217-MOVING-AVERAGES-%7C-The-Smoothed-Moving-Average-%28SMMA%29
-
- */
-
-public class SMMA_Series : Single_TSeries_Indicator
-{
- private readonly System.Collections.Generic.List _buffer = new();
- private double _lastsmma, _lastlastsmma;
-
- public SMMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- this._lastsmma = this._lastlastsmma = double.NaN;
- if (this._data.Count > 0) { base.Add(this._data); }
- }
-
- public override void Add((DateTime t, double v) TValue, bool update)
- {
- double _smma = 0;
- if (update) { this._lastsmma = this._lastlastsmma; }
-
- if (this.Count < this._p)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- _smma = _buffer.Average();
- }
- else
- {
- _smma = ((_lastsmma * (_p-1)) + TValue.v) / _p ;
- }
-
- this._lastlastsmma = this._lastsmma;
- this._lastsmma = _smma;
-
- base.Add((TValue.t, _smma), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Trends/T3_Series.cs b/Calculations/Trends/T3_Series.cs
deleted file mode 100644
index 4f598993..00000000
--- a/Calculations/Trends/T3_Series.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-using System.Numerics;
-
-/*
-T3: Tillson T3 Moving Average
- Tim Tillson described it in "Technical Analysis of Stocks and Commodities", January 1998 in the
- article "Better Moving Averages". Tillson’s moving average becomes a popular indicator of
- technical analysis as it gets less lag with the price chart and its curve is considerably smoother.
-
-Sources:
- https://technicalindicators.net/indicators-technical-analysis/150-t3-moving-average
- http://www.binarytribune.com/forex-trading-indicators/t3-moving-average-indicator/
-
- */
-public class T3_Series : Single_TSeries_Indicator {
- private readonly double _k, _k1m, _c1, _c2, _c3, _c4;
- private readonly System.Collections.Generic.List _buffer1 = new();
- private readonly System.Collections.Generic.List _buffer2 = new();
- private readonly System.Collections.Generic.List _buffer3 = new();
- private readonly System.Collections.Generic.List _buffer4 = new();
- private readonly System.Collections.Generic.List _buffer5 = new();
- private readonly System.Collections.Generic.List _buffer6 = new();
-
- private double _lastema1, _lastema2, _lastema3, _lastema4, _lastema5, _lastema6;
- private double _llastema1, _llastema2, _llastema3, _llastema4, _llastema5, _llastema6;
- private readonly bool _useSMA;
-
- public T3_Series(TSeries source, int period, double vfactor = 0.7, bool useNaN = false, bool useSMA = true) : base(source, period, useNaN) {
- double _a = vfactor; //0.7; //0.618
- _c1 = -_a * _a * _a;
- _c2 = 3 * _a * _a + 3 * _a * _a * _a;
- _c3 = -6 * _a * _a - 3 * _a - 3 * _a * _a * _a;
- _c4 = 1 + 3 * _a + _a * _a * _a + 3 * _a * _a;
-
- _k = 2.0 / (_p + 1);
- _k1m = 1.0 - _k;
- _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = _lastema4 = _llastema4 = _lastema5 = _llastema5 = _lastema5 = _llastema5 = 0;
- _useSMA = useSMA;
- if (this._data.Count > 0) { base.Add(this._data); }
- }
-
- public override void Add((DateTime t, double v) TValue, bool update) {
- double _ema1, _ema2, _ema3, _ema4, _ema5, _ema6;
- if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; _lastema4 = _llastema4; _lastema5 = _llastema5; _lastema6 = _llastema6; }
- else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; _llastema4 = _lastema4; _llastema5 = _lastema5; _llastema6 = _lastema6; }
-
- if (this.Count == 0) { _lastema1 = _lastema2 = _lastema3 = _lastema4 = _lastema5 = _lastema6 = TValue.v; }
-
- if ((this.Count < _p) && _useSMA) {
- Add_Replace(_buffer1, TValue.v, update);
- _ema1 = 0;
- for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; }
- _ema1 /= _buffer1.Count;
-
- Add_Replace(_buffer2, _ema1, update);
- _ema2 = 0;
- for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; }
- _ema2 /= _buffer2.Count;
-
- Add_Replace(_buffer3, _ema2, update);
- _ema3 = 0;
- for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; }
- _ema3 /= _buffer3.Count;
-
- Add_Replace(_buffer4, _ema3, update);
- _ema4 = 0;
- for (int i = 0; i < _buffer4.Count; i++) { _ema4 += _buffer4[i]; }
- _ema4 /= _buffer4.Count;
-
- Add_Replace(_buffer5, _ema4, update);
- _ema5 = 0;
- for (int i = 0; i < _buffer5.Count; i++) { _ema5 += _buffer5[i]; }
- _ema5 /= _buffer5.Count;
-
- Add_Replace(_buffer6, _ema5, update);
- _ema6 = 0;
- for (int i = 0; i < _buffer6.Count; i++) { _ema6 += _buffer6[i]; }
- _ema6 /= _buffer6.Count;
- }
- else {
- _ema1 = (TValue.v * this._k) + (this._lastema1 * this._k1m);
- _ema2 = (_ema1 * this._k) + (this._lastema2 * this._k1m);
- _ema3 = (_ema2 * this._k) + (this._lastema3 * this._k1m);
- _ema4 = (_ema3 * this._k) + (this._lastema4 * this._k1m);
- _ema5 = (_ema4 * this._k) + (this._lastema5 * this._k1m);
- _ema6 = (_ema5 * this._k) + (this._lastema6 * this._k1m);
- }
- _lastema1 = _ema1;
- _lastema2 = _ema2;
- _lastema3 = _ema3;
- _lastema4 = _ema4;
- _lastema5 = _ema5;
- _lastema6 = _ema6;
-
- double _T3 = _c1 * _ema6 + _c2 * _ema5 + _c3 * _ema4 + _c4 * _ema3;
- base.Add((TValue.t, _T3), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Trends/TEMA_Series.cs b/Calculations/Trends/TEMA_Series.cs
deleted file mode 100644
index 71f84698..00000000
--- a/Calculations/Trends/TEMA_Series.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-TEMA: Triple Exponential Moving Average
- TEMA uses EMA(EMA(EMA())) to calculate less laggy Exponential moving average.
-
-Sources:
- https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triple-exponential-moving-average-tema/
-
-Remark:
- ema1 = EMA(close, length)
- ema2 = EMA(ema1, length)
- ema3 = EMA(ema2, length)
- TEMA = 3 * (ema1 - ema2) + ema3
-
- */
-
-public class TEMA_Series : Single_TSeries_Indicator
-{
- private readonly System.Collections.Generic.List _buffer = new();
- private readonly double _k, _k1m;
- private double _lastema1, _lastlastema1;
- private double _lastema2, _lastlastema2;
- private double _lastema3, _lastlastema3;
-
- public TEMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- this._k = 2.0 / (this._p + 1);
- this._k1m = 1.0 - this._k;
- if (_data.Count > 0) { base.Add(_data); }
- }
-
- public override void Add((DateTime t, double v) TValue, bool update)
- {
- if (update)
- {
- this._lastema1 = this._lastlastema1;
- this._lastema2 = this._lastlastema2;
- this._lastema3 = this._lastlastema3;
- }
-
- double _ema1, _ema2, _ema3;
-
- if (this.Count < this._p)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
- double _sma = _buffer.Average();
- _ema1 = _ema2 = _ema3 = _sma;
- }
- else
- {
- _ema1 = (TValue.v * this._k) + (this._lastema1 * this._k1m);
- _ema2 = (_ema1 * this._k) + (this._lastema2 * this._k1m);
- _ema3 = (_ema2 * this._k) + (this._lastema3 * this._k1m);
- }
-
- double _tema = (3 * (_ema1 - _ema2)) + _ema3;
-
- this._lastlastema1 = this._lastema1;
- this._lastlastema2 = this._lastema2;
- this._lastlastema3 = this._lastema3;
- this._lastema1 = _ema1;
- this._lastema2 = _ema2;
- this._lastema3 = _ema3;
-
- base.Add((TValue.t, _tema), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Trends/TRIMA_Series.cs b/Calculations/Trends/TRIMA_Series.cs
deleted file mode 100644
index 7a082b7e..00000000
--- a/Calculations/Trends/TRIMA_Series.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-TRIMA: Triangular Moving Average
- A weighted moving average where the shape of the weights are triangular and the greatest
- weight is in the middle of the period,
-
-Sources:
- https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triangular-moving-average-trima/
-
-Remark:
- trima = sma(sma(signal, n/2), n/2)
-
- */
-
-public class TRIMA_Series : Single_TSeries_Indicator
-{
- private readonly System.Collections.Generic.List _buffer1 = new();
- private readonly System.Collections.Generic.List _buffer2 = new();
- private readonly int _p1a, _p1b;
-
- public TRIMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- _p1a = (int) Math.Floor((period * 0.5) + 1);
- _p1b = (int) Math.Ceiling(0.5 * period);
- if (base._data.Count > 0) { base.Add(base._data); }
- }
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- if (update) { _buffer1[_buffer1.Count - 1] = TValue.v; } else { _buffer1.Add(TValue.v); }
- if (_buffer1.Count > this._p1b && this._p1b != 0) { _buffer1.RemoveAt(0); }
- double _sma1 = _buffer1.Average();
-
- if (update) { _buffer2[_buffer2.Count - 1] = _sma1; } else { _buffer2.Add(_sma1); }
- if (_buffer2.Count > this._p1a && this._p1a != 0) { _buffer2.RemoveAt(0); }
- double _trima = _buffer2.Average();
-
- base.Add((TValue.t, _trima), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Trends/TRIX_Series.cs b/Calculations/Trends/TRIX_Series.cs
deleted file mode 100644
index 7815048c..00000000
--- a/Calculations/Trends/TRIX_Series.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-using System.Numerics;
-
-/*
-TRIX: Triple Exponential Average
- Developed by Jack Hutson in the early 1980s, the triple exponential average (TRIX)
- has become a popular technical analysis tool to aid chartists in spotting diversions
-and directional cues in stock trading patterns.
-
-Sources:
- https://www.investopedia.com/terms/t/trix.asp
-
- */
-public class TRIX_Series : Single_TSeries_Indicator
-{
- private readonly double _k, _k1m;
- private readonly System.Collections.Generic.List _buffer1 = new();
- private readonly System.Collections.Generic.List _buffer2 = new();
- private readonly System.Collections.Generic.List _buffer3 = new();
-
- private double _lastema1, _lastema2, _lastema3;
- private double _llastema1, _llastema2, _llastema3;
- private readonly bool _useSMA;
-
- public TRIX_Series(TSeries source, int period, bool useNaN = false, bool useSMA = true) : base(source, period, useNaN)
- {
-
- _k = 2.0 / (_p + 1);
- _k1m = 1.0 - _k;
- _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = 0;
- _useSMA = useSMA;
- if (this._data.Count > 0) { base.Add(this._data); }
- }
-
- public override void Add((DateTime t, double v) TValue, bool update)
- {
- double _ema1, _ema2, _ema3;
- if (this.Count == 0) { _lastema1 = _lastema2 = _lastema3 = TValue.v; }
-
- if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; }
- else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; }
-
- if ((this.Count < _p) && _useSMA)
- {
- Add_Replace(_buffer1, TValue.v, update);
- _ema1 = 0;
- for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; }
- _ema1 /= _buffer1.Count;
-
- Add_Replace(_buffer2, _ema1, update);
- _ema2 = 0;
- for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; }
- _ema2 /= _buffer2.Count;
-
- Add_Replace(_buffer3, _ema2, update);
- _ema3 = 0;
- for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; }
- _ema3 /= _buffer3.Count;
- }
- else
- {
- _ema1 = (TValue.v * this._k) + (this._lastema1 * this._k1m);
- _ema2 = (_ema1 * this._k) + (this._lastema2 * this._k1m);
- _ema3 = (_ema2 * this._k) + (this._lastema3 * this._k1m);
- }
- double _trix = 100 * (_ema3 - _lastema3) / _lastema3;
- _lastema1 = _ema1;
- _lastema2 = _ema2;
- _lastema3 = _ema3;
-
- base.Add((TValue.t, _trix), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Trends/WMA_Series.cs b/Calculations/Trends/WMA_Series.cs
deleted file mode 100644
index 2b835b7f..00000000
--- a/Calculations/Trends/WMA_Series.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-namespace QuanTAlib;
-using System;
-
-/*
-WMA: (linearly) Weighted Moving Average
- The weights are linearly decreasing over the period and the most recent data has
- the heaviest weight.
-
-Sources:
- https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/weighted-moving-average-wma/
- https://www.technicalindicators.net/indicators-technical-analysis/83-moving-averages-simple-exponential-weighted
-
- */
-
-public class WMA_Series : Single_TSeries_Indicator
-{
- public WMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
- {
- for (int i = 0; i < this._p; i++) { this._weights.Add(i + 1); }
- if (base._data.Count > 0) { base.Add(base._data); }
- }
- private readonly System.Collections.Generic.List _buffer = new();
- private readonly System.Collections.Generic.List _weights = new();
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- Add_Replace_Trim(_buffer, TValue.v, _p, update);
-
- double _wma = 0;
- for (int i = 0; i < _buffer.Count; i++) { _wma += _buffer[i] * this._weights[i]; }
- _wma /= (this._buffer.Count * (this._buffer.Count + 1)) * 0.5;
-
- base.Add((TValue.t, _wma), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Trends/ZLEMA_Series.cs b/Calculations/Trends/ZLEMA_Series.cs
deleted file mode 100644
index a72da3ce..00000000
--- a/Calculations/Trends/ZLEMA_Series.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-namespace QuanTAlib;
-using System;
-using System.Linq;
-
-/*
-ZLEMA: Zero Lag Exponential Moving Average
- The Zero lag exponential moving average (ZLEMA) indicator was created by John
- Ehlers and Ric Way.
-
-The formula for a given N-Day period and for a given Data series is:
- Lag = (Period-1)/2
- Ema Data = {Data+(Data-Data(Lag days ago))
- ZLEMA = EMA (EmaData,Period)
-
-Remark:
- The idea is do a regular exponential moving average (EMA) calculation but on a
- de-lagged data instead of doing it on the regular data. Data is de-lagged by
- removing the data from "lag" days ago thus removing (or attempting to remove)
- the cumulative lag effect of the moving average.
-
- */
-
-public class ZLEMA_Series : Single_TSeries_Indicator
-{
- private readonly System.Collections.Generic.List _buffer = new();
- private readonly double _k, _k1m;
- private double _lastema, _lastema_o;
- private int _llag;
- private readonly bool _useSMA;
-
- public ZLEMA_Series(TSeries source, int period, bool useNaN = false, bool useSMA = true) : base(source, period, useNaN)
- {
- this._k = 2.0 / (this._p + 1);
- this._k1m = 1.0 - this._k;
- this._lastema = this._lastema_o = double.NaN;
- _llag = (int)((_p-1) * 0.5);
- _useSMA = useSMA;
- if (_data.Count > 0) { base.Add(_data); }
- }
-
- public override void Add((System.DateTime t, double v) TValue, bool update)
- {
- int _lag = Math.Max(this.Count-_llag, 0);
- if (update) {
- _lastema = _lastema_o; _lag--;
- } else {
- _lastema_o = _lastema;
- }
- double _zl = TValue.v + (TValue.v - _data[_lag].v);
- double _ema = 0;
-
- if (this.Count < this._p && _useSMA) {
- Add_Replace_Trim(_buffer, _zl, _p, update);
- _ema = _buffer.Average();
- } else {
- _ema = (_zl * _k) + (_lastema * _k1m);
- }
- _lastema = _ema;
-
- base.Add((TValue.t, _ema), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Volatility/ATRP_Series.cs b/Calculations/Volatility/ATRP_Series.cs
index 086db3db..3f4457ea 100644
--- a/Calculations/Volatility/ATRP_Series.cs
+++ b/Calculations/Volatility/ATRP_Series.cs
@@ -19,7 +19,7 @@ public class ATRP_Series : Single_TBars_Indicator {
public ATRP_Series(TBars source, int period, bool useNaN = false) : base(source, period, useNaN) {
_period = period;
- _k = 1.0 / (double)(_p);
+ _k = 1.0 / (double)(_period);
_lastatr = _lastlastatr = _cm1 = _lastcm1 = _sum = _oldsum = 0;
if (this._bars.Count > 0) { base.Add(this._bars); }
}
diff --git a/Calculations/Volatility/CMO_Series.cs b/Calculations/Volatility/CMO_Series.cs
deleted file mode 100644
index 6cabc235..00000000
--- a/Calculations/Volatility/CMO_Series.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-namespace QuanTAlib;
-using System;
-
-/*
-CMO: Chande Momentum Oscillator
- Chande Momentum Oscillator (also known as CMO indicator) was developed by Tushar S. Chande
- CMO is similar to other momentum oscillators (e.g. RSI or Stochastics). Alike RSI oscillator,
- the CMO values move in the range from -100 to +100 points and its aim is to detect the
- overbought and oversold market conditions. CMO calculates the price momentum on both the up
- days as well as the down days. The CMO calculation is based on non-smoothed price values
- meaning that it can reach its extremes more frequently and the short-time swings are more visible.
-
-Sources:
- https://www.technicalindicators.net/indicators-technical-analysis/144-cmo-chande-momentum-oscillator
-
- */
-
-public class CMO_Series : Single_TSeries_Indicator {
- private readonly System.Collections.Generic.List _buff_up = new();
- private readonly System.Collections.Generic.List _buff_dn = new();
- private double _plast_value, _last_value;
-
- public CMO_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN) {
- if (this._data.Count > 0) { base.Add(this._data); }
- }
-
- public override void Add((DateTime t, double v) TValue, bool update) {
- if (this.Count == 0) { _plast_value = _last_value = TValue.v; }
- if (update) {_last_value = _plast_value;} else {_plast_value = _last_value;}
-
- Add_Replace_Trim(_buff_up, (TValue.v > _last_value) ? TValue.v-_last_value : 0, _p, update);
- Add_Replace_Trim(_buff_dn, (TValue.v < _last_value) ? _last_value-TValue.v : 0, _p, update);
- _last_value = TValue.v;
-
- double _cmo_up = 0;
- double _cmo_dn = 0;
- for (int i = 0; i < Math.Min(_buff_up.Count, _buff_dn.Count); i++) {
- _cmo_up += _buff_up[i];
- _cmo_dn += _buff_dn[i];
- }
-
- double _cmo = 100 * (_cmo_up - _cmo_dn) / (_cmo_up + _cmo_dn);
- if (_cmo_up + _cmo_dn == 0) {_cmo = 0;}
- base.Add((TValue.t, _cmo), update, _NaN);
- }
-}
\ No newline at end of file
diff --git a/Calculations/Volume/OBV_Series.cs b/Calculations/Volatility/OBV_Series.cs
similarity index 100%
rename from Calculations/Volume/OBV_Series.cs
rename to Calculations/Volatility/OBV_Series.cs
diff --git a/Calculations/Volatility/RSI_Series.cs b/Calculations/Volatility/RSI_Series.cs
deleted file mode 100644
index 0805df35..00000000
--- a/Calculations/Volatility/RSI_Series.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-namespace QuanTAlib;
-using System;
-
-/*
-RSI: Relative Strength Index
- Created by J. Welles Wilder, the Relative Strength Index measures strength
- of the winning/losing streak over N lookback periods on a scale of 0 to 100,
- to depict overbought and oversold conditions.
-
-Sources:
- https://www.investopedia.com/terms/r/rsi.asp
-
- */
-
-public class RSI_Series : Single_TSeries_Indicator
-{
- private readonly System.Collections.Generic.List _gain = new();
- private readonly System.Collections.Generic.List _loss = new();
- private double _avgGain, _avgLoss, _lastValue;
- private double _avgGain_o, _avgLoss_o, _lastValue_o;
- private int i;
-
- public RSI_Series(TSeries source, int period = 10, bool useNaN = false) : base(source, period: period, useNaN: useNaN) {
- i = 0;
- if (source.Count > 0) { base.Add(source); }
- }
-
- public override void Add((System.DateTime t, double v) TValue, bool update) {
- double _rsi = 0;
- if (update) {
- _lastValue = _lastValue_o;
- _avgGain = _avgGain_o;
- _avgLoss = _avgLoss_o;
- }
- else {
- _lastValue_o = _lastValue;
- _avgGain_o = _avgGain;
- _avgLoss_o = _avgLoss;
- }
-
- if (i == 0) { _lastValue = TValue.v; }
-
- double _gainval = (TValue.v > _lastValue) ? TValue.v - _lastValue : 0;
- Add_Replace_Trim(_gain, _gainval, _p, update);
- double _lossval = (TValue.v < _lastValue) ? _lastValue - TValue.v : 0;
- Add_Replace_Trim(_loss, _lossval, _p, update);
- _lastValue = TValue.v;
-
- // calculate RSI
- if (i > _p)
- {
- _avgGain = ((_avgGain * (_p - 1)) + _gain[_gain.Count - 1]) / _p;
- _avgLoss = ((_avgLoss * (_p - 1)) + _loss[_loss.Count - 1]) / _p;
- if (_avgLoss > 0) {
- double rs = _avgGain / _avgLoss;
- _rsi = 100 - (100 / (1 + rs));
- }
- else { _rsi = 100; }
- }
- // initialize average gain
- else
- {
- double _sumGain = 0;
- for (int p = 0; p < _gain.Count; p++) { _sumGain += _gain[p]; }
- double _sumLoss = 0;
- for (int p = 0; p < _loss.Count; p++) { _sumLoss += _loss[p]; }
-
- _avgGain = _sumGain / _gain.Count;
- _avgLoss = _sumLoss / _loss.Count;
-
- _rsi = (_avgLoss > 0) ? 100 - (100 / (1 + (_avgGain / _avgLoss))) : 100;
- }
-
- if (!update) { i++; }
- var result = (TValue.t, (this.Count < this._p && this._NaN) ? double.NaN : _rsi);
- base.Add(result, update);
- }
-}
\ No newline at end of file
diff --git a/Calculations/_Updated/ALMA_Series.cs b/Calculations/_Updated/ALMA_Series.cs
new file mode 100644
index 00000000..18ba3339
--- /dev/null
+++ b/Calculations/_Updated/ALMA_Series.cs
@@ -0,0 +1,107 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+/*
+ALMA: Arnaud Legoux Moving Average
+ The ALMA moving average uses the curve of the Normal (Gauss) distribution, which
+ can be shifted from 0 to 1. This allows regulating the smoothness and high
+ sensitivity of the indicator. Sigma is another parameter that is responsible for
+ the shape of the curve coefficients. This moving average reduces lag of the data
+ in conjunction with smoothing to reduce noise.
+
+
+Sources:
+ https://phemex.com/academy/what-is-arnaud-legoux-moving-averages
+ https://www.prorealcode.com/prorealtime-indicators/alma-arnaud-legoux-moving-average/
+
+ Discrepancy with Pandas-TA (but passes the validation with Skender.GetAlma)
+ */
+
+public class ALMA_Series : TSeries {
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ private readonly System.Collections.Generic.List _buffer = new();
+ private readonly System.Collections.Generic.List _weight = new();
+ private double _norm;
+ private readonly double _offset, _sigma;
+
+ //core constructors
+ public ALMA_Series(int period, double offset, double sigma, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"ALMA({period})";
+ _offset = offset;
+ _sigma = sigma;
+ _weight = new();
+ }
+ public ALMA_Series(TSeries source, int period, double offset, double sigma, bool useNaN) : this(period, offset, sigma, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+
+ public ALMA_Series() : this(period:0, offset:0.85, sigma:6.0, useNaN: false) { }
+ public ALMA_Series(int period) : this(period: period, offset:0.85, sigma:6.0, useNaN:false) { }
+ public ALMA_Series(TBars source) : this(source:source.Close, period:0, offset:0.85, sigma:6.0, useNaN:false) { }
+ public ALMA_Series(TBars source, int period) : this(source:source.Close, period:period, offset: 0.85, sigma: 6.0, useNaN: false) { }
+ public ALMA_Series(TBars source, int period, double offset, double sigma, bool useNaN) : this(source.Close, period:period, offset: offset, sigma: sigma, useNaN: false) { }
+ public ALMA_Series(TSeries source) : this(source, period:0, offset:0.85, sigma:6.0, useNaN:false) { }
+ public ALMA_Series(TSeries source, int period) : this(source:source, period:period, offset:0.85, sigma:6.0, useNaN:false) { }
+ public ALMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, offset: 0.85, sigma: 6.0, useNaN: useNaN) { }
+
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update=false) {
+ BufferTrim(_buffer, TValue.v, _period, update);
+ if (_weight.Count < _buffer.Count) {
+ for (int i = 0; i < (_buffer.Count - _weight.Count); i++) { _weight.Add(0.0); }
+ }
+ if (this._buffer.Count <= _period || _period ==0) {
+ int _len = this._buffer.Count;
+ _norm = 0;
+ double _m = _offset * (_len - 1);
+ double _s = _len / _sigma;
+ for (int i = 0; i < _len; i++) {
+ double _wt = Math.Exp(-((i - _m) * (i - _m)) / (2 * _s * _s));
+ _weight[i] = _wt;
+ _norm += _wt;
+ }
+ }
+
+ double _weightedSum = 0;
+ for (int i = 0; i < this._buffer.Count; i++) { _weightedSum += _weight[i] * _buffer[i]; }
+ double _alma = _weightedSum / _norm;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _alma);
+ return base.Add(res, update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ _weight.Clear();
+ }
+
+ //variation of Add()
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/BIAS_Series.cs b/Calculations/_Updated/BIAS_Series.cs
new file mode 100644
index 00000000..8fa734d5
--- /dev/null
+++ b/Calculations/_Updated/BIAS_Series.cs
@@ -0,0 +1,75 @@
+namespace QuanTAlib;
+using System;
+
+/*
+BIAS: Rate of change between the source and a moving average.
+ Bias is a statistical term which means a systematic deviation from the actual value.
+
+BIAS = (close - SMA) / SMA
+ = (close / SMA) - 1
+
+Sources:
+ https://en.wikipedia.org/wiki/Bias_of_an_estimator
+
+ */
+
+public class BIAS_Series : TSeries {
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+ private readonly SMA_Series _sma;
+
+ //core constructors
+ public BIAS_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"BIAS({period})";
+ _sma = new(period, false);
+ }
+ public BIAS_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public BIAS_Series() : this(period: 0, useNaN: false) { }
+ public BIAS_Series(int period) : this(period: period, useNaN: false) { }
+ public BIAS_Series(TBars source) : this(source.Close, 0, false) { }
+ public BIAS_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public BIAS_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public BIAS_Series(TSeries source) : this(source, 0, false) { }
+ public BIAS_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ var _s = _sma.Add(TValue,update);
+ double _bias = (TValue.v / ((_s.v!=0)?_s.v:1)) - 1;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _bias);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _sma.Reset();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/CMO_Series.cs b/Calculations/_Updated/CMO_Series.cs
new file mode 100644
index 00000000..6a7520b1
--- /dev/null
+++ b/Calculations/_Updated/CMO_Series.cs
@@ -0,0 +1,92 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+CMO: Chande Momentum Oscillator
+ Chande Momentum Oscillator (also known as CMO indicator) was developed by Tushar S. Chande
+ CMO is similar to other momentum oscillators (e.g. RSI or Stochastics). Alike RSI oscillator,
+ the CMO values move in the range from -100 to +100 points and its aim is to detect the
+ overbought and oversold market conditions. CMO calculates the price momentum on both the up
+ days as well as the down days. The CMO calculation is based on non-smoothed price values
+ meaning that it can reach its extremes more frequently and the short-time swings are more visible.
+
+Sources:
+ https://www.technicalindicators.net/indicators-technical-analysis/144-cmo-chande-momentum-oscillator
+
+ */
+
+public class CMO_Series : TSeries {
+ private readonly System.Collections.Generic.List _buff_up = new();
+ private readonly System.Collections.Generic.List _buff_dn = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+ private double _plast_value, _last_value;
+
+ //core constructors
+ public CMO_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"CMO({period})";
+ }
+ public CMO_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public CMO_Series() : this(period: 0, useNaN: false) { }
+ public CMO_Series(int period) : this(period: period, useNaN: false) { }
+ public CMO_Series(TBars source) : this(source.Close, 0, false) { }
+ public CMO_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public CMO_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public CMO_Series(TSeries source) : this(source, 0, false) { }
+ public CMO_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ if (update) { _last_value = _plast_value; } else { _plast_value = _last_value; }
+ BufferTrim(buffer:_buff_up, (TValue.v > _last_value) ? TValue.v - _last_value : 0, period:_period, update: update);
+ BufferTrim(buffer: _buff_dn, (TValue.v < _last_value) ? _last_value - TValue.v : 0, period: _period, update: update);
+ _last_value = TValue.v;
+ double _cmo_up = 0;
+ double _cmo_dn = 0;
+ for (int i = 0; i < Math.Min(_buff_up.Count, _buff_dn.Count); i++) {
+ _cmo_up += _buff_up[i];
+ _cmo_dn += _buff_dn[i];
+ }
+ double _cmo = 100 * (_cmo_up - _cmo_dn) / (_cmo_up + _cmo_dn);
+ if (_cmo_up + _cmo_dn == 0) { _cmo = 0; }
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _cmo);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buff_up.Clear();
+ _buff_dn.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/CUSUM_Series.cs b/Calculations/_Updated/CUSUM_Series.cs
new file mode 100644
index 00000000..333a1466
--- /dev/null
+++ b/Calculations/_Updated/CUSUM_Series.cs
@@ -0,0 +1,74 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+CUSUM: Cumulative Sum (aka Running Total)
+ SUM across a period provides a rolling sum of all values across the period.
+ If SUM values would be divided with period, the output would be SMA()
+
+Sources:
+ https://en.wikipedia.org/wiki/CUSUM
+ */
+
+public class CUSUM_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public CUSUM_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"CUSUM({period})";
+ }
+ public CUSUM_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public CUSUM_Series() : this(period: 0, useNaN: false) { }
+ public CUSUM_Series(int period) : this(period: period, useNaN: false) { }
+ public CUSUM_Series(TBars source) : this(source.Close, 0, false) { }
+ public CUSUM_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public CUSUM_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public CUSUM_Series(TSeries source) : this(source, 0, false) { }
+ public CUSUM_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ double _sum = 0;
+ for (int i = 0; i < _buffer.Count; i++) { _sum += _buffer[i]; }
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sum);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/DECAY_Series.cs b/Calculations/_Updated/DECAY_Series.cs
new file mode 100644
index 00000000..194f639a
--- /dev/null
+++ b/Calculations/_Updated/DECAY_Series.cs
@@ -0,0 +1,86 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+DECAY:
+ Linear decay can be modeled by a straight line with a negative slope of 1/period.
+ The value decreases in a straight line from the last maximum to 0.
+ Decay = Last Max - distance/period
+
+ Exponential decay is modeled as an exponential curve with diminishing factor of
+ 1-1/p
+
+ */
+
+public class DECAY_Series : TSeries {
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+ private readonly bool _exp;
+ private double _pdecay, _ppdecay;
+ private readonly double _dfactor;
+
+ //core constructors
+ public DECAY_Series(int period, bool exponential, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"DECAY({period})";
+ _exp = exponential;
+ _dfactor = (_exp) ? 1.0 - 1.0 / (double)_period : 1 / (double)_period;
+ _pdecay = _ppdecay = 0;
+ }
+ public DECAY_Series(TSeries source, int period, bool exponential, bool useNaN) : this(period, exponential, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public DECAY_Series() : this(period: 0, exponential: false, useNaN: false) { }
+ public DECAY_Series(int period) : this(period: period, exponential: false, useNaN: false) { }
+ public DECAY_Series(TBars source) : this(source.Close, period: 0, exponential: false, useNaN: false) { }
+ public DECAY_Series(TBars source, int period) : this(source.Close, period: period, exponential:false, useNaN:false) { }
+ public DECAY_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, exponential: false, useNaN) { }
+ public DECAY_Series(TSeries source) : this(source, period: 0, exponential: false, useNaN:false) { }
+ public DECAY_Series(TSeries source, int period) : this(source: source, period: period, exponential: false, useNaN: false) { }
+ public DECAY_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, exponential: false, useNaN: useNaN) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ if (double.IsNaN(TValue.v)) {
+ return base.Add((TValue.t, Double.NaN), update);
+ }
+ if (update) { _pdecay = _ppdecay; }
+ else { _ppdecay = _pdecay; }
+
+ if (this.Count == 0) { _pdecay = TValue.v; }
+ double _decay = Math.Max(TValue.v, Math.Max((_exp) ? _pdecay * _dfactor : _pdecay - _dfactor, 0));
+ _pdecay = _decay;
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _decay);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _pdecay = _ppdecay = 0;
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/DEMA_Series.cs b/Calculations/_Updated/DEMA_Series.cs
new file mode 100644
index 00000000..afec6a2c
--- /dev/null
+++ b/Calculations/_Updated/DEMA_Series.cs
@@ -0,0 +1,131 @@
+namespace QuanTAlib;
+
+using System;
+using System.Linq;
+
+/*
+DEMA: Double Exponential Moving Average
+ DEMA uses EMA(EMA()) to calculate smoother Exponential moving average.
+
+Sources:
+ https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/double-exponential-moving-average-dema/
+
+Remark:
+ ema1 = EMA(close, length)
+ ema2 = EMA(ema1, length)
+ DEMA = 2 * ema1 - ema2
+
+ */
+
+public class DEMA_Series : TSeries {
+ private double _k;
+ private double _sum, _oldsum;
+ private double _lastema1, _oldema1, _lastema2, _oldema2;
+ private int _len;
+ private readonly bool _useSMA;
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+//core constructor
+ public DEMA_Series(int period, bool useNaN, bool useSMA) : base() {
+ _period = period;
+ _NaN = useNaN;
+ _useSMA = useSMA;
+ Name = $"DEMA({period})";
+ _k = 2.0 / (_period + 1);
+ _len = 0;
+ _sum = _oldsum = _lastema1 = _lastema2 = 0;
+ }
+ //generic constructors (source)
+
+ public DEMA_Series() : this(0, false, true) {}
+ public DEMA_Series(int period) : this(period, false, true) {}
+ public DEMA_Series(TBars source) : this(source.Close, 0, false) {}
+ public DEMA_Series(TBars source, int period) : this(source.Close, period, false) {}
+ public DEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
+ public DEMA_Series(TSeries source, int period) : this(source, period, false, true) {}
+ public DEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
+ public DEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+
+// core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ if (update) {
+ _lastema1 = _oldema1;
+ _lastema2 = _oldema2;
+ _sum = _oldsum;
+ }
+ else {
+ _oldema1 = _lastema1;
+ _oldema2 = _lastema2;
+ _oldsum = _sum;
+ _len++;
+ }
+
+ if (_period == 0) {
+ _k = 2.0 / (_len + 1);
+ }
+
+ double _ema1, _ema2, _dema;
+ if (Count == 0) {
+ _ema1 = _ema2 = _sum = TValue.v;
+ }
+ else if (_len <= _period && _useSMA && _period != 0) {
+ _sum += TValue.v;
+ _ema1 = _sum / Math.Min(_len, _period);
+ _ema2 = _ema1;
+ }
+ else {
+ _ema1 = (TValue.v - _lastema1) * _k + _lastema1;
+ _ema2 = (_ema1 - _lastema2) * _k + _lastema2;
+ }
+
+ _dema = 2 * _ema1 - _ema2;
+
+ _lastema1 = double.IsNaN(_ema1) ? _lastema1 : _ema1;
+ _lastema2 = double.IsNaN(_ema2) ? _lastema2 : _ema2;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _dema);
+ return base.Add(res, update);
+ }
+
+//variation of Add()
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) {
+ return (DateTime.Today, double.NaN);
+ }
+
+ foreach (var item in data) {
+ Add(item, false);
+ }
+
+ return _data.Last;
+ }
+
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+
+ public (DateTime t, double v) Add(bool update) {
+ return Add(_data.Last, update);
+ }
+
+ public (DateTime t, double v) Add() {
+ return Add(_data.Last, false);
+ }
+
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(_data.Last, e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _sum = _oldsum = _lastema1 = _lastema2 = 0;
+ _len = 0;
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/DWMA_Series.cs b/Calculations/_Updated/DWMA_Series.cs
new file mode 100644
index 00000000..91df946c
--- /dev/null
+++ b/Calculations/_Updated/DWMA_Series.cs
@@ -0,0 +1,126 @@
+namespace QuanTAlib;
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+/*
+DWMA: Double Weighted Moving Average
+ The weights are decreasing over the period with p^2 decay
+ and the most recent data has the heaviest weight.
+
+ */
+
+public class DWMA_Series : TSeries {
+ private readonly List _buffer = new();
+ private List _weights = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+ protected int _len;
+
+//core constructors
+ public DWMA_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"DWMA({period})";
+ _len = 0;
+ _weights = CalculateWeights(_period);
+ }
+
+ public DWMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+
+ public DWMA_Series() : this(0, false) {
+ }
+
+ public DWMA_Series(int period) : this(period, false) {
+ }
+
+ public DWMA_Series(TBars source) : this(source.Close, 0, false) {
+ }
+
+ public DWMA_Series(TBars source, int period) : this(source.Close, period, false) {
+ }
+
+ public DWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {
+ }
+
+ public DWMA_Series(TSeries source, int period) : this(source, period, false) {
+ }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(_buffer, TValue.v, _period, update);
+ if (_period == 0) {
+ _len++;
+ _weights = CalculateWeights(_len);
+ }
+
+ double _dwma = 0, _wsum = 0;
+ var bufferCount = _buffer.Count;
+
+ var lockObj = new object();
+ Parallel.For(0, bufferCount, i =>
+ {
+ var temp = _buffer[i] * _weights[i];
+ lock (lockObj) {
+ _dwma += temp;
+ _wsum += _weights[i];
+ }
+ });
+ _dwma /= _wsum;
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _dwma);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) {
+ return (DateTime.Today, double.NaN);
+ }
+
+ foreach (var item in data) {
+ Add(item, false);
+ }
+
+ return _data.Last;
+ }
+
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+
+ public (DateTime t, double v) Add(bool update) {
+ return Add(_data.Last, update);
+ }
+
+ public (DateTime t, double v) Add() {
+ return Add(_data.Last, false);
+ }
+
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(_data.Last, e.update);
+ }
+
+ //calculating weights
+ private static List CalculateWeights(int period) {
+ var weights = new List(period);
+ for (var i = 0; i < period; i++) {
+ weights.Add((i + 1) * (i + 1));
+ }
+
+ return weights;
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _len = 0;
+ _buffer.Clear();
+ _weights = CalculateWeights(_period);
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/EMA_Series.cs b/Calculations/_Updated/EMA_Series.cs
new file mode 100644
index 00000000..8136f7da
--- /dev/null
+++ b/Calculations/_Updated/EMA_Series.cs
@@ -0,0 +1,123 @@
+namespace QuanTAlib;
+
+using System;
+using System.Linq;
+
+/*
+EMA: Exponential Moving Average
+ EMA needs very short history buffer and calculates the EMA value using just the
+ previous EMA value. The weight of the new datapoint (k) is k = 2 / (period-1)
+
+Sources:
+ https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages
+ https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp
+ https://blog.fugue88.ws/archives/2017-01/The-correct-way-to-start-an-Exponential-Moving-Average-EMA
+
+Issues:
+ There is no consensus what the first EMA value should be - a zero, a first
+ datapoint, or an average of the initial Period bars. All three starting methods
+ converge within 20+ bars to the same moving average. Most implementations (including this one)
+ use SMA() for the first Period bars as a seeding value for EMA.
+
+ */
+
+public class EMA_Series : TSeries {
+ private double _k;
+ private double _lastema, _oldema;
+ private double _sum, _oldsum;
+ private int _len;
+ private readonly bool _useSMA;
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+//core constructors
+
+ public EMA_Series(int period, bool useNaN, bool useSMA) : base() {
+ _period = period;
+ _NaN = useNaN;
+ _useSMA = useSMA;
+ Name = $"EMA({period})";
+ _k = 2.0 / (_period + 1);
+ _len = 0;
+ _sum = _oldsum = _lastema = _oldema = 0;
+ }
+ public EMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public EMA_Series() : this(0, false, true) {}
+ public EMA_Series(int period) : this(period, false, true) {}
+ public EMA_Series(TBars source) : this(source.Close, 0, false) {}
+ public EMA_Series(TBars source, int period) : this(source.Close, period, false) {}
+ public EMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
+ public EMA_Series(TSeries source, int period) : this(source, period, false, true) {}
+ public EMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
+
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
+ if (update) {
+ _lastema = _oldema;
+ _sum = _oldsum;
+ }
+ else {
+ _oldema = _lastema;
+ _oldsum = _sum;
+ _len++;
+ }
+
+ double _ema = 0;
+ if (_period == 0) {
+ _k = 2.0 / (_len + 1);
+ }
+
+ if (Count == 0) {
+ _ema = _sum = TValue.v;
+ }
+ else if (_len <= _period && _useSMA && _period != 0) {
+ _sum += TValue.v;
+ if (_period != 0 && _len > _period) {
+ _sum -= _data[Count - _period - (update ? 1 : 0)].v;
+ }
+
+ _ema = _sum / Math.Min(_len, _period);
+ }
+ else {
+ _ema = _k * (TValue.v - _lastema) + _lastema;
+ }
+
+ _lastema = double.IsNaN(_ema) ? _lastema : _ema;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _ema);
+ return base.Add(res, update);
+ }
+
+//variation of Add()
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _sum = _oldsum = _lastema = _oldema = 0;
+ _len = 0;
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/ENTROPY_Series.cs b/Calculations/_Updated/ENTROPY_Series.cs
new file mode 100644
index 00000000..4abd1239
--- /dev/null
+++ b/Calculations/_Updated/ENTROPY_Series.cs
@@ -0,0 +1,90 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+/*
+ENTROPY:
+ Introduced by Claude Shannon in 1948, entropy measures the unpredictability
+ of the data, or equivalently, of its average information.
+
+Calculation:
+ P = close / Σ(close)
+ ENTROPY = Σ(-P * Log(P) / Log(base))
+
+Sources:
+ https://en.wikipedia.org/wiki/Entropy_(information_theory)
+ https://math.stackexchange.com/questions/3428693/how-to-calculate-entropy-from-a-set-of-correlated-samples
+
+ */
+
+public class ENTROPY_Series : TSeries {
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+ private readonly double _logbase;
+ private readonly System.Collections.Generic.List _buffer = new();
+ private readonly System.Collections.Generic.List _buff2 = new();
+
+ //core constructors
+ public ENTROPY_Series(int period, double logbase, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ _logbase = logbase;
+ Name = $"ENTROPY({period})";
+ }
+ public ENTROPY_Series(TSeries source, int period, double logbase, bool useNaN) : this(period, logbase, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public ENTROPY_Series() : this(period: 0, logbase: 2.0, useNaN: false) { }
+ public ENTROPY_Series(int period) : this(period: period, logbase: 2.0, useNaN: false) { }
+ public ENTROPY_Series(TBars source) : this(source.Close, period: 0, logbase: 2.0, useNaN: false) { }
+ public ENTROPY_Series(TBars source, int period) : this(source.Close, period, logbase: 2.0, useNaN: false) { }
+ public ENTROPY_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, logbase: 2.0, useNaN: useNaN) { }
+ public ENTROPY_Series(TSeries source) : this(source, period: 0, logbase: 2.0, useNaN: false) { }
+ public ENTROPY_Series(TSeries source, int period) : this(source: source, period: period, logbase: 2.0, useNaN: false) { }
+ public ENTROPY_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, logbase: 2.0, useNaN: useNaN) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ if (double.IsNaN(TValue.v)) {
+ return base.Add((TValue.t, Double.NaN), update);
+ }
+ BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
+ double _sum = _buffer.Sum();
+ double _pp = this._buffer[^1] / _sum;
+ double _ppp = -_pp * Math.Log(_pp) / Math.Log(this._logbase);
+ BufferTrim(_buff2, _ppp, _period, update);
+ double _entp = _buff2.Sum();
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _entp);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ _buff2.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/FWMA_Series.cs b/Calculations/_Updated/FWMA_Series.cs
new file mode 100644
index 00000000..fb50117c
--- /dev/null
+++ b/Calculations/_Updated/FWMA_Series.cs
@@ -0,0 +1,100 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Numerics;
+using System.Linq;
+
+/*
+FWMA: Fibonacci's Weighted Moving Average is similar to a Weighted Moving Average
+ (WMA) where the weights are based on the Fibonacci Sequence.
+
+ */
+public class FWMA_Series : TSeries {
+ private readonly List _buffer = new();
+ private List _weights = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+ protected int _len;
+
+ public FWMA_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"FWMA({period})";
+ _len = 0;
+ _weights = CalculateWeights(_period);
+ }
+
+ public FWMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+
+ public FWMA_Series() : this(period: 0, useNaN: false) { }
+ public FWMA_Series(int period) : this(period: period, useNaN: false) { }
+ public FWMA_Series(TBars source) : this(source.Close, 0, false) { }
+ public FWMA_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public FWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public FWMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
+ if (_period == 0) {
+ _len++;
+ _weights = CalculateWeights(_len);
+ }
+ double _fwma = 0;
+ double totalWeights = _weights.Sum();
+ object lockObj = new object();
+ Parallel.For(0, _buffer.Count, i =>
+ {
+ double temp = _buffer[i] * _weights[i];
+ lock (lockObj) { _fwma += temp; }
+ });
+ _fwma /= totalWeights;
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _fwma);
+ return base.Add(res, update);
+ }
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ private static List CalculateWeights(int period) {
+ //to prevent overflow, max period can be no more than 1476
+ period = (period > 1476) ? 1476 : period;
+ List weights = new List(period);
+ BigInteger a = 0;
+ BigInteger b = 1;
+ for (int i = 0; i < period; i++) {
+ BigInteger temp = a;
+ a = b;
+ b = temp + b;
+ weights.Add((double)Decimal.Parse(a.ToString()));
+ }
+ return weights;
+ }
+
+ public override void Reset() {
+ _weights = CalculateWeights(_period);
+ _buffer.Clear();
+ }
+}
diff --git a/Calculations/_Updated/HEMA_Series.cs b/Calculations/_Updated/HEMA_Series.cs
new file mode 100644
index 00000000..84d292bf
--- /dev/null
+++ b/Calculations/_Updated/HEMA_Series.cs
@@ -0,0 +1,116 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+HEMA: Hull-EMA Moving Average - a hybrid indicator
+ Modified HUll Moving Average; instead of using WMA (Weighted MA) for calculation,
+ HEMA uses EMA for Hull's formula:
+
+EMA1 = EMA(n/2) of price - where k = 4/(n/2 +1)
+EMA2 = EMA(n) of price - where k = 3/(n+1)
+Raw HMA = (2 * EMA1) - EMA2
+EMA3 = EMA(sqrt(n)) of Raw HMA - where k = 2/(sqrt(n)+1)
+ */
+
+public class HEMA_Series : TSeries {
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+ private double _k1, _k2, _k3;
+ private int _len;
+ private double _lastema1, _oldema1;
+ private double _lastema2, _oldema2;
+ private double _lasthema, _oldhema;
+
+ //core constructors
+ public HEMA_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"HEMA({period})";
+ CalculateK(_period, out _k1, out _k2, out _k3);
+ _len = 0;
+ _lastema1 = _oldema1 = _lastema2 = _oldema2 = _lasthema = _oldhema = 0;
+ }
+ public HEMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public HEMA_Series() : this(period: 0, useNaN: false) { }
+ public HEMA_Series(int period) : this(period: period, useNaN: false) { }
+ public HEMA_Series(TBars source) : this(source.Close, 0, false) { }
+ public HEMA_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public HEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public HEMA_Series(TSeries source) : this(source, 0, false) { }
+ public HEMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ if (update) {
+ _lastema1 = _oldema1;
+ _lastema2 = _oldema2;
+ _lasthema = _oldhema;
+ }
+ else {
+ _oldema1 = _lastema1;
+ _oldema2 = _lastema2;
+ _oldhema = _lasthema;
+ }
+ double _ema1, _ema2, _hema;
+ if (_period == 0) {
+ _len++;
+ CalculateK(_len, out _k1, out _k2, out _k3);
+ }
+ if (double.IsNaN(TValue.v)) {
+ return base.Add((TValue.t, double.NaN), update);
+ } else if (this.Count == 0) {
+ _ema1 = _ema2 = _hema = TValue.v;
+ }
+ else {
+ _ema1 = _k1 * (TValue.v - _lastema1) + _lastema1;
+ _ema2 = _k2 * (TValue.v - _lastema2) + _lastema2;
+ _hema = _k3 * (((2 * _ema1) - _ema2) - _lasthema) + _lasthema;
+ }
+
+ _lastema1 = _ema1;
+ _lastema2 = _ema2;
+ _lasthema = _hema;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hema);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _lastema1 = _lastema2 = _lasthema = 0;
+ _oldema1 = _oldema2 = _oldhema = 0;
+ _len = 0;
+ }
+
+ public static void CalculateK(int len, out double k1, out double k2, out double k3) {
+ k1 = 8 / (double)(len + 7);
+ k2 = 3 / (double)(len + 2);
+ k3 = 2 / Math.Sqrt(len + 3);
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/HMA_Series.cs b/Calculations/_Updated/HMA_Series.cs
new file mode 100644
index 00000000..3631acf4
--- /dev/null
+++ b/Calculations/_Updated/HMA_Series.cs
@@ -0,0 +1,91 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+HMA: Hull Moving Average
+ Developed by Alan Hull, an extremely fast and smooth moving average; almost
+ eliminates lag altogether and manages to improve smoothing at the same time.
+
+Sources:
+ https://alanhull.com/hull-moving-average
+ https://school.stockcharts.com/doku.php?id=technical_indicators:hull_moving_average
+
+WMA1 = WMA(n/2) of price
+WMA2 = WMA(n) of price
+Raw HMA = (2 * WMA1) - WMA2
+HMA = WMA(sqrt(n)) of Raw HMA
+
+ */
+
+public class HMA_Series : TSeries {
+ protected int _period, _period2, _psqrt;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+ protected WMA_Series _wma1, _wma2, _wma3;
+
+ //core constructors
+ public HMA_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _period2 = period /2;
+ _psqrt = (int)Math.Sqrt(period);
+ _NaN = useNaN;
+ _wma1 = new(Math.Max(_period2,1), false);
+ _wma2 = new(Math.Max(_period,1), false);
+ _wma3 = new(Math.Max(_psqrt,1), useNaN);
+ Name = $"HMA({period})";
+ }
+ public HMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public HMA_Series() : this(period: 0, useNaN: false) { }
+ public HMA_Series(int period) : this(period: period, useNaN: false) { }
+ public HMA_Series(TBars source) : this(source.Close, 0, false) { }
+ public HMA_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public HMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public HMA_Series(TSeries source) : this(source, 0, false) { }
+ public HMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ if (_period == 0) {
+ _wma1.Len = this.Count / 2;
+ _wma2.Len = this.Count;
+ _wma1.Len = (int)Math.Sqrt(this.Count);
+ }
+ double _w1 = _wma1.Add(TValue, update).v;
+ double _w2 = _wma2.Add(TValue, update).v;
+ double _hma = _wma3.Add((2 * _w1) - _w2, update).v;
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hma);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _wma1.Reset();
+ _wma2.Reset();
+ _wma3.Reset();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/Trends/JMA_Series.cs b/Calculations/_Updated/JMA_Series.cs
similarity index 54%
rename from Calculations/Trends/JMA_Series.cs
rename to Calculations/_Updated/JMA_Series.cs
index f3d221a3..bfa9f445 100644
--- a/Calculations/Trends/JMA_Series.cs
+++ b/Calculations/_Updated/JMA_Series.cs
@@ -1,5 +1,6 @@
namespace QuanTAlib;
using System;
+using System.Collections.Generic;
using System.Linq;
/*
@@ -18,38 +19,51 @@ Issues:
exact - published JMA tests against JMA.CSV fail with small deviation. The
original algo is slightly different, yet this approximation is close enough.
-
-*/
-public class JMA_Series : Single_TSeries_Indicator {
+ */
+
+public class JMA_Series : TSeries {
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
private readonly System.Collections.Generic.List volty_short = new();
private readonly System.Collections.Generic.List vsum_buff = new();
private readonly double pr;
- public TSeries mma1 { get; }
- public TSeries mma2 { get; }
-
private double upperBand, lowerBand, vsum, Kv;
private double prev_ma1, prev_det0, prev_det1, prev_vsum, prev_jma;
private double p_upperBand, p_lowerBand, p_Kv, p_prev_ma1, p_prev_det0, p_prev_det1, p_prev_vsum, p_prev_jma;
private readonly int _voltyS, _voltyL;
- public JMA_Series(TSeries source, int period, double phase = 0.0, int vshort = 10, int vlong = 65, bool useNaN = false) : base(source, period, useNaN) {
+ //core constructors
+ public JMA_Series(int period, double phase, int vshort, int vlong, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"JMA({period})";
upperBand = lowerBand = prev_ma1 = prev_det0 = prev_det1 = prev_vsum = prev_jma = Kv = 0.0;
-
- Kv = 0;
-
pr = (phase * 0.01) + 1.5;
if (phase < -100) { pr = 0.5; }
if (phase > 100) { pr = 2.5; }
_voltyS = vshort;
_voltyL = vlong;
- mma1 = new();
- mma2 = new();
-
- if (base._data.Count > 0) { base.Add(base._data); }
}
- public override void Add((System.DateTime t, double v) TValue, bool update) {
- double del1 = 0.0, del2 = 0.0;
+ public JMA_Series(TSeries source, int period, double phase, int vshort, int vlong, bool useNaN) : this(period, phase, vshort, vlong, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public JMA_Series() : this(period: 0, phase: 0, vshort:10, vlong:65, useNaN: false) { }
+ public JMA_Series(int period) : this(period: period, phase: 0, vshort: 10, vlong: 65, useNaN: false) { }
+ public JMA_Series(TBars source) : this(source.Close, period:0, phase:0.0, vshort:10, vlong:65, useNaN:false) { }
+ public JMA_Series(TBars source, int period) : this(source.Close, period, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { }
+ public JMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, phase: 0.0, vshort: 10, vlong: 65, useNaN: useNaN) { }
+ public JMA_Series(TSeries source) : this(source, period:0, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { }
+ public JMA_Series(TSeries source, int period) : this(source: source, period: period, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { }
+ public JMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, phase: 0.0, vshort: 10, vlong: 65, useNaN: useNaN) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
if (this.Count == 0) { prev_ma1 = prev_jma = TValue.v; }
if (update) {
upperBand = p_upperBand;
@@ -72,9 +86,13 @@ public class JMA_Series : Single_TSeries_Indicator {
p_prev_jma = prev_jma;
}
+ if (double.IsNaN(TValue.v)) {
+ return base.Add((TValue.t, double.NaN),update);
+ }
+
// from Tvalue to volty
- del1 = TValue.v - upperBand;
- del2 = TValue.v - lowerBand;
+ double del1 = TValue.v - upperBand;
+ double del2 = TValue.v - lowerBand;
upperBand = (del1 > 0) ? TValue.v : TValue.v - (Kv * del1);
lowerBand = (del2 < 0) ? TValue.v : TValue.v - (Kv * del2);
double volty = 0;
@@ -96,7 +114,7 @@ public class JMA_Series : Single_TSeries_Indicator {
/// from avolty to rolty
double rvolty = (avolty != 0) ? volty / avolty : 0;
- double len1 = (Math.Log(Math.Sqrt(_p)) / Math.Log(2.0)) + 2;
+ double len1 = (Math.Log(Math.Sqrt(_period)) / Math.Log(2.0)) + 2;
if (len1 < 0) { len1 = 0; }
double pow1 = Math.Max(len1 - 2.0, 0.5);
@@ -105,23 +123,45 @@ public class JMA_Series : Single_TSeries_Indicator {
//// from rvolty to second smoothing
double pow2 = Math.Pow(rvolty, pow1);
- double beta = 0.45 * (_p - 1) / (0.45 * (_p - 1) + 2);
+ double beta = 0.45 * (_period - 1) / (0.45 * (_period - 1) + 2);
Kv = Math.Pow(beta, Math.Sqrt(pow2));
double alpha = Math.Pow(beta, pow2);
double ma1 = (1 - alpha) * TValue.v + alpha * prev_ma1;
prev_ma1 = ma1;
- mma1.Add(ma1);
double det0 = (1 - beta) * (TValue.v - ma1) + beta * prev_det0;
prev_det0 = det0;
double ma2 = ma1 + pr * det0;
- mma2.Add(ma2);
double det1 = ((1 - alpha) * (1 - alpha) * (ma2 - prev_jma)) + (alpha * alpha * prev_det1);
prev_det1 = det1;
double jma = prev_jma + det1;
prev_jma = jma;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : jma);
+ return base.Add(res, update);
+ }
- base.Add((TValue.t, jma), update, _NaN);
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ upperBand = lowerBand = prev_ma1 = prev_det0 = prev_det1 = prev_vsum = prev_jma = Kv = 0.0;
}
}
\ No newline at end of file
diff --git a/Calculations/_Updated/KAMA_Series.cs b/Calculations/_Updated/KAMA_Series.cs
new file mode 100644
index 00000000..87200bef
--- /dev/null
+++ b/Calculations/_Updated/KAMA_Series.cs
@@ -0,0 +1,114 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+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.
+
+ KAMA[i] = KAMA[i-1] + SC * ( price - KAMA[i-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 : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ private double _lastkama, _lastlastkama;
+ private int _len;
+ private readonly double _scFast, _scSlow;
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public KAMA_Series(int period, int fast, int slow, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ _len = 0;
+ _scFast = 2.0 / (((period < fast) ? period : fast) + 1);
+ _scSlow = 2.0 / (slow + 1);
+ _lastkama = _lastlastkama = 0;
+ Name = $"KAMA({period})";
+ }
+ public KAMA_Series(TSeries source, int period, int fast, int slow, bool useNaN) : this(period, fast, slow, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public KAMA_Series() : this(period: 0, fast: 2, slow: 30, useNaN: false) { }
+ public KAMA_Series(int period) : this(period: period, fast: 2, slow: 30, useNaN: false) { }
+ public KAMA_Series(TBars source) : this(source.Close, period: 0, fast: 2, slow: 30, useNaN: false) { }
+ public KAMA_Series(TBars source, int period) : this(source.Close, period: period, fast: 2, slow: 30, useNaN: false) { }
+ public KAMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, fast: 2, slow: 30, useNaN: useNaN) { }
+ public KAMA_Series(TSeries source) : this(source, period: 0, fast: 2, slow: 30, useNaN: false) { }
+ public KAMA_Series(TSeries source, int period) : this(source: source, period: period, fast: 2, slow: 30, useNaN: false) { }
+ public KAMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, fast: 2, slow: 30, useNaN: useNaN) { }
+ public KAMA_Series(TSeries source, int period, int fast, int slow) : this(source: source, period: period, fast: fast, slow: slow, useNaN: false) { }
+
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ if (double.IsNaN(TValue.v)) {
+ return base.Add((TValue.t, Double.NaN), update);
+ }
+
+ if (update) { _lastkama = _lastlastkama; }
+ else { _lastlastkama = _lastkama; }
+ BufferTrim(buffer: _buffer, value: TValue.v, period: _period + 1, update: update);
+
+ double _kama = 0;
+ if (this.Count < _period) { _kama = TValue.v; }
+ else {
+ double _change = Math.Abs(_buffer[^1] - _buffer[(_buffer.Count > _period + 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)));
+ }
+ _len++;
+ _lastkama = _kama;
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _kama);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ _len = 0;
+ _lastkama = _lastlastkama = 0;
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/KURTOSIS_Series.cs b/Calculations/_Updated/KURTOSIS_Series.cs
new file mode 100644
index 00000000..6c1b48c6
--- /dev/null
+++ b/Calculations/_Updated/KURTOSIS_Series.cs
@@ -0,0 +1,99 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+/*
+KURTOSIS: Kurtosis of population
+ Kurtosis characterizes the relative peakedness or flatness of a distribution
+ compared with the normal distribution. Positive kurtosis indicates a relatively
+ peaked distribution. Negative kurtosis indicates a relatively flat distribution.
+
+ The normal curve is called Mesokurtic curve. If the curve of a distribution is
+ more outlier prone (or heavier-tailed) than a normal or mesokurtic curve then
+ it is referred to as a Leptokurtic curve. If a curve is less outlier prone (or
+ lighter-tailed) than a normal curve, it is called as a platykurtic curve.
+
+Calculation:
+ sum4 = Σ(close-SMA)^4
+ sum2 = (Σ(close-SMA)^2)^2
+ KURTOSIS = length * (sum4/sum2)
+
+Sources:
+ https://en.wikipedia.org/wiki/Kurtosis
+ https://stats.oarc.ucla.edu/other/mult-pkg/faq/general/faq-whats-with-the-different-formulas-for-kurtosis/
+
+ */
+
+public class KURTOSIS_Series : TSeries {
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+ private readonly System.Collections.Generic.List _buffer = new();
+
+ //core constructors
+ public KURTOSIS_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"KURTOSIS({period})";
+ }
+ public KURTOSIS_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public KURTOSIS_Series() : this(period: 0, useNaN: false) { }
+ public KURTOSIS_Series(int period) : this(period: period, useNaN: false) { }
+ public KURTOSIS_Series(TSeries source) : this(source, period: 0, useNaN: false) { }
+ public KURTOSIS_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ if (double.IsNaN(TValue.v)) {
+ return base.Add((TValue.t, Double.NaN), update);
+ }
+
+ BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
+ double _n = _buffer.Count;
+ double _avg = _buffer.Average();
+
+ double _s2 = 0;
+ double _s4 = 0;
+ for (int i = 0; i < this._buffer.Count; i++) {
+ _s2 += (_buffer[i] - _avg) * (_buffer[i] - _avg);
+ _s4 += (_buffer[i] - _avg) * (_buffer[i] - _avg) * (_buffer[i] - _avg) * (_buffer[i] - _avg);
+ }
+
+ double _Vx = _s2 / (_n - 1);
+ double _kurt = (_n > 3) ?
+ (_n * (_n + 1) * _s4) / (_Vx * _Vx * (_n - 3) * (_n - 1) * (_n - 2)) - (3 * (_n - 1) * (_n - 1) / ((_n - 2) * (_n - 3))) //using Sheskin Algo
+ : (_s2 * _s2) / _n - 3; //using Snedecor and Cochran (1967) algo
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _kurt);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/MAD_Series.cs b/Calculations/_Updated/MAD_Series.cs
new file mode 100644
index 00000000..e1f358cd
--- /dev/null
+++ b/Calculations/_Updated/MAD_Series.cs
@@ -0,0 +1,82 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+MAD: Mean Absolute Deviation
+ Also known as AAD - Average Absolute Deviation, to differentiate it from Median Absolute Deviation
+ MAD defines the degree of variation across the series.
+
+Calculation:
+ MAD = Σ(|close-SMA|) / period
+
+Sources:
+ https://en.wikipedia.org/wiki/Average_absolute_deviation
+
+ */
+
+public class MAD_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public MAD_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"MAD({period})";
+ }
+ public MAD_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public MAD_Series() : this(period: 0, useNaN: false) { }
+ public MAD_Series(int period) : this(period: period, useNaN: false) { }
+ public MAD_Series(TBars source) : this(source.Close, 0, false) { }
+ public MAD_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public MAD_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public MAD_Series(TSeries source) : this(source, 0, false) { }
+ public MAD_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ double _sma = _buffer.Average();
+ double _mad = 0;
+ for (int i = 0; i < _buffer.Count; i++) { _mad += Math.Abs(_buffer[i] - _sma); }
+ _mad /= this._buffer.Count;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mad);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/MAPE_Series.cs b/Calculations/_Updated/MAPE_Series.cs
new file mode 100644
index 00000000..776a9a12
--- /dev/null
+++ b/Calculations/_Updated/MAPE_Series.cs
@@ -0,0 +1,88 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+MAPE: Mean Absolute Percentage Error
+ Measures the size of the error in percentage terms
+
+Calculation:
+ MAPE = Σ(|close – SMA| / |close|) / n
+
+Sources:
+ https://en.wikipedia.org/wiki/Mean_absolute_percentage_error
+
+Remark:
+ returns infinity if any of observations is 0.
+ Use SMAPE or WMAPE instead to avoid division-by-zero in MAPE
+
+ */
+
+public class MAPE_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public MAPE_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"MAPE({period})";
+ }
+ public MAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public MAPE_Series() : this(period: 0, useNaN: false) { }
+ public MAPE_Series(int period) : this(period: period, useNaN: false) { }
+ public MAPE_Series(TBars source) : this(source.Close, 0, false) { }
+ public MAPE_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public MAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public MAPE_Series(TSeries source) : this(source, 0, false) { }
+ public MAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ double _sma = _buffer.Average();
+
+ double _mape = 0;
+ for (int i = 0; i < _buffer.Count; i++) {
+ _mape += (_buffer[i] != 0) ? Math.Abs(_buffer[i] - _sma) / Math.Abs(_buffer[i]) : double.PositiveInfinity;
+ }
+ _mape /= (_buffer.Count > 0) ? _buffer.Count : 1;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mape);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/MAX_Series.cs b/Calculations/_Updated/MAX_Series.cs
new file mode 100644
index 00000000..0eae2b6f
--- /dev/null
+++ b/Calculations/_Updated/MAX_Series.cs
@@ -0,0 +1,71 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+MAX - Maximum value in the given period in the series.
+ If period = 0 => period = full length of the series
+
+ */
+
+public class MAX_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public MAX_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"MAX({period})";
+ }
+ public MAX_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public MAX_Series() : this(period: 0, useNaN: false) { }
+ public MAX_Series(int period) : this(period: period, useNaN: false) { }
+ public MAX_Series(TBars source) : this(source.Close, 0, false) { }
+ public MAX_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public MAX_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public MAX_Series(TSeries source) : this(source, 0, false) { }
+ public MAX_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ double _max= _buffer.Max();
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _max);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/MEDIAN_Series.cs b/Calculations/_Updated/MEDIAN_Series.cs
new file mode 100644
index 00000000..18e46c67
--- /dev/null
+++ b/Calculations/_Updated/MEDIAN_Series.cs
@@ -0,0 +1,89 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+MED - Median value
+ Median of numbers is the middlemost value of the given set of numbers.
+ It separates the higher half and the lower half of a given data sample.
+ At least half of the observations are smaller than or equal to median
+ and at least half of the observations are greater than or equal to the median.
+
+ If the number of values is odd, the middlemost observation of the sorted
+ list is the median of the given data. If the number of values is even,
+ median is the average of (n/2)th and [(n/2) + 1]th values of the sorted list.
+
+ If period = 0 => period is max
+
+Sources:
+ https://corporatefinanceinstitute.com/resources/knowledge/other/median/
+ https://en.wikipedia.org/wiki/Median
+
+ */
+
+public class MEDIAN_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public MEDIAN_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"MEDIAN({period})";
+ }
+ public MEDIAN_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public MEDIAN_Series() : this(period: 0, useNaN: false) { }
+ public MEDIAN_Series(int period) : this(period: period, useNaN: false) { }
+ public MEDIAN_Series(TBars source) : this(source.Close, 0, false) { }
+ public MEDIAN_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public MEDIAN_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public MEDIAN_Series(TSeries source) : this(source, 0, false) { }
+ public MEDIAN_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ System.Collections.Generic.List _s = new(this._buffer);
+ _s.Sort();
+ int _p1 = _s.Count / 2;
+ int _p2 = Math.Max(0, (_s.Count / 2) - 1);
+ double _med = (_s.Count % 2 != 0) ? _s[_p1] : (_s[_p1] + _s[_p2]) / 2;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _med);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/MIDPOINT_Series.cs b/Calculations/_Updated/MIDPOINT_Series.cs
new file mode 100644
index 00000000..686f9825
--- /dev/null
+++ b/Calculations/_Updated/MIDPOINT_Series.cs
@@ -0,0 +1,75 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+MIDPOINT: Midpoint value (max+min)/2 in the given period in the series.
+ If period = 0 => period = full length of the series
+
+Sources:
+ https://thefaqblog.com/what-is-the-midpoint-in-statistics/
+
+ */
+
+public class MIDPOINT_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public MIDPOINT_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"MIDPOINT({period})";
+ }
+ public MIDPOINT_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public MIDPOINT_Series() : this(period: 0, useNaN: false) { }
+ public MIDPOINT_Series(int period) : this(period: period, useNaN: false) { }
+ public MIDPOINT_Series(TBars source) : this(source.Close, 0, false) { }
+ public MIDPOINT_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public MIDPOINT_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public MIDPOINT_Series(TSeries source) : this(source, 0, false) { }
+ public MIDPOINT_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ double _max= _buffer.Max();
+ double _min = _buffer.Min();
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : (_max+_min)*0.5);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/MIN_Series.cs b/Calculations/_Updated/MIN_Series.cs
new file mode 100644
index 00000000..30b68fae
--- /dev/null
+++ b/Calculations/_Updated/MIN_Series.cs
@@ -0,0 +1,71 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+MIN - Minimum value in the given period in the series.
+ If period = 0 => period = full length of the series
+
+ */
+
+public class MIN_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public MIN_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"MAX({period})";
+ }
+ public MIN_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public MIN_Series() : this(period: 0, useNaN: false) { }
+ public MIN_Series(int period) : this(period: period, useNaN: false) { }
+ public MIN_Series(TBars source) : this(source.Close, 0, false) { }
+ public MIN_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public MIN_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public MIN_Series(TSeries source) : this(source, 0, false) { }
+ public MIN_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ double _max= _buffer.Min();
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _max);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/MSE_Series.cs b/Calculations/_Updated/MSE_Series.cs
new file mode 100644
index 00000000..18406554
--- /dev/null
+++ b/Calculations/_Updated/MSE_Series.cs
@@ -0,0 +1,79 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+MSE: Mean Square Error
+ Defined as a Mean (Average) of the Square of the difference between actual and estimated values.
+
+Sources:
+ https://en.wikipedia.org/wiki/Mean_squared_error
+
+ */
+
+public class MSE_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public MSE_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"MSE({period})";
+ }
+ public MSE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public MSE_Series() : this(period: 0, useNaN: false) { }
+ public MSE_Series(int period) : this(period: period, useNaN: false) { }
+ public MSE_Series(TBars source) : this(source.Close, 0, false) { }
+ public MSE_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public MSE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public MSE_Series(TSeries source) : this(source, 0, false) { }
+ public MSE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ double _sma = _buffer.Average();
+
+ double _mse = 0;
+ for (int i = 0; i < _buffer.Count; i++) { _mse += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
+ _mse /= this._buffer.Count;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mse);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/RMA_Series.cs b/Calculations/_Updated/RMA_Series.cs
new file mode 100644
index 00000000..b5be9d9c
--- /dev/null
+++ b/Calculations/_Updated/RMA_Series.cs
@@ -0,0 +1,119 @@
+namespace QuanTAlib;
+
+using System;
+using System.Linq;
+
+/*
+RMA: wildeR Moving Average
+ J. Welles Wilder introduced RMA as an alternative to EMA. RMA's weight (k) is
+ set as 1/period, giving less weight to the new data compared to EMA.
+
+Sources:
+ https://archive.org/details/newconceptsintec00wild/page/23/mode/2up
+ https://tlc.thinkorswim.com/center/reference/Tech-Indicators/studies-library/V-Z/WildersSmoothing
+ https://www.incrediblecharts.com/indicators/wilder_moving_average.php
+
+Issues:
+ Pandas-TA library calculates RMA using straight Exponential Weighted Mean:
+ pandas.ewm().mean() and returns incorrect first (period) of bars compared to
+ published formula. This implementation passess the validation test in Wilder's book.
+
+ */
+
+public class RMA_Series : TSeries {
+ private double _k;
+ private double _lastrma, _oldrma;
+ private double _sum, _oldsum;
+ private readonly bool _useSMA;
+ private int _len;
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+//core constructor
+ public RMA_Series(int period, bool useNaN, bool useSMA) : base() {
+ _period = period;
+ _NaN = useNaN;
+ _useSMA = useSMA;
+ Name = $"RMA({period})";
+ _k = 1.0 / (double)(this._period);
+ _len = 0;
+ _sum = _oldsum = _lastrma = _oldrma = 0;
+ }
+ //generic constructors (source)
+
+ public RMA_Series() : this(0, false, true) {}
+ public RMA_Series(int period) : this(period, false, true) {}
+ public RMA_Series(TBars source) : this(source.Close, 0, false) {}
+ public RMA_Series(TBars source, int period) : this(source.Close, period, false) {}
+ public RMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
+ public RMA_Series(TSeries source, int period) : this(source, period, false, true) {}
+ public RMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
+ public RMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+
+// core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
+ if (update) {
+ _lastrma = _oldrma;
+ _sum = _oldsum;
+ }
+ else {
+ _oldrma = _lastrma;
+ _oldsum = _sum;
+ _len++;
+ }
+
+ double _rma = 0;
+ if (_period == 0) {
+ _k = 1.0 / (double)(this._len);
+ }
+
+ if (Count == 0) {
+ _rma = _sum = TValue.v;
+
+ } else if (_len <= _period && _useSMA && _period != 0) {
+ _sum += TValue.v;
+ if (_period != 0 && _len > _period) {
+ _sum -= _data[Count - _period - (update ? 1 : 0)].v;
+ }
+ _rma = _sum / Math.Min(_len, _period);
+ }
+ else {
+ _rma = _k * (TValue.v - _lastrma) + _lastrma;
+ }
+
+ _lastrma = double.IsNaN(_rma) ? _lastrma : _rma;
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _rma);
+ return base.Add(res, update);
+ }
+
+//variation of Add()
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _sum = _oldsum = _lastrma = _oldrma = 0;
+ _len = 0;
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/RSI_Series.cs b/Calculations/_Updated/RSI_Series.cs
new file mode 100644
index 00000000..a1cd561e
--- /dev/null
+++ b/Calculations/_Updated/RSI_Series.cs
@@ -0,0 +1,123 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+RSI: Relative Strength Index
+ Created by J. Welles Wilder, the Relative Strength Index measures strength
+ of the winning/losing streak over N lookback periods on a scale of 0 to 100,
+ to depict overbought and oversold conditions.
+
+Sources:
+ https://www.investopedia.com/terms/r/rsi.asp
+
+ */
+
+public class RSI_Series : TSeries {
+ private readonly System.Collections.Generic.List _gain = new();
+ private readonly System.Collections.Generic.List _loss = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+ private double _avgGain, _avgLoss, _lastValue;
+ private double _avgGain_o, _avgLoss_o, _lastValue_o;
+ private int i;
+
+ //core constructors
+ public RSI_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"RSI({period})";
+ i = 0;
+ }
+ public RSI_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public RSI_Series() : this(period: 0, useNaN: false) { }
+ public RSI_Series(int period) : this(period: period, useNaN: false) { }
+ public RSI_Series(TBars source) : this(source.Close, 0, false) { }
+ public RSI_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public RSI_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public RSI_Series(TSeries source) : this(source, 0, false) { }
+ public RSI_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+
+ double _rsi = 0;
+ if (update) {
+ _lastValue = _lastValue_o;
+ _avgGain = _avgGain_o;
+ _avgLoss = _avgLoss_o;
+ }
+ else {
+ _lastValue_o = _lastValue;
+ _avgGain_o = _avgGain;
+ _avgLoss_o = _avgLoss;
+ }
+
+ if (i == 0) { _lastValue = TValue.v; }
+
+ double _gainval = (TValue.v > _lastValue) ? TValue.v - _lastValue : 0;
+ BufferTrim(_gain, _gainval, _period, update);
+ double _lossval = (TValue.v < _lastValue) ? _lastValue - TValue.v : 0;
+ BufferTrim(_loss, _lossval, _period, update);
+ _lastValue = TValue.v;
+
+ // calculate RSI
+ if (i > _period && _period != 0) {
+ _avgGain = ((_avgGain * (_period - 1)) + _gain[^1]) / _period;
+ _avgLoss = ((_avgLoss * (_period - 1)) + _loss[^1]) / _period;
+ if (_avgLoss > 0) {
+ double rs = _avgGain / _avgLoss;
+ _rsi = 100 - (100 / (1 + rs));
+ }
+ else { _rsi = 100; }
+ }
+ // initialize average gain
+ else {
+ double _sumGain = 0;
+ for (int p = 0; p < _gain.Count; p++) { _sumGain += _gain[p]; }
+ double _sumLoss = 0;
+ for (int p = 0; p < _loss.Count; p++) { _sumLoss += _loss[p]; }
+
+ _avgGain = _sumGain / _gain.Count;
+ _avgLoss = _sumLoss / _loss.Count;
+
+ _rsi = (_avgLoss > 0) ? 100 - (100 / (1 + (_avgGain / _avgLoss))) : 100;
+ }
+ if (!update) { i++; }
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _rsi);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ i = 0;
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/SDEV_Series.cs b/Calculations/_Updated/SDEV_Series.cs
new file mode 100644
index 00000000..90d269cd
--- /dev/null
+++ b/Calculations/_Updated/SDEV_Series.cs
@@ -0,0 +1,85 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+SDEV: Population Standard Deviation
+ Population Standard Deviation is the square root of the biased variance, also knons as
+ Uncorrected Sample Standard Deviation
+
+Sources:
+ https://en.wikipedia.org/wiki/Standard_deviation#Uncorrected_sample_standard_deviation
+
+Remark:
+ SDEV (Population Standard Deviation) is also known as a biased/uncorrected Standard Deviation.
+ For unbiased version that uses Bessel's correction, use SDEV instead.
+
+ */
+
+public class SDEV_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public SDEV_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"SDEV({period})";
+ }
+ public SDEV_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public SDEV_Series() : this(period: 0, useNaN: false) { }
+ public SDEV_Series(int period) : this(period: period, useNaN: false) { }
+ public SDEV_Series(TBars source) : this(source.Close, 0, false) { }
+ public SDEV_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public SDEV_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public SDEV_Series(TSeries source) : this(source, 0, false) { }
+ public SDEV_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ double _sma = _buffer.Average();
+
+ double _var = 0;
+ for (int i = 0; i < _buffer.Count; i++) { _var += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
+ _var /= this._buffer.Count;
+ double _sdev = Math.Sqrt(_var);
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sdev);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/SMAPE_Series.cs b/Calculations/_Updated/SMAPE_Series.cs
new file mode 100644
index 00000000..7ab08d6a
--- /dev/null
+++ b/Calculations/_Updated/SMAPE_Series.cs
@@ -0,0 +1,80 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+SMAPE: Symmetric Mean Absolute Percentage Error
+ Measures the size of the error in percentage terms
+
+Sources:
+ https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error
+
+ */
+
+public class SMAPE_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public SMAPE_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"SMAPE({period})";
+ }
+ public SMAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public SMAPE_Series() : this(period: 0, useNaN: false) { }
+ public SMAPE_Series(int period) : this(period: period, useNaN: false) { }
+ public SMAPE_Series(TBars source) : this(source.Close, 0, false) { }
+ public SMAPE_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public SMAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public SMAPE_Series(TSeries source) : this(source, 0, false) { }
+ public SMAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ double _sma = _buffer.Average();
+
+
+ double _smape = 0;
+ for (int i = 0; i < _buffer.Count; i++) { _smape += Math.Abs(_buffer[i] - _sma) / (Math.Abs(_buffer[i]) + Math.Abs(_sma)); }
+ _smape /= this._buffer.Count;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _smape);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/SMA_Series.cs b/Calculations/_Updated/SMA_Series.cs
new file mode 100644
index 00000000..30ccc4ba
--- /dev/null
+++ b/Calculations/_Updated/SMA_Series.cs
@@ -0,0 +1,99 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+SMA: Simple Moving Average
+ The weights are equally distributed across the period, resulting in a mean() of
+ the data within the period
+
+Sources:
+ https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/simple-moving-average-sma/
+ https://stats.stackexchange.com/a/24739
+
+Remark:
+ This calc doesn't use LINQ or SUM() or any of (slow) iterative methods. It is not as fast as TA-LIB
+ implementation, but it does allow incremental additions of inputs and real-time calculations of SMA()
+
+ */
+public class SMA_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+
+ private double _sum, _oldsum;
+ private readonly int _period;
+ private readonly TSeries _data;
+ protected readonly bool _NaN;
+
+ //core constructor
+ public SMA_Series(int period, bool useNaN) : base() {
+ _period = Math.Max(0, period);
+ _NaN = useNaN;
+ Name = $"SMA({period})";
+ _sum = _oldsum = 0;
+ }
+ public SMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public SMA_Series() : this(0, false) {}
+ public SMA_Series(int period) : this(period, false) {}
+ public SMA_Series(TBars source) : this(source.Close, 0, false) {}
+ public SMA_Series(TBars source, int period) : this(source.Close, period, false) {}
+ public SMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
+ public SMA_Series(TSeries source) : this(source, 0, false) {}
+ public SMA_Series(TSeries source, int period) : this(source, period, false) {}
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
+ if (double.IsNaN(TValue.v)) { return (TValue.t, double.NaN);
+ } else {
+ if (update && _buffer.Count > 0) {
+ _sum -= _buffer[^1];
+ _buffer[^1] = TValue.v;
+ _oldsum = _sum;
+ }
+ else {
+ _buffer.Add(TValue.v);
+ _oldsum = _sum;
+ }
+
+ _sum += TValue.v;
+ if (_period != 0 && _buffer.Count > _period) {
+ _sum -= _buffer[0];
+ _buffer.RemoveAt(0);
+ }
+ }
+
+ double _div = _period == 0 ? _buffer.Count : Math.Min(_buffer.Count, _period);
+ var _sma = _sum / _div;
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sma);
+ return base.Add(res, update);
+ }
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+
+ //reset calculation
+ public override void Reset() {
+ _sum = _oldsum = 0;
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/SMMA_Series.cs b/Calculations/_Updated/SMMA_Series.cs
new file mode 100644
index 00000000..50bed0ba
--- /dev/null
+++ b/Calculations/_Updated/SMMA_Series.cs
@@ -0,0 +1,96 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+/*
+SMMA: Smoothed Moving Average
+ The Smoothed Moving Average (SMMA) is a combination of a SMA and an EMA. It gives the recent prices
+ an equal weighting as the historic prices as it takes all available price data into account.
+ The main advantage of a smoothed moving average is that it removes short-term fluctuations.
+
+ SMMA(i) = (SMMA-1*(N-1) + CLOSE (i)) / N
+
+Sources:
+ https://blog.earn2trade.com/smoothed-moving-average
+ https://guide.traderevolution.com/traderevolution/mobile-applications/phone/android/technical-indicators/moving-averages/smma-smoothed-moving-average
+ https://www.chartmill.com/documentation/technical-analysis-indicators/217-MOVING-AVERAGES-%7C-The-Smoothed-Moving-Average-%28SMMA%29
+
+ */
+
+public class SMMA_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+ private double _lastsmma, _lastlastsmma;
+
+ //core constructors
+ public SMMA_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"SMMA({period})";
+ }
+ public SMMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public SMMA_Series() : this(period: 0, useNaN: false) { }
+ public SMMA_Series(int period) : this(period: period, useNaN: false) { }
+ public SMMA_Series(TBars source) : this(source.Close, 0, false) { }
+ public SMMA_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public SMMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public SMMA_Series(TSeries source) : this(source, 0, false) { }
+ public SMMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ if (double.IsNaN(TValue.v)) {
+ return base.Add((TValue.t, double.NaN),update);
+ }
+
+ double _smma = 0;
+ if (update) { this._lastsmma = this._lastlastsmma; }
+
+ if (this.Count < this._period) {
+ BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
+ _smma = _buffer.Average();
+ }
+ else {
+ _smma = ((_lastsmma * (_period - 1)) + TValue.v) / _period;
+ }
+
+ this._lastlastsmma = this._lastsmma;
+ this._lastsmma = _smma;
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _smma);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ this._lastsmma = this._lastlastsmma = 0;
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/SSDEV_Series.cs b/Calculations/_Updated/SSDEV_Series.cs
new file mode 100644
index 00000000..e295199a
--- /dev/null
+++ b/Calculations/_Updated/SSDEV_Series.cs
@@ -0,0 +1,85 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+SSDEV: (Corrected) Sample Standard Deviation
+ Sample Standard Deviaton uses Bessel's correction to correct the bias in the variance.
+
+Sources:
+ https://en.wikipedia.org/wiki/Standard_deviation#Corrected_sample_standard_deviation
+ Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction
+
+Remark:
+ SSDEV (Sample Standard Deviation) is also known as a unbiased/corrected Standard Deviation.
+ For a population/biased/uncorrected Standard Deviation, use PSDEV instead
+
+ */
+
+public class SSDEV_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public SSDEV_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"SSDEV({period})";
+ }
+ public SSDEV_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public SSDEV_Series() : this(period: 0, useNaN: false) { }
+ public SSDEV_Series(int period) : this(period: period, useNaN: false) { }
+ public SSDEV_Series(TBars source) : this(source.Close, 0, false) { }
+ public SSDEV_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public SSDEV_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public SSDEV_Series(TSeries source) : this(source, 0, false) { }
+ public SSDEV_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ double _sma = _buffer.Average();
+
+ double _svar = 0;
+ for (int i = 0; i < this._buffer.Count; i++) { _svar += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
+ _svar /= (_buffer.Count > 1) ? _buffer.Count - 1 : 1; // Bessel's correction
+ double _ssdev = Math.Sqrt(_svar);
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _ssdev);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/SVAR_Series.cs b/Calculations/_Updated/SVAR_Series.cs
new file mode 100644
index 00000000..4c4e0c8a
--- /dev/null
+++ b/Calculations/_Updated/SVAR_Series.cs
@@ -0,0 +1,84 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+VAR: Population Variance
+ Population variance without Bessel's correction
+
+Sources:
+ https://en.wikipedia.org/wiki/Variance
+ Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction
+
+Remark:
+ VAR (Population Variance) is also known as a biased Sample Variance. For unbiased
+ sample variance use SVAR instead.
+
+ */
+
+public class SVAR_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public SVAR_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"SVAR({period})";
+ }
+ public SVAR_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public SVAR_Series() : this(period: 0, useNaN: false) { }
+ public SVAR_Series(int period) : this(period: period, useNaN: false) { }
+ public SVAR_Series(TBars source) : this(source.Close, 0, false) { }
+ public SVAR_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public SVAR_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public SVAR_Series(TSeries source) : this(source, 0, false) { }
+ public SVAR_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ double _sma = _buffer.Average();
+
+ double _svar = 0;
+ for (int i = 0; i < this._buffer.Count; i++) { _svar += (this._buffer[i] - _sma) * (this._buffer[i] - _sma); }
+ _svar /= (this._buffer.Count > 1) ? this._buffer.Count - 1 : 1; // Bessel's correction
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _svar);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/T3_Series.cs b/Calculations/_Updated/T3_Series.cs
new file mode 100644
index 00000000..d9abbb65
--- /dev/null
+++ b/Calculations/_Updated/T3_Series.cs
@@ -0,0 +1,164 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+
+/*
+T3: Tillson T3 Moving Average
+ Tim Tillson described it in "Technical Analysis of Stocks and Commodities", January 1998 in the
+ article "Better Moving Averages". Tillson’s moving average becomes a popular indicator of
+ technical analysis as it gets less lag with the price chart and its curve is considerably smoother.
+
+Sources:
+ https://technicalindicators.net/indicators-technical-analysis/150-t3-moving-average
+ http://www.binarytribune.com/forex-trading-indicators/t3-moving-average-indicator/
+ */
+
+public class T3_Series : TSeries {
+ private readonly double _k, _k1m, _c1, _c2, _c3, _c4;
+ private readonly System.Collections.Generic.List _buffer1 = new();
+ private readonly System.Collections.Generic.List _buffer2 = new();
+ private readonly System.Collections.Generic.List _buffer3 = new();
+ private readonly System.Collections.Generic.List _buffer4 = new();
+ private readonly System.Collections.Generic.List _buffer5 = new();
+ private readonly System.Collections.Generic.List _buffer6 = new();
+ private readonly bool _useSMA;
+ private double _lastema1, _lastema2, _lastema3, _lastema4, _lastema5, _lastema6;
+ private double _llastema1, _llastema2, _llastema3, _llastema4, _llastema5, _llastema6;
+ protected int _len;
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public T3_Series(int period, double vfactor, bool useSMA, bool useNaN) : base() {
+ _period = period;
+ _len = 0;
+ _NaN = useNaN;
+ Name = $"T3({period})";
+ _useSMA = useSMA;
+ double _a = vfactor; //0.7; //0.618
+ _c1 = -_a * _a * _a;
+ _c2 = 3 * _a * _a + 3 * _a * _a * _a;
+ _c3 = -6 * _a * _a - 3 * _a - 3 * _a * _a * _a;
+ _c4 = 1 + 3 * _a + _a * _a * _a + 3 * _a * _a;
+
+ _k = 2.0 / (_period + 1);
+ _k1m = 1.0 - _k;
+ _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = _lastema4 = _llastema4 = _lastema5 = _llastema5 = _lastema5 = _llastema5 = 0;
+ }
+ public T3_Series(TSeries source, int period, double vfactor, bool useSMA, bool useNaN) : this(period, vfactor, useSMA, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public T3_Series() : this(period: 0, vfactor: 0.7, useSMA: true, useNaN: false) { }
+ public T3_Series(int period) : this(period: period, vfactor: 0.7, useSMA: true, useNaN: false) { }
+ public T3_Series(TBars source) : this(source.Close, 0, vfactor: 0.7, useSMA: true, useNaN: false) { }
+ public T3_Series(TBars source, int period) : this(source.Close, period, vfactor: 0.7, useSMA: true, useNaN: false) { }
+ public T3_Series(TBars source, int period, bool useNaN) : this(source.Close, period, vfactor: 0.7, useSMA: true, useNaN: useNaN) { }
+ public T3_Series(TBars source, int period, double vfactor, bool useNaN) : this(source.Close, period, vfactor: vfactor, useSMA: true, useNaN: useNaN) { }
+ public T3_Series(TBars source, int period, bool useSMA, bool useNaN) : this(source.Close, period, vfactor: 0.7, useSMA: useSMA, useNaN: useNaN) { }
+ public T3_Series(TSeries source) : this(source, 0, vfactor: 0.7, useSMA: true, useNaN: false) { }
+ public T3_Series(TSeries source, int period) : this(source: source, period: period, vfactor: 0.7, useSMA: true, useNaN: false) { }
+ public T3_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, vfactor: 0.7, useSMA: true, useNaN: useNaN) { }
+ public T3_Series(TSeries source, int period, double vfactor) : this(source: source, period: period, vfactor: vfactor, useSMA: true, useNaN: false) { }
+ public T3_Series(TSeries source, int period, double vfactor, bool useNaN) : this(source: source, period: period, vfactor: vfactor, useSMA: true, useNaN: useNaN) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ double _ema1, _ema2, _ema3, _ema4, _ema5, _ema6;
+ if (double.IsNaN(TValue.v)) {
+ return base.Add((TValue.t, Double.NaN),update);
+ }
+
+ if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; _lastema4 = _llastema4; _lastema5 = _llastema5; _lastema6 = _llastema6; }
+ else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; _llastema4 = _lastema4; _llastema5 = _lastema5; _llastema6 = _lastema6; }
+
+ if (_len == 0) { _lastema1 = _lastema2 = _lastema3 = _lastema4 = _lastema5 = _lastema6 = TValue.v; }
+
+
+ if ((_len < _period) && _useSMA) {
+ BufferTrim(_buffer1, TValue.v, _period, update);
+ _ema1 = 0;
+ for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; }
+ _ema1 /= _buffer1.Count;
+
+ BufferTrim(_buffer2, _ema1, _period, update);
+ _ema2 = 0;
+ for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; }
+ _ema2 /= _buffer2.Count;
+
+ BufferTrim(_buffer3, _ema2, _period, update);
+ _ema3 = 0;
+ for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; }
+ _ema3 /= _buffer3.Count;
+
+ BufferTrim(_buffer4, _ema3, _period, update);
+ _ema4 = 0;
+ for (int i = 0; i < _buffer4.Count; i++) { _ema4 += _buffer4[i]; }
+ _ema4 /= _buffer4.Count;
+
+ BufferTrim(_buffer5, _ema4, _period, update);
+ _ema5 = 0;
+ for (int i = 0; i < _buffer5.Count; i++) { _ema5 += _buffer5[i]; }
+ _ema5 /= _buffer5.Count;
+
+ BufferTrim(_buffer6, _ema5, _period, update);
+ _ema6 = 0;
+ for (int i = 0; i < _buffer6.Count; i++) { _ema6 += _buffer6[i]; }
+ _ema6 /= _buffer6.Count;
+ }
+ else {
+ _ema1 = (TValue.v * this._k) + (this._lastema1 * this._k1m);
+ _ema2 = (_ema1 * this._k) + (this._lastema2 * this._k1m);
+ _ema3 = (_ema2 * this._k) + (this._lastema3 * this._k1m);
+ _ema4 = (_ema3 * this._k) + (this._lastema4 * this._k1m);
+ _ema5 = (_ema4 * this._k) + (this._lastema5 * this._k1m);
+ _ema6 = (_ema5 * this._k) + (this._lastema6 * this._k1m);
+ }
+ _len++;
+ _lastema1 = _ema1;
+ _lastema2 = _ema2;
+ _lastema3 = _ema3;
+ _lastema4 = _ema4;
+ _lastema5 = _ema5;
+ _lastema6 = _ema6;
+
+ double _T3 = _c1 * _ema6 + _c2 * _ema5 + _c3 * _ema4 + _c4 * _ema3;
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _T3);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = _lastema4 = _llastema4 = _lastema5 = _llastema5 = _lastema5 = _llastema5 = 0;
+ _buffer1.Clear();
+ _buffer2.Clear();
+ _buffer3.Clear();
+ _buffer4.Clear();
+ _buffer5.Clear();
+ _buffer6.Clear();
+ _len = 0;
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/TBars.cs b/Calculations/_Updated/TBars.cs
new file mode 100644
index 00000000..8387219d
--- /dev/null
+++ b/Calculations/_Updated/TBars.cs
@@ -0,0 +1,126 @@
+namespace QuanTAlib;
+using System;
+
+/*
+TBars class - includes all series for common data used in indicators and other calculations.
+ Has a bit limited overloading and casting (compared to TSeries)
+ Includes Select(int) method to simplify choosing the most optimal data source for indicators
+ Includes the most basic pricing calcs: HL2, OC2, OHL3, HLC3, OHLC4, HLCC4
+ (it is 'cheaper' to calculate them once during data capture than each time during data analysis)
+
+ */
+
+public class TBars : System.Collections.Generic.List<(DateTime t, double o, double h, double l, double c, double v)>
+{
+ public string Name { get; set; }
+ private readonly TSeries _open = new("open");
+ private readonly TSeries _high = new("high");
+ private readonly TSeries _low = new("low");
+ private readonly TSeries _close = new("close");
+ private readonly TSeries _volume = new("volume");
+ private readonly TSeries _hl2 = new("HL2");
+ private readonly TSeries _oc2 = new("OC2");
+ private readonly TSeries _ohl3 = new("OHL3");
+ private readonly TSeries _hlc3 = new("HLC3");
+ private readonly TSeries _ohlc4 = new("OHLC4");
+ private readonly TSeries _hlcc4 = new("HLCC4");
+
+ public TSeries Open => this._open;
+ public TSeries High => this._high;
+ public TSeries Low => this._low;
+ public TSeries Close => this._close;
+ public TSeries Volume => this._volume;
+ public TSeries HL2 => this._hl2;
+ public TSeries OC2 => this._oc2;
+ public TSeries OHL3 => this._ohl3;
+ public TSeries HLC3 => this._hlc3;
+ public TSeries OHLC4 => this._ohlc4;
+ public TSeries HLCC4 => this._hlcc4;
+
+ public TBars() { }
+
+ public TBars(string Name) {
+ this.Name = Name;
+ }
+
+ public (DateTime t, double o, double h, double l, double c, double v) Last => this[^1];
+ public TBars Tail(int count = 10)
+ {
+ TBars outBars = new();
+ if (count > this.Count) { count = this.Count; }
+ for (int i = this.Count - count; i < this.Count; i++) { outBars.Add(this[i]); }
+ return outBars;
+ }
+ public TSeries Select(int source)
+ {
+ return source switch
+ {
+ 0 => _open,
+ 1 => _high,
+ 2 => _low,
+ 3 => _close,
+ 4 => _hl2,
+ 5 => _oc2,
+ 6 => _ohl3,
+ 7 => _hlc3,
+ 8 => _ohlc4,
+ _ => _hlcc4,
+ };
+ }
+ public static string SelectStr(int source)
+ {
+ return source switch
+ {
+ 0 => "Open",
+ 1 => "High",
+ 2 => "Low",
+ 3 => "Close",
+ 4 => "HL2",
+ 5 => "OC2",
+ 6 => "OHL3",
+ 7 => "HLC3",
+ 8 => "OHLC4",
+ _ => "HLCC4",
+ };
+ }
+
+ public virtual (DateTime t, double o, double h, double l, double c, double v) Add((double o, double h, double l, double c, double v) p, bool update = false) =>
+ Add((t: (this.Count == 0) ? DateTime.Today : this[^1].t.AddDays(1),p.o,p.h,p.l,p.c,p.v),update);
+
+ public virtual (DateTime t, double o, double h, double l, double c, double v) Add(double o, double h, double l, double c, double v, bool update = false) =>
+ Add((o,h,l,c,v),update);
+
+ public virtual (DateTime t, double o, double h, double l, double c, double v) Add(DateTime t, double o, double h, double l, double c, double v, bool update = false) =>
+ this.Add((t, o, h, l, c, v), update);
+
+ public virtual (DateTime t, double o, double h, double l, double c, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) {
+ if (update) { this[^1] = TBar; } else { base.Add(TBar); }
+
+ _open.Add((TBar.t, TBar.o), update);
+ _high.Add((TBar.t, TBar.h), update);
+ _low.Add((TBar.t, TBar.l), update);
+ _close.Add((TBar.t, TBar.c), update);
+ _volume.Add((TBar.t, TBar.v), update);
+ _hl2.Add((TBar.t, (TBar.h + TBar.l) * 0.5), update);
+ _oc2.Add((TBar.t, (TBar.o + TBar.c) * 0.5), update);
+ _ohl3.Add((TBar.t, (TBar.o + TBar.h + TBar.l) * 0.333333333333333), update);
+ _hlc3.Add((TBar.t, (TBar.h + TBar.l + TBar.c) * 0.333333333333333), update);
+ _ohlc4.Add((TBar.t, (TBar.o + TBar.h + TBar.l + TBar.c) * 0.25), update);
+ _hlcc4.Add((TBar.t, (TBar.h + TBar.l + TBar.c + TBar.c) * 0.25), update);
+
+ this.OnEvent(update);
+ return TBar;
+ }
+
+ public delegate void NewDataEventHandler(object source, TSeriesEventArgs args);
+ public event NewDataEventHandler Pub;
+ protected virtual void OnEvent(bool update = false) { if (Pub != null && Pub.Target != this) {
+ Pub(this, new TSeriesEventArgs { update = update }); } }
+
+ public void Sub(object source, TSeriesEventArgs e) { TBars ss = (TBars)source; if (ss.Count > 1) {
+ for (int i = 0; i < ss.Count; i++) { this.Add(ss[i]); }
+ } else {
+ this.Add(ss[ss.Count - 1], e.update);
+ }
+ }
+}
diff --git a/Calculations/_Updated/TEMA_Series.cs b/Calculations/_Updated/TEMA_Series.cs
new file mode 100644
index 00000000..b109995b
--- /dev/null
+++ b/Calculations/_Updated/TEMA_Series.cs
@@ -0,0 +1,123 @@
+namespace QuanTAlib;
+
+using System;
+using System.Linq;
+
+/*
+TEMA: Triple Exponential Moving Average
+ TEMA uses EMA(EMA(EMA())) to calculate less laggy Exponential moving average.
+
+Sources:
+ https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triple-exponential-moving-average-tema/
+
+Remark:
+ ema1 = EMA(close, length)
+ ema2 = EMA(ema1, length)
+ ema3 = EMA(ema2, length)
+ TEMA = 3 * (ema1 - ema2) + ema3
+
+ */
+
+public class TEMA_Series : TSeries {
+ private double _k;
+ private double _sum, _oldsum;
+ private double _lastema1, _oldema1, _lastema2, _oldema2, _lastema3, _oldema3;
+ private int _len;
+ private readonly bool _useSMA;
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+//core constructor
+ public TEMA_Series(int period, bool useNaN, bool useSMA) : base() {
+ _period = period;
+ _NaN = useNaN;
+ _useSMA = useSMA;
+ Name = $"TEMA({period})";
+ _k = 2.0 / (_period + 1);
+ _len = 0;
+ _sum = _oldsum = _lastema1 = _lastema2 = _lastema3 = 0;
+ }
+ public TEMA_Series() : this(0, false, true) {}
+ public TEMA_Series(int period) : this(period, false, true) {}
+ public TEMA_Series(TBars source) : this(source.Close, 0, false) {}
+ public TEMA_Series(TBars source, int period) : this(source.Close, period, false) {}
+ public TEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
+ public TEMA_Series(TSeries source, int period) : this(source, period, false, true) {}
+ public TEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
+ public TEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+
+// core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ if (update) {
+ _lastema1 = _oldema1;
+ _lastema2 = _oldema2;
+ _lastema3 = _oldema3;
+ _sum = _oldsum;
+ }
+ else {
+ _oldema1 = _lastema1;
+ _oldema2 = _lastema2;
+ _oldema3 = _lastema3;
+ _oldsum = _sum;
+ _len++;
+ }
+
+ if (_period == 0) { _k = 2.0 / (_len + 1); }
+
+ double _ema1, _ema2, _ema3, _tema;
+ if (this.Count == 0) {
+ _ema1 = _ema2 = _ema3 =_sum = TValue.v;
+ }
+ else if (_len <= _period && _useSMA && _period != 0) {
+ _sum += TValue.v;
+ _ema1 = _sum / Math.Min(_len, _period);
+ _ema2 = _ema1;
+ _ema3 = _ema2;
+ }
+ else {
+ _ema1 = (TValue.v - _lastema1) * _k + _lastema1;
+ _ema2 = (_ema1 - _lastema2) * _k + _lastema2;
+ _ema3 = (_ema2 - _lastema3) * _k + _lastema3;
+ }
+
+ _tema = (3 * (_ema1 - _ema2)) + _ema3;
+
+ _lastema1 = Double.IsNaN(_ema1)?_lastema1:_ema1;
+ _lastema2 = Double.IsNaN(_ema2)?_lastema2:_ema2;
+ _lastema3 = Double.IsNaN(_ema3) ? _lastema3 : _ema3;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _tema);
+ return base.Add(res, update);
+ }
+
+//variation of Add()
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _sum = _oldsum = _lastema1 = _lastema2 = 0;
+ _len = 0;
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/TRIMA_Series.cs b/Calculations/_Updated/TRIMA_Series.cs
new file mode 100644
index 00000000..70313693
--- /dev/null
+++ b/Calculations/_Updated/TRIMA_Series.cs
@@ -0,0 +1,87 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+TRIMA: Triangular Moving Average
+ A weighted moving average where the shape of the weights are triangular and the greatest
+ weight is in the middle of the period,
+
+Sources:
+ https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triangular-moving-average-trima/
+
+Remark:
+ trima = sma(sma(signal, n/2), n/2)
+
+ */
+
+public class TRIMA_Series : TSeries {
+ private readonly int _p1a, _p1b;
+ private SMA_Series sma, trima;
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public TRIMA_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"xMA({period})";
+ _p1a = (int)Math.Floor((period * 0.5) + 1);
+ _p1b = (int)Math.Ceiling(0.5 * period);
+ sma = new(_p1a);
+ trima = new(_p1b);
+
+ }
+ public TRIMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public TRIMA_Series() : this(period: 0, useNaN: false) { }
+ public TRIMA_Series(int period) : this(period: period, useNaN: false) { }
+ public TRIMA_Series(TBars source) : this(source.Close, 0, false) { }
+ public TRIMA_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public TRIMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public TRIMA_Series(TSeries source) : this(source, 0, false) { }
+ public TRIMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ if (double.IsNaN(TValue.v)) {
+ return base.Add((TValue.t, Double.NaN), update);
+ }
+
+ var _sma = sma.Add(TValue, update);
+ var _trima = trima.Add(_sma, update);
+
+ var res = (_trima.t, Count < _period - 1 && _NaN ? double.NaN : _trima.v);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ sma.Reset();
+ trima.Reset();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/TRIX_Series.cs b/Calculations/_Updated/TRIX_Series.cs
new file mode 100644
index 00000000..243dba19
--- /dev/null
+++ b/Calculations/_Updated/TRIX_Series.cs
@@ -0,0 +1,119 @@
+namespace QuanTAlib;
+
+using System;
+using System.Linq;
+
+/*
+TRIX: Triple Exponential Average Oscillator
+ Developed by Jack Hutson in the early 1980s, the triple exponential average (TRIX)
+ has become a popular technical analysis tool to aid chartists in spotting diversions
+ and directional cues in stock trading patterns.
+
+Sources:
+ https://www.investopedia.com/terms/t/trix.asp
+
+ */
+
+public class TRIX_Series : TSeries {
+ private readonly double _k;
+ private readonly System.Collections.Generic.List _buffer1 = new();
+ private readonly System.Collections.Generic.List _buffer2 = new();
+ private readonly System.Collections.Generic.List _buffer3 = new();
+ private double _lastema1, _lastema2, _lastema3;
+ private double _llastema1, _llastema2, _llastema3;
+
+ private readonly bool _useSMA;
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+//core constructors
+
+ public TRIX_Series(int period, bool useNaN, bool useSMA) : base() {
+ _period = period;
+ _NaN = useNaN;
+ _useSMA = useSMA;
+ Name = $"TRIX({period})";
+ _k = 2.0 / (_period + 1);
+ _lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = 0;
+ }
+ public TRIX_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public TRIX_Series() : this(0, false, true) {}
+ public TRIX_Series(int period) : this(period, false, true) {}
+ public TRIX_Series(TBars source) : this(source.Close, 0, false) {}
+ public TRIX_Series(TBars source, int period) : this(source.Close, period, false) {}
+ public TRIX_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
+ public TRIX_Series(TSeries source, int period) : this(source, period, false, true) {}
+ public TRIX_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
+
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
+ if (double.IsNaN(TValue.v)) {
+ return base.Add((TValue.t, Double.NaN), update);
+ }
+ if (this.Count == 0) { _lastema1 = _lastema2 = _lastema3 = TValue.v; }
+ if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; }
+ else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; }
+
+ double _ema1, _ema2, _ema3;
+ if ((this.Count < _period) && _useSMA) {
+ BufferTrim(_buffer1, TValue.v, _period, update);
+ _ema1 = 0;
+ for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; }
+ _ema1 /= _buffer1.Count;
+
+ BufferTrim(_buffer2, _ema1, _period, update);
+ _ema2 = 0;
+ for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; }
+ _ema2 /= _buffer2.Count;
+
+ BufferTrim(_buffer3, _ema2, _period, update);
+ _ema3 = 0;
+ for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; }
+ _ema3 /= _buffer3.Count;
+ }
+ else {
+ _ema1 = (TValue.v - _lastema1) * _k + _lastema1;
+ _ema2 = (_ema1 - _lastema2) * _k + _lastema2;
+ _ema3 = (_ema2 - _lastema3) * _k + _lastema3;
+ }
+ double _trix = 100 * (_ema3 - _lastema3) / _lastema3;
+ _lastema1 = _ema1;
+ _lastema2 = _ema2;
+ _lastema3 = _ema3;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _trix);
+ return base.Add(res, update);
+ }
+
+//variation of Add()
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/TSeries.cs b/Calculations/_Updated/TSeries.cs
new file mode 100644
index 00000000..65c09886
--- /dev/null
+++ b/Calculations/_Updated/TSeries.cs
@@ -0,0 +1,85 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Data;
+using System.Linq;
+
+/*
+TSeries is the cornerstone of all QuanTAlib classes.
+ TSeries is a single List of tuples (time, value) and contains several operators, casts, overloads
+ and other helpers that simplify usage of library.
+ Think of TSeries as an equivalent of Numpy array.
+
+ - includes Length property (to mimic array's method)
+ - includes publishing and subscribing methods that attach to events
+
+ */
+public class TSeriesEventArgs : EventArgs {
+ public bool update { get; set; }
+}
+
+public class TSeries : List<(DateTime t, double v)> {
+ public List t => this.Select(item => item.t).ToList();
+ public List v => this.Select(item => item.v).ToList();
+ public (DateTime t, double v) Last => this[^1];
+ public int Length => Count;
+ public string Name { get; set; }
+
+ public TSeries() {
+ this.Name = "data";
+ }
+
+ public TSeries(string Name) {
+ this.Name = Name;
+ }
+
+ public virtual (DateTime t, double v) Add(double v, bool update = false) {
+ var Value = (t: Count == 0 ? DateTime.Today : this[^1].t.AddDays(1), v);
+ return Add(Value, update);
+ }
+
+ public virtual (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ if (update) {
+ this[^1] = TValue;
+ }
+ else {
+ base.Add(TValue);
+ }
+
+ OnEvent(update);
+ return TValue;
+ }
+
+ public virtual (DateTime t, double v) Add(TSeries data) {
+ foreach (var item in data) { Add(item, false); }
+ return data.Last;
+ }
+
+ public void Sub(object source, TSeriesEventArgs e) {
+ var data = (TSeries) source;
+ if (data == null) { return; }
+ foreach (var item in data) { Add(item, update: false); }
+ }
+
+ public delegate void NewEventHandler(object source, TSeriesEventArgs args);
+
+ public event NewEventHandler Pub;
+
+ protected virtual void OnEvent(bool update = false)
+ {
+ Pub?.Invoke(this, new TSeriesEventArgs {update = update});
+ }
+
+ /// common helpers
+ public static void BufferTrim(List buffer, double value, int period, bool update) {
+ if (!update) {
+ buffer.Add(value);
+ if (buffer.Count > period && period > 0) { buffer.RemoveAt(0); }
+ return;
+ }
+ buffer[^1] = value;
+ }
+ public virtual void Reset() {
+ }
+}
diff --git a/Calculations/_Updated/VAR_Series.cs b/Calculations/_Updated/VAR_Series.cs
new file mode 100644
index 00000000..97ad50c3
--- /dev/null
+++ b/Calculations/_Updated/VAR_Series.cs
@@ -0,0 +1,84 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+VAR: Population Variance
+ Population variance without Bessel's correction
+
+Sources:
+ https://en.wikipedia.org/wiki/Variance
+ Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction
+
+Remark:
+ VAR (Population Variance) is also known as a biased Sample Variance. For unbiased
+ sample variance use SVAR instead.
+
+ */
+
+public class VAR_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public VAR_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"VAR({period})";
+ }
+ public VAR_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public VAR_Series() : this(period: 0, useNaN: false) { }
+ public VAR_Series(int period) : this(period: period, useNaN: false) { }
+ public VAR_Series(TBars source) : this(source.Close, 0, false) { }
+ public VAR_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public VAR_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public VAR_Series(TSeries source) : this(source, 0, false) { }
+ public VAR_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ double _sma = _buffer.Average();
+
+ double _pvar = 0;
+ for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
+ _pvar /= this._buffer.Count;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _pvar);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/WMAPE_Series.cs b/Calculations/_Updated/WMAPE_Series.cs
new file mode 100644
index 00000000..b7254958
--- /dev/null
+++ b/Calculations/_Updated/WMAPE_Series.cs
@@ -0,0 +1,85 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+WMAPE: Weighted Mean Absolute Percentage Error
+ Measures the size of the error in percentage terms. Improves problems with MAPE
+ when there are zero or close-to-zero values because there would be a division by zero
+ or values of MAPE tending to infinity.
+
+Sources:
+ https://en.wikipedia.org/wiki/WMAPE
+
+ */
+
+public class WMAPE_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public WMAPE_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"WMAPE({period})";
+ }
+ public WMAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public WMAPE_Series() : this(period: 0, useNaN: false) { }
+ public WMAPE_Series(int period) : this(period: period, useNaN: false) { }
+ public WMAPE_Series(TBars source) : this(source.Close, 0, false) { }
+ public WMAPE_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public WMAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public WMAPE_Series(TSeries source) : this(source, 0, false) { }
+ public WMAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ double _sma = _buffer.Average();
+
+ double _div = 0;
+ double _wmape = 0;
+ for (int i = 0; i < _buffer.Count; i++) {
+ _wmape += Math.Abs(_buffer[i] - _sma);
+ _div += Math.Abs(_buffer[i]);
+ }
+ _wmape = (_div != 0) ? _wmape / _div : double.PositiveInfinity;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _wmape);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/WMA_Series.cs b/Calculations/_Updated/WMA_Series.cs
new file mode 100644
index 00000000..f8d8bb39
--- /dev/null
+++ b/Calculations/_Updated/WMA_Series.cs
@@ -0,0 +1,107 @@
+namespace QuanTAlib;
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+/*
+WMA: (linearly) Weighted Moving Average
+ The weights are linearly decreasing over the period and the most recent data has
+ the heaviest weight.
+
+Sources:
+ https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/weighted-moving-average-wma/
+ https://www.technicalindicators.net/indicators-technical-analysis/83-moving-averages-simple-exponential-weighted
+
+ */
+
+public class WMA_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ private System.Collections.Generic.List _weights = new();
+ protected int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+ protected int _len;
+ public int Len {
+ get { return _len; }
+ set { _len = value; }
+ }
+
+ //core constructors
+ public WMA_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"WMA({period})";
+ _len = 1;
+ _weights = CalculateWeights(_period);
+ }
+ public WMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public WMA_Series() : this(period: 0, useNaN: false) { }
+ public WMA_Series(int period) : this(period: period, useNaN: false) { }
+ public WMA_Series(TBars source) : this(source.Close, 0, false) { }
+ public WMA_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public WMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public WMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update=false) {
+ BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
+ if (_period == 0) {
+ _weights = CalculateWeights(_len);
+ _len++;
+ }
+ double _wma = 0;
+ double totalWeights = (_buffer.Count * (_buffer.Count + 1)) * 0.5;
+ object lockObj = new object();
+ Parallel.For(0, _buffer.Count, i =>
+ {
+ double temp = _buffer[i] * this._weights[i];
+ lock (lockObj) { _wma += temp; }
+ });
+ _wma /= totalWeights;
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _wma);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //calculating weights
+ private static List CalculateWeights(int period) {
+ List weights = new List(period);
+ for (int i = 0; i < period; i++) {
+ weights.Add(i + 1);
+ }
+ return weights;
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _len = 0;
+ _weights = CalculateWeights(_period);
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/ZLEMA_Series.cs b/Calculations/_Updated/ZLEMA_Series.cs
new file mode 100644
index 00000000..81ad2d67
--- /dev/null
+++ b/Calculations/_Updated/ZLEMA_Series.cs
@@ -0,0 +1,97 @@
+namespace QuanTAlib;
+
+using System;
+using System.Linq;
+
+/*
+ZLEMA: Zero Lag Exponential Moving Average
+ The Zero lag exponential moving average (ZLEMA) indicator was created by John
+ Ehlers and Ric Way.
+
+The formula for a given N-Day period and for a given Data series is:
+ Lag = (Period-1)/2
+ Ema Data = {Data+(Data-Data(Lag days ago))
+ ZLEMA = EMA (EmaData,Period)
+
+Remark:
+ The idea is do a regular exponential moving average (EMA) calculation but on a
+ de-lagged data instead of doing it on the regular data. Data is de-lagged by
+ removing the data from "lag" days ago thus removing (or attempting to remove)
+ the cumulative lag effect of the moving average.
+
+ */
+
+public class ZLEMA_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ private int _len;
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+ private readonly EMA_Series _ema;
+
+ //core constructor
+ public ZLEMA_Series(int period, bool useNaN, bool useSMA) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"ZLEMA({period})";
+ _len = 1;
+ _ema = new(period);
+ }
+ //generic constructors (source)
+
+ public ZLEMA_Series() : this(0, false, true) { }
+ public ZLEMA_Series(int period) : this(period, false, true) { }
+ public ZLEMA_Series(TBars source) : this(source.Close, 0, false) { }
+ public ZLEMA_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public ZLEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public ZLEMA_Series(TSeries source, int period) : this(source, period, false, true) { }
+ public ZLEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { }
+ public ZLEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
+ BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
+ int _lag;
+ if (_period == 0) {
+ _lag = (int)((_len - 1) * 0.5);
+ _len++;
+ }
+ else { _lag = (int)((_period - 1) * 0.5); }
+ _lag = Math.Min(_lag, _buffer.Count - 1);
+ _lag = Math.Max(_lag, 0) + 1;
+ double _zlValue = 2 * TValue.v - _buffer[^_lag];
+ double _zlema = _ema.Add((TValue.t, _zlValue), update).v;
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zlema);
+ return base.Add(res, update);
+ }
+
+ //variation of Add()
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ _ema.Reset();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/ZL_Series.cs b/Calculations/_Updated/ZL_Series.cs
new file mode 100644
index 00000000..cfb71d20
--- /dev/null
+++ b/Calculations/_Updated/ZL_Series.cs
@@ -0,0 +1,93 @@
+namespace QuanTAlib;
+
+using System;
+using System.Linq;
+
+/*
+ZL: Zero Lag
+ Data is de-lagged by removing the data from “lag” days ago, thus removing
+ (or attempting to) the cumulative effect of the moving average.
+
+Calculation:
+ Lag = (Period-1)/2
+ ZL = Data + (Data - Data(Lag days ago) )
+
+Sources:
+ https://mudrex.com/blog/zero-lag-ema-trading-strategy/
+
+ */
+
+public class ZL_Series: TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ private int _len;
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+ private readonly EMA_Series _ema;
+
+ //core constructor
+ public ZL_Series(int period, bool useNaN, bool useSMA) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"ZL({period})";
+ _len = 1;
+ _ema = new(period);
+ }
+ //generic constructors (source)
+
+ public ZL_Series() : this(0, false, true) { }
+ public ZL_Series(int period) : this(period, false, true) { }
+ public ZL_Series(TBars source) : this(source.Close, 0, false) { }
+ public ZL_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public ZL_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public ZL_Series(TSeries source, int period) : this(source, period, false, true) { }
+ public ZL_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { }
+ public ZL_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
+ BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
+ int _lag;
+ if (_period == 0) {
+ _lag = (int)((_len - 1) * 0.5);
+ _len++;
+ }
+ else { _lag = (int)((_period - 1) * 0.5); }
+ _lag = Math.Min(_lag, _buffer.Count - 1);
+ _lag = Math.Max(_lag, 0) + 1;
+ double _zlValue = 2 * TValue.v - _buffer[^_lag];
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zlValue);
+ return base.Add(res, update);
+ }
+
+ //variation of Add()
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ _ema.Reset();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/ZSCORE_Series.cs b/Calculations/_Updated/ZSCORE_Series.cs
new file mode 100644
index 00000000..8b213c04
--- /dev/null
+++ b/Calculations/_Updated/ZSCORE_Series.cs
@@ -0,0 +1,91 @@
+using System.Linq;
+
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+ZSCORE: number of standard deviations from SMA
+ Z-score describes a value's relationship to the mean of a series, as measured in
+ terms of standard deviations from the mean. If a Z-score is 0, it indicates that
+ the data point's score is identical to the mean score. A Z-score of 1.0 would
+ indicate a value that is one standard deviation from the mean. Z-scores may be
+ positive or negative, with a positive value indicating the score is above the
+ mean and a negative score indicating it is below the mean.
+
+Sources:
+ https://en.wikipedia.org/wiki/Z-score
+ https://www.investopedia.com/terms/z/zscore.asp
+
+Calculation:
+ std = std * STDEV(close, length)
+ mean = SMA(close, length)
+ ZSCORE = (close - mean) / std
+
+ */
+
+public class ZSCORE_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public ZSCORE_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"ZSCORE({period})";
+ }
+ public ZSCORE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public ZSCORE_Series() : this(period: 0, useNaN: false) { }
+ public ZSCORE_Series(int period) : this(period: period, useNaN: false) { }
+ public ZSCORE_Series(TBars source) : this(source.Close, 0, false) { }
+ public ZSCORE_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public ZSCORE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public ZSCORE_Series(TSeries source) : this(source, 0, false) { }
+ public ZSCORE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+ double _sma = _buffer.Average();
+
+ double _pvar = 0;
+ for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
+ _pvar /= this._buffer.Count;
+ double _psdev = Math.Sqrt(_pvar);
+ double _zscore = (_psdev == 0) ? 1 : (TValue.v - _sma) / _psdev;
+
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zscore);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Calculations/_Updated/zMA_Series.cs b/Calculations/_Updated/zMA_Series.cs
new file mode 100644
index 00000000..c37a41e5
--- /dev/null
+++ b/Calculations/_Updated/zMA_Series.cs
@@ -0,0 +1,72 @@
+namespace QuanTAlib;
+using System;
+using System.Collections.Generic;
+
+/*
+
+ */
+
+public class xMA_Series : TSeries {
+ private readonly System.Collections.Generic.List _buffer = new();
+
+ protected readonly int _period;
+ protected readonly bool _NaN;
+ protected readonly TSeries _data;
+
+ //core constructors
+ public xMA_Series(int period, bool useNaN) : base() {
+ _period = period;
+ _NaN = useNaN;
+ Name = $"xMA({period})";
+ }
+ public xMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
+ _data = source;
+ Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
+ _data.Pub += Sub;
+ Add(_data);
+ }
+ public xMA_Series() : this(period: 0, useNaN: false) { }
+ public xMA_Series(int period) : this(period: period, useNaN: false) { }
+ public xMA_Series(TBars source) : this(source.Close, 0, false) { }
+ public xMA_Series(TBars source, int period) : this(source.Close, period, false) { }
+ public xMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
+ public xMA_Series(TSeries source) : this(source, 0, false) { }
+ public xMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
+
+ //////////////////
+ // core Add() algo
+ public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
+ if (double.IsNaN(TValue.v)) {
+ return base.Add((TValue.t, Double.NaN), update);
+ }
+
+ BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
+
+ double _xma = 0;
+ var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _xma);
+ return base.Add(res, update);
+ }
+
+ public override (DateTime t, double v) Add(TSeries data) {
+ if (data == null) { return (DateTime.Today, Double.NaN); }
+ foreach (var item in data) { Add(item, false); }
+ return _data.Last;
+ }
+ public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
+ return Add(TValue, false);
+ }
+ public (DateTime t, double v) Add(bool update) {
+ return this.Add(TValue: _data.Last, update: update);
+ }
+ public (DateTime t, double v) Add() {
+ return Add(TValue: _data.Last, update: false);
+ }
+ private new void Sub(object source, TSeriesEventArgs e) {
+ Add(TValue: _data.Last, update: e.update);
+ }
+
+ //reset calculation
+ public override void Reset() {
+ _buffer.Clear();
+ }
+}
\ No newline at end of file
diff --git a/Indicators/Charts/2MACross_chart.cs b/Indicators/Charts/2MACross_chart.cs
index 279dab74..2bc81b8e 100644
--- a/Indicators/Charts/2MACross_chart.cs
+++ b/Indicators/Charts/2MACross_chart.cs
@@ -7,7 +7,7 @@ namespace QuanTAlib;
public class MovingAverage_chart : Indicator {
#region Parameters
[InputParameter("MA1: Type:", 0, variants: new object[]
- { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FMA", 7, "DEMA", 8, "TEMA", 9,
+ { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9,
"ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})]
private int MA1type = 15;
@@ -20,7 +20,7 @@ public class MovingAverage_chart : Indicator {
private int MA1DataSource = 3;
[InputParameter("MA2: Type:", 3, variants: new object[]
- { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FMA", 7, "DEMA", 8, "TEMA", 9,
+ { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9,
"ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})]
private int MA2type = 16;
@@ -97,8 +97,8 @@ public class MovingAverage_chart : Indicator {
this.Name += $"DWMA";
break;
case 7:
- MA1 = new FMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period);
- this.Name += $"FMA";
+ MA1 = new FWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period);
+ this.Name += $"FWMA";
break;
case 8:
MA1 = new DEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false);
@@ -171,8 +171,8 @@ public class MovingAverage_chart : Indicator {
this.Name += $"DWMA";
break;
case 7:
- MA2 = new FMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period);
- this.Name += $"FMA";
+ MA2 = new FWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period);
+ this.Name += $"FWMA";
break;
case 8:
MA2 = new DEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false);
diff --git a/Indicators/Charts/2MASlope_chart.cs b/Indicators/Charts/2MASlope_chart.cs
index 2c52110e..360af677 100644
--- a/Indicators/Charts/2MASlope_chart.cs
+++ b/Indicators/Charts/2MASlope_chart.cs
@@ -7,7 +7,7 @@ namespace QuanTAlib;
public class MovingAverageSlope_chart : Indicator {
#region Parameters
[InputParameter("MA1: Type:", 0, variants: new object[]
- { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FMA", 7, "DEMA", 8, "TEMA", 9,
+ { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9,
"ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})]
private int MA1type = 16;
@@ -20,7 +20,7 @@ public class MovingAverageSlope_chart : Indicator {
private int MA1DataSource = 3;
[InputParameter("MA2: Type:", 3, variants: new object[]
- { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FMA", 7, "DEMA", 8, "TEMA", 9,
+ { "SMA", 0, "EMA", 1, "WMA", 2, "T3", 3, "SMMA", 4, "TRIMA", 5, "DWMA", 6, "FWMA", 7, "DEMA", 8, "TEMA", 9,
"ALMA", 10, "HMA", 11, "HEMA", 12, "MAMA", 13, "KAMA", 14, "ZLEMA", 15, "JMA", 16})]
private int MA2type = 6;
@@ -50,6 +50,8 @@ public class MovingAverageSlope_chart : Indicator {
private TSeries MA1, MA2;
private LINREG_Series sMA1, sMA2;
private CROSS_Series sig1, sig2;
+
+ private bool inLong, inShort;
///////
public MovingAverageSlope_chart() {
@@ -99,8 +101,8 @@ public class MovingAverageSlope_chart : Indicator {
this.Name += $"DWMA";
break;
case 7:
- MA1 = new FMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period);
- this.Name += $"FMA";
+ MA1 = new FWMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period);
+ this.Name += $"FWMA";
break;
case 8:
MA1 = new DEMA_Series(source: bars.Select(this.MA1DataSource), period: this.MA1Period, useNaN: false);
@@ -173,8 +175,8 @@ public class MovingAverageSlope_chart : Indicator {
this.Name += $"DWMA";
break;
case 7:
- MA2 = new FMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period);
- this.Name += $"FMA";
+ MA2 = new FWMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period);
+ this.Name += $"FWMA";
break;
case 8:
MA2 = new DEMA_Series(source: bars.Select(this.MA2DataSource), period: this.MA2Period, useNaN: false);
@@ -225,11 +227,7 @@ public class MovingAverageSlope_chart : Indicator {
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);
+ this.bars.Add(this.Time(),this.Open(), this.High(), this.Low(), this.Close(), this.Volume(), update);
this.SetValue(this.MA1[^1].v, lineIndex: 0);
this.SetValue(this.MA2[^1].v, lineIndex: 1);
@@ -240,31 +238,37 @@ public class MovingAverageSlope_chart : Indicator {
this.LinesSeries[1].SetMarker(0,s2Color);
if (sig1[^1].v > 0 || sig2[^1].v > 0) {
- if (sMA1[^1].v >= 0 && sMA2[^1].v >= 0)
+ if (sMA1[^1].v >= 0 && sMA2[^1].v >= 0 && LongTrades)
{
+ inLong = true;
this.BeginCloud(0, 1, Color.FromArgb(127, Color.DarkGreen));
this.LinesSeries[(this.MA1[^1].v < this.MA2[^1].v)? 0 : 1 ].SetMarker(0, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.UpArrow));
}
else {
this.EndCloud(0, 1, Color.Empty);
- this.LinesSeries[(this.MA1[^1].v < this.MA2[^1].v) ? 1 : 0].SetMarker(1, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.DownArrow));
+ if (inShort)
+ {
+ this.LinesSeries[(this.MA1[^1].v < this.MA2[^1].v) ? 1 : 0].SetMarker(1, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.DownArrow));
+ inShort = false;
+ }
}
-
}
if (sig1[^1].v < 0 || sig2[^1].v < 0) {
- if (sMA1[^1].v <= 0 && sMA2[^1].v <= 0)
+ if (sMA1[^1].v <= 0 && sMA2[^1].v <= 0 && ShortTrades)
{
+ inShort = true;
this.BeginCloud(0, 1, Color.FromArgb(100, Color.Red));
this.LinesSeries[(this.MA1[^1].v > this.MA2[^1].v) ? 0 : 1].SetMarker(0, new IndicatorLineMarker(Color.OrangeRed, upperIcon: IndicatorLineMarkerIconType.UpArrow));
-
}
else {
this.EndCloud(0, 1, Color.Empty);
- this.LinesSeries[(this.MA1[^1].v > this.MA2[^1].v)?1:0].SetMarker(1, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.DownArrow));
+ if (inLong) {
+ LinesSeries[(this.MA1[^1].v > this.MA2[^1].v)?1:0].SetMarker(1, new IndicatorLineMarker(Color.LimeGreen, bottomIcon: IndicatorLineMarkerIconType.DownArrow));
+ inLong = false;
+ }
}
}
-
}
public override void OnPaintChart(PaintChartEventArgs args) {
base.OnPaintChart(args);
diff --git a/Indicators/Charts/JMA_chart.cs b/Indicators/Charts/JMA_chart.cs
index 366d759b..9390ea59 100644
--- a/Indicators/Charts/JMA_chart.cs
+++ b/Indicators/Charts/JMA_chart.cs
@@ -64,9 +64,7 @@ public class JMA_chart : Indicator {
rec[PriceType.Close], rec[PriceType.Volume]);
}
- indicator = new(source: bars.Select(DataSource), period: Period,
- phase: Jphase, vshort: Vshort, vlong: Vlong,
- useNaN: true);
+ indicator = new(source: bars.Select(DataSource), period: Period, phase: Jphase, vshort: Vshort, vlong: Vlong, useNaN: true);
}
protected override void OnUpdate(UpdateArgs args) {
diff --git a/Indicators/Charts/TrailingStop.cs b/Indicators/Charts/TrailingStop.cs
index 3413ccb5..2f20542e 100644
--- a/Indicators/Charts/TrailingStop.cs
+++ b/Indicators/Charts/TrailingStop.cs
@@ -90,7 +90,6 @@ public class TrailingStop_chart : Indicator {
this.SetValue(_ratchetL, lineIndex: 1);
this.SetValue(_tslineS, lineIndex: 2);
this.SetValue(_ratchetS, lineIndex: 3);
-
}
public override void OnPaintChart(PaintChartEventArgs args) {
diff --git a/Indicators/Indicators.csproj b/Indicators/Indicators.csproj
index 8c28acf6..0446cc33 100644
--- a/Indicators/Indicators.csproj
+++ b/Indicators/Indicators.csproj
@@ -17,6 +17,8 @@
0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d
0.2.1-dev.2
NETSDK1057
+ true
+ NETSDK1057
True
diff --git a/Strategies/SimpleMACross1.cs b/Strategies/SimpleMACross1.cs
index 117af5ca..f6ffaeb6 100644
--- a/Strategies/SimpleMACross1.cs
+++ b/Strategies/SimpleMACross1.cs
@@ -64,7 +64,7 @@ namespace SimpleMACross {
bars.Add(hdm.Last().TimeLeft, hdm.Last()[PriceType.Open], hdm.Last()[PriceType.High],
hdm.Last()[PriceType.Low], hdm.Last()[PriceType.Close], hdm.Last()[PriceType.Volume], update);
- if (!update) {this.LogInfo($"{bars.Close.Last().t} OHLC4:{(double)bars.OHLC4}");}
+ if (!update) {this.LogInfo($"{bars.Close.Last().t} OHLC4:{(double)bars.OHLC4.Last.v}");}
}
diff --git a/Strategies/Strategies.csproj b/Strategies/Strategies.csproj
index aa04c0c4..08f3bcdf 100644
--- a/Strategies/Strategies.csproj
+++ b/Strategies/Strategies.csproj
@@ -17,6 +17,8 @@
0.2.1-dev.2+Branch.dev.Sha.cb5fe2dc86a78fe9358da810d17952c82299ed3d
0.2.1-dev.2
NETSDK1057
+ true
+ NETSDK1057
True
diff --git a/Tests/Basic tests/Indicators.cs b/Tests/Basic tests/Indicators.cs
new file mode 100644
index 00000000..29476d01
--- /dev/null
+++ b/Tests/Basic tests/Indicators.cs
@@ -0,0 +1,153 @@
+using Xunit;
+using System;
+using QuanTAlib;
+
+namespace Basics;
+#nullable disable
+public class Indicators
+{
+ private static Type[] maSeriesTypes = new Type[]
+ {
+ typeof(SMA_Series),
+ typeof(EMA_Series),
+ typeof(DEMA_Series),
+ typeof(TEMA_Series),
+ typeof(WMA_Series),
+ typeof(ALMA_Series),
+ typeof(DWMA_Series),
+ typeof(FWMA_Series),
+ typeof(HMA_Series),
+ typeof(ZLEMA_Series),
+ typeof(RMA_Series),
+ typeof(HEMA_Series),
+ typeof(JMA_Series),
+ typeof(CUSUM_Series),
+ typeof(SMMA_Series),
+ typeof(T3_Series),
+ typeof(KAMA_Series),
+ typeof(TRIMA_Series),
+};
+
+ [Theory]
+ [MemberData(nameof(MASeriesData))]
+ public void Name_exists(Type classType)
+ {
+ TSeries data = new("Data") {1,2,3};
+
+ var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries;
+ Assert.NotEmpty(MA_Series.Name);
+ }
+
+ [Theory]
+ [MemberData(nameof(MASeriesData))]
+ public void Series_Length(Type classType)
+ {
+ GBM_Feed feed = new(1000);
+ TSeries data = feed.OHLC4;
+
+ var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries;
+ Assert.Equal(1000, MA_Series.Count);
+ }
+
+ [Theory]
+ [MemberData(nameof(MASeriesData))]
+ public void Return_data(Type classType)
+ {
+ TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries;
+ var result = MA_Series.Add(20);
+ Assert.Equal(result.v, MA_Series.Last.v);
+ }
+
+ [Theory]
+ [MemberData(nameof(MASeriesData))]
+ public void Update(Type classType)
+ {
+ TSeries data = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ var MA_Series = Activator.CreateInstance(classType, data, 5, false) as TSeries;
+ var pre_update = MA_Series.Last.v;
+
+ double pre_data = data.Last.v;
+ data.Add(20, true);
+ data.Add(pre_data, true);
+
+ Assert.Equal(pre_update, MA_Series.Last.v);
+ Assert.Equal(data.Count, MA_Series.Count);
+}
+
+ [Theory]
+ [MemberData(nameof(MASeriesData))]
+ public void Period_zero(Type classType)
+ {
+ GBM_Feed feed = new(100);
+ TSeries data = feed.OHLC4;
+
+ var MA_Series = Activator.CreateInstance(classType, data, 0, false) as TSeries;
+ Assert.Equal(data.Count, MA_Series.Count);
+ Assert.False(double.IsNaN(MA_Series.Last.v));
+ }
+
+ [Theory]
+ [MemberData(nameof(MASeriesData))]
+ public void Reset(Type classType)
+ {
+ GBM_Feed feed = new(10);
+ TSeries data = feed.OHLC4;
+ var MA_Series = Activator.CreateInstance(classType, data, 10, false) as TSeries;
+ MA_Series.Reset();
+ data.Add(0);
+ Assert.Equal(data.Last.v, MA_Series.Last.v);
+ }
+
+ [Theory]
+ [MemberData(nameof(MASeriesData))]
+ public void Period_one(Type classType)
+ {
+ GBM_Feed feed = new(100);
+ TSeries data = feed.OHLC4;
+
+ var MA_Series = Activator.CreateInstance(classType, data, 1, false) as TSeries;
+ Assert.InRange(MA_Series.Last.v - data.Last.v, -10e-6, 10e-6);
+ }
+
+ [Theory]
+ [MemberData(nameof(MASeriesData))]
+ public void NaN_test(Type classType)
+ {
+ GBM_Feed feed = new(100);
+ TSeries data = feed.OHLC4;
+
+ var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries;
+ Assert.True(double.IsNaN(MA_Series[0].v));
+ Assert.True(double.IsNaN(MA_Series[8].v));
+ Assert.False(double.IsNaN(MA_Series[9].v));
+ }
+
+ [Theory]
+ [MemberData(nameof(MASeriesData))]
+ public void Edge_numbers(Type classType)
+ {
+ TSeries data = new() { double.Epsilon, double.PositiveInfinity, double.MaxValue, double.NegativeInfinity };
+ var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries;
+ Assert.Equal(4, MA_Series.Count);
+ }
+
+ [Theory]
+ [MemberData(nameof(MASeriesData))]
+ public void handling_NaN(Type classType) {
+ TSeries data = new("Name") { 1, 2, 3, 4, 5, 6, double.NaN, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
+var MA_Series = Activator.CreateInstance(classType, data, 10, true) as TSeries;
+ Assert.False(double.IsNaN(MA_Series.Last.v));
+ }
+
+public static IEnumerable