Skip to content

Commit 5c15d4d

Browse files
Added Date field on x-axis
1 parent bf1a603 commit 5c15d4d

31 files changed

+5898
-0
lines changed

build/lib/mplfinance/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import mplfinance._mpf_warnings
2+
from mplfinance.plotting import plot, make_addplot
3+
from mplfinance._styles import make_mpf_style, make_marketcolors
4+
from mplfinance._styles import available_styles, write_style_file
5+
from mplfinance._version import __version__
6+
from mplfinance._mplwraps import figure, show
7+
from mplfinance._kwarg_help import kwarg_help

build/lib/mplfinance/_arg_validators.py

Lines changed: 445 additions & 0 deletions
Large diffs are not rendered by default.

build/lib/mplfinance/_helpers.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
"""
2+
Some helper functions for mplfinance.
3+
NOTE: This is the lowest level in mplfinance:
4+
This file should have NO dependencies on
5+
any other mplfinance files.
6+
"""
7+
8+
import datetime
9+
import matplotlib.dates as mdates
10+
import matplotlib.colors as mcolors
11+
import numpy as np
12+
13+
def _adjust_color_brightness(color,amount=0.5):
14+
15+
def _adjcb(c1, amount):
16+
import matplotlib.colors as mc
17+
import colorsys
18+
# mc.is_color_like(value)
19+
try:
20+
c = mc.cnames[c1]
21+
except:
22+
c = c1
23+
c = colorsys.rgb_to_hls(*mc.to_rgb(c))
24+
return colorsys.hls_to_rgb(c[0], max(0, min(1, amount * c[1])), c[2])
25+
26+
if not isinstance(color,(list,tuple)):
27+
return _adjcb(color,amount)
28+
29+
cout = []
30+
cadj = {}
31+
for c1 in color:
32+
if c1 in cadj:
33+
cout.append(cadj[c1])
34+
else:
35+
newc = _adjcb(c1,amount)
36+
cadj[c1] = newc
37+
cout.append(cadj[c1])
38+
return cout
39+
40+
41+
def _determine_format_string( dates, datetime_format=None ):
42+
"""
43+
Determine the datetime format string based on the averge number
44+
of days between data points, or if the user passed in kwarg
45+
datetime_format, use that as an override.
46+
"""
47+
avg_days_between_points = (dates[-1] - dates[0]) / float(len(dates))
48+
49+
if datetime_format is not None:
50+
return datetime_format
51+
52+
# avgerage of 3 or more data points per day we will call intraday data:
53+
if avg_days_between_points < 0.33: # intraday
54+
if mdates.num2date(dates[-1]).date() != mdates.num2date(dates[0]).date():
55+
# intraday data for more than one day:
56+
fmtstring = '%b %d, %H:%M'
57+
else: # intraday data for a single day
58+
fmtstring = '%H:%M'
59+
else: # 'daily' data (or could be weekly, etc.)
60+
if mdates.num2date(dates[-1]).date().year != mdates.num2date(dates[0]).date().year:
61+
fmtstring = '%Y-%b-%d'
62+
else:
63+
fmtstring = '%b %d'
64+
return fmtstring
65+
66+
67+
def _list_of_dict(x):
68+
'''
69+
Return True if x is a list of dict's
70+
'''
71+
return isinstance(x,list) and all([isinstance(item,dict) for item in x])
72+
73+
def _num_or_seq_of_num(value):
74+
return ( isinstance(value,(int,float,np.integer,np.floating)) or
75+
(isinstance(value,(list,tuple,np.ndarray)) and
76+
all([isinstance(v,(int,float,np.integer,np.floating)) for v in value]))
77+
)
78+
79+
def roundTime(dt=None, roundTo=60):
80+
"""Round a datetime object to any time lapse in seconds
81+
dt : datetime.datetime object, default now.
82+
roundTo : Closest number of seconds to round to, default 1 minute.
83+
Author: Thierry Husson 2012 - Use it as you want but don't blame me.
84+
"""
85+
if dt is None : dt = datetime.datetime.now()
86+
seconds = (dt.replace(tzinfo=None) - dt.min).seconds
87+
rounding = (seconds+roundTo/2) // roundTo * roundTo
88+
return dt + datetime.timedelta(0,rounding-seconds,-dt.microsecond)
89+
90+
91+
def _is_uint8_rgb_or_rgba(tup):
92+
""" Deterine if rgb or rgba is in (0-255) format:
93+
Matplotlib expects rgb (and rgba) tuples to contain
94+
three (or four) floats between 0.0 and 1.0
95+
96+
Some people express rgb as tuples of three integers
97+
between 0 and 255.
98+
(In rgba, alpha is still a float from 0.0 to 1.0)
99+
"""
100+
if isinstance(tup,str): return False
101+
if not np.iterable(tup): return False
102+
L = len(tup)
103+
if L < 3 or L > 4: return False
104+
if L == 4 and (tup[3] < 0 or tup[3] > 1): return False
105+
return not any([not isinstance(v,(int,np.unsignedinteger)) or v<0 or v>255 for v in tup[0:3]])
106+
107+
def _mpf_is_color_like(c):
108+
"""Determine if an object is a color.
109+
110+
Identical to `matplotlib.colors.is_color_like()`
111+
BUT ALSO considers int (0-255) rgb and rgba colors.
112+
"""
113+
if mcolors.is_color_like(c): return True
114+
return _is_uint8_rgb_or_rgba(c)
115+
116+
def _mpf_to_rgba(c, alpha=None):
117+
cnew = c
118+
if _is_uint8_rgb_or_rgba(c) and any(e>1 for e in c[:3]):
119+
cnew = tuple([e/255. for e in c[:3]])
120+
if len(c) == 4: cnew += c[3:]
121+
return mcolors.to_rgba(cnew, alpha)

