mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-25 22:08:05 +00:00
Update RSI_Series to check for period != 0 before calculating RSI
This commit is contained in:
@@ -1,63 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
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)
|
||||
|
||||
</summary> */
|
||||
|
||||
public class ALMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly System.Collections.Generic.List<double> _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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
|
||||
/* <summary>
|
||||
CCI: Commodity Channel Index
|
||||
Commodity Channel Index is a momentum oscillator used to primarily identify overbought
|
||||
and oversold levels relative to a mean. CCI measures the current price level relative
|
||||
to an average price level over a given period of time:
|
||||
- CCI is relatively high when prices are far above their average.
|
||||
- CCI is relatively low when prices are far below their average.
|
||||
Using this method, CCI can be used to identify overbought and oversold levels.
|
||||
|
||||
Sources:
|
||||
https://www.investopedia.com/terms/c/commoditychannelindex.asp
|
||||
https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/cci
|
||||
|
||||
</summary> */
|
||||
|
||||
public class CCI_Series : Single_TBars_Indicator
|
||||
{
|
||||
private readonly System.Collections.Generic.List<double> _tp = new();
|
||||
|
||||
public CCI_Series(TBars source, int period = 10, bool useNaN = false) : base(source, period: period, useNaN: useNaN)
|
||||
{
|
||||
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)
|
||||
{
|
||||
double _tpItem = (TBar.h + TBar.l + TBar.c) / 3.0;
|
||||
if (update) { this._tp[this._tp.Count - 1] = _tpItem; } else { this._tp.Add(_tpItem); }
|
||||
if (this._tp.Count > this._p) { this._tp.RemoveAt(0); }
|
||||
|
||||
// average TP over _tp buffer
|
||||
double _avgTp = _tp.Average();
|
||||
|
||||
// average Deviation over _tp buffer
|
||||
double _avgDv = 0;
|
||||
for (int i = 0; i < this._tp.Count; i++) { _avgDv += Math.Abs(_avgTp - this._tp[i]); }
|
||||
_avgDv /= this._tp.Count;
|
||||
|
||||
|
||||
double _cci = (_avgDv == 0) ? double.NaN : (this._tp[this._tp.Count-1] - _avgTp) / (0.015 * _avgDv);
|
||||
|
||||
base.Add((TBar.t, _cci), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
/* <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 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);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
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.
|
||||
|
||||
</summary> */
|
||||
|
||||
public class DWMA_Series : Single_TSeries_Indicator {
|
||||
private readonly System.Collections.Generic.List<double> _buffer1 = new();
|
||||
private readonly System.Collections.Generic.List<double> _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);
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <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 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;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
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/
|
||||
|
||||
</summary> */
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
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)
|
||||
|
||||
</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) 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);
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
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)((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);
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <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> volty_short = new();
|
||||
private readonly System.Collections.Generic.List<double> 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) {
|
||||
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;
|
||||
if (this.Count == 0) { prev_ma1 = prev_jma = TValue.v; }
|
||||
if (update) {
|
||||
upperBand = p_upperBand;
|
||||
lowerBand = p_lowerBand;
|
||||
Kv = p_Kv;
|
||||
prev_vsum = p_prev_vsum;
|
||||
prev_ma1 = p_prev_ma1;
|
||||
prev_det0 = p_prev_det0;
|
||||
prev_det1 = p_prev_det1;
|
||||
prev_jma = p_prev_jma;
|
||||
}
|
||||
else {
|
||||
p_upperBand = upperBand;
|
||||
p_lowerBand = lowerBand;
|
||||
p_Kv = Kv;
|
||||
p_prev_vsum = prev_vsum;
|
||||
p_prev_ma1 = prev_ma1;
|
||||
p_prev_det0 = prev_det0;
|
||||
p_prev_det1 = prev_det1;
|
||||
p_prev_jma = prev_jma;
|
||||
}
|
||||
|
||||
// from Tvalue to volty
|
||||
del1 = TValue.v - upperBand;
|
||||
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;
|
||||
if (Math.Abs(del1) > Math.Abs(del2)) { volty = Math.Abs(del1); }
|
||||
if (Math.Abs(del1) < Math.Abs(del2)) { volty = Math.Abs(del2); }
|
||||
|
||||
//// from volty to avolty
|
||||
if (update) { volty_short[volty_short.Count - 1] = volty; }
|
||||
else { volty_short.Add(volty); }
|
||||
if (volty_short.Count > _voltyS) { volty_short.RemoveAt(0); }
|
||||
vsum = prev_vsum + 0.1 * (volty - volty_short.First());
|
||||
prev_vsum = vsum;
|
||||
if (update) { vsum_buff[vsum_buff.Count - 1] = vsum; }
|
||||
else { vsum_buff.Add(vsum); }
|
||||
if (vsum_buff.Count > _voltyL) { vsum_buff.RemoveAt(0); }
|
||||
double avolty = 0;
|
||||
for (int i = 0; i < vsum_buff.Count; i++) { avolty += vsum_buff[i]; }
|
||||
avolty /= vsum_buff.Count;
|
||||
|
||||
/// from avolty to rolty
|
||||
double rvolty = (avolty != 0) ? volty / avolty : 0;
|
||||
double len1 = (Math.Log(Math.Sqrt(_p)) / Math.Log(2.0)) + 2;
|
||||
if (len1 < 0) { len1 = 0; }
|
||||
|
||||
double pow1 = Math.Max(len1 - 2.0, 0.5);
|
||||
if (rvolty > Math.Pow(len1, 1.0 / pow1)) { rvolty = Math.Pow(len1, 1.0 / pow1); }
|
||||
if (rvolty < 1) { rvolty = 1; }
|
||||
|
||||
//// from rvolty to second smoothing
|
||||
double pow2 = Math.Pow(rvolty, pow1);
|
||||
double beta = 0.45 * (_p - 1) / (0.45 * (_p - 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;
|
||||
|
||||
base.Add((TValue.t, jma), update, _NaN);
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
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.
|
||||
|
||||
</summary> */
|
||||
|
||||
public class KAMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly double _scFast, _scSlow;
|
||||
private readonly System.Collections.Generic.List<double> _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);
|
||||
}
|
||||
}
|
||||
@@ -15,105 +15,144 @@ Sources:
|
||||
|
||||
</summary> */
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <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) 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);
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
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
|
||||
|
||||
</summary> */
|
||||
|
||||
public class SMMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly System.Collections.Generic.List<double> _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);
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
/* <summary>
|
||||
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/
|
||||
|
||||
</summary> */
|
||||
public class T3_Series : Single_TSeries_Indicator {
|
||||
private readonly double _k, _k1m, _c1, _c2, _c3, _c4;
|
||||
private readonly System.Collections.Generic.List<double> _buffer1 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buffer2 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buffer3 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buffer4 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buffer5 = new();
|
||||
private readonly System.Collections.Generic.List<double> _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);
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <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) 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);
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
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)
|
||||
|
||||
</summary> */
|
||||
|
||||
public class TRIMA_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly System.Collections.Generic.List<double> _buffer1 = new();
|
||||
private readonly System.Collections.Generic.List<double> _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);
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
/* <summary>
|
||||
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
|
||||
|
||||
</summary> */
|
||||
public class TRIX_Series : Single_TSeries_Indicator
|
||||
{
|
||||
private readonly double _k, _k1m;
|
||||
private readonly System.Collections.Generic.List<double> _buffer1 = new();
|
||||
private readonly System.Collections.Generic.List<double> _buffer2 = new();
|
||||
private readonly System.Collections.Generic.List<double> _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);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
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) 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);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <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 System.Collections.Generic.List<double> _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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user