Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make drawing trend lines simpler #42

Closed
dattran2346 opened this issue Mar 3, 2020 · 13 comments
Closed

make drawing trend lines simpler #42

dattran2346 opened this issue Mar 3, 2020 · 13 comments
Assignees
Labels
enhancement New feature or request released code merged into repo AND released to Pypi

Comments

@dattran2346
Copy link

Is there anyway that I can get the matplotlib axes/figure object and add my custom plotting on top.

I currently want to draw trendlines like this

ac = ([dateA, dateC], [priceA, priceC])
l = mlines.Line2D(*ac)
ax.add_line(l)

The result look like
Screen Shot 2020-03-03 at 23 47 12

@dattran2346 dattran2346 added the question Further information is requested label Mar 3, 2020
@DanielGoldfarb
Copy link
Collaborator

You should be able to do this using the addplot= keyword to mpf.plot().
Please click here for documentation/examples.

The tend line data will need to be in a list, or pandas series or dataframe, that has the same length and datetime index as your original OHLC data. If there are dates over which you do not want the lines to extend (as in the picture you have provided) you can simply use NaN values at the dates over which you do not want to see the trend lines.

Let me know if that works for you.

@DanielGoldfarb
Copy link
Collaborator

a couple of thoughts on this:

  • For this to work as noted in my previous comment above, you will have to generate a time series of data (rather that specify only two points as you did in your example).

    • However I see that is inconvenient, so I am thinking perhaps just for the simple case of trend lines, we could provide a kwarg trend= that accepts two dates and two prices as in your example. It could also accept a list of date/price pairs to be able to plot multiple trend lines.
    • This is not a priority at present, since there is the addplot() alternative, however if you would like to contribute such an enhancement I would consider reviewing and merging the code.
  • there is a possibility, as was mentioned in this issue, that there will be an enhancement to allow mpf.plot() to return the Figure and Axes that it creates. In the meantime, you can do what is described above.

@DanielGoldfarb
Copy link
Collaborator

I did some experimenting with returning Figure and Axes. It didn't really work to allow me to add to the plot after calling mpf.plot(). And, it seemed to me, the issues that arose became only more challenging when I tried to make it work.

Anyone who wants to should feel free to experiment with returning Figure and Axes and let me know if you can get it to work without fundamentally changing the design of mplfinance. Keep in mind that the old API is still available and the approach of the old API is to give you full control and responsibility for the Figure and Axes. So if you really want access to Figure and Axes you can also just use the old API, since it is fundamentally designed for that.

Now, regarding trend lines, as I noted in a comment above, it is definitely possibly to plot them using the make_addplot() functionality. However the caller must generate the data for the trend line. This, it seems to me, is at least not particularly convenient. So I am going to leave this issue open as an enhancement to make drawing trend lines simpler.

I am considering an implementing this as a kwarg to make_addplot()

  • trend= ... a tuple or list of two dates or four dates. If two dates, then the two dates define the trend line, going from close to close on the two sepcified dates. If four dates are provided then the second pair of dates extend the trend line somewhat beyond the two dates that define slope of the trend line.

@DanielGoldfarb DanielGoldfarb changed the title Getting matplotlib figure make drawing trend lines simpler Mar 5, 2020
@DanielGoldfarb DanielGoldfarb added enhancement New feature or request and removed question Further information is requested labels Mar 5, 2020
@dattran2346
Copy link
Author

As you noted above, I currently use the old candlestick_ohlc api to and plot the channel on the return figure/axes object.

Regarding the new trend line api as you suggested, I am thinking that it is more flexible to pass the list of points that users want to draw lines, and also more favorable to change trend=... to lines=..., since user may want to connect multiple interesting points on the chart, i.e, make_addplot(lines=[(dateA, priceA), (dateB, priceB), (dateC, priceC), ...]).

For example, I currently want to plot the double bottom pattern on the chart.
BINANCE_BATBTC_D

Ps: I would like to help you implement this enhancement,. let my know your opinion.

