Skip to content
New issue

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

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

Already on GitHub? # to your account

X-Axis Date and Time series #573

Closed
tdavidge opened this issue Nov 28, 2022 · 13 comments
Closed

X-Axis Date and Time series #573

tdavidge opened this issue Nov 28, 2022 · 13 comments
Labels
question Further information is requested

Comments

@tdavidge
Copy link

I am trying to create a 5 day chart of Intraday Stock data using mplfinance. From Open to Close only, no data gaps. I can get a chart to save and/or show but am struggling with formatting the grid spacing and the x-axis interval/labels. I have been trying to get this working in matplotlib for what seems like an age now and I just cannot get it work. At least with mplfinance I was able to get a chart to work. I'm simply not good enough at python coding to understand how to format the xaxis I am afraid.

My question obviously is how to format grid and x-axis to line the grid with at least the open of the day (as in on change of date) so I can see the overnight GAP, and say a frequency of hourly between 09:30am and 4:00pm. I apologize in advance for my ignorance but this is all kinda new to me and having been trying all manner of examples that I do not understand, this is my last resort.

Any assistance would be very much appreciated.
Using python 3.7 and mplfinance v0.12.9b5 on windows 11 pro.

import sys, os, datetime, time, warnings #csv
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import mplfinance as mpf
from datetime import datetime

sym = sys.argv[1] #symbol name
act_dt = sys.argv[2] #active date of the required process no quotes
act_int = sys.argv[2].replace('/', '-')#this converts the incoming dates into values that can be used in the savefig filename

#the following is the dataframe df
Date    Open    High     Low   Close

2022-11-17 09:30:00  332.73  333.60  332.73  333.54
2022-11-17 09:31:00  333.52  333.70  333.39  333.55
2022-11-17 09:32:00  333.55  333.88  333.47  333.77
2022-11-17 09:33:00  333.76  333.92  333.76  333.86
2022-11-17 09:34:00  333.67  333.67  333.67  333.67
                ...     ...     ...     ...     ...
2022-11-25 13:36:00  343.60  343.60  343.45  343.45
2022-11-25 13:41:00  343.50  343.50  343.50  343.50
2022-11-25 13:46:00  343.47  343.47  343.47  343.47
2022-11-25 13:48:00  343.20  343.20  343.20  343.20
2022-11-25 13:49:00  343.47  343.47  343.47  343.47

df.columns = ['Date', 'Open', 'High', 'Low', 'Close']

df.set_index('Date')
df.index = pd.to_datetime(df.Date)
print(df)

mc = mpf.make_marketcolors(up='g',down='r',inherit=True)
s  = mpf.make_mpf_style(marketcolors=mc, gridaxis='both', gridstyle='dotted')

mpf.plot(df,style=s, 
	datetime_format='%b %d',
	type='line',
	title='\n'+sym+' '+act_int, 
	ylabel='Price',
	xrotation=90,
	tight_layout=True,
	figratio=(12,5),
	figscale=1,
	returnfig=True,
	savefig=dict(fname='ohlc_wide_'+sym+'_'+act_int+'.jpg',dpi=125, quality=95, bbox_inches='tight'),
	)
mpf.show()

This gives me the attached chart.

ohlc_wide_DIA_11-25-2022

@tdavidge tdavidge added the question Further information is requested label Nov 28, 2022
@tdavidge
Copy link
Author

So, I am making progress albeit with great difficulty. With the help of this I have managed to get the first attachement by show and then manually saving. However, when using savefig I get the second attachement.
chart1
chart2

My questions now are
a) how to get the short date (no year) to appear horizontally centered below each of the 5 days time bars
b) why the difference between showing and saving the output ?
c) why is the show result offset to the right ? in the display window I cannot see the full price, not is there a ylabel

code:

import sys, os, datetime, time, warnings #csv
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import mplfinance as mpf
from datetime import datetime

import pyodbc #database connectivity
cnxn = pyodbc.connect(database connection)

sym = sys.argv[1] #symbol name
act_dt = sys.argv[2] #active date of the required process no quotes
act_int = sys.argv[2].replace('/', '-')#this converts the incoming dates into values that can be used in the savefig filename

