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 ,