diff --git a/.github/workflows/Publish.yml b/.github/workflows/Publish.yml index 93ef98f8..417a6093 100644 --- a/.github/workflows/Publish.yml +++ b/.github/workflows/Publish.yml @@ -252,6 +252,7 @@ jobs: (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev')) || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -324,15 +325,9 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | -<<<<<<< - gh release create v${{ github.sha }} \ - --title "Release from commit ${{ github.sha }}" \ - --notes "Release notes for this version." \ -======= gh release create v${{ steps.gitversion.outputs.MajorMinorPatch }} \ --title "Release from commit ${{ steps.gitversion.outputs.MajorMinorPatch }}" \ --notes "Release notes for this version." \ ->>>>>>> quantower/Averages/bin/Release/Averages.dll \ quantower/Statistics/bin/Release/Statistics.dll \ quantower/Volatility/bin/Release/Volatility.dll \ diff --git a/Tests/test_eventing.cs b/Tests/test_eventing.cs index d997b81b..5406876e 100644 --- a/Tests/test_eventing.cs +++ b/Tests/test_eventing.cs @@ -113,7 +113,8 @@ public class EventingTests // Volatility indicators (bar-based) ("Atr", new Atr(14), new Atr(barInput, 14)), // Oscillators (bar-based) - ("Chop", new Chop(14), new Chop(barInput, 14)) + ("Chop", new Chop(14), new Chop(barInput, 14)), + ("Dosc", new Dosc(), new Dosc(barInput)) }; // Generate 200 random values and feed them to indicators diff --git a/Tests/test_updates_oscillators.cs b/Tests/test_updates_oscillators.cs index cbc20ec1..c1630815 100644 --- a/Tests/test_updates_oscillators.cs +++ b/Tests/test_updates_oscillators.cs @@ -1,308 +1,324 @@ -using Xunit; -using System.Security.Cryptography; - -namespace QuanTAlib.Tests; - -public class OscillatorsUpdateTests -{ - private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create(); - private const int RandomUpdates = 100; - private const double ReferenceValue = 100.0; - private const int precision = 8; - - private double GetRandomDouble() - { - byte[] bytes = new byte[8]; - rng.GetBytes(bytes); - return ((double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200) - 100; // Range: -100 to 100 - } - - private TBar GetRandomBar(bool IsNew) - { - double open = GetRandomDouble(); - double high = open + Math.Abs(GetRandomDouble()); - double low = open - Math.Abs(GetRandomDouble()); - double close = low + ((high - low) * GetRandomDouble()); - return new TBar(DateTime.Now, open, high, low, close, 1000, IsNew); - } - - [Fact] - public void Rsi_Update() - { - var indicator = new Rsi(period: 14); - 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(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Rsx_Update() - { - var indicator = new Rsx(period: 14); - 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(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Cmo_Update() - { - var indicator = new Cmo(period: 14); - 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(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Ao_Update() - { - var indicator = new Ao(); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Ac_Update() - { - var indicator = new Ac(); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Aroon_Update() - { - var indicator = new Aroon(period: 25); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Bop_Update() - { - var indicator = new Bop(); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Cci_Update() - { - var indicator = new Cci(period: 20); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Cfo_Update() - { - var indicator = new Cfo(period: 14); - 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(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Chop_Update() - { - var indicator = new Chop(period: 14); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Cog_Update() - { - var indicator = new Cog(period: 10); - 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(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Smi_Update() - { - var indicator = new Smi(period: 10, smooth1: 3, smooth2: 3); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Srsi_Update() - { - var indicator = new Srsi(rsiPeriod: 14, stochPeriod: 14, smoothK: 3, smoothD: 3); - 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(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Stc_Update() - { - var indicator = new Stc(cyclePeriod: 10, fastPeriod: 23, slowPeriod: 50); - 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(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Stoch_Update() - { - var indicator = new Stoch(period: 14, smoothK: 3, smoothD: 3); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Tsi_Update() - { - var indicator = new Tsi(firstPeriod: 25, secondPeriod: 13); - 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(), IsNew: false)); - } - double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Uo_Update() - { - var indicator = new Uo(period1: 7, period2: 14, period3: 28); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } - - [Fact] - public void Willr_Update() - { - var indicator = new Willr(period: 14); - TBar r = GetRandomBar(true); - double initialValue = indicator.Calc(r); - - for (int i = 0; i < RandomUpdates; i++) - { - indicator.Calc(GetRandomBar(IsNew: false)); - } - double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); - - Assert.Equal(initialValue, finalValue, precision); - } -} +using Xunit; +using System.Security.Cryptography; + +namespace QuanTAlib.Tests; + +public class OscillatorsUpdateTests +{ + private readonly RandomNumberGenerator rng = RandomNumberGenerator.Create(); + private const int RandomUpdates = 100; + private const double ReferenceValue = 100.0; + private const int precision = 8; + + private double GetRandomDouble() + { + byte[] bytes = new byte[8]; + rng.GetBytes(bytes); + return ((double)BitConverter.ToUInt64(bytes, 0) / ulong.MaxValue * 200) - 100; // Range: -100 to 100 + } + + private TBar GetRandomBar(bool IsNew) + { + double open = GetRandomDouble(); + double high = open + Math.Abs(GetRandomDouble()); + double low = open - Math.Abs(GetRandomDouble()); + double close = low + ((high - low) * GetRandomDouble()); + return new TBar(DateTime.Now, open, high, low, close, 1000, IsNew); + } + + [Fact] + public void Rsi_Update() + { + var indicator = new Rsi(period: 14); + 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(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Rsx_Update() + { + var indicator = new Rsx(period: 14); + 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(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Cmo_Update() + { + var indicator = new Cmo(period: 14); + 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(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Ao_Update() + { + var indicator = new Ao(); + TBar r = GetRandomBar(true); + double initialValue = indicator.Calc(r); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(IsNew: false)); + } + double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Ac_Update() + { + var indicator = new Ac(); + TBar r = GetRandomBar(true); + double initialValue = indicator.Calc(r); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(IsNew: false)); + } + double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Aroon_Update() + { + var indicator = new Aroon(period: 25); + TBar r = GetRandomBar(true); + double initialValue = indicator.Calc(r); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(IsNew: false)); + } + double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Bop_Update() + { + var indicator = new Bop(); + TBar r = GetRandomBar(true); + double initialValue = indicator.Calc(r); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(IsNew: false)); + } + double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Cci_Update() + { + var indicator = new Cci(period: 20); + TBar r = GetRandomBar(true); + double initialValue = indicator.Calc(r); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(IsNew: false)); + } + double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Cfo_Update() + { + var indicator = new Cfo(period: 14); + 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(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Chop_Update() + { + var indicator = new Chop(period: 14); + TBar r = GetRandomBar(true); + double initialValue = indicator.Calc(r); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(IsNew: false)); + } + double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Cog_Update() + { + var indicator = new Cog(period: 10); + 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(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Smi_Update() + { + var indicator = new Smi(period: 10, smooth1: 3, smooth2: 3); + TBar r = GetRandomBar(true); + double initialValue = indicator.Calc(r); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(IsNew: false)); + } + double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Srsi_Update() + { + var indicator = new Srsi(rsiPeriod: 14, stochPeriod: 14, smoothK: 3, smoothD: 3); + 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(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Stc_Update() + { + var indicator = new Stc(cyclePeriod: 10, fastPeriod: 23, slowPeriod: 50); + 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(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Stoch_Update() + { + var indicator = new Stoch(period: 14, smoothK: 3, smoothD: 3); + TBar r = GetRandomBar(true); + double initialValue = indicator.Calc(r); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(IsNew: false)); + } + double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Tsi_Update() + { + var indicator = new Tsi(firstPeriod: 25, secondPeriod: 13); + 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(), IsNew: false)); + } + double finalValue = indicator.Calc(new TValue(DateTime.Now, ReferenceValue, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Uo_Update() + { + var indicator = new Uo(period1: 7, period2: 14, period3: 28); + TBar r = GetRandomBar(true); + double initialValue = indicator.Calc(r); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(IsNew: false)); + } + double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Willr_Update() + { + var indicator = new Willr(period: 14); + TBar r = GetRandomBar(true); + double initialValue = indicator.Calc(r); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(IsNew: false)); + } + double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } + + [Fact] + public void Dosc_Update() + { + var indicator = new Dosc(); + TBar r = GetRandomBar(true); + double initialValue = indicator.Calc(r); + + for (int i = 0; i < RandomUpdates; i++) + { + indicator.Calc(GetRandomBar(IsNew: false)); + } + double finalValue = indicator.Calc(new TBar(r.Time, r.Open, r.High, r.Low, r.Close, r.Volume, IsNew: false)); + + Assert.Equal(initialValue, finalValue, precision); + } +} diff --git a/docs/indicators/indicators.md b/docs/indicators/indicators.md index 8c607e10..3916949a 100644 --- a/docs/indicators/indicators.md +++ b/docs/indicators/indicators.md @@ -5,12 +5,12 @@ | Basic Transforms | 6 of 6 | 100% | | Averages & Trends | 33 of 33 | 100% | | Momentum | 16 of 16 | 100% | -| Oscillators | 20 of 29 | 69% | +| Oscillators | 21 of 29 | 72% | | Volatility | 24 of 35 | 69% | | Volume | 15 of 19 | 79% | | Numerical Analysis | 13 of 19 | 68% | | Errors | 16 of 16 | 100% | -| **Total** | **143 of 173** | **83%** | +| **Total** | **144 of 173** | **83%** | |Technical Indicator Name| Class Name| |-----------|:----------:| @@ -85,7 +85,6 @@ |COPPOCK - Coppock Curve|`Coppock`| |CRSI - Connor RSI|`Crsi`| |🚧 CTI - Ehler's Correlation Trend Indicator|`Cti`| -|🚧 DOSC - Derivative Oscillator|`Dosc`| |🚧 EFI - Elder Ray's Force Index|`Efi`| |🚧 FISHER - Fisher Transform|`Fisher`| |🚧 FOSC - Forecast Oscillator|`Fosc`| @@ -102,6 +101,7 @@ |TSI - True Strength Index|`Tsi`| |UO - Ultimate Oscillator|`Uo`| |WILLR - Larry Williams' %R|`Willr`| +|DOSC - Derivative Oscillator|`Dosc`| |**VOLATILITY INDICATORS**|| |ADR - Average Daily Range|`Adr`| |AP - Andrew's Pitchfork|`Ap`| diff --git a/lib/oscillators/Dosc.cs b/lib/oscillators/Dosc.cs new file mode 100644 index 00000000..906b2288 --- /dev/null +++ b/lib/oscillators/Dosc.cs @@ -0,0 +1,74 @@ +using System.Runtime.CompilerServices; +namespace QuanTAlib; + +/// +/// DOSC: Derivative Oscillator +/// A momentum indicator that combines the Relative Strength Index (RSI) and the Moving Average Convergence Divergence (MACD) to identify potential trend reversals. +/// +/// +/// The DOSC calculation process: +/// 1. Calculate the RSI +/// 2. Calculate the MACD of the RSI +/// 3. Calculate the signal line (SMA) of the MACD +/// 4. Subtract the signal line from the MACD to get the DOSC +/// +/// Key characteristics: +/// - Combines RSI and MACD +/// - Oscillates above and below zero +/// - Positive values indicate bullish momentum +/// - Negative values indicate bearish momentum +/// - Crosses above zero suggest buying opportunities +/// - Crosses below zero suggest selling opportunities +/// +/// Formula: +/// DOSC = MACD(RSI) - Signal(MACD(RSI)) +/// +/// Sources: +/// Original development +/// https://www.investopedia.com/terms/d/derivativeoscillator.asp +/// +[SkipLocalsInit] +public sealed class Dosc : AbstractBase +{ + private readonly Rsi _rsi; + private readonly Macd _macd; + private readonly Sma _signal; + + /// The data source object that publishes updates. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Dosc(object source) : this() + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new BarSignal(Sub)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Dosc() + { + _rsi = new Rsi(); + _macd = new Macd(); + _signal = new Sma(9); + WarmupPeriod = 34; // RSI requires 14 periods + MACD requires 26 periods + 9 for signal line + Name = "DOSC"; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void ManageState(bool isNew) + { + if (isNew) + { + _index++; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override double Calculation() + { + ManageState(BarInput.IsNew); + var rsi = _rsi.Calc(BarInput.Close, BarInput.IsNew); + var macd = _macd.Calc(rsi, BarInput.IsNew); + _signal.Calc(macd, BarInput.IsNew); + + return macd - _signal.Value; + } +} diff --git a/lib/oscillators/_list.md b/lib/oscillators/_list.md index 0cd477ee..72b74c86 100644 --- a/lib/oscillators/_list.md +++ b/lib/oscillators/_list.md @@ -1,32 +1,32 @@ -# Oscillators indicators -Done: 20, Todo: 9 - -✔️ AC - Acceleration Oscillator -✔️ AO - Awesome Oscillator -✔️ AROON - Aroon oscillator (Up, Down) -✔️ BOP - Balance of Power -✔️ CCI - Commodity Channel Index -✔️ CFO - Chande Forcast Oscillator -✔️ CHOP - Choppiness Index -✔️ CMO - Chande Momentum Oscillator -✔️ COG - Ehler's Center of Gravity -✔️ COPPOCK - Coppock Curve -✔️ CRSI - Connor RSI -CTI - Ehler's Correlation Trend Indicator -DOSC - Derivative Oscillator -EFI - Elder Ray's Force Index -FISHER - Fisher Transform -FOSC - Forecast Oscillator -*GATOR - Williams Alliator Oscillator (Upper Jaw, Lower Jaw, Teeth) -*KDJ - KDJ Indicator (K, D, J lines) -KRI - Kairi Relative Index -✔️ RSI - Relative Strength Index -✔️ RSX - Jurik Trend Strength Index -*RVGI - Relative Vigor Index (RVGI, Signal) -✔️ SMI - Stochastic Momentum Index -✔️ SRSI - Stochastic RSI (SRSI, Signal) -✔️ STC - Schaff Trend Cycle -✔️ STOCH - Stochastic Oscillator (%K, %D) -✔️ TSI - True Strength Index -✔️ UO - Ultimate Oscillator -✔️ WILLR - Larry Williams' %R +# Oscillators indicators +Done: 21, Todo: 8 + +✔️ AC - Acceleration Oscillator +✔️ AO - Awesome Oscillator +✔️ AROON - Aroon oscillator (Up, Down) +✔️ BOP - Balance of Power +✔️ CCI - Commodity Channel Index +✔️ CFO - Chande Forcast Oscillator +✔️ CHOP - Choppiness Index +✔️ CMO - Chande Momentum Oscillator +✔️ COG - Ehler's Center of Gravity +✔️ COPPOCK - Coppock Curve +✔️ CRSI - Connor RSI +CTI - Ehler's Correlation Trend Indicator +✔️ DOSC - Derivative Oscillator +EFI - Elder Ray's Force Index +FISHER - Fisher Transform +FOSC - Forecast Oscillator +*GATOR - Williams Alliator Oscillator (Upper Jaw, Lower Jaw, Teeth) +*KDJ - KDJ Indicator (K, D, J lines) +KRI - Kairi Relative Index +✔️ RSI - Relative Strength Index +✔️ RSX - Jurik Trend Strength Index +*RVGI - Relative Vigor Index (RVGI, Signal) +✔️ SMI - Stochastic Momentum Index +✔️ SRSI - Stochastic RSI (SRSI, Signal) +✔️ STC - Schaff Trend Cycle +✔️ STOCH - Stochastic Oscillator (%K, %D) +✔️ TSI - True Strength Index +✔️ UO - Ultimate Oscillator +✔️ WILLR - Larry Williams' %R