mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-22 20:48:04 +00:00
volume indicators
This commit is contained in:
@@ -16,7 +16,7 @@ namespace QuanTAlib;
|
||||
/// - Period forced to even, >= 2.
|
||||
/// </remarks>
|
||||
[SkipLocalsInit]
|
||||
public sealed class Frama : ITValuePublisher
|
||||
public sealed class Frama : ITValuePublisher, IDisposable
|
||||
{
|
||||
private const double AlphaFloor = 0.01;
|
||||
private const double AlphaCeil = 1.0;
|
||||
@@ -27,6 +27,8 @@ public sealed class Frama : ITValuePublisher
|
||||
private readonly RingBuffer _highs;
|
||||
private readonly RingBuffer _lows;
|
||||
private readonly TValuePublishedHandler _handler;
|
||||
private readonly ITValuePublisher? _source;
|
||||
private bool _disposed;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private record struct State
|
||||
@@ -69,6 +71,7 @@ public sealed class Frama : ITValuePublisher
|
||||
|
||||
public Frama(ITValuePublisher source, int period) : this(period)
|
||||
{
|
||||
_source = source;
|
||||
source.Pub += _handler;
|
||||
}
|
||||
|
||||
@@ -131,12 +134,16 @@ public sealed class Frama : ITValuePublisher
|
||||
|
||||
double price = (high + low) * 0.5;
|
||||
|
||||
// Recent half: last _half values (most recent)
|
||||
double maxRecent = GetMax(_highs, _half);
|
||||
double minRecent = GetMin(_lows, _half);
|
||||
// Full period: all _periodEven values
|
||||
double maxFull = GetMax(_highs, _periodEven);
|
||||
double minFull = GetMin(_lows, _periodEven);
|
||||
double maxPrev = GetMax(_highs, _half, startOffset: 0);
|
||||
double minPrev = GetMin(_lows, _half, startOffset: 0);
|
||||
// Previous half: older _half values (starts at count - _periodEven)
|
||||
int prevOffset = _highs.Count - _periodEven;
|
||||
double maxPrev = GetMax(_highs, _half, startOffset: prevOffset);
|
||||
double minPrev = GetMin(_lows, _half, startOffset: prevOffset);
|
||||
|
||||
double n1 = (maxRecent - minRecent) / _half;
|
||||
double n2 = (maxPrev - minPrev) / _half;
|
||||
@@ -357,4 +364,24 @@ public sealed class Frama : ITValuePublisher
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the indicator and unsubscribes from the source.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing && _source != null)
|
||||
{
|
||||
_source.Pub -= _handler;
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,10 @@ public class HemaTests
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new Hema(0));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new Hema(-1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new Hema(1));
|
||||
|
||||
var hema = new Hema(1);
|
||||
Assert.Equal("Hema(1)", hema.Name);
|
||||
var hema = new Hema(2);
|
||||
Assert.Equal("Hema(2)", hema.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -75,9 +75,9 @@ public sealed class Hema : AbstractBase
|
||||
|
||||
public Hema(int period)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(period);
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(period, 2);
|
||||
|
||||
double n = Math.Max((double)period, 2.0);
|
||||
double n = (double)period;
|
||||
_alphaSlow = AlphaFromHalfLife(n);
|
||||
_alphaFast = AlphaFromHalfLife(Math.Max(1.0, n * 0.5));
|
||||
_alphaSmooth = AlphaFromHalfLife(Math.Max(1.0, Math.Sqrt(n)));
|
||||
|
||||
@@ -150,7 +150,7 @@ public sealed class Mama : AbstractBase
|
||||
|
||||
if (_state.Index > 6)
|
||||
{
|
||||
double adj = (AdjSlope * _p_state.Period) + AdjIntercept;
|
||||
double adj = (AdjSlope * _state.Period) + AdjIntercept;
|
||||
|
||||
// Smooth
|
||||
double smooth = (4.0 * _priceBuffer[^1] + 3.0 * _priceBuffer[^2] + 2.0 * _priceBuffer[^3] + _priceBuffer[^4]) * 0.1;
|
||||
|
||||
@@ -83,7 +83,7 @@ public sealed class Mgdi : AbstractBase
|
||||
else
|
||||
{
|
||||
Last = new TValue(input.Time, double.NaN);
|
||||
PubEvent(Last);
|
||||
PubEvent(Last, isNew);
|
||||
return Last;
|
||||
}
|
||||
}
|
||||
@@ -236,4 +236,4 @@ public sealed class Mgdi : AbstractBase
|
||||
{
|
||||
Init();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,9 @@ public sealed class Rgma : AbstractBase
|
||||
private double _lastValidValue;
|
||||
private double _p_lastValidValue;
|
||||
|
||||
private ITValuePublisher? _publisher;
|
||||
private bool _disposed;
|
||||
|
||||
private const double COVERAGE_THRESHOLD = 0.05;
|
||||
private const int ResyncInterval = 10000;
|
||||
private const int StackAllocThreshold = 512;
|
||||
@@ -78,6 +81,7 @@ public sealed class Rgma : AbstractBase
|
||||
/// </summary>
|
||||
public Rgma(ITValuePublisher source, int period, int passes = 3) : this(period, passes)
|
||||
{
|
||||
_publisher = source;
|
||||
source.Pub += Handle;
|
||||
}
|
||||
|
||||
@@ -91,6 +95,7 @@ public sealed class Rgma : AbstractBase
|
||||
{
|
||||
Last = new TValue(source.LastTime, Last.Value);
|
||||
}
|
||||
_publisher = source;
|
||||
source.Pub += Handle;
|
||||
}
|
||||
|
||||
@@ -470,4 +475,19 @@ public sealed class Rgma : AbstractBase
|
||||
Array.Fill(_p_filters, double.NaN);
|
||||
Last = default;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing && _publisher != null)
|
||||
{
|
||||
_publisher.Pub -= Handle;
|
||||
_publisher = null;
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,9 +229,15 @@ public sealed class Yzvama : AbstractBase
|
||||
private void RemoveSorted(double value, int currentCount)
|
||||
{
|
||||
int removePos = LowerBound(_activeSortedYzv, currentCount, value);
|
||||
if (removePos < currentCount && Math.Abs(_activeSortedYzv[removePos] - value) < EPSILON && removePos < currentCount - 1)
|
||||
if (removePos < currentCount && Math.Abs(_activeSortedYzv[removePos] - value) < EPSILON)
|
||||
{
|
||||
Array.Copy(_activeSortedYzv, removePos + 1, _activeSortedYzv, removePos, currentCount - 1 - removePos);
|
||||
// Shift elements left if not removing the last element
|
||||
if (removePos < currentCount - 1)
|
||||
{
|
||||
Array.Copy(_activeSortedYzv, removePos + 1, _activeSortedYzv, removePos, currentCount - 1 - removePos);
|
||||
}
|
||||
// Clear the now-unused tail slot to avoid stale data
|
||||
_activeSortedYzv[currentCount - 1] = double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,27 +255,22 @@ public sealed class Yzvama : AbstractBase
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
// Save state - pointer swap instead of O(n) Array.Copy
|
||||
// Save state - copy current to backup for potential rollback
|
||||
_p_state = _state;
|
||||
_p_lastValidSource = _lastValidSource;
|
||||
|
||||
// Swap buffer references for backup
|
||||
(_activeSourceBuffer, _backupSourceBuffer) = (_backupSourceBuffer, _activeSourceBuffer);
|
||||
(_activeYzvBuffer, _backupYzvBuffer) = (_backupYzvBuffer, _activeYzvBuffer);
|
||||
(_activeSortedYzv, _backupSortedYzv) = (_backupSortedYzv, _activeSortedYzv);
|
||||
|
||||
// Copy current state to active buffer (only needed for first update after swap)
|
||||
Array.Copy(_backupSourceBuffer, _activeSourceBuffer, _maxLength);
|
||||
Array.Copy(_backupYzvBuffer, _activeYzvBuffer, _percentileLookback);
|
||||
Array.Copy(_backupSortedYzv, _activeSortedYzv, _percentileLookback);
|
||||
// Copy active to backup for rollback capability (only copy, no swap)
|
||||
Array.Copy(_activeSourceBuffer, _backupSourceBuffer, _maxLength);
|
||||
Array.Copy(_activeYzvBuffer, _backupYzvBuffer, _percentileLookback);
|
||||
Array.Copy(_activeSortedYzv, _backupSortedYzv, _percentileLookback);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Restore state - pointer swap back
|
||||
// Restore state from backup - O(1) pointer swap for rollback
|
||||
_state = _p_state;
|
||||
_lastValidSource = _p_lastValidSource;
|
||||
|
||||
// Swap back to restore backup as active
|
||||
// Swap pointers so backup becomes active (true O(1) rollback)
|
||||
(_activeSourceBuffer, _backupSourceBuffer) = (_backupSourceBuffer, _activeSourceBuffer);
|
||||
(_activeYzvBuffer, _backupYzvBuffer) = (_backupYzvBuffer, _activeYzvBuffer);
|
||||
(_activeSortedYzv, _backupSortedYzv) = (_backupSortedYzv, _activeSortedYzv);
|
||||
|
||||
@@ -309,6 +309,7 @@ public sealed class Zlema : AbstractBase
|
||||
_lastValidValue = double.NaN;
|
||||
_p_lastValidValue = double.NaN;
|
||||
|
||||
// Clear the buffer and fill with zeros for proper initialization
|
||||
_lagBuffer.Clear();
|
||||
for (int i = 0; i < _lagBuffer.Capacity; i++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user