Market Profile and Volume Profile in Python

Free yet powerful trade flow profiling tools for intraday stock market analysis

Letian Wang
5 min readSep 19, 2020

Market profile, as its name suggests, is a tool to profile stock market intraday trade activities. It was introduced by CBOT trader J. Peter Steidlmayer. It shows how much time the trading session is spent on a particular price level. Similarly, the volume profile depicts how many shares have been transacted at a particular price.

As time and sales trade flow comes in during a trading session, market profile aggregates it over the price axis to produce useful statistical properties. In particular, the TPO (Time Price Opportunity) chart aggregates across price and time, while volume profile aggregates across price and volume.

Some typical usages of the market profile include:

  • Initial balance and value area serves as support and resistance levels.
  • The Point of Control (POC) highlights the most active price.
  • A single TPO print that is not at tails suggests lack of market consensus.
  • Normal bell shape vs multimodal trend shape.

Intuitively, the market profile indicates the break-even cost for new buyers. If the price drops below, it becomes emotionally hard for them to cut loss. For details, I found this video useful for market profile and this channel is dedicated to the volume profile as well as order flows.

Instead, this post mainly focuses on its implementation in Python, so that you will have this free yet powerful tool in your arsenal. At the same time, it also provides the flexibility to tailor the charts according to your own needs. For example, if you only care about volume greater than a certain size, it is straightforward to filter out those trades by modifying a couple of lines in code.

The remainder of the post includes three parts. In the first part, it demonstrates how to run the tool in Google Colab free Jupyter notebook environment. Then in the second part, it discusses the aggregation logic for both the TPO profile and volume profile because the two are very similar. At last, we use python Plotly library to present the charting.

Generate Market Profile

Of course, you can download this file and run python script locally, but I find it convenient to use Google Colab free Jupyter notebook service. The input data is the one-minute intraday bar data provided by Yahoo Finance, which has been discussed in detail in my previous post.

Github has integrated seamlessly the Google Colab service. When you open the market profile notebook hosted here on Github, you may notice there is an open-in-Colab button at the very top of the notebook. It will bring you to Google Colab. Choose a date and symbol and then select Run all under the Runtime menu, the volume profile and market profile should show up at the bottom of this notebook.

market_profile.ipynb in Google Colab
Volume Profile; Data from Yahoo Finance
Market Profile; Data from Yahoo Finance

If this is all you are looking for, feel free to skip the remainder. In the next two sections, I will explain some implementation details in python.

Trade Aggregation

Under the hood market profile is a real-time statistical measure that allocates trades into price buckets or bars, so the general bar or histogram functions apply. In addition, NumPy provides the option of counting by volume, which serves exactly the purpose of volume aggregation.

vol_bars = np.histogram(df.Close, bins=price_buckets, weights=df.Volume)[0]

The histogram introduces one level of approximation as illustrated by the following example. Say the price bucket is set to $1, then $0.99 belongs to the first bucket while $1.01 belongs to the second bucket. Users should keep this in mind when they use the profile to set up support and resistance levels.

Another approximation comes from charting as illustrated in the code snippet below. Continue with the example, the $1 bucket is centred at $1.5. So when you are looking at the bar at $1.5, some of the trade flow actually comes from $1.01. This information is saved in the variable price_coors or price coordinates.

price_coors = pd.Series(price_buckets).rolling(2).mean().dropna()

For TPO profile, it is slightly more complicated. I first resample the bar data into the desired time frame and then draw a line between high TPO and low TPO for each time block, as below,

price_coors = pd.Series(price_buckets).rolling(2).mean().dropna()
df_agg = df.resample(time_pace).agg({'High': 'max', 'Low': 'min'})
tpo_bars = np.zeros([price_buckets.shape[0] - 1, df_agg.shape[0]], dtype=np.int32)
j = 0
for idx, row in df_agg.iterrows():
time_bars = np.histogram([row.Low, row.High],
ins=price_buckets)[0]
result = np.where(time_bars == 1)[0]
if result.shape[0] == 2:
time_bars[result[0]:result[1] + 1] = 1
tpo_bars[:, j] = time_bars
j += 1

Again, Pandas resample makes it very easy to aggregate into any desired time frames.

Trade Presentation

I’m generally happy with Plotly charting library. Comparing with standard Matplotlib, it allows you to freely zoom and drag the interactive graphs. More importantly, with few changes, it quickly becomes a Dash web app.

In this notebook tool, it demonstrates how to use three axes to generate a relatively advanced chart. The first axis is the candlestick chart, the second is the volume bar chart, and the third one is the market profile overlay.

fig1 = go.Candlestick(
x=df.index,
open=df.Open,
high=df.High,
low=df.Low,
close=df.Close,
xaxis='x',
yaxis='y2',
visible=True,
showlegend=False
)
fig2 = go.Bar(
x=df.index,
y=df.Volume,
yaxis='y',
name='Volume',
showlegend=False
)
fig3 = go.Bar(
x=vol_bars,
y=price_coors.values,
orientation='h',
xaxis='x2',
yaxis='y3',
visible=True,
showlegend=False,
marker_color='blue',
opacity=0.2
)

Again, the TPO chart is slightly more complicated, as we need to label the TPO grid. It is done through annotation function, and I used the numbers instead of alphabets as in the typical market profile charts, which I found more straightforward.

annotations = []for i in range(tpo_bars.shape[0]):  # price
non_zero_time = 0
for j in range(tpo_bars.shape[1]): # time
if tpo_bars[i, j] == 0:
continue
annotations.append(dict(xref='x2', yref='y3',
x=non_zero_time+0.5, y=price_coors.values[i],
text=str(j),
font=dict(family='Arial', size=14,
color='black'),
showarrow=False))
non_zero_time += 1
fig.update_layout(annotations=annotations)

Conclusion

In this post, it illustrates how to combine Yahoo Finance, Google Colab, and Python Plotly to generate a free yet very powerful interactive charting tool for intraday market profiling analysis. There are some improvements to be made.

  • Highlight the initial balance and value area.
  • Label the market open and current price.
  • Present multiple days; currently, you have to open two notebooks and put them side by side.

--

--