I am using matplotlib to create an OHLC candlestick stock price chart. I am using mpl_finance's candlestick_ohlc module to create the chart. Creating the chart is straightforward, however the x
and y
axis display at the bottom of the chart shows the date and y axis value for a given cursor position but I would like the x
and y
axis display to show the date and that dates open, high, low, close (ohlc) values rather than just a date and a y
axis cursor position value. The quotes dataset is in the format of a list of tuples where each tuple contains the date as a number followed by the open, high, low, close and volume.
I am trying to use matplotlib's format_coord function to specify the ohlc values but I can not figure out how to get the format_coord function to accept the list containing the dates and associated ohlc values as an input and to then give the desired date and OHLC output. The following is some simplified code I have written that exhibits my issue: The code below has now been modified so that it fully works:
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, WeekdayLocator, DayLocator, MONDAY
from mpl_finance import candlestick_ohlc
from matplotlib.dates import date2num, num2date
def ohlc_daily_date_axis():
mondays = WeekdayLocator(MONDAY)
alldays = DayLocator()
weekFormatter = DateFormatter('%b %d %Y') # e.g., Jan 12 2018
quotes = [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
(737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
(737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
(737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
(737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
(737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
(737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
(737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
(737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
(737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]
fig, ax = plt.subplots(figsize=(18,5))
plt.subplots_adjust(bottom=0.2)
ax.xaxis.set_major_locator(mondays)
ax.xaxis.set_minor_locator(alldays)
ax.xaxis.set_major_formatter(weekFormatter)
candlestick_ohlc(ax, quotes, width=0.6)
ax.xaxis_date()
ax.autoscale_view()
plt.setp(plt.gca().get_xticklabels(), rotation=45,
horizontalalignment='right')
#the following line puts the ohlc data in the y axis display
ax.format_coord = get_ohlc_from_date_xy
# the following line puts the ohlc data in the x axis display
#ax.fmt_xdata = get_ohlc_from_date_x
plt.show()
def get_ohlc_from_date_x(dateasnum):
print('dateasnum: ', int(dateasnum))
quotes = [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
(737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
(737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
(737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
(737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
(737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
(737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
(737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
(737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
(737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]
for i in range(len(quotes)):
if int(dateasnum) == quotes[i][0]:
open = quotes[i][1]
high = quotes[i][2]
low = quotes[i][3]
close = quotes[i][4]
vol = quotes[i][5]
dte = str(num2date(dateasnum).date())
print('type(dte): ', type(dte))
print('open: ', open)
ohlc_str = dte + ' open: ' + str(open) + ' high: ' + str(high) + '
low: ' + str(low) + ' close: ' + str(close) + ' vol: ' + str(int(vol))
+ ' '
return ohlc_str
def get_ohlc_from_date_xy(dateasnum,y):
print('dateasnum: ', int(dateasnum))
quotes = [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
(737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
(737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
(737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
(737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
(737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
(737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
(737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
(737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
(737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]
for i in range(len(quotes)):
if int(dateasnum) == quotes[i][0]:
open = quotes[i][1]
high = quotes[i][2]
low = quotes[i][3]
close = quotes[i][4]
vol = quotes[i][5]
dte = str(num2date(dateasnum).date())
#print('type(dte): ', type(dte))
#print('open: ', open)
ohlc_str = 'open: ' + str(open) + ' high: ' + str(high) + '
low: ' + str(low) + ' close: ' + str(close) + ' vol: ' + str(int(vol))
return dte, ohlc_str
# This def does not work
def format_coord(x,y, quotes):
for i in range(len(quotes)):
if int(x) == quotes[i]:
open = quotes[i][1]
high = quotes[i][2]
low = quotes[i][3]
close = quotes[i][4]
vol = quotes[i][5]
y = 'open: ' + open # I'm just using open to simplify things
x = DateFormatter('%b %d %Y')
return (x,y)
if __name__ == '__main__':
ohlc_daily_date_axis()
If I run this code as it is I get the following error (This is the error I got when I was using the incorrect def format_coord(x,y, quotes)
approach):
File "/Users/Me/Mee/python_db_programs/learn_matplotlib_test.py", line 33,
in ohlc_daily_date_axis
ax.format_coord = format_coord(quotes)
TypeError: format_coord() missing 2 required positional arguments: 'y'
and 'quotes'
If I comment out the ax.format_coord = format_coord(quotes)
line then the code runs fine but without my desired date and ohlc values in the x
and y
display. Any help on how to proceed would be greatly appreciated.
I ended up not trying to change the y
display but instead added the ohlc values to the x
display. Which means I changed ax.format_coord = format_coord(quotes)
to the command that just formats the x
coordinate, which is ax.fmt_xdata
and then wrote a def
that used the quotes list to get each dates corresponding ohlc data:
ax.fmt_xdata = get_ohlc_from_date
instead of
ax.format_coord = format_coord(quotes)
and then added this def:
def get_ohlc_from_date(dateasnum):
print('dateasnum: ', int(dateasnum))
quotes = [(737042.0, 2.72, 2.78, 2.6815, 2.74, 414378.0),
(737045.0, 2.71, 2.77, 2.57, 2.63, 578841.0),
(737046.0, 2.64, 2.64, 2.4228, 2.47, 1451450.0),
(737047.0, 2.9, 3.15, 2.7, 2.96, 7230260.0),
(737048.0, 2.92, 3.29, 2.67, 2.83, 2784110.0),
(737049.0, 2.78, 2.82, 2.4701, 2.51, 822776.0),
(737052.0, 2.56, 2.6344, 2.49, 2.5, 278883.0),
(737054.0, 2.5, 2.619, 2.34, 2.6, 606002.0),
(737055.0, 2.57, 2.63, 2.45, 2.57, 1295820.0),
(737056.0, 2.57, 2.75, 2.51, 2.65, 435838.0)]
for i in range(len(quotes)):
if int(dateasnum) == quotes[i][0]:
open = quotes[i][1]
high = quotes[i][2]
low = quotes[i][3]
close = quotes[i][4]
vol = quotes[i][5]
dte = str(num2date(dateasnum).date())
print('type(dte): ', type(dte))
print('open: ', open)
ohlc_str = dte + ' open: ' + str(open) + ' high: ' + str(high) + '
low: ' + str(low) + ' close: ' + str(close) + ' vol: ' + str(int(vol))
+ ' '
return ohlc_str
Also because I used matplotlibs dateasnum function I had to import that as well:
from matplotlib.dates import num2date
While this does not replace the y
axis coordinate with the ohlc values it does provide the ohlc values in the x
and y
axis display
After figuring out how to add the ohlc values to the x
axis display I realized that the logic I used to add the ohlc values to the x
axis display could be applied to the y
axis display allowing for the display of the ohlc values in the y
axis parameter. This was accomplished by using the ax.format_coord = format_coord
command and creating a new def that assigned the ohlc values to the y
axis return value. I have modified the original code I posted so that depending on whether or not the ax.format_coord = format_coord
line or the ax.fmt_xdata = get_ohlc_from_date
line is commented out determines if the ohlc values are displayed as part of the x
axis display or as part of the y
axis display