mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-17 01:58:06 +00:00
Merge branch 'dev'
This commit is contained in:
+153
-80
@@ -1,89 +1,162 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
|
||||
public class Afirma : AbstractBase
|
||||
namespace QuanTAlib
|
||||
{
|
||||
private readonly int Period;
|
||||
private readonly CircularBuffer _buffer;
|
||||
private readonly double _alpha; // Adaptive factor
|
||||
private double _lastAfirma, _p_lastAfirma;
|
||||
private double _lastError, _p_lastError;
|
||||
|
||||
public Afirma(int period, double alpha = 0.1)
|
||||
public class Afirma : AbstractBase
|
||||
{
|
||||
if (period < 1)
|
||||
|
||||
public enum WindowType
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||
Rectangular,
|
||||
Hanning1,
|
||||
Hanning2,
|
||||
Blackman,
|
||||
BlackmanHarris
|
||||
}
|
||||
if (alpha <= 0 || alpha >= 1)
|
||||
|
||||
private readonly int Periods;
|
||||
private readonly int Taps;
|
||||
private readonly WindowType Window;
|
||||
private readonly CircularBuffer _buffer;
|
||||
private readonly double[] _weights;
|
||||
private readonly double _wsum;
|
||||
private readonly double[] _armaBuffer;
|
||||
private readonly int _n;
|
||||
private readonly double _sx2, _sx3, _sx4, _sx5, _sx6, _den;
|
||||
|
||||
public Afirma(int periods, int taps, WindowType window)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(alpha), "Alpha must be between 0 and 1 (exclusive).");
|
||||
if (periods < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(periods), "Periods must be greater than or equal to 1.");
|
||||
}
|
||||
if (taps < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(taps), "Taps must be greater than or equal to 1.");
|
||||
}
|
||||
Periods = periods;
|
||||
Taps = taps;
|
||||
Window = window;
|
||||
WarmupPeriod = taps;
|
||||
_buffer = new CircularBuffer(taps);
|
||||
_weights = new double[taps];
|
||||
_wsum = CalculateWeights();
|
||||
_armaBuffer = new double[taps];
|
||||
_n = (Taps - 1) / 2;
|
||||
|
||||
// Calculate least squares coefficients in the constructor
|
||||
_sx2 = (2 * _n + 1) / 3.0;
|
||||
_sx3 = _n * (_n + 1) / 2.0;
|
||||
_sx4 = _sx2 * (3 * _n * _n + 3 * _n - 1) / 5.0;
|
||||
_sx5 = _sx3 * (2 * _n * _n + 2 * _n - 1) / 3.0;
|
||||
_sx6 = _sx2 * (3 * Math.Pow(_n, 3) * (_n + 2) - 3 * _n + 1) / 7.0;
|
||||
_den = _sx6 * _sx4 / _sx5 - _sx5;
|
||||
|
||||
Name = "Afirma";
|
||||
Init();
|
||||
}
|
||||
Period = period;
|
||||
WarmupPeriod = period;
|
||||
_buffer = new CircularBuffer(period);
|
||||
_alpha = alpha;
|
||||
Name = "Afirma";
|
||||
WarmupPeriod = period;
|
||||
Init();
|
||||
|
||||
public Afirma(object source, int periods, int taps, WindowType window) : this(periods, taps, window)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(IsNew);
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
if (_index >= Taps)
|
||||
{
|
||||
double a0 = _buffer[_n];
|
||||
double a1 = _buffer[_n] - _buffer[_n + 1];
|
||||
double sx2y = 0.0;
|
||||
double sx3y = 0.0;
|
||||
|
||||
for (int i = 0; i <= _n; i++)
|
||||
{
|
||||
sx2y += i * i * _buffer[_n - i];
|
||||
sx3y += i * i * i * _buffer[_n - i];
|
||||
}
|
||||
|
||||
sx2y = 2.0 * sx2y / _n / (_n + 1);
|
||||
sx3y = 2.0 * sx3y / _n / (_n + 1);
|
||||
double p = sx2y - a0 * _sx2 - a1 * _sx3;
|
||||
double q = sx3y - a0 * _sx3 - a1 * _sx4;
|
||||
double a2 = (p * _sx6 / _sx5 - q) / _den;
|
||||
double a3 = (q * _sx4 / _sx5 - p) / _den;
|
||||
|
||||
for (int k = 0; k <= _n; k++)
|
||||
{
|
||||
_armaBuffer[_n - k] = a0 + k * a1 + k * k * a2 + k * k * k * a3;
|
||||
}
|
||||
}
|
||||
|
||||
double result = 0.0;
|
||||
for (int k = 0; k < Taps; k++)
|
||||
{
|
||||
result += _buffer[k] * _weights[k] / _wsum;
|
||||
}
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return result;
|
||||
}
|
||||
|
||||
private double CalculateWeights()
|
||||
{
|
||||
double wsum = 0.0;
|
||||
double centerTap = (Taps - 1) / 2.0;
|
||||
for (int k = 0; k < Taps; k++)
|
||||
{
|
||||
double windowWeight;
|
||||
switch (Window)
|
||||
{
|
||||
case WindowType.Rectangular:
|
||||
windowWeight = 1.0;
|
||||
break;
|
||||
case WindowType.Hanning1:
|
||||
windowWeight = 0.50 - 0.50 * Math.Cos(2.0 * Math.PI * k / (Taps - 1));
|
||||
break;
|
||||
case WindowType.Hanning2:
|
||||
windowWeight = 0.54 - 0.46 * Math.Cos(2.0 * Math.PI * k / (Taps - 1));
|
||||
break;
|
||||
case WindowType.Blackman:
|
||||
windowWeight = 0.42 - 0.50 * Math.Cos(2.0 * Math.PI * k / (Taps - 1)) + 0.08 * Math.Cos(4.0 * Math.PI * k / (Taps - 1));
|
||||
break;
|
||||
case WindowType.BlackmanHarris:
|
||||
windowWeight = 0.35875 - 0.48829 * Math.Cos(2.0 * Math.PI * k / (Taps - 1)) + 0.14128 * Math.Cos(4.0 * Math.PI * k / (Taps - 1)) - 0.01168 * Math.Cos(6.0 * Math.PI * k / (Taps - 1));
|
||||
break;
|
||||
default:
|
||||
windowWeight = 1.0;
|
||||
break;
|
||||
}
|
||||
|
||||
double sincWeight;
|
||||
if (Math.Abs(k - centerTap) < 1e-10)
|
||||
{
|
||||
sincWeight = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
sincWeight = Math.Sin(Math.PI * (k - centerTap) / Periods) / (Math.PI * (k - centerTap) / Periods);
|
||||
}
|
||||
|
||||
_weights[k] = windowWeight * sincWeight;
|
||||
wsum += _weights[k];
|
||||
}
|
||||
return wsum;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Afirma(object source, int period, double alpha = 0.1) : this(period: period, alpha: alpha)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_lastAfirma = 0;
|
||||
_lastError = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
_p_lastAfirma = _lastAfirma;
|
||||
_p_lastError = _lastError;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastAfirma = _p_lastAfirma;
|
||||
_lastError = _p_lastError;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core AFIRMA calculation
|
||||
/// </summary>
|
||||
protected override double Calculation()
|
||||
{
|
||||
double result;
|
||||
ManageState(IsNew);
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
if (_index < Period)
|
||||
{
|
||||
// Use simple average during warmup period
|
||||
result = _buffer.Average();
|
||||
}
|
||||
else
|
||||
{
|
||||
// AFIRMA calculation
|
||||
double sma = _buffer.Average();
|
||||
double error = Input.Value - _lastAfirma;
|
||||
double denominator = Math.Abs(error) + Math.Abs(_lastError);
|
||||
double adaptiveFactor = denominator != 0 ? _alpha * Math.Abs(error) / denominator : _alpha;
|
||||
result = sma + adaptiveFactor * (Input.Value - sma);
|
||||
|
||||
_lastError = error;
|
||||
}
|
||||
|
||||
_lastAfirma = result;
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,6 @@ namespace QuanTAlib;
|
||||
/// The weights are decreasing over the period with p^2 decay, and the most recent data has the heaviest weight.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Smoothness: ★★★★★ (5/5)
|
||||
/// Sensitivity: ★★★☆☆ (3/5)
|
||||
/// Overshooting: ★★★★☆ (4/5)
|
||||
/// Lag: ★★☆☆☆ (2/5)
|
||||
///
|
||||
/// The DWMA is calculated by applying two WMAs in sequence:
|
||||
/// 1. An inner WMA is applied to the input data.
|
||||
/// 2. An outer WMA is then applied to the result of the inner WMA.
|
||||
@@ -28,7 +23,6 @@ namespace QuanTAlib;
|
||||
|
||||
public class Dwma : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly Wma _innerWma;
|
||||
private readonly Wma _outerWma;
|
||||
|
||||
@@ -38,11 +32,10 @@ public class Dwma : AbstractBase
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
|
||||
}
|
||||
_period = period;
|
||||
_innerWma = new Wma(period);
|
||||
_outerWma = new Wma(period);
|
||||
Name = "Wma";
|
||||
WarmupPeriod = 2 * _period - 1;
|
||||
WarmupPeriod = 2 * period - 1;
|
||||
Init();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ namespace QuanTAlib;
|
||||
|
||||
public class Fwma : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly Convolution _convolution;
|
||||
|
||||
public Fwma(int period)
|
||||
@@ -11,8 +10,7 @@ public class Fwma : AbstractBase
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
|
||||
}
|
||||
_period = period;
|
||||
_convolution = new Convolution(GenerateKernel(_period));
|
||||
_convolution = new Convolution(GenerateKernel(period));
|
||||
Name = "Fwma";
|
||||
WarmupPeriod = period;
|
||||
Init();
|
||||
|
||||
+1
-3
@@ -2,7 +2,6 @@ namespace QuanTAlib;
|
||||
|
||||
public class Gma : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly Convolution _convolution;
|
||||
|
||||
public Gma(int period)
|
||||
@@ -11,8 +10,7 @@ public class Gma : AbstractBase
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
|
||||
}
|
||||
_period = period;
|
||||
_convolution = new Convolution(GenerateKernel(_period));
|
||||
_convolution = new Convolution(GenerateKernel(period));
|
||||
Name = "Gma";
|
||||
WarmupPeriod = period;
|
||||
Init();
|
||||
|
||||
+2
-4
@@ -2,7 +2,6 @@ namespace QuanTAlib;
|
||||
|
||||
public class Hma : AbstractBase
|
||||
{
|
||||
private readonly int _period, _sqrtPeriod;
|
||||
private readonly Convolution _wmaHalf, _wmaFull, _wmaFinal;
|
||||
|
||||
public Hma(int period)
|
||||
@@ -11,13 +10,12 @@ public class Hma : AbstractBase
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than or equal to 2.", nameof(period));
|
||||
}
|
||||
_period = period;
|
||||
_sqrtPeriod = (int)Math.Sqrt(period);
|
||||
int _sqrtPeriod = (int)Math.Sqrt(period);
|
||||
_wmaHalf = new Convolution(GenerateWmaKernel(period / 2));
|
||||
_wmaFull = new Convolution(GenerateWmaKernel(period));
|
||||
_wmaFinal = new Convolution(GenerateWmaKernel(_sqrtPeriod));
|
||||
Name = "Hma";
|
||||
WarmupPeriod = _period + _sqrtPeriod - 1;
|
||||
WarmupPeriod = period + _sqrtPeriod - 1;
|
||||
Init();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ public class Mama : AbstractBase
|
||||
public override void Init()
|
||||
{
|
||||
Fama = new TValue();
|
||||
base.Init();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
|
||||
@@ -2,7 +2,6 @@ namespace QuanTAlib;
|
||||
|
||||
public class Sinema : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly Convolution _convolution;
|
||||
|
||||
public Sinema(int period)
|
||||
@@ -11,8 +10,7 @@ public class Sinema : AbstractBase
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
|
||||
}
|
||||
_period = period;
|
||||
_convolution = new Convolution(GenerateKernel(_period));
|
||||
_convolution = new Convolution(GenerateKernel(period));
|
||||
Name = "Sinema";
|
||||
WarmupPeriod = period;
|
||||
Init();
|
||||
|
||||
+1
-8
@@ -4,8 +4,7 @@ public class Sma : AbstractBase
|
||||
{
|
||||
// inherited _index
|
||||
// inherited _value
|
||||
public readonly int Period;
|
||||
private CircularBuffer _buffer;
|
||||
private readonly CircularBuffer _buffer;
|
||||
|
||||
public Sma(int period) : base()
|
||||
{
|
||||
@@ -13,7 +12,6 @@ public class Sma : AbstractBase
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||
}
|
||||
Period = period;
|
||||
WarmupPeriod = period;
|
||||
_buffer = new CircularBuffer(period);
|
||||
Name = "Sma";
|
||||
@@ -28,11 +26,6 @@ public class Sma : AbstractBase
|
||||
}
|
||||
//inhereted public void Sub(object source, in ValueEventArgs args)
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
|
||||
+12
-28
@@ -1,7 +1,6 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class T3 : AbstractBase
|
||||
{
|
||||
public class T3 : AbstractBase {
|
||||
private readonly int _period;
|
||||
private readonly double _vfactor;
|
||||
private readonly bool _useSma;
|
||||
@@ -10,10 +9,8 @@ public class T3 : AbstractBase
|
||||
private double _lastEma1, _lastEma2, _lastEma3, _lastEma4, _lastEma5, _lastEma6;
|
||||
private double _p_lastEma1, _p_lastEma2, _p_lastEma3, _p_lastEma4, _p_lastEma5, _p_lastEma6;
|
||||
|
||||
public T3(int period, double vfactor = 0.7, bool useSma = true) : base()
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
public T3(int period, double vfactor = 0.7, bool useSma = true) {
|
||||
if (period < 1) {
|
||||
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
|
||||
}
|
||||
_period = period;
|
||||
@@ -40,15 +37,12 @@ public class T3 : AbstractBase
|
||||
Init();
|
||||
}
|
||||
|
||||
public T3(object source, int period, double vfactor = 0.7, bool useSma = true) : this(period, vfactor, useSma)
|
||||
{
|
||||
public T3(object source, int period, double vfactor = 0.7, bool useSma = true) : this(period, vfactor, useSma) {
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
public override void Init() {
|
||||
_lastEma1 = _lastEma2 = _lastEma3 = _lastEma4 = _lastEma5 = _lastEma6 = 0;
|
||||
_buffer1.Clear();
|
||||
_buffer2.Clear();
|
||||
@@ -58,10 +52,8 @@ public class T3 : AbstractBase
|
||||
_buffer6.Clear();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
protected override void ManageState(bool isNew) {
|
||||
if (isNew) {
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
_p_lastEma1 = _lastEma1;
|
||||
@@ -70,9 +62,7 @@ public class T3 : AbstractBase
|
||||
_p_lastEma4 = _lastEma4;
|
||||
_p_lastEma5 = _lastEma5;
|
||||
_p_lastEma6 = _lastEma6;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
_lastEma1 = _p_lastEma1;
|
||||
_lastEma2 = _p_lastEma2;
|
||||
_lastEma3 = _p_lastEma3;
|
||||
@@ -83,18 +73,14 @@ public class T3 : AbstractBase
|
||||
}
|
||||
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
protected override double Calculation() {
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
double ema1, ema2, ema3, ema4, ema5, ema6;
|
||||
|
||||
if (_index == 1)
|
||||
{
|
||||
if (_index == 1) {
|
||||
ema1 = ema2 = ema3 = ema4 = ema5 = ema6 = Input.Value;
|
||||
}
|
||||
else if (_index <= _period && _useSma)
|
||||
{
|
||||
} else if (_index <= _period && _useSma) {
|
||||
_buffer1.Add(Input.Value, Input.IsNew);
|
||||
ema1 = _buffer1.Average();
|
||||
_buffer2.Add(ema1, Input.IsNew);
|
||||
@@ -107,9 +93,7 @@ public class T3 : AbstractBase
|
||||
ema5 = _buffer5.Average();
|
||||
_buffer6.Add(ema5, Input.IsNew);
|
||||
ema6 = _buffer6.Average();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ema1 = _k * (Input.Value - _lastEma1) + _lastEma1;
|
||||
ema2 = _k * (ema1 - _lastEma2) + _lastEma2;
|
||||
ema3 = _k * (ema2 - _lastEma3) + _lastEma3;
|
||||
|
||||
@@ -2,7 +2,6 @@ namespace QuanTAlib;
|
||||
|
||||
public class Trima : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly Convolution _convolution;
|
||||
|
||||
public Trima(int period)
|
||||
@@ -11,8 +10,7 @@ public class Trima : AbstractBase
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
|
||||
}
|
||||
_period = period;
|
||||
_convolution = new Convolution(GenerateKernel(_period));
|
||||
_convolution = new Convolution(GenerateKernel(period));
|
||||
Name = "Trima";
|
||||
WarmupPeriod = period;
|
||||
Init();
|
||||
|
||||
@@ -6,7 +6,6 @@ namespace QuanTAlib;
|
||||
|
||||
public class Vidya : AbstractBase
|
||||
{
|
||||
private readonly int _shortPeriod;
|
||||
private readonly int _longPeriod;
|
||||
private readonly double _alpha;
|
||||
private double _lastVIDYA, _p_lastVIDYA;
|
||||
@@ -19,11 +18,12 @@ public class Vidya : AbstractBase
|
||||
{
|
||||
throw new ArgumentException("Short period must be greater than or equal to 1.", nameof(shortPeriod));
|
||||
}
|
||||
_shortPeriod = shortPeriod;
|
||||
_longPeriod = (longPeriod == 0) ? shortPeriod * 4 : longPeriod;
|
||||
_alpha = alpha;
|
||||
WarmupPeriod = _longPeriod;
|
||||
Name = $"Vidya({_shortPeriod},{_longPeriod})";
|
||||
Name = $"Vidya({shortPeriod},{_longPeriod})";
|
||||
_shortBuffer = new CircularBuffer(shortPeriod);
|
||||
_longBuffer = new CircularBuffer(_longPeriod);
|
||||
Init();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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); // Stores the current value of indicator
|
||||
public event ValueSignal Pub = delegate { }; // Publisher of generated values
|
||||
|
||||
protected int _index; //tracking the position of output
|
||||
protected double _lastValidValue;
|
||||
// other _internal vars defined here
|
||||
|
||||
protected AbstractBarBase()
|
||||
{ //add parameters into constructor
|
||||
}
|
||||
|
||||
public void Sub(object source, in TBarEventArgs args) => Calc(args.Bar);
|
||||
|
||||
public virtual void Init()
|
||||
{
|
||||
_index = 0;
|
||||
_lastValidValue = 0;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
protected virtual double GetLastValid()
|
||||
{
|
||||
return this.Value;
|
||||
}
|
||||
protected abstract void ManageState(bool isNew);
|
||||
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;
|
||||
}
|
||||
}
|
||||
+6
-12
@@ -30,15 +30,11 @@ public readonly record struct TBar(DateTime Time, double Open, double High, doub
|
||||
|
||||
public TBar() : this(DateTime.UtcNow, 0, 0, 0, 0, 0) { }
|
||||
public TBar(double Open, double High, double Low, double Close, double Volume, bool IsNew = true) : this(DateTime.UtcNow, Open, High, Low, Close, Volume, IsNew) { }
|
||||
|
||||
// when TBar casts to double, it returns its Close
|
||||
public static implicit operator double(TBar bar) => bar.Close;
|
||||
public static implicit operator DateTime(TBar tv) => tv.Time;
|
||||
|
||||
// castings for sloppy people - a single double injected into a TBar, and a single TValue injected into a TBar
|
||||
public TBar(double value) : this(Time: DateTime.UtcNow, Open: value, High: value, Low: value, Close: value, Volume: value, IsNew: true) { }
|
||||
public TBar(TValue value) : this(Time: value.Time, Open: value.Value, High: value.Value, Low: value.Value, Close: value.Value, Volume: value.Value, IsNew: value.IsNew) { }
|
||||
|
||||
public static implicit operator double(TBar bar) => bar.Close;
|
||||
public static implicit operator DateTime(TBar tv) => tv.Time;
|
||||
public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}: O={Open:F2}, H={High:F2}, L={Low:F2}, C={Close:F2}, V={Volume:F2}]";
|
||||
}
|
||||
|
||||
@@ -70,11 +66,8 @@ public class TBarSeries : List<TBar>
|
||||
public TBarSeries()
|
||||
{
|
||||
this.Name = "Bar";
|
||||
Open = new();
|
||||
High = new();
|
||||
Low = new();
|
||||
Close = new();
|
||||
Volume = new();
|
||||
(Open, High, Low, Close, Volume) = ([], [], [], [], []);
|
||||
|
||||
}
|
||||
public TBarSeries(object source) : this()
|
||||
{
|
||||
@@ -84,7 +77,8 @@ public class TBarSeries : List<TBar>
|
||||
|
||||
public new virtual void Add(TBar bar)
|
||||
{
|
||||
if (bar.IsNew) { base.Add(bar); } else { this[^1] = bar; }
|
||||
if (bar.IsNew || base.Count == 0) { base.Add(bar); }
|
||||
else { this[^1] = bar; }
|
||||
Pub?.Invoke(this, new TBarEventArgs(bar));
|
||||
|
||||
Open.Add(bar.Time, bar.Open, IsNew: bar.IsNew, IsHot: true);
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ public class TSeries : List<TValue>
|
||||
|
||||
public new virtual void Add(TValue tick)
|
||||
{
|
||||
if (tick.IsNew) { base.Add(tick); }
|
||||
if (tick.IsNew || base.Count==0) { base.Add(tick); }
|
||||
else { this[^1] = tick; }
|
||||
Pub?.Invoke(this, new ValueEventArgs(tick));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Title>QuanTAlib</Title>
|
||||
@@ -43,7 +43,7 @@
|
||||
<ItemGroup>
|
||||
<None Include="readme.md" Pack="true" PackagePath=""/>
|
||||
<None Include="..\.github\QuanTAlib2.png" Pack="true" Visible="false" PackagePath=""/>
|
||||
<PackageReference Include="Microsoft.DotNet.Interactive.Formatting" Version="1.0.0-beta.21459.1" />
|
||||
<PackageReference Include="System.Text.Json" Version="9.0.0-rc.1.24431.7" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="TradingPlatform.BusinessLayer">
|
||||
|
||||
@@ -27,11 +27,6 @@ namespace QuanTAlib
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
|
||||
@@ -24,11 +24,6 @@ public class Mode : AbstractBase
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class Atr : AbstractBarBase
|
||||
{
|
||||
private readonly Ema _ma;
|
||||
private double _prevClose, _p_prevClose;
|
||||
|
||||
public Atr(int period) : base()
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||
}
|
||||
_ma = new(1.0/period);
|
||||
WarmupPeriod = _ma.WarmupPeriod;
|
||||
Name = $"ATR({period})";
|
||||
}
|
||||
|
||||
public Atr(object source, int period) : this(period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_ma.Init();
|
||||
_prevClose = double.NaN;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_index++;
|
||||
_p_prevClose = _prevClose;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prevClose = _p_prevClose;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
double trueRange = Math.Max(
|
||||
Math.Max(
|
||||
Input.High - Input.Low,
|
||||
Math.Abs(Input.High - _prevClose)
|
||||
),
|
||||
Math.Abs(Input.Low - _prevClose)
|
||||
);
|
||||
if (_index < 2)
|
||||
{
|
||||
trueRange = Input.High - Input.Low;
|
||||
}
|
||||
|
||||
TValue emaTrueRange = _ma.Calc(new TValue(Input.Time, trueRange, Input.IsNew));
|
||||
IsHot = _ma.IsHot;
|
||||
_prevClose = Input.Close;
|
||||
|
||||
return emaTrueRange.Value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user