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
+48 -23
View File
@@ -1,14 +1,26 @@
namespace QuanTAlib;
public class Atr : AbstractBarBase
{
/// <summary>
/// Represents an Average True Range (ATR) calculator, a measure of market volatility.
/// </summary>
/// <remarks>
/// The ATR class calculates the average true range using an Exponential Moving Average (EMA)
/// 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.
/// </remarks>
public class Atr : AbstractBarBase {
private readonly Ema _ma;
private double _prevClose, _p_prevClose;
public Atr(int period) : base()
{
if (period < 1)
{
/// <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>
public Atr(int period) : base() {
if (period < 1) {
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 1.");
}
_ma = new(1.0/period);
@@ -16,34 +28,50 @@ public class Atr : AbstractBarBase
Name = $"ATR({period})";
}
public Atr(object source, int period) : this(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>
public Atr(object source, int period) : this(period) {
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
public override void Init()
{
/// <summary>
/// Initializes the Atr instance by setting up the initial state.
/// </summary>
public override void Init() {
base.Init();
_ma.Init();
_prevClose = double.NaN;
}
protected override void ManageState(bool isNew)
{
if (isNew)
{
/// <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) {
_index++;
_p_prevClose = _prevClose;
}
else
{
} else {
_prevClose = _p_prevClose;
}
}
protected override double Calculation()
{
/// <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 EMA
/// 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(Input.IsNew);
double trueRange = Math.Max(
@@ -53,8 +81,7 @@ public class Atr : AbstractBarBase
),
Math.Abs(Input.Low - _prevClose)
);
if (_index < 2)
{
if (_index < 2) {
trueRange = Input.High - Input.Low;
}
@@ -64,6 +91,4 @@ public class Atr : AbstractBarBase
return emaTrueRange.Value;
}
}
+55 -24
View File
@@ -1,17 +1,31 @@
namespace QuanTAlib;
public class Historical : AbstractBase
{
/// <summary>
/// Represents a historical volatility calculator that measures the dispersion of returns
/// for a given security or market index over a specific period.
/// </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.
/// </remarks>
public class Historical : AbstractBase {
private readonly int Period;
private readonly bool IsAnnualized;
private readonly CircularBuffer _buffer;
private readonly CircularBuffer _logReturns;
private double _previousClose;
public Historical(int period, bool isAnnualized = true) : base()
{
if (period < 2)
{
/// <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>
public Historical(int period, bool isAnnualized = true) : base() {
if (period < 2) {
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
}
Period = period;
@@ -23,46 +37,64 @@ public class Historical : AbstractBase
Init();
}
public Historical(object source, int period, bool isAnnualized = true) : this(period, isAnnualized)
{
/// <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>
public Historical(object source, int period, bool isAnnualized = true) : this(period, isAnnualized) {
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
public override void Init()
{
/// <summary>
/// Initializes the Historical instance by clearing buffers and resetting the previous close value.
/// </summary>
public override void Init() {
base.Init();
_buffer.Clear();
_logReturns.Clear();
_previousClose = 0;
}
protected override void ManageState(bool isNew)
{
if (isNew)
{
/// <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) {
_lastValidValue = Input.Value;
_index++;
}
}
protected override double Calculation()
{
/// <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)
{
if (_previousClose != 0)
{
if (_buffer.Count > 1) {
if (_previousClose != 0) {
double logReturn = Math.Log(Input.Value / _previousClose);
_logReturns.Add(logReturn, Input.IsNew);
}
if (_logReturns.Count == Period)
{
if (_logReturns.Count == Period) {
var returns = _logReturns.GetSpan().ToArray();
double mean = returns.Average();
double sumOfSquaredDifferences = returns.Sum(x => Math.Pow(x - mean, 2));
@@ -70,8 +102,7 @@ public class Historical : AbstractBase
double variance = sumOfSquaredDifferences / (Period - 1); // Using sample standard deviation
volatility = Math.Sqrt(variance);
if (IsAnnualized)
{
if (IsAnnualized) {
// Assuming 252 trading days in a year. Adjust as needed.
volatility *= Math.Sqrt(252);
}
+51 -22
View File
@@ -1,16 +1,31 @@
namespace QuanTAlib;
public class Realized : AbstractBase
{
/// <summary>
/// Represents a realized volatility calculator that measures the actual price fluctuations
/// observed in the market over a specific period.
/// </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.
/// </remarks>
public class Realized : AbstractBase {
private readonly int Period;
private readonly bool IsAnnualized;
private readonly CircularBuffer _returns;
private double _previousClose;
private double _sumSquaredReturns;
public Realized(int period, bool isAnnualized = true) : base()
{
if (period < 2)
{
/// <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>
public Realized(int period, bool isAnnualized = true) : base() {
if (period < 2) {
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than or equal to 2.");
}
Period = period;
@@ -21,34 +36,50 @@ public class Realized : AbstractBase
Init();
}
public override void Init()
{
/// <summary>
/// Initializes the Realized instance by clearing buffers and resetting calculation variables.
/// </summary>
public override void Init() {
base.Init();
_returns.Clear();
_previousClose = 0;
_sumSquaredReturns = 0;
}
protected override void ManageState(bool isNew)
{
if (isNew)
{
/// <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) {
_lastValidValue = Input.Value;
_index++;
}
}
protected override double Calculation()
{
/// <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);
double volatility = 0;
if (_previousClose != 0)
{
if (_previousClose != 0) {
double logReturn = Math.Log(Input.Value / _previousClose);
if (_returns.Count == Period)
{
if (_returns.Count == Period) {
// Remove the oldest squared return from the sum
_sumSquaredReturns -= Math.Pow(_returns[0], 2);
}
@@ -56,13 +87,11 @@ public class Realized : AbstractBase
_returns.Add(logReturn, Input.IsNew);
_sumSquaredReturns += Math.Pow(logReturn, 2);
if (_returns.Count == Period)
{
if (_returns.Count == Period) {
double variance = _sumSquaredReturns / Period;
volatility = Math.Sqrt(variance);
if (IsAnnualized)
{
if (IsAnnualized) {
// Assuming 252 trading days in a year. Adjust as needed.
volatility *= Math.Sqrt(252);
}
+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;
}
}