mplfinance icon indicating copy to clipboard operation
mplfinance copied to clipboard

how to add text to figure

Open liaoshuren opened this issue 4 years ago • 23 comments

Hi,Denial: thanks for your great work! and I want to know if there is a way to add a text to figure,thank you

liaoshuren avatar Mar 22 '20 04:03 liaoshuren

presently there isn't a way to do this. we can possibly add a feature. Can you please give me a clear example or picture so that I can better understand what you want to accomplish? Thank you.

DanielGoldfarb avatar Mar 22 '20 04:03 DanielGoldfarb

wouldn't it be possible just to return the matplotlib figure reference so we can add our stuff without requiring additonal functions? would be so easy

jhmenke avatar Apr 05 '20 14:04 jhmenke

@jhmenke - That feature (set kwarg returnfig=True, and mpf.plot() will return a tuple of len==2 where first element is Figure and second element is List of Axes) is already merged (Pull #65) but not yet released to Pypi. (Follow this issue to track the next release.)

In the meantime, you can clone this repository to your local machine, and pip install from the repository (instead of from pypi) to be able to use that feature.

Let me know if that works for you.

DanielGoldfarb avatar Apr 05 '20 15:04 DanielGoldfarb

Excellent, thanks

jhmenke avatar Apr 05 '20 16:04 jhmenke

That's what I was looking for (I hope). So I can use all matplotlib features (log scale, title, figsize...). Make returnfig=True by default please (it will not make a difference for those who don't use it).

oricou avatar Apr 06 '20 09:04 oricou

Meanwhile you can already use plt.gcf().axes directly after the mpf.plot call to get the figure.

jhmenke avatar Apr 06 '20 09:04 jhmenke

Hi @jhmenke and @oricou it would be nice if you could share an example.

fxhuhn avatar Apr 06 '20 09:04 fxhuhn

mpf.plot(data, block=False)
figure = plt.gcf()
axes = plt.gcf().get_axes()  # probably two axis axes[0] / axes[1]

Then you can do additional plotting, e.g.

axes[0].fill_between(...)
axes[0].plot(...)

plt.show()

jhmenke avatar Apr 06 '20 09:04 jhmenke

I couldn't make it work...

mpf.plot(df, type='candle', style='yahoo')
fig = plt.gcf()
fig.figsize = (10,3)
fig.title = "My title"
plt.show()

oricou avatar Apr 06 '20 09:04 oricou

I am not sure if figsize can be changed after the fact, but you can set figsize in the mpf.plot call mpf.plot(figratio=(10, 3)).

You could try fig.suptitle("title") or plt.suptitle("title")

You also forgot block=False

jhmenke avatar Apr 06 '20 09:04 jhmenke

