namespace QuanTAlib;
///
/// Provides a base implementation for financial indicators in the QuanTAlib library.
///
///
/// 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 data, performing calculations, and publishing results.
///
public abstract class AbstractBase : ITValue
{
public DateTime Time { get; set; }
public double Value { get; set; }
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);
public event ValueSignal Pub = delegate { };
protected int _index;
protected double _lastValidValue;
protected AbstractBase()
{
// Add parameters into constructor if needed
}
///
/// Subscribes to a data source and triggers calculations on new data.
///
/// The class publishing the data.
/// The argument containing the new data point.
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);
///
/// Initializes the indicator's state.
///
public virtual void Init()
{
_index = 0;
_lastValidValue = 0;
}
public virtual TValue Calc(TValue input)
{
Input = input;
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);
}
///
/// Handles error calculations and invalid input values.
///
/// The primary input value to check.
/// The timestamp of the input.
/// Indicates if the input is new.
/// A TValue object with the calculated or last valid value.
///
/// 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.
///
protected virtual TValue HandleErrorCalculations(double value, DateTime time, bool isNew)
{
if (double.IsNaN(value) || double.IsInfinity(value))
{
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));
}
///
/// Handles error calculations for inputs with two values.
///
/// The first input value to check.
/// The second input value to check.
/// The timestamp of the input.
/// Indicates if the input is new.
/// A TValue object with the calculated or last valid value.
///
/// 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.
///
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));
}
///
/// Retrieves the last valid calculated value.
///
/// The last valid value of the indicator.
protected virtual double GetLastValid()
{
return this.Value;
}
///
/// Manages the state of the indicator based on whether a new data point is being processed.
///
/// Indicates whether the current input is a new data point.
protected abstract void ManageState(bool isNew);
///
/// Performs the actual calculation of the indicator value.
///
/// The calculated indicator value.
protected abstract double Calculation();
///
/// Processes the calculated value, updates the indicator's own state,
/// and publishes the result through an event.
///
/// The calculated TValue to process.
/// The processed TValue.
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;
}
}