Documentation

This commit is contained in:
Miha Kralj
2024-10-16 18:28:06 -07:00
parent e5a1948fc0
commit fbe4046b5d
17 changed files with 2619 additions and 286 deletions
+57 -6
View File
@@ -2,10 +2,11 @@ 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>
/// 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)
///
/// 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).
@@ -19,19 +20,52 @@ namespace QuanTAlib;
/// - 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
/// <summary>
/// The period for the EMA calculation.
/// </summary>
private readonly int _period;
/// <summary>
/// Circular buffer for SMA calculation.
/// </summary>
private CircularBuffer _sma;
/// <summary>
/// The last calculated EMA value.
/// </summary>
private double _lastEma, _p_lastEma;
/// <summary>
/// Compensator for early EMA values.
/// </summary>
private double _e, _p_e;
/// <summary>
/// The smoothing factor for EMA calculation.
/// </summary>
private readonly double _k;
/// <summary>
/// Flags to track initialization status.
/// </summary>
private bool _isInit, _p_isInit;
/// <summary>
/// Flag to determine whether to use SMA for initial values.
/// </summary>
private readonly bool _useSma;
/// <summary>
/// Initializes a new instance of the Ema class with a specified period.
/// </summary>
/// <param name="period">The period for EMA calculation.</param>
/// <param name="useSma">Whether to use SMA for initial values. Default is true.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Ema(int period, bool useSma = true)
{
if (period < 1)
@@ -47,6 +81,10 @@ public class Ema : AbstractBase
Init();
}
/// <summary>
/// Initializes a new instance of the Ema class with a specified alpha value.
/// </summary>
/// <param name="alpha">The smoothing factor for EMA calculation.</param>
public Ema(double alpha)
{
_k = alpha;
@@ -57,13 +95,21 @@ public class Ema : AbstractBase
Init();
}
/// <summary>
/// Initializes a new instance of the Ema class with a specified source and period.
/// </summary>
/// <param name="source">The source object for event subscription.</param>
/// <param name="period">The period for EMA calculation.</param>
/// <param name="useSma">Whether to use SMA for initial values. Default is true.</param>
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)
/// <summary>
/// Initializes the Ema instance.
/// </summary>
public override void Init()
{
base.Init();
@@ -74,6 +120,10 @@ public class Ema : AbstractBase
_sma = new(_period);
}
/// <summary>
/// Manages the state of the Ema instance.
/// </summary>
/// <param name="isNew">Indicates whether the input is new.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -92,8 +142,9 @@ public class Ema : AbstractBase
}
/// <summary>
/// Core EMA calculation
/// Performs the EMA calculation.
/// </summary>
/// <returns>The calculated EMA value.</returns>
protected override double Calculation()
{
double result, _ema;
@@ -118,7 +169,7 @@ public class Ema : AbstractBase
_ema = _k * (Input.Value - _lastEma) + _lastEma;
// _useSma decides if we use compensator or not
result = (_useSma || _e == 0) ? _ema : _ema / (1 - _e);
result = (_useSma || _e <= double.Epsilon) ? _ema : _ema / (1 - _e);
}
_lastEma = _ema;
IsHot = _index >= WarmupPeriod;