-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added validated HMA, MAD, SDEV, TEMA and Slope
- Loading branch information
Showing
10 changed files
with
592 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
Oops, something went wrong.