new indicators

This commit is contained in:
Miha Kralj
2024-11-03 18:16:42 -08:00
parent 4f2772f130
commit 5d086a1473
23 changed files with 2100 additions and 201 deletions
+100
View File
@@ -0,0 +1,100 @@
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();
}
+94
View File
@@ -0,0 +1,94 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// NATR: Normalized Average True Range
/// A volatility indicator that expresses ATR as a percentage of closing price,
/// making it more comparable across different price levels.
/// </summary>
/// <remarks>
/// The NATR calculation process:
/// 1. Calculate True Range (TR):
/// TR = max(high-low, abs(high-prevClose), abs(low-prevClose))
/// 2. Calculate ATR using SMA of TR
/// 3. Normalize by dividing ATR by close price and multiply by 100
/// 4. Updates with each new price bar
///
/// Key characteristics:
/// - Normalized volatility measure
/// - Period-based average
/// - Trend independent
/// - Percentage-based measure
/// - Comparable across instruments
///
/// Formula:
/// TR = max(high-low, abs(high-prevClose), abs(low-prevClose))
/// ATR = SMA(TR, period)
/// NATR = (ATR / Close) * 100
///
/// Market Applications:
/// - Cross-market comparison
/// - Position sizing
/// - Volatility analysis
/// - Risk assessment
/// - Market regime identification
///
/// Note: More suitable for comparing volatility across different instruments than ATR
/// </remarks>
[SkipLocalsInit]
public sealed class Natr : AbstractBase
{
private readonly Sma _ma;
private double _prevClose;
private const int DefaultPeriod = 14;
/// <param name="period">The number of periods for NATR calculation (default 14).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Natr(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_ma = new(period);
WarmupPeriod = period;
Name = $"NATR({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for NATR calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Natr(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_prevClose = BarInput.Close;
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate True Range
double hl = BarInput.High - BarInput.Low;
double hc = Math.Abs(BarInput.High - _prevClose);
double lc = Math.Abs(BarInput.Low - _prevClose);
double tr = Math.Max(hl, Math.Max(hc, lc));
// Calculate ATR
double atr = _ma.Calc(tr, BarInput.IsNew);
// Normalize ATR
return (atr / BarInput.Close) * 100.0;
}
}
+102
View File
@@ -0,0 +1,102 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// PCH: Price Channel
/// A volatility indicator that identifies the highest high and lowest low
/// over a specified period, creating a channel that contains price movement.
/// </summary>
/// <remarks>
/// The PCH calculation process:
/// 1. Track highest high over period
/// 2. Track lowest low over period
/// 3. Calculate midline as average of high and low
/// 4. Updates with each new price bar
///
/// Key characteristics:
/// - Trend following indicator
/// - Support/resistance identification
/// - Breakout detection
/// - Volatility measurement
/// - Range-based analysis
///
/// Formula:
/// Upper = Highest High over period
/// Lower = Lowest Low over period
/// Middle = (Upper + Lower) / 2
///
/// Market Applications:
/// - Trend identification
/// - Support/resistance levels
/// - Breakout trading
/// - Volatility analysis
/// - Range-bound trading
///
/// Note: Also known as Donchian Channels
/// </remarks>
[SkipLocalsInit]
public sealed class Pch : AbstractBase
{
private readonly CircularBuffer _highs;
private readonly CircularBuffer _lows;
private const int DefaultPeriod = 20;
/// <param name="period">The number of periods for PCH calculation (default 20).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pch(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_highs = new(period);
_lows = new(period);
WarmupPeriod = period;
Name = $"PCH({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for PCH calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pch(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_highs.Add(BarInput.High);
_lows.Add(BarInput.Low);
_index++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
// Calculate channel boundaries
double upper = _highs.Max();
double lower = _lows.Min();
// Return midline
return (upper + lower) / 2.0;
}
/// <summary>
/// Gets the upper channel value (highest high)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public double Upper() => _highs.Max();
/// <summary>
/// Gets the lower channel value (lowest low)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public double Lower() => _lows.Min();
}
+93
View File
@@ -0,0 +1,93 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// PV: Parkinson Volatility
/// A volatility measure that uses the high and low prices to estimate
/// volatility, assuming continuous trading and log-normal price distribution.
/// </summary>
/// <remarks>
/// The PV calculation process:
/// 1. Calculate squared log range for each period
/// 2. Apply scaling factor (1/4ln2)
/// 3. Average over specified period
/// 4. Take square root for final volatility
///
/// Key characteristics:
/// - Range-based volatility
/// - More efficient than close-to-close
/// - Assumes continuous trading
/// - No gap consideration
/// - Log-normal distribution
///
/// Formula:
/// PV = sqrt(1/(4*ln(2)*n) * Σ(ln(High/Low))²)
/// where n is the number of periods
///
/// Market Applications:
/// - Volatility estimation
/// - Risk assessment
/// - Option pricing
/// - Trading system development
/// - Market regime identification
///
/// Note: More efficient than traditional volatility measures but sensitive to gaps
/// </remarks>
[SkipLocalsInit]
public sealed class Pv : AbstractBase
{
private readonly Sma _ma;
private readonly double _scaleFactor;
private const int DefaultPeriod = 10;
private double _prevValue;
/// <param name="period">The number of periods for PV calculation (default 10).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pv(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
_ma = new(period);
_scaleFactor = 1.0 / (4.0 * Math.Log(2.0));
WarmupPeriod = period;
Name = $"PV({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for PV calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Pv(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
_index++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
if (!BarInput.IsNew)
return _prevValue;
ManageState(true);
// Calculate log range squared
double logRange = Math.Log(BarInput.High / BarInput.Low);
double logRangeSquared = logRange * logRange;
// Apply moving average and scaling
double meanLogRangeSquared = _ma.Calc(logRangeSquared, true);
// Calculate final volatility
_prevValue = Math.Sqrt(_scaleFactor * meanLogRangeSquared);
return _prevValue;
}
}
+93
View File
@@ -0,0 +1,93 @@
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;
}
}
+105
View File
@@ -0,0 +1,105 @@
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;
}
}
+113
View File
@@ -0,0 +1,113 @@
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;
}
}
+9 -9
View File
@@ -1,5 +1,5 @@
# Volatility indicators
Done: 24, Todo: 11
Done: 25, Todo: 10
✔️ ADR - Average Daily Range
✔️ AP - Andrew's Pitchfork
@@ -11,28 +11,28 @@ Done: 24, Todo: 11
✔️ CE - Chandelier Exit
✔️ CV - Conditional Volatility (ARCH/GARCH)
✔️ CVI - Chaikin's Volatility
*DC - Donchian Channels (Upper, Middle, Lower)
✔️ 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
*ICH - Ichimoku Cloud (Conversion, Base, Leading Span A, Leading Span B, Lagging Span)
✔️ JVOLTY - Jurik Volatility
✔️ *JVOLTY - Jurik Volatility (Jvolty, Upper band, Lower band)
*KC - Keltner Channels (Upper, Middle, Lower)
NATR - Normalized Average True Range
PCH - Price Channel Indicator
✔️ NATR - Normalized Average True Range
✔️ PCH - Price Channel Indicator
*PSAR - Parabolic Stop and Reverse (Value, Trend)
PV - Parkinson Volatility
RSV - Rogers-Satchell Volatility
✔️ PV - Parkinson Volatility
✔️ RSV - Rogers-Satchell Volatility
✔️ RV - Realized Volatility
✔️ RVI - Relative Volatility Index
*STARC - Starc Bands (Upper, Middle, Lower)
SV - Stochastic Volatility
✔️ 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
✔️ YZV - Yang-Zhang Volatility