figratio works however I am quite afraid to see to so many arguments to mpf.plot. It makes more work to the developer and confuse users (for example I didn't expected figratio to work like figsize). From my point of view, mpf.plot should behave like plt.plot. It makes the plot and let things around like title, scales, labels, legend... to other functions

In the same spirit, addplot could be replaced by many mpf.plot as plt.plot does.

Now I must say it is easy to say what it should be when you do nothing. There may be many reasons I haven't see so don't feel bad, it is just my first reaction as a regular user of Matplotlib (I downloaded your lib an hour ago, and it seems to have a great potential).

oricou avatar Apr 06 '20 10:04 oricou

@jhmenke I couldn't make suptitle work.

oricou avatar Apr 06 '20 10:04 oricou

@jhmenke I couldn't make suptitle work.

I just tried plt.title("new title") and it worked flawlessly. Are you sure that you used block=False in the plot call?

jhmenke avatar Apr 06 '20 10:04 jhmenke

Yes, still doesn't work but that's not important if it will work with the returnfig option,

mpf.plot(df, block=False)
plt.title = "My title"
plt.show()

I use Jupyter which might be the reason??

oricou avatar Apr 06 '20 11:04 oricou

I'm a little short on time, but will try to respond to as much of the above discussion as possible. First, please understand the main philosophy of the the new mplfinance API. It is a two point philosophy:

  1. It should be easy for a matplotlib neophyte to make a financial plot, and be able to focus on their financial analysis without the need to spend a lot time learning matplotlib, at the same time
  2. It should provide "hooks" to allow matplotlib experts to customize and manipulate their plots.

Now two points about the above: (1) even with customizations we generally want to provide an easy way for users to do the most common customizations without having to write a lot of matplotlib code (while providing the matplotlib expert with access to more detailed matplotlib functionality). AND (2) The second part of the above philosophy is still very much in development!

You may have noticed that the description of the package says, New mplfinance package (to replace mpl-finance by mid 2020). ... In other words, a lot of what you are looking to do will be available, but possibly not until end of May or end of June for some of these features. If you would like to contribute coding skills to the project, some things may go faster.

Note also, a very big step to providing more detailed matplotlib access will come when https://github.com/matplotlib/mplfinance/issues/17 is complete (estimate late April, early May).

Now reagrding some of the above comments. I strongly recommend against grabbing the global Figure and Axes, and rather use returnfig=True. This will return the Figure and List of Axes. If you grab the global Figure and Axes you a mixing paradigms and behavior will be undefined.

Regarding defaulting to returnfig=True. It will make a difference to the average user because the will have to the call plt.show(). The idea is that mpf.plot() is a "higher level" API than your typical matplotlib API. Again, https://github.com/matplotlib/mplfinance/issues/17 will make it behave more like a typical lower level matplotlib API, where you yourself create and control Figure and Axes and pass the Axes into the API. In the meantime, it is what it is.

Regarding figscale and figratio (https://github.com/matplotlib/mplfinance/issues/60#issuecomment-609693616) you are right that this is confusing. This happened for historical reasons. Originally there was only figscale and the people asked for access to the aspect ratio. Perhaps to fix this blunder, we will simply add the kwarg figsize (which essentially figratio is).

Regarding titles, and some other stuff mentioned above. It is clear that [some of] you have not read through the notebooks in the examples folder. You should do that, particularly customization and styles


Finally, if you really prefer to do all the matplotlib work yourself, you may prefer to work with the old api: https://github.com/matplotlib/mplfinance#oldapi
https://github.com/matplotlib/mplfinance/tree/master/examples/original_flavor

DanielGoldfarb avatar Apr 07 '20 16:04 DanielGoldfarb

I actually just found this project a few days ago as I am switching from Java to Python. Awesome work @DanielGoldfarb! I too am looking for a way to add text to the chart, as I am quite new to using python/matplotlib I am a bit loss in how adding text would work when grabbing the axex from mplfinance.

I see here that this is the way to add text to graphs.

My question is, are the x axis in mplfinance numbers or dates? Also which axis am I suppose to grab from. From Daniels quote

(set kwarg returnfig=True, and mpf.plot() will return a tuple of len==2 where first element is Figure and second element is List of Axes)

I imagine that it is the first out of the list of axes so therefore ax1. Am I on the right track with this code, it doesn't seem to work...

date = mdates.date2num(data[x][0]) price = buy[x] ax1.text(date, price, str(price), fontsize=12) or price = buy[x] ax1.text(x, price, str(price), fontsize=12)

Neither seem to work, I feel like with this api which is rather a simplified version of matplotlib which makes it quicker to plot charts, that I am mixing apples and oranges?

cvanolm avatar May 01 '20 02:05 cvanolm

@cvanolm Caelan,

It will be tricky adding text using the Figure and Axes returned by mpf.plot() but it should be possible. It will certainly be easier once I have, or someone else has, time to add the enhancement to mpf.plot() itself.

The main issue is that you need the x,y coordinates for where you want to place the text, and as you suspected the x-axis is not, most of the time, actually dates. For now, let's not even disuss 'renko' or 'pnf' (point and figure) plots, because both of those distort the x-axis to expand and contract time based on price movements.

But for the other plot type (ohlc,candle,line) here is the issue: The default value for show_nontrading is False, which means we effectively do not plot non-trading days. The way to do this in matplotlib is to make the x-axis a series of integers from zero up to the number of data points (rows in your dataframe), and then internally, for the purpose of labeling the datetimes on the x-axis, we maintain a mapping between the integers and the dates that they represent.

So to get the correct x-value for your text, you have two choices:

  1. decide where you want your text in terms of the date, and then determine what row that date is on in your data (and use that as your x value), or
  2. set kwarg show_nontrading=True in mpf.plot() and then use a matplotlib date as your x-value. You convert from a python datetime.date to a matplotlib date using mdates.date2num(date) (where mdates is from import matplotlib.dates as mdates)

Regarding what the axes are that are returned:

  • mpf.plot() will return an array of either 2, 3, or 4 axes.
  • The first 2 are for the main panel
  • The second 2 are for the lower (volume) panel.
  • The second axis in each of the above two "pairs" of axes, is the axes that contains a "secondary_y", which is used if you passed in an addplot with data that required a different scale (order of magniture) than the primary data on that axis.

I hope that helps. Maybe it even gives you enough to consider writing the code to build this into mpf.plot() to make it easier for other in the future.

All the best. --Daniel

DanielGoldfarb avatar May 01 '20 04:05 DanielGoldfarb

Ideally, when we add this feature to mpf.plot(), the user can simply specify a date for the x-position of the text, where the date can be any of (1) a string such as '2020-05-01 09:30', or (2) a datetime.datetime, or (3) a Pandas.Timestamp.

mpf.plot() would automatically convert the date to whatever it needs for the x-axis, keeping things simple for the user.

There is code that does exactly that, recently added to mplfinance for this enhancment. regarding vlines, alines, and tlines.

DanielGoldfarb avatar May 01 '20 04:05 DanielGoldfarb

Thanks a lot Daniel!

It was rather easy to fix as I had just needed the arg of show_nontrading to display the text. For some reason I couldn't figure out the 1st method that you gave me regardless, I have it working.

As for the API, I may be able to find some time to implement this text feature. I will clone the repo and start working on it. Is there any sort of protocol or style you'd like? I guess I can just make the add-text-to-chart function in a separate file for now to make it less confusing.

cvanolm avatar May 01 '20 19:05 cvanolm

@cvanolm Caelan,

Please fork the repository first, then clone your fork. (If you are not familiar with the standard fork/clone/push/PR workflow, take a look at this).

Try to follow, more-or-less, the style of coding that exists in mplfinance, but ultimately do what you think is right and best. We can always adjust as necessary when getting ready to merge.

The interface should be relatively simple. Perhaps a kwarg called text=, where the value that the caller passes in is a dict, containing the following keys: x:, y:, and text:. The value for x: should be is_date_like() (you can see the function is_date_like() in file _arg_validators.py).

The text kwarg should also accept a list of dicts, where each of the dicts is like the "x,y,text" dict described above. In this way the user can put multiple text items on the same plot.

For implementation of the text kwarg, you may also find these methods useful for converting the "datelike" objects to what matplotlib needs for the x coordinate of where to place the text.

If you are unsure about any of this, take some time to read through the existing mplfinance code; there are plenty of examples of doing things as I have described here. And, of course, feel free to ask if you have any questions. Thank you so much! --Daniel

DanielGoldfarb avatar May 01 '20 21:05 DanielGoldfarb

Cheers, yes I am actually new to GitHub but I do have experience with Git. I just read through the forking process and will start this weekend. I have read through quite a bit of the code already but will continue to do so. I also will try to follow the code style in mplfinance to keep it clean and simple for future users/devs.

All the best, Caelan

cvanolm avatar May 01 '20 22:05 cvanolm

Ideally, when we add this feature to mpf.plot(), the user can simply specify a date for the x-position of the text, where the date can be any of (1) a string such as '2020-05-01 09:30', or (2) a datetime.datetime, or (3) a Pandas.Timestamp.

mpf.plot() would automatically convert the date to whatever it needs for the x-axis, keeping things simple for the user.

There is code that does exactly that, recently added to mplfinance for this enhancment. regarding vlines, alines, and tlines.

First, thank you for this fantastic library.

Second: Is this text thingie implemented ?.

traderblakeq avatar Nov 29 '22 11:11 traderblakeq

@traderblakeq Not yet implemented. The ability to enter any type of datetime or timestamp (including both objects and strings of) requires the ability to interpolate and sometimes extrapolate on the time axis. Since most users prefer to not show non-trading periods, the time axis is then non-linear/discontinuous.

Presently in the middle of a low level enhancement to make the non-linear/discontinuous interpolation and extrapolation easier. Once that is done then this text enhancement will be relatively easy to implement.

DanielGoldfarb avatar Nov 29 '22 12:11 DanielGoldfarb