mplfinance icon indicating copy to clipboard operation
mplfinance copied to clipboard

How to write names in hlines(Horizontal lines)

Open Ripper1111 opened this issue 4 years ago • 11 comments

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?

Ripper1111 avatar May 04 '20 21:05 Ripper1111

Do you mean a text annotation on the plot, describing each horizontal line?

DanielGoldfarb avatar May 04 '20 22:05 DanielGoldfarb

Do you mean a text annotation on the plot, describing each horizontal line?

Yes

Ripper1111 avatar May 05 '20 05:05 Ripper1111

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:

  1. https://github.com/matplotlib/mplfinance/issues/60#issuecomment-622220997
  2. https://github.com/matplotlib/mplfinance/issues/60#issuecomment-622237065

DanielGoldfarb avatar May 05 '20 10:05 DanielGoldfarb

Hello @DanielGoldfarb ,

Has there been any progress to be able to add text annotations to mplfinance based on your response above?

Thanks!

mjessa12 avatar Dec 02 '20 04:12 mjessa12

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 avatar Apr 30 '21 22:04 krazykoder

@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

DanielGoldfarb avatar Apr 30 '21 23:04 DanielGoldfarb

See also https://github.com/matplotlib/mplfinance/issues/387#issuecomment-828135146

DanielGoldfarb avatar Apr 30 '21 23:04 DanielGoldfarb

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 avatar May 07 '21 04:05 krazykoder

@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

DanielGoldfarb avatar May 07 '21 15:05 DanielGoldfarb

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))

DanielGoldfarb avatar May 07 '21 20:05 DanielGoldfarb

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?

akarun2405 avatar Dec 20 '21 15:12 akarun2405