mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-20 03:28:05 +00:00
Momentum
charts for Quantower
This commit is contained in:
+45
-68
@@ -24,10 +24,13 @@ namespace QuanTAlib;
|
||||
///
|
||||
/// Formula:
|
||||
/// TR = max(high-low, abs(high-prevClose), abs(low-prevClose))
|
||||
/// +DM = if(high-prevHigh > prevLow-low) then max(high-prevHigh, 0) else 0
|
||||
/// -DM = if(prevLow-low > high-prevHigh) then max(prevLow-low, 0) else 0
|
||||
/// +DI = 100 * smoothed(+DM) / smoothed(TR)
|
||||
/// -DI = 100 * smoothed(-DM) / smoothed(TR)
|
||||
/// +DM = if(high-prevHigh > prevLow-low && high-prevHigh > 0) then high-prevHigh else 0
|
||||
/// -DM = if(prevLow-low > high-prevHigh && prevLow-low > 0) then prevLow-low else 0
|
||||
/// Smoothed TR = Wilder's smoothing of TR (ATR)
|
||||
/// Smoothed +DM = Wilder's smoothing of +DM
|
||||
/// Smoothed -DM = Wilder's smoothing of -DM
|
||||
/// +DI = 100 * Smoothed(+DM) / Smoothed(TR)
|
||||
/// -DI = 100 * Smoothed(-DM) / Smoothed(TR)
|
||||
///
|
||||
/// Sources:
|
||||
/// J. Welles Wilder Jr. - "New Concepts in Technical Trading Systems" (1978)
|
||||
@@ -36,49 +39,41 @@ namespace QuanTAlib;
|
||||
/// Note: Default period of 14 was recommended by Wilder
|
||||
/// </remarks>
|
||||
[SkipLocalsInit]
|
||||
public sealed class Dmi : AbstractBarBase
|
||||
public sealed class Dmi : AbstractBase
|
||||
{
|
||||
private readonly Rma _smoothedTr;
|
||||
private readonly Atr _atr;
|
||||
private readonly Rma _smoothedPlusDm;
|
||||
private readonly Rma _smoothedMinusDm;
|
||||
private double _prevHigh, _prevLow, _prevClose;
|
||||
private double _p_prevHigh, _p_prevLow, _p_prevClose;
|
||||
private double _prevHigh, _prevLow;
|
||||
private double _p_prevHigh, _p_prevLow;
|
||||
private double _plusDi, _minusDi;
|
||||
private const double ScalingFactor = 100.0;
|
||||
private const int DefaultPeriod = 14;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the most recent +DI value
|
||||
/// </summary>
|
||||
public double PlusDI => _plusDi;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the most recent -DI value
|
||||
/// </summary>
|
||||
public double MinusDI => _minusDi;
|
||||
|
||||
/// <param name="period">The number of periods used in the DMI calculation (default 14).</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Dmi(int period = DefaultPeriod)
|
||||
{
|
||||
if (period < 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(period));
|
||||
_smoothedTr = new(period, useSma: true);
|
||||
_smoothedPlusDm = new(period, useSma: true);
|
||||
_smoothedMinusDm = new(period, useSma: true);
|
||||
_index = 0;
|
||||
_atr = new(period);
|
||||
_smoothedPlusDm = new(period);
|
||||
_smoothedMinusDm = new(period);
|
||||
WarmupPeriod = period + 1;
|
||||
Name = $"DMI({period})";
|
||||
}
|
||||
|
||||
/// <param name="source">The data source object that publishes updates.</param>
|
||||
/// <param name="period">The number of periods used in the DMI calculation.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Dmi(object source, int period) : this(period)
|
||||
public override void Init()
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
|
||||
base.Init();
|
||||
_atr.Init();
|
||||
_smoothedPlusDm.Init();
|
||||
_smoothedMinusDm.Init();
|
||||
_prevHigh = _prevLow = double.NaN;
|
||||
_p_prevHigh = _p_prevLow = double.NaN;
|
||||
_plusDi = _minusDi = 0;
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -89,25 +84,14 @@ public sealed class Dmi : AbstractBarBase
|
||||
_index++;
|
||||
_p_prevHigh = _prevHigh;
|
||||
_p_prevLow = _prevLow;
|
||||
_p_prevClose = _prevClose;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prevHigh = _p_prevHigh;
|
||||
_prevLow = _p_prevLow;
|
||||
_prevClose = _p_prevClose;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static double CalculateTrueRange(double high, double low, double prevClose)
|
||||
{
|
||||
double hl = high - low;
|
||||
double hpc = Math.Abs(high - prevClose);
|
||||
double lpc = Math.Abs(low - prevClose);
|
||||
return Math.Max(hl, Math.Max(hpc, lpc));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static (double plusDm, double minusDm) CalculateDirectionalMovement(
|
||||
double high, double low, double prevHigh, double prevLow)
|
||||
@@ -115,13 +99,8 @@ public sealed class Dmi : AbstractBarBase
|
||||
double upMove = high - prevHigh;
|
||||
double downMove = prevLow - low;
|
||||
|
||||
double plusDm = 0.0;
|
||||
double minusDm = 0.0;
|
||||
|
||||
if (upMove > downMove && upMove > 0)
|
||||
plusDm = upMove;
|
||||
else if (downMove > upMove && downMove > 0)
|
||||
minusDm = downMove;
|
||||
double plusDm = (upMove > downMove && upMove > 0) ? upMove : 0;
|
||||
double minusDm = (downMove > upMove && downMove > 0) ? downMove : 0;
|
||||
|
||||
return (plusDm, minusDm);
|
||||
}
|
||||
@@ -129,38 +108,36 @@ public sealed class Dmi : AbstractBarBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
ManageState(BarInput.IsNew);
|
||||
|
||||
if (_index == 1)
|
||||
if (double.IsNaN(_prevHigh))
|
||||
{
|
||||
_prevHigh = Input.High;
|
||||
_prevLow = Input.Low;
|
||||
_prevClose = Input.Close;
|
||||
_prevHigh = BarInput.High;
|
||||
_prevLow = BarInput.Low;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Calculate True Range and Directional Movement
|
||||
double tr = CalculateTrueRange(Input.High, Input.Low, _prevClose);
|
||||
// Calculate ATR
|
||||
double atr = _atr.Calc(BarInput).Value;
|
||||
|
||||
// Calculate Directional Movement
|
||||
var (plusDm, minusDm) = CalculateDirectionalMovement(
|
||||
Input.High, Input.Low, _prevHigh, _prevLow);
|
||||
BarInput.High, BarInput.Low, _prevHigh, _prevLow);
|
||||
|
||||
// Update previous values
|
||||
_prevHigh = Input.High;
|
||||
_prevLow = Input.Low;
|
||||
_prevClose = Input.Close;
|
||||
// Update previous values for next calculation
|
||||
_prevHigh = BarInput.High;
|
||||
_prevLow = BarInput.Low;
|
||||
|
||||
// Smooth the indicators using Wilder's method
|
||||
_smoothedTr.Calc(tr, Input.IsNew);
|
||||
_smoothedPlusDm.Calc(plusDm, Input.IsNew);
|
||||
_smoothedMinusDm.Calc(minusDm, Input.IsNew);
|
||||
// Smooth DM values using Wilder's method
|
||||
double smoothedPlusDm = _smoothedPlusDm.Calc(plusDm, BarInput.IsNew).Value;
|
||||
double smoothedMinusDm = _smoothedMinusDm.Calc(minusDm, BarInput.IsNew).Value;
|
||||
|
||||
// Calculate +DI and -DI
|
||||
double smoothedTr = _smoothedTr.Value;
|
||||
if (smoothedTr > 0)
|
||||
// Calculate DI values
|
||||
if (atr > 0)
|
||||
{
|
||||
_plusDi = ScalingFactor * _smoothedPlusDm.Value / smoothedTr;
|
||||
_minusDi = ScalingFactor * _smoothedMinusDm.Value / smoothedTr;
|
||||
return _plusDi - _minusDi; // Return the difference as main value
|
||||
_plusDi = ScalingFactor * smoothedPlusDm / atr;
|
||||
_minusDi = ScalingFactor * smoothedMinusDm / atr;
|
||||
return _plusDi - _minusDi;
|
||||
}
|
||||
|
||||
_plusDi = 0.0;
|
||||
|
||||
+32
-113
@@ -4,16 +4,13 @@ namespace QuanTAlib;
|
||||
/// <summary>
|
||||
/// DMX: Enhanced Directional Movement Index using JMA smoothing
|
||||
/// An improvement over the traditional DMI indicator that uses Jurik Moving Average (JMA)
|
||||
/// for smoothing instead of Wilder's moving average. This enhancement provides better
|
||||
/// noise reduction while maintaining responsiveness to significant price movements.
|
||||
/// for smoothing. This enhancement provides better noise reduction while maintaining
|
||||
/// responsiveness to significant price movements.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The DMX calculation process:
|
||||
/// 1. Calculate True Range (TR)
|
||||
/// 2. Calculate +DM (Positive Directional Movement)
|
||||
/// 3. Calculate -DM (Negative Directional Movement)
|
||||
/// 4. Smooth TR, +DM, and -DM using JMA instead of Wilder's smoothing
|
||||
/// 5. Calculate +DI and -DI as percentages
|
||||
/// 1. Calculate DMI using the standard Dmi class
|
||||
/// 2. Apply JMA smoothing to the +DI and -DI values
|
||||
///
|
||||
/// Key improvements over DMI:
|
||||
/// - Uses JMA's adaptive volatility-based smoothing
|
||||
@@ -22,11 +19,9 @@ namespace QuanTAlib;
|
||||
/// - Reduced lag through JMA's phase-shifting
|
||||
///
|
||||
/// Formula:
|
||||
/// TR = max(high-low, abs(high-prevClose), abs(low-prevClose))
|
||||
/// +DM = if(high-prevHigh > prevLow-low) then max(high-prevHigh, 0) else 0
|
||||
/// -DM = if(prevLow-low > high-prevHigh) then max(prevLow-low, 0) else 0
|
||||
/// +DI = 100 * JMA(+DM) / JMA(TR)
|
||||
/// -DI = 100 * JMA(-DM) / JMA(TR)
|
||||
/// DMI calculation as per standard DMI
|
||||
/// DMX +DI = JMA(DMI +DI)
|
||||
/// DMX -DI = JMA(DMI -DI)
|
||||
///
|
||||
/// Sources:
|
||||
/// Original DMI by J. Welles Wilder Jr. - "New Concepts in Technical Trading Systems" (1978)
|
||||
@@ -35,53 +30,40 @@ namespace QuanTAlib;
|
||||
[SkipLocalsInit]
|
||||
public sealed class Dmx : AbstractBarBase
|
||||
{
|
||||
private readonly Jma _smoothedTr;
|
||||
private readonly Jma _smoothedPlusDm;
|
||||
private readonly Jma _smoothedMinusDm;
|
||||
private double _prevHigh, _prevLow, _prevClose;
|
||||
private double _p_prevHigh, _p_prevLow, _p_prevClose;
|
||||
private readonly Dmi _dmi;
|
||||
private readonly Jma _smoothedPlusDi;
|
||||
private readonly Jma _smoothedMinusDi;
|
||||
private double _plusDi, _minusDi;
|
||||
private const double ScalingFactor = 100.0;
|
||||
private const int DefaultPeriod = 10;
|
||||
private const int DefaultDmiPeriod = 14;
|
||||
private const int DefaultJmaPeriod = 7;
|
||||
private const int DefaultPhase = 100;
|
||||
private const double DefaultFactor = 0.25;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the most recent +DI value
|
||||
/// Gets the most recent smoothed +DI value
|
||||
/// </summary>
|
||||
public double PlusDI => _plusDi;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the most recent -DI value
|
||||
/// Gets the most recent smoothed -DI value
|
||||
/// </summary>
|
||||
public double MinusDI => _minusDi;
|
||||
|
||||
/// <param name="period">The number of periods used in the DMX calculation (default 14).</param>
|
||||
/// <param name="phase">The phase for the JMA smoothing (default 0).</param>
|
||||
/// <param name="factor">The factor for the JMA smoothing (default 0.45).</param>
|
||||
/// <param name="dmiPeriod">The number of periods used in the DMI calculation (default 14).</param>
|
||||
/// <param name="jmaPeriod">The number of periods used in the JMA smoothing (default 10).</param>
|
||||
/// <param name="phase">The phase for the JMA smoothing (default 100).</param>
|
||||
/// <param name="factor">The factor for the JMA smoothing (default 0.25).</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Dmx(int period = DefaultPeriod, int phase = DefaultPhase, double factor = DefaultFactor)
|
||||
public Dmx(int period = DefaultDmiPeriod, int jmaPeriod = DefaultJmaPeriod, int phase = DefaultPhase, double factor = DefaultFactor)
|
||||
{
|
||||
if (period < 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(period));
|
||||
_smoothedTr = new(period, phase, factor);
|
||||
_smoothedPlusDm = new(period, phase, factor);
|
||||
_smoothedMinusDm = new(period, phase, factor);
|
||||
_index = 0;
|
||||
WarmupPeriod = period * 2; // JMA needs more warmup periods than RMA
|
||||
Name = $"DMX({period})";
|
||||
}
|
||||
|
||||
/// <param name="source">The data source object that publishes updates.</param>
|
||||
/// <param name="period">The number of periods used in the DMX calculation.</param>
|
||||
/// <param name="phase">The phase for the JMA smoothing.</param>
|
||||
/// <param name="factor">The factor for the JMA smoothing.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Dmx(object source, int period, int phase = DefaultPhase, double factor = DefaultFactor) : this(period, phase, factor)
|
||||
{
|
||||
var pubEvent = source.GetType().GetEvent("Pub");
|
||||
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
|
||||
if (period < 1 || jmaPeriod < 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(period), "Periods must be greater than or equal to 1.");
|
||||
_dmi = new(period);
|
||||
_smoothedPlusDi = new(jmaPeriod, phase, factor);
|
||||
_smoothedMinusDi = new(jmaPeriod, phase, factor);
|
||||
WarmupPeriod = period + jmaPeriod;
|
||||
Name = $"DMX({period},{jmaPeriod})";
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -90,43 +72,7 @@ public sealed class Dmx : AbstractBarBase
|
||||
if (isNew)
|
||||
{
|
||||
_index++;
|
||||
_p_prevHigh = _prevHigh;
|
||||
_p_prevLow = _prevLow;
|
||||
_p_prevClose = _prevClose;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prevHigh = _p_prevHigh;
|
||||
_prevLow = _p_prevLow;
|
||||
_prevClose = _p_prevClose;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static double CalculateTrueRange(double high, double low, double prevClose)
|
||||
{
|
||||
double hl = high - low;
|
||||
double hpc = Math.Abs(high - prevClose);
|
||||
double lpc = Math.Abs(low - prevClose);
|
||||
return Math.Max(hl, Math.Max(hpc, lpc));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private static (double plusDm, double minusDm) CalculateDirectionalMovement(
|
||||
double high, double low, double prevHigh, double prevLow)
|
||||
{
|
||||
double upMove = high - prevHigh;
|
||||
double downMove = prevLow - low;
|
||||
|
||||
double plusDm = 0.0;
|
||||
double minusDm = 0.0;
|
||||
|
||||
if (upMove > downMove && upMove > 0)
|
||||
plusDm = upMove;
|
||||
else if (downMove > upMove && downMove > 0)
|
||||
minusDm = downMove;
|
||||
|
||||
return (plusDm, minusDm);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
@@ -134,40 +80,13 @@ public sealed class Dmx : AbstractBarBase
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
|
||||
if (_index == 1)
|
||||
{
|
||||
_prevHigh = Input.High;
|
||||
_prevLow = Input.Low;
|
||||
_prevClose = Input.Close;
|
||||
return 0.0;
|
||||
}
|
||||
// Calculate DMI
|
||||
_dmi.Calc(Input);
|
||||
|
||||
// Calculate True Range and Directional Movement
|
||||
double tr = CalculateTrueRange(Input.High, Input.Low, _prevClose);
|
||||
var (plusDm, minusDm) = CalculateDirectionalMovement(
|
||||
Input.High, Input.Low, _prevHigh, _prevLow);
|
||||
// Smooth the DMI values using JMA
|
||||
_plusDi = _smoothedPlusDi.Calc(_dmi.PlusDI, Input.IsNew).Value;
|
||||
_minusDi = _smoothedMinusDi.Calc(_dmi.MinusDI, Input.IsNew).Value;
|
||||
|
||||
// Update previous values
|
||||
_prevHigh = Input.High;
|
||||
_prevLow = Input.Low;
|
||||
_prevClose = Input.Close;
|
||||
|
||||
// Smooth the indicators using JMA
|
||||
_smoothedTr.Calc(tr, Input.IsNew);
|
||||
_smoothedPlusDm.Calc(plusDm, Input.IsNew);
|
||||
_smoothedMinusDm.Calc(minusDm, Input.IsNew);
|
||||
|
||||
// Calculate +DI and -DI
|
||||
double smoothedTr = _smoothedTr.Value;
|
||||
if (smoothedTr > 0)
|
||||
{
|
||||
_plusDi = ScalingFactor * _smoothedPlusDm.Value / smoothedTr;
|
||||
_minusDi = ScalingFactor * _smoothedMinusDm.Value / smoothedTr;
|
||||
return _plusDi - _minusDi; // Return the difference as main value
|
||||
}
|
||||
|
||||
_plusDi = 0.0;
|
||||
_minusDi = 0.0;
|
||||
return 0.0;
|
||||
return _plusDi - _minusDi; // Return the difference as main value
|
||||
}
|
||||
}
|
||||
|
||||
+3
-8
@@ -82,12 +82,13 @@ public sealed class Dpo : AbstractBase
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
protected override double Calculation()
|
||||
{
|
||||
|
||||
ManageState(BarInput.IsNew);
|
||||
|
||||
// Add current price to buffer
|
||||
_prices.Add(BarInput.Close);
|
||||
|
||||
_prices.Add(BarInput.Close, BarInput.IsNew);
|
||||
// Need enough prices for the shifted SMA calculation
|
||||
|
||||
if (_index <= _shift)
|
||||
{
|
||||
return 0;
|
||||
@@ -96,12 +97,6 @@ public sealed class Dpo : AbstractBase
|
||||
// Add price from shift periods ago to SMA buffer
|
||||
_sma.Add(_prices[_shift]);
|
||||
|
||||
// Need enough prices for full calculation
|
||||
if (_index <= WarmupPeriod)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate DPO
|
||||
double dpo = BarInput.Close - _sma.Average();
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# Momentum indicators
|
||||
Done: 15, Todo: 2
|
||||
|
||||
✔️ ADX - Average Directional Movement Index
|
||||
✔️ ADXR - Average Directional Movement Index Rating
|
||||
|
||||
Reference in New Issue
Block a user