charts for Quantower
This commit is contained in:
Miha Kralj
2024-11-06 20:56:32 -08:00
parent 0bae9ce15b
commit 582a0256ec
75 changed files with 652 additions and 281 deletions
+45 -68
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,5 +1,4 @@
# Momentum indicators
Done: 15, Todo: 2
✔️ ADX - Average Directional Movement Index
✔️ ADXR - Average Directional Movement Index Rating