Quantower adaptation

This commit is contained in:
Miha Kralj
2022-12-21 07:24:21 -08:00
parent 9f01df0e65
commit 9ad467cd5a
28 changed files with 3493 additions and 3204 deletions
+39 -38
View File
@@ -18,49 +18,50 @@ Abstract classes with all scaffolding required to build indicators.
public abstract class Single_TBars_Indicator : TSeries
{
protected readonly int _p;
protected readonly bool _NaN;
protected readonly TBars _bars;
protected readonly int _p;
protected readonly bool _NaN;
protected readonly TBars _bars;
// Chainable Constructor - add it at the end of primary constructor :base(source: source, period: period, useNaN: useNaN)
protected Single_TBars_Indicator(TBars source, int period, bool useNaN)
{
this._p = period;
this._bars = source;
this._NaN = useNaN;
this._bars.Pub += this.Sub;
}
// Chainable Constructor - add it at the end of primary constructor :base(source: source, period: period, useNaN: useNaN)
protected Single_TBars_Indicator(TBars source, int period, bool useNaN)
{
this._p = period;
this._bars = source;
this._NaN = useNaN;
this._bars.Pub += this.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((System.DateTime t, double o, double h, double l, double c, double v) TBar, bool update) => base.Add((TBar.t, 0.0), update);
public virtual void Add((System.DateTime t, double v) TValue, bool update, bool useNaN)
{
var res = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : TValue.v);
base.Add(res, update);
}
public virtual void Add((System.DateTime t, double o, double h, double l, double c, double v) TBar, bool update) => base.Add((TBar.t, 0.0), update);
public virtual void Add((System.DateTime t, double v) TValue, bool update, bool useNaN)
{
var res = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : TValue.v);
base.Add(res, update);
}
// 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 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);
public new void Sub(object source, TSeriesEventArgs e) => this.Add(TBar: this._bars[this._bars.Count - 1], update: e.update);
// 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 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);
public new void Sub(object source, TSeriesEventArgs e) => this.Add(TBar: this._bars[this._bars.Count - 1], update: e.update);
protected static void Add_Replace(List<double> l, double v, bool update)
{
if (update)
{ l[l.Count - 1] = v; }
else
{ l.Add(v); }
}
protected static void Add_Replace_Trim(List<double> l, double v, int p, bool update)
{
Add_Replace(l, v, update);
if (l.Count > p && p != 0)
{ l.RemoveAt(0); }
}
protected static void Add_Replace(List<double> l, double v, bool update)
{
if (update)
{ l[l.Count - 1] = v; }
else
{ l.Add(v); }
}
protected static void Add_Replace_Trim(List<double> l, double v, int p, bool update)
{
Add_Replace(l, v, update);
if (l.Count > p && p != 0)
{ l.RemoveAt(0); }
}
}
+71 -71
View File
@@ -1,71 +1,71 @@
namespace QuanTAlib;
using System;
using System.Collections.Generic;
using System.Linq;
/* <summary>
Abstract classes with all scaffolding required to build indicators.
All abstracts support period, NaN, and all permutations of Add() methods.
Indicator classess need to implement:
- Chaining constructor (Abstract's constructor executes first)
- Default Add(value) class
- optional Add(series) bulk insert class (for optimization of historical analysis)
Single_TSeries_Indicator - one single-value TSeries in, one TSeries out.
Pair_TSeries_Indicator - Two TSeries in, one TSeries out. (includes simple semaphoring)
Single_TBars_Indicator - One OHLCV TBars in, one TSeries out.
</summary> */
public abstract class Single_TSeries_Indicator : TSeries
{
protected readonly int _period;
protected readonly bool _NaN;
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)
{
this._data = source;
this._period = period;
this._p = _period;
this._NaN = useNaN;
this._data.Pub += this.Sub;
}
// overridable Add() method to add/update a single item at the end of the list
public virtual void Add((System.DateTime t, double v) TValue, bool update, bool useNaN)
{
if (_period == 0) { _p = this.Length; }
var res = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : TValue.v);
base.Add(res, update);
}
public new virtual void Add((System.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) { for (int i = 0; i < data.Count; i++) { this.Add(TValue: data[i], 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);
protected static void Add_Replace(List<double> l, double v, bool update)
{
if (update)
{ l[l.Count - 1] = v; }
else
{ l.Add(v); }
}
protected static double Add_Replace_Trim(List<double> l, double v, int p, bool update)
{
Add_Replace(l, v, update);
double ret = (l.Count > 0) ? l.First() : 0;
if (l.Count > p && p != 0)
{
l.RemoveAt(0);
}
return ret;
}
}
namespace QuanTAlib;
using System;
using System.Collections.Generic;
using System.Linq;
/* <summary>
Abstract classes with all scaffolding required to build indicators.
All abstracts support period, NaN, and all permutations of Add() methods.
Indicator classess need to implement:
- Chaining constructor (Abstract's constructor executes first)
- Default Add(value) class
- optional Add(series) bulk insert class (for optimization of historical analysis)
Single_TSeries_Indicator - one single-value TSeries in, one TSeries out.
Pair_TSeries_Indicator - Two TSeries in, one TSeries out. (includes simple semaphoring)
Single_TBars_Indicator - One OHLCV TBars in, one TSeries out.
</summary> */
public abstract class Single_TSeries_Indicator : TSeries
{
protected readonly int _period;
protected readonly bool _NaN;
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)
{
this._data = source;
this._period = period;
this._p = _period;
this._NaN = useNaN;
this._data.Pub += this.Sub;
}
// overridable Add() method to add/update a single item at the end of the list
public virtual void Add((System.DateTime t, double v) TValue, bool update, bool useNaN)
{
if (_period == 0) { _p = this.Length; }
var res = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : TValue.v);
base.Add(res, update);
}
public new virtual void Add((System.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) { for (int i = 0; i < data.Count; i++) { this.Add(TValue: data[i], 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);
protected static void Add_Replace(List<double> l, double v, bool update)
{
if (update)
{ l[l.Count - 1] = v; }
else
{ l.Add(v); }
}
protected static double Add_Replace_Trim(List<double> l, double v, int p, bool update)
{
Add_Replace(l, v, update);
double ret = (l.Count > 0) ? l.First() : 0;
if (l.Count > p && p != 0)
{
l.RemoveAt(0);
}
return ret;
}
}
+136 -132
View File
@@ -1,132 +1,136 @@
namespace QuanTAlib;
using System;
/* <summary>
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)
</summary> */
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 => "Typical",
8 => "Mean",
_ => "Weighted",
};
}
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);
_open[_open.Count - 1] = (t, o);
_high[_high.Count - 1] = (t, h);
_low[_low.Count - 1] = (t, l);
_close[_close.Count - 1] = (t, c);
_volume[_volume.Count - 1] = (t, v);
_hl2[_hl2.Count - 1] = (t, (h + l) * 0.5);
_oc2[_oc2.Count - 1] = (t, (o + c) * 0.5);
_ohl3[_ohl3.Count - 1] = (t, (o + h + l) * 0.333333333333333);
_hlc3[_hlc3.Count - 1] = (t, (h + l + c) * 0.333333333333333);
_ohlc4[_ohlc4.Count - 1] = (t, (o + h + l + c) * 0.25);
_hlcc4[_hlcc4.Count - 1] = (t, (h + l + c + c) * 0.25);
}
else
{
base.Add((t, o, h, l, c, v));
_open.Add((t, o));
_high.Add((t, h));
_low.Add((t, l));
_close.Add((t, c));
_volume.Add((t, v));
_hl2.Add((t, (h + l) * 0.5));
_oc2.Add((t, (o + c) * 0.5));
_ohl3.Add((t, (o + h + l) * 0.333333333333333));
_hlc3.Add((t, (h + l + c) * 0.333333333333333));
_ohlc4.Add((t, (o + h + l + c) * 0.25));
_hlcc4.Add((t, (h + l + c + c) * 0.25));
}
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 });
}
}
}
namespace QuanTAlib;
using System;
/* <summary>
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)
</summary> */
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 => "Typical",
8 => "Mean",
_ => "Weighted",
};
}
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);
}
}
}
+7 -5
View File
@@ -22,13 +22,15 @@ public class DEMA_Series : Single_TSeries_Indicator
private readonly System.Collections.Generic.List<double> _buffer1 = new();
private readonly System.Collections.Generic.List<double> _buffer2 = new();
private readonly double _k;
private double _lastema1, _lastlastema1;
private readonly bool _useSMA;
private double _lastema1, _lastlastema1;
private double _lastema2, _lastlastema2;
public DEMA_Series(TSeries source, int period, bool useNaN = false) : base(source, period, useNaN)
public DEMA_Series(TSeries source, int period, bool useNaN = false, bool useSMA = true) : base(source, period, useNaN)
{
_k = 2.0 / (_p + 1);
if (_data.Count > 0) { base.Add(_data); }
_useSMA = useSMA;
if (_data.Count > 0) { base.Add(_data); }
}
public override void Add((DateTime t, double v) TValue, bool update)
@@ -40,7 +42,7 @@ public class DEMA_Series : Single_TSeries_Indicator
}
double _ema1, _ema2, _dema;
if (this.Count < _p)
if (this.Count < _p && _useSMA)
{
Add_Replace_Trim(_buffer1, TValue.v, _p, update);
_ema1 = 0;
@@ -52,7 +54,7 @@ public class DEMA_Series : Single_TSeries_Indicator
for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; }
_ema2 /= _buffer2.Count;
}
else if(this.Count < (2*_p - 1)) // second _p
else if(this.Count < (2*_p - 1) && _useSMA) // second _p
{
_ema1 = (TValue.v - _lastema1) * _k + _lastema1;
+38 -38
View File
@@ -1,39 +1,39 @@
namespace QuanTAlib;
using System;
/* <summary>
DWMA: Double (linearly) Weighted Moving Average
The weights are linearly decreasing over the period and the most recent data has
the heaviest weight.
Sources:
</summary> */
public class DWMA_Series : Single_TSeries_Indicator
{
public DWMA_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> _buffer1 = new();
private readonly System.Collections.Generic.List<double> _buffer2 = 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(_buffer1, TValue.v, _p, update);
double _wma = 0;
for (int i = 0; i < _buffer1.Count; i++) { _wma += _buffer1[i] * this._weights[i]; }
_wma /= (this._buffer1.Count * (this._buffer1.Count + 1)) * 0.5;
Add_Replace_Trim(_buffer2, TValue.v, _p, update);
double _dwma = 0;
for (int i = 0; i < _buffer2.Count; i++) { _dwma += _buffer2[i] * this._weights[i]; }
_dwma /= (this._buffer2.Count * (this._buffer2.Count + 1)) * 0.5;
base.Add((TValue.t, _dwma), update, _NaN);
}
namespace QuanTAlib;
using System;
/* <summary>
DWMA: Double (linearly) Weighted Moving Average
The weights are linearly decreasing over the period and the most recent data has
the heaviest weight.
Sources:
</summary> */
public class DWMA_Series : Single_TSeries_Indicator
{
public DWMA_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> _buffer1 = new();
private readonly System.Collections.Generic.List<double> _buffer2 = 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(_buffer1, TValue.v, _p, update);
double _wma = 0;
for (int i = 0; i < _buffer1.Count; i++) { _wma += _buffer1[i] * this._weights[i]; }
_wma /= (this._buffer1.Count * (this._buffer1.Count + 1)) * 0.5;
Add_Replace_Trim(_buffer2, TValue.v, _p, update);
double _dwma = 0;
for (int i = 0; i < _buffer2.Count; i++) { _dwma += _buffer2[i] * this._weights[i]; }
_dwma /= (this._buffer2.Count * (this._buffer2.Count + 1)) * 0.5;
base.Add((TValue.t, 2*_wma - _dwma), update, _NaN);
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ 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;
private bool _useSMA;
private readonly bool _useSMA;
public EMA_Series(TSeries source, int period, bool useNaN = false, bool useSMA = true) : base(source, period, useNaN)
{
+53 -27
View File
@@ -23,9 +23,11 @@ Issues:
public class JMA_Series : Single_TSeries_Indicator {
private readonly System.Collections.Generic.List<double> volty_10 = new();
private readonly System.Collections.Generic.List<double> vsum_buff = new();
private readonly double pr, beta;
private readonly double pr;
public TSeries mma1 { get; }
public TSeries mma2 { get; }
private double upperBand, lowerBand, _phase, vsum, Kv, del1, del2, prev_del1, prev_del2;
private double upperBand, lowerBand, vsum, Kv, del1, del2;
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;
@@ -35,18 +37,34 @@ public class JMA_Series : Single_TSeries_Indicator {
pr = (phase * 0.01) + 1.5;
if (phase < -100) pr = 0.5;
if (phase > 100) pr = 2.5;
beta = 0.45 * (_p - 1) / (0.45 * (_p - 1) + 2);
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) {
if (this.Count == 0) { prev_ma1 = 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;
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
@@ -59,41 +77,49 @@ public class JMA_Series : Single_TSeries_Indicator {
if (Math.Abs(del1) < Math.Abs(del2)) { volty = Math.Abs(del2); }
//// from volty to avolty
if (update) { volty_10[volty_10.Count - 1] = volty; } else { volty_10.Add(volty); }
if (volty_10.Count > 10) { volty_10.RemoveAt(0); }
if (update) { volty_10[volty_10.Count - 1] = volty; }
else { volty_10.Add(volty); }
if (volty_10.Count > _p) { volty_10.RemoveAt(0); }
vsum = prev_vsum + 0.1 * (volty - volty_10.First());
if (update) { vsum_buff[vsum_buff.Count - 1] = vsum; } else { vsum_buff.Add(vsum); }
if (vsum_buff.Count > 65) vsum_buff.RemoveAt(0);
if (update) { vsum_buff[vsum_buff.Count - 1] = vsum; }
else { vsum_buff.Add(vsum); }
if (vsum_buff.Count > (65))
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 rvolty = (avolty != 0) ? volty / avolty : 0;
double len1 = (Math.Log(Math.Sqrt(0.5 * (_p - 1))) / 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;
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 len2 = Math.Sqrt(0.5 * (_p - 1)) * len1;
Kv = Math.Pow(len2 / (len2 + 1), Math.Sqrt(pow2));
double alpha = Math.Pow(beta, pow2);
double ma1 = (1 - alpha) * TValue.v + alpha * prev_ma1;
Kv = Math.Pow(len2 / (len2 + 2), Math.Sqrt(pow2));
double beta = 0.45 * (_p - 1) / (0.45 * (_p - 1) + 2);
double alpha = Math.Pow(beta * 1.1, pow2);
double ma1 = (1 - alpha) * TValue.v + alpha * prev_ma1;
prev_ma1 = ma1;
double det0 = (1 - beta) * (TValue.v - ma1) + beta * prev_det0;
prev_det0 = det0;
mma1.Add(ma1);
/// from second smoothing to jma
double det0 = (1 - beta) * (TValue.v - ma1) + beta * prev_det0;
prev_det0 = det0;
double ma2 = ma1 + pr * det0;
double det1 = (1 - alpha) * (1 - alpha) * (ma2 - prev_jma) + alpha * alpha * prev_det1;
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);
base.Add((TValue.t, ma1), update, _NaN);
}
}
}
+118 -118
View File
@@ -1,118 +1,118 @@
namespace QuanTAlib;
using System;
/* <summary>
MAMA: MESA Adaptive Moving Average
Created by John Ehlers, the MAMA indicator is a 5-period adaptive moving average of
high/low price that uses classic electrical radio-frequency signal processing algorithms
to reduce noise.
KAMAi = KAMAi - 1 + SC * ( price - KAMAi-1 )
Sources:
https://mesasoftware.com/papers/MAMA.pdf
https://www.tradingview.com/script/foQxLbU3-Ehlers-MESA-Adaptive-Moving-Average-LazyBear/
</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, 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)
{
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;
// 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;
// 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;
// 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;
// phasor addition for 3-bar averaging
i2.i = i1.i - jQ;
q2.i = q1.i + jI;
i2.i = (0.2 * i2.i) + (0.8 * i2.i1); // smoothing it
q2.i = (0.2 * q2.i) + (0.8 * q2.i1);
// homodyne discriminator
re.i = (i2.i * i2.i1) + (q2.i * q2.i1);
im.i = (i2.i * q2.i1) - (q2.i * i2.i1);
re.i = (0.2 * re.i) + (0.8 * re.i1); // smoothing it
im.i = (0.2 * im.i) + (0.8 * im.i1);
// calculate period
pd.i = (im.i != 0 && re.i != 0) ? (6.283185307179586 / Math.Atan(im.i / re.i)) : 0d;
// 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;
// smooth the period
pd.i = (0.2 * pd.i) + (0.8 * pd.i1);
// determine phase position
ph.i = (i1.i != 0) ? Math.Atan(q1.i / i1.i) * 57.29577951308232 : 0;
// change in phase
double delta = Math.Max(ph.i1 - ph.i, 1d);
// adaptive alpha value
double 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, this.Count < this._p - 1 && this._NaN ? double.NaN : fama.i);
Fama.Add(result, update);
}
}
namespace QuanTAlib;
using System;
/* <summary>
MAMA: MESA Adaptive Moving Average
Created by John Ehlers, the MAMA indicator is a 5-period adaptive moving average of
high/low price that uses classic electrical radio-frequency signal processing algorithms
to reduce noise.
KAMAi = KAMAi - 1 + SC * ( price - KAMAi-1 )
Sources:
https://mesasoftware.com/papers/MAMA.pdf
https://www.tradingview.com/script/foQxLbU3-Ehlers-MESA-Adaptive-Moving-Average-LazyBear/
</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, 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)
{
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;
// 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;
// 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;
// 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;
// phasor addition for 3-bar averaging
i2.i = i1.i - jQ;
q2.i = q1.i + jI;
i2.i = (0.2 * i2.i) + (0.8 * i2.i1); // smoothing it
q2.i = (0.2 * q2.i) + (0.8 * q2.i1);
// homodyne discriminator
re.i = (i2.i * i2.i1) + (q2.i * q2.i1);
im.i = (i2.i * q2.i1) - (q2.i * i2.i1);
re.i = (0.2 * re.i) + (0.8 * re.i1); // smoothing it
im.i = (0.2 * im.i) + (0.8 * im.i1);
// calculate period
pd.i = (im.i != 0 && re.i != 0) ? (6.283185307179586 / Math.Atan(im.i / re.i)) : 0d;
// 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;
// smooth the period
pd.i = (0.2 * pd.i) + (0.8 * pd.i1);
// determine phase position
ph.i = (i1.i != 0) ? Math.Atan(q1.i / i1.i) * 57.29577951308232 : 0;
// change in phase
double delta = Math.Max(ph.i1 - ph.i, 1d);
// adaptive alpha value
double 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, this.Count < this._p - 1 && this._NaN ? double.NaN : fama.i);
Fama.Add(result, update);
}
}
+109 -126
View File
@@ -1,127 +1,110 @@
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". Tillsons 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/
Calculation:
a = 0.7 (but also 0.618);
Ema1 = Ema (Close);
Ema2 = Ema (Ema1);
Ema3 = Ema (Ema2);
Ema4 = Ema (Ema3);
Ema5 = Ema (Ema4);
Ema6 = Ema (Ema5);
T3 = (a*a*a) * Ema6 + (3*a*a + 3*a*a*a) * Ema5 + (6*a*a 3*a 3*a*a*a) * Ema4 + (1 + 3*a + a*a*a + 3*a*a) * Ema3
</summary> */
public class T3_Series : Single_TSeries_Indicator
{
private double k, a;
private double c1, c2, c3, c4;
private double o_c1, o_c2, o_c3, o_c4;
private double e1, e2, e3, e4, e5, e6;
private double o_e1, o_e2, o_e3, o_e4, o_e5, o_e6;
private double sum1, sum2, sum3, sum4, sum5, sum6;
private double o_sum1, o_sum2, o_sum3, o_sum4, o_sum5, o_sum6;
public T3_Series(TSeries source, int period, double vfactor = 0.7, bool useNaN = false) : base(source, period, useNaN)
{
k = 2.0 / (_p + 1);
a = vfactor;
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) + (3 * a * a) + (a * a * a) ;
e1 = e2 = e3 = e4 = e5 = e6 = 0;
sum1 = sum2 = sum3 = sum4 = sum5 = sum6 = 0;
if (_data.Count > 0) { base.Add(data: _data); }
}
public override void Add((DateTime t, double v) TValue, bool update)
{
if (update) {
// roll back (x = oldx)
c1 = o_c1; c2 = o_c2; c3 = o_c3; c4 = o_c4;
e1 = o_e1; e2 = o_e2; e3 = o_e3; e4 = o_e4; e5 = o_e5; e6 = o_e6;
sum1 = o_sum1; sum2 = o_sum2; sum3 = o_sum3; sum4 = o_sum4; sum5 = o_sum5; sum6 = o_sum6;
} else {
// roll forward (oldx = x)
o_c1 = c1; o_c2 = c2; o_c3 = c3; o_c4 = c4;
o_e1 = e1; o_e2 = e2; o_e3 = e3; o_e4 = e4; o_e5 = e5; o_e6 = e6;
o_sum1 = sum1; o_sum2 = sum2; o_sum3 = sum3; o_sum4 = sum4; o_sum5 = sum5; o_sum6 = sum6;
}
double v = TValue.v;
int i = base.Count;
if (i > _p - 1) {
e1 += k * (v - e1);
if (i > 2 * (_p - 1)) {
e2 += k * (e1 - e2);
if (i > 3 * (_p - 1)) {
e3 += k * (e2 - e3);
if (i > 4 * (_p - 1)) {
e4 += k * (e3 - e4);
if (i > 5 * (_p - 1)) {
e5 += k * (e4 - e5);
if (i > 6 * (_p - 1)) {
e6 += k * (e5 - e6);
}
else {
sum6 += e5;
if (i == 6 * (_p - 1)) {
e6 = sum6 / Math.Max(_p, base.Count);
}
}
}
else {
sum5 += e4;
if (i == 5 * (_p - 1)) {
sum6 = e5 = sum5 / Math.Max(_p, base.Count);
}
}
}
else {
sum4 += e3;
if (i == 4 * (_p - 1)) {
sum5 = e4 = sum4 / Math.Max(_p, base.Count);
}
}
}
else {
sum3 += e2;
if (i == 3 * (_p - 1)) {
sum4 = e3 = sum3 / Math.Max(_p, base.Count);
}
}
}
else {
sum2 += e1;
if (i == 2 * (_p - 1)) {
sum3 = e2 = sum2 / Math.Max(_p, base.Count);
}
}
}
else {
sum1 += v;
if (i == _p - 1) {
sum2 = e1 = sum1 / Math.Max(_p, base.Count);
}
}
double t3 = (c1 * e6) + (c2 * e5) + (c3 * e4) + (c4 * e3);
base.Add(TValue: (TValue.t, t3), update: update, useNaN: _NaN);
}
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". Tillsons 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/
Calculation:
Volume Factor is typically 0.7 (but also 0.618);
Ema1 = Ema (Close);
Ema2 = Ema (Ema1);
Ema3 = Ema (Ema2);
Ema4 = Ema (Ema3);
Ema5 = Ema (Ema4);
Ema6 = Ema (Ema5);
T3 = (a*a*a) * Ema6 + (3*a*a + 3*a*a*a) * Ema5 + (6*a*a 3*a 3*a*a*a) * Ema4 + (1 + 3*a + a*a*a + 3*a*a) * Ema3
</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 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);
}
}
+82
View File
@@ -0,0 +1,82 @@
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.
Calculation:
Ema1 = Ema (Close);
Ema2 = Ema (Ema1);
Ema3 = Ema (Ema2);
TRIX = (Ema3-Ema3[1]) / Ema3[1]
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 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);
}
}
+47
View File
@@ -0,0 +1,47 @@
namespace QuanTAlib;
using System;
/* <summary>
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
</summary> */
public class CMO_Series : Single_TSeries_Indicator {
private readonly System.Collections.Generic.List<double> _buff_up = new();
private readonly System.Collections.Generic.List<double> _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);
}
}