Files
QuanTAlib/lib/volatility/Atr.cs
T

133 lines
4.0 KiB
C#
Raw Normal View History

2024-10-27 16:11:08 -07:00
using System.Runtime.CompilerServices;
2024-09-30 06:46:07 -07:00
namespace QuanTAlib;
2024-10-05 15:20:13 -07:00
/// <summary>
2024-10-27 09:38:53 -07:00
/// 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.
2024-10-05 15:20:13 -07:00
/// </summary>
/// <remarks>
2024-10-27 09:38:53 -07:00
/// 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
2024-10-05 15:20:13 -07:00
/// </remarks>
2024-10-27 16:11:08 -07:00
[SkipLocalsInit]
public sealed class Atr : AbstractBase
2024-10-06 06:59:26 +00:00
{
2024-10-21 16:06:47 -07:00
public double Tr { get; private set; }
private readonly Rma _ma;
2024-09-30 06:46:07 -07:00
private double _prevClose, _p_prevClose;
2024-10-27 09:38:53 -07:00
/// <param name="period">The number of periods for ATR calculation.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
2024-10-27 16:11:08 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-10-06 06:59:26 +00:00
public Atr(int period)
{
if (period < 1)
{
2024-10-27 09:38:53 -07:00
throw new ArgumentOutOfRangeException(nameof(period),
"Period must be greater than or equal to 1.");
2024-09-30 06:46:07 -07:00
}
2024-10-21 16:06:47 -07:00
_ma = new(period, useSma: true);
2024-09-30 06:46:07 -07:00
WarmupPeriod = _ma.WarmupPeriod;
2024-09-30 07:30:27 -07:00
Name = $"ATR({period})";
2024-09-30 06:46:07 -07:00
}
2024-10-27 09:38:53 -07:00
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The number of periods for ATR calculation.</param>
2024-10-27 16:11:08 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-10-06 06:59:26 +00:00
public Atr(object source, int period) : this(period)
{
2024-09-30 06:46:07 -07:00
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
2024-10-27 16:11:08 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-10-06 06:59:26 +00:00
public override void Init()
{
2024-09-30 06:46:07 -07:00
base.Init();
_ma.Init();
_prevClose = double.NaN;
2024-10-21 16:06:47 -07:00
Tr = 0;
2024-09-30 06:46:07 -07:00
}
2024-10-27 16:11:08 -07:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2024-10-06 06:59:26 +00:00
protected override void ManageState(bool isNew)
{
if (isNew)
{
2024-09-30 06:46:07 -07:00
_index++;
_p_prevClose = _prevClose;
2024-10-06 06:59:26 +00:00
}
else
{
2024-09-30 06:46:07 -07:00
_prevClose = _p_prevClose;
}
}
2024-10-27 16:11:08 -07:00
[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)]
2024-10-06 06:59:26 +00:00
protected override double Calculation()
{
2024-10-07 21:41:26 -07:00
ManageState(BarInput.IsNew);
2024-09-30 06:46:07 -07:00
2024-10-21 16:06:47 -07:00
if (_index == 1)
2024-10-06 06:59:26 +00:00
{
2024-10-27 09:38:53 -07:00
// First bar uses simple high-low range
2024-10-21 16:06:47 -07:00
Tr = BarInput.High - BarInput.Low;
_prevClose = BarInput.Close;
2024-09-30 06:46:07 -07:00
}
2024-10-21 16:06:47 -07:00
else
{
2024-10-27 09:38:53 -07:00
// Calculate True Range as maximum of three measures
2024-10-27 16:11:08 -07:00
Tr = CalculateTrueRange(BarInput.High, BarInput.Low, _prevClose);
2024-10-21 16:06:47 -07:00
}
2024-10-27 09:38:53 -07:00
// Apply RMA smoothing to True Range
2024-10-21 16:06:47 -07:00
_ma.Calc(new TValue(Input.Time, Tr, BarInput.IsNew));
2024-09-30 06:46:07 -07:00
IsHot = _ma.IsHot;
2024-10-07 21:41:26 -07:00
_prevClose = BarInput.Close;
2024-10-21 16:06:47 -07:00
return _ma.Value;
2024-09-30 06:46:07 -07:00
}
}