Files
QuanTAlib/lib/averages/Vidya.cs
T

136 lines
4.7 KiB
C#
Raw Normal View History

2024-09-23 08:34:47 -07:00
using System.Runtime.CompilerServices;
namespace QuanTAlib;
2024-10-27 09:38:53 -07:00
/// <summary>
/// VIDYA: Variable Index Dynamic Average
/// An adaptive moving average that adjusts its smoothing based on the ratio of
/// short-term to long-term volatility. This allows the average to become more
/// responsive during volatile periods and more stable during quiet periods.
/// </summary>
/// <remarks>
/// The VIDYA calculation process:
/// 1. Calculates standard deviation for short and long periods
/// 2. Uses ratio of short/long volatility to determine smoothing
/// 3. Applies variable smoothing factor to price data
/// 4. Adapts automatically to changing market conditions
///
/// Key characteristics:
/// - Adaptive smoothing based on volatility
/// - More responsive during volatile periods
/// - More stable during quiet periods
/// - Uses standard deviation for volatility measurement
/// - Combines short and long-term market analysis
///
/// Sources:
/// Tushar Chande - "Beyond Technical Analysis"
/// https://www.investopedia.com/terms/v/vidya.asp
/// </remarks>
2024-09-23 08:34:47 -07:00
public class Vidya : AbstractBase
{
private readonly int _longPeriod;
private readonly double _alpha;
2024-10-27 16:11:08 -07:00
private readonly CircularBuffer _shortBuffer;
private readonly CircularBuffer _longBuffer;
2024-09-23 08:34:47 -07:00
private double _lastVIDYA, _p_lastVIDYA;
2024-10-27 09:38:53 -07:00
/// <param name="shortPeriod">The number of periods for short-term volatility calculation.</param>
/// <param name="longPeriod">The number of periods for long-term volatility calculation (default is 4x shortPeriod).</param>
/// <param name="alpha">The alpha parameter controlling the base smoothing factor (default 0.2).</param>
/// <exception cref="ArgumentException">Thrown when shortPeriod is less than 1.</exception>
2024-09-30 09:21:52 -07:00
public Vidya(int shortPeriod, int longPeriod = 0, double alpha = 0.2)
2024-09-23 08:34:47 -07:00
{
if (shortPeriod < 1)
{
2024-10-27 16:11:08 -07:00
throw new System.ArgumentException("Short period must be greater than or equal to 1.", nameof(shortPeriod));
2024-09-23 08:34:47 -07:00
}
_longPeriod = (longPeriod == 0) ? shortPeriod * 4 : longPeriod;
_alpha = alpha;
2024-09-30 07:30:27 -07:00
_shortBuffer = new CircularBuffer(shortPeriod);
2024-09-23 22:08:40 -07:00
_longBuffer = new CircularBuffer(_longPeriod);
2024-10-27 16:11:08 -07:00
WarmupPeriod = _longPeriod;
Name = $"Vidya({shortPeriod},{_longPeriod})";
2024-09-23 08:34:47 -07:00
Init();
}
2024-10-27 09:38:53 -07:00
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="shortPeriod">The number of periods for short-term volatility calculation.</param>
/// <param name="longPeriod">The number of periods for long-term volatility calculation (default is 4x shortPeriod).</param>
/// <param name="alpha">The alpha parameter controlling the base smoothing factor (default 0.2).</param>
2024-09-23 08:34:47 -07:00
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));
}
2024-10-27 16:11:08 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-09-23 08:34:47 -07:00
public override void Init()
{
base.Init();
_lastVIDYA = 0;
}
2024-10-27 16:11:08 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-09-23 08:34:47 -07:00
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Input.Value;
_index++;
_p_lastVIDYA = _lastVIDYA;
}
else
{
_lastVIDYA = _p_lastVIDYA;
}
}
2024-10-27 16:11:08 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static double CalculateStdDev(CircularBuffer buffer)
{
double mean = buffer.Average();
double sumSquaredDiff = 0;
var span = buffer.GetSpan();
for (int i = 0; i < buffer.Count; i++)
{
double diff = span[i] - mean;
sumSquaredDiff += diff * diff;
}
return System.Math.Sqrt(sumSquaredDiff / buffer.Count);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private double CalculateVidya(double shortStdDev, double longStdDev)
{
double s = _alpha * (shortStdDev / longStdDev);
return (s * Input.Value) + ((1.0 - s) * _lastVIDYA);
}
2024-09-23 08:34:47 -07:00
protected override double Calculation()
{
ManageState(Input.IsNew);
2024-09-30 09:21:52 -07:00
2024-10-27 16:11:08 -07:00
_shortBuffer.Add(Input.Value, Input.IsNew);
_longBuffer.Add(Input.Value, Input.IsNew);
2024-09-23 08:34:47 -07:00
double vidya;
if (_index <= _longPeriod)
{
vidya = _shortBuffer.Average();
}
else
{
double shortStdDev = CalculateStdDev(_shortBuffer);
double longStdDev = CalculateStdDev(_longBuffer);
2024-10-27 16:11:08 -07:00
vidya = CalculateVidya(shortStdDev, longStdDev);
2024-09-23 08:34:47 -07:00
}
_lastVIDYA = vidya;
IsHot = _index >= WarmupPeriod;
return vidya;
}
2024-10-27 09:38:53 -07:00
}