TR and ATR

This commit is contained in:
Miha
2022-04-19 22:34:42 -07:00
parent 50ed6f6504
commit 509b6dec02
41 changed files with 488 additions and 236 deletions
+64
View File
@@ -0,0 +1,64 @@
namespace QuanTAlib;
using System;
/* <summary>
ATR: wildeR Moving Average
The average true range (ATR) is a price volatility indicator
showing the average price variation of assets within a given time period.
Sources:
https://en.wikipedia.org/wiki/Average_true_range
https://www.tradingview.com/wiki/Average_True_Range_(ATR)
https://www.investopedia.com/terms/a/atr.asp
</summary> */
public class ATR_Series : Single_TBars_Indicator
{
private readonly System.Collections.Generic.List<double> _buffer = new();
private readonly double _k, _k1m;
private double _lastema, _lastlastema, _lastcm1;
private double _cm1 = double.NaN;
public ATR_Series(TBars 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 (_bars.Count > 0) { base.Add(_bars); }
}
public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false)
{
if (update) {
this._lastema = this._lastlastema;
this._cm1 = this._lastcm1;
}
if (_cm1 is double.NaN) { _cm1 = TBar.c; }
double d1 = Math.Abs(TBar.h - TBar.l);
double d2 = Math.Abs(_cm1 - TBar.h);
double d3 = Math.Abs(_cm1 - TBar.l);
(DateTime t, double v)d = (TBar.t, Math.Max(d1,Math.Max(d2,d3))); //TR value for RMA below
_lastcm1 = _cm1;
_cm1 = TBar.c;
double _ema = 0;
if (this.Count < this._p)
{
if (update) { _buffer[_buffer.Count - 1] = d.v; }
else { _buffer.Add(d.v); }
if (_buffer.Count > this._p) { _buffer.RemoveAt(0); }
for (int i = 0; i < _buffer.Count; i++) { _ema += _buffer[i]; }
_ema /= this._buffer.Count;
}
else { _ema = d.v * _k + _lastema * _k1m; }
this._lastlastema = this._lastema;
this._lastema = _ema;
var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _ema);
base.Add(ret, update);
}
}
+63
View File
@@ -0,0 +1,63 @@
namespace QuanTAlib;
using System;
/* <summary>
ATRP: Average True Range Percent
Average True Range Percent is (ATR/Close Price)*100.
This normalizes so it can be compared to other stocks.
Sources:
https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/atrp
</summary> */
public class ATRP_Series : Single_TBars_Indicator
{
private readonly System.Collections.Generic.List<double> _buffer = new();
private readonly double _k, _k1m;
private double _lastema, _lastlastema, _lastcm1;
private double _cm1 = double.NaN;
public ATRP_Series(TBars 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 (_bars.Count > 0) { base.Add(_bars); }
}
public override void Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false)
{
if (update) {
this._lastema = this._lastlastema;
this._cm1 = this._lastcm1;
}
if (_cm1 is double.NaN) { _cm1 = TBar.c; }
double d1 = Math.Abs(TBar.h - TBar.l);
double d2 = Math.Abs(_cm1 - TBar.h);
double d3 = Math.Abs(_cm1 - TBar.l);
(DateTime t, double v)d = (TBar.t, Math.Max(d1,Math.Max(d2,d3))); //TR value for RMA below
_lastcm1 = _cm1;
_cm1 = TBar.c;
double _ema = 0;
if (this.Count < this._p)
{
if (update) { _buffer[_buffer.Count - 1] = d.v; }
else { _buffer.Add(d.v); }
if (_buffer.Count > this._p) { _buffer.RemoveAt(0); }
for (int i = 0; i < _buffer.Count; i++) { _ema += _buffer[i]; }
_ema /= this._buffer.Count;
}
else { _ema = d.v * _k + _lastema * _k1m; }
this._lastlastema = this._lastema;
this._lastema = _ema;
double _atrp = 100 * (_ema / TBar.c);
var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _atrp);
base.Add(ret, update);
}
}
+73
View File
@@ -0,0 +1,73 @@
namespace QuanTAlib;
using System;
/* <summary>
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
</summary> */
public class DEMA_Series : Single_TSeries_Indicator
{
private readonly System.Collections.Generic.List<double> _buffer = new();
private readonly double _k, _k1m;
private double _lastema1, _lastlastema1;
private double _lastema2, _lastlastema2;
public DEMA_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) d, bool update = false)
{
if (update)
{
this._lastema1 = this._lastlastema1;
this._lastema2 = this._lastlastema2;
}
double _ema1, _ema2;
if (this.Count < this._p)
{
if (update) { _buffer[_buffer.Count - 1] = d.v; }
else
{
_buffer.Add(d.v);
}
if (_buffer.Count > this._p) { _buffer.RemoveAt(0); }
double _sma = 0;
for (int i = 0; i < _buffer.Count; i++) { _sma += _buffer[i]; }
_sma /= this._buffer.Count;
_ema1 = _ema2 = _sma;
}
else
{
_ema1 = d.v * this._k + this._lastema1 * this._k1m;
_ema2 = _ema1 * this._k + this._lastema2 * this._k1m;
}
double _dema = 2 * _ema1 - _ema2;
this._lastlastema1 = this._lastema1;
this._lastlastema2 = this._lastema2;
this._lastema1 = _ema1;
this._lastema2 = _ema2;
var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _dema);
base.Add(ret, update);
}
}
+64
View File
@@ -0,0 +1,64 @@
namespace QuanTAlib;
using System;
/* <summary>
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.
</summary> */
public class EMA_Series : Single_TSeries_Indicator
{
private readonly System.Collections.Generic.List<double> _buffer = new();
private readonly double _k, _k1m;
private double _lastema, _lastlastema;
public EMA_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;
this._lastema = this._lastlastema = double.NaN;
if (this._data.Count > 0) { base.Add(this._data); }
}
public override void Add((DateTime t, double v) d, bool update = false)
{
double _ema = 0;
if (update) { this._lastema = this._lastlastema; }
if (this.Count < this._p)
{
if (update) { this._buffer[this._buffer.Count - 1] = d.v; }
else
{
this._buffer.Add(d.v);
}
if (this._buffer.Count > this._p) { this._buffer.RemoveAt(0); }
for (int i = 0; i < this._buffer.Count; i++) { _ema += this._buffer[i]; }
_ema /= this._buffer.Count;
}
else
{
_ema = d.v * this._k + this._lastema * this._k1m;
}
this._lastlastema = this._lastema;
this._lastema = _ema;
var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _ema);
base.Add(ret, update);
}
}
+65
View File
@@ -0,0 +1,65 @@
namespace QuanTAlib;
using System;
/* <summary>
HEMA: Hull-EMA Moving Average
Modified HUll Moving Average; instead of using WMA (Weighted MA) for acalculation,
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)
</summary> */
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) d, bool update)
{
if (update)
{
this._lastema1 = this._lastlastema1;
this._lastema2 = this._lastlastema2;
this._lastema3 = this._lastlastema3;
}
double _ema1 = System.Double.IsNaN(this._lastema1)
? d.v
: d.v * this._k1 + this._lastema1 * (1 - this._k1);
double _ema2 = System.Double.IsNaN(this._lastema2)
? d.v
: d.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;
(System.DateTime t, double v) result =
(d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _ema3);
base.Add(result, update);
}
}
+120
View File
@@ -0,0 +1,120 @@
namespace QuanTAlib;
using System;
/* <summary>
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
</summary> */
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<double> _buf1 = new();
private readonly System.Collections.Generic.List<double> _buf2 = new();
private readonly System.Collections.Generic.List<double> _buf3 = new();
private readonly System.Collections.Generic.List<double> _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)(Math.Ceiling((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);
}
}
+162
View File
@@ -0,0 +1,162 @@
namespace QuanTAlib;
using System;
/* <summary>
JMA: Jurik Moving Average
Mark Jurik's Moving Average (JMA) attempts to eliminate noise to see the
underlying activity. It has extremely low lag, is very smooth and is responsive
to market gaps.
Sources:
https://c.mql5.com/forextsd/forum/164/jurik_1.pdf
https://www.prorealcode.com/prorealtime-indicators/jurik-volatility-bands/
Issues:
Real JMA algorithm is not published and this formula is derived through
deduction and reverse analysis of JMA behavior. It is really close, but not
exact - published JMA tests against JMA.CSV fail with small deviation. The
original algo is slightly different, yet this approximation is close enough.
</summary> */
public class JMA_Series : Single_TSeries_Indicator
{
private readonly System.Collections.Generic.List<double> vbuffer10;
private readonly System.Collections.Generic.List<double> vsum65;
private double prev_ma1, prev_det0, prev_det1, prev_jma, bsmax, bsmin;
private double o_prev_ma1, o_prev_det0, o_prev_det1, o_prev_jma, o_bsmax, o_bsmin;
private readonly double pr, pow1, len2, beta, rvolty;
private readonly int _l;
public JMA_Series(TSeries source, int period, double phase = 0.0, bool useNaN = false) : base(source, period, useNaN)
{
this.vbuffer10 = new();
this.vsum65 = new();
// constants
this.pr = (phase < -100) ? 0.5 : (phase > 100) ? 2.5 : (phase * 0.01) + 1.5;
double len1 = Math.Max((Math.Log(Math.Sqrt(0.5 * (_p - 1))) / Math.Log(2.0)) + 2.0, 0);
this.pow1 = Math.Max(len1 - 2, 0.5);
this.rvolty = Math.Exp((1 / this.pow1) * Math.Log(len1));
this.len2 = Math.Sqrt(0.5 * (_p - 1)) * len1;
this.beta = 0.45 * (_p - 1) / (0.45 * (_p - 1) + 2);
this._l = (int)Math.Round(this._p - 1 * 0.5);
if (base._data.Count > 0) { base.Add(base._data); }
}
public override void Add((System.DateTime t, double v) d, bool update)
{
if (this.Count == 0)
{
this.prev_ma1 = this.prev_jma = d.v;
this.bsmax = this.bsmin = this.prev_det0 = this.prev_det1 = 0;
}
if (update)
{
this.prev_jma = this.o_prev_jma;
this.prev_ma1 = this.o_prev_ma1;
this.prev_det0 = this.o_prev_det0;
this.prev_det1 = this.o_prev_det1;
this.bsmax = this.o_bsmax;
this.bsmin = this.o_bsmin;
}
else
{
this.o_prev_jma = this.prev_jma;
this.o_prev_ma1 = this.prev_ma1;
this.o_prev_det0 = this.prev_det0;
this.o_prev_det1 = this.prev_det1;
this.o_bsmax = this.bsmax;
this.o_bsmin = this.bsmin;
}
double hprice = d.v;
double lprice = d.v;
for (int i = 0; i <= Math.Min(9, this._data.Count - 1); i++)
{
var _item = this._data[this._data.Count - 1 - i].v;
hprice = (_item > hprice) ? _item : hprice;
lprice = (_item < lprice) ? _item : lprice;
}
double del1 = hprice - this.bsmax;
double del2 = lprice - this.bsmin;
double volty = (Math.Abs(del1) != Math.Abs(del2))
? Math.Max(Math.Abs(del1), Math.Abs(del2))
: 0;
if (update)
{
this.vbuffer10[this.vbuffer10.Count - 1] = volty;
}
else
{
this.vbuffer10.Add(volty);
}
if (this.vbuffer10.Count > 10)
{
this.vbuffer10.RemoveAt(0);
}
double prevvsum =
(this.vsum65.Count > 0) ? this.vsum65[this.vsum65.Count - 1] : 0;
double vsumitem = prevvsum + 0.1 * (volty - this.vbuffer10[0]);
if (update)
{
this.vsum65[this.vsum65.Count - 1] = vsumitem;
}
else
{
this.vsum65.Add(vsumitem);
}
if (this.vsum65.Count > 65)
{
this.vsum65.RemoveAt(0);
}
double avolty = 0;
for (int i = 0; i < this.vsum65.Count; i++)
{
avolty += this.vsum65[i];
}
avolty /= this.vsum65.Count;
double dvolty = (avolty > 0) ? volty / avolty : 0;
dvolty = Math.Max((dvolty > this.rvolty) ? this.rvolty : dvolty, 1.0);
double pow2 = Math.Exp(this.pow1 * Math.Log(dvolty));
double kv =
Math.Exp(Math.Sqrt(pow2) * Math.Log(this.len2 / (this.len2 + 1)));
this.bsmax = (del1 > 0) ? hprice : hprice - (kv * del1);
this.bsmin = (del2 < 0) ? lprice : lprice - (kv * del2);
// adaptive EMA dynamic factor
double pow = Math.Pow(dvolty, this.pow1);
double alpha = Math.Pow(this.beta, pow);
// 1st stage - preliminary smoothing by adaptive EMA
double ma1 = d.v * (1 - alpha) + this.prev_ma1 * alpha;
this.prev_ma1 = ma1;
// 2nd stage - one more preliminary smoothing by Kalman filter
double det0 = (d.v - ma1) * (1 - this.beta) + this.prev_det0 * this.beta;
this.prev_det0 = det0;
double ma2 = ma1 + (this.pr * det0);
// 3rd stage - final smoothing by Jurik adaptive filter
double det1 = ((ma2 - this.prev_jma) * (1 - alpha) * (1 - alpha)) +
(this.prev_det1 * alpha * alpha);
this.prev_det1 = det1;
var jma = this.prev_jma + det1;
this.prev_jma = jma;
(System.DateTime t, double v) result =
(d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : jma);
base.Add(result, update);
}
}
+63
View File
@@ -0,0 +1,63 @@
namespace QuanTAlib;
using System;
/* <summary>
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.
</summary> */
public class RMA_Series : Single_TSeries_Indicator
{
private readonly System.Collections.Generic.List<double> _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) d, bool update = false)
{
double _ema = 0;
if (update) { this._lastema = this._lastlastema; }
if (this.Count < this._p)
{
if (update) { _buffer[_buffer.Count - 1] = d.v; }
else
{
_buffer.Add(d.v);
}
if (_buffer.Count > this._p) { _buffer.RemoveAt(0); }
for (int i = 0; i < _buffer.Count; i++) { _ema += _buffer[i]; }
_ema /= this._buffer.Count;
}
else
{
_ema = d.v * _k + _lastema * _k1m;
}
this._lastlastema = this._lastema;
this._lastema = _ema;
var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _ema);
base.Add(ret, update);
}
}
+41
View File
@@ -0,0 +1,41 @@
namespace QuanTAlib;
using System;
/* <summary>
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()
</summary> */
public class SMA_Series : Single_TSeries_Indicator
{
public SMA_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<double> _buffer = new();
public override void Add((System.DateTime t, double v) d, bool update)
{
if (update) { _buffer[_buffer.Count - 1] = d.v; }
else { _buffer.Add(d.v); }
if (_buffer.Count > this._p && this._p != 0) { _buffer.RemoveAt(0); }
double _sma = 0;
for (int i = 0; i < _buffer.Count; i++) { _sma += _buffer[i]; }
_sma /= this._buffer.Count;
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _sma);
base.Add(result, update);
}
}
+79
View File
@@ -0,0 +1,79 @@
namespace QuanTAlib;
using System;
/* <summary>
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
</summary> */
public class TEMA_Series : Single_TSeries_Indicator
{
private readonly System.Collections.Generic.List<double> _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) d, bool update = false)
{
if (update)
{
this._lastema1 = this._lastlastema1;
this._lastema2 = this._lastlastema2;
this._lastema3 = this._lastlastema3;
}
double _ema1, _ema2, _ema3;
if (this.Count < this._p)
{
if (update) { _buffer[_buffer.Count - 1] = d.v; }
else
{
_buffer.Add(d.v);
}
if (_buffer.Count > this._p) { _buffer.RemoveAt(0); }
double _sma = 0;
for (int i = 0; i < _buffer.Count; i++) { _sma += _buffer[i]; }
_sma /= this._buffer.Count;
_ema1 = _ema2 = _ema3 = _sma;
}
else
{
_ema1 = d.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;
var ret = (d.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _tema);
base.Add(ret, update);
}
}
+39
View File
@@ -0,0 +1,39 @@
namespace QuanTAlib;
using System;
/* <summary>
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
</summary> */
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<double> _buffer = new();
private readonly System.Collections.Generic.List<double> _weights = new();
public override void Add((System.DateTime t, double v) d, bool update)
{
if (update) { _buffer[_buffer.Count - 1] = d.v; }
else { _buffer.Add(d.v); }
if (_buffer.Count > this._p && this._p != 0) { _buffer.RemoveAt(0); }
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;
var result = (d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _wma);
base.Add(result, update);
}
}
+56
View File
@@ -0,0 +1,56 @@
namespace QuanTAlib;
using System;
/* <summary>
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.
</summary> */
public class ZLEMA_Series : Single_TSeries_Indicator
{
private readonly double _k, _k1m;
private double _lastema, _lastlastema;
public ZLEMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
{
this._k = 2.0 / (double)(period + 1);
this._k1m = 1.0 - this._k;
this._lastema = this._lastlastema = double.NaN;
if (base._data.Count > 0) { base.Add(base._data); }
}
public override void Add((System.DateTime t, double v) d, bool update)
{
if (update)
{
this._lastema = this._lastlastema;
}
int _lag = (int)(0.5 * (_p - 1));
int _l = Math.Max(this._data.Count - _lag, 0);
double _lagdata = 1 * d.v - this._data[_l].v;
double _ema = System.Double.IsNaN(this._lastema) ? _lagdata : _lagdata * this._k + this._lastema * this._k1m;
this._lastlastema = this._lastema;
this._lastema = _ema;
(System.DateTime t, double v) result =
(d.t, (this.Count < this._p - 1 && this._NaN) ? double.NaN : _ema);
base.Add(result, update);
}
}