Add Fisher Transform indicator

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/mihakralj/QuanTAlib?shareId=XXXX-XXXX-XXXX-XXXX).
This commit is contained in:
Miha Kralj
2024-11-07 18:48:52 -08:00
parent a3c6133361
commit 708c11ef48
3 changed files with 112 additions and 2 deletions
+15
View File
@@ -321,4 +321,19 @@ public class OscillatorsUpdateTests
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);
}
}
+95
View File
@@ -0,0 +1,95 @@
using System.Runtime.CompilerServices;
namespace QuanTAlib;
/// <summary>
/// FISHER: Fisher Transform
/// A technical indicator that converts prices into a Gaussian normal distribution.
/// </summary>
/// <remarks>
/// 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
/// </remarks>
[SkipLocalsInit]
public sealed class Fisher : AbstractBase
{
private readonly int _period;
private readonly double[] _prices;
private double _prevFisher;
private double _prevValue;
/// <param name="source">The data source object that publishes updates.</param>
/// <param name="period">The calculation period (default: 10)</param>
[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;
}
}
+2 -2
View File
@@ -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)