Merge branch 'dev' into add-fisher-transform

This commit is contained in:
Miha Kralj
2024-11-07 19:03:11 -08:00
committed by GitHub
100 changed files with 1944 additions and 1244 deletions
+3 -6
View File
@@ -51,12 +51,9 @@ public sealed class Coppock : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Coppock(int roc1Period = DefaultRoc1Period, int roc2Period = DefaultRoc2Period, int wmaPeriod = DefaultWmaPeriod)
{
if (roc1Period < 1)
throw new ArgumentOutOfRangeException(nameof(roc1Period), "ROC1 period must be greater than 0");
if (roc2Period < 1)
throw new ArgumentOutOfRangeException(nameof(roc2Period), "ROC2 period must be greater than 0");
if (wmaPeriod < 1)
throw new ArgumentOutOfRangeException(nameof(wmaPeriod), "WMA period must be greater than 0");
ArgumentOutOfRangeException.ThrowIfLessThan(roc1Period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(roc2Period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(wmaPeriod, 1);
_roc1Period = roc1Period;
_roc2Period = roc2Period;
+102
View File
@@ -0,0 +1,102 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// EFI: Elder Ray's Force Index
/// A volume-based oscillator that measures the strength of price movements using volume.
/// It helps identify potential trend reversals and confirm price movements.
/// </summary>
/// <remarks>
/// The EFI calculation process:
/// 1. Calculate the difference between the current close and the previous close
/// 2. Multiply the difference by the current volume
/// 3. Apply an exponential moving average (EMA) to smooth the result
///
/// Key characteristics:
/// - Oscillates above and below zero
/// - Positive values indicate buying pressure
/// - Negative values indicate selling pressure
/// - Crosses above zero suggest buying opportunities
/// - Crosses below zero suggest selling opportunities
///
/// Formula:
/// EFI = EMA((Close - Close[1]) * Volume, period)
///
/// Sources:
/// Alexander Elder - "Trading for a Living" (1993)
/// https://www.investopedia.com/terms/f/force-index.asp
///
/// Note: Default period is 13
/// </remarks>
[SkipLocalsInit]
public sealed class Efi : AbstractBase
{
private readonly Ema _ema;
private double _prevClose;
private double _p_prevClose;
private const int DefaultPeriod = 13;
/// <param name="period">The smoothing period for EMA calculation (default 13).</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when period is less than 1.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Efi(int period = DefaultPeriod)
{
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
_ema = new(period);
WarmupPeriod = period + 1;
Name = $"EFI({period})";
}
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The smoothing period for EMA calculation.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Efi(object source, int period = DefaultPeriod) : this(period)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new BarSignal(Sub));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Init()
{
base.Init();
_ema.Init();
_prevClose = double.NaN;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void ManageState(bool isNew)
{
if (isNew)
{
_index++;
_p_prevClose = _prevClose;
}
else
{
_prevClose = _p_prevClose;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
protected override double Calculation()
{
ManageState(BarInput.IsNew);
if (_index == 1)
{
_prevClose = BarInput.Close;
return 0;
}
// Calculate raw force index
double priceChange = BarInput.Close - _prevClose;
double forceIndex = priceChange * BarInput.Volume;
// Update previous close
_prevClose = BarInput.Close;
// Apply EMA smoothing
return _ema.Calc(forceIndex, BarInput.IsNew);
}
}
+1 -2
View File
@@ -48,8 +48,7 @@ public sealed class Rsi : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Rsi(int period = DefaultPeriod)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period));
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
_avgGain = new(period, useSma: true);
_avgLoss = new(period, useSma: true);
_index = 0;
+3 -6
View File
@@ -57,12 +57,9 @@ public sealed class Smi : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Smi(int period = DefaultPeriod, int smooth1 = DefaultSmooth1, int smooth2 = DefaultSmooth2)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than 0");
if (smooth1 < 1)
throw new ArgumentOutOfRangeException(nameof(smooth1), "Smooth1 must be greater than 0");
if (smooth2 < 1)
throw new ArgumentOutOfRangeException(nameof(smooth2), "Smooth2 must be greater than 0");
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smooth1, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smooth2, 1);
_highs = new(period);
_lows = new(period);
+4 -18
View File
@@ -40,7 +40,6 @@ public sealed class Srsi : AbstractBase
private readonly CircularBuffer _srsiValues;
private readonly Sma _signal;
private readonly int _rsiPeriod;
private readonly int _stochPeriod;
private const int DefaultRsiPeriod = 14;
private const int DefaultStochPeriod = 14;
private const int DefaultSmoothK = 3;
@@ -56,25 +55,12 @@ public sealed class Srsi : AbstractBase
public Srsi(int rsiPeriod = DefaultRsiPeriod, int stochPeriod = DefaultStochPeriod,
int smoothK = DefaultSmoothK, int smoothD = DefaultSmoothD)
{
if (rsiPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(rsiPeriod), "Period must be greater than 0");
}
if (stochPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(stochPeriod), "Period must be greater than 0");
}
if (smoothK < 1)
{
throw new ArgumentOutOfRangeException(nameof(smoothK), "Period must be greater than 0");
}
if (smoothD < 1)
{
throw new ArgumentOutOfRangeException(nameof(smoothD), "Period must be greater than 0");
}
ArgumentOutOfRangeException.ThrowIfLessThan(rsiPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(stochPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smoothK, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smoothD, 1);
_rsiPeriod = rsiPeriod;
_stochPeriod = stochPeriod;
_rsi = new(rsiPeriod);
_rsiValues = new(stochPeriod);
_srsiValues = new(smoothK);
+6 -21
View File
@@ -62,32 +62,17 @@ public sealed class Stc : AbstractBase
int slowPeriod = DefaultSlowPeriod, int d1Period = DefaultD1Period,
int stcPeriod = DefaultStcPeriod)
{
string err = "All periods must be greater than 0";
ArgumentOutOfRangeException.ThrowIfLessThan(cyclePeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(fastPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(slowPeriod, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(d1Period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(stcPeriod, 1);
if (cyclePeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(cyclePeriod), err);
}
if (fastPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(fastPeriod), err);
}
if (slowPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(slowPeriod), err);
}
if (d1Period < 1)
{
throw new ArgumentOutOfRangeException(nameof(d1Period), err);
}
if (stcPeriod < 1)
{
throw new ArgumentOutOfRangeException(nameof(stcPeriod), err);
}
if (fastPeriod >= slowPeriod)
{
throw new ArgumentOutOfRangeException(nameof(fastPeriod), "Fast period must be less than slow period");
}
_fastEma = new(fastPeriod);
_slowEma = new(slowPeriod);
_macdValues = new(cyclePeriod);
+3 -6
View File
@@ -52,12 +52,9 @@ public sealed class Stoch : AbstractBase
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Stoch(int period = DefaultPeriod, int smoothK = DefaultSmoothK, int smoothD = DefaultSmoothD)
{
if (period < 1)
throw new ArgumentOutOfRangeException(nameof(period), "Period must be greater than 0");
if (smoothK < 1)
throw new ArgumentOutOfRangeException(nameof(smoothK), "%K smoothing period must be greater than 0");
if (smoothD < 1)
throw new ArgumentOutOfRangeException(nameof(smoothD), "%D smoothing period must be greater than 0");
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smoothK, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(smoothD, 1);
_highs = new(period);
_lows = new(period);
+6 -24
View File
@@ -67,30 +67,12 @@ public sealed class Uo : AbstractBase
public Uo(int period1 = DefaultPeriod1, int period2 = DefaultPeriod2, int period3 = DefaultPeriod3,
double weight1 = DefaultWeight1, double weight2 = DefaultWeight2, double weight3 = DefaultWeight3)
{
if (period1 < 1)
{
throw new ArgumentOutOfRangeException(nameof(period1), "Period1 must be greater than 0");
}
if (period2 < 1)
{
throw new ArgumentOutOfRangeException(nameof(period2), "Period2 must be greater than 0");
}
if (period3 < 1)
{
throw new ArgumentOutOfRangeException(nameof(period3), "Period3 must be greater than 0");
}
if (weight1 <= 0)
{
throw new ArgumentOutOfRangeException(nameof(weight1), "Weight1 must be greater than 0");
}
if (weight2 <= 0)
{
throw new ArgumentOutOfRangeException(nameof(weight2), "Weight2 must be greater than 0");
}
if (weight3 <= 0)
{
throw new ArgumentOutOfRangeException(nameof(weight3), "Weight3 must be greater than 0");
}
ArgumentOutOfRangeException.ThrowIfLessThan(period1, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(period2, 1);
ArgumentOutOfRangeException.ThrowIfLessThan(period3, 1);
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(weight1, 0);
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(weight2, 0);
ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(weight3, 0);
_weight1 = weight1;
_weight2 = weight2;
+1 -1
View File
@@ -14,8 +14,8 @@ Done: 22, Todo: 7
✔️ CRSI - Connor RSI
CTI - Ehler's Correlation Trend Indicator
✔️ DOSC - Derivative Oscillator
EFI - Elder Ray's Force Index
✔️ FISHER - Fisher Transform
✔️ EFI - Elder Ray's Force Index
FOSC - Forecast Oscillator
*GATOR - Williams Alliator Oscillator (Upper Jaw, Lower Jaw, Teeth)
*KDJ - KDJ Indicator (K, D, J lines)