mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-22 12:38:06 +00:00
feat: add new indicators (Decay, Edecay, MinusDi, MinusDm, PlusDi, PlusDm, Maxindex, Minindex, Sarext) and update pine scripts, core libs, validation tests, and python bindings
This commit is contained in:
@@ -325,5 +325,33 @@ public class TtmSqueezeValidationTests
|
||||
Assert.InRange(squeeze.ColorCode, 0, 3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TtmSqueeze_Correction_Recomputes()
|
||||
{
|
||||
var ind = new TtmSqueeze();
|
||||
var t0 = new DateTime(946_684_800_000_000_0L, DateTimeKind.Utc);
|
||||
|
||||
// Build state well past warmup
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
double p = 100.0 + 10.0 * Math.Sin(2.0 * Math.PI * i / 20.0);
|
||||
ind.Update(new TBar(t0.AddMinutes(i), p, p + 2, p - 2, p, 1000), isNew: true);
|
||||
}
|
||||
|
||||
// Anchor bar
|
||||
var anchorTime = t0.AddMinutes(100);
|
||||
const double anchorClose = 105.5;
|
||||
ind.Update(new TBar(anchorTime, anchorClose, anchorClose + 2, anchorClose - 2, anchorClose, 1000), isNew: true);
|
||||
double anchorMomentum = ind.Momentum.Value;
|
||||
|
||||
// Correction with a dramatically different price — Momentum must change
|
||||
ind.Update(new TBar(anchorTime, anchorClose * 10, (anchorClose + 2) * 10, (anchorClose - 2) * 10, anchorClose * 10, 1000), isNew: false);
|
||||
Assert.NotEqual(anchorMomentum, ind.Momentum.Value);
|
||||
|
||||
// Correction back to original price — must exactly restore original Momentum
|
||||
ind.Update(new TBar(anchorTime, anchorClose, anchorClose + 2, anchorClose - 2, anchorClose, 1000), isNew: false);
|
||||
Assert.Equal(anchorMomentum, ind.Momentum.Value, 1e-9);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -322,7 +322,7 @@ public sealed class TtmSqueeze : ITValuePublisher
|
||||
_priceSum -= oldest;
|
||||
_priceSumSquares -= oldest * oldest;
|
||||
}
|
||||
_priceBuffer.Add(close, isNew);
|
||||
_priceBuffer.Add(close);
|
||||
_priceSum += close;
|
||||
_priceSumSquares += close * close;
|
||||
|
||||
@@ -365,11 +365,11 @@ public sealed class TtmSqueeze : ITValuePublisher
|
||||
_prevSqueezeOn = squeezeOn;
|
||||
|
||||
// === Donchian Midline ===
|
||||
_highBuffer.Add(high, isNew);
|
||||
_lowBuffer.Add(low, isNew);
|
||||
_highBuffer.Add(high);
|
||||
_lowBuffer.Add(low);
|
||||
|
||||
double donchianHigh = GetMax(_highBuffer);
|
||||
double donchianLow = GetMin(_lowBuffer);
|
||||
double donchianHigh = _highBuffer.Max();
|
||||
double donchianLow = _lowBuffer.Min();
|
||||
double donchianMid = (donchianHigh + donchianLow) / 2;
|
||||
|
||||
// === Momentum (Linear Regression) ===
|
||||
@@ -383,7 +383,7 @@ public sealed class TtmSqueeze : ITValuePublisher
|
||||
_momentumSumXY = _momentumSumXY + prevSumY - _momPeriod * oldest;
|
||||
_momentumSumY -= oldest;
|
||||
}
|
||||
_momentumBuffer.Add(deviation, isNew);
|
||||
_momentumBuffer.Add(deviation);
|
||||
_momentumSumY += deviation;
|
||||
|
||||
// Recalculate sumXY during warmup (non-O(1), but short duration)
|
||||
@@ -535,6 +535,10 @@ public sealed class TtmSqueeze : ITValuePublisher
|
||||
_saved_prevMomentum = _prevMomentum;
|
||||
_saved_prevSqueezeOn = _prevSqueezeOn;
|
||||
_saved_barCount = _barCount;
|
||||
_priceBuffer.Snapshot();
|
||||
_highBuffer.Snapshot();
|
||||
_lowBuffer.Snapshot();
|
||||
_momentumBuffer.Snapshot();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -552,43 +556,10 @@ public sealed class TtmSqueeze : ITValuePublisher
|
||||
_prevMomentum = _saved_prevMomentum;
|
||||
_prevSqueezeOn = _saved_prevSqueezeOn;
|
||||
_barCount = _saved_barCount;
|
||||
_priceBuffer.Restore();
|
||||
_highBuffer.Restore();
|
||||
_lowBuffer.Restore();
|
||||
_momentumBuffer.Restore();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static double GetMax(RingBuffer buffer)
|
||||
{
|
||||
if (buffer.Count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var span = buffer.GetSpan();
|
||||
double max = span[0];
|
||||
for (int i = 1; i < span.Length; i++)
|
||||
{
|
||||
if (span[i] > max)
|
||||
{
|
||||
max = span[i];
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static double GetMin(RingBuffer buffer)
|
||||
{
|
||||
if (buffer.Count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var span = buffer.GetSpan();
|
||||
double min = span[0];
|
||||
for (int i = 1; i < span.Length; i++)
|
||||
{
|
||||
if (span[i] < min)
|
||||
{
|
||||
min = span[i];
|
||||
}
|
||||
}
|
||||
return min;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user