event tests

This commit is contained in:
Miha Kralj
2024-10-08 09:25:01 -07:00
parent b7b5a4a1bf
commit af234594cc
15 changed files with 391 additions and 200 deletions
+8 -4
View File
@@ -5,22 +5,26 @@ namespace QuanTAlib;
public class Frama : AbstractBase
{
private readonly int _period;
private readonly double _fc;
private readonly CircularBuffer _buffer;
private double _lastFrama;
private double _prevLastFrama;
public Frama(int period, double fc = 0.5)
public Frama(int period)
{
if (period < 2)
throw new ArgumentException("Period must be at least 2", nameof(period));
_period = period;
_fc = fc;
_buffer = new CircularBuffer(period);
WarmupPeriod = period;
}
public Frama(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
public override void Init()
{
base.Init();
@@ -95,4 +99,4 @@ public class Frama : AbstractBase
{
return _lastFrama;
}
}
}
+6
View File
@@ -35,6 +35,12 @@ public class Jma : AbstractBase
Init();
}
public Jma(object source, int period, double phase = 0, int vshort = 10) : this(period, phase, vshort)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
public override void Init()
{
_upperBand = _lowerBand = _prevMa1 = _prevDet0 = _prevDet1 = _prevJma = 0.0;
+5 -1
View File
@@ -25,7 +25,11 @@ public class Rema : AbstractBase
WarmupPeriod = period;
Init();
}
public Rema(object source, int period, double lambda = 0.5) : this(period, lambda)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
public override void Init()
{
base.Init();
-90
View File
@@ -1,90 +0,0 @@
namespace QuanTAlib;
/// <summary>
/// Provides a base implementation for financial indicators that work with bar data in the QuanTAlib library.
/// </summary>
/// <remarks>
/// This abstract class implements the iTValue interface and defines common properties
/// and methods used by inheriting indicator types. It handles the basic flow of
/// receiving bar data, performing calculations, and publishing results.
/// </remarks>
public abstract class AbstractBarBase : ITValue {
public DateTime Time { get; set; }
public double Value { get; set; }
public bool IsNew { get; set; }
public bool IsHot { get; set; }
public TBar Input { get; set; }
public String Name { get; set; } = "";
public int WarmupPeriod { get; set; }
public TValue Tick => new(Time, Value, IsNew, IsHot);
public event ValueSignal Pub = delegate { };
protected int _index;
protected double _lastValidValue;
protected AbstractBarBase() {
// Add parameters into constructor if needed
}
/// <summary>
/// Subscribes to bar data updates.
/// </summary>
/// <param name="source">The source of the bar data.</param>
/// <param name="args">The event arguments containing the bar data.</param>
public void Sub(object source, in TBarEventArgs args) => Calc(args.Bar);
/// <summary>
/// Initializes the indicator's state.
/// </summary>
public virtual void Init() {
_index = 0;
_lastValidValue = 0;
}
/// <summary>
/// Calculates the indicator value based on the input bar.
/// </summary>
/// <param name="input">The input bar data.</param>
/// <returns>A TValue containing the calculated result.</returns>
public virtual TValue Calc(TBar input) {
Input = input;
if (double.IsNaN(input.Close) || double.IsInfinity(input.Close)) {
return Process(new TValue(Time: input.Time, Value: GetLastValid(), IsNew: input.IsNew, IsHot: true));
}
this.Value = Calculation();
return Process(new TValue(Time: Input.Time, Value: this.Value, IsNew: Input.IsNew, IsHot: this.IsHot));
}
/// <summary>
/// Retrieves the last valid calculated value.
/// </summary>
/// <returns>The last valid value of the indicator.</returns>
protected virtual double GetLastValid() {
return this.Value;
}
/// <summary>
/// Manages the state of the indicator based on whether a new bar is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new bar.</param>
protected abstract void ManageState(bool isNew);
/// <summary>
/// Performs the actual calculation of the indicator value.
/// </summary>
/// <returns>The calculated indicator value.</returns>
protected abstract double Calculation();
/// <summary>
/// Processes the calculated value, updates the indicator's own state,
/// and publishes the result through an event.
/// </summary>
/// <param name="value">The calculated TValue to process.</param>
/// <returns>The processed TValue.</returns>
protected virtual TValue Process(TValue value) {
this.Time = value.Time;
this.Value = value.Value;
this.IsNew = value.IsNew;
this.IsHot = value.IsHot;
Pub?.Invoke(this, new ValueEventArgs(value));
return value;
}
}
+71 -12
View File
@@ -15,6 +15,9 @@ public abstract class AbstractBase : ITValue
public bool IsNew { get; set; }
public bool IsHot { get; set; }
public TValue Input { get; set; }
public TValue Input2 { get; set; }
public TBar BarInput { get; set; }
public TBar BarInput2 { get; set; }
public String Name { get; set; } = "";
public int WarmupPeriod { get; set; }
public TValue Tick => new(Time, Value, IsNew, IsHot);
@@ -34,6 +37,11 @@ public abstract class AbstractBase : ITValue
/// <param name="args">The argument containing the new data point.</param>
public void Sub(object source, in ValueEventArgs args) => Calc(args.Tick);
public void Sub(object source1, object source2, in ValueEventArgs args1, in ValueEventArgs args2) =>
Calc(args1.Tick, args2.Tick);
public void Sub(object source, in TBarEventArgs args) => Calc(args.Bar);
/// <summary>
/// Initializes the indicator's state.
/// </summary>
@@ -43,24 +51,75 @@ public abstract class AbstractBase : ITValue
_lastValidValue = 0;
}
/// <summary>
/// Calculates the indicator value based on the input.
/// </summary>
/// <param name="input">The input value for the calculation.</param>
/// <returns>A TValue representing the calculated indicator value.</returns>
/// <remarks>
/// This method calls the specific Calculation() method where the actual implementation is.
/// If the input value is NaN or infinity, it returns the last valid value instead.
/// </remarks>
public virtual TValue Calc(TValue input)
{
Input = input;
if (double.IsNaN(input.Value) || double.IsInfinity(input.Value))
Input2 = new(Time: Input.Time, Value: double.NaN, IsNew: Input.IsNew, IsHot: Input.IsHot);
return HandleErrorCalculations(input.Value, input.Time, input.IsNew);
}
public virtual TValue Calc(TBar barInput)
{
BarInput = barInput;
return HandleErrorCalculations(barInput.Close, barInput.Time, barInput.IsNew);
}
public virtual TValue Calc(TValue input1, TValue input2)
{
Input = input1;
Input2 = input2;
return HandleErrorCalculations(input1.Value, input2.Value, input1.Time, input1.IsNew);
}
public virtual TValue Calc(TBar input1, TBar input2)
{
BarInput = input1;
BarInput2 = input2;
return HandleErrorCalculations(input1.Close, input2.Close, input1.Time, input1.IsNew);
}
/// <summary>
/// Handles error calculations and invalid input values.
/// </summary>
/// <param name="value">The primary input value to check.</param>
/// <param name="time">The timestamp of the input.</param>
/// <param name="isNew">Indicates if the input is new.</param>
/// <returns>A TValue object with the calculated or last valid value.</returns>
/// <remarks>
/// This method checks for NaN or infinity in the input value. If an invalid value is detected,
/// it returns the last valid value. Otherwise, it proceeds with the calculation.
/// </remarks>
protected virtual TValue HandleErrorCalculations(double value, DateTime time, bool isNew)
{
if (double.IsNaN(value) || double.IsInfinity(value))
{
return Process(new TValue(input.Time, GetLastValid(), input.IsNew, input.IsHot));
return Process(new TValue(time, GetLastValid(), isNew, this.IsHot));
}
this.Value = Calculation();
return Process(new TValue(Time: Input.Time, Value: this.Value, IsNew: Input.IsNew, IsHot: this.IsHot));
return Process(new TValue(Time: time, Value: this.Value, IsNew: isNew, IsHot: this.IsHot));
}
/// <summary>
/// Handles error calculations for inputs with two values.
/// </summary>
/// <param name="value1">The first input value to check.</param>
/// <param name="value2">The second input value to check.</param>
/// <param name="time">The timestamp of the input.</param>
/// <param name="isNew">Indicates if the input is new.</param>
/// <returns>A TValue object with the calculated or last valid value.</returns>
/// <remarks>
/// This method checks for NaN or infinity in both input values. If any invalid value is detected,
/// it returns the last valid value. Otherwise, it proceeds with the calculation.
/// </remarks>
protected virtual TValue HandleErrorCalculations(double value1, double value2, DateTime time, bool isNew)
{
if (double.IsNaN(value1) || double.IsInfinity(value1) ||
double.IsNaN(value2) || double.IsInfinity(value2))
{
return Process(new TValue(time, GetLastValid(), isNew, this.IsHot));
}
this.Value = Calculation();
return Process(new TValue(Time: time, Value: this.Value, IsNew: isNew, IsHot: this.IsHot));
}
/// <summary>
+2 -2
View File
@@ -52,12 +52,12 @@ public class TSeries : List<TValue>
var pubEvent = source.GetType().GetEvent("Pub");
if (pubEvent != null)
{
/*
var nameProperty = source.GetType().GetProperty("Name");
if (nameProperty != null) {
Name = nameProperty.GetValue(nameProperty)?.ToString()!;
}
*/
pubEvent.AddEventHandler(source, new ValueSignal(Sub));
}
}
+7 -7
View File
@@ -8,7 +8,7 @@ namespace QuanTAlib;
/// of the true range. The true range is the greatest of: current high - current low,
/// absolute value of current high - previous close, or absolute value of current low - previous close.
/// </remarks>
public class Atr : AbstractBarBase {
public class Atr : AbstractBase {
private readonly Ema _ma;
private double _prevClose, _p_prevClose;
@@ -72,22 +72,22 @@ public class Atr : AbstractBarBase {
/// as the true range.
/// </remarks>
protected override double Calculation() {
ManageState(Input.IsNew);
ManageState(BarInput.IsNew);
double trueRange = Math.Max(
Math.Max(
Input.High - Input.Low,
Math.Abs(Input.High - _prevClose)
BarInput.High - BarInput.Low,
Math.Abs(BarInput.High - _prevClose)
),
Math.Abs(Input.Low - _prevClose)
Math.Abs(BarInput.Low - _prevClose)
);
if (_index < 2) {
trueRange = Input.High - Input.Low;
trueRange = BarInput.High - BarInput.Low;
}
TValue emaTrueRange = _ma.Calc(new TValue(Input.Time, trueRange, Input.IsNew));
IsHot = _ma.IsHot;
_prevClose = Input.Close;
_prevClose = BarInput.Close;
return emaTrueRange.Value;
}