mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-25 13:58:04 +00:00
Add new data structures and event handling classes for trading platform. Include base classes, value and bar structs, event arguments, emitters, listeners. Update ruleset for SonarLint.
This commit is contained in:
@@ -1,189 +1,189 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
MAMA: MESA Adaptive Moving Average
|
||||
Created by John Ehlers, the MAMA indicator is a 5-period adaptive moving average of
|
||||
high/low price that uses classic electrical radio-frequency signal processing algorithms
|
||||
to reduce noise.
|
||||
|
||||
KAMAi = KAMAi - 1 + SC * ( price - KAMAi-1 )
|
||||
|
||||
Sources:
|
||||
https://mesasoftware.com/papers/MAMA.pdf
|
||||
https://www.tradingview.com/script/foQxLbU3-Ehlers-MESA-Adaptive-Moving-Average-LazyBear/
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MAMA_Series : TSeries {
|
||||
private int _len;
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
private double sumPr;
|
||||
private double fastl, slowl;
|
||||
private (double i, double i1, double i2, double i3, double i4, double i5, double i6, double io) pr, i1, q1, sm, dt;
|
||||
private (double i, double i1, double io) i2, q2, re, im, pd, ph, mama, fama;
|
||||
public TSeries Fama { get; }
|
||||
private double mamaseed, famaseed;
|
||||
|
||||
//core constructors
|
||||
|
||||
public MAMA_Series(double fastlimit, double slowlimit, bool useNaN) {
|
||||
_period = (int)(2 / fastlimit) - 1;
|
||||
fastl = fastlimit;
|
||||
slowl = slowlimit;
|
||||
Fama = new TSeries();
|
||||
_NaN = useNaN;
|
||||
Name = $"MAMA({_period})";
|
||||
_len = 0;
|
||||
}
|
||||
public MAMA_Series(TSeries source, double fastlimit, double slowlimit, bool useNaN = false) : this(fastlimit, slowlimit, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public MAMA_Series() : this(period: 0, useNaN: false) { }
|
||||
public MAMA_Series(int period) : this(period, useNaN: false) { }
|
||||
public MAMA_Series(int period, bool useNaN) : this(fastlimit: 2 / (period + 1), slowlimit: 0.2 / (period + 1), useNaN) {
|
||||
_period = period;
|
||||
}
|
||||
public MAMA_Series(TBars source) : this(source.Close, period: 0, useNaN: false) { }
|
||||
public MAMA_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public MAMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public MAMA_Series(TSeries source, int period) : this(source, period, false) { }
|
||||
public MAMA_Series(TSeries source, int period, bool useNaN) : this(source, fastlimit: 2 / ((double)period + 1), slowlimit: 0.2 / ((double)period + 1), 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) {
|
||||
// roll forward (oldx = x)
|
||||
pr.io = pr.i6; pr.i6 = pr.i5; pr.i5 = pr.i4; pr.i4 = pr.i3; pr.i3 = pr.i2; pr.i2 = pr.i1; pr.i1 = pr.i;
|
||||
i1.io = i1.i6; i1.i6 = i1.i5; i1.i5 = i1.i4; i1.i4 = i1.i3; i1.i3 = i1.i2; i1.i2 = i1.i1; i1.i1 = i1.i;
|
||||
q1.io = q1.i6; q1.i6 = q1.i5; q1.i5 = q1.i4; q1.i4 = q1.i3; q1.i3 = q1.i2; q1.i2 = q1.i1; q1.i1 = q1.i;
|
||||
dt.io = dt.i6; dt.i6 = dt.i5; dt.i5 = dt.i4; dt.i4 = dt.i3; dt.i3 = dt.i2; dt.i2 = dt.i1; dt.i1 = dt.i;
|
||||
sm.io = sm.i6; sm.i6 = sm.i5; sm.i5 = sm.i4; sm.i4 = sm.i3; sm.i3 = sm.i2; sm.i2 = sm.i1; sm.i1 = sm.i;
|
||||
i2.io = i2.i1; i2.i1 = i2.i; q2.io = q2.i1; q2.i1 = q2.i;
|
||||
re.io = re.i1; re.i1 = re.i; im.io = im.i1; im.i1 = im.i;
|
||||
pd.io = pd.i1; pd.i1 = pd.i; ph.io = ph.i1; ph.i1 = ph.i;
|
||||
mama.io = mama.i1; mama.i1 = mama.i;
|
||||
fama.io = fama.i1;
|
||||
fama.i1 = fama.i;
|
||||
_len++;
|
||||
}
|
||||
if (_period == 0) {
|
||||
fastl = 2 / (double)_len;
|
||||
slowl = fastl * 0.1;
|
||||
}
|
||||
if (_period == 1) {
|
||||
fastl = 1;
|
||||
slowl = 1;
|
||||
}
|
||||
var i = _len - 1;
|
||||
pr.i = TValue.v;
|
||||
if (i > 5) {
|
||||
var adj = 0.075 * pd.i1 + 0.54;
|
||||
|
||||
// smooth and detrender
|
||||
sm.i = (4 * pr.i + 3 * pr.i1 + 2 * pr.i2 + pr.i3) / 10;
|
||||
dt.i = (0.0962 * sm.i + 0.5769 * sm.i2 - 0.5769 * sm.i4 - 0.0962 * sm.i6) * adj;
|
||||
|
||||
// in-phase and quadrature
|
||||
q1.i = (0.0962 * dt.i + 0.5769 * dt.i2 - 0.5769 * dt.i4 - 0.0962 * dt.i6) * adj;
|
||||
i1.i = dt.i3;
|
||||
|
||||
// advance the phases by 90 degrees
|
||||
double jI = (0.0962 * i1.i + 0.5769 * i1.i2 - 0.5769 * i1.i4 - 0.0962 * i1.i6) * adj;
|
||||
double jQ = (0.0962 * q1.i + 0.5769 * q1.i2 - 0.5769 * q1.i4 - 0.0962 * q1.i6) * adj;
|
||||
|
||||
// phasor addition for 3-bar averaging
|
||||
i2.i = i1.i - jQ;
|
||||
q2.i = q1.i + jI;
|
||||
|
||||
i2.i = 0.2 * i2.i + 0.8 * i2.i1; // smoothing it
|
||||
q2.i = 0.2 * q2.i + 0.8 * q2.i1;
|
||||
|
||||
// homodyne discriminator
|
||||
re.i = i2.i * i2.i1 + q2.i * q2.i1;
|
||||
im.i = i2.i * q2.i1 - q2.i * i2.i1;
|
||||
|
||||
re.i = 0.2 * re.i + 0.8 * re.i1; // smoothing it
|
||||
im.i = 0.2 * im.i + 0.8 * im.i1;
|
||||
|
||||
// calculate period
|
||||
pd.i = im.i != 0 && re.i != 0 ? 6.283185307179586 / Math.Atan(im.i / re.i) : 0d;
|
||||
|
||||
// adjust period to thresholds
|
||||
pd.i = pd.i > 1.5 * pd.i1 ? 1.5 * pd.i1 : pd.i;
|
||||
pd.i = pd.i < 0.67 * pd.i1 ? 0.67 * pd.i1 : pd.i;
|
||||
pd.i = pd.i < 6d ? 6d : pd.i;
|
||||
pd.i = pd.i > 50d ? 50d : pd.i;
|
||||
|
||||
// smooth the period
|
||||
pd.i = 0.2 * pd.i + 0.8 * pd.i1;
|
||||
|
||||
// determine phase position
|
||||
ph.i = i1.i != 0 ? Math.Atan(q1.i / i1.i) * 57.29577951308232 : 0;
|
||||
|
||||
// change in phase
|
||||
var delta = Math.Max(ph.i1 - ph.i, 1d);
|
||||
|
||||
// adaptive alpha value
|
||||
var alpha = Math.Max(fastl / delta, slowl);
|
||||
|
||||
// final indicators
|
||||
mama.i = alpha * (pr.i - mama.i1) + mama.i1;
|
||||
fama.i = 0.5d * alpha * (mama.i - fama.i1) + fama.i1;
|
||||
}
|
||||
else {
|
||||
sumPr += pr.i;
|
||||
pd.i = sm.i = dt.i = i1.i = q1.i = i2.i = q2.i = re.i = im.i = ph.i = 0;
|
||||
mama.i = fama.i = sumPr / (i + 1);
|
||||
|
||||
if (_len == 1) {
|
||||
mamaseed = famaseed = TValue.v;
|
||||
}
|
||||
else {
|
||||
mamaseed = fastl * (TValue.v - mamaseed) + mamaseed;
|
||||
famaseed = slowl * (TValue.v - famaseed) + famaseed;
|
||||
}
|
||||
}
|
||||
|
||||
double _fama = (i > 5) ? fama.i : famaseed;
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _fama);
|
||||
Fama.Add(res, update);
|
||||
double _mama = (i > 5) ? mama.i : mamaseed;
|
||||
res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mama);
|
||||
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 (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() {
|
||||
_len = 0;
|
||||
}
|
||||
namespace QuanTAlib;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
/* <summary>
|
||||
MAMA: MESA Adaptive Moving Average
|
||||
Created by John Ehlers, the MAMA indicator is a 5-period adaptive moving average of
|
||||
high/low price that uses classic electrical radio-frequency signal processing algorithms
|
||||
to reduce noise.
|
||||
|
||||
KAMAi = KAMAi - 1 + SC * ( price - KAMAi-1 )
|
||||
|
||||
Sources:
|
||||
https://mesasoftware.com/papers/MAMA.pdf
|
||||
https://www.tradingview.com/script/foQxLbU3-Ehlers-MESA-Adaptive-Moving-Average-LazyBear/
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MAMA_Series : TSeries {
|
||||
private int _len;
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
|
||||
private double sumPr;
|
||||
private double fastl, slowl;
|
||||
private (double i, double i1, double i2, double i3, double i4, double i5, double i6, double io) pr, i1, q1, sm, dt;
|
||||
private (double i, double i1, double io) i2, q2, re, im, pd, ph, mama, fama;
|
||||
public TSeries Fama { get; }
|
||||
private double mamaseed, famaseed;
|
||||
|
||||
//core constructors
|
||||
|
||||
public MAMA_Series(double fastlimit, double slowlimit, bool useNaN) {
|
||||
_period = (int)(2 / fastlimit) - 1;
|
||||
fastl = fastlimit;
|
||||
slowl = slowlimit;
|
||||
Fama = new TSeries();
|
||||
_NaN = useNaN;
|
||||
Name = $"MAMA({_period})";
|
||||
_len = 0;
|
||||
}
|
||||
public MAMA_Series(TSeries source, double fastlimit, double slowlimit, bool useNaN = false) : this(fastlimit, slowlimit, useNaN) {
|
||||
_data = source;
|
||||
Name = Name.Substring(0, Name.IndexOf(")")) + $", {(string.IsNullOrEmpty(_data.Name) ? "data" : _data.Name)})";
|
||||
_data.Pub += Sub;
|
||||
Add(_data);
|
||||
}
|
||||
public MAMA_Series() : this(period: 0, useNaN: false) { }
|
||||
public MAMA_Series(int period) : this(period, useNaN: false) { }
|
||||
public MAMA_Series(int period, bool useNaN) : this(fastlimit: 2 / (period + 1), slowlimit: 0.2 / (period + 1), useNaN) {
|
||||
_period = period;
|
||||
}
|
||||
public MAMA_Series(TBars source) : this(source.Close, period: 0, useNaN: false) { }
|
||||
public MAMA_Series(TBars source, int period) : this(source.Close, period, false) { }
|
||||
public MAMA_Series(TBars source, int period, bool useNaN) : this(source.Close, period, useNaN) { }
|
||||
public MAMA_Series(TSeries source, int period) : this(source, period, false) { }
|
||||
public MAMA_Series(TSeries source, int period, bool useNaN) : this(source, fastlimit: 2 / ((double)period + 1), slowlimit: 0.2 / ((double)period + 1), 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) {
|
||||
// roll forward (oldx = x)
|
||||
pr.io = pr.i6; pr.i6 = pr.i5; pr.i5 = pr.i4; pr.i4 = pr.i3; pr.i3 = pr.i2; pr.i2 = pr.i1; pr.i1 = pr.i;
|
||||
i1.io = i1.i6; i1.i6 = i1.i5; i1.i5 = i1.i4; i1.i4 = i1.i3; i1.i3 = i1.i2; i1.i2 = i1.i1; i1.i1 = i1.i;
|
||||
q1.io = q1.i6; q1.i6 = q1.i5; q1.i5 = q1.i4; q1.i4 = q1.i3; q1.i3 = q1.i2; q1.i2 = q1.i1; q1.i1 = q1.i;
|
||||
dt.io = dt.i6; dt.i6 = dt.i5; dt.i5 = dt.i4; dt.i4 = dt.i3; dt.i3 = dt.i2; dt.i2 = dt.i1; dt.i1 = dt.i;
|
||||
sm.io = sm.i6; sm.i6 = sm.i5; sm.i5 = sm.i4; sm.i4 = sm.i3; sm.i3 = sm.i2; sm.i2 = sm.i1; sm.i1 = sm.i;
|
||||
i2.io = i2.i1; i2.i1 = i2.i; q2.io = q2.i1; q2.i1 = q2.i;
|
||||
re.io = re.i1; re.i1 = re.i; im.io = im.i1; im.i1 = im.i;
|
||||
pd.io = pd.i1; pd.i1 = pd.i; ph.io = ph.i1; ph.i1 = ph.i;
|
||||
mama.io = mama.i1; mama.i1 = mama.i;
|
||||
fama.io = fama.i1;
|
||||
fama.i1 = fama.i;
|
||||
_len++;
|
||||
}
|
||||
if (_period == 0) {
|
||||
fastl = 2 / (double)_len;
|
||||
slowl = fastl * 0.1;
|
||||
}
|
||||
if (_period == 1) {
|
||||
fastl = 1;
|
||||
slowl = 1;
|
||||
}
|
||||
var i = _len - 1;
|
||||
pr.i = TValue.v;
|
||||
if (i > 5) {
|
||||
var adj = 0.075 * pd.i1 + 0.54;
|
||||
|
||||
// smooth and detrender
|
||||
sm.i = (4 * pr.i + 3 * pr.i1 + 2 * pr.i2 + pr.i3) / 10;
|
||||
dt.i = (0.0962 * sm.i + 0.5769 * sm.i2 - 0.5769 * sm.i4 - 0.0962 * sm.i6) * adj;
|
||||
|
||||
// in-phase and quadrature
|
||||
q1.i = (0.0962 * dt.i + 0.5769 * dt.i2 - 0.5769 * dt.i4 - 0.0962 * dt.i6) * adj;
|
||||
i1.i = dt.i3;
|
||||
|
||||
// advance the phases by 90 degrees
|
||||
double jI = (0.0962 * i1.i + 0.5769 * i1.i2 - 0.5769 * i1.i4 - 0.0962 * i1.i6) * adj;
|
||||
double jQ = (0.0962 * q1.i + 0.5769 * q1.i2 - 0.5769 * q1.i4 - 0.0962 * q1.i6) * adj;
|
||||
|
||||
// phasor addition for 3-bar averaging
|
||||
i2.i = i1.i - jQ;
|
||||
q2.i = q1.i + jI;
|
||||
|
||||
i2.i = 0.2 * i2.i + 0.8 * i2.i1; // smoothing it
|
||||
q2.i = 0.2 * q2.i + 0.8 * q2.i1;
|
||||
|
||||
// homodyne discriminator
|
||||
re.i = i2.i * i2.i1 + q2.i * q2.i1;
|
||||
im.i = i2.i * q2.i1 - q2.i * i2.i1;
|
||||
|
||||
re.i = 0.2 * re.i + 0.8 * re.i1; // smoothing it
|
||||
im.i = 0.2 * im.i + 0.8 * im.i1;
|
||||
|
||||
// calculate period
|
||||
pd.i = im.i != 0 && re.i != 0 ? 6.283185307179586 / Math.Atan(im.i / re.i) : 0d;
|
||||
|
||||
// adjust period to thresholds
|
||||
pd.i = pd.i > 1.5 * pd.i1 ? 1.5 * pd.i1 : pd.i;
|
||||
pd.i = pd.i < 0.67 * pd.i1 ? 0.67 * pd.i1 : pd.i;
|
||||
pd.i = pd.i < 6d ? 6d : pd.i;
|
||||
pd.i = pd.i > 50d ? 50d : pd.i;
|
||||
|
||||
// smooth the period
|
||||
pd.i = 0.2 * pd.i + 0.8 * pd.i1;
|
||||
|
||||
// determine phase position
|
||||
ph.i = i1.i != 0 ? Math.Atan(q1.i / i1.i) * 57.29577951308232 : 0;
|
||||
|
||||
// change in phase
|
||||
var delta = Math.Max(ph.i1 - ph.i, 1d);
|
||||
|
||||
// adaptive alpha value
|
||||
var alpha = Math.Max(fastl / delta, slowl);
|
||||
|
||||
// final indicators
|
||||
mama.i = alpha * (pr.i - mama.i1) + mama.i1;
|
||||
fama.i = 0.5d * alpha * (mama.i - fama.i1) + fama.i1;
|
||||
}
|
||||
else {
|
||||
sumPr += pr.i;
|
||||
pd.i = sm.i = dt.i = i1.i = q1.i = i2.i = q2.i = re.i = im.i = ph.i = 0;
|
||||
mama.i = fama.i = sumPr / (i + 1);
|
||||
|
||||
if (_len == 1) {
|
||||
mamaseed = famaseed = TValue.v;
|
||||
}
|
||||
else {
|
||||
mamaseed = fastl * (TValue.v - mamaseed) + mamaseed;
|
||||
famaseed = slowl * (TValue.v - famaseed) + famaseed;
|
||||
}
|
||||
}
|
||||
|
||||
double _fama = (i > 5) ? fama.i : famaseed;
|
||||
var res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _fama);
|
||||
Fama.Add(res, update);
|
||||
double _mama = (i > 5) ? mama.i : mamaseed;
|
||||
res = (TValue.t, Count < _period - 1 && _NaN ? double.NaN : _mama);
|
||||
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 (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() {
|
||||
_len = 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user