@DanielGoldfarb
Copy link
Collaborator

DanielGoldfarb commented Mar 5, 2020

@dattran2346

Have you tried using make_addplot() as it exists now to draw these lines??

It simply requires that you have a list (or a Series with a DatetimeIndex) that is the same length as the original dataframe. That list (or Series) should contain the values from your list of date,price tuples, each in their correct location within the list (or Series), and contain NaN at all other points.

This is important, because the way that matplotlib handles not showing non-trading days is to use a range of integers for the x-axis (and then simply format them as dates). This is similar to candlestick2_ohlc(). In the new API you can see the code here where it converts dates to xdates which may or may not be actual dates.

This fact, the fact that internally the x-axis may be dates or may be integers, complicates the matter of you wanting to pass in a sparse list of date,price tuples.

The simpliest thing to do (as mentioned above) is to first convert your sparse list of date,price tuples into a Series (with the same length and same DatetimeIndex from the original ohlc dataframe). Inside this series, the dates corresponding to your date,price tuples would have the value (price) from the tuple, and all other dates would have a value of NaN. (I think that would work; if not we can always interpolate values).

If we were to take a more direct approach (similar to calling Line2D() in your original posting) then we would have to add code to mpf.plot() to do that. Function mpf.plot() would have to handle the special case of trend lines much differently from all of the other addplot configurations. This would complicate mpf.plot() making it more difficult to maintain over time.

Therefore, if you want to specify just a sparse list of date,price tuples, I suggest we write a function that converts such a sparse list to a Series as described above. Then the question becomes where to call that function.

One possibility is to call that function before calling make_addplot(), and to pass the output from that function into the data argument of make_addplot(). In this way, we would not need to make any changes to either make_addplot() nor plot(). We could call this function make_trendlines() or make_lines(). It would look something like this:

data = mpf.make_lines( [(dateA, priceA), (dateB, priceB), (dateC, priceC), ...] )
ap   = mpf.make_addplot(data,...)
mpf.plot( df, addplot=ap, ...)

Another possibility is to call the conversion function inside make_addplot(). If so, then, as you propose above, make_addplot() could have a kwarg called lines=. I am a reluctant to do it this way because it then means there are two ways to pass data into make_addplot() ... through the data variable, or through the lines variable.

If we want make_addplot() to call the conversion function internally, then I would pass the list of date,price tuples into make_addplot() where the data is normally passed. Then we either add a kwarg sparsedata=True to tell make_addplot() to call the converter, or perhaps make_addplot() can figure out on its own that the data it received is a list of date,price tuples (and decide to call the converter).

What do you think?

As I am writing this, I am beginning to like the last proposal the best:
make_addplot() can simply accept a sparse list of date,price tuples (in same data variable where it normally accepts the full list of data). It detects this situation, and calls the converter to convert the sparse list into a full list with prices and NaN values in the correct places. It then places the converted data into the dict that it returns.


Correction:
I just realized there is one thing missing above. Conversion of the sparse list of date,price tuples, to a series with the same datetime index as the dataframe needs also the datetime index, or the list of dates from the datetime index as input to the conversion function. Therefore it may make sense to call the conversion function from within mpf.plot() because that function already has the datetime index available. I also did some playing around with this code, and it appears that addplot will not plot the values as lines, rather only as a scatter plot, unless we [linearly] interpolate missing values (the values between the specified date,price tuples).

@thegamecat
Copy link

Can't it work the same as plt so:
mpf.scatter(trade_list_idx,trade_list_price,c='b')
mpf.plot(pr1, c='r')
mpf.plot(hsr1, c='b')
mpf.plot(lsr1, c='y')

@DanielGoldfarb
Copy link
Collaborator

@thegamecat

I have no idea what you are trying to say with:

Can't it work the same as plt so:
mpf.scatter(trade_list_idx,trade_list_price,c='b')
mpf.plot(pr1, c='r')
mpf.plot(hsr1, c='b')
mpf.plot(lsr1, c='y')

