Fisher Transform in C#

I stole bits and pieces of this from different places. Initial credit goes to cTrader, followed by TradingView and finally to Andrey for helping me get my unit tests passing.

    
    public class FisherTransform : IndicatorBase
    {
        public int Length = 9;

        public decimal[] Fish { get; set; }
        public decimal[] Trigger { get; set; }

        decimal _maxHigh;
        decimal _minLow;

        private decimal _value1;
        private decimal _lastValue1;

        public FisherTransform(IEnumerable<Candle> candles, int length)
            : base(candles)
        {
            Length = length;
            RequiredCount = Length;
            _lastValue1 = 1;
        }

        protected override void Initialize()
        {
            Fish = new decimal[Series.Length];
            Trigger = new decimal[Series.Length];
        }

        public override void Compute(int startIndex = 1, int? endIndex = null)
        {
            if (endIndex == null)
                endIndex = Series.Length;

            for (int index = startIndex; index < endIndex; index++)
            {
                if (index == 1)
                {
                    Fish[index - 1] = 1;
                }

                _minLow = Series.Average.Lowest(Length, index);
                _maxHigh = Series.Average.Highest(Length, index);

                _value1 = Maths.Normalize(0.66m * (Maths.Divide(Series.Average[index] - _minLow, Math.Max(_maxHigh - _minLow, 0.001m)) - 0.5m) + 0.67m * _lastValue1);

                _lastValue1 = _value1;

                Fish[index] = 0.5m * Maths.Log(Maths.Divide(1 + _value1, Math.Max(1 - _value1, .001m))) + 0.5m * Fish[index - 1];
                Trigger[index] = Fish[index - 1];
            }
        }
    }
    
 

Note, base page can be found here. Also, I only post these indicators once I have written unit tests against them. Please comment below if something doesn’t look right.


C# Decimal Math helpers

Here are some helpers I put together for using decimal with my technical indicators.

    
    public static class Maths
    {
        public const decimal Zero = 0.0M;
        public const decimal One = 1.0M;
        private const decimal Half = 0.5M;
        private const decimal Einv = 0.3678794411714423215955237701614608674458111310317678M;
        public const decimal E = 2.7182818284590452353602874713526624977572470936999595749M;
        private const int MaxIteration = 100;
 
        public static decimal Divide(decimal divisor, decimal dividend)
        {
            if (divisor == 0 || dividend == 0)
                return 0;

            return divisor / dividend;
        }

        public static decimal Normalize(decimal input)
        {
            return input > .99m ? .999m : input < -.99m ? -.999m : input;
        }

        // source : https://github.com/raminrahimzada/CSharp-Helper-Classes/blob/master/Math/DecimalMath/DecimalMath.cs#L211
        public static decimal Log(decimal x)
        {
            if (x <= Zero)
            {
                throw new ArgumentException("x must be greater than zero");
            }
            var count = 0;
            while (x >= One)
            {
                x *= Einv;
                count++;
            }
            while (x <= Einv)
            {
                x *= E;
                count--;
            }
            x--;
            if (x == 0) return count;
            var result = Zero;
            var iteration = 0;
            var y = One;
            var cacheResult = result - One;
            while (cacheResult != result && iteration < MaxIteration)
            {
                iteration++;
                cacheResult = result;
                y *= -x;
                result += y / iteration;
            }
            return count - result;
        }
    }
    
In Tags , ,

Wilders Moving Average in C# (RMA)

Note, base page can be found here. Also, I only post these indicators once I have written unit tests against them. Please comment below if something doesn’t look right.

    
 	public class WildersMovingAverage : IndicatorBase
    {
        public decimal[] Result { get; set; }

        public int Period = 14;

        public decimal _previousSum = 0;

        public WildersMovingAverage(IEnumerable<Candle> candles, int length)
            : base(candles)
        {
            Period = length;
        }

        protected override void Initialize()
        {
            Result = new decimal[Series.Length];
        }

        public override void Compute(int startIndex = 0, int? endIndex = null)
        {
            if (endIndex == null)
                endIndex = Series.Length;

            decimal alpha = 1m / Period;
            for (int index = startIndex; index < endIndex; index++)
            {
                Result[index] = alpha * Series.Close[index] + (1 - alpha) * _previousSum;
                _previousSum = Result[index];
            }
        }
    }
 

My current IndicatorBase page

I plan on adding a bunch of technical indicators here on my blog. This is where I’ll (aim) to keep the latest version of my IndicatorBase class.

    
	public abstract class IndicatorBase
    {
        protected CandleSeries Series { get; set; } 
        protected int RequiredCount { get; set; }
        protected bool HasRequiredCandleCount => Series.Length > RequiredCount;
        protected bool IsLastCandle = false;
        protected virtual void Initialize() { }
        public abstract void Compute(int startIndex = 0, int? endIndex = null);
  
        public IndicatorBase(IEnumerable<Candle> source) 
        {
            var ordered = source.OrderBy(x => x.Timestamp).DistinctBy(x => x.Timestamp);
            Series = new CandleSeries(ordered);
            Initialize();
        }
    }

CandleSeries class

I’m still a bit up in the air on this, but I wanted to make it work like ThinkScript or Pine script where you can index into different pieces of the candle.

    	
	public class CandleSeries
    {
        public int Length = 0; 
        public decimal[] Close { get; set; } 
        public decimal[] Open { get; set; } 
        public decimal[] Low { get; set; } 
        public decimal[] High { get; set; } 
        public decimal[] Average { get; set; } 
        public decimal[] Volume { get; set; } 
        public DateTimeOffset[] Timestamp { get; set; }
        public Candle[] Source { get; set; }

        public CandleSeries(Candle[] source)
        {
            Length = source.Length;
            Close = new decimal[source.Length];
            Open = new decimal[source.Length];
            High = new decimal[source.Length];
            Low = new decimal[source.Length];
            Average = new decimal[source.Length];
            Volume = new decimal[source.Length];
            Timestamp = new DateTimeOffset[source.Length];
            Source = source;

            for (int i = 0; i < source.Length; i++)
            {
                Close[i] = source[i].Close;
                Open[i] = source[i].Open;

                var high = source[i].High;
                var low = source[i].Low;
                High[i] = high;
                Low[i] = low;
                Average[i] = (high + low) / 2;
                Volume[i] = source[i].Volume;
                Timestamp[i] = source[i].Timestamp;
            }
        }

        public CandleSeries(IEnumerable source) : this(source.ToArray()) { }
    }
In Tags ,