计算平均绝对偏差

时间:2020-03-17 23:27:07

标签: c#

我想计算mean absolute deviation,目前正在使用link here发布的Stack Overflow(Alex)中的以下类:

public class MovingAverageCalculator
{
    private readonly int _period;
    private readonly double[] _window;
    private int _numAdded;
    private double _varianceSum;

    public MovingAverageCalculator(int period)
    {
        _period = period;
        _window = new double[period];
    }

    public double Average { get; private set; }

    public double StandardDeviation
    {
        get
        {
            var variance = Variance;
            if (variance >= double.Epsilon)
            {
                var sd = Math.Sqrt(variance);
                return double.IsNaN(sd) ? 0.0 : sd;
            }
            return 0.0;
        }
    }

    public double Variance
    {
        get
        {
            var n = N;
            return n > 1 ? _varianceSum / (n - 1) : 0.0;
        }
    }

    public bool HasFullPeriod
    {
        get { return _numAdded >= _period; }
    }

    public IEnumerable<double> Observations
    {
        get { return _window.Take(N); }
    }

    public int N
    {
        get { return Math.Min(_numAdded, _period); }
    }

    public void AddObservation(double observation)
    {
        // Window is treated as a circular buffer.
        var ndx = _numAdded % _period;
        var old = _window[ndx];     // get value to remove from window
        _window[ndx] = observation; // add new observation in its place.
        _numAdded++;

        // Update average and standard deviation using deltas
        var old_avg = Average;
        if (_numAdded <= _period)
        {
            var delta = observation - old_avg;
            Average += delta / _numAdded;
            _varianceSum += (delta * (observation - Average));
        }
        else // use delta vs removed observation.
        {
            var delta = observation - old;
            Average += delta / _period;
            _varianceSum += (delta * ((observation - Average) + (old - old_avg)));
        }
    }
}

创建该类的人以某种方式做了怪异的事,我不理解计算结果,因为他使用了不同的公式。例如,人们通常像this一样计算方差。

有人可以解释一下该类别中的平均绝对偏差吗?

公式:

enter image description here

编辑:

这个准确一些,但还不够。有什么想法吗?

输出(应该是):

CCI = -29.189669
CCI = -57.578105
CCI = 1.537557
CCI = 46.973803
CCI = 68.662979
CCI = 78.647204
CCI = 52.798310
CCI = 84.266845
CCI = 104.694912
CCI = 99.048428
CCI = 58.068118
CCI = 57.575758
CCI = 68.387309
CCI = 127.625967
CCI = 128.826508
CCI = 124.751608
CCI = 112.929293
CCI = 165.170449
CCI = 141.586505
CCI = 114.463325
CCI = 155.766418

输出(它是什么):

CCI = -26.630104
CCI = -53.295597
CCI = 1.476909
CCI = 44.829571
CCI = 67.857143
CCI = 80.059829
CCI = 55.447471
CCI = 90.681818
CCI = 116.030534
CCI = 106.314948
CCI = 61.242833
CCI = 61.664226
CCI = 74.962064
CCI = 150.864780
CCI = 163.034547
CCI = 162.636347
CCI = 153.194865
CCI = 197.583882
CCI = 159.622130
CCI = 122.744143
CCI = 163.325826

另一个CCI的输出(应该是):

