volumeProfile static method

VolumeProfileResult volumeProfile(
  1. List<TaBar> points, {
  2. int buckets = 24,
})

Implementation

static VolumeProfileResult volumeProfile(List<TaBar> points, {int buckets = 24}) {
  if (points.isEmpty) {
    return VolumeProfileResult(buckets: [], pocPrice: 0, pocVolume: 0);
  }

  double lo = points.first.low ?? points.first.close;
  double hi = points.first.high ?? points.first.close;
  for (final p in points) {
    final low = p.low ?? p.close;
    final high = p.high ?? p.close;
    if (low < lo) lo = low;
    if (high > hi) hi = high;
  }
  final range = hi - lo;
  if (range <= 0) {
    return VolumeProfileResult(
      buckets: [VolumeBucket(priceLow: lo, priceHigh: hi, volume: points.fold(0.0, (s, p) => s + (p.volume ?? 0)))],
      pocPrice: (lo + hi) / 2,
      pocVolume: points.fold(0.0, (s, p) => s + (p.volume ?? 0)),
      valueAreaLow: lo,
      valueAreaHigh: hi,
    );
  }

  final vols = List<double>.filled(buckets, 0);
  final ups = List<double>.filled(buckets, 0);
  final bucketSize = range / buckets;

  for (final p in points) {
    final vol = p.volume ?? 0;
    if (vol <= 0) continue;
    final cLow = (p.low ?? p.close);
    final cHigh = (p.high ?? p.close);
    // Find which buckets this candle's range overlaps
    final startBucket = ((cLow - lo) / bucketSize).floor().clamp(0, buckets - 1);
    final endBucket = ((cHigh - lo) / bucketSize).floor().clamp(0, buckets - 1);
    final span = endBucket - startBucket + 1;
    final volPerBucket = vol / span;
    final isUp = p.open == null || p.close >= p.open!;
    for (int b = startBucket; b <= endBucket; b++) {
      vols[b] += volPerBucket;
      if (isUp) ups[b] += volPerBucket;
    }
  }

  double pocVol = 0;
  int pocIdx = 0;
  for (int i = 0; i < buckets; i++) {
    if (vols[i] > pocVol) {
      pocVol = vols[i];
      pocIdx = i;
    }
  }

  final result = List.generate(
    buckets,
    (i) => VolumeBucket(
      priceLow: lo + i * bucketSize,
      priceHigh: lo + (i + 1) * bucketSize,
      volume: vols[i],
      upVolume: ups[i],
    ),
  );

  final (vaLo, vaHi) = valueArea(vols, pocIdx);
  return VolumeProfileResult(
    buckets: result,
    pocPrice: lo + (pocIdx + 0.5) * bucketSize,
    pocVolume: pocVol,
    valueAreaLow: result[vaLo].priceLow,
    valueAreaHigh: result[vaHi].priceHigh,
  );
}