This present issue is about plotting trend lines (not a scatter plot of trades).

@DanielGoldfarb
Copy link
Collaborator

@dattran2346
I think I have a way to make this as simple as you proposed. Something like this:

ap = mpf.make_addplot( [(dateA, priceA), (dateB, priceB), (dateC, priceC), ...] )
mpf.plot( df, addplot=ap)

Will keep you posted. Have a couple other things to finish up first.

@DanielGoldfarb
Copy link
Collaborator

just a note, so I don't forget; i am thinking of implementing this as a LineCollection and, internally, the same implementation could be used for issue #54

@putraxor
Copy link

@dattran2346
I think I have a way to make this as simple as you proposed. Something like this:

ap = mpf.make_addplot( [(dateA, priceA), (dateB, priceB), (dateC, priceC), ...] )
mpf.plot( df, addplot=ap)

Will keep you posted. Have a couple other things to finish up first.

it doesn't work

                lines = []
                for line_date, line_row in data.iterrows():
                    lines.append((line_date, line_row['signal']))

                pattern_line = mpf.make_addplot(lines)

                signals = mpf.make_addplot(
                    window['signal'], scatter=True, markersize=97)
                mpf.plot(window, volume=True, type='candle',
                         addplot=[signals, pattern_line], style='yahoo')

Raises error:

Traceback (most recent call last):
  File "peaks_valleys.py", line 156, in <module>
    addplot=[signals, pattern_line], style='yahoo')
  File "/home/putraxor/.local/lib/python3.7/site-packages/mplfinance/plotting.py", line 35, in decorator
    return func(*args, **kwargs)
  File "/home/putraxor/.local/lib/python3.7/site-packages/mplfinance/plotting.py", line 323, in plot
    raise TypeError('apdata is list but NOT of float or int')
TypeError: apdata is list but NOT of float or int

@DanielGoldfarb
Copy link
Collaborator

@putraxor - This is not implemented yet. I am almost finished with this enhancement; should be ready sometime next week. I decided to add kwargs to mpf.plot() (no need to call make_addplot()).

This is how I expect it to work: There will be three (or four) kwargs:

  • hline= ... specify one or a list of prices to generate a horizontal line at that price level

  • vline= ... specify one or a list of dates/datetimes to generate a vertical line at that point in time

  • aline= ... specify one or more arbitrary line as

    • list of two or more x,y values (multiple lines are contiguous)
      • examples:
        [(dateA,priceA),(dateB,priceB)]
        [(dateA,priceA),(dateB,priceB),(dateC,priceC),(dateD,priceD),...]
    • list of one or more x,y value pairs (multiple lines need not be contiguous)
      where a single x,y pair is like ((dateA,priceA),(dateB,priceB))
      • examples:
        aline=((dateA,priceA),(dateB,priceB))
        aline=[((dateA,priceA),(dateB,priceB)),((dateC,priceC),(dateD,priceD)),...]
  • tline= ... trend line (still deciding whether to implement this).

    • specify two (or more) dates or datetimes: mplfinance will automatically draw trend lines between the dates. An additional kwarg tline_value or something like that may specify which algorithm to use for calculating the trend line between any two dates.

@DanielGoldfarb DanielGoldfarb added merged / awaiting release to pypi code merged into repo, but not yet released to Pypi and removed in progress labels Apr 30, 2020
@DanielGoldfarb DanielGoldfarb added released code merged into repo AND released to Pypi and removed merged / awaiting release to pypi code merged into repo, but not yet released to Pypi labels May 6, 2020
@chuckf201
Copy link

Daniel - I don't need anything - just wanted to thank you and the rest of the team for your (real-time) development. Thank you.

@DanielGoldfarb
Copy link
Collaborator

Daniel - I don't need anything - just wanted to thank you and the rest of the team for your (real-time) development. Thank you.

@chuckf201
You're welcome. Thanks for expressing your appreciation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request released code merged into repo AND released to Pypi
Projects
None yet
Development

No branches or pull requests

5 participants