Typical Price: 0.010153 | SMA: 0.009989 | Mean Deviation: 0.000139
Typical Price: 0.010100 | SMA: 0.009988 | Mean Deviation: 0.000142
Typical Price: 0.010180 | SMA: 0.009991 | Mean Deviation: 0.000150
Typical Price: 0.010230 | SMA: 0.009990 | Mean Deviation: 0.000153
Typical Price: 0.010233 | SMA: 0.010000 | Mean Deviation: 0.000157
Typical Price: 0.010147 | SMA: 0.010008 | Mean Deviation: 0.000159
Typical Price: 0.010160 | SMA: 0.010027 | Mean Deviation: 0.000154
Typical Price: 0.010200 | SMA: 0.010044 | Mean Deviation: 0.000152
Typical Price: 0.010380 | SMA: 0.010077 | Mean Deviation: 0.000158
Typical Price: 0.010413 | SMA: 0.010107 | Mean Deviation: 0.000159
Typical Price: 0.010447 | SMA: 0.010138 | Mean Deviation: 0.000165
Typical Price: 0.010450 | SMA: 0.010171 | Mean Deviation: 0.000165
Typical Price: 0.010657 | SMA: 0.010199 | Mean Deviation: 0.000185
Typical Price: 0.010647 | SMA: 0.010224 | Mean Deviation: 0.000199
Typical Price: 0.010623 | SMA: 0.010252 | Mean Deviation: 0.000216
Typical Price: 0.010880 | SMA: 0.010308 | Mean Deviation: 0.000245
Typical Price: 0.010863 | SMA: 0.010354 | Mean Deviation: 0.000263
Typical Price: 0.010853 | SMA: 0.010397 | Mean Deviation: 0.000285
Typical Price: 0.010967 | SMA: 0.010442 | Mean Deviation: 0.000307
Typical Price: 0.011480 | SMA: 0.010517 | Mean Deviation: 0.000356
Typical Price: 0.011750 | SMA: 0.010600 | Mean Deviation: 0.000408
Typical Price: 0.011653 | SMA: 0.010674 | Mean Deviation: 0.000448

来自CommodityChannelIndex.cs的输出(它是什么):

Typical Price: 0.010153 | SMA: 0.009989 | Mean Deviation: 0.000137
Typical Price: 0.010100 | SMA: 0.009988 | Mean Deviation: 0.000135
Typical Price: 0.010180 | SMA: 0.009991 | Mean Deviation: 0.000139
Typical Price: 0.010230 | SMA: 0.009990 | Mean Deviation: 0.000138
Typical Price: 0.010233 | SMA: 0.010000 | Mean Deviation: 0.000146
Typical Price: 0.010147 | SMA: 0.010008 | Mean Deviation: 0.000151
Typical Price: 0.010160 | SMA: 0.010027 | Mean Deviation: 0.000144
Typical Price: 0.010200 | SMA: 0.010044 | Mean Deviation: 0.000139
Typical Price: 0.010380 | SMA: 0.010077 | Mean Deviation: 0.000134
Typical Price: 0.010413 | SMA: 0.010107 | Mean Deviation: 0.000125
Typical Price: 0.010447 | SMA: 0.010138 | Mean Deviation: 0.000127
Typical Price: 0.010450 | SMA: 0.010171 | Mean Deviation: 0.000122
Typical Price: 0.010657 | SMA: 0.010199 | Mean Deviation: 0.000154
Typical Price: 0.010647 | SMA: 0.010224 | Mean Deviation: 0.000177
Typical Price: 0.010623 | SMA: 0.010252 | Mean Deviation: 0.000202
Typical Price: 0.010880 | SMA: 0.010308 | Mean Deviation: 0.000234
Typical Price: 0.010863 | SMA: 0.010354 | Mean Deviation: 0.000244
Typical Price: 0.010853 | SMA: 0.010397 | Mean Deviation: 0.000255
Typical Price: 0.010967 | SMA: 0.010442 | Mean Deviation: 0.000273
Typical Price: 0.011480 | SMA: 0.010517 | Mean Deviation: 0.000329
Typical Price: 0.011750 | SMA: 0.010600 | Mean Deviation: 0.000389
Typical Price: 0.011653 | SMA: 0.010674 | Mean Deviation: 0.000423

有效的示例代码:

public decimal[] Calculate(IReadOnlyList<(decimal High, decimal Low, decimal Close)> candles, int period)
{
    var ccis = new decimal[candles.Count];

    SMA sma = new SMA(period);
    var smas = sma.Calculate(candles.Select(e => e.Close).ToArray());

    for (int i = 0; i < candles.Count; i++)
    {
        var typicalPrice = (candles[i].High + candles[i].Low + candles[i].Close) / 3m;

        decimal total = 0m;

        for (int j = i; j >= Math.Max(i - period + 1, 0); j--)
        {
            total += Math.Abs(smas[j] - candles[j].Close);

            Console.WriteLine("Sum = " + total.ToString("f6"));
        }

        decimal meanDeviation = total / period;
        decimal cci = meanDeviation != 0 ? (typicalPrice - smas[i]) / meanDeviation / 0.015m : 0;

        //Console.WriteLine($"Typical Price: {typicalPrice.ToString("f6")} | SMA: {smas[i].ToString("f6")} | Mean Deviation: {meanDeviation.ToString("f6")}");

        ccis[i] = cci;
    }

    return ccis;
}

