first iteration

This commit is contained in:
Miha Kralj
2025-11-25 20:40:46 -08:00
parent b5881b9bb4
commit 33ffd3a37a
594 changed files with 117007 additions and 80111 deletions
-82
View File
@@ -1,82 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// ADR: Average Daily Range
/// A volatility indicator that measures the average range of price movement over
/// a specified period. It helps identify normal trading ranges and potential
/// breakout levels.
/// </summary>
/// <remarks>
/// The ADR calculation process:
/// 1. Calculate daily range (High - Low)
/// 2. Apply SMA to daily ranges
/// 3. Updates with each new price bar
///
/// Key characteristics:
/// - Simple volatility measure
/// - Period-based average
/// - Trend independent
/// - Absolute price measure
/// - Support/resistance aid
///
/// Formula:
/// Daily Range = High - Low
/// ADR = SMA(Daily Range, period)
///
/// Market Applications:
/// - Position sizing
/// - Volatility analysis
/// - Support/resistance levels
/// - Breakout identification
/// - Risk assessment
///
/// Note: Simpler alternative to ATR, doesn't consider gaps
/// </remarks>
[SkipLocalsInit]
public sealed class Adr : AbstractBase
{
private readonly Sma _ma;
private const int DefaultPeriod = 14;
/// <param name="period">The number of periods for ADR calculation (default 14).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Adr(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_ma = new(period);
WarmupPeriod = period;
Name = $"ADR({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for ADR calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Adr(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
_index++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate daily range
double range = BarInput.High - BarInput.Low;
// Apply SMA smoothing
return _ma.Calc(range, BarInput.IsNew);
}
}
-119
View File
@@ -1,119 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// AP: Andrew's Pitchfork
/// A trend channel tool that uses three points to create a channel with a median
/// line and two parallel lines. It helps identify potential support and resistance
/// levels based on market pivots.
/// </summary>
/// <remarks>
/// The AP calculation process:
/// 1. Use three pivot points (P0, P1, P2)
/// 2. Calculate median line from P0 to midpoint of P1-P2
/// 3. Draw parallel lines at P1 and P2
/// 4. Project all lines forward
///
/// Key characteristics:
/// - Trend channel tool
/// - Support/resistance levels
/// - Price projection
/// - Market geometry
/// - Pivot-based analysis
///
/// Formula:
/// Median Line = Line from P0 to (P1 + P2)/2
/// Upper Line = Parallel to median at P1
/// Lower Line = Parallel to median at P2
///
/// Market Applications:
/// - Trend analysis
/// - Support/resistance
/// - Price targets
/// - Channel trading
/// - Market structure
///
/// Sources:
/// Dr. Alan Andrews
/// https://www.investopedia.com/terms/a/andrewspitchfork.asp
///
/// Note: Returns median line value for current price level
/// </remarks>
[SkipLocalsInit]
public sealed class Ap : AbstractBase
{
private readonly CircularBuffer _highs;
private readonly CircularBuffer _lows;
private readonly CircularBuffer _closes;
private const int DefaultPeriod = 20;
/// <param name="period">The lookback period for pivot points (default 20).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 3.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ap(int period = DefaultPeriod)
{
if (period < 3)
throw new ArgumentOutOfRangeException(nameof(period));
_highs = new(period);
_lows = new(period);
_closes = new(period);
WarmupPeriod = period;
Name = $"AP({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The lookback period for pivot points.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ap(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
_index++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static (double x, double y) FindPivot(CircularBuffer highs, CircularBuffer lows, CircularBuffer closes, int offset)
{
double high = highs[offset];
double low = lows[offset];
double close = closes[offset];
return (offset, (high + low + close) / 3.0); // Simple pivot point calculation
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Store price data
_highs.Add(BarInput.High, BarInput.IsNew);
_lows.Add(BarInput.Low, BarInput.IsNew);
_closes.Add(BarInput.Close, BarInput.IsNew);
if (_index < WarmupPeriod)
return BarInput.Close;
// Find three pivot points
var p0 = FindPivot(_highs, _lows, _closes, 2);
var p1 = FindPivot(_highs, _lows, _closes, 1);
var p2 = FindPivot(_highs, _lows, _closes, 0);
// Calculate midpoint of P1-P2
double midX = (p1.x + p2.x) / 2.0;
double midY = (p1.y + p2.y) / 2.0;
// Calculate slope of median line
double slope = (midY - p0.y) / (midX - p0.x);
// Project median line to current bar
double currentX = _index - p0.x;
return p0.y + (slope * currentX);
}
}
-132
View File
@@ -1,132 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// 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 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>
[SkipLocalsInit]
public sealed class Atr : AbstractBase
{
public double Tr { get; private set; }
private readonly Rma _ma;
private double _prevClose, _p_prevClose;
/// <param name="period">The number of periods for ATR calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Atr(int period)
{
if (period < 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})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for ATR calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Atr(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_ma.Init();
_prevClose = double.NaN;
Tr = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_index++;
_p_prevClose = _prevClose;
}
else
{
_prevClose = _p_prevClose;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateTrueRange(double high, double low, double prevClose)
{
double highLowRange = high - low;
double highPrevCloseRange = Math.Abs(high - prevClose);
double lowPrevCloseRange = Math.Abs(low - prevClose);
return Math.Max(highLowRange, Math.Max(highPrevCloseRange, lowPrevCloseRange));
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
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 = CalculateTrueRange(BarInput.High, BarInput.Low, _prevClose);
}
// Apply RMA smoothing to True Range
_ma.Calc(new TValue(Input.Time, Tr, BarInput.IsNew));
IsHot = _ma.IsHot;
_prevClose = BarInput.Close;
return _ma.Value;
}
}
-84
View File
@@ -1,84 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// ATRP: Average True Range Percent
/// A volatility indicator that expresses ATR as a percentage of current price.
/// This normalization allows for comparison across different price levels and
/// instruments.
/// </summary>
/// <remarks>
/// The ATRP calculation process:
/// 1. Calculate ATR normally
/// 2. Divide by current price
/// 3. Multiply by 100 for percentage
///
/// Key characteristics:
/// - Normalized volatility measure
/// - Price-independent comparison
/// - Percentage output
/// - Cross-market analysis
/// - Relative volatility measure
///
/// Formula:
/// ATRP = (ATR / Close) * 100
///
/// Market Applications:
/// - Cross-market comparison
/// - Position sizing
/// - Volatility analysis
/// - Risk assessment
/// - Market comparison
///
/// Note: More suitable for comparing different instruments than raw ATR
/// </remarks>
[SkipLocalsInit]
public sealed class Atrp : AbstractBase
{
private readonly Atr _atr;
private const int DefaultPeriod = 14;
private const double ScalingFactor = 100.0;
/// <param name="period">The number of periods for ATR calculation (default 14).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Atrp(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_atr = new(period);
WarmupPeriod = period;
Name = $"ATRP({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for ATR calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Atrp(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
_index++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate ATR
double atr = _atr.Calc(BarInput);
// Convert to percentage of price
return Math.Abs(BarInput.Close) > double.Epsilon
? (atr / BarInput.Close) * ScalingFactor
: 0.0;
}
}
-158
View File
@@ -1,158 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// ATRS: ATR Trailing Stop
/// A volatility-based trailing stop indicator that uses ATR to dynamically adjust
/// stop levels. It helps maintain position while allowing for normal market
/// fluctuations.
/// </summary>
/// <remarks>
/// The ATRS calculation process:
/// 1. Calculate ATR
/// 2. Multiply ATR by factor
/// 3. Apply trailing logic based on trend
/// 4. Update stop levels
///
/// Key characteristics:
/// - Dynamic stop levels
/// - Trend-following
/// - Volatility-based
/// - Position protection
/// - Risk management
///
/// Formula:
/// Long Stop = High - (ATR * Factor)
/// Short Stop = Low + (ATR * Factor)
/// where Factor is multiplier for ATR (default 2.0)
///
/// Market Applications:
/// - Stop loss placement
/// - Position management
/// - Trend following
/// - Risk control
/// - Exit strategy
///
/// Note: Returns stop level based on current trend
/// </remarks>
[SkipLocalsInit]
public sealed class Atrs : AbstractBase
{
private readonly Atr _atr;
private double _prevStop;
private double _p_prevStop;
private bool _isLong;
private bool _p_isLong;
private const int DefaultPeriod = 14;
private const double DefaultFactor = 2.0;
/// <summary>
/// Gets the current trend direction (true for long, false for short)
/// </summary>
public bool IsLong => _isLong;
/// <param name="period">The number of periods for ATR calculation (default 14).</param>
/// <param name="factor">The multiplier for ATR (default 2.0).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1 or factor is less than or equal to 0.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Atrs(int period = DefaultPeriod, double factor = DefaultFactor)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
if (factor <= 0)
throw new ArgumentOutOfRangeException(nameof(factor));
_atr = new(period);
Factor = factor;
WarmupPeriod = period;
Name = $"ATRS({period},{factor:F1})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for ATR calculation.</param>
/// <param name="factor">The multiplier for ATR.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Atrs(object source, int period = DefaultPeriod, double factor = DefaultFactor) : this(period, factor)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
/// <summary>
/// Gets or sets the ATR multiplier factor
/// </summary>
public double Factor { get; set; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_atr.Init();
_prevStop = double.NaN;
_isLong = true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_index++;
_p_prevStop = _prevStop;
_p_isLong = _isLong;
}
else
{
_prevStop = _p_prevStop;
_isLong = _p_isLong;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate ATR
double atr = _atr.Calc(BarInput);
double atrBand = atr * Factor;
if (_index == 1 || double.IsNaN(_prevStop))
{
// Initialize stop level
_isLong = BarInput.Close > BarInput.Open;
_prevStop = _isLong ? BarInput.Low - atrBand : BarInput.High + atrBand;
return _prevStop;
}
// Update stop level based on trend
if (_isLong)
{
double newStop = BarInput.High - atrBand;
if (BarInput.Close < _prevStop)
{
_isLong = false;
_prevStop = BarInput.High + atrBand;
}
else if (newStop > _prevStop)
{
_prevStop = newStop;
}
}
else
{
double newStop = BarInput.Low + atrBand;
if (BarInput.Close > _prevStop)
{
_isLong = true;
_prevStop = BarInput.Low - atrBand;
}
else if (newStop < _prevStop)
{
_prevStop = newStop;
}
}
return _prevStop;
}
}
-142
View File
@@ -1,142 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// BBAND: Bollinger Bands®
/// A technical analysis tool that creates a band of three lines:
/// - Middle Band: n-period simple moving average (SMA)
/// - Upper Band: Middle Band + (standard deviation * multiplier)
/// - Lower Band: Middle Band - (standard deviation * multiplier)
/// </summary>
/// <remarks>
/// The Bollinger Bands calculation process:
/// 1. Calculate the middle band (SMA of closing prices)
/// 2. Calculate the standard deviation of prices
/// 3. Upper and lower bands are the middle band +/- standard deviation * multiplier
///
/// Key characteristics:
/// - Adapts to volatility
/// - Default period is 20 days
/// - Default multiplier is 2.0
/// - Returns three bands (upper, middle, lower)
/// - Wider bands indicate higher volatility
/// - Narrower bands indicate lower volatility
///
/// Formula:
/// Middle Band = SMA(Close, period)
/// Standard Deviation = SQRT(SUM((Close - Middle Band)^2) / period)
/// Upper Band = Middle Band + (multiplier * Standard Deviation)
/// Lower Band = Middle Band - (multiplier * Standard Deviation)
///
/// Market Applications:
/// - Volatility measurement
/// - Overbought/oversold identification
/// - Price breakout detection
/// - Trend strength analysis
/// - Dynamic support/resistance levels
///
/// Sources:
/// John Bollinger (1980s)
/// https://www.bollingerbands.com
///
/// Note: Returns three values: upper, middle, and lower bands
/// </remarks>
[SkipLocalsInit]
public sealed class Bband : AbstractBase
{
private readonly int _period;
private readonly double _multiplier;
private readonly CircularBuffer _prices;
private double _middleBand;
private double _upperBand;
private double _lowerBand;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Bband(int period = 20, double multiplier = 2.0)
{
_period = period;
_multiplier = multiplier;
WarmupPeriod = period;
Name = $"BBAND({_period},{_multiplier})";
_prices = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Bband(object source, int period = 20, double multiplier = 2.0) : this(period, multiplier)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_middleBand = 0;
_upperBand = 0;
_lowerBand = 0;
_prices.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Add current price to buffer
_prices.Add(BarInput.Close);
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Calculate middle band (SMA)
_middleBand = _prices.Average();
// Calculate standard deviation
double sumSquaredDeviations = 0;
for (int i = 0; i < _period; i++)
{
double deviation = _prices[i] - _middleBand;
sumSquaredDeviations += deviation * deviation;
}
double standardDeviation = Math.Sqrt(sumSquaredDeviations / _period);
// Calculate bands
double bandWidth = _multiplier * standardDeviation;
_upperBand = _middleBand + bandWidth;
_lowerBand = _middleBand - bandWidth;
IsHot = _index >= WarmupPeriod;
return _middleBand; // Return middle band as primary value
}
/// <summary>
/// Gets the upper band value
/// </summary>
public double UpperBand => _upperBand;
/// <summary>
/// Gets the middle band value (SMA)
/// </summary>
public double MiddleBand => _middleBand;
/// <summary>
/// Gets the lower band value
/// </summary>
public double LowerBand => _lowerBand;
}
-129
View File
@@ -1,129 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// CCV: Close-to-Close Volatility
/// A measure of price volatility that uses only closing prices,
/// calculated as the standard deviation of logarithmic returns.
/// </summary>
/// <remarks>
/// The CCV calculation process:
/// 1. Calculate logarithmic returns: ln(Close[t]/Close[t-1])
/// 2. Calculate standard deviation of returns over the period
/// 3. Annualize by multiplying by sqrt(trading days per year)
///
/// Key characteristics:
/// - Uses only closing prices
/// - Based on logarithmic returns
/// - Default period is 20 days
/// - Annualized by default (multiply by sqrt(252))
/// - Expressed as a percentage
///
/// Formula:
/// Returns = ln(Close[t]/Close[t-1])
/// CCV = StdDev(Returns, period) * sqrt(252) * 100
///
/// Market Applications:
/// - Volatility measurement
/// - Risk assessment
/// - Option pricing
/// - Trading strategy development
/// - Portfolio management
///
/// Sources:
/// Close-to-Close Volatility concept
/// https://www.investopedia.com/terms/v/volatility.asp
///
/// Note: Returns annualized volatility as a percentage
/// </remarks>
[SkipLocalsInit]
public sealed class Ccv : AbstractBase
{
private readonly int _period;
private readonly bool _annualize;
private readonly CircularBuffer _returns;
private double _prevClose;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ccv(int period = 20, bool annualize = true)
{
_period = period;
_annualize = annualize;
WarmupPeriod = period + 1; // Need one extra period for returns calculation
Name = $"CCV({_period})";
_returns = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ccv(object source, int period = 20, bool annualize = true) : this(period, annualize)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_returns.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate logarithmic return
double logReturn = Math.Log(BarInput.Close / _prevClose);
_returns.Add(logReturn);
_prevClose = BarInput.Close;
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Calculate standard deviation
double mean = _returns.Average();
double sumSquaredDeviations = 0;
for (int i = 0; i < _period; i++)
{
double deviation = _returns[i] - mean;
sumSquaredDeviations += deviation * deviation;
}
double stdDev = Math.Sqrt(sumSquaredDeviations / _period);
// Annualize if requested (sqrt(252) for trading days in a year)
if (_annualize)
{
stdDev *= Math.Sqrt(252);
}
// Convert to percentage
double volatility = stdDev * 100;
IsHot = _index >= WarmupPeriod;
return volatility;
}
}
-156
View File
@@ -1,156 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// CE: Chandelier Exit
/// A volatility-based stop-loss indicator that adapts to market conditions,
/// using ATR to set stop levels above/below recent price extremes.
/// </summary>
/// <remarks>
/// The CE calculation process:
/// 1. Calculate highest high and lowest low over the period
/// 2. Calculate ATR over the period
/// 3. Long Exit = Highest High - (ATR * multiplier)
/// 4. Short Exit = Lowest Low + (ATR * multiplier)
///
/// Key characteristics:
/// - Adapts to market volatility
/// - Default period is 22 days
/// - Default multiplier is 3.0
/// - Returns both long and short exit levels
/// - Based on ATR and price extremes
///
/// Formula:
/// ATR = Average(TR, period)
/// Long Exit = Highest High[period] - (multiplier * ATR)
/// Short Exit = Lowest Low[period] + (multiplier * ATR)
///
/// Market Applications:
/// - Stop loss placement
/// - Position management
/// - Trend following
/// - Risk control
/// - Exit strategy
///
/// Sources:
/// Chuck LeBeau
/// https://www.investopedia.com/terms/c/chandelier-exit.asp
///
/// Note: Returns two values: long exit and short exit levels
/// </remarks>
[SkipLocalsInit]
public sealed class Ce : AbstractBase
{
private readonly int _period;
private readonly double _multiplier;
private readonly CircularBuffer _tr;
private readonly CircularBuffer _highs;
private readonly CircularBuffer _lows;
private double _prevClose;
private double _longExit;
private double _shortExit;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ce(int period = 22, double multiplier = 3.0)
{
_period = period;
_multiplier = multiplier;
WarmupPeriod = period + 1; // Need one extra period for TR
Name = $"CE({_period},{_multiplier})";
_tr = new CircularBuffer(period);
_highs = new CircularBuffer(period);
_lows = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ce(object source, int period = 22, double multiplier = 3.0) : this(period, multiplier)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_longExit = 0;
_shortExit = 0;
_tr.Clear();
_highs.Clear();
_lows.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate True Range
double tr = Math.Max(BarInput.High - BarInput.Low,
Math.Max(Math.Abs(BarInput.High - _prevClose),
Math.Abs(BarInput.Low - _prevClose)));
// Add values to buffers
_tr.Add(tr);
_highs.Add(BarInput.High);
_lows.Add(BarInput.Low);
// Store current close for next calculation
_prevClose = BarInput.Close;
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Calculate ATR
double atr = _tr.Average();
// Find highest high and lowest low
double highestHigh = double.MinValue;
double lowestLow = double.MaxValue;
for (int i = 0; i < _period; i++)
{
highestHigh = Math.Max(highestHigh, _highs[i]);
lowestLow = Math.Min(lowestLow, _lows[i]);
}
// Calculate exit levels
_longExit = highestHigh - (_multiplier * atr);
_shortExit = lowestLow + (_multiplier * atr);
IsHot = _index >= WarmupPeriod;
return _longExit; // Return long exit as primary value
}
/// <summary>
/// Gets the long exit level
/// </summary>
public double LongExit => _longExit;
/// <summary>
/// Gets the short exit level
/// </summary>
public double ShortExit => _shortExit;
}
-137
View File
@@ -1,137 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// CV: Conditional Volatility (GARCH)
/// Implements the GARCH(1,1) model for estimating conditional volatility,
/// which captures volatility clustering and mean reversion in financial markets.
/// </summary>
/// <remarks>
/// The CV (GARCH) calculation process:
/// 1. Calculate returns: (Close[t] - Close[t-1])/Close[t-1]
/// 2. Update variance estimate using GARCH(1,1) formula:
/// σ²[t] = ω + α*r²[t-1] + β*σ²[t-1]
/// 3. Take square root to get volatility
///
/// Key characteristics:
/// - Captures volatility clustering
/// - Mean-reverting behavior
/// - Responds to market shocks
/// - Default period is 20 days
/// - Returns annualized volatility
///
/// Formula:
/// Returns[t] = (Close[t] - Close[t-1])/Close[t-1]
/// σ²[t] = ω + α*Returns²[t-1] + β*σ²[t-1]
/// CV[t] = sqrt(σ²[t]) * sqrt(252) * 100
///
/// Where:
/// ω (omega) = long-term variance * (1 - α - β)
/// α (alpha) = weight of recent squared return
/// β (beta) = weight of previous variance
///
/// Market Applications:
/// - Risk measurement
/// - Option pricing
/// - Value at Risk (VaR)
/// - Portfolio optimization
/// - Volatility forecasting
///
/// Sources:
/// Bollerslev (1986)
/// https://en.wikipedia.org/wiki/GARCH
///
/// Note: Returns annualized volatility as a percentage
/// </remarks>
[SkipLocalsInit]
public sealed class Cv : AbstractBase
{
private readonly int _period;
private readonly double _alpha;
private readonly double _beta;
private readonly double _omega;
private double _prevClose;
private double _prevVariance;
private bool _isInitialized;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cv(int period = 20, double alpha = 0.1, double beta = 0.8)
{
_period = period;
_alpha = alpha;
_beta = beta;
_omega = 0.001 * (1 - alpha - beta); // Initial estimate, will be updated with actual data
WarmupPeriod = period + 1; // Need one extra period for returns
Name = $"CV({_period})";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cv(object source, int period = 20, double alpha = 0.1, double beta = 0.8) : this(period, alpha, beta)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_prevVariance = 0;
_isInitialized = false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate return
double return_ = (BarInput.Close - _prevClose) / _prevClose;
double squaredReturn = return_ * return_;
_prevClose = BarInput.Close;
// Initialize with first available data if not done
if (!_isInitialized && _index > _period)
{
double _longTermVariance = squaredReturn; // Use current squared return as initial estimate
_prevVariance = _longTermVariance;
_isInitialized = true;
}
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Update variance estimate using GARCH(1,1)
double variance = _omega + (_alpha * squaredReturn) + (_beta * _prevVariance);
_prevVariance = variance;
// Calculate annualized volatility as percentage
double volatility = Math.Sqrt(variance) * Math.Sqrt(252) * 100;
IsHot = _index >= WarmupPeriod;
return volatility;
}
}
-118
View File
@@ -1,118 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// CVI: Chaikin's Volatility Index
/// Measures the rate of change of a moving average of the difference
/// between high and low prices, indicating volatility expansion/contraction.
/// </summary>
/// <remarks>
/// The CVI calculation process:
/// 1. Calculate High-Low difference
/// 2. Take EMA of High-Low difference
/// 3. Calculate ROC of the EMA over specified period
///
/// Key characteristics:
/// - Measures volatility expansion/contraction
/// - Default period is 10 days
/// - Default smoothing period is 10 days
/// - Positive values indicate expanding volatility
/// - Negative values indicate contracting volatility
///
/// Formula:
/// HL = High - Low
/// Smoothed = EMA(HL, smoothPeriod)
/// CVI = ((Smoothed - Smoothed[period]) / Smoothed[period]) * 100
///
/// Market Applications:
/// - Volatility measurement
/// - Trend strength analysis
/// - Market regime identification
/// - Trading range analysis
/// - Breakout confirmation
///
/// Sources:
/// Marc Chaikin
/// https://www.investopedia.com/terms/c/chaikinvolatility.asp
///
/// Note: Returns percentage change in volatility
/// </remarks>
[SkipLocalsInit]
public sealed class Cvi : AbstractBase
{
private readonly int _period;
private readonly CircularBuffer _smoothed;
private readonly double _alpha;
private double _ema;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cvi(int period = 10, int smoothPeriod = 10)
{
_period = period;
_alpha = 2.0 / (smoothPeriod + 1);
WarmupPeriod = _period + smoothPeriod;
Name = $"CVI({_period},{smoothPeriod})";
_smoothed = new CircularBuffer(_period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Cvi(object source, int period = 10, int smoothPeriod = 10) : this(period, smoothPeriod)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_ema = 0;
_smoothed.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate High-Low difference
double hl = BarInput.High - BarInput.Low;
// Calculate EMA of High-Low difference
if (_index == 1)
{
_ema = hl;
}
else
{
_ema = (_alpha * hl) + ((1 - _alpha) * _ema);
}
// Add smoothed value to buffer
_smoothed.Add(_ema);
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Calculate rate of change
double roc = ((_ema - _smoothed[_period - 1]) / _smoothed[_period - 1]) * 100;
IsHot = _index >= WarmupPeriod;
return roc;
}
}
-100
View File
@@ -1,100 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// DCHN: Donchian Channels
/// A volatility indicator that identifies the highest high and lowest low
/// over a specified period, creating a channel that contains price movement.
/// </summary>
/// <remarks>
/// The DCHN calculation process:
/// 1. Track highest high over period
/// 2. Track lowest low over period
/// 3. Calculate midline as average of high and low
/// 4. Updates with each new price bar
///
/// Key characteristics:
/// - Trend following indicator
/// - Support/resistance identification
/// - Breakout detection
/// - Volatility measurement
/// - Range-based analysis
///
/// Formula:
/// Upper = Highest High over period
/// Lower = Lowest Low over period
/// Middle = (Upper + Lower) / 2
///
/// Market Applications:
/// - Trend identification
/// - Support/resistance levels
/// - Breakout trading
/// - Volatility analysis
/// - Range-bound trading
/// </remarks>
[SkipLocalsInit]
public sealed class Dchn : AbstractBase
{
private readonly CircularBuffer _highs;
private readonly CircularBuffer _lows;
private const int DefaultPeriod = 20;
/// <param name="period">The number of periods for DCHN calculation (default 20).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Dchn(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_highs = new(period);
_lows = new(period);
WarmupPeriod = period;
Name = $"DCHN({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for DCHN calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Dchn(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_highs.Add(BarInput.High);
_lows.Add(BarInput.Low);
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate channel boundaries
double upper = _highs.Max();
double lower = _lows.Min();
// Return midline
return (upper + lower) / 2.0;
}
/// <summary>
/// Gets the upper channel value (highest high)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public double Upper() => _highs.Max();
/// <summary>
/// Gets the lower channel value (lowest low)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public double Lower() => _lows.Min();
}
-140
View File
@@ -1,140 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// EWMA: Exponential Weighted Moving Average Volatility
/// A volatility measure that gives more weight to recent observations,
/// calculated using squared returns and exponential weighting.
/// </summary>
/// <remarks>
/// The EWMA calculation process:
/// 1. Calculate returns: (Close[t] - Close[t-1])/Close[t-1]
/// 2. Square returns
/// 3. Apply exponential weighting to squared returns
/// 4. Take square root and annualize
///
/// Key characteristics:
/// - More responsive to recent volatility changes
/// - Default decay factor (lambda) is 0.94
/// - Default period is 20 days
/// - Annualized by default (multiply by sqrt(252))
/// - Expressed as a percentage
///
/// Formula:
/// Returns[t] = (Close[t] - Close[t-1])/Close[t-1]
/// EWMA[t] = λ * EWMA[t-1] + (1-λ) * Returns[t]²
/// Volatility = sqrt(EWMA) * sqrt(252) * 100
///
/// Where:
/// λ (lambda) = decay factor (typically 0.94)
///
/// Market Applications:
/// - Risk measurement
/// - Option pricing
/// - Value at Risk (VaR)
/// - Portfolio optimization
/// - Volatility forecasting
///
/// Sources:
/// RiskMetrics™ Technical Document (1996)
/// https://www.msci.com/documents/10199/5915b101-4206-4ba0-aee2-3449d5c7e95a
///
/// Note: Returns annualized volatility as a percentage
/// </remarks>
[SkipLocalsInit]
public sealed class Ewma : AbstractBase
{
private readonly int _period;
private readonly double _lambda;
private readonly bool _annualize;
private double _prevClose;
private double _ewma;
private bool _isInitialized;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ewma(int period = 20, double lambda = 0.94, bool annualize = true)
{
_period = period;
_lambda = lambda;
_annualize = annualize;
WarmupPeriod = period + 1; // Need one extra period for returns
Name = $"EWMA({_period},{_lambda})";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ewma(object source, int period = 20, double lambda = 0.94, bool annualize = true) : this(period, lambda, annualize)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_ewma = 0;
_isInitialized = false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate return
double return_ = (BarInput.Close - _prevClose) / _prevClose;
double squaredReturn = return_ * return_;
_prevClose = BarInput.Close;
// Initialize EWMA if not done
if (!_isInitialized && _index > _period)
{
_ewma = squaredReturn;
_isInitialized = true;
}
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Update EWMA
_ewma = (_lambda * _ewma) + ((1 - _lambda) * squaredReturn);
// Calculate volatility
double volatility = Math.Sqrt(_ewma);
// Annualize if requested
if (_annualize)
{
volatility *= Math.Sqrt(252);
}
// Convert to percentage
volatility *= 100;
IsHot = _index >= WarmupPeriod;
return volatility;
}
}
-158
View File
@@ -1,158 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// FCB: Fractal Chaos Bands
/// Adaptive price bands based on fractal geometry concepts,
/// identifying potential support and resistance levels.
/// </summary>
/// <remarks>
/// The FCB calculation process:
/// 1. Identify fractal highs and lows over the period
/// 2. Calculate high and low bands using fractal points
/// 3. Smooth bands using exponential moving average
///
/// Key characteristics:
/// - Adapts to market structure
/// - Default period is 20 days
/// - Default smoothing factor is 0.5
/// - Returns upper and lower bands
/// - Based on fractal geometry concepts
///
/// Formula:
/// Fractal High = High[t] where High[t] > High[t±1,2]
/// Fractal Low = Low[t] where Low[t] < Low[t±1,2]
/// Upper Band = EMA(Fractal Highs, smoothing)
/// Lower Band = EMA(Fractal Lows, smoothing)
///
/// Market Applications:
/// - Support/resistance identification
/// - Trend analysis
/// - Volatility measurement
/// - Breakout detection
/// - Trading range analysis
///
/// Sources:
/// Bill Williams' Chaos Theory
/// Trading Chaos (2nd Edition) by Bill Williams
///
/// Note: Returns three values: upper, middle, and lower bands
/// </remarks>
[SkipLocalsInit]
public sealed class Fcb : AbstractBase
{
private readonly double _smoothing;
private readonly CircularBuffer _highs;
private readonly CircularBuffer _lows;
private double _upperBand;
private double _middleBand;
private double _lowerBand;
private double _upperEma;
private double _lowerEma;
private readonly double _alpha;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Fcb(int period = 20, double smoothing = 0.5)
{
_smoothing = smoothing;
_alpha = 2.0 / (period + 1);
WarmupPeriod = period + 4; // Need extra periods for fractal identification
Name = $"FCB({period},{_smoothing})";
_highs = new CircularBuffer(period);
_lows = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Fcb(object source, int period = 20, double smoothing = 0.5) : this(period, smoothing)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_upperBand = 0;
_middleBand = 0;
_lowerBand = 0;
_upperEma = 0;
_lowerEma = 0;
_highs.Clear();
_lows.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Add current high/low to buffers
_highs.Add(BarInput.High);
_lows.Add(BarInput.Low);
// Need enough values for calculation
if (_index <= 4)
{
return 0;
}
// Check for fractal patterns
bool isFractalHigh = false;
bool isFractalLow = false;
// Fractal high: current high is higher than 2 bars before and after
isFractalHigh = _highs[2] > _highs[0] && _highs[2] > _highs[1] &&
_highs[2] > _highs[3] && _highs[2] > _highs[4];
// Fractal low: current low is lower than 2 bars before and after
isFractalLow = _lows[2] < _lows[0] && _lows[2] < _lows[1] &&
_lows[2] < _lows[3] && _lows[2] < _lows[4];
// Update EMAs with fractal points
if (isFractalHigh)
{
_upperEma = (_alpha * _highs[2]) + ((1 - _alpha) * _upperEma);
}
if (isFractalLow)
{
_lowerEma = (_alpha * _lows[2]) + ((1 - _alpha) * _lowerEma);
}
// Apply smoothing to bands
_upperBand = (_smoothing * _upperEma) + ((1 - _smoothing) * BarInput.High);
_lowerBand = (_smoothing * _lowerEma) + ((1 - _smoothing) * BarInput.Low);
_middleBand = (_upperBand + _lowerBand) / 2;
IsHot = _index >= WarmupPeriod;
return _middleBand; // Return middle band as primary value
}
/// <summary>
/// Gets the upper band value
/// </summary>
public double UpperBand => _upperBand;
/// <summary>
/// Gets the middle band value
/// </summary>
public double MiddleBand => _middleBand;
/// <summary>
/// Gets the lower band value
/// </summary>
public double LowerBand => _lowerBand;
}
-126
View File
@@ -1,126 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// GKV: Garman-Klass Volatility
/// An efficient estimator of volatility that uses open, high, low,
/// and close prices to capture intraday price movements.
/// </summary>
/// <remarks>
/// The GKV calculation process:
/// 1. Calculate components using OHLC prices
/// 2. Combine components using optimal weights
/// 3. Take rolling average over period
/// 4. Annualize and convert to percentage
///
/// Key characteristics:
/// - More efficient than close-to-close volatility
/// - Uses full OHLC price information
/// - Default period is 20 days
/// - Annualized by default
/// - Expressed as a percentage
///
/// Formula:
/// u = ln(High/Low)²/2
/// c = ln(Close/Open)²
/// GKV = sqrt(sum((0.5*u - (2*ln(2)-1)*c) / period) * 252) * 100
///
/// Market Applications:
/// - Volatility estimation
/// - Risk measurement
/// - Option pricing
/// - Trading strategy development
/// - Market analysis
///
/// Sources:
/// Garman and Klass (1980)
/// Journal of Business 53(1): 67-78
///
/// Note: Returns annualized volatility as a percentage
/// </remarks>
[SkipLocalsInit]
public sealed class Gkv : AbstractBase
{
private readonly int _period;
private readonly bool _annualize;
private readonly CircularBuffer _components;
private readonly double _ln2;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Gkv(int period = 20, bool annualize = true)
{
_period = period;
_annualize = annualize;
WarmupPeriod = period;
Name = $"GKV({_period})";
_components = new CircularBuffer(period);
_ln2 = Math.Log(2);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Gkv(object source, int period = 20, bool annualize = true) : this(period, annualize)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_components.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate components
double u = Math.Log(BarInput.High / BarInput.Low);
u = u * u / 2;
double c = Math.Log(BarInput.Close / BarInput.Open);
c = c * c;
// Combine components with optimal weights
double component = (0.5 * u) - (((2 * _ln2) - 1) * c);
_components.Add(component);
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Calculate average component
double avgComponent = _components.Average();
// Calculate volatility
double volatility = Math.Sqrt(avgComponent);
// Annualize if requested
if (_annualize)
{
volatility *= Math.Sqrt(252);
}
// Convert to percentage
volatility *= 100;
IsHot = _index >= WarmupPeriod;
return volatility;
}
}
-129
View File
@@ -1,129 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// HLV: High-Low Volatility
/// A volatility measure based on the high-low range relative
/// to the previous close, capturing intraday price movements.
/// </summary>
/// <remarks>
/// The HLV calculation process:
/// 1. Calculate normalized high-low range
/// 2. Take rolling average over period
/// 3. Convert to annualized volatility
///
/// Key characteristics:
/// - Captures intraday price movements
/// - Uses high, low, and previous close
/// - Default period is 20 days
/// - Annualized by default
/// - Expressed as a percentage
///
/// Formula:
/// Range = (High - Low) / PrevClose
/// HLV = sqrt(sum(Range² / period) * 252) * 100
///
/// Market Applications:
/// - Volatility measurement
/// - Risk assessment
/// - Trading range analysis
/// - Market regime identification
/// - Position sizing
///
/// Sources:
/// Parkinson (1980) modified
/// The Extreme Value Method for Estimating the Variance of the Rate of Return
/// Journal of Business 53(1): 61-65
///
/// Note: Returns annualized volatility as a percentage
/// </remarks>
[SkipLocalsInit]
public sealed class Hlv : AbstractBase
{
private readonly int _period;
private readonly bool _annualize;
private readonly CircularBuffer _ranges;
private double _prevClose;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hlv(int period = 20, bool annualize = true)
{
_period = period;
_annualize = annualize;
WarmupPeriod = period + 1; // Need one extra period for previous close
Name = $"HLV({_period})";
_ranges = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hlv(object source, int period = 20, bool annualize = true) : this(period, annualize)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_ranges.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate normalized range
double range = (BarInput.High - BarInput.Low) / _prevClose;
double squaredRange = range * range;
_ranges.Add(squaredRange);
// Store current close for next calculation
_prevClose = BarInput.Close;
// Need enough values for calculation
if (_index <= _period)
{
return 0;
}
// Calculate average squared range
double avgSquaredRange = _ranges.Average();
// Calculate volatility
double volatility = Math.Sqrt(avgSquaredRange);
// Annualize if requested
if (_annualize)
{
volatility *= Math.Sqrt(252);
}
// Convert to percentage
volatility *= 100;
IsHot = _index >= WarmupPeriod;
return volatility;
}
}
-169
View File
@@ -1,169 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// 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 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>
[SkipLocalsInit]
public sealed class Hv : AbstractBase
{
private readonly int Period;
private readonly bool IsAnnualized;
private readonly CircularBuffer _buffer;
private readonly CircularBuffer _logReturns;
private double _previousClose;
private const int TradingDaysPerYear = 252;
private const double Epsilon = 1e-10;
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hv(int period, bool isAnnualized = true)
{
if (period < 2)
{
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2.");
}
Period = period;
IsAnnualized = isAnnualized;
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();
}
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hv(object source, int period, bool isAnnualized = true) : this(period, isAnnualized)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_buffer.Clear();
_logReturns.Clear();
_previousClose = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Input.Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateLogReturn(double currentPrice, double previousPrice)
{
return previousPrice > Epsilon ? Math.Log(currentPrice / previousPrice) : 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateMean(ReadOnlySpan<double> values)
{
double sum = 0;
for (int i = 0; i < values.Length; i++)
{
sum += values[i];
}
return sum / values.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateVariance(ReadOnlySpan<double> values, double mean, int degreesOfFreedom)
{
double sumSquaredDiff = 0;
for (int i = 0; i < values.Length; i++)
{
double diff = values[i] - mean;
sumSquaredDiff += diff * diff;
}
return sumSquaredDiff / degreesOfFreedom;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
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 > Epsilon)
{
double logReturn = CalculateLogReturn(Input.Value, _previousClose);
_logReturns.Add(logReturn, Input.IsNew);
}
// Calculate volatility when we have enough returns
if (_logReturns.Count == Period)
{
ReadOnlySpan<double> returns = _logReturns.GetSpan();
double mean = CalculateMean(returns);
double variance = CalculateVariance(returns, mean, Period - 1);
volatility = Math.Sqrt(variance);
if (IsAnnualized)
{
volatility *= Math.Sqrt(TradingDaysPerYear);
}
}
}
_previousClose = Input.Value;
IsHot = _index >= WarmupPeriod;
return volatility;
}
}
-218
View File
@@ -1,218 +0,0 @@
using System.Runtime.CompilerServices;
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>
[SkipLocalsInit]
public sealed class Jvolty : AbstractBase
{
private readonly int _period;
private readonly double _phase;
private readonly CircularBuffer _vsumBuff;
private readonly CircularBuffer _avoltyBuff;
private readonly double _beta;
private const double Epsilon = 1e-10;
private const int DefaultPhase = 0;
private const int VsumBufferSize = 10;
private const int AvoltyBufferSize = 65;
private double _len1;
private double _pow1;
private double _upperBand, _lowerBand, _p_upperBand, _p_lowerBand;
private double _prevMa1, _prevDet0, _prevDet1, _prevJma, _p_prevMa1, _p_prevDet0, _p_prevDet1, _p_prevJma;
private double _vSum, _p_vSum;
public double UpperBand { get; private set; }
public double LowerBand { get; private set; }
public double Volty { get; private set; }
public double VSum { get; private set; }
public double Jma { get; private set; }
public double AvgVolty { get; private set; }
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Jvolty(int period, int phase = DefaultPhase)
{
if (period < 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);
_vsumBuff = new CircularBuffer(VsumBufferSize);
_avoltyBuff = new CircularBuffer(AvoltyBufferSize);
_beta = 0.45 * (period - 1) / ((0.45 * (period - 1)) + 2);
WarmupPeriod = period * 2;
Name = $"JVOLTY({period})";
}
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Jvolty(object source, int period, int phase = DefaultPhase) : this(period, phase)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_upperBand = _lowerBand = 0.0;
_p_upperBand = _p_lowerBand = 0.0;
_len1 = Math.Max((Math.Log(Math.Sqrt(_period - 1)) / Math.Log(2.0)) + 2.0, 0);
_pow1 = Math.Max(_len1 - 2.0, 0.5);
_avoltyBuff.Clear();
_vsumBuff.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_index++;
_p_upperBand = _upperBand;
_p_lowerBand = _lowerBand;
_p_vSum = _vSum;
_p_prevMa1 = _prevMa1;
_p_prevDet0 = _prevDet0;
_p_prevDet1 = _prevDet1;
_p_prevJma = _prevJma;
}
else
{
_upperBand = _p_upperBand;
_lowerBand = _p_lowerBand;
_vSum = _p_vSum;
_prevMa1 = _p_prevMa1;
_prevDet0 = _p_prevDet0;
_prevDet1 = _p_prevDet1;
_prevJma = _p_prevJma;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateVolatility(double price, double upperBand, double lowerBand)
{
double del1 = price - upperBand;
double del2 = price - lowerBand;
return Math.Max(Math.Abs(del1), Math.Abs(del2));
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private double CalculateNormalizedVolatility(double volty, double avgVolty)
{
double rvolty = (avgVolty > Epsilon) ? volty / avgVolty : 1;
return Math.Min(Math.Max(rvolty, 1.0), Math.Pow(_len1, 1.0 / _pow1));
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private double CalculateJma(double price, double alpha, double ma1)
{
double det0 = ((price - ma1) * (1 - _beta)) + (_beta * _prevDet0);
_prevDet0 = det0;
double ma2 = ma1 + (_phase * det0);
double det1 = ((ma2 - _prevJma) * (1 - alpha) * (1 - alpha)) + (alpha * alpha * _prevDet1);
_prevDet1 = det1;
double jma = _prevJma + det1;
_prevJma = jma;
return jma;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(Input.IsNew);
double price = Input.Value;
if (_index == 1)
{
_upperBand = _lowerBand = price;
}
// Calculate volatility from band distances
double volty = CalculateVolatility(price, _upperBand, _lowerBand);
// Calculate moving averages of volatility
_vsumBuff.Add(volty, Input.IsNew);
_vSum += (_vsumBuff[^1] - _vsumBuff[0]) / VsumBufferSize;
_avoltyBuff.Add(_vSum, Input.IsNew);
double avgvolty = _avoltyBuff.Average();
// Normalize and adjust volatility
double rvolty = CalculateNormalizedVolatility(volty, avgvolty);
double pow2 = Math.Pow(rvolty, _pow1);
double Kv = Math.Pow(_beta, Math.Sqrt(pow2));
// Update adaptive bands
double del1 = price - _upperBand;
double del2 = price - _lowerBand;
_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) * price) + (alpha * _prevMa1);
_prevMa1 = ma1;
double jma = CalculateJma(price, alpha, ma1);
// Update public properties
UpperBand = _upperBand;
LowerBand = _lowerBand;
Volty = volty;
VSum = _vSum;
AvgVolty = avgvolty;
Jma = jma;
IsHot = _index >= WarmupPeriod;
return volty;
}
}
-94
View File
@@ -1,94 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// NATR: Normalized Average True Range
/// A volatility indicator that expresses ATR as a percentage of closing price,
/// making it more comparable across different price levels.
/// </summary>
/// <remarks>
/// The NATR calculation process:
/// 1. Calculate True Range (TR):
/// TR = max(high-low, abs(high-prevClose), abs(low-prevClose))
/// 2. Calculate ATR using SMA of TR
/// 3. Normalize by dividing ATR by close price and multiply by 100
/// 4. Updates with each new price bar
///
/// Key characteristics:
/// - Normalized volatility measure
/// - Period-based average
/// - Trend independent
/// - Percentage-based measure
/// - Comparable across instruments
///
/// Formula:
/// TR = max(high-low, abs(high-prevClose), abs(low-prevClose))
/// ATR = SMA(TR, period)
/// NATR = (ATR / Close) * 100
///
/// Market Applications:
/// - Cross-market comparison
/// - Position sizing
/// - Volatility analysis
/// - Risk assessment
/// - Market regime identification
///
/// Note: More suitable for comparing volatility across different instruments than ATR
/// </remarks>
[SkipLocalsInit]
public sealed class Natr : AbstractBase
{
private readonly Sma _ma;
private double _prevClose;
private const int DefaultPeriod = 14;
/// <param name="period">The number of periods for NATR calculation (default 14).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Natr(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_ma = new(period);
WarmupPeriod = period;
Name = $"NATR({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for NATR calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Natr(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_prevClose = BarInput.Close;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate True Range
double hl = BarInput.High - BarInput.Low;
double hc = Math.Abs(BarInput.High - _prevClose);
double lc = Math.Abs(BarInput.Low - _prevClose);
double tr = Math.Max(hl, Math.Max(hc, lc));
// Calculate ATR
double atr = _ma.Calc(tr, BarInput.IsNew);
// Normalize ATR
return (atr / BarInput.Close) * 100.0;
}
}
-102
View File
@@ -1,102 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// PCH: Price Channel
/// A volatility indicator that identifies the highest high and lowest low
/// over a specified period, creating a channel that contains price movement.
/// </summary>
/// <remarks>
/// The PCH calculation process:
/// 1. Track highest high over period
/// 2. Track lowest low over period
/// 3. Calculate midline as average of high and low
/// 4. Updates with each new price bar
///
/// Key characteristics:
/// - Trend following indicator
/// - Support/resistance identification
/// - Breakout detection
/// - Volatility measurement
/// - Range-based analysis
///
/// Formula:
/// Upper = Highest High over period
/// Lower = Lowest Low over period
/// Middle = (Upper + Lower) / 2
///
/// Market Applications:
/// - Trend identification
/// - Support/resistance levels
/// - Breakout trading
/// - Volatility analysis
/// - Range-bound trading
///
/// Note: Also known as Donchian Channels
/// </remarks>
[SkipLocalsInit]
public sealed class Pch : AbstractBase
{
private readonly CircularBuffer _highs;
private readonly CircularBuffer _lows;
private const int DefaultPeriod = 20;
/// <param name="period">The number of periods for PCH calculation (default 20).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pch(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_highs = new(period);
_lows = new(period);
WarmupPeriod = period;
Name = $"PCH({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for PCH calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pch(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_highs.Add(BarInput.High);
_lows.Add(BarInput.Low);
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate channel boundaries
double upper = _highs.Max();
double lower = _lows.Min();
// Return midline
return (upper + lower) / 2.0;
}
/// <summary>
/// Gets the upper channel value (highest high)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public double Upper() => _highs.Max();
/// <summary>
/// Gets the lower channel value (lowest low)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public double Lower() => _lows.Min();
}
-93
View File
@@ -1,93 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// PV: Parkinson Volatility
/// A volatility measure that uses the high and low prices to estimate
/// volatility, assuming continuous trading and log-normal price distribution.
/// </summary>
/// <remarks>
/// The PV calculation process:
/// 1. Calculate squared log range for each period
/// 2. Apply scaling factor (1/4ln2)
/// 3. Average over specified period
/// 4. Take square root for final volatility
///
/// Key characteristics:
/// - Range-based volatility
/// - More efficient than close-to-close
/// - Assumes continuous trading
/// - No gap consideration
/// - Log-normal distribution
///
/// Formula:
/// PV = sqrt(1/(4*ln(2)*n) * Σ(ln(High/Low))²)
/// where n is the number of periods
///
/// Market Applications:
/// - Volatility estimation
/// - Risk assessment
/// - Option pricing
/// - Trading system development
/// - Market regime identification
///
/// Note: More efficient than traditional volatility measures but sensitive to gaps
/// </remarks>
[SkipLocalsInit]
public sealed class Pv : AbstractBase
{
private readonly Sma _ma;
private readonly double _scaleFactor;
private const int DefaultPeriod = 10;
private double _prevValue;
/// <param name="period">The number of periods for PV calculation (default 10).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pv(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_ma = new(period);
_scaleFactor = 1.0 / (4.0 * Math.Log(2.0));
WarmupPeriod = period;
Name = $"PV({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for PV calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pv(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
_index++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
if (!BarInput.IsNew)
return _prevValue;
ManageState(true);
// Calculate log range squared
double logRange = Math.Log(BarInput.High / BarInput.Low);
double logRangeSquared = logRange * logRange;
// Apply moving average and scaling
double meanLogRangeSquared = _ma.Calc(logRangeSquared, true);
// Calculate final volatility
_prevValue = Math.Sqrt(_scaleFactor * meanLogRangeSquared);
return _prevValue;
}
}
-93
View File
@@ -1,93 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// RSV: Rogers-Satchell Volatility
/// A volatility measure that accounts for drift in the price process and
/// is independent of the mean return level.
/// </summary>
/// <remarks>
/// The RSV calculation process:
/// 1. Calculate log differences between prices
/// 2. Combine log differences in specific way
/// 3. Average over specified period
/// 4. Take square root for final volatility
///
/// Key characteristics:
/// - Drift-independent
/// - Uses all price data (HLOC)
/// - More efficient estimator
/// - Handles trending markets
/// - Non-zero mean returns
///
/// Formula:
/// RSV = sqrt(mean(ln(H/C) * ln(H/O) + ln(L/C) * ln(L/O)))
/// where H=High, L=Low, O=Open, C=Close
///
/// Market Applications:
/// - Volatility estimation
/// - Risk measurement
/// - Option pricing
/// - Trading system development
/// - Market regime identification
///
/// Note: More robust than simple volatility measures in trending markets
/// </remarks>
[SkipLocalsInit]
public sealed class Rsv : AbstractBase
{
private readonly Sma _ma;
private const int DefaultPeriod = 10;
private double _prevValue;
/// <param name="period">The number of periods for RSV calculation (default 10).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rsv(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_ma = new(period);
WarmupPeriod = period;
Name = $"RSV({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for RSV calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rsv(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
_index++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
if (!BarInput.IsNew)
return _prevValue;
ManageState(true);
// Calculate log ratios
double lnHC = Math.Log(BarInput.High / BarInput.Close);
double lnHO = Math.Log(BarInput.High / BarInput.Open);
double lnLC = Math.Log(BarInput.Low / BarInput.Close);
double lnLO = Math.Log(BarInput.Low / BarInput.Open);
// Calculate Rogers-Satchell term
double rs = (lnHC * lnHO) + (lnLC * lnLO);
// Apply moving average and take square root
_prevValue = Math.Sqrt(_ma.Calc(rs, true));
return _prevValue;
}
}
-152
View File
@@ -1,152 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// 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 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>
[SkipLocalsInit]
public sealed class Rv : AbstractBase
{
private readonly int Period;
private readonly bool IsAnnualized;
private readonly CircularBuffer _returns;
private double _previousClose;
private double _sumSquaredReturns;
private const int TradingDaysPerYear = 252;
private const double Epsilon = 1e-10;
private const bool DefaultIsAnnualized = true;
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rv(int period, bool isAnnualized = DefaultIsAnnualized)
{
if (period < 2)
{
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2.");
}
Period = period;
IsAnnualized = isAnnualized;
WarmupPeriod = period + 1; // Need extra point for first return
_returns = new CircularBuffer(period);
Name = $"Realized(period={period}, annualized={isAnnualized})";
Init();
}
/// <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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rv(object source, int period, bool isAnnualized = DefaultIsAnnualized) : this(period, isAnnualized)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_returns.Clear();
_previousClose = 0;
_sumSquaredReturns = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Input.Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateLogReturn(double currentPrice, double previousPrice)
{
return previousPrice > Epsilon ? Math.Log(currentPrice / previousPrice) : 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateVolatility(double sumSquaredReturns, int period, bool isAnnualized)
{
double variance = sumSquaredReturns / period;
double volatility = Math.Sqrt(variance);
return isAnnualized ? volatility * Math.Sqrt(TradingDaysPerYear) : volatility;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(Input.IsNew);
double volatility = 0;
if (_previousClose > Epsilon)
{
// Calculate log return
double logReturn = CalculateLogReturn(Input.Value, _previousClose);
if (_returns.Count == Period)
{
// Maintain rolling sum by removing oldest squared return
double oldReturn = _returns[0];
_sumSquaredReturns -= oldReturn * oldReturn;
}
// Add new return and update sum
_returns.Add(logReturn, Input.IsNew);
_sumSquaredReturns += logReturn * logReturn;
if (_returns.Count == Period)
{
// Calculate realized volatility
volatility = CalculateVolatility(_sumSquaredReturns, Period, IsAnnualized);
}
}
_previousClose = Input.Value;
IsHot = _index >= WarmupPeriod;
return volatility;
}
}
-133
View File
@@ -1,133 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// 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 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)
///
/// 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>
[SkipLocalsInit]
public sealed class Rvi : AbstractBase
{
private readonly Stddev _upStdDev, _downStdDev;
private readonly Sma _upSma, _downSma;
private double _previousClose;
private const double ScalingFactor = 100.0;
private const double Epsilon = 1e-10;
/// <param name="period">The number of periods for RVI calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 2.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rvi(int period)
{
if (period < 2)
{
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 2.");
}
WarmupPeriod = period;
Name = $"RVI(period={period})";
_upStdDev = new Stddev(period);
_downStdDev = new Stddev(period);
_upSma = new(period);
_downSma = new(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for RVI calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rvi(object source, int period) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_previousClose = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static (double upMove, double downMove) CalculateMoves(double change)
{
return (Math.Max(change, 0), Math.Max(-change, 0));
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static double CalculateRvi(double upSma, double downSma)
{
double totalSma = upSma + downSma;
return totalSma > Epsilon ? ScalingFactor * upSma / totalSma : 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(Input.IsNew);
double close = Input.Value;
double change = close - _previousClose;
// Separate into up and down moves
var (upMove, downMove) = CalculateMoves(change);
// 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 = CalculateRvi(_upSma.Value, _downSma.Value);
_previousClose = close;
IsHot = _index >= WarmupPeriod;
return rvi;
}
}
-105
View File
@@ -1,105 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// SV: Stochastic Volatility
/// A volatility measure that models price volatility as a random process,
/// capturing both the magnitude and the rate of change in price movements.
/// </summary>
/// <remarks>
/// The SV calculation process:
/// 1. Calculate log returns
/// 2. Compute exponentially weighted variance
/// 3. Apply smoothing to variance estimate
/// 4. Take square root for volatility
///
/// Key characteristics:
/// - Time-varying volatility
/// - Mean-reverting process
/// - Captures volatility clustering
/// - Handles leverage effects
/// - Accounts for fat tails
///
/// Formula:
/// Returns = ln(Close/PrevClose)
/// Variance = λ * PrevVariance + (1-λ) * Returns²
/// SV = sqrt(Variance)
/// where λ is the decay factor
///
/// Market Applications:
/// - Option pricing
/// - Risk management
/// - Trading strategies
/// - Portfolio optimization
/// - Market regime detection
///
/// Note: More sophisticated than simple volatility measures, better captures market dynamics
/// </remarks>
[SkipLocalsInit]
public sealed class Sv : AbstractBase
{
private readonly double _lambda;
private readonly Sma _ma;
private double _prevClose;
private double _prevVariance;
private double _prevValue;
private const int DefaultPeriod = 20;
private const double DefaultLambda = 0.94;
/// <param name="period">The number of periods for smoothing (default 20).</param>
/// <param name="lambda">The decay factor for variance calculation (default 0.94).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1 or lambda is not between 0 and 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Sv(int period = DefaultPeriod, double lambda = DefaultLambda)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
if (lambda <= 0 || lambda >= 1)
throw new ArgumentOutOfRangeException(nameof(lambda));
_lambda = lambda;
_ma = new(period);
WarmupPeriod = period;
Name = $"SV({period},{lambda:F2})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for smoothing.</param>
/// <param name="lambda">The decay factor for variance calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Sv(object source, int period = DefaultPeriod, double lambda = DefaultLambda) : this(period, lambda)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_prevClose = BarInput.Close;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
if (!BarInput.IsNew)
return _prevValue;
ManageState(true);
// Calculate log return
double logReturn = Math.Log(BarInput.Close / _prevClose);
double squaredReturn = logReturn * logReturn;
// Update variance estimate
_prevVariance = (_lambda * _prevVariance) + ((1.0 - _lambda) * squaredReturn);
// Apply smoothing and take square root
_prevValue = Math.Sqrt(_ma.Calc(_prevVariance, true));
return _prevValue;
}
}
-101
View File
@@ -1,101 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// TR: True Range
/// A basic volatility measure that represents the greatest of three price ranges:
/// current high-low, current high-previous close, or current low-previous close.
/// </summary>
/// <remarks>
/// The TR calculation process:
/// 1. Calculate three differences:
/// - Current High minus Current Low
/// - |Current High minus Previous Close|
/// - |Current Low minus Previous Close|
/// 2. TR is the maximum of these three values
///
/// Key characteristics:
/// - Basic volatility measure
/// - Accounts for gaps between trading periods
/// - Foundation for other indicators (ATR, etc.)
/// - No upper bound
/// - Always positive
///
/// Formula:
/// TR = max(High - Low, |High - Previous Close|, |Low - Previous Close|)
///
/// Market Applications:
/// - Volatility measurement
/// - Stop loss placement
/// - Position sizing
/// - Market analysis
/// - Risk assessment
///
/// Sources:
/// J. Welles Wilder Jr. - Original development
/// https://www.investopedia.com/terms/t/truerange.asp
///
/// Note: True Range accounts for gaps between periods, making it more accurate than simple high-low range
/// </remarks>
[SkipLocalsInit]
public sealed class Tr : AbstractBase
{
private double _prevClose;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Tr()
{
WarmupPeriod = 2; // Need previous close
Name = "TR";
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Tr(object source) : this()
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return BarInput.High - BarInput.Low;
}
// Calculate True Range
double tr = Math.Max(BarInput.High - BarInput.Low,
Math.Max(Math.Abs(BarInput.High - _prevClose),
Math.Abs(BarInput.Low - _prevClose)));
// Store current close for next calculation
_prevClose = BarInput.Close;
IsHot = _index >= WarmupPeriod;
return tr;
}
}
-113
View File
@@ -1,113 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// UI: Ulcer Index
/// A technical indicator that measures downside risk by incorporating both
/// the depth and duration of price declines over a given period.
/// </summary>
/// <remarks>
/// The UI calculation process:
/// 1. Calculate percentage drawdown from recent high for each period
/// 2. Square the drawdowns to emphasize larger declines
/// 3. Calculate the average of squared drawdowns
/// 4. Take the square root of the average
///
/// Key characteristics:
/// - Measures downside volatility
/// - Emphasizes larger drawdowns
/// - Default period is 14 days
/// - Always positive
/// - No upper bound
///
/// Formula:
/// Drawdown = ((Close - 14-period High) / 14-period High) * 100
/// UI = sqrt(sum(Drawdown^2) / period)
///
/// Market Applications:
/// - Risk assessment
/// - Portfolio analysis
/// - Trading system evaluation
/// - Market timing
/// - Trend strength measurement
///
/// Sources:
/// Peter Martin - Original development (1987)
/// https://www.investopedia.com/terms/u/ulcerindex.asp
///
/// Note: Higher values indicate higher risk due to deeper or more frequent drawdowns
/// </remarks>
[SkipLocalsInit]
public sealed class Ui : AbstractBase
{
private readonly int _period;
private readonly CircularBuffer _prices;
private readonly CircularBuffer _drawdowns;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ui(int period = 14)
{
_period = period;
WarmupPeriod = period;
Name = $"UI({_period})";
_prices = new CircularBuffer(period);
_drawdowns = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Ui(object source, int period = 14) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prices.Clear();
_drawdowns.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Add current price to buffer
_prices.Add(BarInput.Close);
// Need enough prices for calculation
if (_index <= _period)
{
return 0;
}
// Calculate maximum price in period
double maxPrice = _prices.Max();
// Calculate percentage drawdown
double drawdown = Math.Abs(maxPrice) > double.Epsilon ? ((BarInput.Close - maxPrice) / maxPrice) * 100 : 0;
// Add squared drawdown to buffer
_drawdowns.Add(drawdown * drawdown);
// Calculate Ulcer Index
double ui = Math.Sqrt(_drawdowns.Average());
IsHot = _index >= WarmupPeriod;
return ui;
}
}
-163
View File
@@ -1,163 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// VC: Volatility Cone
/// A technical indicator that analyzes volatility across different time periods
/// to identify normal ranges and extreme values.
/// </summary>
/// <remarks>
/// The VC calculation process:
/// 1. Calculate volatility for the specified period
/// 2. Track mean and standard deviation of volatility
/// 3. Calculate upper and lower bounds:
/// Upper = Mean + (deviations * StdDev)
/// Lower = Mean - (deviations * StdDev)
///
/// Key characteristics:
/// - Multi-period volatility analysis
/// - Statistical approach
/// - Default period is 20 days
/// - Returns mean and bounds
/// - Adaptive to market conditions
///
/// Formula:
/// Volatility = StdDev(Returns) * sqrt(252) // Annualized
/// Upper = Mean(Volatility) + (deviations * StdDev(Volatility))
/// Lower = Mean(Volatility) - (deviations * StdDev(Volatility))
///
/// Market Applications:
/// - Options trading
/// - Risk assessment
/// - Volatility forecasting
/// - Trading strategy development
/// - Market regime analysis
///
/// Sources:
/// https://www.investopedia.com/terms/v/volatility-cone.asp
///
/// Note: Returns three values: mean volatility and its upper/lower bounds
/// </remarks>
[SkipLocalsInit]
public sealed class Vc : AbstractBase
{
private readonly int _period;
private readonly double _deviations;
private readonly CircularBuffer _returns;
private readonly CircularBuffer _volatilities;
private double _prevClose;
private double _upperBound;
private double _lowerBound;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vc(int period = 20, double deviations = 2.0)
{
_period = period;
_deviations = deviations;
WarmupPeriod = period * 2; // Need enough data for stable statistics
Name = $"VC({_period},{_deviations})";
_returns = new CircularBuffer(period);
_volatilities = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vc(object source, int period = 20, double deviations = 2.0) : this(period, deviations)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_upperBound = 0;
_lowerBound = 0;
_returns.Clear();
_volatilities.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private double CalculateVariance(CircularBuffer buffer)
{
if (buffer.Count == 0) return 0;
double mean = buffer.Average();
double sumSquaredDiff = 0;
for (int i = 0; i < buffer.Count; i++)
{
double diff = buffer[i] - mean;
sumSquaredDiff += diff * diff;
}
return sumSquaredDiff / buffer.Count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate return
double ret = Math.Abs(_prevClose) > double.Epsilon ? Math.Log(BarInput.Close / _prevClose) : 0;
_returns.Add(ret);
// Store current close for next calculation
_prevClose = BarInput.Close;
// Need enough returns for volatility calculation
if (_index <= _period)
{
return 0;
}
// Calculate current volatility (annualized)
double vol = Math.Sqrt(CalculateVariance(_returns)) * Math.Sqrt(252);
_volatilities.Add(vol);
// Need enough volatilities for cone calculation
if (_index <= WarmupPeriod)
{
return vol;
}
// Calculate mean and standard deviation of volatilities
double meanVol = _volatilities.Average();
double stdVol = Math.Sqrt(CalculateVariance(_volatilities));
// Calculate bounds
_upperBound = meanVol + (_deviations * stdVol);
_lowerBound = Math.Max(0, meanVol - (_deviations * stdVol));
IsHot = _index >= WarmupPeriod;
return meanVol;
}
/// <summary>
/// Gets the upper bound of the volatility cone
/// </summary>
public double UpperBound => _upperBound;
/// <summary>
/// Gets the lower bound of the volatility cone
/// </summary>
public double LowerBound => _lowerBound;
}
-130
View File
@@ -1,130 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// VOV: Volatility of Volatility
/// A technical indicator that measures the volatility of volatility itself,
/// providing insight into the stability of market volatility.
/// </summary>
/// <remarks>
/// The VOV calculation process:
/// 1. Calculate primary volatility (e.g., using True Range)
/// 2. Calculate standard deviation of primary volatility
/// 3. Normalize result for comparison
///
/// Key characteristics:
/// - Second-order volatility measure
/// - Default period is 20 days
/// - Always positive
/// - No upper bound
/// - Measures volatility stability
///
/// Formula:
/// Primary Volatility = TR (True Range)
/// VOV = StdDev(Primary Volatility, period) / Average(Primary Volatility, period)
///
/// Market Applications:
/// - Risk of risk assessment
/// - Volatility regime changes
/// - Market stability analysis
/// - Trading strategy adaptation
/// - Risk management
///
/// Note: Higher values indicate more unstable volatility conditions
/// </remarks>
[SkipLocalsInit]
public sealed class Vov : AbstractBase
{
private readonly int _period;
private readonly CircularBuffer _volatilities;
private double _prevClose;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vov(int period = 20)
{
_period = period;
WarmupPeriod = period + 1; // Need extra period for TR calculation
Name = $"VOV({_period})";
_volatilities = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vov(object source, int period = 20) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_volatilities.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private double CalculateVariance(CircularBuffer buffer)
{
if (buffer.Count == 0) return 0;
double mean = buffer.Average();
double sumSquaredDiff = 0;
for (int i = 0; i < buffer.Count; i++)
{
double diff = buffer[i] - mean;
sumSquaredDiff += diff * diff;
}
return sumSquaredDiff / buffer.Count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate True Range as primary volatility measure
double tr = Math.Max(BarInput.High - BarInput.Low,
Math.Max(Math.Abs(BarInput.High - _prevClose),
Math.Abs(BarInput.Low - _prevClose)));
// Store current close for next calculation
_prevClose = BarInput.Close;
// Add volatility to buffer
_volatilities.Add(tr);
// Need enough volatilities for VOV calculation
if (_index <= _period)
{
return 0;
}
// Calculate mean volatility
double meanVol = _volatilities.Average();
// Calculate VOV (normalized standard deviation)
double vov = meanVol > double.Epsilon ? Math.Sqrt(CalculateVariance(_volatilities)) / meanVol : 0;
IsHot = _index >= WarmupPeriod;
return vov;
}
}
-134
View File
@@ -1,134 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// VR: Volatility Ratio
/// A technical indicator that compares volatility across different time periods
/// to identify changes in market conditions.
/// </summary>
/// <remarks>
/// The VR calculation process:
/// 1. Calculate short-term volatility
/// 2. Calculate long-term volatility
/// 3. Calculate ratio between them
///
/// Key characteristics:
/// - Relative volatility measure
/// - Default periods are 10 and 20 days
/// - Values above 1 indicate increasing volatility
/// - Values below 1 indicate decreasing volatility
/// - Normalized comparison
///
/// Formula:
/// Short Volatility = StdDev(Returns, shortPeriod)
/// Long Volatility = StdDev(Returns, longPeriod)
/// VR = Short Volatility / Long Volatility
///
/// Market Applications:
/// - Volatility regime changes
/// - Market condition analysis
/// - Risk assessment
/// - Trading strategy adaptation
/// - Trend confirmation
///
/// Note: Values significantly different from 1 indicate changing market conditions
/// </remarks>
[SkipLocalsInit]
public sealed class Vr : AbstractBase
{
private readonly int _longPeriod;
private readonly CircularBuffer _shortReturns;
private readonly CircularBuffer _longReturns;
private double _prevClose;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vr(int shortPeriod = 10, int longPeriod = 20)
{
_longPeriod = longPeriod;
WarmupPeriod = longPeriod + 1; // Need one extra period for returns
Name = $"VR({shortPeriod},{_longPeriod})";
_shortReturns = new CircularBuffer(shortPeriod);
_longReturns = new CircularBuffer(longPeriod);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vr(object source, int shortPeriod = 10, int longPeriod = 20) : this(shortPeriod, longPeriod)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_shortReturns.Clear();
_longReturns.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private double CalculateVariance(CircularBuffer buffer)
{
if (buffer.Count == 0) return 0;
double mean = buffer.Average();
double sumSquaredDiff = 0;
for (int i = 0; i < buffer.Count; i++)
{
double diff = buffer[i] - mean;
sumSquaredDiff += diff * diff;
}
return sumSquaredDiff / buffer.Count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate return
double ret = _prevClose > double.Epsilon ? Math.Log(BarInput.Close / _prevClose) : 0;
// Add return to buffers
_shortReturns.Add(ret);
_longReturns.Add(ret);
// Store current close for next calculation
_prevClose = BarInput.Close;
// Need enough returns for both periods
if (_index <= _longPeriod)
{
return 0;
}
// Calculate volatilities
double shortVol = Math.Sqrt(CalculateVariance(_shortReturns));
double longVol = Math.Sqrt(CalculateVariance(_longReturns));
// Calculate ratio
double vr = longVol > double.Epsilon ? shortVol / longVol : 1;
IsHot = _index >= WarmupPeriod;
return vr;
}
}
-154
View File
@@ -1,154 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// VS: Volatility Stop
/// A technical indicator that uses volatility to determine stop levels,
/// adapting to market conditions for dynamic risk management.
/// </summary>
/// <remarks>
/// The VS calculation process:
/// 1. Calculate Average True Range (ATR)
/// 2. Calculate stop levels:
/// Long Stop = Close - (multiplier * ATR)
/// Short Stop = Close + (multiplier * ATR)
/// 3. Trail stops based on price movement
///
/// Key characteristics:
/// - Adaptive stop levels
/// - Based on ATR volatility
/// - Default period is 14 days
/// - Returns both long and short stops
/// - Trails with price movement
///
/// Formula:
/// ATR = Average(TR, period)
/// Long Stop = Close - (multiplier * ATR)
/// Short Stop = Close + (multiplier * ATR)
///
/// Market Applications:
/// - Stop loss placement
/// - Position management
/// - Risk control
/// - Trend following
/// - Exit strategy
///
/// Sources:
/// Adaptation of Volatility-Based Stops concept
/// https://www.investopedia.com/terms/v/volatility-stop.asp
///
/// Note: Returns two values: long stop and short stop levels
/// </remarks>
[SkipLocalsInit]
public sealed class Vs : AbstractBase
{
private readonly int _period;
private readonly double _multiplier;
private readonly CircularBuffer _tr;
private double _prevClose;
private double _longStop;
private double _shortStop;
private double _prevLongStop;
private double _prevShortStop;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vs(int period = 14, double multiplier = 2.0)
{
_period = period;
_multiplier = multiplier;
WarmupPeriod = period + 1; // Need one extra period for TR
Name = $"VS({_period},{_multiplier})";
_tr = new CircularBuffer(period);
Init();
}
/// <param name="source">The data source object that publishes updates.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vs(object source, int period = 14, double multiplier = 2.0) : this(period, multiplier)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_prevClose = 0;
_longStop = 0;
_shortStop = 0;
_prevLongStop = 0;
_prevShortStop = 0;
_tr.Clear();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_lastValidValue = Value;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Skip first period to establish previous close
if (_index == 1)
{
_prevClose = BarInput.Close;
_longStop = BarInput.Close;
_shortStop = BarInput.Close;
return 0;
}
// Calculate True Range
double tr = Math.Max(BarInput.High - BarInput.Low,
Math.Max(Math.Abs(BarInput.High - _prevClose),
Math.Abs(BarInput.Low - _prevClose)));
// Add TR to buffer
_tr.Add(tr);
// Store current close for next calculation
_prevClose = BarInput.Close;
// Need enough values for ATR calculation
if (_index <= _period)
{
return 0;
}
// Calculate ATR
double atr = _tr.Average();
// Calculate initial stop levels
double potentialLongStop = BarInput.Close - (_multiplier * atr);
double potentialShortStop = BarInput.Close + (_multiplier * atr);
// Trail stops
_longStop = BarInput.Close > _prevShortStop ? potentialLongStop : Math.Max(potentialLongStop, _prevLongStop);
_shortStop = BarInput.Close < _prevLongStop ? potentialShortStop : Math.Min(potentialShortStop, _prevShortStop);
// Store current stops for next calculation
_prevLongStop = _longStop;
_prevShortStop = _shortStop;
IsHot = _index >= WarmupPeriod;
return _longStop; // Return long stop as primary value
}
/// <summary>
/// Gets the long stop level
/// </summary>
public double LongStop => _longStop;
/// <summary>
/// Gets the short stop level
/// </summary>
public double ShortStop => _shortStop;
}
-113
View File
@@ -1,113 +0,0 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// YZV: Yang-Zhang Volatility
/// A volatility estimator that combines overnight and trading volatilities,
/// providing a more complete picture of price variation while being drift-independent.
/// </summary>
/// <remarks>
/// The YZV calculation process:
/// 1. Calculate overnight (close-to-open) volatility
/// 2. Calculate open-to-close volatility
/// 3. Calculate Rogers-Satchell volatility
/// 4. Combine components with optimal weights
///
/// Key characteristics:
/// - Drift independence
/// - Minimum variance
/// - Handles overnight gaps
/// - Uses all HLOC prices
/// - Optimal weighting
///
/// Formula:
/// YZV = sqrt(Vo + k*Vc + (1-k)*Vrs)
/// where:
/// Vo = overnight volatility
/// Vc = open-to-close volatility
/// Vrs = Rogers-Satchell volatility
/// k ≈ 0.34 (optimal weight)
///
/// Market Applications:
/// - Option pricing
/// - Risk measurement
/// - Trading systems
/// - Portfolio management
/// - Market analysis
///
/// Note: Most efficient unbiased estimator among drift-independent estimators
/// </remarks>
[SkipLocalsInit]
public sealed class Yzv : AbstractBase
{
private readonly Sma _maCo; // Close-to-Open
private readonly Sma _maOc; // Open-to-Close
private readonly Sma _maRs; // Rogers-Satchell
private double _prevClose;
private double _prevValue;
private const double K = 0.34; // Optimal weight
private const int DefaultPeriod = 20;
/// <param name="period">The number of periods for volatility calculation (default 20).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Yzv(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_maCo = new(period);
_maOc = new(period);
_maRs = new(period);
WarmupPeriod = period;
Name = $"YZV({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for volatility calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Yzv(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_prevClose = BarInput.Close;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
if (!BarInput.IsNew)
return _prevValue;
ManageState(true);
// Calculate overnight volatility (close-to-open)
double co = Math.Log(BarInput.Open / _prevClose);
double vo = _maCo.Calc(co * co, true);
// Calculate open-to-close volatility
double oc = Math.Log(BarInput.Close / BarInput.Open);
double vc = _maOc.Calc(oc * oc, true);
// Calculate Rogers-Satchell volatility component
double lnHC = Math.Log(BarInput.High / BarInput.Close);
double lnHO = Math.Log(BarInput.High / BarInput.Open);
double lnLC = Math.Log(BarInput.Low / BarInput.Close);
double lnLO = Math.Log(BarInput.Low / BarInput.Open);
double rs = (lnHC * lnHO) + (lnLC * lnLO);
double vrs = _maRs.Calc(rs, true);
// Combine components with optimal weights
_prevValue = Math.Sqrt(vo + (K * vc) + ((1.0 - K) * vrs));
return _prevValue;
}
}
-38
View File
@@ -1,38 +0,0 @@
# Volatility indicators
Done: 31, Todo: 4
✔️ ADR - Average Daily Range
✔️ AP - Andrew's Pitchfork
✔️ ATR - Average True Range
✔️ ATRP - Average True Range Percent
✔️ ATRS - ATR Trailing Stop
✔️ BBAND - Bollinger Bands® (Upper, Middle, Lower)
✔️ CCV - Close-to-Close Volatility
✔️ CE - Chandelier Exit
✔️ CV - Conditional Volatility (ARCH/GARCH)
✔️ CVI - Chaikin's Volatility
✔️ DCHN - Donchian Channels (Upper, Middle, Lower)
✔️ EWMA - Exponential Weighted Moving Average Volatility
✔️ FCB - Fractal Chaos Bands
✔️ GKV - Garman-Klass Volatility
✔️ HLV - High-Low Volatility
✔️ HV - Historical Volatility
✔️ JVOLTY - Jurik Volatility (Jvolty, Upper band, Lower band)
✔️ NATR - Normalized Average True Range
✔️ PCH - Price Channel Indicator
✔️ PV - Parkinson Volatility
✔️ RSV - Rogers-Satchell Volatility
✔️ RV - Realized Volatility
✔️ RVI - Relative Volatility Index
✔️ SV - Stochastic Volatility
✔️ TR - True Range
✔️ UI - Ulcer Index
✔️ VC - Volatility Cone (Mean, Upper Bound, Lower Bound)
✔️ VOV - Volatility of Volatility
✔️ VR - Volatility Ratio
✔️ *VS - Volatility Stop (Long Stop, Short Stop)
✔️ YZV - Yang-Zhang Volatility
ICH - Ichimoku Cloud (Conversion, Base, Leading Span A, Leading Span B, Lagging Span)
KC - Keltner Channels (Upper, Middle, Lower)
PSAR - Parabolic Stop and Reverse (Value, Trend)
STARC - Starc Bands (Upper, Middle, Lower)