mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-24 05:28:05 +00:00
Refactor and enhance various channel indicators for improved performance and stability
- Updated Codacy instructions to streamline usage guidelines. - Refactored Bbands class to utilize ArrayPool for memory management, preventing stack overflow on large series. - Changed Fcb class to use long for monotonic deques to avoid truncation issues. - Enhanced Kchannel class to ensure safe defaults for non-finite values. - Improved Maenv class to prevent double-priming during calculations. - Modified Mmchannel class to ensure non-negative buffer indices and removed unnecessary state tracking. - Updated Pchannel class to correctly reference IsHot state. - Refined Regchannel class to avoid double-processing during calculations. - Enhanced Starchannel class to sanitize non-finite values during calculations. - Adjusted Stbands.Quantower.cs to allow finer control over multiplier precision. - Updated Ubands class to only update last valid values on new bars. - Modified Uchannel.Quantower.cs to allow for finer multiplier precision. - Enhanced Vwapbands classes to include standard deviation calculations and ensure consistent array lengths. - Refactored Vwapsd classes to include standard deviation outputs and ensure consistent array lengths. - Updated MonotonicDeque to use long for indices to prevent overflow. - Improved Mdape class to handle zero actual values with a substitute value for error calculation. - Enhanced Rae class to ensure correct state management during updates. - Refined Wmape class to simplify the logic for finding last valid actual and predicted values. - Updated Cmf.Quantower classes to ensure MinHistoryDepths reflects the current period.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@@ -168,15 +169,30 @@ public sealed class Bbands : AbstractBase
|
||||
int len = sourceSpan.Length;
|
||||
|
||||
TSeries middleSeries = new(capacity: len);
|
||||
Span<double> middleSpan = stackalloc double[len];
|
||||
Span<double> upperSpan = stackalloc double[len];
|
||||
Span<double> lowerSpan = stackalloc double[len];
|
||||
|
||||
Calculate(sourceSpan, middleSpan, upperSpan, lowerSpan, _period, _multiplier);
|
||||
// Use ArrayPool to avoid stack overflow for large series
|
||||
double[] middleRented = ArrayPool<double>.Shared.Rent(len);
|
||||
double[] upperRented = ArrayPool<double>.Shared.Rent(len);
|
||||
double[] lowerRented = ArrayPool<double>.Shared.Rent(len);
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
try
|
||||
{
|
||||
middleSeries.Add(timeSpan[i], middleSpan[i], isNew: true);
|
||||
Span<double> middleSpan = middleRented.AsSpan(0, len);
|
||||
Span<double> upperSpan = upperRented.AsSpan(0, len);
|
||||
Span<double> lowerSpan = lowerRented.AsSpan(0, len);
|
||||
|
||||
Calculate(sourceSpan, middleSpan, upperSpan, lowerSpan, _period, _multiplier);
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
middleSeries.Add(timeSpan[i], middleSpan[i], isNew: true);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<double>.Shared.Return(middleRented);
|
||||
ArrayPool<double>.Shared.Return(upperRented);
|
||||
ArrayPool<double>.Shared.Return(lowerRented);
|
||||
}
|
||||
|
||||
// Restore state from the last period values
|
||||
@@ -257,8 +273,10 @@ public sealed class Bbands : AbstractBase
|
||||
|
||||
// Calculate standard deviation and bands using O(n) rolling sums
|
||||
// Instead of O(n²) nested loop, maintain running sum and sumSq
|
||||
// Track count of finite values to properly compute mean/variance
|
||||
double rollingSum = 0.0;
|
||||
double rollingSumSq = 0.0;
|
||||
int finiteCount = 0;
|
||||
|
||||
// Initialize rolling sums for first window
|
||||
for (int i = 0; i < Math.Min(period, len); i++)
|
||||
@@ -268,6 +286,7 @@ public sealed class Bbands : AbstractBase
|
||||
{
|
||||
rollingSum += val;
|
||||
rollingSumSq += val * val;
|
||||
finiteCount++;
|
||||
}
|
||||
|
||||
if (i < period - 1)
|
||||
@@ -280,13 +299,22 @@ public sealed class Bbands : AbstractBase
|
||||
// Process first complete window
|
||||
if (len >= period)
|
||||
{
|
||||
double mean = rollingSum / period;
|
||||
double variance = (rollingSumSq / period) - (mean * mean);
|
||||
variance = Math.Max(0.0, variance); // Guard against negative due to floating point
|
||||
double stdDev = Math.Sqrt(variance);
|
||||
double offset = multiplier * stdDev;
|
||||
upper[period - 1] = middle[period - 1] + offset;
|
||||
lower[period - 1] = middle[period - 1] - offset;
|
||||
if (finiteCount == period)
|
||||
{
|
||||
double mean = rollingSum / finiteCount;
|
||||
double variance = (rollingSumSq / finiteCount) - (mean * mean);
|
||||
variance = Math.Max(0.0, variance); // Guard against negative due to floating point
|
||||
double stdDev = Math.Sqrt(variance);
|
||||
double offset = multiplier * stdDev;
|
||||
upper[period - 1] = middle[period - 1] + offset;
|
||||
lower[period - 1] = middle[period - 1] - offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not all values in window are finite, emit NaN
|
||||
upper[period - 1] = double.NaN;
|
||||
lower[period - 1] = double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
// Process remaining bars with O(1) rolling update
|
||||
@@ -298,6 +326,7 @@ public sealed class Bbands : AbstractBase
|
||||
{
|
||||
rollingSum -= outgoing;
|
||||
rollingSumSq -= outgoing * outgoing;
|
||||
finiteCount--;
|
||||
}
|
||||
|
||||
// Add incoming value (current)
|
||||
@@ -306,18 +335,29 @@ public sealed class Bbands : AbstractBase
|
||||
{
|
||||
rollingSum += incoming;
|
||||
rollingSumSq += incoming * incoming;
|
||||
finiteCount++;
|
||||
}
|
||||
|
||||
// Calculate variance from rolling sums: Var = E[X²] - E[X]²
|
||||
double mean = rollingSum / period;
|
||||
double variance = (rollingSumSq / period) - (mean * mean);
|
||||
variance = Math.Max(0.0, variance); // Guard against negative due to floating point
|
||||
// Only compute bands when all values in window are finite
|
||||
if (finiteCount == period)
|
||||
{
|
||||
// Calculate variance from rolling sums: Var = E[X²] - E[X]²
|
||||
double mean = rollingSum / finiteCount;
|
||||
double variance = (rollingSumSq / finiteCount) - (mean * mean);
|
||||
variance = Math.Max(0.0, variance); // Guard against negative due to floating point
|
||||
|
||||
double stdDev = Math.Sqrt(variance);
|
||||
double offset = multiplier * stdDev;
|
||||
double stdDev = Math.Sqrt(variance);
|
||||
double offset = multiplier * stdDev;
|
||||
|
||||
upper[i] = middle[i] + offset;
|
||||
lower[i] = middle[i] - offset;
|
||||
upper[i] = middle[i] + offset;
|
||||
lower[i] = middle[i] - offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Window contains non-finite values, emit NaN
|
||||
upper[i] = double.NaN;
|
||||
lower[i] = double.NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user