我要修复的实际(损坏)代码:

public class MovingAverageCalculator
{
    private readonly int _period;
    private readonly double[] _window;
    private int _numAdded;
    private double _varianceSum;

    public MovingAverageCalculator(int period)
    {
        _period = period;
        _window = new double[period];
    }

    public double Average { get; private set; }

    public double StandardDeviation
    {
        get
        {
            var variance = Variance;
            if (variance >= double.Epsilon)
            {
                var sd = Math.Sqrt(variance);
                return double.IsNaN(sd) ? 0.0 : sd;
            }
            return 0.0;
        }
    }

    public double Variance
    {
        get
        {
            var n = N;
            return n > 1 ? _varianceSum / (n - 1) : 0.0;
        }
    }

    public double MeanAbsoluteDeviation
    {
        get
        {
            //return _window.Average(e => Math.Abs(e - Average));

            // https://stackoverflow.com/questions/5336457/how-to-calculate-a-standard-deviation-array
            var n = N;
            var sumOfDifferences = _window.Sum(e => Math.Abs(e - Average));
            return n > 1 ? sumOfDifferences / (n - 1) : 0.0;
        }
    }

    public bool HasFullPeriod
    {
        get { return _numAdded >= _period; }
    }

    public IEnumerable<double> Observations
    {
        get { return _window.Take(N); }
    }

    public int N
    {
        get { return Math.Min(_numAdded, _period); }
    }

    public void AddObservation(double observation)
    {
        // Window is treated as a circular buffer.
        var ndx = _numAdded % _period;
        var old = _window[ndx];     // get value to remove from window
        _window[ndx] = observation; // add new observation in its place.
        _numAdded++;

        // Update average and standard deviation using deltas
        var oldAvg = Average;
        if (_numAdded <= _period)
        {
            var delta = observation - oldAvg;
            Average += delta / _numAdded;
            _varianceSum += (delta * (observation - Average));
        }
        else // use delta vs removed observation.
        {
            var delta = observation - old;
            Average += delta / _period;
            _varianceSum += (delta * ((observation - Average) + (old - oldAvg)));
        }
    }

    public void Reset()
    {
        _numAdded = 0;
        _varianceSum = 0;
    }
}

public class CommodityChannelIndex : Indicator<(decimal High, decimal Low, decimal Close), decimal>
{
    private readonly int _period;
    private readonly MovingAverageCalculator _movingAvg;

    public CommodityChannelIndex(int period)
    {
        _period = period;

        _movingAvg = new MovingAverageCalculator(_period);
    }

    public override decimal ComputeNextValue((decimal High, decimal Low, decimal Close) input)
    {
        decimal typicalPrice = (input.High + input.Low + input.Close) / 3m;

        _movingAvg.AddObservation((double)input.Close);

        if (_movingAvg.HasFullPeriod)
        {
            var average = (decimal)_movingAvg.Average;
            var meanDeviation = (decimal)_movingAvg.MeanAbsoluteDeviation;

            return meanDeviation != 0m ? (typicalPrice - average) / meanDeviation / 0.015m : 0m;
        }

        return 0;
    }

    public override void Reset()
    {
        throw new System.NotImplementedException();
    }
}

// USAGE
CommodityChannelIndex rsi = new CommodityChannelIndex(20);
for (int i = 0; i < candles.Count - 1; i++)
{
    var result = rsi.ComputeNextValue((candles[i].High, candles[i].Low, candles[i].Close));
    Console.WriteLine($"CCI = {result.ToString("f6")}");
}

