mplfinance
mplfinance copied to clipboard
How to write names in hlines(Horizontal lines)
mpl.plot(frame,hlines=dict(hlines=[support1,support2,resistance1,resistance2],colors=['g','g','r','r'],linestyle='-.'),type='candlestick', style = s,title='Pivot Chart',ylabel='Price',returnfig=True)
I want to give name in front of these horizontal lines. How to do that?
Do you mean a text annotation on the plot, describing each horizontal line?
Do you mean a text annotation on the plot, describing each horizontal line?
Yes
Direct and full support for text annotations is not yet available; probably will be within the next month or two.
In the meantime, there is a workaround, to set returnfig=True
when calling mpf.plot()
. This will cause 'mpf.plot()
to return the Figure and Axes (and not call plt.show()
) which will allow you to call text()
on the axes of your choice (and the call plt.show()
).
For more details, see these two comments here:
- https://github.com/matplotlib/mplfinance/issues/60#issuecomment-622220997
- https://github.com/matplotlib/mplfinance/issues/60#issuecomment-622237065
Hello @DanielGoldfarb ,
Has there been any progress to be able to add text annotations to mplfinance based on your response above?
Thanks!
Hello @DanielGoldfarb just checking in to see if you got a chance to add the text annotations functionality to mplfinance. Appreciate your help.
thanks!
@krazykoder
Not yet. I realize it has been a while. Working on some other major enhancemnts, one in particular taking a long time (to be able to extrapolate x-axis, and have more control over placement of ticks on the x-axis). Have you been able to make any of the above mentioned workarounds work for you? Let me know if you need any assistance.
All the best. --Daniel
See also https://github.com/matplotlib/mplfinance/issues/387#issuecomment-828135146
Daniel, I was able to follow the instructions in your post on the row number and use this to my purpose. Sharing a snippet of my code here.
#Text annotate example
ax1 = axlist[0]
style = dict(size=5, color='black')
xpos = df[s:e].index.get_loc('2021-03-19 10:30', method='nearest') # find row # with the nearest index to x
axlist[0].text(xpos, 40, "| 03/19", **style)
fig.show()
Would appreciate it if you have a better idea. In this case, I am annotating on a larger time frame with data from a smaller timeframe. Ideally, I would want to place the marker and text somewhere in between the two-row of a larger timeframe - which is not technically possible in this approach. So I am finding the nearest date and row number and annotating the text here.
@krazykoder
Thanks for posting your snippet of code. I didn't know about the method
kwarg to Index.get_loc()
.
The basic idea, when the date you want is between two of the dates in the index, is to linearly interpolate between them. The trick is that, to interpolate, you must first convert the dates to matplotlib dates (which are actually floats and thus permit basic math).
Here's a piece of code from the enhancement that I am working on. I hope to release this into mplfinance sometime this summer. In the meantime you can copy/paste the code below.
When I wrote the code below, I used slices on a series (taking the first or last item from the slice) in order to find the previous and next loc
for a given date. Now that I know about the method
kwarg, it seems to me it can also work using .get_loc
method=ffill
and method=bfill
but I have not tested it that way. It works fine as written below:
#!/usr/bin/env python
# coding: utf-8
import pandas as pd
import datetime as datetime
import matplotlib.dates as mdates
def date_to_mdate(date):
"""Convert a `date` to a matplotlib date:
Input: `date` may be any of:
(1) parseble string containing a date or datetime as a string,
(2) python `datetime.date` or `datetime.datetime`,object
(3) pandas.Timestamp object
Returns a Matplotlib Date: floating point number of days from 01-Jan-0001, plus one day.
"""
if isinstance(date,str):
pydt = pd.to_datetime(date).to_pydatetime()
elif isinstance(date,pd.Timestamp):
pydt = date.to_pydatetime()
elif isinstance(date,(datetime.datetime,datetime.date)):
pydt = date
else:
return None
return mdates.date2num(pydt)
def date_to_loc(dtindex,date):
if not isinstance(dtindex,pd.DatetimeIndex):
raise TypeError('dtindex must be a `pandas.DatetimeIndex` object')
dtseries = dtindex.to_series()
d1s = dtseries.loc[date:]
if len(d1s) < 1:
sdtrange = str(dtseries[0])+' to '+str(dtseries[-1])
raise ValueError('Specified date "'+str(date)+'" is beyond '+
'(greater than) range of datetime index ('+sdtrange+').')
d1 = d1s.index[0]
d2s = dtseries.loc[:date]
if len(d2s) < 1:
sdtrange = str(dtseries[0])+' to '+str(dtseries[-1])
raise ValueError('Specified date "'+str(date)+'" is before '+
'(less than) range of datetime index ('+sdtrange+').')
d2 = d2s.index[-1]
# If there are duplicate dates in the series, for example in a renko plot
# then .get_loc(date) will return a slice containing all the dups, so:
loc1 = dtseries.index.get_loc(d1)
if isinstance(loc1,slice): loc1 = loc1.start
loc2 = dtseries.index.get_loc(d2)
if isinstance(loc2,slice): loc2 = loc2.stop - 1
if loc1 == loc2:
return float(loc1)
# convert dates to matplotlib dates which are
# floats and thus permit basic math (+-*/) in
# order to be able to linearly interpolate:
mpd1 = date_to_mdate(d1)
mpd2 = date_to_mdate(d2)
mpd = date_to_mdate(date)
slope = (loc2 - loc1) / (mpd2 - mpd1)
yitrcpt = loc1 - (slope*mpd1)
x = (slope)*mpd + yitrcpt
return x
# Test the code:
# Generate a datetime index with a point every 15 minutes:
ix = pd.date_range('5/7/2021 09:30','5/7/2021 16:00',freq='15T')
print('datetime index=')
print(ix,'\n')
# Check some dates that fall on and around one of the datetime index points:
checkdates = ['2021-05-07 11:43','2021-05-07 11:44','2021-05-07 11:45',
'2021-05-07 11:46','2021-05-07 11:55','2021-05-07 11:50']
for date in checkdates:
loc = date_to_loc(ix,date)
print('date=',date, ' loc=',loc,)
Output from the above test:
datetime index=
DatetimeIndex(['2021-05-07 09:30:00', '2021-05-07 09:45:00',
'2021-05-07 10:00:00', '2021-05-07 10:15:00',
'2021-05-07 10:30:00', '2021-05-07 10:45:00',
'2021-05-07 11:00:00', '2021-05-07 11:15:00',
'2021-05-07 11:30:00', '2021-05-07 11:45:00',
'2021-05-07 12:00:00', '2021-05-07 12:15:00',
'2021-05-07 12:30:00', '2021-05-07 12:45:00',
'2021-05-07 13:00:00', '2021-05-07 13:15:00',
'2021-05-07 13:30:00', '2021-05-07 13:45:00',
'2021-05-07 14:00:00', '2021-05-07 14:15:00',
'2021-05-07 14:30:00', '2021-05-07 14:45:00',
'2021-05-07 15:00:00', '2021-05-07 15:15:00',
'2021-05-07 15:30:00', '2021-05-07 15:45:00',
'2021-05-07 16:00:00'],
dtype='datetime64[ns]', freq='15T')
date= 2021-05-07 11:43 loc= 8.866666666930541
date= 2021-05-07 11:44 loc= 8.933333333581686
date= 2021-05-07 11:45 loc= 9.0
date= 2021-05-07 11:46 loc= 9.066666666883975
date= 2021-05-07 11:55 loc= 9.666666666744277
date= 2021-05-07 11:50 loc= 9.333333333255723
Btw, just realized that loc2 - loc1
is always 1.0
so these three lines:
slope = (loc2 - loc1) / (mpd2 - mpd1)
yitrcpt = loc1 - (slope*mpd1)
x = (slope)*mpd + yitrcpt
can be replaced with one line:
x = loc1 + ((mpd-mpd1)/(mpd2-mpd1))
Good to know that annotate feature is something on the roadmap Daniel.. Looking forward to it. Do you know when you expect the feature to drop?