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)