build/lib/mplfinance/_kwarg_help.py

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import mplfinance as mpf
2+
import pandas as pd
3+
import textwrap
4+
5+
def df_wrapcols(df,wrap_columns=None):
6+
7+
if wrap_columns is None: return df
8+
if not isinstance(wrap_columns,dict):
9+
raise TypeError('wrap_columns must be a dict of column_names and wrap_lengths')
10+
11+
for col in wrap_columns:
12+
if col not in df.columns:
13+
raise ValueError('column "'+str(col)+'" not found in df.columns')
14+
15+
index = []
16+
column_data = {}
17+
for col in df.columns:
18+
column_data[col] = []
19+
20+
for ix in df.index:
21+
row = df.loc[ix,]
22+
23+
row_data = {}
24+
for col in row.index:
25+
cstr = str(row[col])
26+
if col in wrap_columns:
27+
wlen = wrap_columns[col]
28+
tw = textwrap.wrap(cstr,wlen) if not cstr.isspace() else [' ']
29+
else:
30+
tw = [cstr]
31+
row_data[col] = tw
32+
33+
cmax = max(row_data,key=lambda k: len(row_data[k]))
34+
rlen = len(row_data[cmax])
35+
for r in range(rlen):
36+
for col in row.index:
37+
extension = [' ']*(rlen - len(row_data[col]))
38+
row_data[col].extend(extension)
39+
column_data[col].append(row_data[col][r])
40+
ixstr = str(ix)+'.'+str(r) if r > 0 else str(ix)
41+
index.append(ixstr)
42+
43+
return pd.DataFrame(column_data,index=index)
44+
45+
def make_left_formatter(maxwidth):
46+
wm3 = maxwidth-3
47+
w = maxwidth
48+
def left_formatter(value):
49+
if not isinstance(value,str):
50+
return f'{value:<}'
51+
elif value[0:maxwidth] == '-'*maxwidth:
52+
return f'{value:<{w}.{w}s}'
53+
elif len(value) > maxwidth:
54+
return f'{value:<{wm3}.{wm3}s}...'
55+
else:
56+
return f'{value:<{w}.{w}s}'
57+
return left_formatter
58+
59+
60+
def kwarg_help( func_name=None, kwarg_names=None, sort=False ):
61+
62+
func_kwarg_map = {
63+
'plot' : mpf.plotting._valid_plot_kwargs,
64+
'make_addplot' : mpf.plotting._valid_addplot_kwargs,
65+
'make_marketcolors' : mpf._styles._valid_make_marketcolors_kwargs,
66+
'make_mpf_style' : mpf._styles._valid_make_mpf_style_kwargs,
67+
'renko_params' : mpf._utils._valid_renko_kwargs,
68+
'pnf_params' : mpf._utils._valid_pnf_kwargs,
69+
'lines' : mpf._utils._valid_lines_kwargs,
70+
'scale_width_adjustment': mpf._widths._valid_scale_width_kwargs,
71+
'update_width_config': mpf._widths._valid_update_width_kwargs,
72+
}
73+
74+
func_kwarg_aliases = {
75+
'addplot' : mpf.plotting._valid_addplot_kwargs,
76+
'marketcolors' : mpf._styles._valid_make_marketcolors_kwargs,
77+
'mpf_style' : mpf._styles._valid_make_mpf_style_kwargs,
78+
'style' : mpf._styles._valid_make_mpf_style_kwargs,
79+
'renko' : mpf._utils._valid_renko_kwargs,
80+
'pnf' : mpf._utils._valid_pnf_kwargs,
81+
'hlines' : mpf._utils._valid_lines_kwargs,
82+
'alines' : mpf._utils._valid_lines_kwargs,
83+
'tlines' : mpf._utils._valid_lines_kwargs,
84+
'vlines' : mpf._utils._valid_lines_kwargs,
85+
}
86+
87+
if func_name is None:
88+
print('\nUsage: `kwarg_help(func_name)` or `kwarg_help(func_name,kwarg_names)`')
89+
print(' kwarg_help is available for the following func_names:')
90+
s = str(list(func_kwarg_map.keys()))
91+
text = textwrap.wrap(s,68)
92+
for t in text:
93+
print(' ',t)
94+
print()
95+
return
96+
97+
fkmap = {**func_kwarg_map, **func_kwarg_aliases}
98+
99+
if func_name not in fkmap:
100+
raise ValueError('Function name "'+func_name+'" NOT a valid function name')
101+
102+
if kwarg_names is not None and isinstance(kwarg_names,str):
103+
kwarg_names = [ kwarg_names, ]
104+
105+
if ( kwarg_names is not None
106+
and (not isinstance(kwarg_names,(list,tuple))
107+
or not all([isinstance(k,str) for k in kwarg_names])
108+
)
109+
):
110+
raise ValueError('kwarg_names must be a sequence (list,tuple) of strings')
111+
112+
vks = fkmap[func_name]()
113+
114+
df = (pd.DataFrame(vks).T).drop('Validator',axis=1)
115+
df.index.name = 'Kwarg'
116+
if sort: df.sort_index(inplace=True)
117+
df.reset_index(inplace=True)
118+
119+
if kwarg_names is not None:
120+
for k in kwarg_names:
121+
if k not in df['Kwarg'].values:
122+
print(' Warning: "'+k+'" is not a valid `kwarg_name` for `func_name` "'+func_name,'"')
123+
df = df[ df['Kwarg'].isin(kwarg_names) ]
124+
if len(df) < 1:
125+
raise ValueError(' None of specified `kwarg_names` are valid for `func_name` "'+func_name,'"')
126+
127+
df['Default'] = ["'"+d+"'" if isinstance(d,str) else str(d) for d in df['Default']]
128+
129+
klen = df['Kwarg'].str.len().max()+1
130+
dlen = df['Default'].str.len().max()+1
131+
132+
wraplen = max( 40, 80-(klen+dlen) )
133+
df = df_wrapcols(df,wrap_columns={'Description':wraplen})
134+
135+
dividers = []
136+
for col in df.columns:
137+
dividers.append('-'*int(df[col].str.len().max()))
138+
dfd = pd.DataFrame(dividers).T
139+
dfd.columns = df.columns
140+
dfd.index = pd.Index(['---'])
141+
142+
df = dfd.append(df)
143+
144+
formatters = { 'Kwarg' : make_left_formatter( klen ),
145+
'Default' : make_left_formatter( dlen ),
146+
'Description' : make_left_formatter( wraplen ),
147+
}
148+
149+
print('\n ','-'*78)
150+
print(' Kwargs for func_name "'+func_name+'":')
151+
152+
s = df.to_string(formatters=formatters,index=False,justify='left')
153+
154+
print('\n ',s.replace('\n','\n '))

