Skip to content

Commit

Permalink
Added validated HMA, MAD, SDEV, TEMA and Slope
Browse files Browse the repository at this point in the history
  • Loading branch information
mihakralj committed Aug 7, 2024
1 parent 1c6e724 commit 0c39c9e
Show file tree
Hide file tree
Showing 10 changed files with 592 additions and 86 deletions.
79 changes: 79 additions & 0 deletions v2/Indicators/HMA.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
public class HMA
{
public TValue Tick { get; private set; }
public int Period => Math.Min(_index, _period);
public event Signal Pub = delegate { };
private bool IsHot => _index > _period;
private readonly int _period;
private int _index, _hotIndex;

private WMA _wmaHalf;
private WMA _wmaFull;
private WMA _wmaFinal;
private CircularBuffer _diffBuffer;
private double _lastValidHMA;

public HMA(int period)
{
_period = period;
Init();
}

public HMA(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new Signal(Sub));
}

public void Init()
{
_wmaHalf = new WMA(_period / 2);
_wmaFull = new WMA(_period);
_wmaFinal = new WMA((int)Math.Sqrt(_period));
_diffBuffer = new CircularBuffer((int)Math.Sqrt(_period));
_lastValidHMA = 0;
_index = 0;
_hotIndex = 0;
}

public TValue Update(TValue input)
{
if (!input.IsHot && input.IsNew) { _hotIndex++; }

if (double.IsNaN(input.Value) || double.IsInfinity(input.Value))
{
Tick = new TValue(input.Time, _lastValidHMA, input.IsNew, IsHot);
Pub?.Invoke(this, new ValueEventArgs(Tick));
return Tick;
}

if (input.IsNew)
{
_index++;
}

// Calculate WMA with period/2 and period
var wmaHalf = _wmaHalf.Update(input);
var wmaFull = _wmaFull.Update(input);

// Calculate 2 * WMA(n/2) - WMA(n)
double diffWma = 2 * wmaHalf.Value - wmaFull.Value;

// Add the difference to the buffer for final WMA calculation
_diffBuffer.Add(diffWma, input.IsNew);

// Calculate final WMA
var hma = _wmaFinal.Update(new TValue(input.Time, diffWma, input.IsNew, input.IsHot));

_lastValidHMA = hma.Value;

Tick = new TValue(input.Time, hma.Value, input.IsNew, IsHot);
Pub?.Invoke(this, new ValueEventArgs(Tick));
return Tick;
}

public void Sub(object source, ValueEventArgs args)
{
Update(args.Tick);
}
}
86 changes: 86 additions & 0 deletions v2/Indicators/MAD.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
public class MAD
{
public TValue Tick { get; private set; }
public int Period => Math.Min(_index, _period);
public event Signal Pub = delegate { };
private bool IsHot => _index > _period;
private readonly int _period;
private int _index, _hotIndex;
private CircularBuffer _buffer;
private double _sum;
private double _lastValidMAD;
private double _lastAddedValue;

public MAD(int period)
{
_period = period;
_buffer = new CircularBuffer(0);
Init();
}

public MAD(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new Signal(Sub));
}

public void Init()
{
_buffer = new CircularBuffer(_period);
_sum = 0;
_lastValidMAD = 0;
_lastAddedValue = 0;
_index = 0;
_hotIndex = 0;
}

public TValue Update(TValue input)
{
if (!input.IsHot && input.IsNew) { _hotIndex++; }

if (double.IsNaN(input.Value) || double.IsInfinity(input.Value))
{
Tick = new TValue(input.Time, _lastValidMAD, input.IsNew, IsHot);
Pub?.Invoke(this, new ValueEventArgs(Tick));
return Tick;
}

if (input.IsNew)
{
if (_buffer.Count == _buffer.Capacity)
{
_sum -= _buffer[0];
}
_buffer.Add(input.Value);
_sum += input.Value;
_lastAddedValue = input.Value;
_index++;
}
else
{
_sum = _sum - _lastAddedValue + input.Value;
_buffer[_buffer.Count - 1] = input.Value;
_lastAddedValue = input.Value;
}

double mean = _sum / _buffer.Count;
double madSum = 0;

for (int i = 0; i < _buffer.Count; i++)
{
madSum += Math.Abs(_buffer[i] - mean);
}

double mad = madSum / _buffer.Count;
_lastValidMAD = mad;

Tick = new TValue(input.Time, mad, input.IsNew, IsHot);
Pub?.Invoke(this, new ValueEventArgs(Tick));
return Tick;
}

