This commit is contained in:
Miha Kralj
2024-11-01 17:38:50 -07:00
parent 447d90f4d6
commit 5b3f302ce4
4 changed files with 133 additions and 2 deletions
+1
View File
@@ -58,6 +58,7 @@ public class EventingTests
("Trima", new Trima(p), new Trima(input, p)),
("Vidya", new Vidya(p), new Vidya(input, p)),
("Apo", new Apo(12, 26), new Apo(input, 12, 26)),
("Macd", new Macd(12, 26, 9), new Macd(input, 12, 26, 9)),
("Rsi", new Rsi(p), new Rsi(input, p)),
("Rsx", new Rsx(p), new Rsx(input, p)),
("Cmo", new Cmo(p), new Cmo(input, p)),
+15
View File
@@ -120,6 +120,21 @@ public class MomentumUpdateTests
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Macd_Update()
{
var indicator = new Macd(fastPeriod: 12, slowPeriod: 26, signalPeriod: 9);
double initialValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: true));
for (int i = 0; i < RandomUpdates; i++)
{
indicator.Calc(new TValue(DateTime.Now, GetRandomDouble() + 100, IsNew: false)); // Ensure positive prices
}
double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false));
Assert.Equal(initialValue, finalValue, precision);
}
[Fact]
public void Pmo_Update()
{
+3 -2
View File
@@ -14,7 +14,7 @@
- Total: 116 of 175 indicators implemented (66%)
|BASIC TRANSFORMS|Class Name|
|**BASIC TRANSFORMS**|**Class Name**|
|---|:--:|
|OC2 - Midpoint price|`.OC2`|
|HL2 - Median Price|`.HL2`|
@@ -22,8 +22,9 @@
|OHL3 - Mean Price|`.OHL3`|
|OHLC4 - Average Price|`.OHLC4`|
|HLCC4 - Weighted Price|`.HLCC4`|
<br>
|**AVERAGES & TRENDS**|**Class Name**|
|--|:--:|
|AFIRMA - Adaptive FIR Moving Average|`Afirma`|
|ALMA - Arnaud Legoux Moving Average|`Alma`|
|DEMA - Double Exponential Moving Average|`Dema`|
+114
View File
@@ -0,0 +1,114 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// MACD: Moving Average Convergence Divergence
/// A trend-following momentum indicator that shows the relationship between two moving
/// averages of an asset's price. MACD is calculated by subtracting the longer-period
/// EMA from the shorter-period EMA. The result is then used to calculate a signal line
/// (EMA of MACD) and histogram (MACD - Signal).
/// </summary>
/// <remarks>
/// The MACD calculation process:
/// 1. Calculate the fast EMA (default 12 periods)
/// 2. Calculate the slow EMA (default 26 periods)
/// 3. MACD Line = Fast EMA - Slow EMA
/// 4. Signal Line = EMA of MACD Line (default 9 periods)
/// 5. MACD Histogram = MACD Line - Signal Line
///
/// Key characteristics:
/// - Centerline crossovers signal trend changes
/// - Signal line crossovers indicate trading opportunities
/// - Histogram shows momentum of price movement
/// - Divergences can signal potential reversals
///
/// Formula:
/// MACD Line = EMA(fast) - EMA(slow)
/// Signal Line = EMA(MACD Line, signal)
/// Histogram = MACD Line - Signal Line
///
/// Sources:
/// https://www.investopedia.com/terms/m/macd.asp
/// https://school.stockcharts.com/doku.php?id=technical_indicators:macd
/// </remarks>
[SkipLocalsInit]
public sealed class Macd : AbstractBase
{
private readonly Ema _fastEma;
private readonly Ema _slowEma;
private readonly Ema _signalEma;
private const int DefaultFastPeriod = 12;
private const int DefaultSlowPeriod = 26;
private const int DefaultSignalPeriod = 9;
private double _macdLine;
private double _signalLine;
/// <summary>
/// Gets the MACD line value (Fast EMA - Slow EMA)
/// </summary>
public double MacdLine => _macdLine;
/// <summary>
/// Gets the Signal line value (EMA of MACD line)
/// </summary>
public double SignalLine => _signalLine;
/// <param name="fastPeriod">The number of periods for the fast EMA (default 12).</param>
/// <param name="slowPeriod">The number of periods for the slow EMA (default 26).</param>
/// <param name="signalPeriod">The number of periods for the signal line EMA (default 9).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when any period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Macd(int fastPeriod = DefaultFastPeriod, int slowPeriod = DefaultSlowPeriod, int signalPeriod = DefaultSignalPeriod)
{
if (fastPeriod < 1)
throw new ArgumentOutOfRangeException(nameof(fastPeriod));
if (slowPeriod < 1)
throw new ArgumentOutOfRangeException(nameof(slowPeriod));
if (signalPeriod < 1)
throw new ArgumentOutOfRangeException(nameof(signalPeriod));
if (fastPeriod >= slowPeriod)
throw new ArgumentException("Fast period must be less than slow period");
_fastEma = new(fastPeriod);
_slowEma = new(slowPeriod);
_signalEma = new(signalPeriod);
WarmupPeriod = slowPeriod + signalPeriod;
Name = $"MACD({fastPeriod},{slowPeriod},{signalPeriod})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="fastPeriod">The number of periods for the fast EMA.</param>
/// <param name="slowPeriod">The number of periods for the slow EMA.</param>
/// <param name="signalPeriod">The number of periods for the signal line EMA.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Macd(object source, int fastPeriod, int slowPeriod, int signalPeriod) : this(fastPeriod, slowPeriod, signalPeriod)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
_index++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(Input.IsNew);
// Calculate MACD line
double fastEma = _fastEma.Calc(Input.Value, Input.IsNew);
double slowEma = _slowEma.Calc(Input.Value, Input.IsNew);
_macdLine = fastEma - slowEma;
// Calculate Signal line
_signalLine = _signalEma.Calc(_macdLine, Input.IsNew);
// Return histogram
return _macdLine - _signalLine;
}
}