mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-04 12:07:44 +00:00
fix: address code review issues in indicators and core components
This commit is contained in:
@@ -82,6 +82,22 @@ public sealed class RingBuffer : IEnumerable<double>
|
||||
get => _sum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the sum by iterating over all elements.
|
||||
/// Useful for correcting floating-point drift after many updates.
|
||||
/// </summary>
|
||||
public double RecalculateSum()
|
||||
{
|
||||
double sum = 0;
|
||||
var span = GetSpan();
|
||||
for (int i = 0; i < span.Length; i++)
|
||||
{
|
||||
sum += span[i];
|
||||
}
|
||||
_sum = sum;
|
||||
return sum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Average of all elements in the buffer.
|
||||
/// Returns 0 if buffer is empty.
|
||||
|
||||
@@ -129,12 +129,10 @@ public class TBarSeries : IReadOnlyList<TBar>
|
||||
throw new ArgumentException("All arrays must have the same length");
|
||||
}
|
||||
|
||||
_t.AddRange(tArr);
|
||||
_o.AddRange(oArr);
|
||||
_h.AddRange(hArr);
|
||||
_l.AddRange(lArr);
|
||||
_c.AddRange(cArr);
|
||||
_v.AddRange(vArr);
|
||||
for (int i = 0; i < tArr.Length; i++)
|
||||
{
|
||||
Add(tArr[i], oArr[i], hArr[i], lArr[i], cArr[i], vArr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<TBar> GetEnumerator()
|
||||
|
||||
@@ -24,7 +24,7 @@ public readonly struct TValue : IEquatable<TValue>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue(DateTime time, double value)
|
||||
{
|
||||
Time = time.Ticks;
|
||||
Time = time.Kind == DateTimeKind.Utc ? time.Ticks : time.ToUniversalTime().Ticks;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public readonly struct TValue : IEquatable<TValue>
|
||||
public override string ToString() => $"[{AsDateTime:yyyy-MM-dd HH:mm:ss}, {Value:F2}]";
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(TValue other) => Time == other.Time && Math.Abs(Value - other.Value) < 1e-9;
|
||||
public bool Equals(TValue other) => Time == other.Time && Value == other.Value;
|
||||
|
||||
public override bool Equals(object? obj) => obj is TValue other && Equals(other);
|
||||
public override int GetHashCode() => HashCode.Combine(Time, Value);
|
||||
|
||||
@@ -70,8 +70,9 @@ public class CsvFeed : IFeed
|
||||
var line = dataLines[i];
|
||||
|
||||
var parts = line.Split(',');
|
||||
int originalLineNumber = dataLines.Count - i + 1;
|
||||
if (parts.Length != 6)
|
||||
throw new FormatException($"Invalid CSV format at line {i + 2}. Expected 6 columns, found {parts.Length}");
|
||||
throw new FormatException($"Invalid CSV format at line {originalLineNumber}. Expected 6 columns, found {parts.Length}");
|
||||
|
||||
try
|
||||
{
|
||||
@@ -89,7 +90,7 @@ public class CsvFeed : IFeed
|
||||
}
|
||||
catch (Exception ex) when (ex is FormatException or OverflowException)
|
||||
{
|
||||
throw new FormatException($"Failed to parse CSV line {i + 2}: {line}", ex);
|
||||
throw new FormatException($"Failed to parse CSV line {originalLineNumber}: {line}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +154,7 @@ public class CsvFeed : IFeed
|
||||
var result = new TBarSeries(count);
|
||||
|
||||
// Find starting index
|
||||
int startIndex = 0;
|
||||
int startIndex = -1;
|
||||
for (int i = 0; i < _data.Count; i++)
|
||||
{
|
||||
if (_data[i].Time >= startTime)
|
||||
@@ -163,6 +164,9 @@ public class CsvFeed : IFeed
|
||||
}
|
||||
}
|
||||
|
||||
if (startIndex == -1)
|
||||
return result;
|
||||
|
||||
// Collect bars matching interval
|
||||
long expectedTime = startTime;
|
||||
int collected = 0;
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace QuanTAlib;
|
||||
public sealed class Alma : ITValuePublisher
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly double _offset;
|
||||
private readonly double _sigma;
|
||||
private readonly double[] _weights;
|
||||
private readonly double _weightSum;
|
||||
private readonly RingBuffer _buffer;
|
||||
@@ -62,6 +64,8 @@ public sealed class Alma : ITValuePublisher
|
||||
throw new ArgumentException("Sigma must be greater than 0", nameof(sigma));
|
||||
|
||||
_period = period;
|
||||
_offset = offset;
|
||||
_sigma = sigma;
|
||||
_buffer = new RingBuffer(period);
|
||||
_weights = new double[period];
|
||||
Name = $"Alma({period}, {offset:F2}, {sigma:F2})";
|
||||
@@ -129,7 +133,7 @@ public sealed class Alma : ITValuePublisher
|
||||
var tSpan = CollectionsMarshal.AsSpan(t);
|
||||
var vSpan = CollectionsMarshal.AsSpan(v);
|
||||
|
||||
Calculate(source.Values, vSpan, _period);
|
||||
Calculate(source.Values, vSpan, _period, _offset, _sigma);
|
||||
source.Times.CopyTo(tSpan);
|
||||
|
||||
// Restore state
|
||||
|
||||
@@ -57,6 +57,7 @@ public sealed class Dema : ITValuePublisher
|
||||
private EmaState _p_state2 = EmaState.New();
|
||||
|
||||
private double _lastValidValue;
|
||||
private double _p_lastValidValue;
|
||||
|
||||
public string Name { get; }
|
||||
public TValue Last { get; private set; }
|
||||
@@ -93,11 +94,13 @@ public sealed class Dema : ITValuePublisher
|
||||
{
|
||||
_p_state1 = _state1;
|
||||
_p_state2 = _state2;
|
||||
_p_lastValidValue = _lastValidValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
_state1 = _p_state1;
|
||||
_state2 = _p_state2;
|
||||
_lastValidValue = _p_lastValidValue;
|
||||
}
|
||||
|
||||
// EMA1
|
||||
@@ -302,6 +305,7 @@ public sealed class Dema : ITValuePublisher
|
||||
_p_state1 = EmaState.New();
|
||||
_p_state2 = EmaState.New();
|
||||
_lastValidValue = 0;
|
||||
_p_lastValidValue = 0;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ public sealed class Ema : ITValuePublisher
|
||||
private State _state = State.New();
|
||||
private State _p_state = State.New();
|
||||
private double _lastValidValue;
|
||||
private double _p_lastValidValue;
|
||||
|
||||
/// <summary>
|
||||
/// Display name for the indicator.
|
||||
@@ -134,10 +135,12 @@ public sealed class Ema : ITValuePublisher
|
||||
if (isNew)
|
||||
{
|
||||
_p_state = _state;
|
||||
_p_lastValidValue = _lastValidValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
_state = _p_state;
|
||||
_lastValidValue = _p_lastValidValue;
|
||||
}
|
||||
|
||||
double val = GetValidValue(input.Value);
|
||||
@@ -305,6 +308,7 @@ public sealed class Ema : ITValuePublisher
|
||||
_state = State.New();
|
||||
_p_state = _state;
|
||||
_lastValidValue = 0;
|
||||
_p_lastValidValue = 0;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,12 +177,7 @@ public sealed class Kama : ITValuePublisher
|
||||
|
||||
// Use static Calculate for performance
|
||||
var outputSpan = new double[len];
|
||||
Calculate(source.Values, outputSpan, _period,
|
||||
(int)(2.0 / _fastAlpha - 1), (int)(2.0 / _slowAlpha - 1)); // Reverse calc periods from alphas?
|
||||
// Actually better to pass alphas or periods.
|
||||
// The static method signature should match constructor params.
|
||||
|
||||
// Wait, I need to pass periods to static method.
|
||||
|
||||
// fastPeriod = 2/fastAlpha - 1.
|
||||
int fastPeriod = (int)Math.Round(2.0 / _fastAlpha - 1);
|
||||
int slowPeriod = (int)Math.Round(2.0 / _slowAlpha - 1);
|
||||
|
||||
@@ -252,6 +252,8 @@ public sealed class Lsma : ITValuePublisher
|
||||
int windowSize = Math.Min(len, _period);
|
||||
int startIndex = len - windowSize;
|
||||
|
||||
Reset();
|
||||
|
||||
// Initialize lastValidValue
|
||||
if (startIndex > 0)
|
||||
{
|
||||
@@ -269,8 +271,6 @@ public sealed class Lsma : ITValuePublisher
|
||||
_lastValidValue = 0;
|
||||
}
|
||||
|
||||
Reset();
|
||||
|
||||
for (int i = startIndex; i < len; i++)
|
||||
{
|
||||
double val = GetValidValue(source.Values[i]);
|
||||
|
||||
@@ -103,7 +103,7 @@ public sealed class Sma : ITValuePublisher
|
||||
if (_buffer.IsFull && _tickCount >= ResyncInterval)
|
||||
{
|
||||
_tickCount = 0;
|
||||
_sum = _buffer.Sum();
|
||||
_sum = _buffer.RecalculateSum();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,9 +386,12 @@ public sealed class Sma : ITValuePublisher
|
||||
public void Reset()
|
||||
{
|
||||
_buffer.Clear();
|
||||
var resetSum = 0;
|
||||
_sum = resetSum;
|
||||
Last = default;
|
||||
_sum = 0;
|
||||
_p_sum = 0;
|
||||
_p_lastInput = 0;
|
||||
_lastValidValue = 0;
|
||||
_p_lastValidValue = 0;
|
||||
_tickCount = 0;
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ public sealed class Trima : ITValuePublisher
|
||||
source.Times.CopyTo(tSpan);
|
||||
|
||||
// Restore state
|
||||
int lookback = _p1 + _p2;
|
||||
int lookback = _p1 + _p2 - 1;
|
||||
int startIndex = Math.Max(0, len - lookback);
|
||||
Reset();
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ public sealed class Wma : ITValuePublisher
|
||||
|
||||
_p_sum = _sum;
|
||||
_p_wsum = _wsum;
|
||||
_p_lastInput = source.Values[len - 1];
|
||||
_p_lastInput = _lastValidValue;
|
||||
_p_lastValidValue = _lastValidValue;
|
||||
|
||||
Last = new TValue(tSpan[len - 1], vSpan[len - 1]);
|
||||
|
||||
Reference in New Issue
Block a user