Noobie Noobie - 6 months ago 98
Python Question

how to get an intraday price - volume plot in pandas?

I have a dataframe that contains price/volume data on an intraday basis:

time price volume
2015-04-15 10:10:00 10 500
2015-04-15 10:20:00 15 100
2015-04-15 10:30:00 20 70
2015-04-15 10:30:00 etc etc


I need to get a standard price - volume chart, where the top chart contains the prices (a regular line), and the bottom chart contains the volume (a bar chart).

Both charts should share the same axis, of course.

So far, I have come up with:

plt.figure(figsize=(20,15))

ax1=plt.subplot2grid((2,2),(0,0),colspan=2)
ax2=plt.subplot2grid((2,2),(1,0),colspan=2)

ax2.xaxis.set_major_locator(HourLocator(interval=3))
ax2.xaxis.set_major_formatter(DateFormatter('%H:%M'))

data.ix['2015-10-01': '2015-10-02','price'].plot(ax=ax1)
data.ix['2015-10-01': '2015-10-02','volume'].plot(ax=ax2, kind='bar')


But I get super-dense tick labels for the bar charts (the chart is unusable).

How can I simply specify to have minor ticks every hours, and major ticks every 3 hours (so that the chart is still readable)?

Answer

There are some challenges with pandas.plot.bar() and DateTimeIndex that are discussed for instance here. The following:

import matplotlib as mpl
import matplotlib.pyplot as plt

plt.style.use('ggplot')
import pandas as pd
import numpy as np
from datetime import datetime

n = 100
idx = pd.date_range(start=datetime(2016, 1, 1, 10), freq='10Min', periods=n)
data = pd.DataFrame(data={'price': np.cumsum([0.0001] * n + np.random.random(n)),
                          'volume': np.random.randint(low=100, high=10000, size=n)}, index=idx)

fig, ax = plt.subplots(nrows=2, sharex=True, figsize=(15,8))

ax[0].plot(data.index, data.price)
ax[1].bar(data.index, data.volume, width=1/(5*len(data.index)))

xfmt = mpl.dates.DateFormatter('%H:%M')
ax[1].xaxis.set_major_locator(mpl.dates.HourLocator(interval=3))
ax[1].xaxis.set_major_formatter(xfmt)

ax[1].xaxis.set_minor_locator(mpl.dates.HourLocator(interval=1))
ax[1].xaxis.set_minor_formatter(xfmt)

ax[1].get_xaxis().set_tick_params(which='major', pad=25)

fig.autofmt_xdate()
plt.show()

produces the below result. Notice that falling back to matplotlib requires some fiddling with the width parameter in ax[1].bar(). For minorticks, you may want to look here for more detailed formatting options, esp. re skipping intervals to avoid overlap.

enter image description here