mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-19 11:08:05 +00:00
refactor: optimize event args handling for performance improvements
This commit is contained in:
@@ -6,9 +6,8 @@ using System.Runtime.InteropServices;
|
||||
namespace QuanTAlib;
|
||||
|
||||
/// <summary>
|
||||
/// A high-performance OHLCV time series implementation using Structure of Arrays (SoA) layout.
|
||||
/// 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.
|
||||
/// Performance-focused event args for TBar updates.
|
||||
/// Implemented as struct to avoid heap allocations in high-frequency event dispatch.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public readonly struct TBarEventArgs
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public sealed class TValueEventArgs : EventArgs
|
||||
/// <summary>
|
||||
/// Performance-focused event args for TValue updates.
|
||||
/// Implemented as struct to avoid heap allocations in high-frequency event dispatch.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public readonly struct TValueEventArgs
|
||||
{
|
||||
public TValue Value { get; init; }
|
||||
public bool IsNew { get; init; }
|
||||
}
|
||||
|
||||
public delegate void TValuePublishedHandler(object? sender, TValueEventArgs args);
|
||||
// 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 TValuePublishedHandler(object? sender, in TValueEventArgs args);
|
||||
#pragma warning restore MA0046
|
||||
|
||||
/// <summary>
|
||||
/// Interface for objects that publish TValue updates.
|
||||
|
||||
@@ -109,8 +109,7 @@ public class TSeries : IReadOnlyList<TValue>, ITValuePublisher
|
||||
_v[lastIdx] = value.Value;
|
||||
}
|
||||
|
||||
var args = new TValueEventArgs { Value = value, IsNew = isNew };
|
||||
Pub?.Invoke(this, args);
|
||||
Pub?.Invoke(this, new TValueEventArgs { Value = value, IsNew = isNew });
|
||||
}
|
||||
|
||||
// Overload for backward compatibility (assumes isNew=true)
|
||||
|
||||
@@ -145,7 +145,7 @@ public sealed class Apo : ITValuePublisher
|
||||
return new TSeries(t, v);
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs args)
|
||||
private void Handle(object? sender, in TValueEventArgs args)
|
||||
{
|
||||
Update(args.Value, args.IsNew);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Skender.Stock.Indicators;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Skender.Stock.Indicators;
|
||||
|
||||
@@ -93,7 +93,7 @@ public sealed class Cfb : ITValuePublisher
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Handle(object? sender, TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Reset()
|
||||
|
||||
@@ -103,7 +103,7 @@ public sealed class Macd : ITValuePublisher
|
||||
return new TSeries(t, v);
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs args)
|
||||
private void Handle(object? sender, in TValueEventArgs args)
|
||||
{
|
||||
Update(args.Value, args.IsNew);
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ public sealed class Rsi : AbstractBase
|
||||
return new TSeries(t, v);
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs args)
|
||||
private void Handle(object? sender, in TValueEventArgs args)
|
||||
{
|
||||
Update(args.Value, args.IsNew);
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public sealed class Rsx : ITValuePublisher
|
||||
public bool IsHot => _state.IsInitialized;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Handle(object? sender, TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
|
||||
public TValue Update(TValue input, bool isNew = true)
|
||||
{
|
||||
|
||||
@@ -48,7 +48,7 @@ public sealed class Vel : ITValuePublisher
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Handle(object? sender, TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue Update(TValue input, bool isNew = true)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Xunit;
|
||||
|
||||
@@ -102,7 +102,7 @@ public sealed class LinReg : AbstractBase
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Handle(object? sender, TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private double GetValidValue(double input)
|
||||
|
||||
@@ -82,7 +82,7 @@ public sealed class Median : AbstractBase
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Handle(object? sender, TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override TValue Update(TValue input, bool isNew = true)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
using QuanTAlib;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
using QuanTAlib;
|
||||
|
||||
@@ -89,7 +89,7 @@ public sealed class Alma : AbstractBase, IDisposable
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
@@ -319,7 +319,7 @@ public sealed class Bessel : AbstractBase, IDisposable
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
|
||||
@@ -117,7 +117,7 @@ public sealed class Bilateral : AbstractBase
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Handle(object? sender, TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
|
||||
public override TSeries Update(TSeries source)
|
||||
{
|
||||
|
||||
@@ -50,7 +50,7 @@ public sealed class Blma : AbstractBase, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs args)
|
||||
private void Handle(object? sender, in TValueEventArgs args)
|
||||
{
|
||||
Update(args.Value, args.IsNew);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public sealed class Butter : AbstractBase
|
||||
source.Pub += _handler;
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs args)
|
||||
private void Handle(object? sender, in TValueEventArgs args)
|
||||
{
|
||||
Update(args.Value, args.IsNew);
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public sealed class Conv : AbstractBase, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private double GetValidValue(double input)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Xunit;
|
||||
using Xunit;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
namespace QuanTAlib.Tests;
|
||||
|
||||
@@ -337,5 +337,5 @@ public sealed class Dema : AbstractBase, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public sealed class Dwma : AbstractBase
|
||||
return new TSeries(t, v);
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs args)
|
||||
private void Handle(object? sender, in TValueEventArgs args)
|
||||
{
|
||||
Update(args.Value, args.IsNew);
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ public sealed class Ema : AbstractBase
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private double GetValidValue(double input)
|
||||
|
||||
@@ -117,7 +117,7 @@ public sealed class Hma : AbstractBase
|
||||
return new TSeries(t, v);
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs args)
|
||||
private void Handle(object? sender, in TValueEventArgs args)
|
||||
{
|
||||
Update(args.Value, args.IsNew);
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ public sealed class Htit : AbstractBase
|
||||
return new TSeries(t, v);
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs args)
|
||||
private void Handle(object? sender, in TValueEventArgs args)
|
||||
{
|
||||
Update(args.Value, args.IsNew);
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ public sealed class Jma : AbstractBase
|
||||
return new TSeries(t, v);
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
|
||||
@@ -208,7 +208,7 @@ public sealed class Kama : AbstractBase
|
||||
return new TSeries(t, v);
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs args)
|
||||
private void Handle(object? sender, in TValueEventArgs args)
|
||||
{
|
||||
Update(args.Value, args.IsNew);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OoplesFinance.StockIndicators;
|
||||
|
||||
@@ -87,7 +87,7 @@ public sealed class Lsma : AbstractBase, IDisposable
|
||||
_source.Pub += _handler;
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private double GetValidValue(double input)
|
||||
|
||||
@@ -80,7 +80,7 @@ public sealed class Mama : AbstractBase
|
||||
source.Pub += _handler;
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
private void Init()
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ public sealed class Mgdi : AbstractBase
|
||||
source.Pub += _handler;
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
private void Init()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TradingPlatform.BusinessLayer;
|
||||
|
||||
@@ -62,7 +62,7 @@ public sealed class Pwma : AbstractBase
|
||||
source.Pub += _handler;
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private double GetValidValue(double input, double lastValid)
|
||||
|
||||
@@ -80,7 +80,7 @@ public sealed class Rma : AbstractBase
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
public override TValue Update(TValue input, bool isNew = true)
|
||||
{
|
||||
|
||||
@@ -71,7 +71,7 @@ public sealed class Sma : AbstractBase
|
||||
source.Pub += _handler;
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Mode B: Streaming (Stateful)
|
||||
|
||||
@@ -77,7 +77,7 @@ public sealed class Ssf : AbstractBase
|
||||
source.Pub += _handler;
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
public override bool IsHot => _state.IsHot;
|
||||
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ public sealed class T3 : AbstractBase, IDisposable
|
||||
_publisher.Pub += _handler;
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
/// <summary>
|
||||
/// True if the T3 has been initialized (received at least one value).
|
||||
|
||||
@@ -85,7 +85,7 @@ public sealed class Tema : AbstractBase
|
||||
_handler = Handle;
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the indicator state using the provided history.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Skender.Stock.Indicators;
|
||||
|
||||
@@ -99,7 +99,7 @@ public sealed class Trima : AbstractBase, IDisposable
|
||||
return new TSeries(t, v);
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs args) => Update(args.Value, args.IsNew);
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
|
||||
@@ -73,7 +73,7 @@ public sealed class Usf : AbstractBase
|
||||
source.Pub += _handler;
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
public override bool IsHot => _state.IsHot;
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public sealed class Vidya : AbstractBase, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
public override bool IsHot => _state.BarCount >= _period;
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ public sealed class Wma : AbstractBase, IDisposable
|
||||
return new TSeries(t, v);
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
public override void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
|
||||
@@ -64,7 +64,7 @@ public sealed class Atr : AbstractBase
|
||||
// but we can rely on manual updates or the user subscribing.
|
||||
}
|
||||
|
||||
private void Handle(object? sender, TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
private void Handle(object? sender, in TValueEventArgs e) => Update(e.Value, e.IsNew);
|
||||
|
||||
/// <summary>
|
||||
/// True if the ATR has warmed up and is providing valid results.
|
||||
|
||||
Reference in New Issue
Block a user