mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-07 05:27:43 +00:00
Add Fisher Transform indicator (#49)
This commit is contained in:
@@ -157,4 +157,19 @@ public class OscillatorsUpdateTests : UpdateTestBase
|
|||||||
var indicator = new Efi(period: 13);
|
var indicator = new Efi(period: 13);
|
||||||
TestTBarUpdate(indicator, indicator.Calc);
|
TestTBarUpdate(indicator, indicator.Calc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,8 +14,8 @@ Done: 22, Todo: 7
|
|||||||
✔️ CRSI - Connor RSI
|
✔️ CRSI - Connor RSI
|
||||||
CTI - Ehler's Correlation Trend Indicator
|
CTI - Ehler's Correlation Trend Indicator
|
||||||
✔️ DOSC - Derivative Oscillator
|
✔️ DOSC - Derivative Oscillator
|
||||||
|
✔️ FISHER - Fisher Transform
|
||||||
✔️ EFI - Elder Ray's Force Index
|
✔️ EFI - Elder Ray's Force Index
|
||||||
FISHER - Fisher Transform
|
|
||||||
FOSC - Forecast Oscillator
|
FOSC - Forecast Oscillator
|
||||||
*GATOR - Williams Alliator Oscillator (Upper Jaw, Lower Jaw, Teeth)
|
*GATOR - Williams Alliator Oscillator (Upper Jaw, Lower Jaw, Teeth)
|
||||||
*KDJ - KDJ Indicator (K, D, J lines)
|
*KDJ - KDJ Indicator (K, D, J lines)
|
||||||
|
|||||||
Reference in New Issue
Block a user