feat: Implement IEquatable for state structures in Dema, Ema, T3, Tema, and Hma classes; update IsHot logic in Hma

This commit is contained in:
Miha Kralj
2025-12-07 18:12:24 -08:00
parent 3975ff2d7f
commit 2a554963d8
6 changed files with 83 additions and 10 deletions
+15 -1
View File
@@ -24,7 +24,7 @@ namespace QuanTAlib;
[SkipLocalsInit]
public sealed class Dema : ITValuePublisher
{
private struct EmaState
private struct EmaState : IEquatable<EmaState>
{
public double Ema;
public double E;
@@ -32,6 +32,20 @@ public sealed class Dema : ITValuePublisher
public bool IsCompensated;
public static EmaState New() => new() { Ema = 0, E = 1.0, IsHot = false, IsCompensated = false };
public override bool Equals(object? obj) => obj is EmaState other && Equals(other);
public bool Equals(EmaState other) =>
Ema == other.Ema &&
E == other.E &&
IsHot == other.IsHot &&
IsCompensated == other.IsCompensated;
public override int GetHashCode() => HashCode.Combine(Ema, E, IsHot, IsCompensated);
public static bool operator ==(EmaState left, EmaState right) => left.Equals(right);
public static bool operator !=(EmaState left, EmaState right) => !left.Equals(right);
}
private readonly double _alpha;
+15 -1
View File
@@ -27,7 +27,7 @@ namespace QuanTAlib;
[SkipLocalsInit]
public sealed class Ema : ITValuePublisher
{
private struct State
private struct State : IEquatable<State>
{
public double Ema;
public double E;
@@ -35,6 +35,20 @@ public sealed class Ema : ITValuePublisher
public bool IsCompensated;
public static State New() => new() { Ema = 0, E = 1.0, IsHot = false, IsCompensated = false };
public override bool Equals(object? obj) => obj is State other && Equals(other);
public bool Equals(State other) =>
Ema == other.Ema &&
E == other.E &&
IsHot == other.IsHot &&
IsCompensated == other.IsCompensated;
public override int GetHashCode() => HashCode.Combine(Ema, E, IsHot, IsCompensated);
public static bool operator ==(State left, State right) => left.Equals(right);
public static bool operator !=(State left, State right) => !left.Equals(right);
}
private readonly double _alpha;
+2 -2
View File
@@ -37,9 +37,9 @@ public class HmaTests
// IsHot is defined as Full.IsHot && Sqrt.IsHot.
// Full becomes hot after 9 updates.
// Sqrt becomes hot after 3 updates.
// So HMA should be hot after 9 updates.
// So HMA should be hot after 9 + 3 - 1 = 11 updates.
for (int i = 0; i < 8; i++)
for (int i = 0; i < 10; i++)
{
hma.Update(new TValue(DateTime.UtcNow, 100));
Assert.False(hma.IsHot);
+8 -3
View File
@@ -21,13 +21,15 @@ namespace QuanTAlib;
public sealed class Hma : ITValuePublisher
{
private readonly int _period;
private readonly int _sqrtPeriod;
private readonly Wma _wmaFull;
private readonly Wma _wmaHalf;
private readonly Wma _wmaSqrt;
private int _sampleCount;
public string Name { get; }
public TValue Last { get; private set; }
public bool IsHot => _wmaFull.IsHot && _wmaSqrt.IsHot;
public bool IsHot => _sampleCount >= _period + _sqrtPeriod - 1;
public event Action<TValue>? Pub;
public Hma(int period)
@@ -36,11 +38,11 @@ public sealed class Hma : ITValuePublisher
_period = period;
int halfPeriod = period / 2;
int sqrtPeriod = (int)Math.Sqrt(period);
_sqrtPeriod = (int)Math.Sqrt(period);
_wmaFull = new Wma(period);
_wmaHalf = new Wma(halfPeriod);
_wmaSqrt = new Wma(sqrtPeriod);
_wmaSqrt = new Wma(_sqrtPeriod);
Name = $"Hma({period})";
}
@@ -53,6 +55,8 @@ public sealed class Hma : ITValuePublisher
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TValue Update(TValue input, bool isNew = true)
{
if (isNew) _sampleCount++;
// 1. Calculate WMA(n)
TValue full = _wmaFull.Update(input, isNew);
@@ -188,6 +192,7 @@ public sealed class Hma : ITValuePublisher
_wmaFull.Reset();
_wmaHalf.Reset();
_wmaSqrt.Reset();
_sampleCount = 0;
Last = default;
}
}
+28 -2
View File
@@ -26,15 +26,28 @@ namespace QuanTAlib;
[SkipLocalsInit]
public sealed class T3 : ITValuePublisher
{
private struct State
private struct State : IEquatable<State>
{
public double E1, E2, E3, E4, E5, E6;
public bool IsInitialized;
public static State New() => new() { IsInitialized = false };
public override bool Equals(object? obj) => obj is State other && Equals(other);
public bool Equals(State other) =>
E1 == other.E1 && E2 == other.E2 && E3 == other.E3 &&
E4 == other.E4 && E5 == other.E5 && E6 == other.E6 &&
IsInitialized == other.IsInitialized;
public override int GetHashCode() => HashCode.Combine(E1, E2, E3, E4, E5, E6, IsInitialized);
public static bool operator ==(State left, State right) => left.Equals(right);
public static bool operator !=(State left, State right) => !left.Equals(right);
}
private readonly struct Parameters
private readonly struct Parameters : IEquatable<Parameters>
{
public readonly double Alpha;
public readonly double C1, C2, C3, C4;
@@ -47,6 +60,19 @@ public sealed class T3 : ITValuePublisher
C3 = c3;
C4 = c4;
}
public override bool Equals(object? obj) => obj is Parameters other && Equals(other);
public bool Equals(Parameters other) =>
Alpha == other.Alpha &&
C1 == other.C1 && C2 == other.C2 &&
C3 == other.C3 && C4 == other.C4;
public override int GetHashCode() => HashCode.Combine(Alpha, C1, C2, C3, C4);
public static bool operator ==(Parameters left, Parameters right) => left.Equals(right);
public static bool operator !=(Parameters left, Parameters right) => !left.Equals(right);
}
private readonly Parameters _params;
+15 -1
View File
@@ -26,7 +26,7 @@ namespace QuanTAlib;
[SkipLocalsInit]
public sealed class Tema : ITValuePublisher
{
private struct EmaState
private struct EmaState : IEquatable<EmaState>
{
public double Ema;
public double E;
@@ -34,6 +34,20 @@ public sealed class Tema : ITValuePublisher
public bool IsCompensated;
public static EmaState New() => new() { Ema = 0, E = 1.0, IsHot = false, IsCompensated = false };
public override bool Equals(object? obj) => obj is EmaState other && Equals(other);
public bool Equals(EmaState other) =>
Ema == other.Ema &&
E == other.E &&
IsHot == other.IsHot &&
IsCompensated == other.IsCompensated;
public override int GetHashCode() => HashCode.Combine(Ema, E, IsHot, IsCompensated);
public static bool operator ==(EmaState left, EmaState right) => left.Equals(right);
public static bool operator !=(EmaState left, EmaState right) => !left.Equals(right);
}
private readonly double _alpha;