XML Documentation

This commit is contained in:
Miha Kralj
2024-10-05 15:20:13 -07:00
parent 30d93e724d
commit 3458b14ebb
23 changed files with 1596 additions and 986 deletions
+102 -79
View File
@@ -1,87 +1,110 @@
/*
Reference:
Donald Dorsey, who introduced the concept in the 1993 issue of Technical Analysis
of Stocks & Commodities Magazine. He designed the RVI to focus on the direction of
price movements in relation to volatility. Dorseys methodology is often cited in
technical analysis literature and further elaborated on in various technical analysis
guides and platforms.
*/
namespace QuanTAlib;
/// <summary>
/// Represents a Relative Volatility Index (RVI) calculator, which measures the direction
/// of volatility in relation to price movements.
/// </summary>
/// <remarks>
/// The RVI was introduced by Donald Dorsey in the 1993 issue of Technical Analysis
/// of Stocks &amp; Commodities Magazine. It focuses on the direction of price movements
/// in relation to volatility. The indicator uses standard deviation calculations
/// to determine whether volatility is increasing more in up moves or down moves.
///
/// This implementation uses a combination of Standard Deviation and Simple Moving Average
/// calculations to compute the RVI.
/// </remarks>
public class Rvi : AbstractBase {
private readonly int Period;
private Stddev _upStdDev, _downStdDev;
private Sma _upSma, _downSma;
private double _previousClose;
using System;
namespace QuanTAlib
{
public class Rvi : AbstractBase
{
private readonly int Period;
private Stddev _upStdDev, _downStdDev;
private Sma _upSma, _downSma;
private double _previousClose;
public Rvi(int period) : base()
{
if (period < 2)
{
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
}
Period = period;
WarmupPeriod = period;
Name = $"RVI(period={period})";
_upStdDev = new Stddev(Period);
_downStdDev = new Stddev(Period);
_upSma = new(Period);
_downSma = new(Period);
Init();
/// <summary>
/// Initializes a new instance of the Rvi class with the specified period.
/// </summary>
/// <param name="period">The period over which to calculate the RVI.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
public Rvi(int period) : base() {
if (period < 2) {
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
}
Period = period;
WarmupPeriod = period;
Name = $"RVI(period={period})";
_upStdDev = new Stddev(Period);
_downStdDev = new Stddev(Period);
_upSma = new(Period);
_downSma = new(Period);
Init();
}
public Rvi(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes a new instance of the Rvi class with the specified source and period.
/// </summary>
/// <param name="source">The source object to subscribe to for value updates.</param>
/// <param name="period">The period over which to calculate the RVI.</param>
public Rvi(object source, int period) : this(period) {
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
public override void Init()
{
base.Init();
_previousClose = 0;
}
/// <summary>
/// Initializes the Rvi instance by setting up the initial state.
/// </summary>
public override void Init() {
base.Init();
_previousClose = 0;
}
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
protected override double Calculation()
{
ManageState(Input.IsNew);
double close = Input.Value;
double change = close - _previousClose;
double upMove = Math.Max(change, 0);
double downMove = Math.Max(-change, 0);
_upSma.Calc(_upStdDev.Calc(new TValue(Input.Time, upMove, Input.IsNew)));
_downSma.Calc(_downStdDev.Calc(new TValue(Input.Time, downMove, Input.IsNew)));
double rvi;
if (_upSma.Value + _downSma.Value != 0)
{
rvi = 100 * _upSma.Value / (_upSma.Value + _downSma.Value);
}
else
{
rvi = 0;
}
_previousClose = close;
IsHot = _index >= WarmupPeriod;
return rvi;
/// <summary>
/// Manages the state of the Rvi instance based on whether a new value is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new value.</param>
protected override void ManageState(bool isNew) {
if (isNew) {
_lastValidValue = Value;
_index++;
}
}
}
/// <summary>
/// Performs the RVI calculation for the current input.
/// </summary>
/// <returns>
/// The calculated RVI value for the current input.
/// </returns>
/// <remarks>
/// This method calculates the RVI using the following steps:
/// 1. Calculate the change in price from the previous close.
/// 2. Determine the up move and down move based on the change.
/// 3. Calculate standard deviations of up and down moves.
/// 4. Apply a simple moving average to the standard deviations.
/// 5. Compute the RVI as a percentage of up volatility to total volatility.
/// The method returns 0 if the sum of up and down volatility is zero.
/// </remarks>
protected override double Calculation() {
ManageState(Input.IsNew);
double close = Input.Value;
double change = close - _previousClose;
double upMove = Math.Max(change, 0);
double downMove = Math.Max(-change, 0);
_upSma.Calc(_upStdDev.Calc(new TValue(Input.Time, upMove, Input.IsNew)));
_downSma.Calc(_downStdDev.Calc(new TValue(Input.Time, downMove, Input.IsNew)));
double rvi;
if (_upSma.Value + _downSma.Value != 0) {
rvi = 100 * _upSma.Value / (_upSma.Value + _downSma.Value);
} else {
rvi = 0;
}
_previousClose = close;
IsHot = _index >= WarmupPeriod;
return rvi;
}
}