Volume cluster analysis visualization development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Volume cluster analysis visualization development
Complex
~5 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Volume Cluster Analysis Visualization Development

Volume cluster analysis (also Volume Profile, Footprint chart) is a way to distribute trading volume by price levels rather than time. Instead of asking "how much volume was there at 14:00?" the question becomes "how much volume was there at price $43,500?". This is a fundamentally different view of the market, showing zones of real participant interest.

Theory: What Volume Cluster Shows

Volume Profile vs Regular Volume

A standard volume bar on a chart shows total volume for a period (candle). Volume profile distributes that volume by prices within the period:

Price $43,800 │████████████████████ 1,240 BTC
Price $43,750 │███████████████ 870 BTC
Price $43,700 │████████████████████████████ 2,100 BTC ← POC
Price $43,650 │████████████ 680 BTC
Price $43,600 │██████████████ 780 BTC

POC (Point of Control) — price level with maximum volume. Market spent the most time and/or volume here. Strong support/resistance level.

VAH / VAL (Value Area High / Low) — boundaries of the value zone where 70% of volume occurred (standard — one standard deviation from POC).

HVN / LVN (High Volume Node / Low Volume Node) — zones of attraction and zones of fast price movement respectively.

Footprint chart

Footprint (trace) is an improvement on volume profile, adding separation of buy volume and sell volume for each price cell:

$43,700 │ 890B × 1,210S │ delta: -320
$43,650 │ 1,450B × 680S │ delta: +770  ← seller absorption
$43,600 │ 340B × 1,890S │ delta: -1,550 ← aggressive sellers

Delta = Buy Volume - Sell Volume. Positive delta = buyers more aggressive. Divergences between price and delta — often precursors of reversals.

Imbalance — when bid/ask volume on adjacent levels differs by >300% (configurable parameter). Indicates aggressive absorption.

Data Sources

Problem: Public APIs Don't Provide Tick Data

Binance, Bybit, OKX publicly provide K-line (OHLCV) data, but not tick-by-tick trades with bid/ask split. For full footprint need aggTrades (aggregated trades) or raw trades.

Binance aggTrades WebSocket:

import asyncio
import websockets
import json

class FootprintCollector:
    def __init__(self, symbol: str):
        self.symbol = symbol.lower()
        self.price_levels = {}  # price -> {buy: 0, sell: 0}
        self.tick_size = 10  # aggregate into $10 clusters

    async def collect(self):
        url = f"wss://stream.binance.com:9443/ws/{self.symbol}@aggTrade"

        async with websockets.connect(url) as ws:
            async for message in ws:
                trade = json.loads(message)
                await self.process_trade(trade)

    async def process_trade(self, trade: dict):
        price = float(trade["p"])
        quantity = float(trade["q"])
        is_buyer_maker = trade["m"]  # True = aggressive seller

        # Round to cluster
        cluster_price = round(price / self.tick_size) * self.tick_size

        if cluster_price not in self.price_levels:
            self.price_levels[cluster_price] = {"buy": 0.0, "sell": 0.0}

        if is_buyer_maker:
            # Maker = limit order. Aggressive = market seller
            self.price_levels[cluster_price]["sell"] += quantity
        else:
            self.price_levels[cluster_price]["buy"] += quantity

Important nuance: is_buyer_maker = True means buyer was in book (limit), seller came with market order. So aggressive side is seller. This is often confused.

Historical Data

For building historical volume profile:

Source Depth Quality Cost
Binance aggTrades REST Up to 1000 records per request Good Free
Tardis.dev Full history Excellent (tick data) $50-500/month
Kaiko Up to 7 years Institutional $1,000+/month
Own collector From start Full control Infrastructure

For production: own data collector (aggTrades streaming → TimescaleDB) plus historical backfill via REST API on startup.

System Architecture

Backend: Collection and Aggregation

aggTrade WebSocket ──► Trade Collector ──► Kafka Topic (raw_trades)
                                               │
                                  ┌────────────┤
                                  ▼            ▼
                         Volume Aggregator  Footprint Builder
                                  │            │
                                  ▼            ▼
                           TimescaleDB    TimescaleDB
                         (volume_profile) (footprint_data)
                                  │            │
                                  └────────┬───┘
                                           ▼
                                     WebSocket API ──► Frontend

TimescaleDB ideal for this: PostgreSQL with hypertables for time-series. Time partitioning + continuous aggregates for pre-computed timeframes.

-- Hypertable for raw trades
CREATE TABLE trades (
    time TIMESTAMPTZ NOT NULL,
    symbol VARCHAR(20) NOT NULL,
    price NUMERIC(20, 8) NOT NULL,
    quantity NUMERIC(20, 8) NOT NULL,
    side VARCHAR(4) NOT NULL,  -- 'buy' | 'sell'
    trade_id BIGINT
);
SELECT create_hypertable('trades', 'time');

-- Materialized volume profile
CREATE MATERIALIZED VIEW volume_profile_1h AS
SELECT
    time_bucket('1 hour', time) AS bucket,
    symbol,
    round(price / 10) * 10 AS price_cluster,  -- $10 cluster
    SUM(CASE WHEN side = 'buy' THEN quantity ELSE 0 END) AS buy_volume,
    SUM(CASE WHEN side = 'sell' THEN quantity ELSE 0 END) AS sell_volume,
    SUM(quantity) AS total_volume
FROM trades
GROUP BY bucket, symbol, price_cluster;

Continuous aggregate automatically updates aggregate on new data without full recalculation.

Volume Profile Metric Calculation

