mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-23 21:18:04 +00:00
feat: add new indicators (Decay, Edecay, MinusDi, MinusDm, PlusDi, PlusDm, Maxindex, Minindex, Sarext) and update pine scripts, core libs, validation tests, and python bindings
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace QuanTAlib;
|
||||
|
||||
/// <summary>
|
||||
/// MINUS_DI: Minus Directional Indicator (Wilder, 1978)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Measures downward directional movement as a percentage of true range.
|
||||
/// Extracted from the DX calculation: -DI = Smoothed(-DM) / Smoothed(TR) × 100.
|
||||
/// Range: 0 to 100. Higher values indicate stronger downward movement.
|
||||
/// </remarks>
|
||||
[SkipLocalsInit]
|
||||
public sealed class MinusDi : ITValuePublisher
|
||||
{
|
||||
private readonly Dx _dx;
|
||||
|
||||
/// <summary>Display name for the indicator.</summary>
|
||||
public string Name { get; }
|
||||
|
||||
public event TValuePublishedHandler? Pub;
|
||||
|
||||
/// <summary>Current -DI value.</summary>
|
||||
public TValue Last { get; private set; }
|
||||
|
||||
/// <summary>True when the indicator has warmed up.</summary>
|
||||
public bool IsHot => _dx.IsHot;
|
||||
|
||||
/// <summary>Bars required for warmup.</summary>
|
||||
public int WarmupPeriod => _dx.WarmupPeriod;
|
||||
|
||||
/// <summary>The period parameter.</summary>
|
||||
public int Period => _dx.Period;
|
||||
|
||||
/// <summary>
|
||||
/// Creates MinusDi with specified period.
|
||||
/// </summary>
|
||||
/// <param name="period">Wilder smoothing period (must be > 0)</param>
|
||||
public MinusDi(int period = 14)
|
||||
{
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
_dx = new Dx(period);
|
||||
Name = $"MinusDi({period})";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates MinusDi and immediately processes the bar series.
|
||||
/// </summary>
|
||||
public MinusDi(TBarSeries source, int period = 14) : this(period)
|
||||
{
|
||||
var result = Batch(source, period);
|
||||
Last = result[^1];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue Update(TBar input, bool isNew = true)
|
||||
{
|
||||
_dx.Update(input, isNew);
|
||||
Last = _dx.DiMinus;
|
||||
if (isNew)
|
||||
{
|
||||
Pub?.Invoke(this, new TValueEventArgs { Value = Last, IsNew = isNew });
|
||||
}
|
||||
return Last;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TValue Update(TValue input, bool isNew = true)
|
||||
{
|
||||
// DI requires OHLC data — scalar update not meaningful
|
||||
return Last;
|
||||
}
|
||||
|
||||
public TSeries Update(TBarSeries source)
|
||||
{
|
||||
var result = new TSeries(source.Count);
|
||||
foreach (var bar in source)
|
||||
{
|
||||
result.Add(Update(bar));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the indicator state using the provided bar series history.
|
||||
/// </summary>
|
||||
public void Prime(TBarSeries source)
|
||||
{
|
||||
foreach (var bar in source)
|
||||
{
|
||||
Update(bar);
|
||||
}
|
||||
}
|
||||
|
||||
public void Prime(ReadOnlySpan<double> source, TimeSpan? step = null)
|
||||
{
|
||||
// Not applicable — DI requires OHLC bar data, not scalar values
|
||||
}
|
||||
|
||||
public static TSeries Batch(TBarSeries source, int period = 14)
|
||||
{
|
||||
var indicator = new MinusDi(period);
|
||||
return indicator.Update(source);
|
||||
}
|
||||
|
||||
public static (TSeries Results, MinusDi Indicator) Calculate(TBarSeries source, int period = 14)
|
||||
{
|
||||
var indicator = new MinusDi(period);
|
||||
return (indicator.Update(source), indicator);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Reset()
|
||||
{
|
||||
_dx.Reset();
|
||||
Last = default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
# MINUS_DI: Minus Directional Indicator
|
||||
|
||||
### TL;DR
|
||||
Measures downward directional movement strength as a percentage (0-100).
|
||||
|
||||
## Introduction
|
||||
The Minus Directional Indicator (-DI) measures the strength of downward price movement relative to the true range. It is one of the components of the Directional Movement System developed by J. Welles Wilder Jr.
|
||||
|
||||
When -DI is rising, downward price pressure is increasing. When -DI crosses above +DI, it signals a potential bearish trend. The -DI line is commonly plotted alongside +DI to visualize directional balance.
|
||||
|
||||
## Calculation
|
||||
-DI = Smoothed(-DM) / Smoothed(TR) × 100
|
||||
|
||||
Where:
|
||||
- -DM (Minus Directional Movement) = max(PrevLow - Low, 0) when PrevLow - Low > High - PrevHigh, else 0
|
||||
- TR (True Range) = max(High - Low, |High - PrevClose|, |Low - PrevClose|)
|
||||
- Smoothing uses Wilder's method: Smooth = Smooth - Smooth/N + Input
|
||||
|
||||
## Parameters
|
||||
| Parameter | Default | Range | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| Period | 14 | 2-∞ | Wilder smoothing period |
|
||||
|
||||
## Interpretation
|
||||
- **Rising -DI:** Strengthening downward movement
|
||||
- **-DI > +DI:** Bears dominate; potential downtrend
|
||||
- **-DI crossover above +DI:** Bearish signal
|
||||
- **High -DI (>40):** Strong downward momentum
|
||||
|
||||
## References
|
||||
- Wilder, J. Welles Jr. "New Concepts in Technical Trading Systems" (1978)
|
||||
Reference in New Issue
Block a user