mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-12 23:58:04 +00:00
first iteration
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user