From 708c11ef4896ddaba928b1da5fdc9abde7aae3a1 Mon Sep 17 00:00:00 2001 From: Miha Kralj <31756078+mihakralj@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:48:52 -0800 Subject: [PATCH] Add Fisher Transform indicator --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/mihakralj/QuanTAlib?shareId=XXXX-XXXX-XXXX-XXXX). --- Tests/test_updates_oscillators.cs | 15 +++++ lib/oscillators/Fisher.cs | 95 +++++++++++++++++++++++++++++++ lib/oscillators/_list.md | 4 +- 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 lib/oscillators/Fisher.cs diff --git a/Tests/test_updates_oscillators.cs b/Tests/test_updates_oscillators.cs index c163081..d66ccf3 100644 --- a/Tests/test_updates_oscillators.cs +++ b/Tests/test_updates_oscillators.cs @@ -321,4 +321,19 @@ public void Dosc_Update() Assert.Equal(initialValue, finalValue, precision); } + + [Fact] + public void Fisher_Update() + { + var indicator = new Fisher(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); + } } diff --git a/lib/oscillators/Fisher.cs b/lib/oscillators/Fisher.cs new file mode 100644 index 0000000..0866783 --- /dev/null +++ b/lib/oscillators/Fisher.cs @@ -0,0 +1,95 @@ +using System.Runtime.CompilerServices; +namespace QuanTAlib; + +/// +/// FISHER: Fisher Transform +/// A technical indicator that converts prices into a Gaussian normal distribution. +/// +/// +/// The Fisher Transform calculation process: +/// 1. Calculate the value of the price relative to its high-low range. +/// 2. Apply the Fisher Transform formula to the normalized price. +/// 3. Smooth the result using an exponential moving average. +/// +/// Key characteristics: +/// - Oscillates between -1 and 1 +/// - Emphasizes price reversals +/// - Can be used to identify overbought and oversold conditions +/// +/// Formula: +/// Fisher Transform = 0.5 * log((1 + x) / (1 - x)) +/// where: +/// x = 2 * ((price - min) / (max - min) - 0.5) +/// +/// Sources: +/// John F. Ehlers - "Rocket Science for Traders" (2001) +/// https://www.investopedia.com/terms/f/fisher-transform.asp +/// +[SkipLocalsInit] +public sealed class Fisher : AbstractBase +{ + private readonly int _period; + private readonly double[] _prices; + private double _prevFisher; + private double _prevValue; + + /// The data source object that publishes updates. + /// The calculation period (default: 10) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Fisher(object source, int period = 10) : this(period) + { + var pubEvent = source.GetType().GetEvent("Pub"); + pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Fisher(int period = 10) + { + _period = period; + _prices = new double[period]; + WarmupPeriod = period; + Name = "FISHER"; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override void ManageState(bool isNew) + { + if (isNew) + { + _index++; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private double NormalizePrice(double price, double min, double max) + { + return 2 * ((price - min) / (max - min) - 0.5); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private double FisherTransform(double value) + { + return 0.5 * System.Math.Log((1 + value) / (1 - value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override double Calculation() + { + ManageState(Input.IsNew); + + var idx = _index % _period; + _prices[idx] = Input.Value; + + if (_index < _period - 1) return double.NaN; + + var min = _prices.Min(); + var max = _prices.Max(); + var normalizedPrice = NormalizePrice(Input.Value, min, max); + var fisherValue = FisherTransform(normalizedPrice); + + var smoothedFisher = 0.5 * (fisherValue + _prevFisher); + _prevFisher = smoothedFisher; + + return smoothedFisher; + } +} diff --git a/lib/oscillators/_list.md b/lib/oscillators/_list.md index 72b74c8..445ab03 100644 --- a/lib/oscillators/_list.md +++ b/lib/oscillators/_list.md @@ -1,5 +1,5 @@ # Oscillators indicators -Done: 21, Todo: 8 +Done: 22, Todo: 7 ✔️ AC - Acceleration Oscillator ✔️ AO - Awesome Oscillator @@ -15,7 +15,7 @@ Done: 21, Todo: 8 CTI - Ehler's Correlation Trend Indicator ✔️ DOSC - Derivative Oscillator EFI - Elder Ray's Force Index -FISHER - Fisher Transform +✔️ FISHER - Fisher Transform FOSC - Forecast Oscillator *GATOR - Williams Alliator Oscillator (Upper Jaw, Lower Jaw, Teeth) *KDJ - KDJ Indicator (K, D, J lines)