mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-26 06:18:05 +00:00
refactor structs to records, add new classes for financial calculations, implement EMA and SMA with circular buffer. Generate random financial data using GBM model. Also, test the SMA calculation with sample data.
This commit is contained in:
@@ -17,173 +17,191 @@ Sources:
|
||||
|
||||
</summary> */
|
||||
|
||||
public class MAMA_Series : TSeries {
|
||||
private int _len;
|
||||
protected readonly int _period;
|
||||
protected readonly bool _NaN;
|
||||
protected readonly TSeries _data;
|
||||
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;
|
||||
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
|
||||
//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) { }
|
||||
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;
|
||||
//////////////////
|
||||
// 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;
|
||||
// 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;
|
||||
// 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;
|
||||
// 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;
|
||||
// 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;
|
||||
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;
|
||||
// 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;
|
||||
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;
|
||||
// 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;
|
||||
// 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;
|
||||
// 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;
|
||||
// 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);
|
||||
// change in phase
|
||||
var delta = Math.Max(ph.i1 - ph.i, 1d);
|
||||
|
||||
// adaptive alpha value
|
||||
var alpha = Math.Max(fastl / delta, slowl);
|
||||
// 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);
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
//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;
|
||||
}
|
||||
//reset calculation
|
||||
public override void Reset()
|
||||
{
|
||||
_len = 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user