I want to calculate the mean absolute deviation and I'm currently using the following class from Stack Overflow (link here), posted by 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)));
}
}
}
The person who made that class, did it somehow weirdly and I don't understand the calculations, because he's using different formulas. For example, people normally calculate the variance just like this.
Can someone explain me how to calculate the mean absolution deviation in that class?
Formulas:
Edit:
This one is a bit more accurate but not enough. Any ideas?
Output (should be):
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
Output (what it is):
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
Output from another CCI (should be):
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
Output from CommodityChannelIndex.cs (what it is):
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
A working example code:
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;
}
The actual (broken) code that I want to fix:
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")}");
}
The sum for the mean absolute deviation is broken. The question is how to fix it?
Edit:
After Alireza's correction. Still inaccurate.
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