This commit is contained in:
Miha Kralj
2022-04-29 12:32:13 -07:00
parent 50ae1d4917
commit a6ed7c78eb
6 changed files with 127 additions and 2 deletions
+1 -1
View File
@@ -41,7 +41,7 @@
||||||
| **Moving Averages** |||||
| AFIRMA - Autoregressive Finite Impulse Response Moving Average |||||
| ALMA - Arnaud Legoux Moving Average |||✔️|✔️|
| ALMA - Arnaud Legoux Moving Average |✔️||✔️|✔️|
| ARIMA - Autoregressive Integrated Moving Average |||||
| ATR - Average True Range |✔️|✔️|✔️|✔️|
| ATRP - Average True Range Percent |✔️||✔️||
+1 -1
View File
@@ -141,7 +141,7 @@ public abstract class Single_TBars_Indicator : TSeries
this._p = period;
this._bars = source;
this._NaN = useNaN;
this._bars.Close.Pub += this.Sub;
this._bars.Pub += this.Sub;
}
// overridable Add() method to add/update a single item at the end of the list
+17
View File
@@ -108,5 +108,22 @@ public class TBars : System.Collections.Generic.List<(DateTime t, double o, doub
_ohlc4.Add((t, (o + h + l + c) * 0.25));
_hlcc4.Add((t, (h + l + c + c) * 0.25));
}
this.OnEvent(update);
}
// delegate used by event handler + event handler (Pub == publisher)
public delegate
void NewDataEventHandler(object source, TSeriesEventArgs args);
public event NewDataEventHandler Pub;
// Broadcast handler - only to valid targets
protected virtual void OnEvent(bool update = false)
{
if (Pub != null && Pub.Target != this)
{
Pub(this, new TSeriesEventArgs { update = update });
}
}
}
+66
View File
@@ -0,0 +1,66 @@
namespace QuanTAlib;
using System;
/* <summary>
ALMA: Arnaud Legoux Moving Average
The ALMA moving average uses the curve of the Normal (Gauss) distribution, which
can be shifted from 0 to 1. This allows regulating the smoothness and high
sensitivity of the indicator. Sigma is another parameter that is responsible for
the shape of the curve coefficients. This moving average reduces lag of the data
in conjunction with smoothing to reduce noise.
Sources:
https://phemex.com/academy/what-is-arnaud-legoux-moving-averages
https://www.prorealcode.com/prorealtime-indicators/alma-arnaud-legoux-moving-average/
</summary> */
public class ALMA_Series : Single_TSeries_Indicator
{
private readonly System.Collections.Generic.List<double> _buffer = new();
private readonly double[] _weight;
private double _norm;
private readonly double _offset, _sigma;
public ALMA_Series(TSeries source, int period, double offset = 0.85, double sigma = 6.0, bool useNaN = false)
: base(source, period, useNaN)
{
_offset = offset;
_sigma = sigma;
_weight = new double[period];
if (this._data.Count > 0) { base.Add(this._data); }
}
public override void Add((System.DateTime t, double v) TValue, bool update)
{
if (update) { this._buffer[this._buffer.Count - 1] = TValue.v; }
else { this._buffer.Add(TValue.v); }
if (this._buffer.Count > this._p) { this._buffer.RemoveAt(0); }
if (this._buffer.Count <= _p) { calc_weights(); }
double _weightedSum = 0;
for (int i = 0; i < this._buffer.Count; i++) { _weightedSum += _weight[i] * _buffer[i]; }
double _alma = _weightedSum / _norm;
var ret = (TValue.t, this.Count < this._p - 1 && this._NaN ? double.NaN : _alma);
base.Add(ret, update);
}
private void calc_weights()
{
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;
}
}
}
+33
View File
@@ -0,0 +1,33 @@
using Xunit;
using System;
using QuanTAlib;
namespace MovingAvg;
public class ALMA_Test
{
[Fact]
public void Add_Test()
{
TSeries a = new() { 0, 1, 2, 3, 4, 5 };
ALMA_Series c = new(a, 4);
Assert.Equal(6, c.Count);
a.Add(5);
Assert.Equal(a.Count, c.Count);
a.Add(10, update: true);
Assert.Equal(a.Count, c.Count);
}
[Fact]
public void Edge_Test()
{
TSeries a = new() { double.NaN, double.Epsilon, double.PositiveInfinity, double.MaxValue };
ALMA_Series c = new(a, 3);
Assert.Equal(a.Count, c.Count);
a.Add(double.NaN);
Assert.Equal(a.Count, c.Count);
a.Add(double.PositiveInfinity);
Assert.Equal(a.Count, c.Count);
}
}
+9
View File
@@ -152,4 +152,13 @@ public class Skender_Stock
Assert.Equal(Math.Round((double)SK.Last().Rsi!, 8), Math.Round(QL.Last().v, 8));
}
[Fact]
public void ALMA()
{
ALMA_Series QL = new(this.bars.Close, this.period, useNaN: false);
var SK = this.quotes.GetAlma(this.period);
Assert.Equal(Math.Round((double)SK.Last().Alma!, 8), Math.Round(QL.Last().v, 8));
}
}