public void Sub(object source, ValueEventArgs args)
{
Update(args.Tick);
}
}
85 changes: 85 additions & 0 deletions v2/Indicators/SDEV.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
public class StdDev
{
public TValue Tick { get; private set; }
public int Period => Math.Min(_index, _period);
public event Signal Pub = delegate { };
private bool IsHot => _index > _period;
private double _sum;
private double _sumSquared;
private CircularBuffer _buffer;
private double _lastAddedValue;
private readonly int _period;
private int _index, _hotIndex;
private double _lastValidStdDev;

public StdDev(int period)
{
_period = period;
Init();
}

public StdDev(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new Signal(Sub));
}

public void Init()
{
_buffer = new CircularBuffer(_period);
_sum = 0;
_sumSquared = 0;
_lastValidStdDev = 0;
_lastAddedValue = 0;
_index = 0;
_hotIndex = 0;
}

public TValue Update(TValue input)
{
if (!input.IsHot && input.IsNew) { _hotIndex++; }

if (double.IsNaN(input.Value) || double.IsInfinity(input.Value))
{
Tick = new TValue(input.Time, _lastValidStdDev, input.IsNew, IsHot);
Pub?.Invoke(this, new ValueEventArgs(Tick));
return Tick;
}

if (input.IsNew)
{
if (_buffer.Count == _buffer.Capacity)
{
_sum -= _buffer[0];
_sumSquared -= _buffer[0] * _buffer[0];
}
_buffer.Add(input.Value);
_sum += input.Value;
_sumSquared += input.Value * input.Value;
_lastAddedValue = input.Value;
_index++;
}
else
{
_sum = _sum - _lastAddedValue + input.Value;
_sumSquared = _sumSquared - (_lastAddedValue * _lastAddedValue) + (input.Value * input.Value);
_buffer[_buffer.Count - 1] = input.Value;
_lastAddedValue = input.Value;
}

double mean = _sum / _buffer.Count;
double variance = (_sumSquared / _buffer.Count) - (mean * mean);
double stdDev = Math.Sqrt(Math.Max(0, variance)); // Ensure non-negative value under the sqrt

_lastValidStdDev = stdDev;

Tick = new TValue(input.Time, stdDev, input.IsNew, IsHot);
Pub?.Invoke(this, new ValueEventArgs(Tick));
return Tick;
}

public void Sub(object source, ValueEventArgs args)
{
Update(args.Tick);
}
}
91 changes: 91 additions & 0 deletions v2/Indicators/Slope.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
public class Slope
{
public TValue Tick { get; private set; }
public int Period => Math.Min(_index, _period);
public event Signal Pub = delegate { };
private bool IsHot => _index > _period;
private readonly int _period;
private int _index, _hotIndex;
private CircularBuffer _buffer;
private double _sumX, _sumY, _sumXY, _sumX2;
private double _lastValidSlope;

public Slope(int period)
{
_period = period;
Init();
}

public Slope(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new Signal(Sub));
}

public void Init()
{
_buffer = new CircularBuffer(_period);
_sumX = 0;
_sumY = 0;
_sumXY = 0;
_sumX2 = 0;
_lastValidSlope = 0;
_index = 0;
_hotIndex = 0;
}

public TValue Update(TValue input)
{
if (!input.IsHot && input.IsNew) { _hotIndex++; }

if (double.IsNaN(input.Value) || double.IsInfinity(input.Value))
{
Tick = new TValue(input.Time, _lastValidSlope, input.IsNew, IsHot);
Pub?.Invoke(this, new ValueEventArgs(Tick));
return Tick;
}

if (input.IsNew)
{
if (_buffer.Count == _buffer.Capacity)
{
double oldestY = _buffer[0];
_sumY -= oldestY;
_sumXY -= oldestY * (_index - _period);
_sumX -= _index - _period;
_sumX2 -= (_index - _period) * (_index - _period);
}

_buffer.Add(input.Value);
_sumY += input.Value;
_sumXY += input.Value * _index;
_sumX += _index;
_sumX2 += _index * _index;
_index++;
}
else
{
double lastY = _buffer[_buffer.Count - 1];
_sumY = _sumY - lastY + input.Value;
_sumXY = _sumXY - (lastY * (_index - 1)) + (input.Value * (_index - 1));
_buffer[_buffer.Count - 1] = input.Value;
}

double n = _buffer.Count;
double slope = (n * _sumXY - _sumX * _sumY) / (n * _sumX2 - _sumX * _sumX);

if (!double.IsNaN(slope) && !double.IsInfinity(slope))
{
_lastValidSlope = slope;
}

Tick = new TValue(input.Time, _lastValidSlope, input.IsNew, IsHot);
Pub?.Invoke(this, new ValueEventArgs(Tick));
return Tick;
}

public void Sub(object source, ValueEventArgs args)
{
Update(args.Tick);
}
}
Loading

0 comments on commit 0c39c9e

Please sign in to comment.