from dataclasses import dataclass
from typing import List, Dict
import statistics

@dataclass
class VolumeLevelData:
    price: float
    buy_volume: float
    sell_volume: float
    total_volume: float

    @property
    def delta(self) -> float:
        return self.buy_volume - self.sell_volume

    @property
    def delta_percent(self) -> float:
        if self.total_volume == 0:
            return 0
        return (self.delta / self.total_volume) * 100


class VolumeProfileCalculator:
    def __init__(self, levels: List[VolumeLevelData]):
        self.levels = sorted(levels, key=lambda x: x.price)
        self._poc: VolumeLevelData = None

    @property
    def poc(self) -> VolumeLevelData:
        """Point of Control — level with max volume"""
        if not self._poc:
            self._poc = max(self.levels, key=lambda x: x.total_volume)
        return self._poc

    @property
    def value_area(self) -> tuple:
        """Value Area — 70% volume around POC"""
        total = sum(l.total_volume for l in self.levels)
        target = total * 0.70

        accumulated = self.poc.total_volume
        poc_idx = self.levels.index(self.poc)
        up_idx = poc_idx
        down_idx = poc_idx

        while accumulated < target:
            can_up = up_idx < len(self.levels) - 1
            can_down = down_idx > 0

            up_vol = self.levels[up_idx + 1].total_volume if can_up else 0
            down_vol = self.levels[down_idx - 1].total_volume if can_down else 0

            if up_vol >= down_vol and can_up:
                up_idx += 1
                accumulated += up_vol
            elif can_down:
                down_idx -= 1
                accumulated += down_vol
            else:
                break

        return self.levels[up_idx].price, self.levels[down_idx].price  # VAH, VAL

    def get_hvn_lvn(self, threshold_percentile: float = 70) -> Dict:
        """High-volume and low-volume nodes"""
        volumes = [l.total_volume for l in self.levels]
        threshold_high = statistics.quantiles(volumes, n=100)[threshold_percentile - 1]
        threshold_low = statistics.quantiles(volumes, n=100)[100 - threshold_percentile - 1]

        return {
            "hvn": [l for l in self.levels if l.total_volume >= threshold_high],
            "lvn": [l for l in self.levels if l.total_volume <= threshold_low]
        }

Frontend Visualization

Canvas-based Rendering

Standard libraries like Recharts or Chart.js can't handle rendering thousands of price levels in real-time. Need Canvas 2D or WebGL render.

Lightweight Charts (TradingView) supports custom series via Plugin API — preferred option if platform already uses this chart:

import { createChart, ISeriesApi } from 'lightweight-charts';

class VolumeProfilePlugin {
  private canvas: HTMLCanvasElement;
  private ctx: CanvasRenderingContext2D;

  draw(data: VolumeProfileLevel[], priceRange: PriceRange) {
    const maxVolume = Math.max(...data.map(d => d.totalVolume));

    data.forEach(level => {
      const y = this.priceToY(level.price, priceRange);
      const barWidth = (level.totalVolume / maxVolume) * this.maxBarWidth;

      // Buy volume — green
      const buyWidth = barWidth * (level.buyVolume / level.totalVolume);
      this.ctx.fillStyle = 'rgba(38, 166, 154, 0.6)';
      this.ctx.fillRect(0, y, buyWidth, this.levelHeight);

      // Sell volume — red
      this.ctx.fillStyle = 'rgba(239, 83, 80, 0.6)';
      this.ctx.fillRect(buyWidth, y, barWidth - buyWidth, this.levelHeight);

      // POC highlight
      if (level.isPoc) {
        this.ctx.strokeStyle = '#FFD700';
        this.ctx.lineWidth = 2;
        this.ctx.strokeRect(0, y, barWidth, this.levelHeight);
      }
    });
  }
}

Performance: with 1000+ levels use requestAnimationFrame with throttle to 60fps, redraw only changed levels (dirty checking). WebGL via PixiJS gives another 5-10x speedup with very high detail.

Real-time Updates

WebSocket architecture for live footprint:

class FootprintWebSocket {
  private ws: WebSocket;
  private pendingUpdates: Map<number, LevelUpdate> = new Map();
  private renderScheduled = false;

  onUpdate(update: LevelUpdate) {
    // Buffer updates
    const existing = this.pendingUpdates.get(update.price) || emptyLevel;
    this.pendingUpdates.set(update.price, merge(existing, update));

    if (!this.renderScheduled) {
      this.renderScheduled = true;
      requestAnimationFrame(() => {
        this.flushUpdates();
        this.renderScheduled = false;
      });
    }
  }

  private flushUpdates() {
    // Apply all accumulated updates in one render frame
    this.pendingUpdates.forEach((update, price) => {
      this.chart.updateLevel(price, update);
    });
    this.pendingUpdates.clear();
  }
}

Batching updates in requestAnimationFrame is critical: without it each trade triggers redraw, which at 1000 trades/sec kills browser performance.

Additional Volume Profile Indicators

Cumulative Volume Delta (CVD) — accumulated delta sum across all periods. CVD divergence with price = trend weakness signal.

VWAP (Volume Weighted Average Price) — volume-weighted average price:

VWAP = Σ(Price_i × Volume_i) / Σ(Volume_i)

Institutional algorithms often use VWAP as execution benchmark.

TPO (Time Price Opportunity) — each letter on TPO chart = 30 min trading at that price level. Classic Market Profile from CME Group.

Volume cluster analysis is serious analytical superstructure that attracts professional traders. Well-implemented footprint chart is competitive advantage hard to copy quickly.