df=pd.read_sql_query(" \
database query
",cnxn, )

df.columns = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']

df.set_index('Date')
df.index = pd.to_datetime(df.Date)
print(df)

start_date = df['Date'].iloc[0]
end_date = df['Date'].iloc[-1]

print(start_date)
print(end_date)

fig, axlist= mpf.plot(df,
	style='yahoo', 
	type='line', 
	title='\n'+sym+' '+act_int, 
	volume=False, 
	xrotation=90, 
	ylabel='Price',
	figsize=(15,7), 
	figscale=1,
	returnfig=True,
	tight_layout=True,
	show_nontrading=False,
	#savefig=dict(fname='ohlc_wide_'+sym+'_'+act_int+'.jpg',dpi=125, quality=95, bbox_inches='tight')
	)

#idx = pd.date_range(start_date, end_date, freq='30T')
idx = pd.date_range(start_date, end_date, freq='1H')
df_label_idx = pd.DataFrame(index=idx)

df_label = pd.merge(df_label_idx, df, how='inner', left_index=True, right_index=True )

# Tick labels are generated based on df_label
tick_labels = list(df_label.index.strftime('%H:%M'))
ticklocations = [df.index.get_loc(tick) for tick in df_label.index ]

axlist[-2].xaxis.set_ticks(ticklocations)
axlist[-2].set_xticklabels(tick_labels)

mpf.show()

@DanielGoldfarb
Copy link
Collaborator

DanielGoldfarb commented Dec 2, 2022

This is not a trivial problem, but it is do-able, just a bit tricky. Also, for what it's worth, I am hoping at some point to build a more generalized solution into mplfinance to make it easier for users to do things like this. In the meantime this is what we have and you are mostly on the right track.

First, I have some questions about your questions because I don't understand them entirely.

"a) how to get the short date (no year) to appear horizontally centered below each of the 5 days time bars"
Is your question about formatting (not showing a year) or about where the tick appears (in the middle of each day vs the beginning of each day)? I am fairly certain that the tick label must appear where the tick itself is. But there may be a matplotlib work-around (that I have not tried yet) such as this. If that is what this question is about (shifting the tick labels left or right relative to the ticks themselves) it can be done but lets first focus on getting the exact ticks that you want.

"b) why the difference between showing and saving the output ?"
What exactly is the difference between the First Image, and this Second Image? It sounds like you are implying that the code is exactly the same except for how you saved the plot, which at first doesn't make sense to me because the x-axis is clearly formatted differently. Maybe I'm missing something. On the other hand, indeed it could be ... I faintly recall (will have to try to find the issue/discussion) seeing something, maybe a year ago, about matplotlib's savefig method doing it's own axis formatting, ignoring what you otherwise specified when calling .show(). I will see if I can find that.

"c) why is the show result offset to the right ? in the display window I cannot see the full price, not is there a ylabel"
It could be due to tight_layout. Try it without tight_layout. If that is the issue, you may be able to use tight_layout in combination with scale_padding. See this answer for more information about scale_padding. You may need to increase (scale up) the padding on the right to provide additional space for the y-axis label.


That said, ultimately what do you want? Would you be satisfied with one date label per day at the beginning of each day, or maybe at the middle of each day? I'm going to play around and see what I can get to work.

@DanielGoldfarb
Copy link
Collaborator

I worked up some sample code. Let me know if this works for you:

import mplfinance as mpf
import yfinance as yf
import datetime

df = yf.download('DIA',start='2022-11-07',end='2022-11-15',interval='15m')

ticks = []
tlabs = []
dates = sorted(list(set([d.date() for d in df.index])))
for d1 in dates:
    d2 = d1 + datetime.timedelta(days=1)
    ts = df.loc[d1:d2].index[0]  # first timestamp of each day
    ticks.append(df.index.get_loc(ts))
    tlabs.append(d1.strftime('%b %d'))

fig, axlist = mpf.plot(df,type='candle',xrotation=0,style='yahoo',tight_layout=True,returnfig=True)
axlist[-2].set_xticks(ticks,labels=tlabs,ha='left')

mpf.show()

image

@tdavidge
Copy link
Author

tdavidge commented Dec 2, 2022 via email

@DanielGoldfarb
Copy link
Collaborator

Perhaps you have an older version of matplotlib. Don't upgrade just yet as that may introduce too many new changes. Rather try splitting up setting ticks and labels as in your code:

axlist[-2].xaxis.set_ticks(ticks)
axlist[-2].set_xticklabels(tlabs)

(

@tdavidge
Copy link
Author

tdavidge commented Dec 2, 2022 via email

@tdavidge
Copy link
Author

tdavidge commented Dec 2, 2022 via email

@DanielGoldfarb
Copy link
Collaborator

a) I saw once that there is a way (in matplotlib) to create "two level" axis labels, so that you have one level showing the times (perhaps every two hours) and another level showing the date, but I would have to research how to do it. A simpler solution, that may not be exactly what you want, would be to format the datetime to include both date and time and modify the code to show maybe 2 or 3 ticks per day (instead of 1 tick per day as I had it). Alternatively, do major ticks as I showed (one per day with date), and do minor ticks perhaps hourly.

b) Yes, as I mentioned before there is definitely some kind of a problem with savefig() tries to do its own axis formatting. I ran into the once before and can't remember what the solution was. I am going to spend a little time looking into this savefig issue,

