Merge branch 'dev'

This commit is contained in:
Miha Kralj
2024-09-30 09:07:08 -07:00
95 changed files with 7982 additions and 2375 deletions
+153 -80
View File
@@ -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;
}
}
}
+1 -8
View File
@@ -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();
}
+1 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
-1
View File
@@ -40,7 +40,6 @@ public class Mama : AbstractBase
public override void Init()
{
Fama = new TValue();
base.Init();
}
protected override void ManageState(bool isNew)
+1 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+1 -3
View File
@@ -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();
+3 -3
View File
@@ -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();
}