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:
Miha Kralj
2026-02-16 22:30:30 -08:00
parent b63b9730e1
commit fa56e2b76d
10 changed files with 38 additions and 54 deletions
+8 -9
View File
@@ -9,8 +9,8 @@ namespace QuanTAlib;
/// </summary>
/// <remarks>
/// DecisionPoint PMO Algorithm:
/// <c>ROC = (Close / Close[1] - 1) × 100</c> (always 1-bar),
/// <c>RocEma = CustomEMA(ROC, timePeriods) × 10</c>,
/// <c>ROC = (Close / Close[1] - 1) × 100</c> (always 1-bar),
/// <c>RocEma = CustomEMA(ROC, timePeriods) × 10</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
@@ -19,7 +19,7 @@ namespace QuanTAlib;
///
/// PMO oscillates around zero; positive values indicate upward momentum, negative values
/// 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
/// companion files in the same directory.
@@ -133,8 +133,7 @@ public sealed class Pmo : AbstractBase
}
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
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
? ((value / _state.PrevClose) - 1.0) * 100.0
: 0.0;
_state.PrevClose = value;
@@ -179,7 +178,7 @@ public sealed class Pmo : AbstractBase
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;
if (!_state.RocEmaSeeded)
{
@@ -300,8 +299,8 @@ public sealed class Pmo : AbstractBase
double alpha2 = 2.0 / smoothPeriods;
// Step 1: Compute 1-bar ROC for all bars
// Step 2: First CustomEMA(ROC, timePeriods) with SMA seed, then ×10
// Step 3: Second CustomEMA(scaled, smoothPeriods) with SMA seed PMO
// Step 2: First CustomEMA(ROC, timePeriods) with SMA seed, then ×10
// Step 3: Second CustomEMA(scaled, smoothPeriods) with SMA seed → PMO
double rocEmaRaw = 0.0;
bool rocEmaSeeded = false;
@@ -352,7 +351,7 @@ public sealed class Pmo : AbstractBase
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)
{
output[i] = 0.0;
+2 -4
View File
@@ -81,8 +81,7 @@ public sealed class Rocp : AbstractBase
else
{
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;
result = past != 0 ? 100.0 * (value - past) / past : 0.0; // skipcq: CS-R1077 - Exact-zero IEEE 754 div guard
}
Last = new TValue(input.Time, result);
@@ -151,8 +150,7 @@ public sealed class Rocp : AbstractBase
else
{
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;
output[i] = past != 0 ? 100.0 * (source[i] - past) / past : 0.0; // skipcq: CS-R1077 - Exact-zero IEEE 754 div guard
}
}
}
+2 -4
View File
@@ -81,8 +81,7 @@ public sealed class Rocr : AbstractBase
else
{
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;
result = past != 0 ? value / past : 1.0; // skipcq: CS-R1077 - Exact-zero IEEE 754 div guard
}
Last = new TValue(input.Time, result);
@@ -151,8 +150,7 @@ public sealed class Rocr : AbstractBase
else
{
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;
output[i] = past != 0 ? source[i] / past : 1.0; // skipcq: CS-R1077 - Exact-zero IEEE 754 div guard
}
}
}