mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-14 00:28:05 +00:00
Add TEMA (Triple Exponential Moving Average) implementation and validation tests
- Implemented TEMA calculation in QuanTAlib with O(1) update complexity. - Added validation tests for TEMA against Skender, TA-Lib, and Tulip indicators. - Updated documentation for TEMA, including its mathematical foundation and usage examples. - Enhanced existing tests for other indicators (TRIMA, WMA) to generate more records. - Adjusted benchmark tests to include DEMA and TEMA comparisons. - Refactored code for better readability and performance, including zero-allocation Span API.
This commit is contained in:
@@ -7,7 +7,7 @@ namespace QuanTAlib.Tests;
|
||||
|
||||
public class IndicatorExtensionsTests
|
||||
{
|
||||
private class TestIndicator : Indicator
|
||||
private sealed class TestIndicator : Indicator
|
||||
{
|
||||
public TestIndicator()
|
||||
{
|
||||
@@ -15,11 +15,11 @@ public class IndicatorExtensionsTests
|
||||
}
|
||||
}
|
||||
|
||||
private class TestCoordinatesConverter : ICoordinatesConverter
|
||||
private sealed class TestCoordinatesConverter : ICoordinatesConverter
|
||||
{
|
||||
private readonly DateTime _time;
|
||||
public TestCoordinatesConverter(DateTime time) => _time = time;
|
||||
|
||||
|
||||
public DateTime GetTime(int x) => _time;
|
||||
public double GetChartX(DateTime time) => 0;
|
||||
public double GetChartY(double value) => 0;
|
||||
@@ -28,8 +28,8 @@ public class IndicatorExtensionsTests
|
||||
[Fact]
|
||||
public void DataSourceInputAttribute_HasCorrectDefaults()
|
||||
{
|
||||
var attr = new IndicatorExtensions.DataSourceInputAttribute();
|
||||
|
||||
IndicatorExtensions.DataSourceInputAttribute attr = new();
|
||||
|
||||
Assert.Equal("Data source", attr.Name);
|
||||
Assert.Equal(20, attr.SortIndex);
|
||||
Assert.NotNull(attr.Variants);
|
||||
@@ -39,39 +39,45 @@ public class IndicatorExtensionsTests
|
||||
[Fact]
|
||||
public void GetInputValue_ReturnsCorrectValues_ForSourceTypes()
|
||||
{
|
||||
var indicator = new TestIndicator();
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
TestIndicator indicator = new();
|
||||
DateTime now = new(2024, 1, 1, 12, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
// Open=100, High=110, Low=90, Close=105, Volume=1000
|
||||
indicator.HistoricalData.AddBar(now, 100, 110, 90, 105, 1000);
|
||||
|
||||
const double open = 100;
|
||||
const double high = 110;
|
||||
const double low = 90;
|
||||
const double close = 105;
|
||||
const double volume = 1000;
|
||||
|
||||
indicator.HistoricalData.AddBar(now, open, high, low, close, volume);
|
||||
|
||||
// Ensure Count is updated (mock implementation detail)
|
||||
// The mock HistoricalData.Count reflects added items.
|
||||
// Indicator.Count => HistoricalData.Count.
|
||||
|
||||
var args = new UpdateArgs(UpdateReason.NewBar);
|
||||
|
||||
UpdateArgs args = new(UpdateReason.NewBar);
|
||||
|
||||
// Test each SourceType
|
||||
Assert.Equal(100, IndicatorExtensions.GetInputValue(indicator, args, SourceType.Open).Value);
|
||||
Assert.Equal(110, IndicatorExtensions.GetInputValue(indicator, args, SourceType.High).Value);
|
||||
Assert.Equal(90, IndicatorExtensions.GetInputValue(indicator, args, SourceType.Low).Value);
|
||||
Assert.Equal(105, IndicatorExtensions.GetInputValue(indicator, args, SourceType.Close).Value);
|
||||
|
||||
Assert.Equal(open, IndicatorExtensions.GetInputValue(indicator, args, SourceType.Open).Value);
|
||||
Assert.Equal(high, IndicatorExtensions.GetInputValue(indicator, args, SourceType.High).Value);
|
||||
Assert.Equal(low, IndicatorExtensions.GetInputValue(indicator, args, SourceType.Low).Value);
|
||||
Assert.Equal(close, IndicatorExtensions.GetInputValue(indicator, args, SourceType.Close).Value);
|
||||
|
||||
// HL2 = (110 + 90) / 2 = 100
|
||||
Assert.Equal(100, IndicatorExtensions.GetInputValue(indicator, args, SourceType.HL2).Value);
|
||||
|
||||
|
||||
// OC2 = (100 + 105) / 2 = 102.5
|
||||
Assert.Equal(102.5, IndicatorExtensions.GetInputValue(indicator, args, SourceType.OC2).Value);
|
||||
|
||||
|
||||
// OHL3 = (100 + 110 + 90) / 3 = 100
|
||||
Assert.Equal(100, IndicatorExtensions.GetInputValue(indicator, args, SourceType.OHL3).Value);
|
||||
|
||||
|
||||
// HLC3 = (110 + 90 + 105) / 3 = 101.666...
|
||||
Assert.Equal(101.66666666666667, IndicatorExtensions.GetInputValue(indicator, args, SourceType.HLC3).Value, 5);
|
||||
|
||||
|
||||
// OHLC4 = (100 + 110 + 90 + 105) / 4 = 101.25
|
||||
Assert.Equal(101.25, IndicatorExtensions.GetInputValue(indicator, args, SourceType.OHLC4).Value);
|
||||
|
||||
|
||||
// HLCC4 = (110 + 90 + 105 + 105) / 4 = 102.5
|
||||
Assert.Equal(102.5, IndicatorExtensions.GetInputValue(indicator, args, SourceType.HLCC4).Value);
|
||||
}
|
||||
@@ -79,20 +85,26 @@ public class IndicatorExtensionsTests
|
||||
[Fact]
|
||||
public void GetInputBar_ReturnsCorrectBar()
|
||||
{
|
||||
var indicator = new TestIndicator();
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
indicator.HistoricalData.AddBar(now, 100, 110, 90, 105, 1000);
|
||||
var args = new UpdateArgs(UpdateReason.NewBar);
|
||||
TestIndicator indicator = new();
|
||||
DateTime now = new(2024, 1, 1, 12, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
const double open = 100;
|
||||
const double high = 110;
|
||||
const double low = 90;
|
||||
const double close = 105;
|
||||
const double volume = 1000;
|
||||
|
||||
indicator.HistoricalData.AddBar(now, open, high, low, close, volume);
|
||||
UpdateArgs args = new(UpdateReason.NewBar);
|
||||
|
||||
var bar = IndicatorExtensions.GetInputBar(indicator, args);
|
||||
|
||||
Assert.Equal(now, bar.AsDateTime);
|
||||
Assert.Equal(100, bar.Open);
|
||||
Assert.Equal(110, bar.High);
|
||||
Assert.Equal(90, bar.Low);
|
||||
Assert.Equal(105, bar.Close);
|
||||
Assert.Equal(1000, bar.Volume);
|
||||
Assert.Equal(open, bar.Open);
|
||||
Assert.Equal(high, bar.High);
|
||||
Assert.Equal(low, bar.Low);
|
||||
Assert.Equal(close, bar.Close);
|
||||
Assert.Equal(volume, bar.Volume);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -147,6 +159,6 @@ public class IndicatorExtensionsTests
|
||||
IndicatorExtensions.DrawText(indicator, args, "Test Text");
|
||||
|
||||
// Assert that we reached the end without throwing
|
||||
Assert.True(true);
|
||||
// If we got here, no exception was thrown
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user