平均绝对偏差之和被破坏。问题是如何解决?

编辑:

在Alireza纠正之后。仍然不准确。

Correct (how it should be):
CCI = -11.554556
CCI = 21.045918
CCI = 38.828097
CCI = 22.566381
CCI = 59.149184
CCI = 77.075455
CCI = 38.104311
CCI = 13.746847
CCI = -41.996578
CCI = -89.997229
CCI = -77.630112
CCI = 18.273976
CCI = 9.525936
CCI = -11.306480
CCI = 74.880871
CCI = 186.070619
CCI = 19.839042
CCI = -159.106198

Incorrect (n - 1) - before Alireza's answer:
CCI = -29.587542
CCI = 45.010768
CCI = 69.666667
CCI = 34.518799
CCI = 75.449922
CCI = 89.486260
CCI = 43.181818
CCI = 15.962060
CCI = -47.174211
CCI = -99.664083
CCI = -80.542391
CCI = 17.952962
CCI = 8.888889
CCI = -10.138104
CCI = 66.560510
CCI = 169.550087
CCI = 18.679280
CCI = -154.360812

Incorrect (n) - after Alireza's answer
CCI = -31.144781
CCI = 47.379756
CCI = 73.333333
CCI = 36.335578
CCI = 79.420970
CCI = 94.196064
CCI = 45.454545
CCI = 16.802168
CCI = -49.657064
CCI = -104.909561
CCI = -84.781464
CCI = 18.897855
CCI = 9.356725
CCI = -10.671689
CCI = 70.063694
CCI = 178.473776
CCI = 19.662400
CCI = -162.485066

项目存储库:

https://github.com/Warrolen/test-project

2 个答案:

答案 0 :(得分:1)

您为什么不滚动自己的处理数据的统计信息类?这是一个开始:

class Program
{
    static void Main(string[] args)
    {
        double[] observations = new double[]
        {
            61.27657038 ,
            6.738725617 ,
            4.888532706 ,
            68.5439831  ,
            7.979694724 ,
            46.29444503 ,
            55.91040488 ,
            2.120589448 ,
            18.75847801 ,
            1.340159128 ,
            1.188675161 ,
            0.444025252 ,
            0.126010202 ,
            55.90778938 ,
            55.76919429 ,
            4.976797265 ,
            56.27591183 ,
            34.25639959 ,
            1.045892651 ,
            15.92770207 ,
        };

        // take the last 10 data for statistics
        var data = observations.Reverse().Take(10).ToArray();
        var stats = new Statistics(data);

        Debug.WriteLine($"Count     ={stats.Count}");
        Debug.WriteLine($"Min       ={stats.MinValue}");
        Debug.WriteLine($"Max       ={stats.MaxValue}");
        Debug.WriteLine($"Median    ={stats.MedianValue}");
        Debug.WriteLine($"Mean      ={stats.MeanValue}");
        Debug.WriteLine($"Var       ={stats.Variance}");
        Debug.WriteLine($"Mad       ={stats.MeanAbsoluteDeviation}");
        Debug.WriteLine($"SDev      ={stats.StandardDeviation}");
    }
}

public class Statistics
{
    public Statistics(params double[] data)
    {
        this.Count = data.Length;
        // first pass to get mean;
        double sum = 0, min = 0, max = 0;
        for (int i = 0; i < data.Length; i++)
        {
            double x = data[i];
            if (i==0) // use first data for min/max
            {
                min = x;
                max = x;
            }
            else
            {
                min = Math.Min(min, x);
                max = Math.Max(max, x);
            }
            sum += x;
        }
        this.MinValue = min;
        this.MaxValue = max;
        this.MeanValue = sum/Count;

        // second pass for variance
        double var = 0, dev = 0, dx;
        for (int i = 0; i < data.Length; i++)
        {
            dx = data[i]-MeanValue;
            dev += Math.Abs(dx);
            var += dx*dx;
        }
        this.MeanAbsoluteDeviation = dev/Count;
        this.Variance = var/Count;
    }
    public int Count { get; }
    public double MaxValue { get; }
    public double MinValue { get; }
    public double MedianValue => (MinValue+MaxValue)/2;
    public double MeanValue { get; }
    public double MeanAbsoluteDeviation { get; }
    public double Variance { get; }
    public double StandardDeviation => Sqrt(Variance);
}

