mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-07 13:37:44 +00:00
dev branch
This commit is contained in:
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
period = 10
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
namespace QuanTAlib;
|
||||
|
||||
/// <summary>
|
||||
/// ALMA: Arnaud Legoux Moving Average
|
||||
/// Uses the curve of the Normal (Gauss) distribution. This moving average reduces lag
|
||||
/// of the data in conjunction with smoothing to reduce noise.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Smoothness: ★★★★☆ (4/5)
|
||||
/// Sensitivity: ★★★★☆ (4/5)
|
||||
/// Overshooting: ★★★★☆ (4/5)
|
||||
/// Lag: ★★★★★ (5/5)
|
||||
///
|
||||
/// Validation:
|
||||
/// Skender.Stock.Indicators
|
||||
/// </remarks>
|
||||
|
||||
public class Alma : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly double _offset;
|
||||
private readonly double _sigma;
|
||||
private CircularBuffer? _buffer;
|
||||
private CircularBuffer? _weight;
|
||||
private double _norm;
|
||||
|
||||
/// <param name="period">The number of data points used in the ALMA calculation.</param>
|
||||
/// <param name="offset">Controls the smoothness and high-frequency filtering. Default is 0.85.</param>
|
||||
/// <param name="sigma">Controls the shape of the Gaussian distribution. Default is 6.</param>
|
||||
/// <exception cref="ArgumentException">Thrown when period is less than 1.</exception>
|
||||
public Alma(int period, double offset = 0.85, double sigma = 6)
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
|
||||
}
|
||||
_period = period;
|
||||
_offset = offset;
|
||||
_sigma = sigma;
|
||||
WarmupPeriod = period;
|
||||
Name = "Alma";
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <param name="source">The data source object that publishes updates.</param>
|
||||
/// <param name="period">The number of data points used in the ALMA calculation.</param>
|
||||
/// <param name="offset">Controls the smoothness and high-frequency filtering. Default is 0.85.</param>
|
||||
/// <param name="sigma">Controls the shape of the Gaussian distribution. Default is 6.</param>
|
||||
public Alma(object source, int period, double offset = 0.85, double sigma = 6) : this(period, offset, sigma)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_buffer = new CircularBuffer(_period);
|
||||
_weight = new CircularBuffer(_period);
|
||||
_norm = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_index++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the core ALMA calculation. Called from parent abstractBase Calc()
|
||||
/// </summary>
|
||||
/// <returns>The calculated ALMA value.</returns>
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_buffer!.Add(Input.Value, Input.IsNew);
|
||||
if (_weight!.Count < _buffer.Count)
|
||||
{
|
||||
for (var i = 0; i < _buffer.Count - _weight.Count; i++)
|
||||
{
|
||||
_weight.Add(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
if (_buffer.Count <= _period)
|
||||
{
|
||||
UpdateWeights();
|
||||
}
|
||||
|
||||
double weightedSum = 0;
|
||||
for (var i = 0; i < _buffer.Count; i++)
|
||||
{
|
||||
weightedSum += _weight[i] * _buffer[i];
|
||||
}
|
||||
|
||||
double result = weightedSum / _norm;
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void UpdateWeights()
|
||||
{
|
||||
int len = _buffer!.Count;
|
||||
_norm = 0;
|
||||
double m = _offset * (len - 1);
|
||||
double s = len / _sigma;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
double wt = Math.Exp(-((i - m) * (i - m)) / (2 * s * s));
|
||||
_weight![i] = wt;
|
||||
_norm += wt;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
/// <summary>
|
||||
/// DEMA: Double Exponential Moving Average
|
||||
/// DEMA reduces the lag of a traditional EMA by applying a second EMA over EMA.
|
||||
/// It responds more quickly to price changes than a standard EMA while maintaining
|
||||
/// smoothness, at the cost of overshooting the signal line.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Smoothness: ★★★☆☆ (3/5)
|
||||
/// Sensitivity: ★★★★☆ (4/5)
|
||||
/// Overshooting: ★★★☆☆ (3/5)
|
||||
/// Lag: ★★★★☆ (4/5)
|
||||
///
|
||||
/// Sources:
|
||||
/// https://www.investopedia.com/terms/d/double-exponential-moving-average.asp
|
||||
/// https://www.tradingview.com/support/solutions/43000502589-double-exponential-moving-average-dema/
|
||||
///
|
||||
/// Validation:
|
||||
/// Skender.Stock.Indicators
|
||||
/// </remarks>
|
||||
public class Dema : AbstractBase
|
||||
{
|
||||
// inherited _index
|
||||
// inherited _value
|
||||
private readonly int _period;
|
||||
private double _lastEma1, _p_lastEma1;
|
||||
private double _lastEma2, _p_lastEma2;
|
||||
private double _k, _e, _p_e;
|
||||
|
||||
public Dema(int period)
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||
}
|
||||
_period = period;
|
||||
Name = "Dema";
|
||||
double percentile = 0.85; //targeting 85th percentile of correctness of converging EMA
|
||||
WarmupPeriod = (int)Math.Ceiling(-period * Math.Log(1 - percentile));
|
||||
Init();
|
||||
}
|
||||
|
||||
public Dema(object source, int period) : this(period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
//inhereted public void Sub(object source, in ValueEventArgs args)
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_k = 2.0 / (_period + 1);
|
||||
_e = 1.0;
|
||||
_lastEma1 = 0;
|
||||
_lastEma2 = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_p_lastEma1 = _lastEma1;
|
||||
_p_lastEma2 = _lastEma2;
|
||||
_p_e = _e;
|
||||
_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastEma1 = _p_lastEma1;
|
||||
_lastEma2 = _p_lastEma2;
|
||||
_e = _p_e;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core DEMA calculation
|
||||
/// </summary>
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
double result, _ema1, _ema2;
|
||||
// compensator for early ema values
|
||||
_e = (_e > 1e-10) ? (1 - _k) * _e : 0;
|
||||
double _invE = (_e > 1e-10) ? 1 / (1 - _e) : 1;
|
||||
|
||||
// Calculate EMA1
|
||||
_ema1 = _k * (Input.Value - _lastEma1) + _lastEma1;
|
||||
|
||||
// Calculate EMA2 using compensatedEma1
|
||||
_ema2 = _k * (_ema1 * _invE - _lastEma2) + _lastEma2;
|
||||
|
||||
// Calculate DEMA
|
||||
double _dema = 2 * _ema1 * _invE - (_ema2 * _invE);
|
||||
|
||||
result = _dema;
|
||||
_lastEma1 = _ema1;
|
||||
_lastEma2 = _ema2;
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
/// <summary>
|
||||
/// DSMA: Deviation Scaled Moving Average
|
||||
/// Adaptive moving average that adjusts its smoothing factor based on the volatility of the input data.
|
||||
/// It aims to be more responsive during trending periods and more stable during ranging periods.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Smoothness: ★★★★☆ (4/5)
|
||||
/// Sensitivity: ★★★★☆ (4/5)
|
||||
/// Overshooting: ★★★★☆ (4/5)
|
||||
/// Lag: ★★★★☆ (4/5)
|
||||
///
|
||||
/// The DSMA uses a SuperSmoother filter to reduce noise and a dynamic alpha calculation based on the
|
||||
/// scaled deviation of the input data. This allows it to adapt to changing market conditions.
|
||||
///
|
||||
/// The algorithm involves these main steps:
|
||||
/// 1. Apply a SuperSmoother filter to the zero-mean input data.
|
||||
/// 2. Calculate the Root Mean Square (RMS) of the filtered data.
|
||||
/// 3. Scale the filtered data by the RMS to get a measure in terms of standard deviations.
|
||||
/// 4. Use the scaled deviation to calculate an adaptive alpha for the moving average.
|
||||
///
|
||||
/// Source:
|
||||
/// https://www.mesasoftware.com/papers/DEVIATION%20SCALED%20MOVING%20AVERAGE.pdf
|
||||
/// </remarks>
|
||||
|
||||
public class Dsma : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly CircularBuffer _buffer;
|
||||
private readonly double _a1, _b1, _c1, _c2, _c3;
|
||||
private double _lastDsma, _p_lastDsma;
|
||||
private double _filt, _filt1, _filt2, _zeros, _zeros1;
|
||||
private double _p_filt, _p_filt1, _p_filt2, _p_zeros, _p_zeros1;
|
||||
private bool _isInit, _p_isInit;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Dsma"/> class.
|
||||
/// </summary>
|
||||
/// <param name="period">The number of data points used in the DSMA calculation.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
|
||||
public Dsma(int period)
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||
}
|
||||
_period = period;
|
||||
_buffer = new CircularBuffer(period);
|
||||
|
||||
// SuperSmoother filter coefficients
|
||||
_a1 = Math.Exp(-1.414 * Math.PI / (0.5 * period));
|
||||
_b1 = 2 * _a1 * Math.Cos(1.414 * Math.PI / (0.5 * period));
|
||||
_c2 = _b1;
|
||||
_c3 = -_a1 * _a1;
|
||||
_c1 = 1 - _c2 - _c3;
|
||||
|
||||
Name = "Dsma";
|
||||
WarmupPeriod = period * 2; // A conservative estimate
|
||||
Init();
|
||||
}
|
||||
|
||||
public Dsma(object source, int period) : this(period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_lastDsma = 0;
|
||||
_filt = _filt1 = _filt2 = 0;
|
||||
_zeros = _zeros1 = 0;
|
||||
_isInit = false;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_p_lastDsma = _lastDsma;
|
||||
_p_isInit = _isInit;
|
||||
_p_zeros = _zeros;
|
||||
_p_zeros1 = _zeros1;
|
||||
_p_filt = _filt;
|
||||
_p_filt1 = _filt1;
|
||||
_p_filt2 = _filt2;
|
||||
_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastDsma = _p_lastDsma;
|
||||
_isInit = _p_isInit;
|
||||
_zeros = _p_zeros;
|
||||
_zeros1 = _p_zeros1;
|
||||
_filt = _p_filt;
|
||||
_filt1 = _p_filt1;
|
||||
_filt2 = _p_filt2;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
if (!_isInit)
|
||||
{
|
||||
_lastDsma = Input.Value;
|
||||
_isInit = true;
|
||||
return _lastDsma;
|
||||
}
|
||||
|
||||
// Produce nominal zero mean
|
||||
_zeros = Input.Value - _lastDsma;
|
||||
|
||||
// SuperSmoother Filter
|
||||
_filt = _c1 * (_zeros + _zeros1) / 2 + _c2 * _filt1 + _c3 * _filt2;
|
||||
|
||||
// Update buffer for RMS calculation
|
||||
_buffer.Add(_filt * _filt, Input.IsNew);
|
||||
|
||||
// Compute RMS (Root Mean Square)
|
||||
double rms = Math.Sqrt(_buffer.Sum() / _period);
|
||||
|
||||
// Rescale Filt in terms of Standard Deviations
|
||||
double scaledFilt = rms != 0 ? _filt / rms : 0;
|
||||
|
||||
// Calculate adaptive alpha
|
||||
double alpha = Math.Abs(scaledFilt) * 5 / _period;
|
||||
|
||||
// DSMA calculation
|
||||
double dsma = alpha * Input.Value + (1 - alpha) * _lastDsma;
|
||||
|
||||
// Update state variables
|
||||
_zeros1 = _zeros;
|
||||
_filt2 = _filt1;
|
||||
_filt1 = _filt;
|
||||
_lastDsma = dsma;
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return dsma;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
/// <summary>
|
||||
/// EMA: Exponential Moving Average
|
||||
/// EMA needs very short history buffer and calculates the EMA value using just the
|
||||
/// previous EMA value. The weight of the new datapoint (alpha) is alpha = 2 / (period + 1)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Smoothness: ★★★☆☆ (3/5)
|
||||
/// Sensitivity: ★★★★☆ (4/5)
|
||||
/// Overshooting: ★★★★★ (5/5)
|
||||
/// Lag: ★★★☆☆ (3/5)
|
||||
///
|
||||
/// Key characteristics:
|
||||
/// - Uses no buffer, relying only on the previous EMA value.
|
||||
/// - The weight of new data points is calculated as alpha = 2 / (period + 1).
|
||||
/// - Provides a balance between responsiveness and smoothing. No overshooting. Significant lag
|
||||
///
|
||||
/// Calculation method:
|
||||
/// This implementation can use SMA for the first Period bars as a seeding value for EMA when useSma is true.
|
||||
///
|
||||
/// Sources:
|
||||
/// - https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages
|
||||
/// - https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp
|
||||
/// - https://blog.fugue88.ws/archives/2017-01/The-correct-way-to-start-an-Exponential-Moving-Average-EMA
|
||||
/// </remarks>
|
||||
|
||||
public class Ema : AbstractBase
|
||||
{
|
||||
// inherited _index
|
||||
// inherited _value
|
||||
private readonly int _period;
|
||||
private CircularBuffer _sma;
|
||||
private double _lastEma, _p_lastEma;
|
||||
private double _k, _e, _p_e;
|
||||
private bool _isInit, _p_isInit, _useSma;
|
||||
|
||||
public Ema(int period, bool useSma = true)
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||
}
|
||||
_period = period;
|
||||
_k = 2.0 / (_period + 1);
|
||||
_useSma = useSma;
|
||||
_sma = new(period);
|
||||
Name = "Ema";
|
||||
WarmupPeriod = (int)Math.Ceiling(Math.Log(0.05) / Math.Log(1 - _k)); //95th percentile
|
||||
Init();
|
||||
}
|
||||
|
||||
public Ema(double alpha) : base()
|
||||
{
|
||||
_k = alpha;
|
||||
_useSma = false;
|
||||
_sma = new(1);
|
||||
_period = 1;
|
||||
WarmupPeriod = (int)Math.Ceiling(Math.Log(0.05) / Math.Log(1 - _k)); //95th percentile
|
||||
Init();
|
||||
}
|
||||
|
||||
public Ema(object source, int period, bool useSma = true) : this(period, useSma)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
//inhereted public void Sub(object source, in ValueEventArgs args)
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_e = 1.0;
|
||||
_lastEma = 0;
|
||||
_isInit = false;
|
||||
_p_isInit = false;
|
||||
_sma = new(_period);
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_p_lastEma = _lastEma;
|
||||
_p_isInit = _isInit;
|
||||
_p_e = _e;
|
||||
_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastEma = _p_lastEma;
|
||||
_isInit = _p_isInit;
|
||||
_e = _p_e;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core EMA calculation
|
||||
/// </summary>
|
||||
protected override double Calculation()
|
||||
{
|
||||
double result, _ema;
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
// when _UseSma == true, use SMA calculation until we have enough data points
|
||||
if (!_isInit && _useSma)
|
||||
{
|
||||
_sma.Add(Input.Value, Input.IsNew);
|
||||
_ema = _sma.Average();
|
||||
result = _ema;
|
||||
if (_index >= _period)
|
||||
{
|
||||
_isInit = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// compensator for early ema values
|
||||
_e = (_e > 1e-10) ? (1 - _k) * _e : 0;
|
||||
|
||||
_ema = _k * (Input.Value - _lastEma) + _lastEma;
|
||||
|
||||
// _useSma decides if we use compensator or not
|
||||
result = (_useSma || _e == 0) ? _ema : _ema / (1 - _e);
|
||||
}
|
||||
_lastEma = _ema;
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
|
||||
namespace QuanTAlib
|
||||
{
|
||||
public class Frama : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly double _fc;
|
||||
private CircularBuffer _buffer;
|
||||
private double _lastFrama;
|
||||
private double _prevLastFrama;
|
||||
|
||||
public Frama(int period, double fc = 0.5)
|
||||
{
|
||||
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 override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_buffer.Clear();
|
||||
_lastFrama = 0;
|
||||
_prevLastFrama = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_prevLastFrama = _lastFrama;
|
||||
_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastFrama = _prevLastFrama;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
if (_buffer.Count < _period)
|
||||
{
|
||||
_lastFrama = _buffer.Average();
|
||||
return _lastFrama;
|
||||
}
|
||||
|
||||
int half = _period / 2;
|
||||
double hh = double.MinValue, ll = double.MaxValue;
|
||||
double hh1 = double.MinValue, ll1 = double.MaxValue;
|
||||
double hh2 = double.MinValue, ll2 = double.MaxValue;
|
||||
|
||||
for (int i = 0; i < _period; i++)
|
||||
{
|
||||
double price = _buffer[i];
|
||||
hh = Math.Max(hh, price);
|
||||
ll = Math.Min(ll, price);
|
||||
|
||||
if (i < half)
|
||||
{
|
||||
hh1 = Math.Max(hh1, price);
|
||||
ll1 = Math.Min(ll1, price);
|
||||
}
|
||||
else
|
||||
{
|
||||
hh2 = Math.Max(hh2, price);
|
||||
ll2 = Math.Min(ll2, price);
|
||||
}
|
||||
}
|
||||
|
||||
double n1 = (hh - ll) / _period;
|
||||
double n2 = (hh1 - ll1 + hh2 - ll2) / (_period / 2);
|
||||
|
||||
double d = (Math.Log(n2 + double.Epsilon) - Math.Log(n1 + double.Epsilon)) / Math.Log(2);
|
||||
|
||||
double alpha = Math.Exp(-4.6 * (d - 1));
|
||||
alpha = Math.Max(Math.Min(alpha, 1), 0.01); // Ensure alpha is between 0.01 and 1
|
||||
|
||||
_lastFrama = alpha * (Input.Value - _lastFrama) + _lastFrama;
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return _lastFrama;
|
||||
}
|
||||
|
||||
protected override double GetLastValid()
|
||||
{
|
||||
return _lastFrama;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
//not working yet
|
||||
//TODO consistency test
|
||||
|
||||
using QuanTAlib;
|
||||
|
||||
public class Htit : AbstractBase
|
||||
{
|
||||
private readonly CircularBuffer _priceBuffer = new(7);
|
||||
private readonly CircularBuffer _spBuffer = new(7);
|
||||
private readonly CircularBuffer _dtBuffer = new(7);
|
||||
private readonly CircularBuffer _i1Buffer = new(7);
|
||||
private readonly CircularBuffer _q1Buffer = new(7);
|
||||
private readonly CircularBuffer _i2Buffer = new(2);
|
||||
private readonly CircularBuffer _q2Buffer = new(2);
|
||||
private readonly CircularBuffer _reBuffer = new(2);
|
||||
private readonly CircularBuffer _imBuffer = new(2);
|
||||
private readonly CircularBuffer _pdBuffer = new(2);
|
||||
private readonly CircularBuffer _sdBuffer = new(2);
|
||||
private readonly CircularBuffer _itBuffer = new(4);
|
||||
|
||||
private double _lastPd = 0;
|
||||
private double _p_lastPd = 0;
|
||||
|
||||
public Htit()
|
||||
{
|
||||
Name = "Htit";
|
||||
WarmupPeriod = 12;
|
||||
}
|
||||
|
||||
public Htit(object source) : this()
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_p_lastPd = _lastPd;
|
||||
_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastPd = _p_lastPd;
|
||||
}
|
||||
}
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
double pr = Input.Value;
|
||||
_priceBuffer.Add(pr, Input.IsNew);
|
||||
|
||||
if (_index <= 5)
|
||||
{
|
||||
_spBuffer.Add(0, Input.IsNew);
|
||||
_dtBuffer.Add(0, Input.IsNew);
|
||||
_i1Buffer.Add(0, Input.IsNew);
|
||||
_q1Buffer.Add(0, Input.IsNew);
|
||||
_i2Buffer.Add(0, Input.IsNew);
|
||||
_q2Buffer.Add(0, Input.IsNew);
|
||||
_reBuffer.Add(0, Input.IsNew);
|
||||
_imBuffer.Add(0, Input.IsNew);
|
||||
_pdBuffer.Add(0, Input.IsNew);
|
||||
_sdBuffer.Add(0, Input.IsNew);
|
||||
_itBuffer.Add(pr, Input.IsNew);
|
||||
return pr;
|
||||
}
|
||||
|
||||
double adj = (0.075 * _lastPd) + 0.54;
|
||||
|
||||
// Smooth and detrender
|
||||
double sp = ((4 * _priceBuffer[0]) + (3 * _priceBuffer[1]) + (2 * _priceBuffer[2]) + _priceBuffer[3]) / 10;
|
||||
_spBuffer.Add(sp, Input.IsNew);
|
||||
|
||||
double dt = ((0.0962 * _spBuffer[0]) + (0.5769 * _spBuffer[2]) - (0.5769 * _spBuffer[4]) - (0.0962 * _spBuffer[6])) * adj;
|
||||
_dtBuffer.Add(dt, Input.IsNew);
|
||||
|
||||
// In-phase and quadrature
|
||||
double q1 = ((0.0962 * _dtBuffer[0]) + (0.5769 * _dtBuffer[2]) - (0.5769 * _dtBuffer[4]) - (0.0962 * _dtBuffer[6])) * adj;
|
||||
_q1Buffer.Add(q1, Input.IsNew);
|
||||
|
||||
double i1 = _dtBuffer[3];
|
||||
_i1Buffer.Add(i1, Input.IsNew);
|
||||
|
||||
// Advance the phases by 90 degrees
|
||||
double jI = ((0.0962 * _i1Buffer[0]) + (0.5769 * _i1Buffer[2]) - (0.5769 * _i1Buffer[4]) - (0.0962 * _i1Buffer[6])) * adj;
|
||||
double jQ = ((0.0962 * _q1Buffer[0]) + (0.5769 * _q1Buffer[2]) - (0.5769 * _q1Buffer[4]) - (0.0962 * _q1Buffer[6])) * adj;
|
||||
|
||||
// Phasor addition for 3-bar averaging
|
||||
double i2 = i1 - jQ;
|
||||
double q2 = q1 + jI;
|
||||
|
||||
i2 = (0.2 * i2) + (0.8 * _i2Buffer[0]);
|
||||
q2 = (0.2 * q2) + (0.8 * _q2Buffer[0]);
|
||||
|
||||
_i2Buffer.Add(i2, Input.IsNew);
|
||||
_q2Buffer.Add(q2, Input.IsNew);
|
||||
|
||||
// Homodyne discriminator
|
||||
double re = (i2 * _i2Buffer[1]) + (q2 * _q2Buffer[1]);
|
||||
double im = (i2 * _q2Buffer[1]) - (q2 * _i2Buffer[1]);
|
||||
|
||||
re = (0.2 * re) + (0.8 * _reBuffer[0]);
|
||||
im = (0.2 * im) + (0.8 * _imBuffer[0]);
|
||||
|
||||
_reBuffer.Add(re, Input.IsNew);
|
||||
_imBuffer.Add(im, Input.IsNew);
|
||||
|
||||
// Calculate period
|
||||
double pd = (im != 0 && re != 0) ? 2 * Math.PI / Math.Atan(im / re) : 0;
|
||||
|
||||
// Adjust period to thresholds
|
||||
pd = (pd > 1.5 * _lastPd) ? 1.5 * _lastPd : pd;
|
||||
pd = (pd < 0.67 * _lastPd) ? 0.67 * _lastPd : pd;
|
||||
pd = (pd < 6) ? 6 : pd;
|
||||
pd = (pd > 50) ? 50 : pd;
|
||||
|
||||
// Smooth the period
|
||||
pd = (0.2 * pd) + (0.8 * _lastPd);
|
||||
_pdBuffer.Add(pd, Input.IsNew);
|
||||
|
||||
double sd = (0.33 * pd) + (0.67 * _sdBuffer[0]);
|
||||
_sdBuffer.Add(sd, Input.IsNew);
|
||||
|
||||
// Smooth dominant cycle period
|
||||
int dcPeriods = (int)(sd + 0.5);
|
||||
double sumPr = _priceBuffer.GetSpan().Slice(0, Math.Min(dcPeriods, _priceBuffer.Count)).ToArray().Sum();
|
||||
double it = dcPeriods > 0 ? sumPr / dcPeriods : pr;
|
||||
_itBuffer.Add(it, Input.IsNew);
|
||||
|
||||
_p_lastPd = _lastPd;
|
||||
_lastPd = pd;
|
||||
|
||||
// Final indicator
|
||||
if (_index >= 11) // 12th bar
|
||||
{
|
||||
return ((4 * _itBuffer[0]) + (3 * _itBuffer[1]) + (2 * _itBuffer[2]) + _itBuffer[3]) / 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
return pr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class Hwma : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly double _nA, _nB, _nC;
|
||||
private double _pF, _pV, _pA;
|
||||
private double _ppF, _ppV, _ppA;
|
||||
|
||||
public Hwma(int period) : this(period, 2.0 / (1 + period), 1.0 / period, 1.0 / period)
|
||||
{
|
||||
}
|
||||
|
||||
public Hwma(double nA, double nB, double nC) : this((int)((2 - nA) / nA), nA, nB, nC)
|
||||
{
|
||||
}
|
||||
|
||||
public Hwma(int period, double nA, double nB, double nC)
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
|
||||
}
|
||||
_period = period;
|
||||
_nA = nA;
|
||||
_nB = nB;
|
||||
_nC = nC;
|
||||
WarmupPeriod = period;
|
||||
Name = $"Hwma({_period})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Hwma(object source, int period) : this(period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_pF = _pV = _pA = 0;
|
||||
_ppF = _ppV = _ppA = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
_ppF = _pF;
|
||||
_ppV = _pV;
|
||||
_ppA = _pA;
|
||||
}
|
||||
else
|
||||
{
|
||||
_pF = _ppF;
|
||||
_pV = _ppV;
|
||||
_pA = _ppA;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
if (_index == 1)
|
||||
{
|
||||
_pF = Input.Value;
|
||||
_pA = _pV = 0;
|
||||
}
|
||||
|
||||
double nA = _nA, nB = _nB, nC = _nC;
|
||||
if (_period == 1)
|
||||
{
|
||||
nA = 1;
|
||||
nB = 0;
|
||||
nC = 0;
|
||||
}
|
||||
|
||||
double F = (1 - nA) * (_pF + _pV + 0.5 * _pA) + nA * Input.Value;
|
||||
double V = (1 - nB) * (_pV + _pA) + nB * (F - _pF);
|
||||
double A = (1 - nC) * _pA + nC * (V - _pV);
|
||||
|
||||
double hwma = F + V + 0.5 * A;
|
||||
|
||||
_pF = F;
|
||||
_pV = V;
|
||||
_pA = A;
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return hwma;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
using QuanTAlib;
|
||||
//TODO consistency test
|
||||
public class Jma : AbstractBase
|
||||
{
|
||||
public readonly int Period;
|
||||
private readonly double _phase;
|
||||
private readonly int _vshort, _vlong;
|
||||
private CircularBuffer _values;
|
||||
private CircularBuffer _voltyShort;
|
||||
private CircularBuffer _vsumBuff;
|
||||
private CircularBuffer _avoltyBuff;
|
||||
|
||||
private double _beta, _len1, _pow1;
|
||||
private double _upperBand, _lowerBand, _prevMa1, _prevDet0, _prevDet1, _prevJma;
|
||||
private double _p_UpperBand, _p_LowerBand, _p_prevMa1, _p_prevDet0, _p_prevDet1, _p_prevJma;
|
||||
|
||||
public Jma(int period, double phase = 0, int vshort = 10)
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||
}
|
||||
Period = period;
|
||||
_vshort = vshort;
|
||||
_vlong = 65;
|
||||
_phase = Math.Clamp((phase * 0.01) + 1.5, 0.5, 2.5);
|
||||
|
||||
_values = new CircularBuffer(period);
|
||||
_voltyShort = new CircularBuffer(vshort);
|
||||
_vsumBuff = new CircularBuffer(_vlong);
|
||||
_avoltyBuff = new CircularBuffer(2);
|
||||
|
||||
Name = "JMA";
|
||||
WarmupPeriod = period * 2;
|
||||
Init();
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
_upperBand = _lowerBand = _prevMa1 = _prevDet0 = _prevDet1 = _prevJma = 0.0;
|
||||
_p_UpperBand = _p_LowerBand = _p_prevMa1 = _p_prevDet0 = _p_prevDet1 = _p_prevJma = 0.0;
|
||||
_beta = 0.45 * (Period - 1) / (0.45 * (Period - 1) + 2);
|
||||
_len1 = Math.Max((Math.Log(Math.Sqrt(Period - 1)) / Math.Log(2.0)) + 2.0, 0);
|
||||
_pow1 = Math.Max(_len1 - 2.0, 0.5);
|
||||
_avoltyBuff.Clear();
|
||||
_avoltyBuff.Add(0, true);
|
||||
_avoltyBuff.Add(0, true);
|
||||
base.Init();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
// Save current state
|
||||
_p_UpperBand = _upperBand;
|
||||
_p_LowerBand = _lowerBand;
|
||||
_p_prevMa1 = _prevMa1;
|
||||
_p_prevDet0 = _prevDet0;
|
||||
_p_prevDet1 = _prevDet1;
|
||||
_p_prevJma = _prevJma;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Restore previous state
|
||||
_upperBand = _p_UpperBand;
|
||||
_lowerBand = _p_LowerBand;
|
||||
_prevMa1 = _p_prevMa1;
|
||||
_prevDet0 = _p_prevDet0;
|
||||
_prevDet1 = _p_prevDet1;
|
||||
_prevJma = _p_prevJma;
|
||||
|
||||
}
|
||||
}
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_values.Add(Input.Value, Input.IsNew);
|
||||
|
||||
if (_index == 1)
|
||||
{
|
||||
_prevMa1 = _prevJma = Input.Value;
|
||||
return Input.Value;
|
||||
}
|
||||
|
||||
double hprice = _values.Max();
|
||||
double lprice = _values.Min();
|
||||
|
||||
double del1 = hprice - _upperBand;
|
||||
double del2 = lprice - _lowerBand;
|
||||
double volty = Math.Max(Math.Abs(del1), Math.Abs(del2));
|
||||
|
||||
_voltyShort.Add(volty, Input.IsNew);
|
||||
double vsum = _vsumBuff.Newest() + 0.1 * (volty - _voltyShort.Oldest());
|
||||
_vsumBuff.Add(vsum, Input.IsNew);
|
||||
|
||||
double prevAvolty = _avoltyBuff.Newest();
|
||||
double avolty = prevAvolty + 2.0 / (Math.Max(4.0 * Period, 30) + 1.0) * (vsum - prevAvolty);
|
||||
_avoltyBuff.Add(avolty, Input.IsNew);
|
||||
|
||||
double dVolty = (avolty > 0) ? volty / avolty : 0;
|
||||
dVolty = Math.Min(Math.Max(dVolty, 1.0), Math.Pow(_len1, 1.0 / _pow1));
|
||||
|
||||
double pow2 = Math.Pow(dVolty, _pow1);
|
||||
double len2 = Math.Sqrt(0.5 * (Period - 1)) * _len1;
|
||||
double _Kv = Math.Pow(len2 / (len2 + 1), Math.Sqrt(pow2));
|
||||
|
||||
_upperBand = (del1 > 0) ? hprice : hprice - (_Kv * del1);
|
||||
_lowerBand = (del2 < 0) ? lprice : lprice - (_Kv * del2);
|
||||
|
||||
double alpha = Math.Pow(_beta, pow2);
|
||||
double ma1 = (1 - alpha) * Input.Value + alpha * _prevMa1;
|
||||
_prevMa1 = ma1;
|
||||
|
||||
double det0 = (1 - _beta) * (Input.Value - ma1) + _beta * _prevDet0;
|
||||
_prevDet0 = det0;
|
||||
double ma2 = ma1 + (_phase + 1) * det0;
|
||||
|
||||
double det1 = ((1 - alpha) * (1 - alpha) * (ma2 - _prevJma)) + (alpha * alpha * _prevDet1);
|
||||
_prevDet1 = det1;
|
||||
double jma = _prevJma + det1;
|
||||
_prevJma = jma;
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return jma;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class Kama : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly double _scFast, _scSlow;
|
||||
private CircularBuffer? _buffer;
|
||||
private double _lastKama, _p_lastKama;
|
||||
|
||||
public Kama(int period, int fast = 2, int slow = 30)
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
|
||||
}
|
||||
_period = period;
|
||||
_scFast = 2.0 / (((period < fast) ? period : fast) + 1);
|
||||
_scSlow = 2.0 / (slow + 1);
|
||||
WarmupPeriod = period;
|
||||
Name = $"Kama({_period}, {fast}, {slow})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Kama(object source, int period, int fast = 2, int slow = 30) : this(period, fast, slow)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_buffer = new CircularBuffer(_period + 1);
|
||||
_lastKama = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
_p_lastKama = _lastKama;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastKama = _p_lastKama;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_buffer!.Add(Input.Value, Input.IsNew);
|
||||
|
||||
double kama;
|
||||
if (_index <= _period)
|
||||
{
|
||||
kama = Input.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
double change = Math.Abs(_buffer[^1] - _buffer[0]);
|
||||
double volatility = 0;
|
||||
for (int i = 1; i < _buffer.Count; i++)
|
||||
{
|
||||
volatility += Math.Abs(_buffer[i] - _buffer[i - 1]);
|
||||
}
|
||||
|
||||
double er = volatility != 0 ? change / volatility : 0;
|
||||
double sc = (er * (_scFast - _scSlow)) + _scSlow;
|
||||
sc *= sc; // Square the smoothing constant
|
||||
|
||||
kama = _lastKama + (sc * (Input.Value - _lastKama));
|
||||
}
|
||||
|
||||
_lastKama = kama;
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
|
||||
return kama;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
// https://www.mesasoftware.com/papers/TimeWarp.pdf
|
||||
|
||||
public class Ltma : AbstractBase
|
||||
{
|
||||
private readonly double _gamma;
|
||||
private double _prevL0, _prevL1, _prevL2, _prevL3;
|
||||
private double _p_prevL0, _p_prevL1, _p_prevL2, _p_prevL3;
|
||||
|
||||
public double Gamma => _gamma;
|
||||
|
||||
public Ltma(double gamma = 0.1)
|
||||
{
|
||||
if (gamma < 0 || gamma > 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(gamma), "Gamma must be between 0 and 1.");
|
||||
_gamma = gamma;
|
||||
Name = $"Laguerre({gamma:F2})";
|
||||
WarmupPeriod = 4; // Minimum number of samples needed
|
||||
Init();
|
||||
}
|
||||
|
||||
public Ltma(object source, double gamma = 0.1) : this(gamma)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_prevL0 = _prevL1 = _prevL2 = _prevL3 = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_p_prevL0 = _prevL0;
|
||||
_p_prevL1 = _prevL1;
|
||||
_p_prevL2 = _prevL2;
|
||||
_p_prevL3 = _prevL3;
|
||||
_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prevL0 = _p_prevL0;
|
||||
_prevL1 = _p_prevL1;
|
||||
_prevL2 = _p_prevL2;
|
||||
_prevL3 = _p_prevL3;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
// Laguerre filter calculation
|
||||
double _l0 = (1 - _gamma) * Input.Value + _gamma * _prevL0;
|
||||
double _l1 = -_gamma * _l0 + _prevL0 + _gamma * _prevL1;
|
||||
double _l2 = -_gamma * _l1 + _prevL1 + _gamma * _prevL2;
|
||||
double _l3 = -_gamma * _l2 + _prevL2 + _gamma * _prevL3;
|
||||
_prevL0 = _l0;
|
||||
_prevL1 = _l1;
|
||||
_prevL2 = _l2;
|
||||
_prevL3 = _l3;
|
||||
|
||||
double filteredValue = (_l0 + 2 * _l1 + 2 * _l2 + _l3) / 6;
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
|
||||
return filteredValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
//TODO: consistency test
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
// https://efs.kb.esignal.com/hc/en-us/articles/6362791434395-2005-Mar-The-Secret-Behind-The-Filter-MedianAdaptiveFilter-efs
|
||||
|
||||
public class Maaf : AbstractBase
|
||||
{
|
||||
private readonly CircularBuffer _priceBuffer;
|
||||
private readonly CircularBuffer _smoothBuffer;
|
||||
private double _prevFilter, _prevValue2, _threshold;
|
||||
private double _p_prevFilter, _p_prevValue2;
|
||||
|
||||
private readonly int _period;
|
||||
|
||||
public Maaf(int Period = 39, double Threshold = 0.002)
|
||||
{
|
||||
_period = Period;
|
||||
_threshold = Threshold;
|
||||
_priceBuffer = new CircularBuffer(4);
|
||||
_smoothBuffer = new CircularBuffer(Period);
|
||||
Name = "MAAF";
|
||||
WarmupPeriod = Period;
|
||||
Init();
|
||||
}
|
||||
|
||||
public Maaf(object source, int Period = 39, double Threshold = 0.002) : this(Period, Threshold)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
_priceBuffer.Clear();
|
||||
_smoothBuffer.Clear();
|
||||
_prevFilter = 0;
|
||||
_prevValue2 = 0;
|
||||
base.Init();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
_p_prevFilter = _prevFilter;
|
||||
_p_prevValue2 = _prevValue2;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prevFilter = _p_prevFilter;
|
||||
_prevValue2 = _p_prevValue2;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(IsNew);
|
||||
|
||||
_priceBuffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
if (_priceBuffer.Count < 4)
|
||||
{
|
||||
return Input.Value;
|
||||
}
|
||||
|
||||
double smooth = (_priceBuffer[^1] + (2 * _priceBuffer[^2]) + (2 * _priceBuffer[^3]) + _priceBuffer[^4]) / 6;
|
||||
_smoothBuffer.Add(smooth, Input.IsNew);
|
||||
|
||||
if (_smoothBuffer.Count < _period)
|
||||
{
|
||||
return smooth;
|
||||
}
|
||||
|
||||
int length = _period;
|
||||
double value3 = 0.2;
|
||||
double value2 = _prevValue2;
|
||||
|
||||
while (value3 > _threshold && length > 0)
|
||||
{
|
||||
double alpha = 2.0 / (length + 1);
|
||||
|
||||
var sortedValues = _smoothBuffer.TakeLast(length).OrderBy(x => x).ToList();
|
||||
double value1 = sortedValues[length / 2];
|
||||
value2 = alpha * (smooth - _prevValue2) + _prevValue2;
|
||||
|
||||
if (value1 != 0)
|
||||
{
|
||||
value3 = Math.Abs(value1 - value2) / value1;
|
||||
}
|
||||
|
||||
length -= 2;
|
||||
}
|
||||
|
||||
if (length < 3) length = 3;
|
||||
|
||||
double finalAlpha = 2.0 / (length + 1);
|
||||
double filter = finalAlpha * (smooth - _prevFilter) + _prevFilter;
|
||||
|
||||
_p_prevFilter = _prevFilter;
|
||||
_prevFilter = filter;
|
||||
_p_prevValue2 = _prevValue2;
|
||||
_prevValue2 = value2;
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
using QuanTAlib;
|
||||
using System;
|
||||
|
||||
public class Mama : AbstractBase
|
||||
{
|
||||
private readonly double _fastLimit, _slowLimit;
|
||||
private CircularBuffer _pr, _sm, _dt, _i1, _q1, _i2, _q2, _re, _im, _pd, _ph;
|
||||
private double _mama, _fama;
|
||||
private double _prevMama, _prevFama, _sumPr;
|
||||
private double _p_prevMama, _p_prevFama, _p_sumPr;
|
||||
|
||||
public TValue Fama { get; private set; }
|
||||
|
||||
public Mama(double fastLimit = 0.5, double slowLimit = 0.05)
|
||||
{
|
||||
Fama = new TValue();
|
||||
Name = $"Mama({_fastLimit:F2}, {_slowLimit:F2})";
|
||||
_fastLimit = fastLimit;
|
||||
_slowLimit = slowLimit;
|
||||
_pr = new(7);
|
||||
_sm = new(7);
|
||||
_dt = new(7);
|
||||
_q1 = new(7);
|
||||
_i1 = new(7);
|
||||
_i2 = new(2);
|
||||
_q2 = new(2);
|
||||
_re = new(2);
|
||||
_im = new(2);
|
||||
_pd = new(2);
|
||||
_ph = new(2);
|
||||
Init();
|
||||
}
|
||||
|
||||
public Mama(object source, double fastLimit = 0.5, double slowLimit = 0.05) : this(fastLimit, slowLimit)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
Fama = new TValue();
|
||||
base.Init();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_p_prevMama = _prevMama;
|
||||
_p_prevFama = _prevFama;
|
||||
_p_sumPr = _sumPr;
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prevMama = _p_prevMama;
|
||||
_prevFama = _p_prevFama;
|
||||
_sumPr = _p_sumPr;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_pr.Add(Input.Value, Input.IsNew);
|
||||
|
||||
if (_index > 6)
|
||||
{
|
||||
double adj = (0.075 * _pd[^1]) + 0.54;
|
||||
|
||||
// Smooth
|
||||
_sm.Add(((4 * _pr[^1]) + (3 * _pr[^2]) + (2 * _pr[^3]) + _pr[^4]) / 10, Input.IsNew);
|
||||
|
||||
// Detrender
|
||||
_dt.Add(((0.0962 * _sm[^1]) + (0.5769 * _sm[^3]) - (0.5769 * _sm[^5]) - (0.0962 * _sm[^7])) * adj, Input.IsNew);
|
||||
|
||||
// In-phase and quadrature
|
||||
_q1.Add(((0.0962 * _dt[^1]) + (0.5769 * _dt[^3]) - (0.5769 * _dt[^5]) - (0.0962 * _dt[^7])) * adj, Input.IsNew);
|
||||
_i1.Add(_dt[^4], Input.IsNew);
|
||||
|
||||
// Advance the phases by 90 degrees
|
||||
double jI = ((0.0962 * _i1[^1]) + (0.5769 * _i1[^3]) - (0.5769 * _i1[^5]) - (0.0962 * _i1[^7])) * adj;
|
||||
double jQ = ((0.0962 * _q1[^1]) + (0.5769 * _q1[^3]) - (0.5769 * _q1[^5]) - (0.0962 * _q1[^7])) * adj;
|
||||
|
||||
// Phasor addition for 3-bar averaging
|
||||
_i2.Add(_i1[^1] - jQ, Input.IsNew);
|
||||
_q2.Add(_q1[^1] + jI, Input.IsNew);
|
||||
_i2[^1] = 0.2 * _i2[^1] + 0.8 * _i2[^2];
|
||||
_q2[^1] = 0.2 * _q2[^1] + 0.8 * _q2[^2];
|
||||
|
||||
// Homodyne discriminator
|
||||
_re.Add((_i2[^1] * _i2[^2]) + (_q2[^1] * _q2[^2]), Input.IsNew);
|
||||
_im.Add((_i2[^1] * _q2[^2]) - (_q2[^1] * _i2[^2]), Input.IsNew);
|
||||
_re[^1] = (0.2 * _re[^1]) + (0.8 * _re[^2]);
|
||||
_im[^1] = (0.2 * _im[^1]) + (0.8 * _im[^2]);
|
||||
|
||||
// Calculate period
|
||||
if (_im[^1] != 0 && _re[^1] != 0)
|
||||
{
|
||||
_pd.Add(2 * Math.PI / Math.Atan(_im[^1] / _re[^1]), Input.IsNew);
|
||||
}
|
||||
else
|
||||
{
|
||||
_pd.Add(_pd[^2], Input.IsNew);
|
||||
}
|
||||
|
||||
// Adjust period to thresholds
|
||||
_pd[^1] = Math.Max(Math.Min(_pd[^1], 1.5 * _pd[^2]), 0.67 * _pd[^2]);
|
||||
_pd[^1] = Math.Max(Math.Min(_pd[^1], 50), 6);
|
||||
_pd[^1] = (0.2 * _pd[^1]) + (0.8 * _pd[^2]);
|
||||
|
||||
// Determine phase position
|
||||
if (_i1[^1] != 0)
|
||||
{
|
||||
_ph.Add(Math.Atan(_q1[^1] / _i1[^1]) * 180 / Math.PI, Input.IsNew);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ph.Add(_ph[^2], Input.IsNew);
|
||||
}
|
||||
|
||||
// Change in phase
|
||||
double delta = Math.Max(_ph[^2] - _ph[^1], 1);
|
||||
|
||||
// Adaptive alpha value
|
||||
double alpha = Math.Max(_fastLimit / delta, _slowLimit);
|
||||
|
||||
// Final indicators
|
||||
_mama = alpha * (_pr[^1] - _prevMama) + _prevMama;
|
||||
_fama = 0.5 * alpha * (_mama - _prevFama) + _prevFama;
|
||||
|
||||
_prevMama = _mama;
|
||||
_prevFama = _fama;
|
||||
}
|
||||
else
|
||||
{
|
||||
_pd.Add(0, Input.IsNew);
|
||||
_sm.Add(0, Input.IsNew);
|
||||
_dt.Add(0, Input.IsNew);
|
||||
_i1.Add(0, Input.IsNew);
|
||||
_q1.Add(0, Input.IsNew);
|
||||
_i2.Add(0, Input.IsNew);
|
||||
_q2.Add(0, Input.IsNew);
|
||||
_re.Add(0, Input.IsNew);
|
||||
_im.Add(0, Input.IsNew);
|
||||
_ph.Add(0, Input.IsNew);
|
||||
|
||||
_sumPr += Input.Value;
|
||||
_mama = _fama = _prevMama = _prevFama = _sumPr / _index;
|
||||
}
|
||||
|
||||
Fama = new TValue(Time: Input.Time, Value: _fama, IsNew: Input.IsNew);
|
||||
IsHot = _index >= 6;
|
||||
|
||||
return _mama;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class Mgdi : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly double _kFactor;
|
||||
private double _prevMd, _p_prevMd;
|
||||
public Mgdi(int period, double kFactor = 0.6)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than 0.");
|
||||
}
|
||||
if (kFactor <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(kFactor), "K-Factor must be greater than 0.");
|
||||
}
|
||||
_period = period;
|
||||
_kFactor = kFactor;
|
||||
Name = "Mgdi";
|
||||
WarmupPeriod = period;
|
||||
Init();
|
||||
}
|
||||
|
||||
public Mgdi(object source, int period, double kFactor = 1.0) : this(period, kFactor)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_prevMd = _p_prevMd = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_p_prevMd = _prevMd;
|
||||
_index++;
|
||||
} else {
|
||||
_prevMd = _p_prevMd;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
double value = Input.Value;
|
||||
if (_index < 2){
|
||||
_prevMd = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
double md = _prevMd + ((value - _prevMd) /
|
||||
(_kFactor * _period * Math.Pow(value / _prevMd, 4)));
|
||||
_prevMd = md;
|
||||
}
|
||||
|
||||
IsHot = _index >= _period;
|
||||
return _prevMd;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuanTAlib
|
||||
{
|
||||
public class Mma : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly CircularBuffer _buffer;
|
||||
private double _lastMma;
|
||||
|
||||
public Mma(int period)
|
||||
{
|
||||
if (period < 2)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
|
||||
}
|
||||
_period = period;
|
||||
_buffer = new CircularBuffer(period);
|
||||
Name = "Mma";
|
||||
WarmupPeriod = period;
|
||||
Init();
|
||||
}
|
||||
|
||||
public Mma(object source, int period) : this(period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_lastMma = 0;
|
||||
_buffer.Clear();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_index++;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
|
||||
if (_index >= _period)
|
||||
{
|
||||
double T = _buffer.Sum();
|
||||
double S = CalculateWeightedSum();
|
||||
_lastMma = (T / _period) + (6 * S) / ((_period + 1) * _period);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use simple average until we have enough data points
|
||||
_lastMma = _buffer.Average();
|
||||
}
|
||||
|
||||
IsHot = _index >= _period;
|
||||
return _lastMma;
|
||||
}
|
||||
|
||||
private double CalculateWeightedSum()
|
||||
{
|
||||
double sum = 0;
|
||||
for (int i = 0; i < _period; i++)
|
||||
{
|
||||
double weight = (_period - (2 * i + 1)) / 2.0;
|
||||
sum += weight * _buffer[^(i + 1)];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class Qema : AbstractBase
|
||||
{
|
||||
private readonly double _k1, _k2, _k3, _k4;
|
||||
private readonly Ema _ema1, _ema2, _ema3, _ema4;
|
||||
private double _lastQema, _p_lastQema;
|
||||
|
||||
public Qema(double k1=0.2, double k2=0.2, double k3=0.2, double k4=0.2)
|
||||
{
|
||||
if (k1 <= 0 || k2 <= 0 || k3 <= 0 || k4 <= 0 )
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("All k values must be in the range (0, 1].");
|
||||
}
|
||||
|
||||
_k1 = k1;
|
||||
_k2 = k2;
|
||||
_k3 = k3;
|
||||
_k4 = k4;
|
||||
|
||||
_ema1 = new Ema(k1);
|
||||
_ema2 = new Ema(k2);
|
||||
_ema3 = new Ema(k3);
|
||||
_ema4 = new Ema(k4);
|
||||
|
||||
Name = $"QEMA ({k1:F2},{k2:F2},{k3:F2},{k4:F2})";
|
||||
double smK = Math.Min(Math.Min(_k1, _k2), Math.Min(_k3, _k4));
|
||||
|
||||
WarmupPeriod = (int) ((2 - smK) / smK);
|
||||
Init();
|
||||
}
|
||||
|
||||
public Qema(object source, double k1, double k2, double k3, double k4)
|
||||
: this(k1, k2, k3, k4)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_lastQema = 0;
|
||||
_p_lastQema = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_p_lastQema = _lastQema;
|
||||
_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastQema = _p_lastQema;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
double ema1 = _ema1.Calc(new TValue(Input.Time, Input.Value, Input.IsNew));
|
||||
double ema2 = _ema2.Calc(new TValue(Input.Time, ema1, Input.IsNew));
|
||||
double ema3 = _ema3.Calc(new TValue(Input.Time, ema2, Input.IsNew));
|
||||
double ema4 = _ema4.Calc(new TValue(Input.Time, ema3, Input.IsNew));
|
||||
|
||||
_lastQema = 4 * ema1 - 6 * ema2 + 4 * ema3 - ema4;
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return _lastQema;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
//https://user42.tuxfamily.org/chart/manual/Regularized-Exponential-Moving-Average.html
|
||||
|
||||
public class Rema : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly double _lambda;
|
||||
private double _lastRema, _prevRema;
|
||||
private double _savedLastRema, _savedPrevRema;
|
||||
|
||||
public int Period => _period;
|
||||
public double Lambda => _lambda;
|
||||
|
||||
public Rema(int period, double lambda = 0.5)
|
||||
{
|
||||
if (period < 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||
if (lambda < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(lambda), "Lambda must be non-negative.");
|
||||
|
||||
_period = period;
|
||||
_lambda = lambda;
|
||||
Name = $"REMA({period},{lambda:F2})";
|
||||
WarmupPeriod = period;
|
||||
Init();
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_lastRema = 0;
|
||||
_prevRema = 0;
|
||||
_savedLastRema = 0;
|
||||
_savedPrevRema = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_savedLastRema = _lastRema;
|
||||
_savedPrevRema = _prevRema;
|
||||
_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastRema = _savedLastRema;
|
||||
_prevRema = _savedPrevRema;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
double alpha = 2.0 / (Math.Min(_period, _index) + 1);
|
||||
|
||||
if (_index > 2)
|
||||
{
|
||||
double rema = (_lastRema + alpha * (Input.Value - _lastRema) + _lambda * (_lastRema + (_lastRema - _prevRema))) / (1 + _lambda);
|
||||
_prevRema = _lastRema;
|
||||
_lastRema = rema;
|
||||
}
|
||||
else if (_index == 2)
|
||||
{
|
||||
_prevRema = _lastRema;
|
||||
_lastRema = Input.Value;
|
||||
}
|
||||
else
|
||||
{ // _index == 1
|
||||
_lastRema = Input.Value;
|
||||
}
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return _lastRema;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace QuanTAlib {
|
||||
|
||||
public class Rma : AbstractBase {
|
||||
private readonly int _period;
|
||||
private double _alpha;
|
||||
private double _lastRMA;
|
||||
private double _savedLastRMA;
|
||||
|
||||
public Rma(int period)
|
||||
{
|
||||
if (period < 1) {
|
||||
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
|
||||
}
|
||||
_period = period;
|
||||
WarmupPeriod = period * 2;
|
||||
_alpha = 1.0 / _period; // Wilder's smoothing factor
|
||||
Name = $"Rma({_period})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Rma(object source, int period) : this(period) {
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init() {
|
||||
base.Init();
|
||||
_lastRMA = 0;
|
||||
_savedLastRMA = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew) {
|
||||
if (isNew) {
|
||||
_savedLastRMA = _lastRMA;
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
} else {
|
||||
_lastRMA = _savedLastRMA;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation() {
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
double rma;
|
||||
|
||||
if (_index == 1) {
|
||||
rma = Input.Value;
|
||||
} else if (_index <= _period) {
|
||||
// Simple average during initial period
|
||||
rma = (_lastRMA * (_index - 1) + Input.Value) / _index;
|
||||
} else {
|
||||
// Wilder's smoothing method
|
||||
rma = _alpha * (Input.Value - _lastRMA) + _lastRMA;
|
||||
}
|
||||
|
||||
_lastRMA = rma;
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
|
||||
return rma;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class Sma : AbstractBase
|
||||
{
|
||||
// inherited _index
|
||||
// inherited _value
|
||||
public readonly int Period;
|
||||
private CircularBuffer _buffer;
|
||||
|
||||
public Sma(int period)
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||
}
|
||||
Period = period;
|
||||
WarmupPeriod = period;
|
||||
_buffer = new CircularBuffer(period);
|
||||
Name = "Sma";
|
||||
WarmupPeriod = period;
|
||||
Init();
|
||||
}
|
||||
|
||||
public Sma(object source, int period) : this(period: period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
//inhereted public void Sub(object source, in ValueEventArgs args)
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core SMA calculation - using _buffer.Average
|
||||
/// </summary>
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
double result;
|
||||
ManageState(IsNew);
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
result = _buffer.Average();
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class Smma : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private CircularBuffer? _buffer;
|
||||
private double _lastSmma, _p_lastSmma;
|
||||
|
||||
public Smma(int period)
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
|
||||
}
|
||||
_period = period;
|
||||
WarmupPeriod = period;
|
||||
Name = $"Smma({_period})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Smma(object source, int period) : this(period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_buffer = new CircularBuffer(_period);
|
||||
_lastSmma = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_p_lastSmma = _lastSmma;
|
||||
_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastSmma = _p_lastSmma;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_buffer!.Add(Input.Value, Input.IsNew);
|
||||
|
||||
double smma;
|
||||
|
||||
if (_index <= _period)
|
||||
{
|
||||
smma = _buffer.Average();
|
||||
|
||||
if (_index == _period)
|
||||
{
|
||||
_lastSmma = smma; // Initialize _lastSmma for the transition
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
smma = ((_lastSmma * (_period - 1)) + Input.Value) / _period;
|
||||
}
|
||||
|
||||
_lastSmma = smma;
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
|
||||
return smma;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class T3 : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private readonly double _vfactor;
|
||||
private readonly bool _useSma;
|
||||
private readonly double _k, _k1m, _c1, _c2, _c3, _c4;
|
||||
private readonly CircularBuffer _buffer1, _buffer2, _buffer3, _buffer4, _buffer5, _buffer6;
|
||||
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)
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
|
||||
}
|
||||
_period = period;
|
||||
_vfactor = vfactor;
|
||||
_useSma = useSma;
|
||||
WarmupPeriod = period;
|
||||
|
||||
_k = 2.0 / (_period + 1);
|
||||
_k1m = 1.0 - _k;
|
||||
_c1 = -_vfactor * _vfactor * _vfactor;
|
||||
_c2 = 3 * _vfactor * _vfactor + 3 * _vfactor * _vfactor * _vfactor;
|
||||
_c3 = -6 * _vfactor * _vfactor - 3 * _vfactor - 3 * _vfactor * _vfactor * _vfactor;
|
||||
_c4 = 1 + 3 * _vfactor + _vfactor * _vfactor * _vfactor + 3 * _vfactor * _vfactor;
|
||||
|
||||
_buffer1 = new(period);
|
||||
_buffer2 = new(period);
|
||||
_buffer3 = new(period);
|
||||
_buffer4 = new(period);
|
||||
_buffer5 = new(period);
|
||||
_buffer6 = new(period);
|
||||
|
||||
|
||||
Name = $"T3({_period}, {_vfactor})";
|
||||
Init();
|
||||
}
|
||||
|
||||
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();
|
||||
_lastEma1 = _lastEma2 = _lastEma3 = _lastEma4 = _lastEma5 = _lastEma6 = 0;
|
||||
_buffer1.Clear();
|
||||
_buffer2.Clear();
|
||||
_buffer3.Clear();
|
||||
_buffer4.Clear();
|
||||
_buffer5.Clear();
|
||||
_buffer6.Clear();
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
_p_lastEma1 = _lastEma1;
|
||||
_p_lastEma2 = _lastEma2;
|
||||
_p_lastEma3 = _lastEma3;
|
||||
_p_lastEma4 = _lastEma4;
|
||||
_p_lastEma5 = _lastEma5;
|
||||
_p_lastEma6 = _lastEma6;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastEma1 = _p_lastEma1;
|
||||
_lastEma2 = _p_lastEma2;
|
||||
_lastEma3 = _p_lastEma3;
|
||||
_lastEma4 = _p_lastEma4;
|
||||
_lastEma5 = _p_lastEma5;
|
||||
_lastEma6 = _p_lastEma6;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
double ema1, ema2, ema3, ema4, ema5, ema6;
|
||||
|
||||
if (_index == 1)
|
||||
{
|
||||
ema1 = ema2 = ema3 = ema4 = ema5 = ema6 = Input.Value;
|
||||
}
|
||||
else if (_index <= _period && _useSma)
|
||||
{
|
||||
_buffer1.Add(Input.Value, Input.IsNew);
|
||||
ema1 = _buffer1.Average();
|
||||
_buffer2.Add(ema1, Input.IsNew);
|
||||
ema2 = _buffer2.Average();
|
||||
_buffer3.Add(ema2, Input.IsNew);
|
||||
ema3 = _buffer3.Average();
|
||||
_buffer4.Add(ema3, Input.IsNew);
|
||||
ema4 = _buffer4.Average();
|
||||
_buffer5.Add(ema4, Input.IsNew);
|
||||
ema5 = _buffer5.Average();
|
||||
_buffer6.Add(ema5, Input.IsNew);
|
||||
ema6 = _buffer6.Average();
|
||||
}
|
||||
else
|
||||
{
|
||||
ema1 = _k * (Input.Value - _lastEma1) + _lastEma1;
|
||||
ema2 = _k * (ema1 - _lastEma2) + _lastEma2;
|
||||
ema3 = _k * (ema2 - _lastEma3) + _lastEma3;
|
||||
ema4 = _k * (ema3 - _lastEma4) + _lastEma4;
|
||||
ema5 = _k * (ema4 - _lastEma5) + _lastEma5;
|
||||
ema6 = _k * (ema5 - _lastEma6) + _lastEma6;
|
||||
}
|
||||
|
||||
_lastEma1 = ema1;
|
||||
_lastEma2 = ema2;
|
||||
_lastEma3 = ema3;
|
||||
_lastEma4 = ema4;
|
||||
_lastEma5 = ema5;
|
||||
_lastEma6 = ema6;
|
||||
|
||||
double t3 = _c1 * ema6 + _c2 * ema5 + _c3 * ema4 + _c4 * ema3;
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return t3;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class Tema : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private double _lastEma1, _p_lastEma1;
|
||||
private double _lastEma2, _p_lastEma2;
|
||||
private double _lastEma3, _p_lastEma3;
|
||||
private double _k, _e, _p_e;
|
||||
|
||||
public Tema(int period)
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
|
||||
}
|
||||
_period = period;
|
||||
Name = "Tema";
|
||||
double percentile = 0.85; //targeting 85th percentile of correctness of converging EMA
|
||||
WarmupPeriod = (int)Math.Ceiling(-period * Math.Log(1 - percentile));
|
||||
Init();
|
||||
}
|
||||
|
||||
public Tema(object source, int period) : this(period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_k = 2.0 / (_period + 1);
|
||||
_e = 1.0;
|
||||
_lastEma1 = _lastEma2 = _lastEma3 = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_p_lastEma1 = _lastEma1;
|
||||
_p_lastEma2 = _lastEma2;
|
||||
_p_lastEma3 = _lastEma3;
|
||||
_p_e = _e;
|
||||
_index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastEma1 = _p_lastEma1;
|
||||
_lastEma2 = _p_lastEma2;
|
||||
_lastEma3 = _p_lastEma3;
|
||||
_e = _p_e;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
double result, _ema1, _ema2, _ema3;
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_e = (_e > 1e-10) ? (1 - _k) * _e : 0;
|
||||
double _invE = (_e > 1e-10) ? 1 / (1 - _e) : 1;
|
||||
|
||||
_ema1 = _k * (Input.Value - _lastEma1) + _lastEma1;
|
||||
|
||||
_ema2 = _k * (_ema1 * _invE - _lastEma2) + _lastEma2;
|
||||
|
||||
_ema3 = _k * (_ema2 * _invE - _lastEma3) + _lastEma3;
|
||||
|
||||
double _tema = 3 * _ema1 * _invE - 3 * _ema2 * _invE + _ema3 * _invE;
|
||||
|
||||
result = _tema;
|
||||
_lastEma1 = _ema1;
|
||||
_lastEma2 = _ema2;
|
||||
_lastEma3 = _ema3;
|
||||
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class Vidya : AbstractBase
|
||||
{
|
||||
private readonly int _shortPeriod;
|
||||
private readonly int _longPeriod;
|
||||
private readonly double _alpha;
|
||||
private double _lastVIDYA, _p_lastVIDYA;
|
||||
private CircularBuffer? _shortBuffer;
|
||||
private CircularBuffer? _longBuffer;
|
||||
|
||||
public Vidya(int shortPeriod, int longPeriod = 0, double alpha = 0.2)
|
||||
{
|
||||
if (shortPeriod < 1)
|
||||
{
|
||||
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})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Vidya(object source, int shortPeriod, int longPeriod = 0, double alpha = 0.2)
|
||||
: this(shortPeriod, longPeriod, alpha)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_lastVIDYA = 0;
|
||||
_shortBuffer = new CircularBuffer(_shortPeriod);
|
||||
_longBuffer = new CircularBuffer(_longPeriod);
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
_p_lastVIDYA = _lastVIDYA;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastVIDYA = _p_lastVIDYA;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_shortBuffer!.Add(Input.Value, Input.IsNew);
|
||||
_longBuffer!.Add(Input.Value, Input.IsNew);
|
||||
|
||||
double vidya;
|
||||
if (_index <= _longPeriod)
|
||||
{
|
||||
vidya = _shortBuffer.Average();
|
||||
}
|
||||
else
|
||||
{
|
||||
double shortStdDev = CalculateStdDev(_shortBuffer);
|
||||
double longStdDev = CalculateStdDev(_longBuffer);
|
||||
double s = _alpha * (shortStdDev / longStdDev);
|
||||
vidya = (s * Input.Value) + ((1 - s) * _lastVIDYA);
|
||||
}
|
||||
|
||||
_lastVIDYA = vidya;
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
|
||||
return vidya;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private double CalculateStdDev(CircularBuffer buffer)
|
||||
{
|
||||
double mean = buffer.Average();
|
||||
double sumSquaredDiff = buffer.Sum(x => Math.Pow(x - mean, 2));
|
||||
return Math.Sqrt(sumSquaredDiff / buffer.Count);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class Zlema : AbstractBase
|
||||
{
|
||||
private readonly int _period;
|
||||
private CircularBuffer? _buffer;
|
||||
private double _alpha;
|
||||
private int _lag;
|
||||
private double _lastZLEMA, _p_lastZLEMA;
|
||||
|
||||
public Zlema(int period)
|
||||
{
|
||||
if (period < 1)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period));
|
||||
}
|
||||
_period = period;
|
||||
WarmupPeriod = period;
|
||||
_alpha = 2.0 / (_period + 1);
|
||||
_lag = (_period - 1) / 2;
|
||||
Name = $"Zlema({_period})";
|
||||
Init();
|
||||
}
|
||||
|
||||
public Zlema(object source, int period) : this(period)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
_buffer = new CircularBuffer(_period);
|
||||
_lastZLEMA = 0;
|
||||
}
|
||||
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew)
|
||||
{
|
||||
_lastValidValue = Input.Value;
|
||||
_index++;
|
||||
_p_lastZLEMA = _lastZLEMA;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastZLEMA = _p_lastZLEMA;
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
_buffer!.Add(Input.Value, Input.IsNew);
|
||||
|
||||
int lag = Math.Max(Math.Min((int)((_period - 1) * 0.5), _buffer.Count - 1), 0) + 1;
|
||||
double zlValue = 2 * Input.Value - _buffer[_buffer.Count - lag];
|
||||
|
||||
// Dynamic alpha factor for index <= period
|
||||
double k = (_index <= _period) ? (2.0 / (_index + 1)) : _alpha;
|
||||
double zlema = (zlValue - _lastZLEMA) * k + _lastZLEMA;
|
||||
|
||||
_lastZLEMA = zlema;
|
||||
IsHot = _index >= WarmupPeriod;
|
||||
|
||||
return zlema;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user