xml doc rewrite

This commit is contained in:
Miha
2024-10-27 09:38:53 -07:00
parent c21b96152c
commit b2fcdda785
71 changed files with 2607 additions and 1102 deletions
+48 -35
View File
@@ -1,51 +1,75 @@
using System;
namespace QuanTAlib;
/// <summary>
/// Represents an Average True Range (ATR) calculator, a measure of market volatility.
/// ATR: Average True Range
/// A technical indicator that measures market volatility by decomposing the entire
/// range of an asset's price for a period. ATR accounts for gaps between periods
/// and provides a comprehensive view of price volatility.
/// </summary>
/// <remarks>
/// The ATR class calculates the average true range using a Relative Moving Average (RMA)
/// of the true range. The true range is the greatest of: current high - current low,
/// absolute value of current high - previous close, or absolute value of current low - previous close.
/// The ATR calculation process:
/// 1. Calculates True Range (TR) as maximum of:
/// - Current High - Current Low
/// - |Current High - Previous Close|
/// - |Current Low - Previous Close|
/// 2. Applies RMA smoothing to TR values
/// 3. Updates with each new price bar
/// 4. Adapts to changing volatility
///
/// Key characteristics:
/// - Absolute price measure
/// - Gap-inclusive calculation
/// - Trend independent
/// - Volatility focused
/// - Smoothed output
///
/// Formula:
/// TR = max(high-low, |high-prevClose|, |low-prevClose|)
/// ATR = RMA(TR, period)
///
/// Market Applications:
/// - Position sizing
/// - Stop loss placement
/// - Volatility breakouts
/// - Risk assessment
/// - Entry/exit timing
///
/// Sources:
/// J. Welles Wilder - "New Concepts in Technical Trading Systems"
/// https://www.investopedia.com/terms/a/atr.asp
///
/// Note: Higher ATR indicates higher volatility
/// </remarks>
public class Atr : AbstractBase
{
public double Tr { get; private set; }
private readonly Rma _ma;
private double _prevClose, _p_prevClose;
/// <summary>
/// Initializes a new instance of the Atr class with the specified period.
/// </summary>
/// <param name="period">The period over which to calculate the ATR.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 1.
/// </exception>
/// <param name="period">The number of periods for ATR calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Atr(int period)
{
if (period < 1)
{
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 1.");
}
_ma = new(period, useSma: true);
WarmupPeriod = _ma.WarmupPeriod;
Name = $"ATR({period})";
}
/// <summary>
/// Initializes a new instance of the Atr class with the specified source and period.
/// </summary>
/// <param name="source">The source object to subscribe to for bar updates.</param>
/// <param name="period">The period over which to calculate the ATR.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for ATR calculation.</param>
public Atr(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
/// <summary>
/// Initializes the Atr instance by setting up the initial state.
/// </summary>
public override void Init()
{
base.Init();
@@ -54,10 +78,6 @@ public class Atr : AbstractBase
Tr = 0;
}
/// <summary>
/// Manages the state of the Atr instance based on whether a new bar is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new bar.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -71,28 +91,19 @@ public class Atr : AbstractBase
}
}
/// <summary>
/// Performs the ATR calculation for the current bar.
/// </summary>
/// <returns>
/// The calculated ATR value for the current bar.
/// </returns>
/// <remarks>
/// This method calculates the true range for the current bar and then uses an RMA
/// to smooth the true range values. For the first bar, it uses the high-low range
/// as the true range.
/// </remarks>
protected override double Calculation()
{
ManageState(BarInput.IsNew);
if (_index == 1)
{
// First bar uses simple high-low range
Tr = BarInput.High - BarInput.Low;
_prevClose = BarInput.Close;
}
else
{
// Calculate True Range as maximum of three measures
Tr = Math.Max(
BarInput.High - BarInput.Low,
Math.Max(
@@ -101,6 +112,8 @@ public class Atr : AbstractBase
)
);
}
// Apply RMA smoothing to True Range
_ma.Calc(new TValue(Input.Time, Tr, BarInput.IsNew));
IsHot = _ma.IsHot;
+54 -45
View File
@@ -1,14 +1,49 @@
using System;
using System.Linq;
namespace QuanTAlib;
/// <summary>
/// Represents a historical volatility calculator that measures the dispersion of returns
/// for a given security or market index over a specific period.
/// HV: Historical Volatility
/// A statistical measure that calculates the dispersion of returns over time,
/// providing insights into past price variability. Historical volatility is
/// fundamental to options pricing and risk assessment.
/// </summary>
/// <remarks>
/// The Historical class calculates volatility based on logarithmic returns. It can provide
/// both annualized and non-annualized volatility measures. The calculation uses a sample
/// standard deviation formula and assumes 252 trading days in a year for annualization.
/// The HV calculation process:
/// 1. Computes daily log returns
/// 2. Calculates standard deviation
/// 3. Annualizes if specified
/// 4. Uses sample variance formula
///
/// Key characteristics:
/// - Backward-looking measure
/// - Log-return based
/// - Optional annualization
/// - Sample-based calculation
/// - Trading-day adjusted
///
/// Formula:
/// HV = √[(Σ(ln(P[t]/P[t-1]) - μ)²)/(n-1)] * √252
/// where:
/// P = price
/// μ = mean of log returns
/// n = number of observations
/// 252 = trading days per year
///
/// Market Applications:
/// - Options pricing
/// - Risk assessment
/// - Trading ranges
/// - Portfolio management
/// - Volatility trading
///
/// Sources:
/// Black-Scholes Option Pricing Model
/// https://en.wikipedia.org/wiki/Volatility_(finance)
///
/// Note: Assumes 252 trading days for annualization
/// </remarks>
public class Hv : AbstractBase
{
private readonly int Period;
@@ -17,44 +52,34 @@ public class Hv : AbstractBase
private readonly CircularBuffer _logReturns;
private double _previousClose;
/// <summary>
/// Initializes a new instance of the Historical class with the specified period and annualization flag.
/// </summary>
/// <param name="period">The period over which to calculate historical volatility.</param>
/// <param name="isAnnualized">Whether to annualize the volatility (default is true).</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
/// <param name="period">The number of periods for volatility calculation.</param>
/// <param name="isAnnualized">Whether to annualize the result (default true).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
public Hv(int period, bool isAnnualized = true)
{
if (period < 2)
{
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2.");
}
Period = period;
IsAnnualized = isAnnualized;
WarmupPeriod = period + 1; // We need one extra data point to calculate the first return
WarmupPeriod = period + 1; // Need extra point for first return
_buffer = new CircularBuffer(period + 1);
_logReturns = new CircularBuffer(period);
Name = $"Historical(period={period}, annualized={isAnnualized})";
Init();
}
/// <summary>
/// Initializes a new instance of the Historical class with the specified source, period, and annualization flag.
/// </summary>
/// <param name="source">The source object to subscribe to for value updates.</param>
/// <param name="period">The period over which to calculate historical volatility.</param>
/// <param name="isAnnualized">Whether to annualize the volatility (default is true).</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for volatility calculation.</param>
/// <param name="isAnnualized">Whether to annualize the result (default true).</param>
public Hv(object source, int period, bool isAnnualized = true) : this(period, isAnnualized)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Historical instance by clearing buffers and resetting the previous close value.
/// </summary>
public override void Init()
{
base.Init();
@@ -63,10 +88,6 @@ public class Hv : AbstractBase
_previousClose = 0;
}
/// <summary>
/// Manages the state of the Historical 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)
@@ -76,47 +97,35 @@ public class Hv : AbstractBase
}
}
/// <summary>
/// Performs the historical volatility calculation for the current period.
/// </summary>
/// <returns>
/// The calculated historical volatility value for the current period.
/// </returns>
/// <remarks>
/// This method calculates the volatility using the following steps:
/// 1. Compute logarithmic returns.
/// 2. Calculate the sample standard deviation of the log returns.
/// 3. If annualized, multiply by the square root of 252 (assumed trading days in a year).
/// The method returns 0 until enough data points are available for the calculation.
/// </remarks>
protected override double Calculation()
{
ManageState(Input.IsNew);
_buffer.Add(Input.Value, Input.IsNew);
double volatility = 0;
if (_buffer.Count > 1)
{
// Calculate log return if we have previous close
if (_previousClose != 0)
{
double logReturn = Math.Log(Input.Value / _previousClose);
_logReturns.Add(logReturn, Input.IsNew);
}
// Calculate volatility when we have enough returns
if (_logReturns.Count == Period)
{
var returns = _logReturns.GetSpan().ToArray();
double mean = returns.Average();
double sumOfSquaredDifferences = returns.Sum(x => Math.Pow(x - mean, 2));
double variance = sumOfSquaredDifferences / (Period - 1); // Using sample standard deviation
// Sample standard deviation
double variance = sumOfSquaredDifferences / (Period - 1);
volatility = Math.Sqrt(variance);
if (IsAnnualized)
{
// Assuming 252 trading days in a year. Adjust as needed.
volatility *= Math.Sqrt(252);
volatility *= Math.Sqrt(252); // Annualize using trading days
}
}
}
+56 -41
View File
@@ -1,9 +1,46 @@
/// <summary>
/// Represents a Jurik Volatility (Jvolty) calculator, a measure of market volatility based on Jurik Moving Average (JMA) concepts.
/// </summary>
using System;
namespace QuanTAlib;
/// <summary>
/// JVOLTY: Jurik Volatility
/// An advanced volatility measure developed by Mark Jurik that combines adaptive
/// bands with JMA smoothing. JVOLTY provides a sophisticated approach to measuring
/// market volatility with reduced noise and better responsiveness.
/// </summary>
/// <remarks>
/// The JVOLTY calculation process:
/// 1. Calculates adaptive price bands
/// 2. Measures volatility from band distances
/// 3. Applies volatility normalization
/// 4. Uses JMA-style smoothing
/// 5. Provides multiple outputs
///
/// Key characteristics:
/// - Adaptive measurement
/// - Noise reduction
/// - Multiple timeframe analysis
/// - Price band integration
/// - Volatility normalization
///
/// Formula:
/// volty = max(|price - upperBand|, |price - lowerBand|)
/// bands = adaptive calculation using Jurik's methods
/// final = JMA smoothing of normalized volatility
///
/// Market Applications:
/// - Dynamic position sizing
/// - Adaptive stop placement
/// - Volatility breakout systems
/// - Risk management
/// - Market regime detection
///
/// Sources:
/// Mark Jurik Research
/// https://www.jurikresearch.com/
///
/// Note: Proprietary enhancement of volatility measurement
/// </remarks>
public class Jvolty : AbstractBase
{
private readonly int _period;
@@ -18,7 +55,6 @@ public class Jvolty : AbstractBase
private double _prevMa1, _prevDet0, _prevDet1, _prevJma, _p_prevMa1, _p_prevDet0, _p_prevDet1, _p_prevJma;
private double _vSum, _p_vSum;
public double UpperBand { get; set; }
public double LowerBand { get; set; }
public double Volty { get; set; }
@@ -26,21 +62,15 @@ public class Jvolty : AbstractBase
public double Jma { get; set; }
public double AvgVolty { get; set; }
/// <summary>
/// Initializes a new instance of the Jvolty class with the specified parameters.
/// </summary>
/// <param name="period">The period over which to calculate the Jvolty.</param>
/// <param name="phase">The phase parameter for the JMA-style calculation.</param>
/// <param name="vshort">The short-term volatility period.</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 1.
/// </exception>
/// <param name="period">The number of periods for volatility calculation.</param>
/// <param name="phase">Phase parameter for JMA smoothing (default 0).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
public Jvolty(int period, int phase = 0)
{
if (period < 1)
{
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 1.");
}
_period = period;
_phase = Math.Clamp((phase * 0.01) + 1.5, 0.5, 2.5);
@@ -53,22 +83,15 @@ public class Jvolty : AbstractBase
Name = $"JVOLTY({period})";
}
/// <summary>
/// Initializes a new instance of the Jvolty class with the specified source and parameters.
/// </summary>
/// <param name="source">The source object to subscribe to for bar updates.</param>
/// <param name="period">The period over which to calculate the Jvolty.</param>
/// <param name="phase">The phase parameter for the JMA-style calculation.</param>
/// <param name="vshort">The short-term volatility period.</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for volatility calculation.</param>
/// <param name="phase">Phase parameter for JMA smoothing (default 0).</param>
public Jvolty(object source, int period, int phase = 0) : this(period, phase)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
/// <summary>
/// Initializes the Jvolty instance by setting up the initial state.
/// </summary>
public override void Init()
{
base.Init();
@@ -80,10 +103,6 @@ public class Jvolty : AbstractBase
_vsumBuff.Clear();
}
/// <summary>
/// Manages the state of the Jvolty instance based on whether a new bar is being processed.
/// </summary>
/// <param name="isNew">Indicates whether the current input is a new bar.</param>
protected override void ManageState(bool isNew)
{
if (isNew)
@@ -109,12 +128,6 @@ public class Jvolty : AbstractBase
}
}
/// <summary>
/// Performs the Jvolty calculation for the current bar.
/// </summary>
/// <returns>
/// The calculated Jvolty value for the current bar.
/// </returns>
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -125,26 +138,29 @@ public class Jvolty : AbstractBase
_upperBand = _lowerBand = price;
}
// Calculate volatility from band distances
double del1 = price - _upperBand;
double del2 = price - _lowerBand;
double volty = Math.Max(Math.Abs(del1), Math.Abs(del2));
// Calculate moving averages of volatility
_vsumBuff.Add(volty, Input.IsNew);
_vSum += (_vsumBuff[^1] - _vsumBuff[0]) / 10;
_avoltyBuff.Add(_vSum, Input.IsNew);
double avgvolty = _avoltyBuff.Average();
// Normalize and adjust volatility
double rvolty = (avgvolty > 0) ? volty / avgvolty : 1;
rvolty = Math.Min(Math.Max(rvolty, 1.0), Math.Pow(_len1, 1.0 / _pow1));
double pow2 = Math.Pow(rvolty, _pow1);
double Kv = Math.Pow(_beta, Math.Sqrt(pow2));
// Update adaptive bands
_upperBand = (del1 >= 0) ? price : price - (Kv * del1);
_lowerBand = (del2 <= 0) ? price : price - (Kv * del2);
// Apply JMA smoothing
double alpha = Math.Pow(_beta, pow2);
double ma1 = (1 - alpha) * Input.Value + alpha * _prevMa1;
_prevMa1 = ma1;
@@ -153,11 +169,12 @@ public class Jvolty : AbstractBase
_prevDet0 = det0;
double ma2 = ma1 + _phase * det0;
double det1 = ((ma2 - _prevJma) * (1 - alpha) * (1 - alpha) ) + (alpha * alpha * _prevDet1);
double det1 = ((ma2 - _prevJma) * (1 - alpha) * (1 - alpha)) + (alpha * alpha * _prevDet1);
_prevDet1 = det1;
double jma = _prevJma + det1;
_prevJma = jma;
// Update public properties
UpperBand = _upperBand;
LowerBand = _lowerBand;
Volty = volty;
@@ -169,5 +186,3 @@ public class Jvolty : AbstractBase
return volty;
}
}
+53 -46
View File
@@ -1,14 +1,48 @@
using System;
namespace QuanTAlib;
/// <summary>
/// Represents a realized volatility calculator that measures the actual price fluctuations
/// observed in the market over a specific period.
/// RV: Realized Volatility
/// A precise volatility measure that captures actual observed price fluctuations
/// using high-frequency returns. RV provides a more accurate assessment of true
/// market volatility compared to traditional estimators.
/// </summary>
/// <remarks>
/// The Realized class calculates volatility based on logarithmic returns. It can provide
/// both annualized and non-annualized volatility measures. The calculation uses a rolling
/// sum of squared returns for efficiency and assumes 252 trading days in a year for annualization.
/// The RV calculation process:
/// 1. Computes log returns
/// 2. Squares each return
/// 3. Maintains rolling sum
/// 4. Takes square root of average
/// 5. Optionally annualizes
///
/// Key characteristics:
/// - Model-free measurement
/// - High-frequency capable
/// - Rolling calculation
/// - Memory efficient
/// - Optional annualization
///
/// Formula:
/// RV = √(Σ(ln(P[t]/P[t-1]))²/n) * √252
/// where:
/// P = price
/// n = number of observations
/// 252 = trading days per year
///
/// Market Applications:
/// - High-frequency trading
/// - Options pricing
/// - Risk forecasting
/// - Market microstructure
/// - Volatility trading
///
/// Sources:
/// Andersen, Bollerslev - "Answering the Skeptics"
/// https://en.wikipedia.org/wiki/Realized_volatility
///
/// Note: Efficient implementation using rolling sums
/// </remarks>
public class Rv : AbstractBase
{
private readonly int Period;
@@ -17,43 +51,33 @@ public class Rv : AbstractBase
private double _previousClose;
private double _sumSquaredReturns;
/// <summary>
/// Initializes a new instance of the Realized class with the specified period and annualization flag.
/// </summary>
/// <param name="period">The period over which to calculate realized volatility.</param>
/// <param name="isAnnualized">Whether to annualize the volatility (default is true).</param>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when period is less than 2.
/// </exception>
/// <param name="period">The number of periods for volatility calculation.</param>
/// <param name="isAnnualized">Whether to annualize the result (default true).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
public Rv(int period, bool isAnnualized = true)
{
if (period < 2)
{
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2.");
}
Period = period;
IsAnnualized = isAnnualized;
WarmupPeriod = period + 1; // We need one extra data point to calculate the first return
WarmupPeriod = period + 1; // Need extra point for first return
_returns = new CircularBuffer(period);
Name = $"Realized(period={period}, annualized={isAnnualized})";
Init();
}
/// <summary>
/// Initializes a new instance of the Realized class with a data source.
/// </summary>
/// <param name="source">The source object that publishes data.</param>
/// <param name="period">The period over which to calculate realized volatility.</param>
/// <param name="isAnnualized">Whether to annualize the volatility (default is true).</param>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for volatility calculation.</param>
/// <param name="isAnnualized">Whether to annualize the result (default true).</param>
public Rv(object source, int period, bool isAnnualized = true) : this(period, isAnnualized)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Realized instance by clearing buffers and resetting calculation variables.
/// </summary>
public override void Init()
{
base.Init();
@@ -62,10 +86,6 @@ public class Rv : AbstractBase
_sumSquaredReturns = 0;
}
/// <summary>
/// Manages the state of the Realized 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)
@@ -75,21 +95,6 @@ public class Rv : AbstractBase
}
}
/// <summary>
/// Performs the realized volatility calculation for the current period.
/// </summary>
/// <returns>
/// The calculated realized volatility value for the current period.
/// </returns>
/// <remarks>
/// This method calculates the volatility using the following steps:
/// 1. Compute logarithmic returns.
/// 2. Maintain a rolling sum of squared returns.
/// 3. Calculate the variance using the sum of squared returns.
/// 4. Take the square root of the variance to get volatility.
/// 5. If annualized, multiply by the square root of 252 (assumed trading days in a year).
/// The method returns 0 until enough data points are available for the calculation.
/// </remarks>
protected override double Calculation()
{
ManageState(Input.IsNew);
@@ -97,26 +102,28 @@ public class Rv : AbstractBase
double volatility = 0;
if (_previousClose != 0)
{
// Calculate log return
double logReturn = Math.Log(Input.Value / _previousClose);
if (_returns.Count == Period)
{
// Remove the oldest squared return from the sum
// Maintain rolling sum by removing oldest squared return
_sumSquaredReturns -= Math.Pow(_returns[0], 2);
}
// Add new return and update sum
_returns.Add(logReturn, Input.IsNew);
_sumSquaredReturns += Math.Pow(logReturn, 2);
if (_returns.Count == Period)
{
// Calculate realized volatility
double variance = _sumSquaredReturns / Period;
volatility = Math.Sqrt(variance);
if (IsAnnualized)
{
// Assuming 252 trading days in a year. Adjust as needed.
volatility *= Math.Sqrt(252);
volatility *= Math.Sqrt(252); // Annualize using trading days
}
}
}
+49 -44
View File
@@ -1,36 +1,61 @@
using System;
namespace QuanTAlib;
/// <summary>
/// Represents a Relative Volatility Index (RVI) calculator, which measures the direction
/// of volatility in relation to price movements.
/// RVI: Relative Volatility Index
/// A technical indicator developed by Donald Dorsey that measures the direction
/// of volatility by comparing upward and downward price movements. RVI helps
/// identify whether volatility is increasing more in up or down moves.
/// </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.
/// The RVI calculation process:
/// 1. Separates price changes into up/down moves
/// 2. Calculates standard deviation for each
/// 3. Applies moving average smoothing
/// 4. Computes relative strength ratio
/// 5. Scales to percentage (0-100)
///
/// This implementation uses a combination of Standard Deviation and Simple Moving Average
/// calculations to compute the RVI.
/// Key characteristics:
/// - Oscillator (0-100 range)
/// - Directional volatility measure
/// - Combines volatility and momentum
/// - Uses standard deviation
/// - Smoothed output
///
/// Formula:
/// RVI = 100 * SMA(StdDev(upMoves)) / (SMA(StdDev(upMoves)) + SMA(StdDev(downMoves)))
/// where:
/// upMove = max(close - prevClose, 0)
/// downMove = max(prevClose - close, 0)
///
/// Market Applications:
/// - Trend confirmation
/// - Divergence analysis
/// - Volatility breakouts
/// - Market reversals
/// - Overbought/oversold levels
///
/// Sources:
/// Donald Dorsey - "Technical Analysis of Stocks & Commodities" (1993)
/// https://www.investopedia.com/terms/r/relative_volatility_index.asp
///
/// Note: Similar concept to RSI but using volatility
/// </remarks>
public class Rvi : AbstractBase
{
private readonly Stddev _upStdDev, _downStdDev;
private readonly Sma _upSma, _downSma;
private double _previousClose;
/// <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>
/// <param name="period">The number of periods for RVI calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
public Rvi(int period)
{
if (period < 2)
{
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2.");
}
int Period = period;
WarmupPeriod = period;
@@ -42,30 +67,20 @@ public class Rvi : AbstractBase
Init();
}
/// <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>
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for RVI calculation.</param>
public Rvi(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
/// <summary>
/// Initializes the Rvi instance by setting up the initial state.
/// </summary>
public override void Init()
{
base.Init();
_previousClose = 0;
}
/// <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)
@@ -75,21 +90,6 @@ public class Rvi : AbstractBase
}
}
/// <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);
@@ -97,14 +97,19 @@ public class Rvi : AbstractBase
double close = Input.Value;
double change = close - _previousClose;
// Separate into up and down moves
double upMove = Math.Max(change, 0);
double downMove = Math.Max(-change, 0);
// Calculate standard deviations and apply smoothing
_upSma.Calc(_upStdDev.Calc(new TValue(Input.Time, upMove, Input.IsNew)));
_downSma.Calc(_downStdDev.Calc(new TValue(Input.Time, downMove, Input.IsNew)));
// Calculate RVI ratio
double rvi;
rvi = (_upSma.Value + _downSma.Value != 0) ? 100 * _upSma.Value / (_upSma.Value + _downSma.Value) : 0;
rvi = (_upSma.Value + _downSma.Value != 0)
? 100 * _upSma.Value / (_upSma.Value + _downSma.Value)
: 0;
_previousClose = close;
IsHot = _index >= WarmupPeriod;