带有示例结果:

Count     =10
Min       =0.126010202
Max       =56.27591183
Median    =28.200961016
Mean      =22.5918397691
Var       =575.363095007382
Mad       =22.36838720272
SDev      =23.9867274759893

答案 1 :(得分:1)

我认为此更正是您想要的。您应该替换以下不正确的代码:

public double MeanAbsoluteDeviation
{
    get
    {
        //return _window.Average(e => Math.Abs(e - Average));

        // https://stackoverflow.com/questions/5336457/how-to-calculate-a-standard-deviation-array
        var n = N;
        var sumOfDifferences = _window.Sum(e => Math.Abs(e - Average));
        return n > 1 ? sumOfDifferences / (n - 1) : 0.0;
    }
}

使用正确的一个:

 public double MeanAbsoluteDeviation
{
    get
    {
        //return _window.Average(e => Math.Abs(e - Average));

        // https://stackoverflow.com/questions/5336457/how-to-calculate-a-standard-deviation-array
        var n = N;
        var sumOfDifferences = _window.Sum(e => Math.Abs(e - Average));
        return n > 1 ? sumOfDifferences / n : 0.0;
    }
}

编辑

还请注意,您对MeanAbsoluteDeviation的计算是错误的!您正在考虑所有值的当前平均值,同时应该将每个值与其平均值进行比较。

这是完整的解决方案:

public class Candle
    {
        public double average { get; set; } = 0;
        public double close { get; set; } = 0;
    }
    public class MovingAverageCalculator
    {
        private readonly int _period;
        private readonly Candle[] _window;
        private int _numAdded;
        private double _varianceSum;

        public MovingAverageCalculator(int period)
        {
            _period = period;
            _window = new Candle[period];
            for (int i = 0; i < period; i++)
                _window[i] = new Candle();

        }

        public double Average { get; private set; }

        public double StandardDeviation
        {
            get
            {
                var variance = Variance;
                if (variance >= double.Epsilon)
                {
                    var sd = Math.Sqrt(variance);
                    return double.IsNaN(sd) ? 0.0 : sd;
                }
                return 0.0;
            }
        }

        public double Variance
        {
            get
            {
                var n = N;
                return n > 1 ? _varianceSum / (n - 1) : 0.0;
            }
        }

        public double MeanAbsoluteDeviation
        {
            get
            {
                //return _window.Average(e => Math.Abs(e - Average));

                // https://stackoverflow.com/questions/5336457/how-to-calculate-a-standard-deviation-array
                var n = N;
                var sumOfDifferences = _window.Sum(e => Math.Abs(e.close - e.average));

                return n > 1 ? sumOfDifferences / n : 0.0;
            }
        }

        public bool HasFullPeriod
        {
            get { return _numAdded > _period; }
        }

        public IEnumerable<double> Observations
        {
            get { return _window.Take(N).Select(a=>a.close); }
        }

        public int N
        {
            get { return Math.Min(_numAdded, _period); }
        }

        public void AddObservation(double observation)
        {
            // Window is treated as a circular buffer.
            var ndx = _numAdded % _period;
            var old = _window[ndx].close;     // get value to remove from window
            _window[ndx] = new Candle() { close=observation,average=0 }; // add new observation in its place.
            _numAdded++;

            // Update average and standard deviation using deltas
            var oldAvg = Average;
            if (_numAdded <= _period)
            {
                var delta = observation - oldAvg;
                Average += delta / _numAdded;
                _window[ndx].average = Average;
                _varianceSum += (delta * (observation - Average));
            }
            else // use delta vs removed observation.
            {
                var delta = observation - old;
                Average += delta / _period;
                _window[ndx].average = Average;
                _varianceSum += (delta * ((observation - Average) + (old - oldAvg)));
            }
        }

        public void Reset()
        {
            _numAdded = 0;
            _varianceSum = 0;
        }
    }