Back To Top

August 14, 2025

Visualizing Market Pressure of Buyers and Sellers

Map cumulative buy/sell pressure and dominance across price levels with this Price-Volume Map

We derive an analysis to show where price conviction concentrates. We segment price into bullish/bearish buckets and aggregate volume directionally.

The aim is to see where market participants, both bears and bulls, commit capital and show dominance in key price zones.

This approach helps in spotting support & resistance. We borrow the visualization from liquidity dynamics, i.e. where transactions occur.

The complete Python notebook for the analysis is provided below.

Visualizing Market Pressure of Buyers and Sellers

1. The Dual-Sided Price-Volume Model

Traditional volume bars show how much was traded, but not where or in which direction.

We structure a price-volume map that splits volume into two directional layers:

  • Bullish Volume: Trades where the closing price exceeds the opening price (Ct > Ot)
  • Bearish Volume: Trades where the closing price falls below the opening price (Ct < Ot)

Each trading day contributes to one of these groups. We then aggregate volume by price bucket, rather than by time.

The time-series becomes a price-distribution model to show the capital concetrated across the price ladder.

Price Bucketing

To create consistent volume profiles, we round each closing price to a fixed tick size δ:

Visualizing Market Pressure of Buyers and Sellers

Pt​ is the closing price on day t, and δ is the tick size (e.g. $0.01). Each bucket holds aggregated volume from days that closed near that price.

Volume Aggregation

Once bucketed, we compute:

Visualizing Market Pressure of Buyers and Sellers
  • Ct​: Close price
  • Ot​: Open price
  • Vt​: Volume
  • B: Set of all visible bars
  • p: Price bucket

Cumulative Volume Distribution

To quantify dominance, we normalize each side:

Visualizing Market Pressure of Buyers and Sellers

This cumulative fraction lets us interpret how deep into the price range buyers or sellers are committing.

A steeper curve means volume is front-loaded around a narrow zone, i.e. it shows conviction.

2. Implementation in Python

2.1. Set Parameters and Download Data

We start by importing libraries and defining user parameters.

These include the ticker symbol, date range, time interval, and number of bars to display.

The tick size controls the resolution of price bucketing.

We then use yfinance to fetch adjusted historical OHLCV data and retain the most recent VISIBLE_BARS.

				
					import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
from matplotlib.dates import AutoDateLocator, AutoDateFormatter

# ── USER PARAMS
TICKER       = "PLTR"
START        = "2024-01-01"
END          = "2025-07-13"
INTERVAL     = "1d"
VISIBLE_BARS = 180             # decrease for fewer datapoints or increase for broader history
TICK_SIZE    = 0.01            # smaller values give finer resolution, larger smooth the chart

# color definitions
BULL_EDGE_COLOR = "#089981"
BULL_FILL_COLOR = (8/255,153/255,129/255,0.90)
BEAR_EDGE_COLOR = "#F23645"
BEAR_FILL_COLOR = (242/255,54/255,69/255,0.90)

# ── FETCH DATA
df = yf.download(
    TICKER,
    start=START,
    end=END,
    interval=INTERVAL,
    auto_adjust=True,
    progress=False
)
if isinstance(df.columns, pd.MultiIndex):
    df.columns = df.columns.get_level_values(0)
vis = df.tail(VISIBLE_BARS).copy()
				
			

2.2. Segment Directional Trades and Bucket Prices

We label each row as either bullish or bearish based on the candle body. A day is bullish if it closes above the open, bearish if it closes below.

We then round each closing price to the nearest tick size to create discrete price buckets.

These buckets define the y-axis for our chart. For each side, bullish and bearish, we aggregate total volume by price bucket.

				
					vis['bucket'] = (vis['Close']/TICK_SIZE).round().astype(int) * TICK_SIZE
bull = vis[vis['Close'] > vis['Open']]
bear = vis[vis['Close'] < vis['Open']]

bull_map = bull.groupby('bucket')['Volume'].sum()
bear_map = bear.groupby('bucket')['Volume'].sum()
				
			

2.3. Compute Cumulative Distributions and Metrics

We normalize raw volume to prepare it for visual scaling. This avoids distortion from outlier bars.

Next, we calculate the total volume on each side and identify the price with the highest directional volume.

We sort price levels in descending order, then compute cumulative volume shares. These show how volume stacks up across the price ladder.

From the cumulative distribution, we extract the median price where volume share crosses 50 percent.

We also compute VWAP as a volume-weighted average across buckets. We draw these reference lines to mark where conviction is most concentrated.

Lastly, we define the price axis range using the min and max of all directional levels and raw price data.

Prev Post

Stock Market News are Mostly Noise

Next Post

Detecting Stock Market Cycles

post-bars
Mail Icon

Newsletter

Get Every Weekly Update & Insights

[mc4wp_form id=]

Leave a Comment