feat: Add new CodeQL extension for C# and SonarLint configuration

- Introduced a new CodeQL extension for C# in `.github/codeql/extensions/quantalib-csharp/codeql-pack.yml`.
- Added SonarLint configuration in `.sonarlint/CSharp/SonarLint.xml` and `.sonarlint/csharp.ruleset` to suppress specific rules for high-performance indicators.
- Removed outdated `.vscode/launch.json` configurations.
- Updated `.vscode/tasks.json` to streamline build and test tasks, including renaming and consolidating tasks.
- Modified `Directory.Build.props` to enhance SARIF output directory handling and integrate SonarLint rules.
- Refactored various indicator classes to improve code clarity and maintainability, including updates to method parameters for consistency.
- Added XML documentation comments to several classes and methods for better code understanding.
- Improved numerical stability in calculations by replacing direct comparisons with `double.Epsilon` checks in multiple classes.
This commit is contained in:
Miha Kralj
2026-01-21 23:05:38 -06:00
parent 32f3366952
commit da4e56bf40
32 changed files with 187 additions and 187 deletions
Vendored
BIN
View File
Binary file not shown.
@@ -0,0 +1,7 @@
name: mihakralj/quantalib-csharp
version: 0.0.0
library: true
extensionTargets:
codeql/csharp-all: '*'
dataExtensions:
- models/**/*.yml
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<AnalysisInput>
<Settings>
</Settings>
<Rules>
<!-- Cognitive Complexity: disabled for indicator state machines with necessary branching -->
<Rule>
<Key>csharpsquid:S3776</Key>
<Parameters>
</Parameters>
</Rule>
</Rules>
</AnalysisInput>
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="QuanTAlib SonarLint Rules" Description="Custom rule suppressions for QuanTAlib high-performance indicators" ToolsVersion="17.0">
<Rules AnalyzerId="SonarAnalyzer.CSharp" RuleNamespace="SonarAnalyzer.CSharp">
<!-- S3776: Cognitive Complexity - disabled for state machine patterns in indicators -->
<Rule Id="S3776" Action="None" />
<!-- S107: Methods should not have too many parameters - SIMD methods require multiple params -->
<Rule Id="S107" Action="None" />
<!-- S1144: Unused private types or members should be removed -->
<Rule Id="S1144" Action="None" />
<!-- S2245: Random should not be used for security purposes -->
<Rule Id="S2245" Action="None" />
<!-- S3604: Remove member initializer - null! is intentional -->
<Rule Id="S3604" Action="None" />
</Rules>
</RuleSet>
-73
View File
@@ -1,73 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Build lib/quantalib.csproj (Debug net10.0)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
"build",
"${workspaceFolder}/lib/quantalib.csproj",
"--configuration",
"Debug",
"--framework",
"net10.0"
],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"console": "integratedTerminal",
"logging": {
"moduleLoad": false
}
},
{
"name": "Debug Tests",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-sln-debug",
"program": "dotnet",
"args": [
"test",
"${workspaceFolder}/QuanTAlib.sln",
"--no-build"
],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"console": "internalConsole",
"logging": {
"moduleLoad": false
}
},
{
"name": "Debug Library",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}",
"justMyCode": true,
"logging": {
"moduleLoad": false
}
},
{
"name": "Build solution (Debug)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-sln-debug",
"program": "dotnet",
"args": [
"build",
"${workspaceFolder}/QuanTAlib.sln",
"--configuration",
"Debug"
],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"console": "integratedTerminal",
"logging": {
"moduleLoad": false
}
}
]
}
+15 -57
View File
@@ -2,23 +2,8 @@
"version": "2.0.0",
"tasks": [
{
"label": "Build & Audit (Roslyn + JetBrains)",
"dependsOrder": "sequence",
"dependsOn": ["Create .sarif Dir", "Build: QuanTAlib", "JetBrains: Inspect"],
"group": { "kind": "build", "isDefault": true }
},
{
"label": "Create .sarif Dir",
"type": "shell",
"command": "mkdir -p .sarif",
"windows": {
"command": "powershell",
"args": ["-Command", "if (!(Test-Path .sarif)) { New-Item -Path .sarif -ItemType Directory }"]
},
"presentation": { "reveal": "silent" }
},
{
"label": "Build: QuanTAlib",
"label": "Build",
"detail": "dotnet build QuanTAlib.sln",
"type": "process",
"command": "dotnet",
"args": [
@@ -27,63 +12,36 @@
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "JetBrains: Inspect",
"type": "shell",
"command": "jb inspectcode QuanTAlib.sln --output=.sarif/jetbrains.sarif --format=Sarif",
"presentation": { "reveal": "always" },
"problemMatcher": [],
"postLines": ["Analysis complete. Open .sarif/jetbrains.sarif to view results."]
},
{
"label": "JetBrains: Cleanup",
"type": "shell",
"command": "jb cleanupcode QuanTAlib.sln",
"problemMatcher": []
},
{
"label": "test-net10",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/lib/QuanTAlib.Tests.csproj",
"--framework",
"net10.0"
],
"problemMatcher": "$msCompile",
"group": { "kind": "test", "isDefault": true },
"presentation": { "reveal": "always", "panel": "new" }
"group": { "kind": "build", "isDefault": true }
},
{
"label": "build-lib-net10-debug",
"label": "Build Library",
"detail": "dotnet build lib/quantalib.csproj (Debug, net10.0)",
"type": "process",
"command": "dotnet",
"args": [
"build",
"${workspaceFolder}/lib/quantalib.csproj",
"--configuration",
"Debug",
"--framework",
"net10.0"
"--configuration", "Debug",
"--framework", "net10.0"
],
"problemMatcher": "$msCompile",
"group": { "kind": "build", "isDefault": false }
"group": "build"
},
{
"label": "build-sln-debug",
"label": "Test",
"detail": "dotnet test lib/QuanTAlib.Tests.csproj (net10.0)",
"type": "process",
"command": "dotnet",
"args": [
"build",
"${workspaceFolder}/QuanTAlib.sln",
"--configuration",
"Debug"
"test",
"${workspaceFolder}/lib/QuanTAlib.Tests.csproj",
"--framework", "net10.0"
],
"problemMatcher": "$msCompile",
"group": { "kind": "build", "isDefault": false }
"group": { "kind": "test", "isDefault": true },
"presentation": { "reveal": "always", "panel": "new" }
}
]
}
+22 -8
View File
@@ -19,7 +19,7 @@
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<IsLocalBuild Condition="'$(GITHUB_ACTIONS)' == ''">true</IsLocalBuild>
<!-- AOT and Trim compatibility analyzers (build-time validation) -->
<IsAotCompatible>true</IsAotCompatible>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
@@ -33,12 +33,15 @@
<!-- SARIF 2.1 output for Roslyn analyzers - all projects -->
<PropertyGroup>
<SarifOutputDir>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'QuanTAlib.sln'))\.sarif</SarifOutputDir>
<ErrorLog>$(SarifOutputDir)\$(MSBuildProjectName).sarif,version=2.1</ErrorLog>
<!-- Use forward slashes for cross-platform compatibility (works on Windows, Linux, macOS) -->
<SarifOutputDir>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'QuanTAlib.sln'))/.sarif</SarifOutputDir>
<ErrorLog>$(SarifOutputDir)/$(MSBuildProjectName).sarif,version=2.1</ErrorLog>
</PropertyGroup>
<Target Name="CreateSarifDir" BeforeTargets="CoreCompile">
<MakeDir Directories="$(SarifOutputDir)" Condition="!Exists('$(SarifOutputDir)')" />
<MakeDir Directories="$(SarifOutputDir)" />
<Message Importance="High" Text="SARIF output directory: $(SarifOutputDir)" />
<Message Importance="High" Text="SARIF file: $(ErrorLog)" />
</Target>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
@@ -46,15 +49,15 @@
<DebugType>portable</DebugType>
<DebugSymbols>true</DebugSymbols>
<Deterministic>true</Deterministic>
<!-- Trim mode for library compatibility validation -->
<TrimMode>link</TrimMode>
<!-- Runtime feature switches (reduce size when library is trimmed by consumer) -->
<EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
<EnableUnsafeUTF7Encoding>false</EnableUnsafeUTF7Encoding>
<InvariantGlobalization>true</InvariantGlobalization>
<!-- Suppress reflection-based serialization (faster startup, smaller footprint) -->
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
@@ -62,7 +65,8 @@
<PropertyGroup>
<!-- S3604: "Remove member initializer" - null! is intentional for nullable reference types in Quantower adapters -->
<!-- S107: "Methods should not have too many parameters" - high-performance SIMD methods require multiple parameters for zero-allocation patterns -->
<NoWarn>$(NoWarn);S107;S1144;S1944;S2053;S2245;S2259;S2583;S2589;S3329;S3604;S3655;S3776;S3949;S3966;S4158;S4347;S5773;S6781;MA0048;MA0051;MA0076;RCS1123;RCS1159;IDE0007</NoWarn>
<!-- MA0007: "Add comma after the last value" - trailing commas not enforced in this codebase -->
<NoWarn>$(NoWarn);S107;S1144;S1944;S2053;S2245;S2259;S2583;S2589;S3329;S3604;S3655;S3776;S3949;S3966;S4158;S4347;S5773;S6781;MA0007;MA0048;MA0051;MA0076;RCS1123;RCS1159;IDE0007</NoWarn>
</PropertyGroup>
<!-- Code Coverage Configuration (for NDepend integration) -->
@@ -74,6 +78,16 @@
<Exclude>[xunit.*]*,[*.Tests]*</Exclude>
</PropertyGroup>
<!-- SonarLint local rule configuration -->
<PropertyGroup>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory).sonarlint\csharp.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="$(MSBuildThisFileDirectory).sonarlint\CSharp\SonarLint.xml">
<Link>Properties\SonarLint.xml</Link>
</AdditionalFiles>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
<PackageReference Include="GitVersion.MsBuild" Version="6.5.1">
+1 -1
View File
@@ -289,7 +289,7 @@ public sealed class Maenv : ITValuePublisher
private double CalculateEMA(double value, bool isNew)
{
// Use EmaWeight==0 to detect first value for correct isNew=false behavior
if (_state.EmaWeight == 0)
if (Math.Abs(_state.EmaWeight) < double.Epsilon)
{
// First value - persist only for isNew=true
if (isNew)
+3
View File
@@ -75,6 +75,9 @@ public sealed class Sdchannel : ITValuePublisher
public event TValuePublishedHandler? Pub;
/// <summary>
/// Initializes a new Standard Deviation Channel indicator with specified period and multiplier.
/// </summary>
/// <param name="period">Lookback period for regression (default 20, must be > 1)</param>
/// <param name="multiplier">Standard deviation multiplier for bands (default 2.0, must be > 0)</param>
public Sdchannel(int period = 20, double multiplier = 2.0)
+1 -3
View File
@@ -1,6 +1,5 @@
using System;
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -121,7 +120,6 @@ public sealed class Stc : AbstractBase
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[SuppressMessage("SonarQube", "S3776:Cognitive Complexity", Justification = "High-performance SIMD code with intentionally optimized control flow")]
private static void UpdateMinMax(double added, double removed, bool hasRemoved, RingBuffer buf, ref double min, ref double max)
{
if (double.IsNaN(added)) return;
@@ -559,4 +557,4 @@ public sealed class Stc : AbstractBase
ArrayPool<double>.Shared.Return(rentedStoch1);
}
}
}
}
+6
View File
@@ -27,6 +27,9 @@ public sealed class Roc : AbstractBase
public override bool IsHot => _buffer.Count > _period;
/// <summary>
/// Initializes a new Rate of Change indicator with specified lookback period.
/// </summary>
/// <param name="period">Lookback period (must be >= 1)</param>
public Roc(int period = 9)
{
@@ -39,6 +42,9 @@ public sealed class Roc : AbstractBase
WarmupPeriod = period + 1;
}
/// <summary>
/// Initializes a new Rate of Change indicator with source for event-based chaining.
/// </summary>
/// <param name="source">Source indicator for chaining</param>
/// <param name="period">Lookback period</param>
public Roc(ITValuePublisher source, int period = 9) : this(period)
+8 -2
View File
@@ -27,6 +27,9 @@ public sealed class Change : AbstractBase
public override bool IsHot => _buffer.Count > _period;
/// <summary>
///
/// </summary>
/// <param name="period">Lookback period (must be >= 1)</param>
public Change(int period = 1)
{
@@ -39,6 +42,9 @@ public sealed class Change : AbstractBase
WarmupPeriod = period + 1;
}
/// <summary>
///
/// </summary>
/// <param name="source">Source indicator for chaining</param>
/// <param name="period">Lookback period</param>
public Change(ITValuePublisher source, int period = 1) : this(period)
@@ -99,7 +105,7 @@ public sealed class Change : AbstractBase
for (int i = 0; i < source.Length; i++)
{
Update(new TValue(time, source[i]), true);
Update(new TValue(time, value: source[i]), isNew: true);
time += interval;
}
}
@@ -189,4 +195,4 @@ public sealed class Change : AbstractBase
_p_state = default;
Last = default;
}
}
}
+4 -1
View File
@@ -30,6 +30,9 @@ public sealed class Exptrans : AbstractBase
WarmupPeriod = 0;
}
/// <summary>
/// Creates Exptrans with source for event-based chaining.
/// </summary>
/// <param name="source">Source indicator for chaining</param>
public Exptrans(ITValuePublisher source) : this()
{
@@ -146,4 +149,4 @@ public sealed class Exptrans : AbstractBase
_p_state = new(1.0);
Last = default;
}
}
}
+7 -1
View File
@@ -26,6 +26,9 @@ public sealed class Highest : AbstractBase
public override bool IsHot => _buffer.Count >= _period;
/// <summary>
/// Initializes a new Highest indicator with specified lookback period.
/// </summary>
/// <param name="period">Lookback window size (must be >= 1)</param>
public Highest(int period)
{
@@ -38,6 +41,9 @@ public sealed class Highest : AbstractBase
WarmupPeriod = period;
}
/// <summary>
/// Initializes a new Highest indicator with source for event-based chaining.
/// </summary>
/// <param name="source">Source indicator for chaining</param>
/// <param name="period">Lookback window size</param>
public Highest(ITValuePublisher source, int period) : this(period)
@@ -89,7 +95,7 @@ public sealed class Highest : AbstractBase
for (int i = 0; i < source.Length; i++)
{
Update(new TValue(time, source[i]), true);
Update(input: new TValue(time, source[i]), isNew: true);
time += interval;
}
}
+3
View File
@@ -48,6 +48,9 @@ public sealed class Lineartrans : AbstractBase
WarmupPeriod = 0;
}
/// <summary>
/// Creates a Linear transformer with source for event-based chaining.
/// </summary>
/// <param name="source">Source indicator for chaining</param>
/// <param name="slope">Multiplicative factor (default: 1.0)</param>
/// <param name="intercept">Additive constant (default: 0.0)</param>
+4 -1
View File
@@ -30,6 +30,9 @@ public sealed class Logtrans : AbstractBase
WarmupPeriod = 0;
}
/// <summary>
/// Creates Logtrans with source for event-based chaining.
/// </summary>
/// <param name="source">Source indicator for chaining</param>
public Logtrans(ITValuePublisher source) : this()
{
@@ -132,4 +135,4 @@ public sealed class Logtrans : AbstractBase
_p_state = default;
Last = default;
}
}
}
+7 -1
View File
@@ -26,6 +26,9 @@ public sealed class Lowest : AbstractBase
public override bool IsHot => _buffer.Count >= _period;
/// <summary>
/// Initializes a new Lowest indicator with specified lookback period.
/// </summary>
/// <param name="period">Lookback window size (must be >= 1)</param>
public Lowest(int period)
{
@@ -38,6 +41,9 @@ public sealed class Lowest : AbstractBase
WarmupPeriod = period;
}
/// <summary>
/// Initializes a new Lowest indicator with source for event-based chaining.
/// </summary>
/// <param name="source">Source indicator for chaining</param>
/// <param name="period">Lookback window size</param>
public Lowest(ITValuePublisher source, int period) : this(period)
@@ -192,4 +198,4 @@ public sealed class Lowest : AbstractBase
_p_state = default;
Last = default;
}
}
}
+7 -1
View File
@@ -26,6 +26,9 @@ public sealed class Midpoint : AbstractBase
public override bool IsHot => _highest.IsHot && _lowest.IsHot;
/// <summary>
/// Initializes a new Midpoint indicator with specified lookback period.
/// </summary>
/// <param name="period">Lookback window size (must be >= 1)</param>
public Midpoint(int period)
{
@@ -38,6 +41,9 @@ public sealed class Midpoint : AbstractBase
WarmupPeriod = period;
}
/// <summary>
/// Initializes a new Midpoint indicator with source for event-based chaining.
/// </summary>
/// <param name="source">Source indicator for chaining</param>
/// <param name="period">Lookback window size</param>
public Midpoint(ITValuePublisher source, int period) : this(period)
@@ -157,4 +163,4 @@ public sealed class Midpoint : AbstractBase
_lowest.Reset();
Last = default;
}
}
}
+6
View File
@@ -30,6 +30,9 @@ public sealed class Normalize : AbstractBase
public override bool IsHot => _buffer.Count >= _period;
/// <summary>
/// Initializes a new Normalize indicator with specified lookback period.
/// </summary>
/// <param name="period">Lookback period for min/max calculation (default 14)</param>
public Normalize(int period = 14)
{
@@ -44,6 +47,9 @@ public sealed class Normalize : AbstractBase
_p_state = _state;
}
/// <summary>
/// Initializes a new Normalize indicator with source for event-based chaining.
/// </summary>
/// <param name="source">Source indicator for chaining</param>
/// <param name="period">Lookback period (default 14)</param>
public Normalize(ITValuePublisher source, int period = 14) : this(period)
+3
View File
@@ -34,6 +34,9 @@ public sealed class Relu : AbstractBase
WarmupPeriod = 0;
}
/// <summary>
///
/// </summary>
/// <param name="source">Source indicator for chaining</param>
public Relu(ITValuePublisher source) : this()
{
+6
View File
@@ -30,6 +30,9 @@ public sealed class Sigmoid : AbstractBase
public override bool IsHot => true; // No warmup needed
/// <summary>
/// Initializes a new Sigmoid indicator with specified steepness and midpoint.
/// </summary>
/// <param name="k">Steepness factor (default 1.0). Higher values create steeper transitions.</param>
/// <param name="x0">Midpoint value where output equals 0.5 (default 0.0).</param>
public Sigmoid(double k = 1.0, double x0 = 0.0)
@@ -43,6 +46,9 @@ public sealed class Sigmoid : AbstractBase
WarmupPeriod = 0;
}
/// <summary>
/// Initializes a new Sigmoid indicator with source for event-based chaining.
/// </summary>
/// <param name="source">Source indicator for chaining</param>
/// <param name="k">Steepness factor (default 1.0)</param>
/// <param name="x0">Midpoint value (default 0.0)</param>
+4 -1
View File
@@ -30,6 +30,9 @@ public sealed class Sqrttrans : AbstractBase
WarmupPeriod = 0;
}
/// <summary>
/// Creates a Square Root transformer with source for event-based chaining.
/// </summary>
/// <param name="source">Source indicator for chaining</param>
public Sqrttrans(ITValuePublisher source) : this()
{
@@ -131,4 +134,4 @@ public sealed class Sqrttrans : AbstractBase
_p_state = default;
Last = default;
}
}
}
+4 -1
View File
@@ -75,6 +75,9 @@ public sealed class Hamma : AbstractBase
_state.LastValidValue = double.NaN;
}
/// <summary>
/// Creates HAMMA with source for event-based chaining.
/// </summary>
/// <param name="source">Data source for event-based updates</param>
/// <param name="period">Lookback period for the Hamming window (default: 10)</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -391,4 +394,4 @@ public sealed class Hamma : AbstractBase
_p_state = _state;
Last = default;
}
}
}
+3
View File
@@ -70,6 +70,9 @@ public sealed class Hanma : AbstractBase
_state = new State(double.NaN, IsInitialized: false);
}
/// <summary>
///
/// </summary>
/// <param name="source">Data source for event-based updates</param>
/// <param name="period">Lookback period for the Hanning window (default: 10)</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+4 -1
View File
@@ -109,6 +109,9 @@ public sealed class Hwma : AbstractBase
_state = new State(double.NaN, 0, 0, double.NaN, IsInitialized: false);
}
/// <summary>
/// Creates HWMA with source for event-based chaining.
/// </summary>
/// <param name="source">Data source for event-based updates</param>
/// <param name="period">Period for smoothing factor calculation (default: 10)</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -339,4 +342,4 @@ public sealed class Hwma : AbstractBase
_p_state = _state;
Last = default;
}
}
}
+9 -9
View File
@@ -101,7 +101,7 @@ public sealed class Sgma : AbstractBase
weights[3] = 0.3429;
weights[4] = -0.0857;
double sum5 = weights[0] + weights[1] + weights[2] + weights[3] + weights[4];
invWeightSum = sum5 != 0.0 ? 1.0 / sum5 : 0.0;
invWeightSum = Math.Abs(sum5) > double.Epsilon ? 1.0 / sum5 : 0.0;
return;
}
@@ -116,7 +116,7 @@ public sealed class Sgma : AbstractBase
weights[6] = -0.0476;
double sum7 = 0.0;
for (int i = 0; i < 7; i++) sum7 += weights[i];
invWeightSum = sum7 != 0.0 ? 1.0 / sum7 : 0.0;
invWeightSum = Math.Abs(sum7) > double.Epsilon ? 1.0 / sum7 : 0.0;
return;
}
@@ -133,7 +133,7 @@ public sealed class Sgma : AbstractBase
weights[8] = -0.0281;
double sum9 = 0.0;
for (int i = 0; i < 9; i++) sum9 += weights[i];
invWeightSum = sum9 != 0.0 ? 1.0 / sum9 : 0.0;
invWeightSum = Math.Abs(sum9) > double.Epsilon ? 1.0 / sum9 : 0.0;
return;
}
}
@@ -376,7 +376,7 @@ public sealed class Sgma : AbstractBase
continue;
}
if (invWeightSum == 0.0)
if (Math.Abs(invWeightSum) < double.Epsilon)
{
output[i] = val;
continue;
@@ -399,7 +399,7 @@ public sealed class Sgma : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static double CalculateWeightedSumFull(RingBuffer buffer, double[] weights, double invWeightSum, double fallbackValue)
{
if (invWeightSum == 0.0)
if (Math.Abs(invWeightSum) < double.Epsilon)
return fallbackValue;
ReadOnlySpan<double> internalBuf = buffer.InternalBuffer;
@@ -435,7 +435,7 @@ public sealed class Sgma : AbstractBase
Math.FusedMultiplyAdd(window[3], w3, window[4] * w4))));
double weightSum = w0 + w1 + w2 + w3 + w4;
return weightSum != 0.0 ? sum / weightSum : fallbackValue;
return Math.Abs(weightSum) > double.Epsilon ? sum / weightSum : fallbackValue;
}
if (p == 7)
@@ -458,7 +458,7 @@ public sealed class Sgma : AbstractBase
sum = Math.FusedMultiplyAdd(window[6], w6, sum);
double weightSum = w0 + w1 + w2 + w3 + w4 + w5 + w6;
return weightSum != 0.0 ? sum / weightSum : fallbackValue;
return Math.Abs(weightSum) > double.Epsilon ? sum / weightSum : fallbackValue;
}
if (p == 9)
@@ -485,7 +485,7 @@ public sealed class Sgma : AbstractBase
sum = Math.FusedMultiplyAdd(window[8], w8, sum);
double weightSum = w0 + w1 + w2 + w3 + w4 + w5 + w6 + w7 + w8;
return weightSum != 0.0 ? sum / weightSum : fallbackValue;
return Math.Abs(weightSum) > double.Epsilon ? sum / weightSum : fallbackValue;
}
}
@@ -522,4 +522,4 @@ public sealed class Sgma : AbstractBase
_p_lastValidValue = double.NaN;
Last = default;
}
}
}
-1
View File
@@ -189,7 +189,6 @@ public sealed class Sma : AbstractBase
double val = GetValidValue(input.Value);
UpdateState(val);
}
else
{
+6 -6
View File
@@ -39,8 +39,8 @@ public sealed class Vama : AbstractBase
{
public static VamaState New() => new()
{
ShortAtr = new RmaState(0, 1.0, false),
LongAtr = new RmaState(0, 1.0, false),
ShortAtr = new RmaState(Ema: 0, E: 1.0, IsCompensated: false),
LongAtr = new RmaState(Ema: 0, E: 1.0, IsCompensated: false),
PrevClose = double.NaN,
BufferHead = 0,
BufferSum = 0,
@@ -244,7 +244,7 @@ public sealed class Vama : AbstractBase
newHead,
bufferSum,
validCount,
true);
IsInitialized: true);
Last = new TValue(input.Time, result);
PubEvent(Last, isNew);
@@ -285,7 +285,7 @@ public sealed class Vama : AbstractBase
for (int i = 0; i < len; i++)
{
var bar = source[i];
var result = Update(bar, true);
var result = Update(bar, isNew: true);
tSpan[i] = bar.Time;
vSpan[i] = result.Value;
}
@@ -314,7 +314,7 @@ public sealed class Vama : AbstractBase
for (int i = 0; i < len; i++)
{
var result = Update(new TValue(sourceTimes[i], sourceValues[i]), true);
var result = Update(new TValue(sourceTimes[i], sourceValues[i]), isNew: true);
tSpan[i] = sourceTimes[i];
vSpan[i] = result.Value;
}
@@ -330,7 +330,7 @@ public sealed class Vama : AbstractBase
Reset();
foreach (double val in source)
{
Update(new TValue(DateTime.MinValue, val), true);
Update(new TValue(DateTime.MinValue, val), isNew: true);
}
}
+7 -7
View File
@@ -37,8 +37,8 @@ public sealed class Yzvama : AbstractBase
{
public static YzvamaState New() => new()
{
ShortVar = new RmaState(0, 1.0, false),
LongVar = new RmaState(0, 1.0, false),
ShortVar = new RmaState(Ema: 0, E: 1.0, IsCompensated: false),
LongVar = new RmaState(Ema: 0, E: 1.0, IsCompensated: false),
PrevClose = double.NaN,
SourceHead = 0,
SourceSum = 0,
@@ -390,7 +390,7 @@ public sealed class Yzvama : AbstractBase
validCount,
yzvHead,
yzvCount,
true);
IsInitialized: true);
Last = new TValue(input.Time, result);
PubEvent(Last, isNew);
@@ -428,7 +428,7 @@ public sealed class Yzvama : AbstractBase
for (int i = 0; i < len; i++)
{
var bar = source[i];
var result = Update(bar, true);
var result = Update(bar, isNew: true);
tSpan[i] = bar.Time;
vSpan[i] = result.Value;
}
@@ -457,7 +457,7 @@ public sealed class Yzvama : AbstractBase
for (int i = 0; i < len; i++)
{
var result = Update(new TValue(sourceTimes[i], sourceValues[i]), true);
var result = Update(new TValue(sourceTimes[i], sourceValues[i]), isNew: true);
tSpan[i] = sourceTimes[i];
vSpan[i] = result.Value;
}
@@ -473,7 +473,7 @@ public sealed class Yzvama : AbstractBase
{
Reset();
foreach (double val in source)
Update(new TValue(DateTime.MinValue, val), true);
Update(new TValue(DateTime.MinValue, val), isNew: true);
}
/// <summary>
@@ -484,7 +484,7 @@ public sealed class Yzvama : AbstractBase
{
Reset();
foreach (TValue tv in source)
Update(tv, true);
Update(tv, isNew: true);
}
/// <summary>
@@ -7,7 +7,7 @@ public class ZlemaValidationTests
[Fact]
public void Zlema_Streaming_MatchesReference()
{
int period = 20;
const int period = 20;
TSeries series = BuildSeries(300, seed: 5);
double[] reference = new double[series.Count];
@@ -24,7 +24,7 @@ public class ZlemaValidationTests
[Fact]
public void Zlema_Batch_MatchesReference()
{
int period = 14;
const int period = 14;
TSeries series = BuildSeries(250, seed: 9);
double[] reference = new double[series.Count];
@@ -40,7 +40,7 @@ public class ZlemaValidationTests
[Fact]
public void Zlema_Span_MatchesReference()
{
int period = 30;
const int period = 30;
TSeries series = BuildSeries(200, seed: 12);
double[] values = series.Values.ToArray();
var output = new double[values.Length];
+3 -3
View File
@@ -54,7 +54,7 @@ public class AtrnTests
public void Update_ReturnsValidTValue()
{
var atrn = new Atrn(DefaultPeriod);
var result = atrn.Update(_bars[0], true);
var result = atrn.Update(_bars[0], isNew: true);
Assert.IsType<TValue>(result);
Assert.Equal(_bars[0].Time, result.Time);
@@ -67,7 +67,7 @@ public class AtrnTests
for (int i = 0; i < _bars.Count; i++)
{
var result = atrn.Update(_bars[i], true);
var result = atrn.Update(_bars[i], isNew: true);
Assert.True(result.Value >= 0 && result.Value <= 1,
$"Value {result.Value} at index {i} is outside [0,1] range");
}
@@ -447,4 +447,4 @@ public class AtrnTests
}
#endregion
}
}
+6 -6
View File
@@ -51,7 +51,7 @@ public sealed class Atrn : AbstractBase
Name = $"Atrn({period})";
WarmupPeriod = _rma.WarmupPeriod + _lookbackWindow;
_state = new State(default, false, 0.0, 0.0);
_state = new State(PrevBar: default, IsInitialized: false, LastValidTr: 0.0, LastValidAtr: 0.0);
_p_state = _state;
}
@@ -95,7 +95,7 @@ public sealed class Atrn : AbstractBase
for (int i = 0; i < source.Length; i++)
{
double tr = source[i];
TValue atr = _rma.Update(new TValue(DateTime.UtcNow.AddMinutes(i), tr), true);
TValue atr = _rma.Update(new TValue(DateTime.UtcNow.AddMinutes(i), tr), isNew: true);
_atrBuffer.Add(atr.Value);
}
@@ -117,7 +117,7 @@ public sealed class Atrn : AbstractBase
{
_rma.Reset();
_atrBuffer.Clear();
_state = new State(default, false, 0.0, 0.0);
_state = new State(PrevBar: default, IsInitialized: false, LastValidTr: 0.0, LastValidAtr: 0.0);
_p_state = _state;
Last = default;
}
@@ -180,7 +180,7 @@ public sealed class Atrn : AbstractBase
// Update state
_state = isNew
? new State(input, true, tr, currentAtr)
? new State(PrevBar: input, IsInitialized: true, LastValidTr: tr, LastValidAtr: currentAtr)
: _state with { LastValidTr = tr, LastValidAtr = currentAtr };
TValue result = new(input.Time, normalized);
@@ -246,7 +246,7 @@ public sealed class Atrn : AbstractBase
for (int i = 0; i < source.Count; i++)
{
TValue result = Update(source[i], true);
TValue result = Update(source[i], isNew: true);
t.Add(result.Time);
v.Add(result.Value);
}
@@ -266,7 +266,7 @@ public sealed class Atrn : AbstractBase
for (int i = 0; i < source.Count; i++)
{
TValue result = Update(source[i], true);
TValue result = Update(source[i], isNew: true);
t.Add(source[i].Time);
v.Add(result.Value);
}