mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-22 20:48:04 +00:00
Update RSI_Series to check for period != 0 before calculating RSI
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
/* <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 : TSeries {
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
private readonly System.Collections.Generic.List<double> _weight = new();
|
||||
private double _norm;
|
||||
private readonly double _offset, _sigma;
|
||||
|
||||
//core constructors
|
||||
public ALMA_Series(int period, double offset, double sigma, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"ALMA({period})";
|
||||
_offset = offset;
|
||||
_sigma = sigma;
|
||||
_weight = new();
|
||||
}
|
||||
public ALMA_Series(TSeries source, int period, double offset, double sigma, bool useNaN) : this(period, offset, sigma, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
|
||||
public ALMA_Series() : this(period:0, offset:0.85, sigma:6.0, useNaN: false) { }
|
||||
public ALMA_Series(int period) : this(period: period, offset:0.85, sigma:6.0, useNaN:false) { }
|
||||
public ALMA_Series(TBars source) : this(source:source.Close, period:0, offset:0.85, sigma:6.0, useNaN:false) { }
|
||||
public ALMA_Series(TBars source, int period) : this(source:source.Close, period:period, offset: 0.85, sigma: 6.0, useNaN: false) { }
|
||||
public ALMA_Series(TBars source, int period, double offset, double sigma, bool useNaN) : this(source.Close, period:period, offset: offset, sigma: sigma, useNaN: false) { }
|
||||
public ALMA_Series(TSeries source) : this(source, period:0, offset:0.85, sigma:6.0, useNaN:false) { }
|
||||
public ALMA_Series(TSeries source, int period) : this(source:source, period:period, offset:0.85, sigma:6.0, useNaN:false) { }
|
||||
public ALMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, offset: 0.85, sigma: 6.0, useNaN: useNaN) { }
|
||||
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update=false) {
|
||||
BufferTrim(_buffer, TValue.v, _period, update);
|
||||
if (_weight.Count < _buffer.Count) {
|
||||
for (int i = 0; i < (_buffer.Count - _weight.Count); i++) { _weight.Add(0.0); }
|
||||
}
|
||||
if (this._buffer.Count <= _period || _period ==0) {
|
||||
int _len = this._buffer.Count;
|
||||
_norm = 0;
|
||||
double _m = _offset * (_len - 1);
|
||||
double _s = _len / _sigma;
|
||||
for (int i = 0; i < _len; i++) {
|
||||
double _wt = Math.Exp(-((i - _m) * (i - _m)) / (2 * _s * _s));
|
||||
_weight[i] = _wt;
|
||||
_norm += _wt;
|
||||
}
|
||||
}
|
||||
|
||||
double _weightedSum = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++) { _weightedSum += _weight[i] * _buffer[i]; }
|
||||
double _alma = _weightedSum / _norm;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _alma);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
_weight.Clear();
|
||||
}
|
||||
|
||||
//variation of Add()
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
/* <summary>
|
||||
BIAS: Rate of change between the source and a moving average.
|
||||
Bias is a statistical term which means a systematic deviation from the actual value.
|
||||
|
||||
BIAS = (close - SMA) / SMA
|
||||
= (close / SMA) - 1
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Bias_of_an_estimator
|
||||
|
||||
</summary> */
|
||||
|
||||
public class BIAS_Series : TSeries {
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
private readonly SMA_Series _sma;
|
||||
|
||||
//core constructors
|
||||
public BIAS_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"BIAS({period})";
|
||||
_sma = new(period, false);
|
||||
}
|
||||
public BIAS_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public BIAS_Series() : this(period: 0, useNaN: false) { }
|
||||
public BIAS_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public BIAS_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public BIAS_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public BIAS_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public BIAS_Series(TSeries source) : this(source, 0, false) { }
|
||||
public BIAS_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
var _s = _sma.Add(TValue,update);
|
||||
double _bias = (TValue.v / ((_s.v!=0)?_s.v:1)) - 1;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _bias);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_sma.Reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <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 : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buff_up = new();
|
||||
private readonly System.Collections.Generic.List<double> _buff_dn = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
private double _plast_value, _last_value;
|
||||
|
||||
//core constructors
|
||||
public CMO_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"CMO({period})";
|
||||
}
|
||||
public CMO_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public CMO_Series() : this(period: 0, useNaN: false) { }
|
||||
public CMO_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public CMO_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public CMO_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public CMO_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public CMO_Series(TSeries source) : this(source, 0, false) { }
|
||||
public CMO_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (update) { _last_value = _plast_value; } else { _plast_value = _last_value; }
|
||||
BufferTrim(buffer:_buff_up, (TValue.v > _last_value) ? TValue.v - _last_value : 0, period:_period, update: update);
|
||||
BufferTrim(buffer: _buff_dn, (TValue.v < _last_value) ? _last_value - TValue.v : 0, period: _period, update: update);
|
||||
_last_value = TValue.v;
|
||||
double _cmo_up = 0;
|
||||
double _cmo_dn = 0;
|
||||
for (int i = 0; i < Math.Min(_buff_up.Count, _buff_dn.Count); i++) {
|
||||
_cmo_up += _buff_up[i];
|
||||
_cmo_dn += _buff_dn[i];
|
||||
}
|
||||
double _cmo = 100 * (_cmo_up - _cmo_dn) / (_cmo_up + _cmo_dn);
|
||||
if (_cmo_up + _cmo_dn == 0) { _cmo = 0; }
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _cmo);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buff_up.Clear();
|
||||
_buff_dn.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
CUSUM: Cumulative Sum (aka Running Total)
|
||||
SUM across a period provides a rolling sum of all values across the period.
|
||||
If SUM values would be divided with period, the output would be SMA()
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/CUSUM
|
||||
</summary> */
|
||||
|
||||
public class CUSUM_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public CUSUM_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"CUSUM({period})";
|
||||
}
|
||||
public CUSUM_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public CUSUM_Series() : this(period: 0, useNaN: false) { }
|
||||
public CUSUM_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public CUSUM_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public CUSUM_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public CUSUM_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public CUSUM_Series(TSeries source) : this(source, 0, false) { }
|
||||
public CUSUM_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
double _sum = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _sum += _buffer[i]; }
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sum);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
DECAY:
|
||||
Linear decay can be modeled by a straight line with a negative slope of 1/period.
|
||||
The value decreases in a straight line from the last maximum to 0.
|
||||
Decay = Last Max - distance/period
|
||||
|
||||
Exponential decay is modeled as an exponential curve with diminishing factor of
|
||||
1-1/p
|
||||
|
||||
</summary> */
|
||||
|
||||
public class DECAY_Series : TSeries {
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
private readonly bool _exp;
|
||||
private double _pdecay, _ppdecay;
|
||||
private readonly double _dfactor;
|
||||
|
||||
//core constructors
|
||||
public DECAY_Series(int period, bool exponential, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"DECAY({period})";
|
||||
_exp = exponential;
|
||||
_dfactor = (_exp) ? 1.0 - 1.0 / (double)_period : 1 / (double)_period;
|
||||
_pdecay = _ppdecay = 0;
|
||||
}
|
||||
public DECAY_Series(TSeries source, int period, bool exponential, bool useNaN) : this(period, exponential, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public DECAY_Series() : this(period: 0, exponential: false, useNaN: false) { }
|
||||
public DECAY_Series(int period) : this(period: period, exponential: false, useNaN: false) { }
|
||||
public DECAY_Series(TBars source) : this(source.Close, period: 0, exponential: false, useNaN: false) { }
|
||||
public DECAY_Series(TBars source, int period) : this(source.Close, period: period, exponential:false, useNaN:false) { }
|
||||
public DECAY_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, exponential: false, useNaN) { }
|
||||
public DECAY_Series(TSeries source) : this(source, period: 0, exponential: false, useNaN:false) { }
|
||||
public DECAY_Series(TSeries source, int period) : this(source: source, period: period, exponential: false, useNaN: false) { }
|
||||
public DECAY_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, exponential: false, useNaN: useNaN) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (double.IsNaN(TValue.v)) {
|
||||
return base.Add((TValue.t, Double.NaN), update);
|
||||
}
|
||||
if (update) { _pdecay = _ppdecay; }
|
||||
else { _ppdecay = _pdecay; }
|
||||
|
||||
if (this.Count == 0) { _pdecay = TValue.v; }
|
||||
double _decay = Math.Max(TValue.v, Math.Max((_exp) ? _pdecay * _dfactor : _pdecay - _dfactor, 0));
|
||||
_pdecay = _decay;
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _decay);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_pdecay = _ppdecay = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <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 : TSeries {
|
||||
private double _k;
|
||||
private double _sum, _oldsum;
|
||||
private double _lastema1, _oldema1, _lastema2, _oldema2;
|
||||
private int _len;
|
||||
private readonly bool _useSMA;
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructor
|
||||
public DEMA_Series(int period, bool useNaN, bool useSMA) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
_useSMA = useSMA;
|
||||
Name = $"DEMA({period})";
|
||||
_k = 2.0 / (_period + 1);
|
||||
_len = 0;
|
||||
_sum = _oldsum = _lastema1 = _lastema2 = 0;
|
||||
}
|
||||
//generic constructors (source)
|
||||
|
||||
public DEMA_Series() : this(0, false, true) {}
|
||||
public DEMA_Series(int period) : this(period, false, true) {}
|
||||
public DEMA_Series(TBars source) : this(source.Close, 0, false) {}
|
||||
public DEMA_Series(TBars source, int period) : this(source.Close, period, false) {}
|
||||
public DEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
|
||||
public DEMA_Series(TSeries source, int period) : this(source, period, false, true) {}
|
||||
public DEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
|
||||
public DEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (update) {
|
||||
_lastema1 = _oldema1;
|
||||
_lastema2 = _oldema2;
|
||||
_sum = _oldsum;
|
||||
}
|
||||
else {
|
||||
_oldema1 = _lastema1;
|
||||
_oldema2 = _lastema2;
|
||||
_oldsum = _sum;
|
||||
_len++;
|
||||
}
|
||||
|
||||
if (_period == 0) {
|
||||
_k = 2.0 / (_len + 1);
|
||||
}
|
||||
|
||||
double _ema1, _ema2, _dema;
|
||||
if (Count == 0) {
|
||||
_ema1 = _ema2 = _sum = TValue.v;
|
||||
}
|
||||
else if (_len <= _period && _useSMA && _period != 0) {
|
||||
_sum += TValue.v;
|
||||
_ema1 = _sum / Math.Min(_len, _period);
|
||||
_ema2 = _ema1;
|
||||
}
|
||||
else {
|
||||
_ema1 = (TValue.v - _lastema1) * _k + _lastema1;
|
||||
_ema2 = (_ema1 - _lastema2) * _k + _lastema2;
|
||||
}
|
||||
|
||||
_dema = 2 * _ema1 - _ema2;
|
||||
|
||||
_lastema1 = double.IsNaN(_ema1) ? _lastema1 : _ema1;
|
||||
_lastema2 = double.IsNaN(_ema2) ? _lastema2 : _ema2;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _dema);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
//variation of Add()
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) {
|
||||
return (DateTime.Today, double.NaN);
|
||||
}
|
||||
|
||||
foreach (var item in data) {
|
||||
Add(item, false);
|
||||
}
|
||||
|
||||
return _data.Last;
|
||||
}
|
||||
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return Add(_data.Last, update);
|
||||
}
|
||||
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(_data.Last, false);
|
||||
}
|
||||
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(_data.Last, e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_sum = _oldsum = _lastema1 = _lastema2 = 0;
|
||||
_len = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/* <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 : TSeries {
|
||||
private readonly List<double> _buffer = new();
|
||||
private List<double> _weights = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
protected int _len;
|
||||
|
||||
//core constructors
|
||||
public DWMA_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"DWMA({period})";
|
||||
_len = 0;
|
||||
_weights = CalculateWeights(_period);
|
||||
}
|
||||
|
||||
public DWMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
|
||||
public DWMA_Series() : this(0, false) {
|
||||
}
|
||||
|
||||
public DWMA_Series(int period) : this(period, false) {
|
||||
}
|
||||
|
||||
public DWMA_Series(TBars source) : this(source.Close, 0, false) {
|
||||
}
|
||||
|
||||
public DWMA_Series(TBars source, int period) : this(source.Close, period, false) {
|
||||
}
|
||||
|
||||
public DWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {
|
||||
}
|
||||
|
||||
public DWMA_Series(TSeries source, int period) : this(source, period, false) {
|
||||
}
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(_buffer, TValue.v, _period, update);
|
||||
if (_period == 0) {
|
||||
_len++;
|
||||
_weights = CalculateWeights(_len);
|
||||
}
|
||||
|
||||
double _dwma = 0, _wsum = 0;
|
||||
var bufferCount = _buffer.Count;
|
||||
|
||||
var lockObj = new object();
|
||||
Parallel.For(0, bufferCount, i =>
|
||||
{
|
||||
var temp = _buffer[i] * _weights[i];
|
||||
lock (lockObj) {
|
||||
_dwma += temp;
|
||||
_wsum += _weights[i];
|
||||
}
|
||||
});
|
||||
_dwma /= _wsum;
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _dwma);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) {
|
||||
return (DateTime.Today, double.NaN);
|
||||
}
|
||||
|
||||
foreach (var item in data) {
|
||||
Add(item, false);
|
||||
}
|
||||
|
||||
return _data.Last;
|
||||
}
|
||||
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return Add(_data.Last, update);
|
||||
}
|
||||
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(_data.Last, false);
|
||||
}
|
||||
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(_data.Last, e.update);
|
||||
}
|
||||
|
||||
//calculating weights
|
||||
private static List<double> CalculateWeights(int period) {
|
||||
var weights = new List<double>(period);
|
||||
for (var i = 0; i < period; i++) {
|
||||
weights.Add((i + 1) * (i + 1));
|
||||
}
|
||||
|
||||
return weights;
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_len = 0;
|
||||
_buffer.Clear();
|
||||
_weights = CalculateWeights(_period);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
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 : TSeries {
|
||||
private double _k;
|
||||
private double _lastema, _oldema;
|
||||
private double _sum, _oldsum;
|
||||
private int _len;
|
||||
private readonly bool _useSMA;
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
|
||||
public EMA_Series(int period, bool useNaN, bool useSMA) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
_useSMA = useSMA;
|
||||
Name = $"EMA({period})";
|
||||
_k = 2.0 / (_period + 1);
|
||||
_len = 0;
|
||||
_sum = _oldsum = _lastema = _oldema = 0;
|
||||
}
|
||||
public EMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public EMA_Series() : this(0, false, true) {}
|
||||
public EMA_Series(int period) : this(period, false, true) {}
|
||||
public EMA_Series(TBars source) : this(source.Close, 0, false) {}
|
||||
public EMA_Series(TBars source, int period) : this(source.Close, period, false) {}
|
||||
public EMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
|
||||
public EMA_Series(TSeries source, int period) : this(source, period, false, true) {}
|
||||
public EMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
|
||||
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
|
||||
if (update) {
|
||||
_lastema = _oldema;
|
||||
_sum = _oldsum;
|
||||
}
|
||||
else {
|
||||
_oldema = _lastema;
|
||||
_oldsum = _sum;
|
||||
_len++;
|
||||
}
|
||||
|
||||
double _ema = 0;
|
||||
if (_period == 0) {
|
||||
_k = 2.0 / (_len + 1);
|
||||
}
|
||||
|
||||
if (Count == 0) {
|
||||
_ema = _sum = TValue.v;
|
||||
}
|
||||
else if (_len <= _period && _useSMA && _period != 0) {
|
||||
_sum += TValue.v;
|
||||
if (_period != 0 && _len > _period) {
|
||||
_sum -= _data[Count - _period - (update ? 1 : 0)].v;
|
||||
}
|
||||
|
||||
_ema = _sum / Math.Min(_len, _period);
|
||||
}
|
||||
else {
|
||||
_ema = _k * (TValue.v - _lastema) + _lastema;
|
||||
}
|
||||
|
||||
_lastema = double.IsNaN(_ema) ? _lastema : _ema;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _ema);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
//variation of Add()
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_sum = _oldsum = _lastema = _oldema = 0;
|
||||
_len = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
ENTROPY:
|
||||
Introduced by Claude Shannon in 1948, entropy measures the unpredictability
|
||||
of the data, or equivalently, of its average information.
|
||||
|
||||
Calculation:
|
||||
P = close / Σ(close)
|
||||
ENTROPY = Σ(-P * Log(P) / Log(base))
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Entropy_(information_theory)
|
||||
https://math.stackexchange.com/questions/3428693/how-to-calculate-entropy-from-a-set-of-correlated-samples
|
||||
|
||||
</summary> */
|
||||
|
||||
public class ENTROPY_Series : TSeries {
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
private readonly double _logbase;
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
private readonly System.Collections.Generic.List<double> _buff2 = new();
|
||||
|
||||
//core constructors
|
||||
public ENTROPY_Series(int period, double logbase, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
_logbase = logbase;
|
||||
Name = $"ENTROPY({period})";
|
||||
}
|
||||
public ENTROPY_Series(TSeries source, int period, double logbase, bool useNaN) : this(period, logbase, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public ENTROPY_Series() : this(period: 0, logbase: 2.0, useNaN: false) { }
|
||||
public ENTROPY_Series(int period) : this(period: period, logbase: 2.0, useNaN: false) { }
|
||||
public ENTROPY_Series(TBars source) : this(source.Close, period: 0, logbase: 2.0, useNaN: false) { }
|
||||
public ENTROPY_Series(TBars source, int period) : this(source.Close, period, logbase: 2.0, useNaN: false) { }
|
||||
public ENTROPY_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, logbase: 2.0, useNaN: useNaN) { }
|
||||
public ENTROPY_Series(TSeries source) : this(source, period: 0, logbase: 2.0, useNaN: false) { }
|
||||
public ENTROPY_Series(TSeries source, int period) : this(source: source, period: period, logbase: 2.0, useNaN: false) { }
|
||||
public ENTROPY_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, logbase: 2.0, useNaN: useNaN) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (double.IsNaN(TValue.v)) {
|
||||
return base.Add((TValue.t, Double.NaN), update);
|
||||
}
|
||||
BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
|
||||
double _sum = _buffer.Sum();
|
||||
double _pp = this._buffer[^1] / _sum;
|
||||
double _ppp = -_pp * Math.Log(_pp) / Math.Log(this._logbase);
|
||||
BufferTrim(_buff2, _ppp, _period, update);
|
||||
double _entp = _buff2.Sum();
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _entp);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
_buff2.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Numerics;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
FWMA: Fibonacci's Weighted Moving Average is similar to a Weighted Moving Average
|
||||
(WMA) where the weights are based on the Fibonacci Sequence.
|
||||
|
||||
</summary> */
|
||||
public class FWMA_Series : TSeries {
|
||||
private readonly List<double> _buffer = new();
|
||||
private List<double> _weights = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
protected int _len;
|
||||
|
||||
public FWMA_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"FWMA({period})";
|
||||
_len = 0;
|
||||
_weights = CalculateWeights(_period);
|
||||
}
|
||||
|
||||
public FWMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
|
||||
public FWMA_Series() : this(period: 0, useNaN: false) { }
|
||||
public FWMA_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public FWMA_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public FWMA_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public FWMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public FWMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
|
||||
if (_period == 0) {
|
||||
_len++;
|
||||
_weights = CalculateWeights(_len);
|
||||
}
|
||||
double _fwma = 0;
|
||||
double totalWeights = _weights.Sum();
|
||||
object lockObj = new object();
|
||||
Parallel.For(0, _buffer.Count, i =>
|
||||
{
|
||||
double temp = _buffer[i] * _weights[i];
|
||||
lock (lockObj) { _fwma += temp; }
|
||||
});
|
||||
_fwma /= totalWeights;
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _fwma);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
private static List<double> CalculateWeights(int period) {
|
||||
//to prevent overflow, max period can be no more than 1476
|
||||
period = (period > 1476) ? 1476 : period;
|
||||
List<double> weights = new List<double>(period);
|
||||
BigInteger a = 0;
|
||||
BigInteger b = 1;
|
||||
for (int i = 0; i < period; i++) {
|
||||
BigInteger temp = a;
|
||||
a = b;
|
||||
b = temp + b;
|
||||
weights.Add((double)Decimal.Parse(a.ToString()));
|
||||
}
|
||||
return weights;
|
||||
}
|
||||
|
||||
public override void Reset() {
|
||||
_weights = CalculateWeights(_period);
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <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 : TSeries {
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
private double _k1, _k2, _k3;
|
||||
private int _len;
|
||||
private double _lastema1, _oldema1;
|
||||
private double _lastema2, _oldema2;
|
||||
private double _lasthema, _oldhema;
|
||||
|
||||
//core constructors
|
||||
public HEMA_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"HEMA({period})";
|
||||
CalculateK(_period, out _k1, out _k2, out _k3);
|
||||
_len = 0;
|
||||
_lastema1 = _oldema1 = _lastema2 = _oldema2 = _lasthema = _oldhema = 0;
|
||||
}
|
||||
public HEMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public HEMA_Series() : this(period: 0, useNaN: false) { }
|
||||
public HEMA_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public HEMA_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public HEMA_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public HEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public HEMA_Series(TSeries source) : this(source, 0, false) { }
|
||||
public HEMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (update) {
|
||||
_lastema1 = _oldema1;
|
||||
_lastema2 = _oldema2;
|
||||
_lasthema = _oldhema;
|
||||
}
|
||||
else {
|
||||
_oldema1 = _lastema1;
|
||||
_oldema2 = _lastema2;
|
||||
_oldhema = _lasthema;
|
||||
}
|
||||
double _ema1, _ema2, _hema;
|
||||
if (_period == 0) {
|
||||
_len++;
|
||||
CalculateK(_len, out _k1, out _k2, out _k3);
|
||||
}
|
||||
if (double.IsNaN(TValue.v)) {
|
||||
return base.Add((TValue.t, double.NaN), update);
|
||||
} else if (this.Count == 0) {
|
||||
_ema1 = _ema2 = _hema = TValue.v;
|
||||
}
|
||||
else {
|
||||
_ema1 = _k1 * (TValue.v - _lastema1) + _lastema1;
|
||||
_ema2 = _k2 * (TValue.v - _lastema2) + _lastema2;
|
||||
_hema = _k3 * (((2 * _ema1) - _ema2) - _lasthema) + _lasthema;
|
||||
}
|
||||
|
||||
_lastema1 = _ema1;
|
||||
_lastema2 = _ema2;
|
||||
_lasthema = _hema;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hema);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_lastema1 = _lastema2 = _lasthema = 0;
|
||||
_oldema1 = _oldema2 = _oldhema = 0;
|
||||
_len = 0;
|
||||
}
|
||||
|
||||
public static void CalculateK(int len, out double k1, out double k2, out double k3) {
|
||||
k1 = 8 / (double)(len + 7);
|
||||
k2 = 3 / (double)(len + 2);
|
||||
k3 = 2 / Math.Sqrt(len + 3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <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 {
|
||||
protected int _period, _period2, _psqrt;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
protected WMA_Series _wma1, _wma2, _wma3;
|
||||
|
||||
//core constructors
|
||||
public HMA_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_period2 = period /2;
|
||||
_psqrt = (int)Math.Sqrt(period);
|
||||
_NaN = useNaN;
|
||||
_wma1 = new(Math.Max(_period2,1), false);
|
||||
_wma2 = new(Math.Max(_period,1), false);
|
||||
_wma3 = new(Math.Max(_psqrt,1), useNaN);
|
||||
Name = $"HMA({period})";
|
||||
}
|
||||
public HMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public HMA_Series() : this(period: 0, useNaN: false) { }
|
||||
public HMA_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public HMA_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public HMA_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public HMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public HMA_Series(TSeries source) : this(source, 0, false) { }
|
||||
public HMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (_period == 0) {
|
||||
_wma1.Len = this.Count / 2;
|
||||
_wma2.Len = this.Count;
|
||||
_wma1.Len = (int)Math.Sqrt(this.Count);
|
||||
}
|
||||
double _w1 = _wma1.Add(TValue, update).v;
|
||||
double _w2 = _wma2.Add(TValue, update).v;
|
||||
double _hma = _wma3.Add((2 * _w1) - _w2, update).v;
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _hma);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_wma1.Reset();
|
||||
_wma2.Reset();
|
||||
_wma3.Reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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 : TSeries {
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
private readonly System.Collections.Generic.List<double> volty_short = new();
|
||||
private readonly System.Collections.Generic.List<double> vsum_buff = new();
|
||||
private readonly double pr;
|
||||
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;
|
||||
|
||||
//core constructors
|
||||
public JMA_Series(int period, double phase, int vshort, int vlong, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"JMA({period})";
|
||||
upperBand = lowerBand = prev_ma1 = prev_det0 = prev_det1 = prev_vsum = prev_jma = Kv = 0.0;
|
||||
pr = (phase * 0.01) + 1.5;
|
||||
if (phase < -100) { pr = 0.5; }
|
||||
if (phase > 100) { pr = 2.5; }
|
||||
_voltyS = vshort;
|
||||
_voltyL = vlong;
|
||||
}
|
||||
|
||||
public JMA_Series(TSeries source, int period, double phase, int vshort, int vlong, bool useNaN) : this(period, phase, vshort, vlong, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public JMA_Series() : this(period: 0, phase: 0, vshort:10, vlong:65, useNaN: false) { }
|
||||
public JMA_Series(int period) : this(period: period, phase: 0, vshort: 10, vlong: 65, useNaN: false) { }
|
||||
public JMA_Series(TBars source) : this(source.Close, period:0, phase:0.0, vshort:10, vlong:65, useNaN:false) { }
|
||||
public JMA_Series(TBars source, int period) : this(source.Close, period, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { }
|
||||
public JMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, phase: 0.0, vshort: 10, vlong: 65, useNaN: useNaN) { }
|
||||
public JMA_Series(TSeries source) : this(source, period:0, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { }
|
||||
public JMA_Series(TSeries source, int period) : this(source: source, period: period, phase: 0.0, vshort: 10, vlong: 65, useNaN: false) { }
|
||||
public JMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, phase: 0.0, vshort: 10, vlong: 65, useNaN: useNaN) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (this.Count == 0) { prev_ma1 = prev_jma = TValue.v; }
|
||||
if (update) {
|
||||
upperBand = p_upperBand;
|
||||
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;
|
||||
}
|
||||
|
||||
if (double.IsNaN(TValue.v)) {
|
||||
return base.Add((TValue.t, double.NaN),update);
|
||||
}
|
||||
|
||||
// from Tvalue to volty
|
||||
double del1 = TValue.v - upperBand;
|
||||
double del2 = TValue.v - lowerBand;
|
||||
upperBand = (del1 > 0) ? TValue.v : TValue.v - (Kv * del1);
|
||||
lowerBand = (del2 < 0) ? TValue.v : TValue.v - (Kv * del2);
|
||||
double volty = 0;
|
||||
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(_period)) / 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 * (_period - 1) / (0.45 * (_period - 1) + 2);
|
||||
Kv = Math.Pow(beta, Math.Sqrt(pow2));
|
||||
double alpha = Math.Pow(beta, pow2);
|
||||
double ma1 = (1 - alpha) * TValue.v + alpha * prev_ma1;
|
||||
prev_ma1 = ma1;
|
||||
|
||||
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);
|
||||
prev_det1 = det1;
|
||||
double jma = prev_jma + det1;
|
||||
prev_jma = jma;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : jma);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
upperBand = lowerBand = prev_ma1 = prev_det0 = prev_det1 = prev_vsum = prev_jma = Kv = 0.0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <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.
|
||||
|
||||
KAMA[i] = KAMA[i-1] + SC * ( price - KAMA[i-1] )
|
||||
|
||||
Sources:
|
||||
https://www.tutorialspoint.com/kaufman-s-adaptive-moving-average-kama-formula-and-how-does-it-work
|
||||
https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/kaufmans-adaptive-moving-average-kama/
|
||||
https://www.technicalindicators.net/indicators-technical-analysis/152-kama-kaufman-adaptive-moving-average
|
||||
|
||||
Remark:
|
||||
If useNaN:true argument is provided, KAMA starts calculating values from [period] bar onwards.
|
||||
Without useNaN argument (default setting), KAMA starts calculating values from bar 1 - and yields
|
||||
slightly different results for the first 50 bars - and then converges with the other one.
|
||||
|
||||
</summary> */
|
||||
|
||||
public class KAMA_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
private double _lastkama, _lastlastkama;
|
||||
private int _len;
|
||||
private readonly double _scFast, _scSlow;
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public KAMA_Series(int period, int fast, int slow, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
_len = 0;
|
||||
_scFast = 2.0 / (((period < fast) ? period : fast) + 1);
|
||||
_scSlow = 2.0 / (slow + 1);
|
||||
_lastkama = _lastlastkama = 0;
|
||||
Name = $"KAMA({period})";
|
||||
}
|
||||
public KAMA_Series(TSeries source, int period, int fast, int slow, bool useNaN) : this(period, fast, slow, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public KAMA_Series() : this(period: 0, fast: 2, slow: 30, useNaN: false) { }
|
||||
public KAMA_Series(int period) : this(period: period, fast: 2, slow: 30, useNaN: false) { }
|
||||
public KAMA_Series(TBars source) : this(source.Close, period: 0, fast: 2, slow: 30, useNaN: false) { }
|
||||
public KAMA_Series(TBars source, int period) : this(source.Close, period: period, fast: 2, slow: 30, useNaN: false) { }
|
||||
public KAMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period: period, fast: 2, slow: 30, useNaN: useNaN) { }
|
||||
public KAMA_Series(TSeries source) : this(source, period: 0, fast: 2, slow: 30, useNaN: false) { }
|
||||
public KAMA_Series(TSeries source, int period) : this(source: source, period: period, fast: 2, slow: 30, useNaN: false) { }
|
||||
public KAMA_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, fast: 2, slow: 30, useNaN: useNaN) { }
|
||||
public KAMA_Series(TSeries source, int period, int fast, int slow) : this(source: source, period: period, fast: fast, slow: slow, useNaN: false) { }
|
||||
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (double.IsNaN(TValue.v)) {
|
||||
return base.Add((TValue.t, Double.NaN), update);
|
||||
}
|
||||
|
||||
if (update) { _lastkama = _lastlastkama; }
|
||||
else { _lastlastkama = _lastkama; }
|
||||
BufferTrim(buffer: _buffer, value: TValue.v, period: _period + 1, update: update);
|
||||
|
||||
double _kama = 0;
|
||||
if (this.Count < _period) { _kama = TValue.v; }
|
||||
else {
|
||||
double _change = Math.Abs(_buffer[^1] - _buffer[(_buffer.Count > _period + 1) ? 1 : 0]);
|
||||
double _sumpv = 0;
|
||||
for (int i = 1; i < _buffer.Count; i++) { _sumpv += Math.Abs(_buffer[(_buffer.Count > 0) ? i : 0] - _buffer[i - 1]); }
|
||||
double _er = (_sumpv == 0) ? 0 : _change / _sumpv;
|
||||
double _sc = (_er * (_scFast - _scSlow)) + _scSlow;
|
||||
_kama = (_lastkama + (_sc * _sc * (TValue.v - _lastkama)));
|
||||
}
|
||||
_len++;
|
||||
_lastkama = _kama;
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _kama);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
_len = 0;
|
||||
_lastkama = _lastlastkama = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
KURTOSIS: Kurtosis of population
|
||||
Kurtosis characterizes the relative peakedness or flatness of a distribution
|
||||
compared with the normal distribution. Positive kurtosis indicates a relatively
|
||||
peaked distribution. Negative kurtosis indicates a relatively flat distribution.
|
||||
|
||||
The normal curve is called Mesokurtic curve. If the curve of a distribution is
|
||||
more outlier prone (or heavier-tailed) than a normal or mesokurtic curve then
|
||||
it is referred to as a Leptokurtic curve. If a curve is less outlier prone (or
|
||||
lighter-tailed) than a normal curve, it is called as a platykurtic curve.
|
||||
|
||||
Calculation:
|
||||
sum4 = Σ(close-SMA)^4
|
||||
sum2 = (Σ(close-SMA)^2)^2
|
||||
KURTOSIS = length * (sum4/sum2)
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Kurtosis
|
||||
https://stats.oarc.ucla.edu/other/mult-pkg/faq/general/faq-whats-with-the-different-formulas-for-kurtosis/
|
||||
|
||||
</summary> */
|
||||
|
||||
public class KURTOSIS_Series : TSeries {
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
//core constructors
|
||||
public KURTOSIS_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"KURTOSIS({period})";
|
||||
}
|
||||
public KURTOSIS_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public KURTOSIS_Series() : this(period: 0, useNaN: false) { }
|
||||
public KURTOSIS_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public KURTOSIS_Series(TSeries source) : this(source, period: 0, useNaN: false) { }
|
||||
public KURTOSIS_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (double.IsNaN(TValue.v)) {
|
||||
return base.Add((TValue.t, Double.NaN), update);
|
||||
}
|
||||
|
||||
BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
|
||||
double _n = _buffer.Count;
|
||||
double _avg = _buffer.Average();
|
||||
|
||||
double _s2 = 0;
|
||||
double _s4 = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++) {
|
||||
_s2 += (_buffer[i] - _avg) * (_buffer[i] - _avg);
|
||||
_s4 += (_buffer[i] - _avg) * (_buffer[i] - _avg) * (_buffer[i] - _avg) * (_buffer[i] - _avg);
|
||||
}
|
||||
|
||||
double _Vx = _s2 / (_n - 1);
|
||||
double _kurt = (_n > 3) ?
|
||||
(_n * (_n + 1) * _s4) / (_Vx * _Vx * (_n - 3) * (_n - 1) * (_n - 2)) - (3 * (_n - 1) * (_n - 1) / ((_n - 2) * (_n - 3))) //using Sheskin Algo
|
||||
: (_s2 * _s2) / _n - 3; //using Snedecor and Cochran (1967) algo
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _kurt);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
MAD: Mean Absolute Deviation
|
||||
Also known as AAD - Average Absolute Deviation, to differentiate it from Median Absolute Deviation
|
||||
MAD defines the degree of variation across the series.
|
||||
|
||||
Calculation:
|
||||
MAD = Σ(|close-SMA|) / period
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Average_absolute_deviation
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MAD_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public MAD_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"MAD({period})";
|
||||
}
|
||||
public MAD_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public MAD_Series() : this(period: 0, useNaN: false) { }
|
||||
public MAD_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public MAD_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public MAD_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public MAD_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public MAD_Series(TSeries source) : this(source, 0, false) { }
|
||||
public MAD_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
double _sma = _buffer.Average();
|
||||
double _mad = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _mad += Math.Abs(_buffer[i] - _sma); }
|
||||
_mad /= this._buffer.Count;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mad);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
MAPE: Mean Absolute Percentage Error
|
||||
Measures the size of the error in percentage terms
|
||||
|
||||
Calculation:
|
||||
MAPE = Σ(|close – SMA| / |close|) / n
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Mean_absolute_percentage_error
|
||||
|
||||
Remark:
|
||||
returns infinity if any of observations is 0.
|
||||
Use SMAPE or WMAPE instead to avoid division-by-zero in MAPE
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MAPE_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public MAPE_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"MAPE({period})";
|
||||
}
|
||||
public MAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public MAPE_Series() : this(period: 0, useNaN: false) { }
|
||||
public MAPE_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public MAPE_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public MAPE_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public MAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public MAPE_Series(TSeries source) : this(source, 0, false) { }
|
||||
public MAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
double _mape = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) {
|
||||
_mape += (_buffer[i] != 0) ? Math.Abs(_buffer[i] - _sma) / Math.Abs(_buffer[i]) : double.PositiveInfinity;
|
||||
}
|
||||
_mape /= (_buffer.Count > 0) ? _buffer.Count : 1;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mape);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
MAX - Maximum value in the given period in the series.
|
||||
If period = 0 => period = full length of the series
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MAX_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public MAX_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"MAX({period})";
|
||||
}
|
||||
public MAX_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public MAX_Series() : this(period: 0, useNaN: false) { }
|
||||
public MAX_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public MAX_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public MAX_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public MAX_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public MAX_Series(TSeries source) : this(source, 0, false) { }
|
||||
public MAX_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
double _max= _buffer.Max();
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _max);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
MED - Median value
|
||||
Median of numbers is the middlemost value of the given set of numbers.
|
||||
It separates the higher half and the lower half of a given data sample.
|
||||
At least half of the observations are smaller than or equal to median
|
||||
and at least half of the observations are greater than or equal to the median.
|
||||
|
||||
If the number of values is odd, the middlemost observation of the sorted
|
||||
list is the median of the given data. If the number of values is even,
|
||||
median is the average of (n/2)th and [(n/2) + 1]th values of the sorted list.
|
||||
|
||||
If period = 0 => period is max
|
||||
|
||||
Sources:
|
||||
https://corporatefinanceinstitute.com/resources/knowledge/other/median/
|
||||
https://en.wikipedia.org/wiki/Median
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MEDIAN_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public MEDIAN_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"MEDIAN({period})";
|
||||
}
|
||||
public MEDIAN_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public MEDIAN_Series() : this(period: 0, useNaN: false) { }
|
||||
public MEDIAN_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public MEDIAN_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public MEDIAN_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public MEDIAN_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public MEDIAN_Series(TSeries source) : this(source, 0, false) { }
|
||||
public MEDIAN_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
System.Collections.Generic.List<double> _s = new(this._buffer);
|
||||
_s.Sort();
|
||||
int _p1 = _s.Count / 2;
|
||||
int _p2 = Math.Max(0, (_s.Count / 2) - 1);
|
||||
double _med = (_s.Count % 2 != 0) ? _s[_p1] : (_s[_p1] + _s[_p2]) / 2;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _med);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
MIDPOINT: Midpoint value (max+min)/2 in the given period in the series.
|
||||
If period = 0 => period = full length of the series
|
||||
|
||||
Sources:
|
||||
https://thefaqblog.com/what-is-the-midpoint-in-statistics/
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MIDPOINT_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public MIDPOINT_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"MIDPOINT({period})";
|
||||
}
|
||||
public MIDPOINT_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public MIDPOINT_Series() : this(period: 0, useNaN: false) { }
|
||||
public MIDPOINT_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public MIDPOINT_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public MIDPOINT_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public MIDPOINT_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public MIDPOINT_Series(TSeries source) : this(source, 0, false) { }
|
||||
public MIDPOINT_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
double _max= _buffer.Max();
|
||||
double _min = _buffer.Min();
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : (_max+_min)*0.5);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
MIN - Minimum value in the given period in the series.
|
||||
If period = 0 => period = full length of the series
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MIN_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public MIN_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"MAX({period})";
|
||||
}
|
||||
public MIN_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public MIN_Series() : this(period: 0, useNaN: false) { }
|
||||
public MIN_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public MIN_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public MIN_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public MIN_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public MIN_Series(TSeries source) : this(source, 0, false) { }
|
||||
public MIN_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
double _max= _buffer.Min();
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _max);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
MSE: Mean Square Error
|
||||
Defined as a Mean (Average) of the Square of the difference between actual and estimated values.
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Mean_squared_error
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MSE_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public MSE_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"MSE({period})";
|
||||
}
|
||||
public MSE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public MSE_Series() : this(period: 0, useNaN: false) { }
|
||||
public MSE_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public MSE_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public MSE_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public MSE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public MSE_Series(TSeries source) : this(source, 0, false) { }
|
||||
public MSE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
double _mse = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _mse += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
|
||||
_mse /= this._buffer.Count;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mse);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
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 : TSeries {
|
||||
private double _k;
|
||||
private double _lastrma, _oldrma;
|
||||
private double _sum, _oldsum;
|
||||
private readonly bool _useSMA;
|
||||
private int _len;
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructor
|
||||
public RMA_Series(int period, bool useNaN, bool useSMA) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
_useSMA = useSMA;
|
||||
Name = $"RMA({period})";
|
||||
_k = 1.0 / (double)(this._period);
|
||||
_len = 0;
|
||||
_sum = _oldsum = _lastrma = _oldrma = 0;
|
||||
}
|
||||
//generic constructors (source)
|
||||
|
||||
public RMA_Series() : this(0, false, true) {}
|
||||
public RMA_Series(int period) : this(period, false, true) {}
|
||||
public RMA_Series(TBars source) : this(source.Close, 0, false) {}
|
||||
public RMA_Series(TBars source, int period) : this(source.Close, period, false) {}
|
||||
public RMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
|
||||
public RMA_Series(TSeries source, int period) : this(source, period, false, true) {}
|
||||
public RMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
|
||||
public RMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
|
||||
if (update) {
|
||||
_lastrma = _oldrma;
|
||||
_sum = _oldsum;
|
||||
}
|
||||
else {
|
||||
_oldrma = _lastrma;
|
||||
_oldsum = _sum;
|
||||
_len++;
|
||||
}
|
||||
|
||||
double _rma = 0;
|
||||
if (_period == 0) {
|
||||
_k = 1.0 / (double)(this._len);
|
||||
}
|
||||
|
||||
if (Count == 0) {
|
||||
_rma = _sum = TValue.v;
|
||||
|
||||
} else if (_len <= _period && _useSMA && _period != 0) {
|
||||
_sum += TValue.v;
|
||||
if (_period != 0 && _len > _period) {
|
||||
_sum -= _data[Count - _period - (update ? 1 : 0)].v;
|
||||
}
|
||||
_rma = _sum / Math.Min(_len, _period);
|
||||
}
|
||||
else {
|
||||
_rma = _k * (TValue.v - _lastrma) + _lastrma;
|
||||
}
|
||||
|
||||
_lastrma = double.IsNaN(_rma) ? _lastrma : _rma;
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _rma);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
//variation of Add()
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_sum = _oldsum = _lastrma = _oldrma = 0;
|
||||
_len = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
RSI: Relative Strength Index
|
||||
Created by J. Welles Wilder, the Relative Strength Index measures strength
|
||||
of the winning/losing streak over N lookback periods on a scale of 0 to 100,
|
||||
to depict overbought and oversold conditions.
|
||||
|
||||
Sources:
|
||||
https://www.investopedia.com/terms/r/rsi.asp
|
||||
|
||||
</summary> */
|
||||
|
||||
public class RSI_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _gain = new();
|
||||
private readonly System.Collections.Generic.List<double> _loss = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
private double _avgGain, _avgLoss, _lastValue;
|
||||
private double _avgGain_o, _avgLoss_o, _lastValue_o;
|
||||
private int i;
|
||||
|
||||
//core constructors
|
||||
public RSI_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"RSI({period})";
|
||||
i = 0;
|
||||
}
|
||||
public RSI_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public RSI_Series() : this(period: 0, useNaN: false) { }
|
||||
public RSI_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public RSI_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public RSI_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public RSI_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public RSI_Series(TSeries source) : this(source, 0, false) { }
|
||||
public RSI_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
|
||||
double _rsi = 0;
|
||||
if (update) {
|
||||
_lastValue = _lastValue_o;
|
||||
_avgGain = _avgGain_o;
|
||||
_avgLoss = _avgLoss_o;
|
||||
}
|
||||
else {
|
||||
_lastValue_o = _lastValue;
|
||||
_avgGain_o = _avgGain;
|
||||
_avgLoss_o = _avgLoss;
|
||||
}
|
||||
|
||||
if (i == 0) { _lastValue = TValue.v; }
|
||||
|
||||
double _gainval = (TValue.v > _lastValue) ? TValue.v - _lastValue : 0;
|
||||
BufferTrim(_gain, _gainval, _period, update);
|
||||
double _lossval = (TValue.v < _lastValue) ? _lastValue - TValue.v : 0;
|
||||
BufferTrim(_loss, _lossval, _period, update);
|
||||
_lastValue = TValue.v;
|
||||
|
||||
// calculate RSI
|
||||
if (i > _period && _period != 0) {
|
||||
_avgGain = ((_avgGain * (_period - 1)) + _gain[^1]) / _period;
|
||||
_avgLoss = ((_avgLoss * (_period - 1)) + _loss[^1]) / _period;
|
||||
if (_avgLoss > 0) {
|
||||
double rs = _avgGain / _avgLoss;
|
||||
_rsi = 100 - (100 / (1 + rs));
|
||||
}
|
||||
else { _rsi = 100; }
|
||||
}
|
||||
// initialize average gain
|
||||
else {
|
||||
double _sumGain = 0;
|
||||
for (int p = 0; p < _gain.Count; p++) { _sumGain += _gain[p]; }
|
||||
double _sumLoss = 0;
|
||||
for (int p = 0; p < _loss.Count; p++) { _sumLoss += _loss[p]; }
|
||||
|
||||
_avgGain = _sumGain / _gain.Count;
|
||||
_avgLoss = _sumLoss / _loss.Count;
|
||||
|
||||
_rsi = (_avgLoss > 0) ? 100 - (100 / (1 + (_avgGain / _avgLoss))) : 100;
|
||||
}
|
||||
if (!update) { i++; }
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _rsi);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
SDEV: Population Standard Deviation
|
||||
Population Standard Deviation is the square root of the biased variance, also knons as
|
||||
Uncorrected Sample Standard Deviation
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Standard_deviation#Uncorrected_sample_standard_deviation
|
||||
|
||||
Remark:
|
||||
SDEV (Population Standard Deviation) is also known as a biased/uncorrected Standard Deviation.
|
||||
For unbiased version that uses Bessel's correction, use SDEV instead.
|
||||
|
||||
</summary> */
|
||||
|
||||
public class SDEV_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public SDEV_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"SDEV({period})";
|
||||
}
|
||||
public SDEV_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public SDEV_Series() : this(period: 0, useNaN: false) { }
|
||||
public SDEV_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public SDEV_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public SDEV_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public SDEV_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public SDEV_Series(TSeries source) : this(source, 0, false) { }
|
||||
public SDEV_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
double _var = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _var += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
|
||||
_var /= this._buffer.Count;
|
||||
double _sdev = Math.Sqrt(_var);
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sdev);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
SMAPE: Symmetric Mean Absolute Percentage Error
|
||||
Measures the size of the error in percentage terms
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error
|
||||
|
||||
</summary> */
|
||||
|
||||
public class SMAPE_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public SMAPE_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"SMAPE({period})";
|
||||
}
|
||||
public SMAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public SMAPE_Series() : this(period: 0, useNaN: false) { }
|
||||
public SMAPE_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public SMAPE_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public SMAPE_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public SMAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public SMAPE_Series(TSeries source) : this(source, 0, false) { }
|
||||
public SMAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
|
||||
double _smape = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _smape += Math.Abs(_buffer[i] - _sma) / (Math.Abs(_buffer[i]) + Math.Abs(_sma)); }
|
||||
_smape /= this._buffer.Count;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _smape);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <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 : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
private double _sum, _oldsum;
|
||||
private readonly int _period;
|
||||
private readonly TSeries _data;
|
||||
protected readonly bool _NaN;
|
||||
|
||||
//core constructor
|
||||
public SMA_Series(int period, bool useNaN) : base() {
|
||||
_period = Math.Max(0, period);
|
||||
_NaN = useNaN;
|
||||
Name = $"SMA({period})";
|
||||
_sum = _oldsum = 0;
|
||||
}
|
||||
public SMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public SMA_Series() : this(0, false) {}
|
||||
public SMA_Series(int period) : this(period, false) {}
|
||||
public SMA_Series(TBars source) : this(source.Close, 0, false) {}
|
||||
public SMA_Series(TBars source, int period) : this(source.Close, period, false) {}
|
||||
public SMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
|
||||
public SMA_Series(TSeries source) : this(source, 0, false) {}
|
||||
public SMA_Series(TSeries source, int period) : this(source, period, false) {}
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
|
||||
if (double.IsNaN(TValue.v)) { return (TValue.t, double.NaN);
|
||||
} else {
|
||||
if (update && _buffer.Count > 0) {
|
||||
_sum -= _buffer[^1];
|
||||
_buffer[^1] = TValue.v;
|
||||
_oldsum = _sum;
|
||||
}
|
||||
else {
|
||||
_buffer.Add(TValue.v);
|
||||
_oldsum = _sum;
|
||||
}
|
||||
|
||||
_sum += TValue.v;
|
||||
if (_period != 0 && _buffer.Count > _period) {
|
||||
_sum -= _buffer[0];
|
||||
_buffer.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
double _div = _period == 0 ? _buffer.Count : Math.Min(_buffer.Count, _period);
|
||||
var _sma = _sum / _div;
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _sma);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_sum = _oldsum = 0;
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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 : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
private double _lastsmma, _lastlastsmma;
|
||||
|
||||
//core constructors
|
||||
public SMMA_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"SMMA({period})";
|
||||
}
|
||||
public SMMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public SMMA_Series() : this(period: 0, useNaN: false) { }
|
||||
public SMMA_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public SMMA_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public SMMA_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public SMMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public SMMA_Series(TSeries source) : this(source, 0, false) { }
|
||||
public SMMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (double.IsNaN(TValue.v)) {
|
||||
return base.Add((TValue.t, double.NaN),update);
|
||||
}
|
||||
|
||||
double _smma = 0;
|
||||
if (update) { this._lastsmma = this._lastlastsmma; }
|
||||
|
||||
if (this.Count < this._period) {
|
||||
BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
|
||||
_smma = _buffer.Average();
|
||||
}
|
||||
else {
|
||||
_smma = ((_lastsmma * (_period - 1)) + TValue.v) / _period;
|
||||
}
|
||||
|
||||
this._lastlastsmma = this._lastsmma;
|
||||
this._lastsmma = _smma;
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _smma);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
this._lastsmma = this._lastlastsmma = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
SSDEV: (Corrected) Sample Standard Deviation
|
||||
Sample Standard Deviaton uses Bessel's correction to correct the bias in the variance.
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Standard_deviation#Corrected_sample_standard_deviation
|
||||
Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction
|
||||
|
||||
Remark:
|
||||
SSDEV (Sample Standard Deviation) is also known as a unbiased/corrected Standard Deviation.
|
||||
For a population/biased/uncorrected Standard Deviation, use PSDEV instead
|
||||
|
||||
</summary> */
|
||||
|
||||
public class SSDEV_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public SSDEV_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"SSDEV({period})";
|
||||
}
|
||||
public SSDEV_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public SSDEV_Series() : this(period: 0, useNaN: false) { }
|
||||
public SSDEV_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public SSDEV_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public SSDEV_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public SSDEV_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public SSDEV_Series(TSeries source) : this(source, 0, false) { }
|
||||
public SSDEV_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
double _svar = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++) { _svar += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
|
||||
_svar /= (_buffer.Count > 1) ? _buffer.Count - 1 : 1; // Bessel's correction
|
||||
double _ssdev = Math.Sqrt(_svar);
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _ssdev);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
VAR: Population Variance
|
||||
Population variance without Bessel's correction
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Variance
|
||||
Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction
|
||||
|
||||
Remark:
|
||||
VAR (Population Variance) is also known as a biased Sample Variance. For unbiased
|
||||
sample variance use SVAR instead.
|
||||
|
||||
</summary> */
|
||||
|
||||
public class SVAR_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public SVAR_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"SVAR({period})";
|
||||
}
|
||||
public SVAR_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public SVAR_Series() : this(period: 0, useNaN: false) { }
|
||||
public SVAR_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public SVAR_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public SVAR_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public SVAR_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public SVAR_Series(TSeries source) : this(source, 0, false) { }
|
||||
public SVAR_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
double _svar = 0;
|
||||
for (int i = 0; i < this._buffer.Count; i++) { _svar += (this._buffer[i] - _sma) * (this._buffer[i] - _sma); }
|
||||
_svar /= (this._buffer.Count > 1) ? this._buffer.Count - 1 : 1; // Bessel's correction
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _svar);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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 : TSeries {
|
||||
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 readonly bool _useSMA;
|
||||
private double _lastema1, _lastema2, _lastema3, _lastema4, _lastema5, _lastema6;
|
||||
private double _llastema1, _llastema2, _llastema3, _llastema4, _llastema5, _llastema6;
|
||||
protected int _len;
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public T3_Series(int period, double vfactor, bool useSMA, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_len = 0;
|
||||
_NaN = useNaN;
|
||||
Name = $"T3({period})";
|
||||
_useSMA = useSMA;
|
||||
double _a = vfactor; //0.7; //0.618
|
||||
_c1 = -_a * _a * _a;
|
||||
_c2 = 3 * _a * _a + 3 * _a * _a * _a;
|
||||
_c3 = -6 * _a * _a - 3 * _a - 3 * _a * _a * _a;
|
||||
_c4 = 1 + 3 * _a + _a * _a * _a + 3 * _a * _a;
|
||||
|
||||
_k = 2.0 / (_period + 1);
|
||||
_k1m = 1.0 - _k;
|
||||
_lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = _lastema4 = _llastema4 = _lastema5 = _llastema5 = _lastema5 = _llastema5 = 0;
|
||||
}
|
||||
public T3_Series(TSeries source, int period, double vfactor, bool useSMA, bool useNaN) : this(period, vfactor, useSMA, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public T3_Series() : this(period: 0, vfactor: 0.7, useSMA: true, useNaN: false) { }
|
||||
public T3_Series(int period) : this(period: period, vfactor: 0.7, useSMA: true, useNaN: false) { }
|
||||
public T3_Series(TBars source) : this(source.Close, 0, vfactor: 0.7, useSMA: true, useNaN: false) { }
|
||||
public T3_Series(TBars source, int period) : this(source.Close, period, vfactor: 0.7, useSMA: true, useNaN: false) { }
|
||||
public T3_Series(TBars source, int period, bool useNaN) : this(source.Close, period, vfactor: 0.7, useSMA: true, useNaN: useNaN) { }
|
||||
public T3_Series(TBars source, int period, double vfactor, bool useNaN) : this(source.Close, period, vfactor: vfactor, useSMA: true, useNaN: useNaN) { }
|
||||
public T3_Series(TBars source, int period, bool useSMA, bool useNaN) : this(source.Close, period, vfactor: 0.7, useSMA: useSMA, useNaN: useNaN) { }
|
||||
public T3_Series(TSeries source) : this(source, 0, vfactor: 0.7, useSMA: true, useNaN: false) { }
|
||||
public T3_Series(TSeries source, int period) : this(source: source, period: period, vfactor: 0.7, useSMA: true, useNaN: false) { }
|
||||
public T3_Series(TSeries source, int period, bool useNaN) : this(source: source, period: period, vfactor: 0.7, useSMA: true, useNaN: useNaN) { }
|
||||
public T3_Series(TSeries source, int period, double vfactor) : this(source: source, period: period, vfactor: vfactor, useSMA: true, useNaN: false) { }
|
||||
public T3_Series(TSeries source, int period, double vfactor, bool useNaN) : this(source: source, period: period, vfactor: vfactor, useSMA: true, useNaN: useNaN) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
double _ema1, _ema2, _ema3, _ema4, _ema5, _ema6;
|
||||
if (double.IsNaN(TValue.v)) {
|
||||
return base.Add((TValue.t, Double.NaN),update);
|
||||
}
|
||||
|
||||
if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; _lastema4 = _llastema4; _lastema5 = _llastema5; _lastema6 = _llastema6; }
|
||||
else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; _llastema4 = _lastema4; _llastema5 = _lastema5; _llastema6 = _lastema6; }
|
||||
|
||||
if (_len == 0) { _lastema1 = _lastema2 = _lastema3 = _lastema4 = _lastema5 = _lastema6 = TValue.v; }
|
||||
|
||||
|
||||
if ((_len < _period) && _useSMA) {
|
||||
BufferTrim(_buffer1, TValue.v, _period, update);
|
||||
_ema1 = 0;
|
||||
for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; }
|
||||
_ema1 /= _buffer1.Count;
|
||||
|
||||
BufferTrim(_buffer2, _ema1, _period, update);
|
||||
_ema2 = 0;
|
||||
for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; }
|
||||
_ema2 /= _buffer2.Count;
|
||||
|
||||
BufferTrim(_buffer3, _ema2, _period, update);
|
||||
_ema3 = 0;
|
||||
for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; }
|
||||
_ema3 /= _buffer3.Count;
|
||||
|
||||
BufferTrim(_buffer4, _ema3, _period, update);
|
||||
_ema4 = 0;
|
||||
for (int i = 0; i < _buffer4.Count; i++) { _ema4 += _buffer4[i]; }
|
||||
_ema4 /= _buffer4.Count;
|
||||
|
||||
BufferTrim(_buffer5, _ema4, _period, update);
|
||||
_ema5 = 0;
|
||||
for (int i = 0; i < _buffer5.Count; i++) { _ema5 += _buffer5[i]; }
|
||||
_ema5 /= _buffer5.Count;
|
||||
|
||||
BufferTrim(_buffer6, _ema5, _period, update);
|
||||
_ema6 = 0;
|
||||
for (int i = 0; i < _buffer6.Count; i++) { _ema6 += _buffer6[i]; }
|
||||
_ema6 /= _buffer6.Count;
|
||||
}
|
||||
else {
|
||||
_ema1 = (TValue.v * this._k) + (this._lastema1 * this._k1m);
|
||||
_ema2 = (_ema1 * this._k) + (this._lastema2 * this._k1m);
|
||||
_ema3 = (_ema2 * this._k) + (this._lastema3 * this._k1m);
|
||||
_ema4 = (_ema3 * this._k) + (this._lastema4 * this._k1m);
|
||||
_ema5 = (_ema4 * this._k) + (this._lastema5 * this._k1m);
|
||||
_ema6 = (_ema5 * this._k) + (this._lastema6 * this._k1m);
|
||||
}
|
||||
_len++;
|
||||
_lastema1 = _ema1;
|
||||
_lastema2 = _ema2;
|
||||
_lastema3 = _ema3;
|
||||
_lastema4 = _ema4;
|
||||
_lastema5 = _ema5;
|
||||
_lastema6 = _ema6;
|
||||
|
||||
double _T3 = _c1 * _ema6 + _c2 * _ema5 + _c3 * _ema4 + _c4 * _ema3;
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _T3);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = _lastema4 = _llastema4 = _lastema5 = _llastema5 = _lastema5 = _llastema5 = 0;
|
||||
_buffer1.Clear();
|
||||
_buffer2.Clear();
|
||||
_buffer3.Clear();
|
||||
_buffer4.Clear();
|
||||
_buffer5.Clear();
|
||||
_buffer6.Clear();
|
||||
_len = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
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)>
|
||||
{
|
||||
public string Name { get; set; }
|
||||
private readonly TSeries _open = new("open");
|
||||
private readonly TSeries _high = new("high");
|
||||
private readonly TSeries _low = new("low");
|
||||
private readonly TSeries _close = new("close");
|
||||
private readonly TSeries _volume = new("volume");
|
||||
private readonly TSeries _hl2 = new("HL2");
|
||||
private readonly TSeries _oc2 = new("OC2");
|
||||
private readonly TSeries _ohl3 = new("OHL3");
|
||||
private readonly TSeries _hlc3 = new("HLC3");
|
||||
private readonly TSeries _ohlc4 = new("OHLC4");
|
||||
private readonly TSeries _hlcc4 = new("HLCC4");
|
||||
|
||||
public TSeries Open => this._open;
|
||||
public TSeries High => this._high;
|
||||
public TSeries Low => this._low;
|
||||
public TSeries Close => this._close;
|
||||
public TSeries Volume => this._volume;
|
||||
public TSeries HL2 => this._hl2;
|
||||
public TSeries OC2 => this._oc2;
|
||||
public TSeries OHL3 => this._ohl3;
|
||||
public TSeries HLC3 => this._hlc3;
|
||||
public TSeries OHLC4 => this._ohlc4;
|
||||
public TSeries HLCC4 => this._hlcc4;
|
||||
|
||||
public TBars() { }
|
||||
|
||||
public TBars(string Name) {
|
||||
this.Name = Name;
|
||||
}
|
||||
|
||||
public (DateTime t, double o, double h, double l, double c, double v) Last => this[^1];
|
||||
public TBars Tail(int count = 10)
|
||||
{
|
||||
TBars outBars = new();
|
||||
if (count > this.Count) { count = this.Count; }
|
||||
for (int i = this.Count - count; i < this.Count; i++) { outBars.Add(this[i]); }
|
||||
return outBars;
|
||||
}
|
||||
public TSeries Select(int source)
|
||||
{
|
||||
return source switch
|
||||
{
|
||||
0 => _open,
|
||||
1 => _high,
|
||||
2 => _low,
|
||||
3 => _close,
|
||||
4 => _hl2,
|
||||
5 => _oc2,
|
||||
6 => _ohl3,
|
||||
7 => _hlc3,
|
||||
8 => _ohlc4,
|
||||
_ => _hlcc4,
|
||||
};
|
||||
}
|
||||
public static string SelectStr(int source)
|
||||
{
|
||||
return source switch
|
||||
{
|
||||
0 => "Open",
|
||||
1 => "High",
|
||||
2 => "Low",
|
||||
3 => "Close",
|
||||
4 => "HL2",
|
||||
5 => "OC2",
|
||||
6 => "OHL3",
|
||||
7 => "HLC3",
|
||||
8 => "OHLC4",
|
||||
_ => "HLCC4",
|
||||
};
|
||||
}
|
||||
|
||||
public virtual (DateTime t, double o, double h, double l, double c, double v) Add((double o, double h, double l, double c, double v) p, bool update = false) =>
|
||||
Add((t: (this.Count == 0) ? DateTime.Today : this[^1].t.AddDays(1),p.o,p.h,p.l,p.c,p.v),update);
|
||||
|
||||
public virtual (DateTime t, double o, double h, double l, double c, double v) Add(double o, double h, double l, double c, double v, bool update = false) =>
|
||||
Add((o,h,l,c,v),update);
|
||||
|
||||
public virtual (DateTime t, double o, double h, double l, double c, double v) Add(DateTime t, double o, double h, double l, double c, double v, bool update = false) =>
|
||||
this.Add((t, o, h, l, c, v), update);
|
||||
|
||||
public virtual (DateTime t, double o, double h, double l, double c, double v) Add((DateTime t, double o, double h, double l, double c, double v) TBar, bool update = false) {
|
||||
if (update) { this[^1] = TBar; } else { base.Add(TBar); }
|
||||
|
||||
_open.Add((TBar.t, TBar.o), update);
|
||||
_high.Add((TBar.t, TBar.h), update);
|
||||
_low.Add((TBar.t, TBar.l), update);
|
||||
_close.Add((TBar.t, TBar.c), update);
|
||||
_volume.Add((TBar.t, TBar.v), update);
|
||||
_hl2.Add((TBar.t, (TBar.h + TBar.l) * 0.5), update);
|
||||
_oc2.Add((TBar.t, (TBar.o + TBar.c) * 0.5), update);
|
||||
_ohl3.Add((TBar.t, (TBar.o + TBar.h + TBar.l) * 0.333333333333333), update);
|
||||
_hlc3.Add((TBar.t, (TBar.h + TBar.l + TBar.c) * 0.333333333333333), update);
|
||||
_ohlc4.Add((TBar.t, (TBar.o + TBar.h + TBar.l + TBar.c) * 0.25), update);
|
||||
_hlcc4.Add((TBar.t, (TBar.h + TBar.l + TBar.c + TBar.c) * 0.25), update);
|
||||
|
||||
this.OnEvent(update);
|
||||
return TBar;
|
||||
}
|
||||
|
||||
public delegate void NewDataEventHandler(object source, TSeriesEventArgs args);
|
||||
public event NewDataEventHandler Pub;
|
||||
protected virtual void OnEvent(bool update = false) { if (Pub != null && Pub.Target != this) {
|
||||
Pub(this, new TSeriesEventArgs { update = update }); } }
|
||||
|
||||
public void Sub(object source, TSeriesEventArgs e) { TBars ss = (TBars)source; if (ss.Count > 1) {
|
||||
for (int i = 0; i < ss.Count; i++) { this.Add(ss[i]); }
|
||||
} else {
|
||||
this.Add(ss[ss.Count - 1], e.update);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
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 : TSeries {
|
||||
private double _k;
|
||||
private double _sum, _oldsum;
|
||||
private double _lastema1, _oldema1, _lastema2, _oldema2, _lastema3, _oldema3;
|
||||
private int _len;
|
||||
private readonly bool _useSMA;
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructor
|
||||
public TEMA_Series(int period, bool useNaN, bool useSMA) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
_useSMA = useSMA;
|
||||
Name = $"TEMA({period})";
|
||||
_k = 2.0 / (_period + 1);
|
||||
_len = 0;
|
||||
_sum = _oldsum = _lastema1 = _lastema2 = _lastema3 = 0;
|
||||
}
|
||||
public TEMA_Series() : this(0, false, true) {}
|
||||
public TEMA_Series(int period) : this(period, false, true) {}
|
||||
public TEMA_Series(TBars source) : this(source.Close, 0, false) {}
|
||||
public TEMA_Series(TBars source, int period) : this(source.Close, period, false) {}
|
||||
public TEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
|
||||
public TEMA_Series(TSeries source, int period) : this(source, period, false, true) {}
|
||||
public TEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
|
||||
public TEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (update) {
|
||||
_lastema1 = _oldema1;
|
||||
_lastema2 = _oldema2;
|
||||
_lastema3 = _oldema3;
|
||||
_sum = _oldsum;
|
||||
}
|
||||
else {
|
||||
_oldema1 = _lastema1;
|
||||
_oldema2 = _lastema2;
|
||||
_oldema3 = _lastema3;
|
||||
_oldsum = _sum;
|
||||
_len++;
|
||||
}
|
||||
|
||||
if (_period == 0) { _k = 2.0 / (_len + 1); }
|
||||
|
||||
double _ema1, _ema2, _ema3, _tema;
|
||||
if (this.Count == 0) {
|
||||
_ema1 = _ema2 = _ema3 =_sum = TValue.v;
|
||||
}
|
||||
else if (_len <= _period && _useSMA && _period != 0) {
|
||||
_sum += TValue.v;
|
||||
_ema1 = _sum / Math.Min(_len, _period);
|
||||
_ema2 = _ema1;
|
||||
_ema3 = _ema2;
|
||||
}
|
||||
else {
|
||||
_ema1 = (TValue.v - _lastema1) * _k + _lastema1;
|
||||
_ema2 = (_ema1 - _lastema2) * _k + _lastema2;
|
||||
_ema3 = (_ema2 - _lastema3) * _k + _lastema3;
|
||||
}
|
||||
|
||||
_tema = (3 * (_ema1 - _ema2)) + _ema3;
|
||||
|
||||
_lastema1 = Double.IsNaN(_ema1)?_lastema1:_ema1;
|
||||
_lastema2 = Double.IsNaN(_ema2)?_lastema2:_ema2;
|
||||
_lastema3 = Double.IsNaN(_ema3) ? _lastema3 : _ema3;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _tema);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
//variation of Add()
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_sum = _oldsum = _lastema1 = _lastema2 = 0;
|
||||
_len = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <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 : TSeries {
|
||||
private readonly int _p1a, _p1b;
|
||||
private SMA_Series sma, trima;
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public TRIMA_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"xMA({period})";
|
||||
_p1a = (int)Math.Floor((period * 0.5) + 1);
|
||||
_p1b = (int)Math.Ceiling(0.5 * period);
|
||||
sma = new(_p1a);
|
||||
trima = new(_p1b);
|
||||
|
||||
}
|
||||
public TRIMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public TRIMA_Series() : this(period: 0, useNaN: false) { }
|
||||
public TRIMA_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public TRIMA_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public TRIMA_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public TRIMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public TRIMA_Series(TSeries source) : this(source, 0, false) { }
|
||||
public TRIMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (double.IsNaN(TValue.v)) {
|
||||
return base.Add((TValue.t, Double.NaN), update);
|
||||
}
|
||||
|
||||
var _sma = sma.Add(TValue, update);
|
||||
var _trima = trima.Add(_sma, update);
|
||||
|
||||
var res = (_trima.t, Count < _period - 1 && _NaN ? double.NaN : _trima.v);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
sma.Reset();
|
||||
trima.Reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
TRIX: Triple Exponential Average Oscillator
|
||||
Developed by Jack Hutson in the early 1980s, the triple exponential average (TRIX)
|
||||
has become a popular technical analysis tool to aid chartists in spotting diversions
|
||||
and directional cues in stock trading patterns.
|
||||
|
||||
Sources:
|
||||
https://www.investopedia.com/terms/t/trix.asp
|
||||
|
||||
</summary> */
|
||||
|
||||
public class TRIX_Series : TSeries {
|
||||
private readonly double _k;
|
||||
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;
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
|
||||
public TRIX_Series(int period, bool useNaN, bool useSMA) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
_useSMA = useSMA;
|
||||
Name = $"TRIX({period})";
|
||||
_k = 2.0 / (_period + 1);
|
||||
_lastema1 = _llastema1 = _lastema2 = _llastema2 = _lastema3 = _llastema3 = 0;
|
||||
}
|
||||
public TRIX_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public TRIX_Series() : this(0, false, true) {}
|
||||
public TRIX_Series(int period) : this(period, false, true) {}
|
||||
public TRIX_Series(TBars source) : this(source.Close, 0, false) {}
|
||||
public TRIX_Series(TBars source, int period) : this(source.Close, period, false) {}
|
||||
public TRIX_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) {}
|
||||
public TRIX_Series(TSeries source, int period) : this(source, period, false, true) {}
|
||||
public TRIX_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) {}
|
||||
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
|
||||
if (double.IsNaN(TValue.v)) {
|
||||
return base.Add((TValue.t, Double.NaN), update);
|
||||
}
|
||||
if (this.Count == 0) { _lastema1 = _lastema2 = _lastema3 = TValue.v; }
|
||||
if (update) { _lastema1 = _llastema1; _lastema2 = _llastema2; _lastema3 = _llastema3; }
|
||||
else { _llastema1 = _lastema1; _llastema2 = _lastema2; _llastema3 = _lastema3; }
|
||||
|
||||
double _ema1, _ema2, _ema3;
|
||||
if ((this.Count < _period) && _useSMA) {
|
||||
BufferTrim(_buffer1, TValue.v, _period, update);
|
||||
_ema1 = 0;
|
||||
for (int i = 0; i < _buffer1.Count; i++) { _ema1 += _buffer1[i]; }
|
||||
_ema1 /= _buffer1.Count;
|
||||
|
||||
BufferTrim(_buffer2, _ema1, _period, update);
|
||||
_ema2 = 0;
|
||||
for (int i = 0; i < _buffer2.Count; i++) { _ema2 += _buffer2[i]; }
|
||||
_ema2 /= _buffer2.Count;
|
||||
|
||||
BufferTrim(_buffer3, _ema2, _period, update);
|
||||
_ema3 = 0;
|
||||
for (int i = 0; i < _buffer3.Count; i++) { _ema3 += _buffer3[i]; }
|
||||
_ema3 /= _buffer3.Count;
|
||||
}
|
||||
else {
|
||||
_ema1 = (TValue.v - _lastema1) * _k + _lastema1;
|
||||
_ema2 = (_ema1 - _lastema2) * _k + _lastema2;
|
||||
_ema3 = (_ema2 - _lastema3) * _k + _lastema3;
|
||||
}
|
||||
double _trix = 100 * (_ema3 - _lastema3) / _lastema3;
|
||||
_lastema1 = _ema1;
|
||||
_lastema2 = _ema2;
|
||||
_lastema3 = _ema3;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _trix);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
//variation of Add()
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
TSeries is the cornerstone of all QuanTAlib classes.
|
||||
TSeries is a single List of tuples (time, value) and contains several operators, casts, overloads
|
||||
and other helpers that simplify usage of library.
|
||||
Think of TSeries as an equivalent of Numpy array.
|
||||
|
||||
- includes Length property (to mimic array's method)
|
||||
- includes publishing and subscribing methods that attach to events
|
||||
|
||||
</summary> */
|
||||
public class TSeriesEventArgs : EventArgs {
|
||||
public bool update { get; set; }
|
||||
}
|
||||
|
||||
public class TSeries : List<(DateTime t, double v)> {
|
||||
public List<DateTime> t => this.Select(item => item.t).ToList();
|
||||
public List<double> v => this.Select(item => item.v).ToList();
|
||||
public (DateTime t, double v) Last => this[^1];
|
||||
public int Length => Count;
|
||||
public string Name { get; set; }
|
||||
|
||||
public TSeries() {
|
||||
this.Name = "data";
|
||||
}
|
||||
|
||||
public TSeries(string Name) {
|
||||
this.Name = Name;
|
||||
}
|
||||
|
||||
public virtual (DateTime t, double v) Add(double v, bool update = false) {
|
||||
var Value = (t: Count == 0 ? DateTime.Today : this[^1].t.AddDays(1), v);
|
||||
return Add(Value, update);
|
||||
}
|
||||
|
||||
public virtual (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (update) {
|
||||
this[^1] = TValue;
|
||||
}
|
||||
else {
|
||||
base.Add(TValue);
|
||||
}
|
||||
|
||||
OnEvent(update);
|
||||
return TValue;
|
||||
}
|
||||
|
||||
public virtual (DateTime t, double v) Add(TSeries data) {
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return data.Last;
|
||||
}
|
||||
|
||||
public void Sub(object source, TSeriesEventArgs e) {
|
||||
var data = (TSeries) source;
|
||||
if (data == null) { return; }
|
||||
foreach (var item in data) { Add(item, update: false); }
|
||||
}
|
||||
|
||||
public delegate void NewEventHandler(object source, TSeriesEventArgs args);
|
||||
|
||||
public event NewEventHandler Pub;
|
||||
|
||||
protected virtual void OnEvent(bool update = false)
|
||||
{
|
||||
Pub?.Invoke(this, new TSeriesEventArgs {update = update});
|
||||
}
|
||||
|
||||
/// common helpers
|
||||
public static void BufferTrim(List<double> buffer, double value, int period, bool update) {
|
||||
if (!update) {
|
||||
buffer.Add(value);
|
||||
if (buffer.Count > period && period > 0) { buffer.RemoveAt(0); }
|
||||
return;
|
||||
}
|
||||
buffer[^1] = value;
|
||||
}
|
||||
public virtual void Reset() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
VAR: Population Variance
|
||||
Population variance without Bessel's correction
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Variance
|
||||
Bessel's correction: https://en.wikipedia.org/wiki/Bessel%27s_correction
|
||||
|
||||
Remark:
|
||||
VAR (Population Variance) is also known as a biased Sample Variance. For unbiased
|
||||
sample variance use SVAR instead.
|
||||
|
||||
</summary> */
|
||||
|
||||
public class VAR_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public VAR_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"VAR({period})";
|
||||
}
|
||||
public VAR_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public VAR_Series() : this(period: 0, useNaN: false) { }
|
||||
public VAR_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public VAR_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public VAR_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public VAR_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public VAR_Series(TSeries source) : this(source, 0, false) { }
|
||||
public VAR_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
double _pvar = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
|
||||
_pvar /= this._buffer.Count;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _pvar);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
WMAPE: Weighted Mean Absolute Percentage Error
|
||||
Measures the size of the error in percentage terms. Improves problems with MAPE
|
||||
when there are zero or close-to-zero values because there would be a division by zero
|
||||
or values of MAPE tending to infinity.
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/WMAPE
|
||||
|
||||
</summary> */
|
||||
|
||||
public class WMAPE_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public WMAPE_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"WMAPE({period})";
|
||||
}
|
||||
public WMAPE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public WMAPE_Series() : this(period: 0, useNaN: false) { }
|
||||
public WMAPE_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public WMAPE_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public WMAPE_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public WMAPE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public WMAPE_Series(TSeries source) : this(source, 0, false) { }
|
||||
public WMAPE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
double _div = 0;
|
||||
double _wmape = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) {
|
||||
_wmape += Math.Abs(_buffer[i] - _sma);
|
||||
_div += Math.Abs(_buffer[i]);
|
||||
}
|
||||
_wmape = (_div != 0) ? _wmape / _div : double.PositiveInfinity;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _wmape);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/* <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 : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
private System.Collections.Generic.List<double> _weights = new();
|
||||
protected int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
protected int _len;
|
||||
public int Len {
|
||||
get { return _len; }
|
||||
set { _len = value; }
|
||||
}
|
||||
|
||||
//core constructors
|
||||
public WMA_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"WMA({period})";
|
||||
_len = 1;
|
||||
_weights = CalculateWeights(_period);
|
||||
}
|
||||
public WMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public WMA_Series() : this(period: 0, useNaN: false) { }
|
||||
public WMA_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public WMA_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public WMA_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public WMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public WMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update=false) {
|
||||
BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
|
||||
if (_period == 0) {
|
||||
_weights = CalculateWeights(_len);
|
||||
_len++;
|
||||
}
|
||||
double _wma = 0;
|
||||
double totalWeights = (_buffer.Count * (_buffer.Count + 1)) * 0.5;
|
||||
object lockObj = new object();
|
||||
Parallel.For(0, _buffer.Count, i =>
|
||||
{
|
||||
double temp = _buffer[i] * this._weights[i];
|
||||
lock (lockObj) { _wma += temp; }
|
||||
});
|
||||
_wma /= totalWeights;
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _wma);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//calculating weights
|
||||
private static List<double> CalculateWeights(int period) {
|
||||
List<double> weights = new List<double>(period);
|
||||
for (int i = 0; i < period; i++) {
|
||||
weights.Add(i + 1);
|
||||
}
|
||||
return weights;
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_len = 0;
|
||||
_weights = CalculateWeights(_period);
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
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 : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
private int _len;
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
private readonly EMA_Series _ema;
|
||||
|
||||
//core constructor
|
||||
public ZLEMA_Series(int period, bool useNaN, bool useSMA) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"ZLEMA({period})";
|
||||
_len = 1;
|
||||
_ema = new(period);
|
||||
}
|
||||
//generic constructors (source)
|
||||
|
||||
public ZLEMA_Series() : this(0, false, true) { }
|
||||
public ZLEMA_Series(int period) : this(period, false, true) { }
|
||||
public ZLEMA_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public ZLEMA_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public ZLEMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public ZLEMA_Series(TSeries source, int period) : this(source, period, false, true) { }
|
||||
public ZLEMA_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { }
|
||||
public ZLEMA_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
|
||||
BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
|
||||
int _lag;
|
||||
if (_period == 0) {
|
||||
_lag = (int)((_len - 1) * 0.5);
|
||||
_len++;
|
||||
}
|
||||
else { _lag = (int)((_period - 1) * 0.5); }
|
||||
_lag = Math.Min(_lag, _buffer.Count - 1);
|
||||
_lag = Math.Max(_lag, 0) + 1;
|
||||
double _zlValue = 2 * TValue.v - _buffer[^_lag];
|
||||
double _zlema = _ema.Add((TValue.t, _zlValue), update).v;
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zlema);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
//variation of Add()
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
_ema.Reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
ZL: Zero Lag
|
||||
Data is de-lagged by removing the data from “lag” days ago, thus removing
|
||||
(or attempting to) the cumulative effect of the moving average.
|
||||
|
||||
Calculation:
|
||||
Lag = (Period-1)/2
|
||||
ZL = Data + (Data - Data(Lag days ago) )
|
||||
|
||||
Sources:
|
||||
https://mudrex.com/blog/zero-lag-ema-trading-strategy/
|
||||
|
||||
</summary> */
|
||||
|
||||
public class ZL_Series: TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
private int _len;
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
private readonly EMA_Series _ema;
|
||||
|
||||
//core constructor
|
||||
public ZL_Series(int period, bool useNaN, bool useSMA) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"ZL({period})";
|
||||
_len = 1;
|
||||
_ema = new(period);
|
||||
}
|
||||
//generic constructors (source)
|
||||
|
||||
public ZL_Series() : this(0, false, true) { }
|
||||
public ZL_Series(int period) : this(period, false, true) { }
|
||||
public ZL_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public ZL_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public ZL_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public ZL_Series(TSeries source, int period) : this(source, period, false, true) { }
|
||||
public ZL_Series(TSeries source, int period, bool useNaN) : this(source, period, useNaN, true) { }
|
||||
public ZL_Series(TSeries source, int period, bool useNaN, bool useSMA) : this(period, useNaN, useSMA) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update) {
|
||||
BufferTrim(buffer: _buffer, value: TValue.v, period: _period, update: update);
|
||||
int _lag;
|
||||
if (_period == 0) {
|
||||
_lag = (int)((_len - 1) * 0.5);
|
||||
_len++;
|
||||
}
|
||||
else { _lag = (int)((_period - 1) * 0.5); }
|
||||
_lag = Math.Min(_lag, _buffer.Count - 1);
|
||||
_lag = Math.Max(_lag, 0) + 1;
|
||||
double _zlValue = 2 * TValue.v - _buffer[^_lag];
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zlValue);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
//variation of Add()
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
_ema.Reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
ZSCORE: number of standard deviations from SMA
|
||||
Z-score describes a value's relationship to the mean of a series, as measured in
|
||||
terms of standard deviations from the mean. If a Z-score is 0, it indicates that
|
||||
the data point's score is identical to the mean score. A Z-score of 1.0 would
|
||||
indicate a value that is one standard deviation from the mean. Z-scores may be
|
||||
positive or negative, with a positive value indicating the score is above the
|
||||
mean and a negative score indicating it is below the mean.
|
||||
|
||||
Sources:
|
||||
https://en.wikipedia.org/wiki/Z-score
|
||||
https://www.investopedia.com/terms/z/zscore.asp
|
||||
|
||||
Calculation:
|
||||
std = std * STDEV(close, length)
|
||||
mean = SMA(close, length)
|
||||
ZSCORE = (close - mean) / std
|
||||
|
||||
</summary> */
|
||||
|
||||
public class ZSCORE_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public ZSCORE_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"ZSCORE({period})";
|
||||
}
|
||||
public ZSCORE_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public ZSCORE_Series() : this(period: 0, useNaN: false) { }
|
||||
public ZSCORE_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public ZSCORE_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public ZSCORE_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public ZSCORE_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public ZSCORE_Series(TSeries source) : this(source, 0, false) { }
|
||||
public ZSCORE_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
double _sma = _buffer.Average();
|
||||
|
||||
double _pvar = 0;
|
||||
for (int i = 0; i < _buffer.Count; i++) { _pvar += (_buffer[i] - _sma) * (_buffer[i] - _sma); }
|
||||
_pvar /= this._buffer.Count;
|
||||
double _psdev = Math.Sqrt(_pvar);
|
||||
double _zscore = (_psdev == 0) ? 1 : (TValue.v - _sma) / _psdev;
|
||||
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _zscore);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/* <summary>
|
||||
|
||||
</summary> */
|
||||
|
||||
public class xMA_Series : TSeries {
|
||||
private readonly System.Collections.Generic.List<double> _buffer = new();
|
||||
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
//core constructors
|
||||
public xMA_Series(int period, bool useNaN) : base() {
|
||||
_period = period;
|
||||
_NaN = useNaN;
|
||||
Name = $"xMA({period})";
|
||||
}
|
||||
public xMA_Series(TSeries source, int period, bool useNaN) : this(period, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public xMA_Series() : this(period: 0, useNaN: false) { }
|
||||
public xMA_Series(int period) : this(period: period, useNaN: false) { }
|
||||
public xMA_Series(TBars source) : this(source.Close, 0, false) { }
|
||||
public xMA_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public xMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public xMA_Series(TSeries source) : this(source, 0, false) { }
|
||||
public xMA_Series(TSeries source, int period) : this(source: source, period: period, useNaN: false) { }
|
||||
|
||||
//////////////////
|
||||
// core Add() algo
|
||||
public override (DateTime t, double v) Add((DateTime t, double v) TValue, bool update = false) {
|
||||
if (double.IsNaN(TValue.v)) {
|
||||
return base.Add((TValue.t, Double.NaN), update);
|
||||
}
|
||||
|
||||
BufferTrim(buffer:_buffer, value:TValue.v, period:_period, update: update);
|
||||
|
||||
double _xma = 0;
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _xma);
|
||||
return base.Add(res, update);
|
||||
}
|
||||
|
||||
public override (DateTime t, double v) Add(TSeries data) {
|
||||
if (data == null) { return (DateTime.Today, Double.NaN); }
|
||||
foreach (var item in data) { Add(item, false); }
|
||||
return _data.Last;
|
||||
}
|
||||
public new (DateTime t, double v) Add((DateTime t, double v) TValue) {
|
||||
return Add(TValue, false);
|
||||
}
|
||||
public (DateTime t, double v) Add(bool update) {
|
||||
return this.Add(TValue: _data.Last, update: update);
|
||||
}
|
||||
public (DateTime t, double v) Add() {
|
||||
return Add(TValue: _data.Last, update: false);
|
||||
}
|
||||
private new void Sub(object source, TSeriesEventArgs e) {
|
||||
Add(TValue: _data.Last, update: e.update);
|
||||
}
|
||||
|
||||
//reset calculation
|
||||
public override void Reset() {
|
||||
_buffer.Clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user