mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-06 04:57:44 +00:00
Refactor event handling and improve argument validation across indicators
- Updated event handler signatures to use TValueEventArgs for consistency in Mama, Mgdi, Pwma, Rma, Sma, Ssf, Super, T3, Tema, Trima, Usf, Vidya, Wma, and Atr classes. - Enhanced argument validation by specifying parameter names in exceptions for clarity. - Adjusted tests to align with new event handler signatures. - Improved code readability and maintainability by using structured records and lambda expressions.
This commit is contained in:
@@ -31,14 +31,14 @@ public abstract class AbstractBase : ITValuePublisher
|
||||
/// <summary>
|
||||
/// Event triggered when a new TValue is available.
|
||||
/// </summary>
|
||||
public event Action<TValue>? Pub;
|
||||
public event TValuePublishedHandler? Pub;
|
||||
|
||||
/// <summary>
|
||||
/// Helper to invoke the Pub event.
|
||||
/// </summary>
|
||||
protected void PubEvent(TValue value)
|
||||
protected void PubEvent(TValue value, bool isNew = true)
|
||||
{
|
||||
Pub?.Invoke(value);
|
||||
Pub?.Invoke(this, new TValueEventArgs { Value = value, IsNew = isNew });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -395,7 +395,7 @@ public static class SimdExtensions
|
||||
public static void Add(ReadOnlySpan<double> left, ReadOnlySpan<double> right, Span<double> result)
|
||||
{
|
||||
if (left.Length != right.Length || left.Length != result.Length)
|
||||
throw new ArgumentException("All spans must have the same length");
|
||||
throw new ArgumentException("All spans must have the same length", nameof(result));
|
||||
|
||||
int i = 0;
|
||||
if (Vector.IsHardwareAccelerated && left.Length >= Vector<double>.Count)
|
||||
@@ -423,7 +423,7 @@ public static class SimdExtensions
|
||||
public static void Subtract(ReadOnlySpan<double> left, ReadOnlySpan<double> right, Span<double> result)
|
||||
{
|
||||
if (left.Length != right.Length || left.Length != result.Length)
|
||||
throw new ArgumentException("All spans must have the same length");
|
||||
throw new ArgumentException("All spans must have the same length", nameof(result));
|
||||
|
||||
int i = 0;
|
||||
if (Vector.IsHardwareAccelerated && left.Length >= Vector<double>.Count)
|
||||
@@ -451,7 +451,7 @@ public static class SimdExtensions
|
||||
public static double DotProduct(this ReadOnlySpan<double> a, ReadOnlySpan<double> b)
|
||||
{
|
||||
if (a.Length != b.Length)
|
||||
throw new ArgumentException("Spans must have equal length");
|
||||
throw new ArgumentException("Spans must have equal length", nameof(b));
|
||||
|
||||
if (a.IsEmpty) return 0.0;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
@@ -7,6 +8,7 @@ namespace QuanTAlib;
|
||||
/// Pure data type: 48 bytes (long + 5 doubles).
|
||||
/// </summary>
|
||||
[SkipLocalsInit]
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public readonly record struct TBar(long Time, double Open, double High, double Low, double Close, double Volume)
|
||||
{
|
||||
public DateTime AsDateTime => new(Time, DateTimeKind.Utc);
|
||||
|
||||
@@ -358,8 +358,8 @@ namespace QuanTAlib.Tests
|
||||
{
|
||||
var series = new TBarSeries();
|
||||
TBar? received = null;
|
||||
series.Pub += bar => received = bar;
|
||||
|
||||
series.Pub += (object? sender, in TBarEventArgs args) => received = args.Value;
|
||||
|
||||
var barToAdd = new TBar(100, 10, 15, 5, 12, 100);
|
||||
series.Add(barToAdd, isNew: true);
|
||||
|
||||
@@ -374,8 +374,8 @@ namespace QuanTAlib.Tests
|
||||
var series = new TBarSeries();
|
||||
TBar? received = null;
|
||||
series.Add(100, 10, 15, 5, 12, 100);
|
||||
series.Pub += bar => received = bar;
|
||||
|
||||
series.Pub += (object? sender, in TBarEventArgs args) => received = args.Value;
|
||||
|
||||
series.Add(100, 10, 18, 5, 15, 150, isNew: false);
|
||||
|
||||
Assert.NotNull(received);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -9,17 +10,32 @@ namespace QuanTAlib;
|
||||
/// Stores Time, Open, High, Low, Close, Volume in separate contiguous arrays for SIMD efficiency.
|
||||
/// Exposes TSeries views for each component that share the underlying Time array.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public readonly struct TBarEventArgs
|
||||
{
|
||||
public TBar Value { get; init; }
|
||||
public bool IsNew { get; init; }
|
||||
}
|
||||
|
||||
// Performance-focused event args struct; not derived from EventArgs by design.
|
||||
// We intentionally deviate from the standard EventArgs pattern here for perf.
|
||||
#pragma warning disable MA0046 // The second parameter must be of type 'System.EventArgs' or a derived type
|
||||
public delegate void TBarPublishedHandler(object? sender, in TBarEventArgs args);
|
||||
#pragma warning restore MA0046
|
||||
|
||||
public class TBarSeries : IReadOnlyList<TBar>
|
||||
{
|
||||
#pragma warning disable MA0016 // Prefer using collection abstraction instead of implementation
|
||||
protected readonly List<long> _t;
|
||||
protected readonly List<double> _o;
|
||||
protected readonly List<double> _h;
|
||||
protected readonly List<double> _l;
|
||||
protected readonly List<double> _c;
|
||||
protected readonly List<double> _v;
|
||||
#pragma warning restore MA0016
|
||||
|
||||
public string Name { get; set; } = "Bar";
|
||||
public event Action<TBar>? Pub;
|
||||
public event TBarPublishedHandler? Pub;
|
||||
|
||||
// Note: These views share underlying storage. Do not modify directly; use TBarSeries.Add() instead.
|
||||
public TSeries Open { get; }
|
||||
@@ -102,7 +118,7 @@ public class TBarSeries : IReadOnlyList<TBar>
|
||||
_v[lastIdx] = bar.Volume;
|
||||
}
|
||||
|
||||
Pub?.Invoke(bar);
|
||||
Pub?.Invoke(this, new TBarEventArgs { Value = bar, IsNew = isNew });
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -126,7 +142,7 @@ public class TBarSeries : IReadOnlyList<TBar>
|
||||
hArr.Length != lArr.Length || lArr.Length != cArr.Length ||
|
||||
cArr.Length != vArr.Length)
|
||||
{
|
||||
throw new ArgumentException("All arrays must have the same length");
|
||||
throw new ArgumentException("All arrays must have the same length", nameof(t));
|
||||
}
|
||||
|
||||
for (int i = 0; i < tArr.Length; i++)
|
||||
|
||||
@@ -2,6 +2,14 @@ using System;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public sealed class TValueEventArgs : EventArgs
|
||||
{
|
||||
public TValue Value { get; init; }
|
||||
public bool IsNew { get; init; }
|
||||
}
|
||||
|
||||
public delegate void TValuePublishedHandler(object? sender, TValueEventArgs args);
|
||||
|
||||
/// <summary>
|
||||
/// Interface for objects that publish TValue updates.
|
||||
/// </summary>
|
||||
@@ -10,5 +18,5 @@ public interface ITValuePublisher
|
||||
/// <summary>
|
||||
/// Event triggered when a new TValue is available.
|
||||
/// </summary>
|
||||
event Action<TValue> Pub;
|
||||
event TValuePublishedHandler? Pub;
|
||||
}
|
||||
|
||||
@@ -298,7 +298,7 @@ public class TSeriesTests
|
||||
{
|
||||
var series = new TSeries();
|
||||
TValue? received = null;
|
||||
series.Pub += tv => received = tv;
|
||||
series.Pub += (object? sender, TValueEventArgs args) => received = args.Value;
|
||||
|
||||
series.Add(100, 42.0);
|
||||
|
||||
@@ -313,7 +313,7 @@ public class TSeriesTests
|
||||
var series = new TSeries();
|
||||
TValue? received = null;
|
||||
series.Add(100, 42.0);
|
||||
series.Pub += tv => received = tv;
|
||||
series.Pub += (object? sender, TValueEventArgs args) => received = args.Value;
|
||||
|
||||
series.Add(100, 43.0, isNew: false);
|
||||
|
||||
|
||||
@@ -13,12 +13,14 @@ namespace QuanTAlib;
|
||||
/// </summary>
|
||||
public class TSeries : IReadOnlyList<TValue>, ITValuePublisher
|
||||
{
|
||||
#pragma warning disable MA0016 // Prefer using collection abstraction instead of implementation
|
||||
protected readonly List<long> _t;
|
||||
protected readonly List<double> _v;
|
||||
#pragma warning restore MA0016
|
||||
|
||||
public string Name { get; set; } = "Data";
|
||||
|
||||
public event Action<TValue>? Pub;
|
||||
public event TValuePublishedHandler? Pub;
|
||||
|
||||
public TSeries() : this(0)
|
||||
{
|
||||
@@ -30,10 +32,18 @@ public class TSeries : IReadOnlyList<TValue>, ITValuePublisher
|
||||
_v = new List<double>(capacity);
|
||||
}
|
||||
|
||||
public TSeries(List<long> time, List<double> values)
|
||||
public TSeries(IReadOnlyList<long> time, IReadOnlyList<double> values)
|
||||
{
|
||||
_t = time;
|
||||
_v = values;
|
||||
if (time is List<long> timeList && values is List<double> valueList)
|
||||
{
|
||||
_t = timeList;
|
||||
_v = valueList;
|
||||
}
|
||||
else
|
||||
{
|
||||
_t = new List<long>(time);
|
||||
_v = new List<double>(values);
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
@@ -98,7 +108,9 @@ public class TSeries : IReadOnlyList<TValue>, ITValuePublisher
|
||||
_t[lastIdx] = value.Time;
|
||||
_v[lastIdx] = value.Value;
|
||||
}
|
||||
Pub?.Invoke(value);
|
||||
|
||||
var args = new TValueEventArgs { Value = value, IsNew = isNew };
|
||||
Pub?.Invoke(this, args);
|
||||
}
|
||||
|
||||
// Overload for backward compatibility (assumes isNew=true)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
@@ -7,6 +8,7 @@ namespace QuanTAlib;
|
||||
/// Pure data type: 16 bytes (long + double).
|
||||
/// </summary>
|
||||
[SkipLocalsInit]
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public readonly record struct TValue(long Time, double Value)
|
||||
{
|
||||
public DateTime AsDateTime => new(Time, DateTimeKind.Utc);
|
||||
|
||||
Reference in New Issue
Block a user