mtapi icon indicating copy to clipboard operation
mtapi copied to clipboard

double should be replaced by decimal

Open arteny opened this issue 4 years ago • 3 comments

As soon we are working in the financial field all these objects like ask, bid, last, high, low, volume should be decimal instead of double. For instance, we can't compare doubles directly without add precision.

arteny avatar May 09 '20 12:05 arteny

Good point. We need to think how it can be implemented without back capability broken.

vdemydiuk avatar Aug 09 '20 14:08 vdemydiuk

It's not possible to directly switch from double to decimal without breaking compatibility.

But you can add a custom type like MtDouble with implicit operators, which behaves like a double or like a decimal regarding what the code requests:

    public struct MtDouble
    {
        const string DOUBLE_OBSOLETE = "Doubles should not be used in financial operations. This will be removed in a later version, please use decimal instead.";
        private decimal _value;
        public MtDouble(double val) => _value = (decimal)val;
        public MtDouble(decimal val) => _value = val;
        [Obsolete(DOUBLE_OBSOLETE)]
        public static implicit operator double(MtDouble mtDouble) => (double)mtDouble._value;
        public static implicit operator decimal(MtDouble mtDouble) => mtDouble._value;
        [Obsolete(DOUBLE_OBSOLETE)]
        public static implicit operator MtDouble(double val) => new MtDouble(val);
        public static implicit operator MtDouble(decimal val) => new MtDouble(val);
    }

usage:

    MtDouble doubleValue = 5.0d;
    MtDouble decimalValue = 5.0m;
    double conversion1 = decimalValue;
    double conversion2 = doubleValue;
    decimal conversion3 = decimalValue;
    decimal conversion4 = doubleValue;

Thanks to the Obsolete-Attribute, the dev get a hint everytime he uses a MtDouble as double: image

Then you can just replace double with MtDouble e.g. in MtOrder:

public class MtOrder
    {
        public int Ticket { get; set; }
        public string Symbol { get; set; }
        public TradeOperation Operation { get; set; }
        public MtDouble OpenPrice { get; set; }
        public MtDouble ClosePrice { get; set; }
        public MtDouble Lots { get; set; }
        public int MtOpenTime { get; set; }
        public int MtCloseTime { get; set; }
        public MtDouble Profit { get; set; }
        public string Comment { get; set; }
        public MtDouble Commission { get; set; }
        public int MagicNumber { get; set; }
        public MtDouble Swap { get; set; }
        public int MtExpiration { get; set; }
        public MtDouble TakeProfit { get; set; }
        public MtDouble StopLoss { get; set; }

        public DateTime OpenTime
        {
            get { return MtApiTimeConverter.ConvertFromMtTime(MtOpenTime); }
        }

        public DateTime CloseTime
        {
            get { return MtApiTimeConverter.ConvertFromMtTime(MtCloseTime); }
        }

        public DateTime Expiration
        {
            get { return MtApiTimeConverter.ConvertFromMtTime(MtExpiration); }
        }
    }

mbochmann avatar Oct 15 '20 07:10 mbochmann

@mbochmann Excellent. I think your solution can be used as temporary fix for migration from double to decimal.

vdemydiuk avatar Oct 15 '20 08:10 vdemydiuk