build/lib/mplfinance/_mpf_warnings.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import sys as __sys
2+
if not __sys.warnoptions:
3+
import os as __os
4+
import warnings as __warnings
5+
__warnings.filterwarnings("default",category=DeprecationWarning,module='mplfinance') # Change the filter in this process
6+
__os.environ["PYTHONWARNINGS"] = "default::DeprecationWarning:mplfinance" # Also affect subprocesses
7+
8+
if __sys.version_info <= (3, 6):
9+
__warnings.filterwarnings("default",category=ImportWarning,module='mplfinance') # Change the filter in this process
10+
__os.environ["PYTHONWARNINGS"] = "default::ImportWarning:mplfinance" # Also affect subprocesses
11+
__warnings.warn('\n\n ================================================================= '+
12+
'\n\n WARNING: `mplfinance` is NOT supported for Python versions '+
13+
'\n less than 3.6'
14+
'\n\n ================================================================= ',
15+
category=ImportWarning)
16+

build/lib/mplfinance/_mplrcputils.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env python
2+
"""
3+
rcparams utilities
4+
"""
5+
6+
import pandas as pd
7+
import matplotlib.pyplot as plt
8+
import sys
9+
10+
__author__ = "Daniel Goldfarb"
11+
__version__ = "0.1.0"
12+
__license__ = "MIT"
13+
14+
def rcParams_to_df(rcp,name=None):
15+
keys = []
16+
vals = []
17+
for item in rcp:
18+
keys.append(item)
19+
vals.append(rcp[item])
20+
df = pd.DataFrame(vals,index=pd.Index(keys,name='rcParamsKey'))
21+
if name is not None:
22+
df.columns = [name]
23+
else:
24+
df.columns = ['Value']
25+
return df
26+
27+
def compare_styles(s1,s2):
28+
with plt.rc_context():
29+
plt.style.use('default')
30+
plt.style.use(s1)
31+
df1 = rcParams_to_df(plt.rcParams,name=s1)
32+
33+
with plt.rc_context():
34+
plt.style.use('default')
35+
plt.style.use(s2)
36+
df2 = rcParams_to_df(plt.rcParams,name=s2)
37+
38+
df = pd.concat([df1,df2],axis=1)
39+
dif = df[df[s1] != df[s2]].dropna(how='all')
40+
return (dif,df,df1,df2)
41+
42+
def main():
43+
""" Main entry point of the app """
44+
def usage():
45+
print('\n Usage: rcparams <command> <arguments> \n')
46+
print(' Available commands: ')
47+
print(' rcparams find <findstring>')
48+
print(' rcparams compare <style1> <style2>')
49+
print('')
50+
exit(1)
51+
commands = ('find','compare')
52+
53+
if len(sys.argv) < 3 :
54+
print('\n Too few arguments!')
55+
usage()
56+
57+
command = sys.argv[1]
58+
if command not in commands:
59+
print('\n Unrecognized command \"'+command+'\"')
60+
usage()
61+
62+
if command == 'find':
63+
findstr = sys.argv[2]
64+
df = rcParams_to_df(plt.rcParams)
65+
if findstr == '--all':
66+
for key in df.index:
67+
print(key+':',df.loc[key,'Value'])
68+
else:
69+
print(df[df.index.str.contains(findstr)])
70+
71+
elif command == 'compare':
72+
if len(sys.argv) < 4 :
73+
print('\n Need two styles to compare!')
74+
usage()
75+
style1 = sys.argv[2]
76+
style2 = sys.argv[3]
77+
dif,df,df1,df2 = compare_styles(style1,style2)
78+
print('\n==== dif ====\n',dif)
79+
80+
else:
81+
print('\n Unrecognized command \"'+command+'\"')
82+
usage()
83+
84+
85+
if __name__ == "__main__":
86+
""" This is executed when run from the command line """
87+
main()

0 commit comments

Comments
 (0)