17 using System.Collections.Generic;
44 private readonly decimal _valueAreaVolumePercentage;
49 private readonly decimal _priceRangeRoundOff;
65 private SortedList<decimal, decimal> _volumePerPrice;
75 private int _pointOfControl;
80 public SortedList<decimal, decimal>
VolumePerPrice =>
new SortedList<decimal, decimal>(_volumePerPrice);
125 public override bool IsReady => _totalVolume.IsReady;
140 protected MarketProfile(
string name,
int period, decimal valueAreaVolumePercentage = 0.70m, decimal priceRangeRoundOff = 0.05m)
144 if (priceRangeRoundOff <= 0)
146 throw new ArgumentException(
"Must be strictly bigger than zero.", nameof(priceRangeRoundOff));
150 _valueAreaVolumePercentage = valueAreaVolumePercentage;
152 _volumePerPrice =
new SortedList<decimal, decimal>();
153 _totalVolume =
new Sum(name +
"_Sum", period);
154 _priceRangeRoundOff = 1 / priceRangeRoundOff;
166 Add(input, VolumeQuantity);
169 _pointOfControl = GetMax();
182 CalculateValueArea();
199 private void Add(
TradeBar input, decimal VolumeQuantity)
202 var isFilled = _oldDataPoints.IsReady;
204 _oldDataPoints.Add(
new Tuple<decimal, decimal>(input.
Close, VolumeQuantity));
206 var ClosePrice = Round(input.
Close);
207 if (!_volumePerPrice.Keys.Contains(ClosePrice))
209 _volumePerPrice.Add(ClosePrice,VolumeQuantity);
213 _volumePerPrice[ClosePrice] += VolumeQuantity;
216 _totalVolume.Update(input.
Time, VolumeQuantity);
223 var RemovedDataPoint = _oldDataPoints.MostRecentlyRemoved;
224 ClosePrice = Round(RemovedDataPoint.Item1);
228 if (_volumePerPrice.ContainsKey(ClosePrice))
230 _volumePerPrice[ClosePrice] -= RemovedDataPoint.Item2;
231 if (_volumePerPrice[ClosePrice] == 0)
233 _volumePerPrice.Remove(ClosePrice);
256 if(Math.Abs(mid/2 - index)<Math.Abs(mid/2 - maxIdx))
268 private void CalculateValueArea()
271 ValueAreaVolume = _totalVolume.Current.Value * _valueAreaVolumePercentage;
275 var minIndex = _pointOfControl;
276 var maxIndex = _pointOfControl;
278 int lastMin, lastMax;
279 int nextMinIndex, nextMaxIndex;
281 decimal lowVolume, highVolume;
291 nextMinIndex = Math.Max(minIndex - 1, 0);
294 if (nextMinIndex != lastMin)
303 if (nextMaxIndex != lastMax)
315 if ((highVolume == 0) || ((lowVolume != 0) && (lowVolume > highVolume)))
317 currentVolume += lowVolume;
318 minIndex = nextMinIndex;
320 else if ((lowVolume == 0) || ((highVolume != 0) && (highVolume >= lowVolume)))
322 currentVolume += highVolume;
323 maxIndex = nextMaxIndex;
342 private decimal Round(decimal a)
344 return Math.Ceiling(a * _priceRangeRoundOff) / _priceRangeRoundOff;
352 _oldDataPoints.Reset();
353 _volumePerPrice.Clear();
354 _totalVolume.Reset();