mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-15 09:08:04 +00:00
Refactor exact-zero guards and update mathematical notations across multiple classes to enhance clarity and prevent division by zero errors.
This commit is contained in:
+4
-6
@@ -13,8 +13,8 @@ namespace QuanTAlib;
|
|||||||
/// making it useful for timing entries and exits.
|
/// making it useful for timing entries and exits.
|
||||||
///
|
///
|
||||||
/// Formula:
|
/// Formula:
|
||||||
/// num = Σ(count * price[count-1]) for count = 1 to length
|
/// num = Σ(count * price[count-1]) for count = 1 to length
|
||||||
/// den = Σ(price[count-1]) for count = 1 to length
|
/// den = Σ(price[count-1]) for count = 1 to length
|
||||||
/// CG = (num / den) - (length + 1) / 2
|
/// CG = (num / den) - (length + 1) / 2
|
||||||
///
|
///
|
||||||
/// Properties:
|
/// Properties:
|
||||||
@@ -180,8 +180,7 @@ public sealed class Cg : AbstractBase
|
|||||||
private double CalculateCg()
|
private double CalculateCg()
|
||||||
{
|
{
|
||||||
int n = _buffer.Count;
|
int n = _buffer.Count;
|
||||||
// skipcq: CS-R1077 - Exact-zero guard: _sum is cumulative price sum; zero means empty buffer, division by zero below
|
if (n == 0 || _sum == 0) // skipcq: CS-R1077 - Exact-zero guard: _sum is cumulative price sum; zero means empty buffer, division by zero below
|
||||||
if (n == 0 || _sum == 0)
|
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -300,8 +299,7 @@ public sealed class Cg : AbstractBase
|
|||||||
sum += price;
|
sum += price;
|
||||||
}
|
}
|
||||||
|
|
||||||
// skipcq: CS-R1077 - Exact-zero guard: sum is cumulative price sum; zero means empty buffer, division by zero below
|
if (sum == 0) // skipcq: CS-R1077 - Exact-zero guard: sum is cumulative price sum; zero means empty buffer, division by zero below
|
||||||
if (sum == 0)
|
|
||||||
{
|
{
|
||||||
output[i] = 0;
|
output[i] = 0;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace QuanTAlib;
|
|||||||
/// 3. Homodyne mixing: multiply I/Q with their 1-bar delayed values
|
/// 3. Homodyne mixing: multiply I/Q with their 1-bar delayed values
|
||||||
/// 4. Re = I*I[1] + Q*Q[1], Im = I*Q[1] - Q*I[1]
|
/// 4. Re = I*I[1] + Q*Q[1], Im = I*Q[1] - Q*I[1]
|
||||||
/// 5. Angle = atan2(Im, Re) gives instantaneous phase change
|
/// 5. Angle = atan2(Im, Re) gives instantaneous phase change
|
||||||
/// 6. Period = 2π / angle with clamping and smoothing
|
/// 6. Period = 2Ï€ / angle with clamping and smoothing
|
||||||
///
|
///
|
||||||
/// Properties:
|
/// Properties:
|
||||||
/// - Returns smoothed dominant cycle period
|
/// - Returns smoothed dominant cycle period
|
||||||
@@ -321,8 +321,7 @@ public sealed class Homod : AbstractBase
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static double Atan2(double y, double x)
|
private static double Atan2(double y, double x)
|
||||||
{
|
{
|
||||||
// skipcq: CS-R1077 - Exact-zero guard: both quadrature components zero means no signal; atan2(0,0) is undefined
|
if (y == 0.0 && x == 0.0) // skipcq: CS-R1077 - Exact-zero guard: both quadrature components zero means no signal; atan2(0,0) is undefined
|
||||||
if (y == 0.0 && x == 0.0)
|
|
||||||
{
|
{
|
||||||
return 0.0; // Return 0 instead of error for robustness
|
return 0.0; // Return 0 instead of error for robustness
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,12 +206,10 @@ public sealed class HtSine : AbstractBase
|
|||||||
prevI2 = i2;
|
prevI2 = i2;
|
||||||
|
|
||||||
double tempReal1 = period;
|
double tempReal1 = period;
|
||||||
// skipcq: CS-R1077 - Exact-zero guard: atan(im/re) requires nonzero denominator; zero im/re means no signal energy
|
if (im != 0.0 && re != 0.0) // skipcq: CS-R1077 - Exact-zero guard: atan(im/re) needs nonzero; zero means no signal
|
||||||
if (im != 0.0 && re != 0.0)
|
|
||||||
{
|
{
|
||||||
double angle = Math.Atan(im / re);
|
double angle = Math.Atan(im / re);
|
||||||
// skipcq: CS-R1077 - Exact-zero guard: angle == 0 means period is undefined (division by angle below)
|
if (angle != 0.0) // skipcq: CS-R1077 - Exact-zero guard: angle == 0 means period undefined (div by angle)
|
||||||
if (angle != 0.0)
|
|
||||||
{
|
{
|
||||||
period = (2.0 * Math.PI) / angle;
|
period = (2.0 * Math.PI) / angle;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ namespace QuanTAlib;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// DecisionPoint PMO Algorithm:
|
/// DecisionPoint PMO Algorithm:
|
||||||
/// <c>ROC = (Close / Close[1] - 1) × 100</c> (always 1-bar),
|
/// <c>ROC = (Close / Close[1] - 1) × 100</c> (always 1-bar),
|
||||||
/// <c>RocEma = CustomEMA(ROC, timePeriods) × 10</c>,
|
/// <c>RocEma = CustomEMA(ROC, timePeriods) × 10</c>,
|
||||||
/// <c>PMO = CustomEMA(RocEma, smoothPeriods)</c>.
|
/// <c>PMO = CustomEMA(RocEma, smoothPeriods)</c>.
|
||||||
///
|
///
|
||||||
/// Custom EMA uses alpha = 2/N (not the standard 2/(N+1)), and is seeded with the SMA
|
/// Custom EMA uses alpha = 2/N (not the standard 2/(N+1)), and is seeded with the SMA
|
||||||
@@ -19,7 +19,7 @@ namespace QuanTAlib;
|
|||||||
///
|
///
|
||||||
/// PMO oscillates around zero; positive values indicate upward momentum, negative values
|
/// PMO oscillates around zero; positive values indicate upward momentum, negative values
|
||||||
/// indicate downward momentum. Crossings of zero or a signal line suggest trend changes.
|
/// indicate downward momentum. Crossings of zero or a signal line suggest trend changes.
|
||||||
/// Non-finite inputs (NaN/±Inf) are sanitized by substituting the last finite value observed.
|
/// Non-finite inputs (NaN/±Inf) are sanitized by substituting the last finite value observed.
|
||||||
///
|
///
|
||||||
/// For the authoritative algorithm reference, full rationale, and behavioral contracts, see the
|
/// For the authoritative algorithm reference, full rationale, and behavioral contracts, see the
|
||||||
/// companion files in the same directory.
|
/// companion files in the same directory.
|
||||||
@@ -133,8 +133,7 @@ public sealed class Pmo : AbstractBase
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// skipcq: CS-R1077 - Exact-zero guard: PrevClose is a price; zero means no prior data, division by zero produces Infinity
|
roc = _state.PrevClose != 0.0 // skipcq: CS-R1077 - Exact-zero guard: PrevClose is a price; zero means no prior data, division by zero produces Infinity
|
||||||
roc = _state.PrevClose != 0.0
|
|
||||||
? ((value / _state.PrevClose) - 1.0) * 100.0
|
? ((value / _state.PrevClose) - 1.0) * 100.0
|
||||||
: 0.0;
|
: 0.0;
|
||||||
_state.PrevClose = value;
|
_state.PrevClose = value;
|
||||||
@@ -179,7 +178,7 @@ public sealed class Pmo : AbstractBase
|
|||||||
rocEmaScaled = _state.RocEmaRaw * 10.0;
|
rocEmaScaled = _state.RocEmaRaw * 10.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Second Custom EMA smoothing → PMO (SMA-seeded, alpha = 2/smoothPeriods)
|
// Step 3: Second Custom EMA smoothing → PMO (SMA-seeded, alpha = 2/smoothPeriods)
|
||||||
double pmoValue;
|
double pmoValue;
|
||||||
if (!_state.RocEmaSeeded)
|
if (!_state.RocEmaSeeded)
|
||||||
{
|
{
|
||||||
@@ -300,8 +299,8 @@ public sealed class Pmo : AbstractBase
|
|||||||
double alpha2 = 2.0 / smoothPeriods;
|
double alpha2 = 2.0 / smoothPeriods;
|
||||||
|
|
||||||
// Step 1: Compute 1-bar ROC for all bars
|
// Step 1: Compute 1-bar ROC for all bars
|
||||||
// Step 2: First CustomEMA(ROC, timePeriods) with SMA seed, then ×10
|
// Step 2: First CustomEMA(ROC, timePeriods) with SMA seed, then ×10
|
||||||
// Step 3: Second CustomEMA(scaled, smoothPeriods) with SMA seed → PMO
|
// Step 3: Second CustomEMA(scaled, smoothPeriods) with SMA seed → PMO
|
||||||
|
|
||||||
double rocEmaRaw = 0.0;
|
double rocEmaRaw = 0.0;
|
||||||
bool rocEmaSeeded = false;
|
bool rocEmaSeeded = false;
|
||||||
@@ -352,7 +351,7 @@ public sealed class Pmo : AbstractBase
|
|||||||
rocEmaScaled = rocEmaRaw * 10.0;
|
rocEmaScaled = rocEmaRaw * 10.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second Custom EMA of scaled RocEma with SMA seed → PMO
|
// Second Custom EMA of scaled RocEma with SMA seed → PMO
|
||||||
if (!rocEmaSeeded)
|
if (!rocEmaSeeded)
|
||||||
{
|
{
|
||||||
output[i] = 0.0;
|
output[i] = 0.0;
|
||||||
|
|||||||
@@ -81,8 +81,7 @@ public sealed class Rocp : AbstractBase
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
double past = _buffer[0];
|
double past = _buffer[0];
|
||||||
// skipcq: CS-R1077 - Exact-zero guard: IEEE 754 div-by-zero produces Infinity; any nonzero denominator is valid
|
result = past != 0 ? 100.0 * (value - past) / past : 0.0; // skipcq: CS-R1077 - Exact-zero IEEE 754 div guard
|
||||||
result = past != 0 ? 100.0 * (value - past) / past : 0.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Last = new TValue(input.Time, result);
|
Last = new TValue(input.Time, result);
|
||||||
@@ -151,8 +150,7 @@ public sealed class Rocp : AbstractBase
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
double past = source[i - period];
|
double past = source[i - period];
|
||||||
// skipcq: CS-R1077 - Exact-zero guard: IEEE 754 div-by-zero produces Infinity; any nonzero denominator is valid
|
output[i] = past != 0 ? 100.0 * (source[i] - past) / past : 0.0; // skipcq: CS-R1077 - Exact-zero IEEE 754 div guard
|
||||||
output[i] = past != 0 ? 100.0 * (source[i] - past) / past : 0.0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,8 +81,7 @@ public sealed class Rocr : AbstractBase
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
double past = _buffer[0];
|
double past = _buffer[0];
|
||||||
// skipcq: CS-R1077 - Exact-zero guard: IEEE 754 div-by-zero produces Infinity; any nonzero denominator is valid
|
result = past != 0 ? value / past : 1.0; // skipcq: CS-R1077 - Exact-zero IEEE 754 div guard
|
||||||
result = past != 0 ? value / past : 1.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Last = new TValue(input.Time, result);
|
Last = new TValue(input.Time, result);
|
||||||
@@ -151,8 +150,7 @@ public sealed class Rocr : AbstractBase
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
double past = source[i - period];
|
double past = source[i - period];
|
||||||
// skipcq: CS-R1077 - Exact-zero guard: IEEE 754 div-by-zero produces Infinity; any nonzero denominator is valid
|
output[i] = past != 0 ? source[i] / past : 1.0; // skipcq: CS-R1077 - Exact-zero IEEE 754 div guard
|
||||||
output[i] = past != 0 ? source[i] / past : 1.0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -329,8 +329,7 @@ public sealed class Bbs : ITValuePublisher
|
|||||||
_prevSqueezeOn = squeezeOn;
|
_prevSqueezeOn = squeezeOn;
|
||||||
|
|
||||||
// === Bandwidth ===
|
// === Bandwidth ===
|
||||||
// skipcq: CS-R1077 - Exact-zero guard: bbMean is a price average; zero means no data, not rounding artifact
|
double bandwidth = bbMean != 0.0 ? ((bbUpper - bbLower) / bbMean) * 100.0 : 0.0; // skipcq: CS-R1077 - Exact-zero div guard: price avg
|
||||||
double bandwidth = bbMean != 0.0 ? ((bbUpper - bbLower) / bbMean) * 100.0 : 0.0;
|
|
||||||
|
|
||||||
// === Resync for floating-point drift ===
|
// === Resync for floating-point drift ===
|
||||||
if (isNew)
|
if (isNew)
|
||||||
@@ -612,8 +611,7 @@ public sealed class Bbs : ITValuePublisher
|
|||||||
squeezeOn[i] = bbUpper < kcUpper && bbLower > kcLower;
|
squeezeOn[i] = bbUpper < kcUpper && bbLower > kcLower;
|
||||||
|
|
||||||
// Bandwidth
|
// Bandwidth
|
||||||
// skipcq: CS-R1077 - Exact-zero guard: bbMean is a price average; zero means no data, not rounding artifact
|
bandwidth[i] = bbMean != 0.0 ? ((bbUpper - bbLower) / bbMean) * 100.0 : 0.0; // skipcq: CS-R1077 - Exact-zero div guard: price avg
|
||||||
bandwidth[i] = bbMean != 0.0 ? ((bbUpper - bbLower) / bbMean) * 100.0 : 0.0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace QuanTAlib;
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Measures the percentage difference between the current price and the
|
/// Measures the percentage difference between the current price and the
|
||||||
/// Time Series Forecast (linear regression endpoint):
|
/// Time Series Forecast (linear regression endpoint):
|
||||||
/// <c>CFO = 100 × (source − TSF) / source</c>
|
/// <c>CFO = 100 × (source − TSF) / source</c>
|
||||||
///
|
///
|
||||||
/// Uses O(1) incremental sumY / sumXY maintenance from the PineScript reference.
|
/// Uses O(1) incremental sumY / sumXY maintenance from the PineScript reference.
|
||||||
/// When source equals zero, returns NaN to avoid division by zero.
|
/// When source equals zero, returns NaN to avoid division by zero.
|
||||||
@@ -26,7 +26,7 @@ public sealed class Cfo : AbstractBase
|
|||||||
|
|
||||||
// Precomputed linear regression constants (full window)
|
// Precomputed linear regression constants (full window)
|
||||||
private readonly double _sumX; // 0 + 1 + ... + (period-1)
|
private readonly double _sumX; // 0 + 1 + ... + (period-1)
|
||||||
private readonly double _denomX; // period * sumX2 - sumX²
|
private readonly double _denomX; // period * sumX2 - sumX²
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Auto)]
|
[StructLayout(LayoutKind.Auto)]
|
||||||
private record struct State(
|
private record struct State(
|
||||||
@@ -147,8 +147,7 @@ public sealed class Cfo : AbstractBase
|
|||||||
double tsf = Math.FusedMultiplyAdd(slope, _period - 1, intercept);
|
double tsf = Math.FusedMultiplyAdd(slope, _period - 1, intercept);
|
||||||
|
|
||||||
// CFO = 100 * (source - tsf) / source
|
// CFO = 100 * (source - tsf) / source
|
||||||
// skipcq: CS-R1077 - Exact-zero guard: value is a price; zero means no data, division by zero produces Infinity
|
double cfo = value == 0.0 ? double.NaN : 100.0 * (value - tsf) / value; // skipcq: CS-R1077 - Exact-zero guard: value is a price; zero means no data, division by zero produces Infinity
|
||||||
double cfo = value == 0.0 ? double.NaN : 100.0 * (value - tsf) / value;
|
|
||||||
|
|
||||||
Last = new TValue(input.Time, cfo);
|
Last = new TValue(input.Time, cfo);
|
||||||
PubEvent(Last, isNew);
|
PubEvent(Last, isNew);
|
||||||
@@ -305,8 +304,7 @@ public sealed class Cfo : AbstractBase
|
|||||||
double intercept = (sumY - slope * sumX) / period;
|
double intercept = (sumY - slope * sumX) / period;
|
||||||
double tsf = Math.FusedMultiplyAdd(slope, period - 1, intercept);
|
double tsf = Math.FusedMultiplyAdd(slope, period - 1, intercept);
|
||||||
|
|
||||||
// skipcq: CS-R1077 - Exact-zero guard: val is a price; zero means no data, division by zero produces Infinity
|
output[i] = val == 0.0 ? double.NaN : 100.0 * (val - tsf) / val; // skipcq: CS-R1077 - Exact-zero guard: val is a price; zero means no data, division by zero produces Infinity
|
||||||
output[i] = val == 0.0 ? double.NaN : 100.0 * (val - tsf) / val;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -307,8 +307,7 @@ public sealed class Mode : AbstractBase
|
|||||||
|
|
||||||
for (int i = 1; i < count; i++)
|
for (int i = 1; i < count; i++)
|
||||||
{
|
{
|
||||||
// skipcq: CS-R1077 - Exact-equality required: mode detection counts identical values in a sorted array; epsilon would merge distinct prices
|
if (sorted[i] == sorted[i - 1]) // skipcq: CS-R1077 - Exact-equality required: mode detection counts identical values in a sorted array; epsilon would merge distinct prices
|
||||||
if (sorted[i] == sorted[i - 1])
|
|
||||||
{
|
{
|
||||||
currentFreq++;
|
currentFreq++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,24 +4,24 @@ using System.Runtime.CompilerServices;
|
|||||||
namespace QuanTAlib;
|
namespace QuanTAlib;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Computes the Spearman Rank Correlation Coefficient (Spearman's ρ), which measures
|
/// Computes the Spearman Rank Correlation Coefficient (Spearman's Ï), which measures
|
||||||
/// the monotonic relationship between two series by applying Pearson correlation to
|
/// the monotonic relationship between two series by applying Pearson correlation to
|
||||||
/// their ranks.
|
/// their ranks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Spearman's Rho Algorithm:
|
/// Spearman's Rho Algorithm:
|
||||||
/// <c>ρ = Pearson(rank(X), rank(Y))</c>
|
/// <c>Ï = Pearson(rank(X), rank(Y))</c>
|
||||||
///
|
///
|
||||||
/// Ranks are 1-based with average-rank tie-breaking: if k values share the same value,
|
/// Ranks are 1-based with average-rank tie-breaking: if k values share the same value,
|
||||||
/// each receives the mean of the positions they would occupy.
|
/// each receives the mean of the positions they would occupy.
|
||||||
///
|
///
|
||||||
/// When no ties exist, the simplified formula applies:
|
/// When no ties exist, the simplified formula applies:
|
||||||
/// <c>ρ = 1 - 6·Σd² / (n·(n²-1))</c>, where d_i = rank(x_i) - rank(y_i).
|
/// <c>Ï = 1 - 6·Σd² / (n·(n²-1))</c>, where d_i = rank(x_i) - rank(y_i).
|
||||||
///
|
///
|
||||||
/// This implementation uses the general Pearson-on-ranks method because ties can occur
|
/// This implementation uses the general Pearson-on-ranks method because ties can occur
|
||||||
/// in financial data (identical closes, rounded prices). Ranking is O(n²) per series.
|
/// in financial data (identical closes, rounded prices). Ranking is O(n²) per series.
|
||||||
///
|
///
|
||||||
/// Non-finite inputs (NaN/±Inf) are sanitized by substituting the last finite value observed.
|
/// Non-finite inputs (NaN/±Inf) are sanitized by substituting the last finite value observed.
|
||||||
///
|
///
|
||||||
/// For the authoritative algorithm reference, full rationale, and behavioral contracts, see the
|
/// For the authoritative algorithm reference, full rationale, and behavioral contracts, see the
|
||||||
/// companion files in the same directory.
|
/// companion files in the same directory.
|
||||||
@@ -65,7 +65,7 @@ public sealed class Spearman : AbstractBase
|
|||||||
/// <param name="seriesX">First series value</param>
|
/// <param name="seriesX">First series value</param>
|
||||||
/// <param name="seriesY">Second series value</param>
|
/// <param name="seriesY">Second series value</param>
|
||||||
/// <param name="isNew">Whether this is a new bar</param>
|
/// <param name="isNew">Whether this is a new bar</param>
|
||||||
/// <returns>Spearman's ρ coefficient (-1 to +1)</returns>
|
/// <returns>Spearman's Ï coefficient (-1 to +1)</returns>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public TValue Update(TValue seriesX, TValue seriesY, bool isNew = true)
|
public TValue Update(TValue seriesX, TValue seriesY, bool isNew = true)
|
||||||
{
|
{
|
||||||
@@ -144,7 +144,7 @@ public sealed class Spearman : AbstractBase
|
|||||||
return double.NaN;
|
return double.NaN;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate rank arrays — stackalloc for small, ArrayPool for large
|
// Allocate rank arrays — stackalloc for small, ArrayPool for large
|
||||||
double[]? rentedRx = null;
|
double[]? rentedRx = null;
|
||||||
double[]? rentedRy = null;
|
double[]? rentedRy = null;
|
||||||
scoped Span<double> rankX;
|
scoped Span<double> rankX;
|
||||||
@@ -189,7 +189,7 @@ public sealed class Spearman : AbstractBase
|
|||||||
|
|
||||||
if (sumXX < Epsilon || sumYY < Epsilon)
|
if (sumXX < Epsilon || sumYY < Epsilon)
|
||||||
{
|
{
|
||||||
return 0.0; // Constant series → zero correlation
|
return 0.0; // Constant series → zero correlation
|
||||||
}
|
}
|
||||||
|
|
||||||
return sumXY / Math.Sqrt(sumXX * sumYY);
|
return sumXY / Math.Sqrt(sumXX * sumYY);
|
||||||
@@ -208,7 +208,7 @@ public sealed class Spearman : AbstractBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Computes 1-based average ranks for buffer values. O(n²).
|
/// Computes 1-based average ranks for buffer values. O(n²).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static void ComputeRanks(RingBuffer buffer, int n, Span<double> ranks)
|
private static void ComputeRanks(RingBuffer buffer, int n, Span<double> ranks)
|
||||||
@@ -226,8 +226,7 @@ public sealed class Spearman : AbstractBase
|
|||||||
{
|
{
|
||||||
countSmaller++;
|
countSmaller++;
|
||||||
}
|
}
|
||||||
// skipcq: CS-R1077 - Exact-equality required: Spearman tie-detection needs bit-identical values; epsilon would create false ties
|
if (vj == vi) // skipcq: CS-R1077 - Exact-equality required: Spearman tie-detection needs bit-identical values; epsilon would create false ties
|
||||||
if (vj == vi)
|
|
||||||
{
|
{
|
||||||
countEqual++; // includes self
|
countEqual++; // includes self
|
||||||
}
|
}
|
||||||
@@ -256,7 +255,7 @@ public sealed class Spearman : AbstractBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates Spearman's ρ for two time series.
|
/// Calculates Spearman's Ï for two time series.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static TSeries Batch(TSeries seriesX, TSeries seriesY, int period = 20, Spearman? indicator = null)
|
public static TSeries Batch(TSeries seriesX, TSeries seriesY, int period = 20, Spearman? indicator = null)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user