test: setup common stability and robustness properties tracking

This commit is contained in:
Miha Kralj
2026-02-27 12:50:05 -08:00
parent 4ab3a7fb53
commit 769a923a24
287 changed files with 1316 additions and 869 deletions
+1 -14
View File
@@ -2,20 +2,7 @@
name: beast
description: Meticulous auto-agent for high-performance .NET library development with full MCP tool integration
tools:
- execute
- read
- edit
- search
- web
- codacy-mcp-server/*
- gitkraken/*
- github/*
- qdrant-mcp/*
- sequential-thinking-mcp/*
- tavily-mcp/*
- wolfram-mcp/*
- agent
- todo
[execute/runNotebookCell, execute/testFailure, execute/getTerminalOutput, execute/awaitTerminal, execute/killTerminal, execute/runTask, execute/createAndRunTask, execute/runInTerminal, execute/runTests, read/getNotebookSummary, read/problems, read/readFile, read/readNotebookCellOutput, read/terminalSelection, read/terminalLastCommand, read/getTaskOutput, agent/runSubagent, edit/createDirectory, edit/createFile, edit/createJupyterNotebook, edit/editFiles, edit/editNotebook, search/changes, search/codebase, search/fileSearch, search/listDirectory, search/searchResults, search/textSearch, search/usages, web/fetch, github/add_comment_to_pending_review, github/add_issue_comment, github/assign_copilot_to_issue, github/create_branch, github/create_or_update_file, github/create_pull_request, github/create_repository, github/delete_file, github/fork_repository, github/get_commit, github/get_file_contents, github/get_label, github/get_latest_release, github/get_me, github/get_release_by_tag, github/get_tag, github/get_team_members, github/get_teams, github/issue_read, github/issue_write, github/list_branches, github/list_commits, github/list_issue_types, github/list_issues, github/list_pull_requests, github/list_releases, github/list_tags, github/merge_pull_request, github/pull_request_read, github/pull_request_review_write, github/push_files, github/request_copilot_review, github/search_code, github/search_issues, github/search_pull_requests, github/search_repositories, github/search_users, github/sub_issue_write, github/update_pull_request, github/update_pull_request_branch, codacy-mcp-server/codacy_cli_analyze, codacy-mcp-server/codacy_cli_install, codacy-mcp-server/codacy_get_file_clones, codacy-mcp-server/codacy_get_file_coverage, codacy-mcp-server/codacy_get_file_issues, codacy-mcp-server/codacy_get_file_with_analysis, codacy-mcp-server/codacy_get_issue, codacy-mcp-server/codacy_get_pattern, codacy-mcp-server/codacy_get_pull_request_files_coverage, codacy-mcp-server/codacy_get_pull_request_git_diff, codacy-mcp-server/codacy_get_repository_pull_request, codacy-mcp-server/codacy_get_repository_with_analysis, codacy-mcp-server/codacy_list_files, codacy-mcp-server/codacy_list_organization_repositories, codacy-mcp-server/codacy_list_organizations, codacy-mcp-server/codacy_list_pull_request_issues, codacy-mcp-server/codacy_list_repository_issues, codacy-mcp-server/codacy_list_repository_pull_requests, codacy-mcp-server/codacy_list_repository_tool_patterns, codacy-mcp-server/codacy_list_repository_tools, codacy-mcp-server/codacy_list_tools, codacy-mcp-server/codacy_search_organization_srm_items, codacy-mcp-server/codacy_search_repository_srm_items, codacy-mcp-server/codacy_setup_repository, dotnet-semantic-mcp/ast_diff_unified, dotnet-semantic-mcp/attrs, dotnet-semantic-mcp/code_security, dotnet-semantic-mcp/deps, dotnet-semantic-mcp/diag, dotnet-semantic-mcp/diff, dotnet-semantic-mcp/explore, dotnet-semantic-mcp/hierarchy, dotnet-semantic-mcp/map, dotnet-semantic-mcp/metrics, dotnet-semantic-mcp/nuget_vulnerabilities, dotnet-semantic-mcp/prepare_change, dotnet-semantic-mcp/refs, dotnet-semantic-mcp/scan_cancel, dotnet-semantic-mcp/scan_list, dotnet-semantic-mcp/scan_status, dotnet-semantic-mcp/search, dotnet-semantic-mcp/source, dotnet-semantic-mcp/symbol, dotnet-semantic-mcp/understand, gitkraken/git_add_or_commit, gitkraken/git_blame, gitkraken/git_branch, gitkraken/git_checkout, gitkraken/git_log_or_diff, gitkraken/git_push, gitkraken/git_stash, gitkraken/git_status, gitkraken/git_worktree, gitkraken/gitkraken_workspace_list, gitkraken/issues_add_comment, gitkraken/issues_assigned_to_me, gitkraken/issues_get_detail, gitkraken/pull_request_assigned_to_me, gitkraken/pull_request_create, gitkraken/pull_request_create_review, gitkraken/pull_request_get_comments, gitkraken/pull_request_get_detail, gitkraken/repository_get_file_content, qdrant/qdrant-find, qdrant/qdrant-store, ref/ref_read_url, ref/ref_search_documentation, sequential-thinking/sequentialthinking, tavily/tavily_crawl, tavily/tavily_extract, tavily/tavily_map, tavily/tavily_research, tavily/tavily_search, todo]
---
# Role: Meticulous Auto-Agent
+10 -18
View File
@@ -41,36 +41,28 @@ jobs:
**/*.sln
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v4.2.0
uses: gittools/actions/gitversion/setup@d0139503a9321f76b4a417dfdc8aebcec24decdd # v4.2.0
with:
versionSpec: "6.x"
includePrerelease: true
- name: Determine Version
id: gitversion
uses: gittools/actions/gitversion/execute@v4.2.0
uses: gittools/actions/gitversion/execute@d0139503a9321f76b4a417dfdc8aebcec24decdd # v4.2.0
with:
updateAssemblyInfo: false
- name: Restore dependencies
run: dotnet restore
- name: Build
run: |
dotnet build ./lib/quantalib.csproj --configuration Release --no-restore --nologo
dotnet build ./quantower/Channels.csproj --configuration Release --nologo
dotnet build ./quantower/Cycles.csproj --configuration Release --nologo
dotnet build ./quantower/Dynamics.csproj --configuration Release --nologo
dotnet build ./quantower/Filters.csproj --configuration Release --nologo
dotnet build ./quantower/Forecasts.csproj --configuration Release --nologo
dotnet build ./quantower/Momentum.csproj --configuration Release --nologo
dotnet build ./quantower/Oscillators.csproj --configuration Release --nologo
dotnet build ./quantower/Reversals.csproj --configuration Release --nologo
dotnet build ./quantower/Statistics.csproj --configuration Release --nologo
dotnet build ./quantower/Trends_FIR.csproj --configuration Release --nologo
dotnet build ./quantower/Trends_IIR.csproj --configuration Release --nologo
dotnet build ./quantower/Volatility.csproj --configuration Release --nologo
dotnet build ./quantower/Volume.csproj --configuration Release --nologo
- name: Build (warnings as errors)
run: dotnet build ./QuanTAlib.slnx --configuration Release --no-restore --nologo /warnaserror
- name: Test (core)
run: dotnet test ./lib/QuanTAlib.Tests.csproj --configuration Release --no-build --nologo
- name: Test (quantower)
run: dotnet test ./quantower/Quantower.Tests.csproj --configuration Release --no-build --nologo
- name: Pack NuGet
run: |
+1
View File
@@ -12,6 +12,7 @@
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="FsCheck.Xunit" Version="3.3.2" />
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="OoplesFinance.StockIndicators" Version="1.1.1" />
@@ -387,7 +387,7 @@ public sealed class AccBandsValidationTests : IDisposable
out var outRange,
period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.AccbandsLookback(period);
@@ -432,7 +432,7 @@ public sealed class AccBandsValidationTests : IDisposable
out var outRange,
period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.AccbandsLookback(period);
@@ -466,7 +466,7 @@ public sealed class AccBandsValidationTests : IDisposable
out var outRange,
period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
var (qMiddle, qUpper, qLower) = AccBands.Batch(_testData.Bars, period, 4.0);
@@ -180,7 +180,7 @@ public sealed class AtrBandsValidationTests : IDisposable
out var outRange,
period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.SmaLookback(period);
@@ -215,7 +215,7 @@ public sealed class AtrBandsValidationTests : IDisposable
out var outRange,
period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.AtrLookback(period);
var (offset, _) = outRange.GetOffsetAndLength(atrOutput.Length);
@@ -140,9 +140,9 @@ public sealed class BbandsValidationTests : IDisposable
period,
multiplier,
multiplier,
Core.MAType.Sma);
TALib.Core.MAType.Sma);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.BbandsLookback(period);
@@ -185,9 +185,9 @@ public sealed class BbandsValidationTests : IDisposable
period,
multiplier,
multiplier,
Core.MAType.Sma);
TALib.Core.MAType.Sma);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.BbandsLookback(period);
@@ -228,9 +228,9 @@ public sealed class BbandsValidationTests : IDisposable
period,
multiplier,
multiplier,
Core.MAType.Sma);
TALib.Core.MAType.Sma);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.BbandsLookback(period);
@@ -347,4 +347,4 @@ public sealed class DchannelValidationTests : IDisposable
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
+1 -1
View File
@@ -392,4 +392,4 @@ public sealed class FcbValidationTests : IDisposable
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
@@ -556,4 +556,4 @@ public sealed class KchannelValidationTests : IDisposable
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
+4 -4
View File
@@ -12,7 +12,7 @@ public class MaenvIndicatorTests
Assert.Equal(20, ind.Period);
Assert.Equal(1.0, ind.Percentage);
Assert.Equal(MaenvType.EMA, ind.MaType);
Assert.Equal(MaenvType.EMA, ind.maType);
Assert.Equal(PriceType.Close, ind.SourceType);
Assert.True(ind.ShowColdValues);
Assert.Equal("Maenv - Moving Average Envelope", ind.Name);
@@ -30,7 +30,7 @@ public class MaenvIndicatorTests
[Fact]
public void ShortName_ReflectsParameters()
{
var ind = new MaenvIndicator { Period = 12, Percentage = 2.5, MaType = MaenvType.SMA };
var ind = new MaenvIndicator { Period = 12, Percentage = 2.5, maType = MaenvType.SMA };
Assert.Contains("12", ind.ShortName, StringComparison.Ordinal);
Assert.Contains("2.5", ind.ShortName, StringComparison.Ordinal);
Assert.Contains("SMA", ind.ShortName, StringComparison.Ordinal);
@@ -209,11 +209,11 @@ public class MaenvIndicatorTests
}
[Fact]
public void AllMaTypes_ProduceFiniteResults()
public void AllmaTypes_ProduceFiniteResults()
{
foreach (MaenvType maType in Enum.GetValues<MaenvType>())
{
var ind = new MaenvIndicator { Period = 10, Percentage = 2.0, MaType = maType };
var ind = new MaenvIndicator { Period = 10, Percentage = 2.0, maType = maType };
ind.Initialize();
var now = DateTime.UtcNow;
+1 -1
View File
@@ -347,7 +347,7 @@ public class MaenvTests
}
[Fact]
public void Maenv_AllMaTypes_ProduceFiniteResults()
public void Maenv_AllmaTypes_ProduceFiniteResults()
{
var gbm = new GBM(startPrice: 100, mu: 0.01, sigma: 0.1, seed: 42);
+2 -2
View File
@@ -440,7 +440,7 @@ public sealed class MaenvValidationTests : IDisposable
}
[Fact]
public void Validate_MaTypesDifferent()
public void Validate_maTypesDifferent()
{
// Different MA types should produce different results (except for first bar)
var indSma = new Maenv(10, 2.0, MaenvType.SMA);
@@ -595,4 +595,4 @@ public sealed class MaenvValidationTests : IDisposable
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
@@ -559,7 +559,7 @@ public sealed class RegchannelValidationTests : IDisposable
out var outRange,
period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.LinearRegLookback(period);
@@ -593,7 +593,7 @@ public sealed class RegchannelValidationTests : IDisposable
out var outRange,
period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.LinearRegSlopeLookback(period);
@@ -650,4 +650,4 @@ public sealed class SdchannelValidationTests : IDisposable
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
@@ -680,4 +680,4 @@ public sealed class StarchannelValidationTests : IDisposable
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
@@ -671,7 +671,7 @@ public sealed class TtmLrcValidationTests : IDisposable
out var outRange,
period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.LinearRegLookback(period);
@@ -704,7 +704,7 @@ public sealed class TtmLrcValidationTests : IDisposable
out var outRange,
period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.LinearRegSlopeLookback(period);
var (offset, _) = outRange.GetOffsetAndLength(slopeOutput.Length);
+12 -6
View File
@@ -19,15 +19,21 @@ public delegate void BiInputBatchDelegate(
int period);
/// <summary>
/// Abstract base class for bi-input indicators (indicators that require two inputs like error metrics).
/// Abstract base class for error-metric indicators that compare two input series (actual vs predicted).
/// Provides common infrastructure for RingBuffer-based sliding window calculations with O(1) updates.
/// </summary>
/// <remarks>
/// This base class eliminates code duplication across error indicators (MAE, MSE, RMSE, MAPE, etc.)
/// by providing:
/// - Common state management with bar correction (isNew semantics)
/// - RingBuffer-based sliding window with running sum
/// - Periodic resync for floating-point drift correction
/// This base class is designed specifically for error metrics (MAE, MSE, RMSE, MAPE, SMAPE, etc.)
/// where each bar contributes a single scalar error value to a running mean.
///
/// It is NOT intended for statistical bi-input indicators (Correlation, Cointegration) which
/// maintain multiple running sums (Σx, Σy, Σx², Σy², Σxy) and have different state-restoration
/// semantics — those indicators manage their own state directly.
///
/// Infrastructure provided:
/// - _p_state / _buffer.Snapshot() / _buffer.Restore() for bar correction (isNew semantics)
/// - RingBuffer-based sliding window with a single running sum
/// - Periodic resync every 1000 updates for floating-point drift correction
/// - NaN/Infinity handling with last-valid-value substitution
/// - Template Method pattern: subclasses only implement ComputeError and optionally PostProcess
/// </remarks>
@@ -49,7 +49,7 @@ public sealed class AvgpriceValidationTests : IDisposable
var taOut = new double[open.Length];
var retCode = Functions.AvgPrice(open.AsSpan(), high.AsSpan(), low.AsSpan(), close.AsSpan(),
0..^0, taOut, out var outRange);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
var (offset, length) = outRange.GetOffsetAndLength(taOut.Length);
// QuanTAlib batch span
@@ -46,7 +46,7 @@ public sealed class MedpriceValidationTests : IDisposable
// TA-Lib MedPrice
var taOut = new double[high.Length];
var retCode = Functions.MedPrice(high.AsSpan(), low.AsSpan(), 0..^0, taOut, out var outRange);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
var (offset, length) = outRange.GetOffsetAndLength(taOut.Length);
// QuanTAlib batch via TBarSeries
@@ -51,7 +51,7 @@ public sealed class MidpointValidationTests : IDisposable
// Calculate TA-Lib MIDPOINT
var retCode = TALib.Functions.MidPoint<double>(tData, 0..^0, output, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.MidPointLookback(period);
@@ -81,7 +81,7 @@ public sealed class MidpointValidationTests : IDisposable
// Calculate TA-Lib MIDPOINT
var retCode = TALib.Functions.MidPoint<double>(tData, 0..^0, output, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.MidPointLookback(period);
@@ -107,7 +107,7 @@ public sealed class MidpointValidationTests : IDisposable
// Calculate TA-Lib MIDPOINT
var retCode = TALib.Functions.MidPoint<double>(sourceData, 0..^0, talibOutput, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.MidPointLookback(period);
@@ -47,7 +47,7 @@ public sealed class MidpriceValidationTests : IDisposable
// TA-Lib MidPrice
var taOut = new double[high.Length];
var retCode = Functions.MidPrice(high.AsSpan(), low.AsSpan(), 0..^0, taOut, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
var (offset, length) = outRange.GetOffsetAndLength(taOut.Length);
// QuanTAlib batch span
@@ -76,7 +76,7 @@ public sealed class MidpriceValidationTests : IDisposable
var taOut = new double[high.Length];
var retCode = Functions.MidPrice(high.AsSpan(), low.AsSpan(), 0..^0, taOut, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
var (offset, length) = outRange.GetOffsetAndLength(taOut.Length);
var qlOut = new double[high.Length];
+35 -9
View File
@@ -91,25 +91,51 @@ public sealed class RingBuffer : IEnumerable<double>
}
/// <summary>
/// Recalculates the sum by iterating over all elements.
/// Recalculates the sum by iterating over all elements using SIMD acceleration.
/// Useful for correcting floating-point drift after many updates.
/// Uses GetSequencedSpans to avoid allocation when buffer wraps.
/// </summary>
public double RecalculateSum()
{
double sum = 0;
GetSequencedSpans(out var first, out var second);
_sum = SumSpanSimd(first) + SumSpanSimd(second);
return _sum;
}
for (int i = 0; i < first.Length; i++)
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double SumSpanSimd(ReadOnlySpan<double> span)
{
if (span.IsEmpty)
{
sum += first[i];
}
for (int i = 0; i < second.Length; i++)
{
sum += second[i];
return 0.0;
}
int vectorSize = Vector<double>.Count;
var acc = Vector<double>.Zero;
int i = 0;
if (span.Length >= vectorSize)
{
ref double spanRef = ref MemoryMarshal.GetReference(span);
for (; i <= span.Length - vectorSize; i += vectorSize)
{
acc += Unsafe.As<double, Vector<double>>(ref Unsafe.Add(ref spanRef, i));
}
}
// Horizontal sum of SIMD accumulator
double sum = 0.0;
for (int j = 0; j < vectorSize; j++)
{
sum += acc[j];
}
// Scalar tail
for (; i < span.Length; i++)
{
sum += span[i];
}
_sum = sum;
return sum;
}
@@ -0,0 +1,81 @@
using System;
using System.Linq;
using Xunit;
using QuanTAlib;
namespace QuanTAlib.Tests.Core;
/// <summary>
/// Tests verifying the structural stability of indicators across the repository.
/// </summary>
public class IndicatorPropertiesTests
{
[Fact]
public void Sma_ShouldNotProduceNaN_WithValidInputs()
{
var sma = new Sma(period: 10);
var random = new Random(42);
for (int i = 0; i < 100; i++)
{
double price = (random.Next(1, 1000000) / 10.0);
sma.Update(new TValue(DateTime.Today.AddDays(i), price));
// Only check for NaN after warmup
if (i >= sma.WarmupPeriod && double.IsNaN(sma.Last.Value))
{
Assert.Fail($"Produced NaN at index {i}");
}
}
}
[Fact]
public void Ema_ShouldNotProduceNaN_WithValidInputs()
{
var ema = new Ema(period: 10);
var random = new Random(42);
for (int i = 0; i < 100; i++)
{
double price = (random.Next(1, 1000000) / 10.0);
ema.Update(new TValue(DateTime.Today.AddDays(i), price));
// Only check for NaN after warmup
if (i >= ema.WarmupPeriod && double.IsNaN(ema.Last.Value))
{
Assert.Fail($"Produced NaN at index {i}");
}
}
}
[Fact]
public void Indicator_ShouldRecoverFromNaN_WhenReset()
{
var ema = new Ema(period: 10);
// Feed valid value
ema.Update(new TValue(DateTime.Today.AddDays(1), 100));
// Feed NaN, which should corrupt state
ema.Update(new TValue(DateTime.Today.AddDays(2), double.NaN));
// Let's actually ensure it is corrupted depending on implementation
// Some robust implementations might discard NaN internally, so we don't assert it strictly
// We just ensure it recovers properly.
// Reset should clear the corrupted state
ema.Reset();
// Feed valid value again
ema.Update(new TValue(DateTime.Today.AddDays(3), 100));
// Wait for Warmup
for (int i = 4; i < 3 + ema.WarmupPeriod; i++) {
ema.Update(new TValue(DateTime.Today.AddDays(i), 100));
}
// Verify recovery after warmup
Assert.False(double.IsNaN(ema.Last.Value));
Assert.Equal(100, Math.Round(ema.Last.Value, 5));
}
}
@@ -48,7 +48,7 @@ public sealed class TyppriceValidationTests : IDisposable
var taOut = new double[high.Length];
var retCode = Functions.TypPrice(high.AsSpan(), low.AsSpan(), close.AsSpan(),
0..^0, taOut, out var outRange);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
var (offset, length) = outRange.GetOffsetAndLength(taOut.Length);
// QuanTAlib batch span
@@ -48,7 +48,7 @@ public sealed class WclpriceValidationTests : IDisposable
var taOut = new double[high.Length];
var retCode = Functions.WclPrice(high.AsSpan(), low.AsSpan(), close.AsSpan(),
0..^0, taOut, out var outRange);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
var (offset, length) = outRange.GetOffsetAndLength(taOut.Length);
// QuanTAlib batch span
+1 -1
View File
@@ -380,4 +380,4 @@ public class CcycValidationTests
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
+1 -1
View File
@@ -338,4 +338,4 @@ public class CgIndicatorTests
double cgValue = indicator.LinesSeries[0].GetValue(0);
Assert.True(cgValue < 0, $"Downtrend should produce negative CG, got {cgValue}");
}
}
}
+1 -1
View File
@@ -755,4 +755,4 @@ public class CgTests
}
#endregion
}
}
+1 -1
View File
@@ -378,4 +378,4 @@ public class CgValidationTests
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
+1 -1
View File
@@ -339,4 +339,4 @@ public class DspIndicatorTests
Assert.True(positiveCount > 0, "Should have positive DSP values");
Assert.True(negativeCount > 0, "Should have negative DSP values");
}
}
}
+1 -1
View File
@@ -451,4 +451,4 @@ public class DspTests
}
#endregion
}
}
+1 -1
View File
@@ -401,4 +401,4 @@ public class DspValidationTests
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
+1 -1
View File
@@ -359,4 +359,4 @@ public class EacpIndicatorTests
Assert.True(double.IsFinite(indicatorEnhanced.LinesSeries[0].GetValue(0)));
Assert.True(double.IsFinite(indicatorNormal.LinesSeries[0].GetValue(0)));
}
}
}
+1 -1
View File
@@ -521,4 +521,4 @@ public class EacpTests
}
#endregion
}
}
+1 -1
View File
@@ -419,4 +419,4 @@ public class EacpValidationTests
}
#endregion
}
}
+1 -1
View File
@@ -383,4 +383,4 @@ public class EbswIndicatorTests
// Should have multiple zero crossings for oscillating price
Assert.True(crossings >= 3, $"Should have multiple zero crossings, got {crossings}");
}
}
}
+1 -1
View File
@@ -505,4 +505,4 @@ public class EbswTests
}
#endregion
}
}
+1 -1
View File
@@ -559,4 +559,4 @@ public class EbswValidationTests
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
+1 -1
View File
@@ -338,4 +338,4 @@ public class HomodIndicatorTests
double cycleValue = indicator.LinesSeries[0].GetValue(0);
Assert.True(double.IsFinite(cycleValue));
}
}
}
+1 -1
View File
@@ -482,4 +482,4 @@ public class HomodTests
}
#endregion
}
}
+1 -1
View File
@@ -378,4 +378,4 @@ public class HomodValidationTests
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
@@ -42,7 +42,7 @@ public sealed class HtDcperiodValidationTests : IDisposable
var outPeriod = new double[input.Length];
var rc = TALib.Functions.HtDcPeriod(input, 0..^0, outPeriod, out var outRange);
Assert.Equal(Core.RetCode.Success, rc);
Assert.Equal(TALib.Core.RetCode.Success, rc);
var q = new HtDcperiod();
var qSeries = q.Update(_data.Data);
@@ -65,7 +65,7 @@ public sealed class HtDcperiodValidationTests : IDisposable
var outPeriod = new double[input.Length];
var rc = TALib.Functions.HtDcPeriod(input, 0..^0, outPeriod, out var outRange);
Assert.Equal(Core.RetCode.Success, rc);
Assert.Equal(TALib.Core.RetCode.Success, rc);
var streaming = new List<double>(_data.Data.Count);
var q = new HtDcperiod();
@@ -42,7 +42,7 @@ public sealed class HtDcphaseValidationTests : IDisposable
var outPhase = new double[input.Length];
var rc = TALib.Functions.HtDcPhase(input, 0..^0, outPhase, out var outRange);
Assert.Equal(Core.RetCode.Success, rc);
Assert.Equal(TALib.Core.RetCode.Success, rc);
var q = new HtDcphase();
var qSeries = q.Update(_data.Data);
@@ -65,7 +65,7 @@ public sealed class HtDcphaseValidationTests : IDisposable
var outPhase = new double[input.Length];
var rc = TALib.Functions.HtDcPhase(input, 0..^0, outPhase, out var outRange);
Assert.Equal(Core.RetCode.Success, rc);
Assert.Equal(TALib.Core.RetCode.Success, rc);
var streaming = new List<double>(_data.Data.Count);
var q = new HtDcphase();
+1 -1
View File
@@ -292,4 +292,4 @@ public class HtSineIndicatorTests
Assert.Contains("github.com/mihakralj/QuanTAlib", indicator.SourceCodeLink, StringComparison.Ordinal);
Assert.Contains("HtSine.Quantower.cs", indicator.SourceCodeLink, StringComparison.Ordinal);
}
}
}
+1 -1
View File
@@ -487,4 +487,4 @@ public class HtSineTests
}
#endregion
}
}
@@ -42,7 +42,7 @@ public sealed class HtSineValidationTests : IDisposable
var outLeadSine = new double[input.Length];
var retCode = TALib.Functions.HtSine(input, 0..^0, outSine, outLeadSine, out var outRange);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
// Calculate QuanTAlib HtSine
var htSine = new HtSine();
@@ -83,7 +83,7 @@ public sealed class HtSineValidationTests : IDisposable
var outLeadSine = new double[input.Length];
var retCode = TALib.Functions.HtSine(input, 0..^0, outSine, outLeadSine, out var outRange);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
// Calculate QuanTAlib HtSine Streaming
var htSine = new HtSine();
+1 -1
View File
@@ -266,4 +266,4 @@ public class LunarIndicatorTests
Assert.Contains("Lunar.Quantower.cs", indicator.SourceCodeLink, StringComparison.Ordinal);
Assert.Contains("github.com", indicator.SourceCodeLink, StringComparison.Ordinal);
}
}
}
+1 -1
View File
@@ -306,4 +306,4 @@ public class LunarTests
Assert.Equal(phase1, phase2, Tolerance);
}
}
}
+1 -1
View File
@@ -259,4 +259,4 @@ public class SolarIndicatorTests
Assert.Contains("Solar.Quantower.cs", indicator.SourceCodeLink, StringComparison.Ordinal);
Assert.Contains("github.com", indicator.SourceCodeLink, StringComparison.Ordinal);
}
}
}
+1 -1
View File
@@ -295,4 +295,4 @@ public class SolarTests
Assert.True(cycle > 0.95, $"Expected cycle > 0.95 at 2000 summer solstice, got {cycle}");
}
}
}
+1 -1
View File
@@ -269,4 +269,4 @@ public class SsfdspIndicatorTests
Assert.Contains("github.com/mihakralj/QuanTAlib", indicator.SourceCodeLink, StringComparison.Ordinal);
Assert.Contains("Ssfdsp.Quantower.cs", indicator.SourceCodeLink, StringComparison.Ordinal);
}
}
}
+1 -1
View File
@@ -500,4 +500,4 @@ public class SsfdspTests
}
#endregion
}
}
+1 -1
View File
@@ -352,4 +352,4 @@ public class SsfdspValidationTests
}
#endregion
}
}
+1 -1
View File
@@ -56,7 +56,7 @@ public sealed class AdxValidationTests : IDisposable
double[] outReal = new double[_data.Bars.Count];
var retCode = Functions.Adx(hData, lData, cData, 0..^0, outReal, out var outRange, 14);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.AdxLookback(14);
ValidationHelper.VerifyData(results, outReal, outRange, lookback);
+1 -1
View File
@@ -35,7 +35,7 @@ public sealed class AdxrValidationTests : IDisposable
double[] outReal = new double[_data.Bars.Count];
var retCode = Functions.Adxr(hData, lData, cData, 0..^0, outReal, out var outRange, 14);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.AdxrLookback(14);
ValidationHelper.VerifyData(results, outReal, outRange, lookback);
@@ -164,4 +164,4 @@ public sealed class AlligatorValidationTests : IDisposable
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
+2 -2
View File
@@ -126,7 +126,7 @@ public sealed class AmatValidationTests : IDisposable
// Calculate TA-Lib EMA (fast period)
var retCode = TALib.Functions.Ema<double>(tData, 0..^0, outEma, out var outRange, fastPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.EmaLookback(fastPeriod);
@@ -161,7 +161,7 @@ public sealed class AmatValidationTests : IDisposable
// Calculate TA-Lib EMA (slow period)
var retCode = TALib.Functions.Ema<double>(tData, 0..^0, outEma, out var outRange, slowPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.EmaLookback(slowPeriod);
+2 -2
View File
@@ -70,11 +70,11 @@ public sealed class AroonValidationTests : IDisposable
// TA-Lib Aroon (Up/Down)
var retCode = TALib.Functions.Aroon(hData, lData, 0..^0, outAroonDown, outAroonUp, out var outRange, 14);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
// TA-Lib AroonOsc
var retCodeOsc = TALib.Functions.AroonOsc(hData, lData, 0..^0, outAroonOsc, out var outRangeOsc, 14);
Assert.Equal(Core.RetCode.Success, retCodeOsc);
Assert.Equal(TALib.Core.RetCode.Success, retCodeOsc);
int lookback = TALib.Functions.AroonLookback(14);
@@ -54,7 +54,7 @@ public sealed class AroonOscValidationTests : IDisposable
// TA-Lib AroonOsc
var retCodeOsc = TALib.Functions.AroonOsc(hData, lData, 0..^0, outAroonOsc, out var outRangeOsc, 14);
Assert.Equal(Core.RetCode.Success, retCodeOsc);
Assert.Equal(TALib.Core.RetCode.Success, retCodeOsc);
int lookback = TALib.Functions.AroonLookback(14);
+2 -2
View File
@@ -72,7 +72,7 @@ public sealed class DxValidationTests : IDisposable
double[] outReal = new double[_data.Bars.Count];
var retCode = Functions.PlusDI(hData, lData, cData, 0..^0, outReal, out var outRange, 14);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.PlusDILookback(14);
ValidationHelper.VerifyData(diPlusResults, outReal, outRange, lookback);
@@ -96,7 +96,7 @@ public sealed class DxValidationTests : IDisposable
double[] outReal = new double[_data.Bars.Count];
var retCode = Functions.MinusDI(hData, lData, cData, 0..^0, outReal, out var outRange, 14);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.MinusDILookback(14);
ValidationHelper.VerifyData(diMinusResults, outReal, outRange, lookback);
@@ -162,7 +162,7 @@ public sealed class HtTrendmodeValidationTests : IDisposable
int[] outInteger = new int[inReal.Length];
var retCode = Functions.HtTrendMode(inReal, 0..^0, outInteger, out var outRange);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
// Compare after warmup
int lookback = Functions.HtTrendModeLookback();
+24 -24
View File
@@ -52,10 +52,10 @@ public class QstickIndicatorTests
}
[Fact]
public void DefaultMaType_IsSMA()
public void DefaultTALib.Core.MAType_IsSMA()
{
var indicator = new QstickIndicator();
Assert.Equal("SMA", indicator.MaType);
Assert.Equal("SMA", indicator.TALib.Core.MAType);
}
[Fact]
@@ -68,7 +68,7 @@ public class QstickIndicatorTests
// ── ShortName ───────────────────────────────────────────────────────
[Fact]
public void ShortName_DefaultParameters_IncludesPeriodAndMaType()
public void ShortName_DefaultParameters_IncludesPeriodAndTALib.Core.MAType()
{
var indicator = new QstickIndicator();
Assert.Equal("QSTICK(14,SMA)", indicator.ShortName);
@@ -84,7 +84,7 @@ public class QstickIndicatorTests
[Fact]
public void ShortName_EmaMode_IncludesEMA()
{
var indicator = new QstickIndicator { Period = 20, MaType = "EMA" };
var indicator = new QstickIndicator { Period = 20, TALib.Core.MAType = "EMA" };
Assert.Equal("QSTICK(20,EMA)", indicator.ShortName);
}
@@ -109,7 +109,7 @@ public class QstickIndicatorTests
[Fact]
public void Initialize_SmaMode_CreatesInternalIndicator()
{
var indicator = new QstickIndicator { MaType = "SMA" };
var indicator = new QstickIndicator { TALib.Core.MAType = "SMA" };
indicator.Initialize();
Assert.NotNull(indicator);
@@ -118,7 +118,7 @@ public class QstickIndicatorTests
[Fact]
public void Initialize_EmaMode_CreatesInternalIndicator()
{
var indicator = new QstickIndicator { MaType = "EMA" };
var indicator = new QstickIndicator { TALib.Core.MAType = "EMA" };
indicator.Initialize();
Assert.NotNull(indicator);
@@ -139,7 +139,7 @@ public class QstickIndicatorTests
[Fact]
public void ProcessUpdate_HistoricalBar_ComputesValue()
{
var indicator = new QstickIndicator { Period = 3, MaType = "SMA" };
var indicator = new QstickIndicator { Period = 3, TALib.Core.MAType = "SMA" };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -156,7 +156,7 @@ public class QstickIndicatorTests
[Fact]
public void ProcessUpdate_HistoricalBar_BullishBars_PositiveValue()
{
var indicator = new QstickIndicator { Period = 3, MaType = "SMA" };
var indicator = new QstickIndicator { Period = 3, TALib.Core.MAType = "SMA" };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -175,7 +175,7 @@ public class QstickIndicatorTests
[Fact]
public void ProcessUpdate_NewBar_ComputesValue()
{
var indicator = new QstickIndicator { Period = 3, MaType = "SMA" };
var indicator = new QstickIndicator { Period = 3, TALib.Core.MAType = "SMA" };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -193,7 +193,7 @@ public class QstickIndicatorTests
[Fact]
public void ProcessUpdate_NewTick_ProcessesWithoutError()
{
var indicator = new QstickIndicator { Period = 3, MaType = "SMA" };
var indicator = new QstickIndicator { Period = 3, TALib.Core.MAType = "SMA" };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -214,7 +214,7 @@ public class QstickIndicatorTests
[Fact]
public void SmaMode_BearishBars_ProducesNegativeQstick()
{
var indicator = new QstickIndicator { Period = 5, MaType = "SMA" };
var indicator = new QstickIndicator { Period = 5, TALib.Core.MAType = "SMA" };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -231,7 +231,7 @@ public class QstickIndicatorTests
[Fact]
public void SmaMode_DojiBars_ProducesZeroQstick()
{
var indicator = new QstickIndicator { Period = 5, MaType = "SMA" };
var indicator = new QstickIndicator { Period = 5, TALib.Core.MAType = "SMA" };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -247,7 +247,7 @@ public class QstickIndicatorTests
[Fact]
public void EmaMode_BullishBars_ProducesPositiveQstick()
{
var indicator = new QstickIndicator { Period = 5, MaType = "EMA" };
var indicator = new QstickIndicator { Period = 5, TALib.Core.MAType = "EMA" };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -663,8 +663,8 @@ public class QstickIndicatorTests
[Fact]
public void DifferentPeriods_ProduceDifferentResults()
{
var indicator1 = new QstickIndicator { Period = 3, MaType = "SMA" };
var indicator2 = new QstickIndicator { Period = 10, MaType = "SMA" };
var indicator1 = new QstickIndicator { Period = 3, TALib.Core.MAType = "SMA" };
var indicator2 = new QstickIndicator { Period = 10, TALib.Core.MAType = "SMA" };
indicator1.Initialize();
indicator2.Initialize();
@@ -687,8 +687,8 @@ public class QstickIndicatorTests
[Fact]
public void SmaVsEma_SameData_ProduceDifferentResults()
{
var smaIndicator = new QstickIndicator { Period = 5, MaType = "SMA" };
var emaIndicator = new QstickIndicator { Period = 5, MaType = "EMA" };
var smaIndicator = new QstickIndicator { Period = 5, TALib.Core.MAType = "SMA" };
var emaIndicator = new QstickIndicator { Period = 5, TALib.Core.MAType = "EMA" };
smaIndicator.Initialize();
emaIndicator.Initialize();
@@ -715,7 +715,7 @@ public class QstickIndicatorTests
[Fact]
public void Reinitialize_WithDifferentParameters_ResetsState()
{
var indicator = new QstickIndicator { Period = 5, MaType = "SMA" };
var indicator = new QstickIndicator { Period = 5, TALib.Core.MAType = "SMA" };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -726,7 +726,7 @@ public class QstickIndicatorTests
}
indicator.Period = 10;
indicator.MaType = "EMA";
indicator.TALib.Core.MAType = "EMA";
indicator.Initialize();
Assert.Equal("QSTICK(10,EMA)", indicator.ShortName);
@@ -762,7 +762,7 @@ public class QstickIndicatorTests
[Fact]
public void MultipleBars_ThroughAdapter_ProducesExpectedValues()
{
var indicator = new QstickIndicator { Period = 3, MaType = "SMA" };
var indicator = new QstickIndicator { Period = 3, TALib.Core.MAType = "SMA" };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -795,12 +795,12 @@ public class QstickIndicatorTests
}
[Fact]
public void MaType_CanBeChanged()
public void TALib.Core.MAType_CanBeChanged()
{
var indicator = new QstickIndicator();
Assert.Equal("SMA", indicator.MaType);
Assert.Equal("SMA", indicator.TALib.Core.MAType);
indicator.MaType = "EMA";
Assert.Equal("EMA", indicator.MaType);
indicator.TALib.Core.MAType = "EMA";
Assert.Equal("EMA", indicator.TALib.Core.MAType);
}
}
+1 -1
View File
@@ -362,4 +362,4 @@ public class MdapeTests
Assert.Equal(mdape1.Last.Value, mdape2.Last.Value, Precision);
}
}
}
+6
View File
@@ -33,6 +33,9 @@ public sealed class Edcf : AbstractBase
private State _s;
private State _ps;
/// <summary>
/// Initializes an Ehlers Distance Coefficient Filter.
/// </summary>
/// <param name="length">Filter window length (≥ 2). Default: 15.</param>
public Edcf(int length = 15)
{
@@ -51,6 +54,9 @@ public sealed class Edcf : AbstractBase
Name = $"Edcf({_length})";
}
/// <summary>
/// Initializes an EDCF indicator and subscribes it to a source publisher.
/// </summary>
/// <param name="source">Input data source for event-based chaining.</param>
/// <param name="length">Filter window length (≥ 2). Default: 15.</param>
public Edcf(ITValuePublisher source, int length = 15) : this(length)
+1 -1
View File
@@ -191,4 +191,4 @@ public class GaussValidationTests : IDisposable
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
+1 -1
View File
@@ -154,4 +154,4 @@ public class HpfValidationTests : IDisposable
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
+1 -1
View File
@@ -241,4 +241,4 @@ public class BiasIndicatorTests
indicator.Period = 100;
Assert.Equal(100, indicator.Period);
}
}
}
+1 -1
View File
@@ -591,4 +591,4 @@ public class BiasTests
// SMA = 0, Bias = (0-0)/0 = 0/0 -> should return 0 to avoid NaN
Assert.Equal(0.0, bias.Last.Value, 1e-10);
}
}
}
+1 -1
View File
@@ -433,4 +433,4 @@ public sealed class BiasValidationTests : IDisposable
_output.WriteLine($"Oscillating sequence: avg bias = {avgBias:F6}");
}
}
}
+1 -1
View File
@@ -39,7 +39,7 @@ public sealed class BopValidationTests : IDisposable
var talibResult = new double[_data.Bars.Count];
var retCode = TALib.Functions.Bop(open, high, low, close, 0..^0, talibResult, out var outRange);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
var quanTAlibResult = Bop.Batch(_data.Bars);
+3 -3
View File
@@ -47,7 +47,7 @@ public sealed class CciValidationTests(ITestOutputHelper output) : IDisposable
// TALib CCI
double[] tOutput = new double[high.Length];
var retCode = TALib.Functions.Cci<double>(high, low, close, 0..^0, tOutput, out var outRange, TestPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.CciLookback(TestPeriod);
@@ -86,7 +86,7 @@ public sealed class CciValidationTests(ITestOutputHelper output) : IDisposable
// TALib CCI
double[] tOutput = new double[high.Length];
var retCode = TALib.Functions.Cci<double>(high, low, close, 0..^0, tOutput, out var outRange, TestPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.CciLookback(TestPeriod);
@@ -123,7 +123,7 @@ public sealed class CciValidationTests(ITestOutputHelper output) : IDisposable
double[] tOutput = new double[high.Length];
var retCode = TALib.Functions.Cci<double>(high, low, close, 0..^0, tOutput, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.CciLookback(period);
+1 -1
View File
@@ -280,7 +280,7 @@ public sealed class CmoValidationTests(ITestOutputHelper output) : IDisposable
// --- TALib CMO ---
double[] taOut = new double[tData.Length];
var retCode = Functions.Cmo<double>(tData, 0..^0, taOut, out var outRange, TestPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
var (taOffset, taLength) = outRange.GetOffsetAndLength(taOut.Length);
Assert.True(taLength > 0, "TALib produced no output");
+1 -1
View File
@@ -127,7 +127,7 @@ public sealed class MacdValidationTests : IDisposable
// Calculate TA-Lib MACD
var retCode = TALib.Functions.Macd<double>(tData, 0..^0, outMacd, outSignal, outHist, out var outRange, fastPeriod, slowPeriod, signalPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.MacdLookback(fastPeriod, slowPeriod, signalPeriod);
+5 -5
View File
@@ -48,7 +48,7 @@ public sealed class MomValidationTests(ITestOutputHelper output) : IDisposable
// TALib Mom
double[] tOutput = new double[tData.Length];
var retCode = TALib.Functions.Mom<double>(tData, 0..^0, tOutput, out var outRange, TestPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.MomLookback(TestPeriod);
@@ -81,7 +81,7 @@ public sealed class MomValidationTests(ITestOutputHelper output) : IDisposable
// TALib Mom
double[] tOutput = new double[tData.Length];
var retCode = TALib.Functions.Mom<double>(tData, 0..^0, tOutput, out var outRange, TestPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.MomLookback(TestPeriod);
@@ -106,7 +106,7 @@ public sealed class MomValidationTests(ITestOutputHelper output) : IDisposable
// TALib Mom
double[] tOutput = new double[tData.Length];
var retCode = TALib.Functions.Mom<double>(tData, 0..^0, tOutput, out var outRange, TestPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.MomLookback(TestPeriod);
@@ -129,7 +129,7 @@ public sealed class MomValidationTests(ITestOutputHelper output) : IDisposable
double[] tOutput = new double[tData.Length];
var retCode = TALib.Functions.Mom<double>(tData, 0..^0, tOutput, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.MomLookback(period);
@@ -413,4 +413,4 @@ public sealed class MomValidationTests(ITestOutputHelper output) : IDisposable
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
+4 -4
View File
@@ -126,11 +126,11 @@ public sealed class PpoValidationTests(ITestOutputHelper output) : IDisposable
qPpo.Add(ppo.Last.Value);
}
// TA-Lib PPO (must specify MAType.Ema — default is SMA which differs from our EMA-based PPO)
var retCode = TALib.Functions.Ppo<double>(tData, 0..^0, outPpo, out var outRange, fastPeriod, slowPeriod, Core.MAType.Ema);
Assert.Equal(Core.RetCode.Success, retCode);
// TA-Lib PPO (must specify TALib.Core.MAType.Ema — default is SMA which differs from our EMA-based PPO)
var retCode = TALib.Functions.Ppo<double>(tData, 0..^0, outPpo, out var outRange, fastPeriod, slowPeriod, TALib.Core.MAType.Ema);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.PpoLookback(fastPeriod, slowPeriod, Core.MAType.Ema);
int lookback = TALib.Functions.PpoLookback(fastPeriod, slowPeriod, TALib.Core.MAType.Ema);
// Compare
ValidationHelper.VerifyData(qPpo, outPpo, outRange, lookback);
+3 -3
View File
@@ -309,7 +309,7 @@ public sealed class RocValidationTests(ITestOutputHelper output) : IDisposable
// TALib MOM (absolute momentum = price - prevPrice)
double[] taOut = new double[tData.Length];
var retCode = Functions.Mom<double>(tData, 0..^0, taOut, out var outRange, TestPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.MomLookback(TestPeriod);
ValidationHelper.VerifyData(qOutput, taOut, outRange, lookback);
@@ -333,7 +333,7 @@ public sealed class RocValidationTests(ITestOutputHelper output) : IDisposable
// TALib MOM
double[] taOut = new double[tData.Length];
var retCode = Functions.Mom<double>(tData, 0..^0, taOut, out var outRange, TestPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.MomLookback(TestPeriod);
ValidationHelper.VerifyData(qResults, taOut, outRange, lookback);
@@ -355,7 +355,7 @@ public sealed class RocValidationTests(ITestOutputHelper output) : IDisposable
double[] taOut = new double[tData.Length];
var retCode = Functions.Mom<double>(tData, 0..^0, taOut, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = Functions.MomLookback(period);
ValidationHelper.VerifyData(qOutput, taOut, outRange, lookback);
+4 -4
View File
@@ -52,7 +52,7 @@ public sealed class RocpValidationTests(ITestOutputHelper output) : IDisposable
// TALib RocP (returns decimal fraction)
double[] tOutput = new double[tData.Length];
var retCode = TALib.Functions.RocP<double>(tData, 0..^0, tOutput, out var outRange, TestPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.RocPLookback(TestPeriod);
@@ -93,7 +93,7 @@ public sealed class RocpValidationTests(ITestOutputHelper output) : IDisposable
// TALib RocP
double[] tOutput = new double[tData.Length];
var retCode = TALib.Functions.RocP<double>(tData, 0..^0, tOutput, out var outRange, TestPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.RocPLookback(TestPeriod);
@@ -137,7 +137,7 @@ public sealed class RocpValidationTests(ITestOutputHelper output) : IDisposable
// TALib RocP
double[] tOutput = new double[tData.Length];
var retCode = TALib.Functions.RocP<double>(tData, 0..^0, tOutput, out var outRange, TestPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.RocPLookback(TestPeriod);
@@ -179,7 +179,7 @@ public sealed class RocpValidationTests(ITestOutputHelper output) : IDisposable
double[] tOutput = new double[tData.Length];
var retCode = TALib.Functions.RocP<double>(tData, 0..^0, tOutput, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.RocPLookback(period);
var (offset, length) = outRange.GetOffsetAndLength(tOutput.Length);
+4 -4
View File
@@ -45,7 +45,7 @@ public sealed class RocrValidationTests(ITestOutputHelper output) : IDisposable
// TALib RocR
double[] tOutput = new double[tData.Length];
var retCode = TALib.Functions.RocR<double>(tData, 0..^0, tOutput, out var outRange, TestPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.RocRLookback(TestPeriod);
@@ -78,7 +78,7 @@ public sealed class RocrValidationTests(ITestOutputHelper output) : IDisposable
// TALib RocR
double[] tOutput = new double[tData.Length];
var retCode = TALib.Functions.RocR<double>(tData, 0..^0, tOutput, out var outRange, TestPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.RocRLookback(TestPeriod);
@@ -115,7 +115,7 @@ public sealed class RocrValidationTests(ITestOutputHelper output) : IDisposable
// TALib RocR
double[] tOutput = new double[tData.Length];
var retCode = TALib.Functions.RocR<double>(tData, 0..^0, tOutput, out var outRange, TestPeriod);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.RocRLookback(TestPeriod);
@@ -150,7 +150,7 @@ public sealed class RocrValidationTests(ITestOutputHelper output) : IDisposable
double[] tOutput = new double[tData.Length];
var retCode = TALib.Functions.RocR<double>(tData, 0..^0, tOutput, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.RocRLookback(period);
var (offset, length) = outRange.GetOffsetAndLength(tOutput.Length);
+2 -2
View File
@@ -65,7 +65,7 @@ public sealed class RsiValidationTests(ITestOutputHelper output) : IDisposable
double[] tOutput = new double[tData.Length];
var retCode = TALib.Functions.Rsi<double>(tData, 0..^0, tOutput, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.RsiLookback(period);
@@ -143,7 +143,7 @@ public sealed class RsiValidationTests(ITestOutputHelper output) : IDisposable
// Calculate TA-Lib RSI
var retCode = TALib.Functions.Rsi<double>(tData, 0..^0, tOutput, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.RsiLookback(period);
+1 -1
View File
@@ -142,4 +142,4 @@ public class RsxValidationTests
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
@@ -51,7 +51,7 @@ public sealed class HighestValidationTests : IDisposable
// Calculate TA-Lib MAX
var retCode = TALib.Functions.Max<double>(tData, 0..^0, output, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.MaxLookback(period);
@@ -81,7 +81,7 @@ public sealed class HighestValidationTests : IDisposable
// Calculate TA-Lib MAX
var retCode = TALib.Functions.Max<double>(tData, 0..^0, output, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.MaxLookback(period);
@@ -107,7 +107,7 @@ public sealed class HighestValidationTests : IDisposable
// Calculate TA-Lib MAX
var retCode = TALib.Functions.Max<double>(sourceData, 0..^0, talibOutput, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.MaxLookback(period);
@@ -51,7 +51,7 @@ public sealed class LowestValidationTests : IDisposable
// Calculate TA-Lib MIN
var retCode = TALib.Functions.Min<double>(tData, 0..^0, output, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.MinLookback(period);
@@ -81,7 +81,7 @@ public sealed class LowestValidationTests : IDisposable
// Calculate TA-Lib MIN
var retCode = TALib.Functions.Min<double>(tData, 0..^0, output, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.MinLookback(period);
@@ -107,7 +107,7 @@ public sealed class LowestValidationTests : IDisposable
// Calculate TA-Lib MIN
var retCode = TALib.Functions.Min<double>(sourceData, 0..^0, talibOutput, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.MinLookback(period);
+1 -1
View File
@@ -197,4 +197,4 @@ public sealed class AcValidationTests
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
+7
View File
@@ -15,6 +15,13 @@ namespace QuanTAlib;
/// AO = SMA(Median Price, fastPeriod) - SMA(Median Price, slowPeriod)
/// AC = AO - SMA(AO, acPeriod)
///
/// Design note: Ac implements <see cref="ITValuePublisher"/> directly rather than inheriting
/// from AbstractBase. This is intentional: Ac is an OHLC-based indicator whose primary input
/// is a <see cref="TBar"/> (requiring High and Low), not a single <see cref="TValue"/>.
/// AbstractBase's contract (Update(TValue), Prime(ReadOnlySpan&lt;double&gt;)) does not fit
/// OHLC indicators. The practical entry points are Update(TBar) and Prime(TBarSeries).
/// If a future TBarIndicatorBase is introduced, Ac would be a candidate to migrate.
///
/// Sources:
/// https://www.investopedia.com/terms/a/accelerationdeceleration-indicator.asp
/// https://www.tradingview.com/support/solutions/43000501837-accelerator-oscillator-ac/
+7
View File
@@ -16,6 +16,13 @@ namespace QuanTAlib;
/// Median Price = (High + Low) / 2
/// AO = SMA(Median Price, 5) - SMA(Median Price, 34)
///
/// Design note: Ao implements <see cref="ITValuePublisher"/> directly rather than inheriting
/// from AbstractBase. This is intentional: Ao is an OHLC-based indicator whose primary input
/// is a <see cref="TBar"/> (requiring High and Low), not a single <see cref="TValue"/>.
/// AbstractBase's contract (Update(TValue), Prime(ReadOnlySpan&lt;double&gt;)) does not fit
/// OHLC indicators. The practical entry points are Update(TBar) and Prime(TBarSeries).
/// If a future TBarIndicatorBase is introduced, Ao would be a candidate to migrate.
///
/// Sources:
/// https://www.investopedia.com/terms/a/awesomeoscillator.asp
/// https://www.tradingview.com/support/solutions/43000501826-awesome-oscillator-ao/
+2 -2
View File
@@ -43,8 +43,8 @@ public sealed class ApoValidationTests : IDisposable
double[] input = _testData.Data.Values.ToArray();
double[] output = new double[input.Length];
// TA-Lib APO: double[] inReal, int optInFastPeriod, int optInSlowPeriod, int optInMAType
// MAType 1 = EMA
// TA-Lib APO: double[] inReal, int optInFastPeriod, int optInSlowPeriod, int optInTALib.Core.MAType
// TALib.Core.MAType 1 = EMA
var retCode = TALib.Functions.Apo<double>(input, 0..^0, output, out var outRange, fastPeriod, slowPeriod, TALib.Core.MAType.Ema);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
+1 -1
View File
@@ -308,4 +308,4 @@ public sealed class DemValidationTests(ITestOutputHelper output)
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
@@ -243,4 +243,4 @@ public sealed class DymoiValidationTests
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
@@ -226,4 +226,4 @@ public sealed class InertiaValidationTests : IDisposable
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
@@ -350,4 +350,4 @@ public sealed class LrsiValidationTests
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
+1 -1
View File
@@ -242,4 +242,4 @@ public sealed class PgoValidationTests
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
+1 -1
View File
@@ -178,4 +178,4 @@ public sealed class SmiValidationTests
int finiteCount = values.Count(v => double.IsFinite(v));
Assert.True(finiteCount > 100, $"Expected >100 finite values, got {finiteCount}");
}
}
}
@@ -258,10 +258,10 @@ public sealed class StochValidationTests : IDisposable
// --- I) TALib cross-validation ---
/// <summary>
/// TALib Stoch(fastKPeriod=14, slowKPeriod=1, slowKMAType=SMA, slowDPeriod=3, slowDMAType=SMA)
/// TALib Stoch(fastKPeriod=14, slowKPeriod=1, slowKTALib.Core.MAType=SMA, slowDPeriod=3, slowDTALib.Core.MAType=SMA)
/// with slowKPeriod=1 (no K smoothing) produces raw %K == our K output.
/// slowD with SMA(3) matches our D output.
/// Note: TALib Stoch uses SMA for both K and D smoothing (MAType=SMA).
/// Note: TALib Stoch uses SMA for both K and D smoothing (TALib.Core.MAType=SMA).
/// QuanTAlib Stoch also uses SMA. With slowKPeriod=1 (identity) the K lines match directly.
/// </summary>
[Fact]
@@ -105,7 +105,7 @@ public sealed class TrixValidationTests(ITestOutputHelper output) : IDisposable
double[] tOutput = new double[tData.Length];
var retCode = TALib.Functions.Trix<double>(tData, 0..^0, tOutput, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.TrixLookback(period);
@@ -132,7 +132,7 @@ public sealed class TrixValidationTests(ITestOutputHelper output) : IDisposable
}
var retCode = TALib.Functions.Trix<double>(tData, 0..^0, tOutput, out var outRange, period);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.TrixLookback(period);
@@ -243,7 +243,7 @@ public sealed class UltoscValidationTests : IDisposable
var qResult = ultosc.Update(_data.Bars);
var retCode = TALib.Functions.UltOsc(hData, lData, cData, 0..^0, output, out var outRange, p1, p2, p3);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.UltOscLookback(p1, p2, p3);
@@ -279,7 +279,7 @@ public sealed class UltoscValidationTests : IDisposable
}
var retCode = TALib.Functions.UltOsc(hData, lData, cData, 0..^0, output, out var outRange, p1, p2, p3);
Assert.Equal(Core.RetCode.Success, retCode);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
int lookback = TALib.Functions.UltOscLookback(p1, p2, p3);
@@ -259,4 +259,4 @@ public sealed class PivotValidationTests
}
}
}
}
@@ -273,4 +273,4 @@ public sealed class PivotcamValidationTests
}
}
}
}
@@ -240,4 +240,4 @@ public sealed class PivotdemValidationTests
}
}
}
}
@@ -246,4 +246,4 @@ public sealed class PivotfibValidationTests
}
}
}
}
@@ -254,4 +254,4 @@ public sealed class PivotwoodValidationTests
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More