mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-07-27 17:27:43 +00:00
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:
@@ -0,0 +1,7 @@
|
||||
name: mihakralj/quantalib-csharp
|
||||
version: 0.0.0
|
||||
library: true
|
||||
extensionTargets:
|
||||
codeql/csharp-all: '*'
|
||||
dataExtensions:
|
||||
- models/**/*.yml
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Vendored
-73
@@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+15
-57
@@ -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
@@ -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">
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,6 @@ public sealed class Sma : AbstractBase
|
||||
|
||||
double val = GetValidValue(input.Value);
|
||||
UpdateState(val);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user