@DanielGoldfarb
Copy link
Collaborator

I did some playing around, and I was able to get both dates and times on separate levels of the tick labels, using major and minor ticks. Please let me know if this is something like what you are trying to do:

ticks = []
tlabs = []
mitks = []
milab = []
dates = sorted(list(set([d.date() for d in df.index])))

for d1 in dates:

    # Major Ticks:
    d2 = d1 + datetime.timedelta(days=1)
    ts = df.loc[d1:d2].index[0]
    ticks.append(df.index.get_loc(ts))
    # line feed at beginning of label so it appears a lower below the x-axis:
    tlabs.append('\n'+d1.strftime('%b %d'))

    # Minor Ticks:
    mitks.append(ticks[-1]+0.1)
    milab.append('09:30')
    ts = df.loc[str(d1)+' 13:00':d2].index[0]
    mitks.append(df.index.get_loc(ts))
    milab.append('13:00')

fig, axlist = mpf.plot(df,type='candle',xrotation=0,style='yahoo',tight_layout=True,returnfig=True,figratio=(2,1))

axlist[-2].set_xticks(ticks)
axlist[-2].set_xticklabels(tlabs, ha='left')
axlist[-2].set_xticks(mitks,minor=True)
axlist[-2].set_xticklabels(milab, ha='center', minor=True, rotation=0)

mpf.show()

The result:

image

@tdavidge
Copy link
Author

tdavidge commented Dec 4, 2022 via email

@DanielGoldfarb
Copy link
Collaborator

@tdavidge
First, please be aware that there is a bug in GitHub when replying via email.
To post or format something to this discussion, please navigate directly to this issue page on the website and post here.
(Even though the email says that you can reply, it does not work correctly; for example, see here).

Regarding savefig, please try the following. It worked for me.

Do not use the savefig kwarg of mpf.plot(). Rather, call savefig() directly on the Figure object as in the following code, and use the bbox_inches='tight' kwarg of savefig:

fig, axlist = mpf.plot(df,type='candle',xrotation=0,style='yahoo',tight_layout=True,returnfig=True,figratio=(2,1))

axlist[-2].set_xticks(ticks)
axlist[-2].set_xticklabels(tlabs, ha='left')
axlist[-2].set_xticks(mitks,minor=True)
axlist[-2].set_xticklabels(milab, ha='center', minor=True, rotation=0)

fig.savefig( 'myplot.pdf', bbox_inches='tight' )

That should work OK. If not, please post the images from the two ways of saving (using the above code). But again, do not post via email since that doesn't work. Please post directly to the GitHub website. Thanks.

@tdavidge
Copy link
Author

tdavidge commented Dec 5, 2022

Hi Daniel, that worked great. Duly noted on the email reply, my apologies. Thanks again for all the assistance, I am now trying to port your code to some existing charts that I was struggling with that use a different dataframe format to be able to plot multiple series across a similar timeframe. Will let you know how I get on.

@DanielGoldfarb
Copy link
Collaborator

... that worked great

Glad to hear it.

I am now trying to port your code to some existing charts ...

Good luck. If you are willing, please share the charts once you get them looking the way you want. It's always exciting to see the things people are doing with mplfinance.

All the best. --Daniel

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants