From 59b42e8efddaa919e9782e97092dc99dab1df2f7 Mon Sep 17 00:00:00 2001 From: nijek Date: Sun, 9 Oct 2022 09:47:58 -0300 Subject: [PATCH 1/3] mpf now accepts alpha parameter as list for [hvat]lines so it is possible to set a different alpha for each line. --- src/mplfinance/_utils.py | 195 ++++++++++++++++++++------------------- 1 file changed, 99 insertions(+), 96 deletions(-) diff --git a/src/mplfinance/_utils.py b/src/mplfinance/_utils.py index 447a52ea..5d555ffa 100644 --- a/src/mplfinance/_utils.py +++ b/src/mplfinance/_utils.py @@ -9,7 +9,7 @@ from itertools import cycle -from matplotlib import colors as mcolors +from matplotlib import colors as mcolors, pyplot as plt from matplotlib.patches import Ellipse from matplotlib.collections import LineCollection, PolyCollection, PatchCollection @@ -83,7 +83,7 @@ def _check_and_convert_xlim_configuration(data, config): xlim = [ _date_to_mdate(dt) for dt in xlim] else: xlim = [ _date_to_iloc_extrapolate(data.index.to_series(),dt) for dt in xlim] - + return xlim @@ -109,7 +109,7 @@ def _construct_mpf_collections(ptype,dates,xdates,opens,highs,lows,closes,volume dates, highs, lows, volumes, config['pnf_params'], closes, marketcolors=style['marketcolors']) else: raise TypeError('Unknown ptype="',str(ptype),'"') - + return collections @@ -140,7 +140,7 @@ def combine_adjacent(arr): Returns ------- output: new summed array - indexes: indexes indicating the first + indexes: indexes indicating the first element summed for each group in arr """ output, indexes = [], [] @@ -153,7 +153,7 @@ def combine_adjacent(arr): output.append(sum(arr[:index])) indexes.append(curr_i) curr_i += index - + for _ in range(index): arr.pop(0) return output, indexes @@ -190,7 +190,7 @@ def _updown_colors(upcolor,downcolor,opens,closes,use_prev_close=False): if not use_prev_close: return [ cmap[opn < cls] for opn,cls in zip(opens,closes) ] else: - first = cmap[opens[0] < closes[0]] + first = cmap[opens[0] < closes[0]] _list = [ cmap[pre < cls] for cls,pre in zip(closes[1:], closes) ] return [first] + _list @@ -208,8 +208,8 @@ def _make_updown_color_list(key,marketcolors,opens,closes,overrides=None): ups[ix] = mco[key][ 'up' ] downs[ix] = mco[key]['down'] return [ups[ix] if opens[ix] < closes[ix] else downs[ix] for ix in range(length)] - - + + def _updownhollow_colors(upcolor,downcolor,hollowcolor,opens,closes): if upcolor == downcolor: return upcolor @@ -221,7 +221,7 @@ def _updownhollow_colors(upcolor,downcolor,hollowcolor,opens,closes): def _date_to_iloc(dtseries,date): - '''Convert a `date` to a location, given a date series w/a datetime index. + '''Convert a `date` to a location, given a date series w/a datetime index. If `date` does not exactly match a date in the series then interpolate between two dates. If `date` is outside the range of dates in the series, then raise an exception . @@ -258,7 +258,7 @@ def _date_to_iloc_linear(dtseries,date,trace=False): i1 = 0.0 i2 = len(dtseries) - 1.0 if trace: print('i1,i2=',i1,i2) - + slope = (i2 - i1) / (d2 - d1) yitrcpt1 = i1 - (slope*d1) if trace: print('slope,yitrcpt=',slope,yitrcpt1) @@ -268,7 +268,7 @@ def _date_to_iloc_linear(dtseries,date,trace=False): print('WARNING: yintercepts NOT equal!!!(',yitrcpt1,yitrcpt2,')') yitrcpt = (yitrcpt1 + yitrcpt2) / 2.0 else: - yitrcpt = yitrcpt1 + yitrcpt = yitrcpt1 return (slope * _date_to_mdate(date)) + yitrcpt def _date_to_iloc_5_7ths(dtseries,date,direction,trace=False): @@ -288,14 +288,14 @@ def _date_to_iloc_5_7ths(dtseries,date,direction,trace=False): return loc_5_7ths def _date_to_iloc_extrapolate(dtseries,date): - '''Convert a `date` to a location, given a date series w/a datetime index. + '''Convert a `date` to a location, given a date series w/a datetime index. If `date` does not exactly match a date in the series then interpolate between two dates. If `date` is outside the range of dates in the series, then extrapolate: Extrapolation results in increased error as the distance of the extrapolation increases. We have two methods to extrapolate: (1) Determine a linear equation based on the data provided in `dtseries`, and use that equation to calculate the location for the date. - (2) Multiply by 5/7 the number of days between the edge date of dtseries and the + (2) Multiply by 5/7 the number of days between the edge date of dtseries and the date for which we are requesting a location. THIS ASSUMES DAILY data AND a 5 DAY TRADING WEEK. Empirical observation (scratch_pad/date_to_iloc_extrapolation.ipynb) shows that @@ -348,7 +348,7 @@ def _date_to_mdate(date): def _convert_segment_dates(segments,dtindex): ''' - Convert line segment dates to matplotlib dates + Convert line segment dates to matplotlib dates Inputted segment dates may be: pandas-parseable date-time string, pandas timestamp, or a python datetime or date, or (if dtindex is not None) integer index A "segment" is a "sequence of lines", @@ -363,7 +363,7 @@ def _convert_segment_dates(segments,dtindex): new_line = [] for dt,value in line: if dtindex is not None: - date = _date_to_iloc(dtseries,dt) + date = _date_to_iloc(dtseries,dt) else: date = _date_to_mdate(dt) if date is None: @@ -374,14 +374,14 @@ def _convert_segment_dates(segments,dtindex): def _valid_renko_kwargs(): ''' - Construct and return the "valid renko kwargs table" for the mplfinance.plot(type='renko') - function. A valid kwargs table is a `dict` of `dict`s. The keys of the outer dict are - the valid key-words for the function. The value for each key is a dict containing 3 + Construct and return the "valid renko kwargs table" for the mplfinance.plot(type='renko') + function. A valid kwargs table is a `dict` of `dict`s. The keys of the outer dict are + the valid key-words for the function. The value for each key is a dict containing 3 specific keys: "Default", "Description" and "Validator" with the following values: "Default" - The default value for the kwarg if none is specified. "Description" - The description for the kwarg. "Validator" - A function that takes the caller specified value for the kwarg, - and validates that it is the correct type, and (for kwargs with + and validates that it is the correct type, and (for kwargs with a limited set of allowed values) may also validate that the kwarg value is one of the allowed values. ''' @@ -404,14 +404,14 @@ def _valid_renko_kwargs(): def _valid_pnf_kwargs(): ''' - Construct and return the "valid pnf kwargs table" for the mplfinance.plot(type='pnf') - function. A valid kwargs table is a `dict` of `dict`s. The keys of the outer dict are - the valid key-words for the function. The value for each key is a dict containing 3 + Construct and return the "valid pnf kwargs table" for the mplfinance.plot(type='pnf') + function. A valid kwargs table is a `dict` of `dict`s. The keys of the outer dict are + the valid key-words for the function. The value for each key is a dict containing 3 specific keys: "Default", "Description" and "Validator" with the following values: "Default" - The default value for the kwarg if none is specified. "Description" - The description for the kwarg. "Validator" - A function that takes the caller specified value for the kwarg, - and validates that it is the correct type, and (for kwargs with + and validates that it is the correct type, and (for kwargs with a limited set of allowed values) may also validate that the kwarg value is one of the allowed values. ''' @@ -438,16 +438,17 @@ def _valid_pnf_kwargs(): def _valid_lines_kwargs(): + ''' - Construct and return the "valid lines (hlines,vlines,alines,tlines) kwargs table" + Construct and return the "valid lines (hlines,vlines,alines,tlines) kwargs table" for the mplfinance.plot() `[h|v|a|t]lines=` kwarg functions. - A valid kwargs table is a `dict` of `dict`s. The keys of the outer dict are - the valid key-words for the function. The value for each key is a dict containing 3 + A valid kwargs table is a `dict` of `dict`s. The keys of the outer dict are + the valid key-words for the function. The value for each key is a dict containing 3 specific keys: "Default", "Description" and "Validator" with the following values: "Default" - The default value for the kwarg if none is specified. "Description" - The description for the kwarg. "Validator" - A function that takes the caller specified value for the kwarg, - and validates that it is the correct type, and (for kwargs with + and validates that it is the correct type, and (for kwargs with a limited set of allowed values) may also validate that the kwarg value is one of the allowed values. ''' @@ -496,7 +497,7 @@ def _valid_lines_kwargs(): 'Description' : 'line style of [hvat]lines (or sequence of line styles, if each line to have a different linestyle)', 'Validator' : lambda value: value is None or value in valid_linestyles or all([v in valid_linestyles for v in value]) }, - + 'linewidths': { 'Default' : None, 'Description' : 'line width of [hvat]lines (or sequence of line widths, if each line to have a different width)', 'Validator' : lambda value: value is None @@ -504,9 +505,11 @@ def _valid_lines_kwargs(): or all([isinstance(v,(float,int)) for v in value]) }, 'alpha' : { 'Default' : 1.0, - 'Description' : 'Opacity of [hvat]lines. float from 0.0 to 1.0 '+ - ' (1.0 means fully opaque; 0.0 means transparent.', - 'Validator' : lambda value: isinstance(value,(float,int)) }, + 'Description' : 'Opacity of [hvat]lines (or sequence of opacities,' + + 'if each line is to have a different opacity)' + + 'float from 0.0 to 1.0 '+' (1.0 means fully opaque; 0.0 means transparent.', + 'Validator' : lambda value: isinstance(value,(float,int)) + or all([isinstance(v, (float, int)) for v in value]) }, 'tline_use' : { 'Default' : 'close', @@ -549,7 +552,7 @@ def _construct_ohlc_collections(dates, opens, highs, lows, closes, marketcolors= Returns ------- - ret : list + ret : list a list or tuple of matplotlib collections to be added to the axes """ @@ -629,7 +632,7 @@ def _construct_candlestick_collections(dates, opens, highs, lows, closes, market ret : list (lineCollection, barCollection) """ - + _check_input(opens, highs, lows, closes) if marketcolors is None: @@ -649,10 +652,10 @@ def _construct_candlestick_collections(dates, opens, highs, lows, closes, market rangeSegLow = [((date, low), (date, min(open,close))) for date, low, open, close in zip(dates, lows, opens, closes)] - + rangeSegHigh = [((date, high), (date, max(open,close))) for date, high, open, close in zip(dates, highs, opens, closes)] - + rangeSegments = rangeSegLow + rangeSegHigh alpha = marketcolors['alpha'] @@ -685,7 +688,7 @@ def _construct_candlestick_collections(dates, opens, highs, lows, closes, market def _construct_hollow_candlestick_collections(dates, opens, highs, lows, closes, marketcolors=None, config=None): """Represent today's open to close as a "bar" line (candle body) and high low range as a vertical line (candle wick) - + If config['type']=='hollow_and_filled' (hollow and filled candles) then candle edge and wick color depend on PREVIOUS close to today's close (up or down), and the center of the candle body (hollow or filled) depends on the today's open to close (up or down). @@ -712,7 +715,7 @@ def _construct_hollow_candlestick_collections(dates, opens, highs, lows, closes, ret : list (lineCollection, barCollection) """ - + _check_input(opens, highs, lows, closes) if marketcolors is None: @@ -732,23 +735,23 @@ def _construct_hollow_candlestick_collections(dates, opens, highs, lows, closes, rangeSegLow = [((date, low), (date, min(open,close))) for date, low, open, close in zip(dates, lows, opens, closes)] - + rangeSegHigh = [((date, high), (date, max(open,close))) for date, high, open, close in zip(dates, highs, opens, closes)] - + rangeSegments = rangeSegLow + rangeSegHigh alpha = marketcolors['alpha'] uc = mcolors.to_rgba(marketcolors['candle'][ 'up' ], alpha) dc = mcolors.to_rgba(marketcolors['candle']['down'], alpha) - + hc = mcolors.to_rgba(marketcolors['hollow']) if 'hollow' in marketcolors else (0,0,0,0) - + colors = _updownhollow_colors(uc, dc, hc, opens, closes) # for candle body. edgecolor = _updown_colors(uc, dc, opens, closes, use_prev_close=True) - + wickcolor = _updown_colors(uc, dc, opens, closes, use_prev_close=True) # For hollow candles, we scale the candle linewidth up a little: @@ -778,19 +781,19 @@ def _construct_renko_collections(dates, highs, lows, volumes, config_renko_param --------------------- In the first part of the algorithm, we populate the cdiff array along with adjusting the dates and volumes arrays into the new_dates and - new_volumes arrays. A single date includes a range from no bricks to many - bricks, if a date has no bricks it shall not be included in new_dates, - and if it has n bricks then it will be included n times. Volumes use a + new_volumes arrays. A single date includes a range from no bricks to many + bricks, if a date has no bricks it shall not be included in new_dates, + and if it has n bricks then it will be included n times. Volumes use a volume cache to save volume amounts for dates that do not have any bricks before adding the cache to the next date that has at least one brick. - We populate the cdiff array with each close values difference from the + We populate the cdiff array with each close values difference from the previously created brick divided by the brick size. In the second part of the algorithm, we iterate through the values in cdiff - and add 1s or -1s to the bricks array depending on whether the value is + and add 1s or -1s to the bricks array depending on whether the value is positive or negative. Every time there is a trend change (ex. previous brick is an upbrick, current brick is a down brick) we draw one less brick to account - for the price having to move the previous bricks amount before creating a + for the price having to move the previous bricks amount before creating a brick in the opposite direction. In the final part of the algorithm, we enumerate through the bricks array and @@ -801,7 +804,7 @@ def _construct_renko_collections(dates, highs, lows, volumes, config_renko_param Useful sources: https://avilpage.com/2018/01/how-to-plot-renko-charts-with-python.html https://school.stockcharts.com/doku.php?id=chart_analysis:renko - + Parameters ---------- dates : sequence @@ -825,10 +828,10 @@ def _construct_renko_collections(dates, highs, lows, volumes, config_renko_param renko_params = _process_kwargs(config_renko_params, _valid_renko_kwargs()) if marketcolors is None: marketcolors = _get_mpfstyle('classic')['marketcolors'] - + brick_size = renko_params['brick_size'] atr_length = renko_params['atr_length'] - + if brick_size == 'atr': if atr_length == 'total': @@ -849,7 +852,7 @@ def _construct_renko_collections(dates, highs, lows, volumes, config_renko_param dc = mcolors.to_rgba(marketcolors['candle']['down'], alpha) euc = mcolors.to_rgba(marketcolors['edge'][ 'up' ], 1.0) edc = mcolors.to_rgba(marketcolors['edge']['down'], 1.0) - + cdiff = [] # holds the differences between each close and the previously created brick / the brick size prev_close_brick = closes[0] volume_cache = 0 # holds the volumes for the dates that were skipped @@ -876,7 +879,7 @@ def _construct_renko_collections(dates, highs, lows, volumes, config_renko_param last_diff_sign = 0 # direction the bricks were last going in -1 -> down, 1 -> up dates_volumes_index = 0 # keeps track of the index of the current date/volume for diff in cdiff: - + curr_diff_sign = diff/abs(diff) if last_diff_sign != 0 and curr_diff_sign != last_diff_sign: last_diff_sign = curr_diff_sign @@ -889,7 +892,7 @@ def _construct_renko_collections(dates, highs, lows, volumes, config_renko_param new_volumes.pop(dates_volumes_index) continue last_diff_sign = curr_diff_sign - + if diff > 0: bricks.extend([1]*abs(diff)) else: @@ -911,7 +914,7 @@ def _construct_renko_collections(dates, highs, lows, volumes, config_renko_param curr_price += (brick_size * number) brick_values.append(curr_price) - + x, y = index, curr_price verts.append(( @@ -943,41 +946,41 @@ def _construct_pointnfig_collections(dates, highs, lows, volumes, config_pointnf --------------------- In the first part of the algorithm, we populate the boxes array along with adjusting the dates and volumes arrays into the new_dates and - new_volumes arrays. A single date includes a range from no boxes to many - boxes, if a date has no boxes it shall not be included in new_dates, - and if it has n boxes then it will be included n times. Volumes use a + new_volumes arrays. A single date includes a range from no boxes to many + boxes, if a date has no boxes it shall not be included in new_dates, + and if it has n boxes then it will be included n times. Volumes use a volume cache to save volume amounts for dates that do not have any boxes before adding the cache to the next date that has at least one box. - We populate the boxes array with each close values difference from the + We populate the boxes array with each close values difference from the previously created brick divided by the box size. The second part of the algorithm has a series of step. First we combine the adjacent like signed values in the boxes array (ex. [-1, -2, 3, -4] -> [-3, 3, -4]). - Next we subtract 1 from the absolute value of each element in boxes except the + Next we subtract 1 from the absolute value of each element in boxes except the first to ensure every time there is a trend change (ex. previous box is - an X, current brick is a O) we draw one less box to account for the price - having to move the previous box's amount before creating a box in the - opposite direction. During this same step we also combine like signed elements - and associated volume/date data ignoring any zero values that are created by - subtracting 1 from the box value. Next we recreate the box array utilizing a - rolling_change and volume_cache to store and sum the changes that don't break + an X, current brick is a O) we draw one less box to account for the price + having to move the previous box's amount before creating a box in the + opposite direction. During this same step we also combine like signed elements + and associated volume/date data ignoring any zero values that are created by + subtracting 1 from the box value. Next we recreate the box array utilizing a + rolling_change and volume_cache to store and sum the changes that don't break the reversal threshold. Lastly, we enumerate through the boxes to populate the line_seg and circle_patches - arrays. line_seg holds the / and \ line segments that make up an X and + arrays. line_seg holds the / and \ line segments that make up an X and circle_patches holds matplotlib.patches Ellipse objects for each O. We start - by filling an x and y array each iteration which contain the x and y + by filling an x and y array each iteration which contain the x and y coordinates for each box in the column. Then for each coordinate pair in - x, y we add to either the line_seg array or the circle_patches array - depending on the value of sign for the current column (1 indicates - line_seg, -1 indicates circle_patches). The height of the boxes take - into account padding which separates each box by a small margin in + x, y we add to either the line_seg array or the circle_patches array + depending on the value of sign for the current column (1 indicates + line_seg, -1 indicates circle_patches). The height of the boxes take + into account padding which separates each box by a small margin in order to increase readability. Useful sources: https://stackoverflow.com/questions/8750648/point-and-figure-chart-with-matplotlib https://www.investopedia.com/articles/technical/03/081303.asp - + Parameters ---------- dates : sequence @@ -1001,7 +1004,7 @@ def _construct_pointnfig_collections(dates, highs, lows, volumes, config_pointnf pointnfig_params = _process_kwargs(config_pointnfig_params, _valid_pnf_kwargs()) if marketcolors is None: marketcolors = _get_mpfstyle('classic')['marketcolors'] - + box_size = pointnfig_params['box_size'] atr_length = pointnfig_params['atr_length'] reversal = pointnfig_params['reversal'] @@ -1021,7 +1024,7 @@ def _construct_pointnfig_collections(dates, highs, lows, volumes, config_pointnf if reversal < 1 or reversal > 9: raise ValueError("Specified reversal must be an integer in the range [1,9]") - + alpha = marketcolors['alpha'] uc = mcolors.to_rgba(marketcolors['ohlc'][ 'up' ], alpha) @@ -1032,7 +1035,7 @@ def _construct_pointnfig_collections(dates, highs, lows, volumes, config_pointnf prev_close_box = closes[0] # represents the value of the last box in the previous column volume_cache = 0 # holds the volumes for the dates that were skipped temp_volumes, temp_dates = [], [] # holds the temp adjusted volumes and dates respectively - + for i in range(len(closes)-1): box_diff = int((closes[i+1] - prev_close_box) / box_size) if box_diff == 0: @@ -1050,7 +1053,7 @@ def _construct_pointnfig_collections(dates, highs, lows, volumes, config_pointnf # combine adjacent similarly signed differences boxes, indexes = combine_adjacent(boxes) new_volumes, new_dates = coalesce_volume_dates(temp_volumes, temp_dates, indexes) - + adjusted_boxes = [boxes[0]] temp_volumes, temp_dates = [new_volumes[0]], [new_dates[0]] volume_cache = 0 @@ -1086,7 +1089,7 @@ def _construct_pointnfig_collections(dates, highs, lows, volumes, config_pointnf boxes = [adjusted_boxes[0]] new_volumes = [temp_volumes[0]] new_dates = [temp_dates[0]] - + rolling_change = 0 volume_cache = 0 biggest_difference = 0 # only used for the last column @@ -1094,11 +1097,11 @@ def _construct_pointnfig_collections(dates, highs, lows, volumes, config_pointnf #Clean data to account for reversal size (added to allow overriding the default reversal of 1) for i in range(1, len(adjusted_boxes)): - # Add to rolling_change and volume_cache which stores the box and volume values + # Add to rolling_change and volume_cache which stores the box and volume values rolling_change += adjusted_boxes[i] volume_cache += temp_volumes[i] - # if rolling_change is the same sign as the previous box and the abs value is bigger than the + # if rolling_change is the same sign as the previous box and the abs value is bigger than the # abs value of biggest_difference then we should replace biggest_difference with rolling_change if rolling_change*boxes[-1] > 0 and abs(rolling_change) > abs(biggest_difference): biggest_difference = rolling_change @@ -1116,14 +1119,14 @@ def _construct_pointnfig_collections(dates, highs, lows, volumes, config_pointnf boxes.append(rolling_change) new_volumes.append(volume_cache) new_dates.append(temp_dates[i]) - + # reset rolling_change and volume_cache once we've used them rolling_change = 0 volume_cache = 0 - + # reset biggest_difference as we start from the beginning every time there is a reversal biggest_difference = 0 - + # Adjust the last box column if the left over rolling_change is the same sign as the column boxes[-1] += biggest_difference new_volumes[-1] += volume_cache @@ -1138,33 +1141,33 @@ def _construct_pointnfig_collections(dates, highs, lows, volumes, config_pointnf sign = (difference / abs(difference)) # -1 or 1 start_iteration = 0 if sign > 0 else 1 - + x = [index] * (diff) y = [curr_price + (i * box_size * sign) for i in range(start_iteration, diff+start_iteration)] curr_price += (box_size * sign * (diff)) box_values.append( y ) - + for i in range(len(x)): # x and y have the same length height = box_size * 0.85 width = 0.6 if height < 0.5: width = height - + padding = (box_size * 0.075) if sign == 1: # X line_seg.append([(x[i]-width/2, y[i] + padding), (x[i]+width/2, y[i]+height + padding)]) # create / part of the X line_seg.append([(x[i]-width/2, y[i]+height+padding), (x[i]+width/2, y[i]+padding)]) # create \ part of the X else: # O circle_patches.append(Ellipse((x[i], y[i]-(height/2) - padding), width, height)) - + useAA = 0, # use tuple here - lw = 0.5 + lw = 0.5 cirCollection = PatchCollection(circle_patches) cirCollection.set_facecolor([tfc] * len(circle_patches)) cirCollection.set_edgecolor([dc] * len(circle_patches)) - + xCollection = LineCollection(line_seg, colors=[uc] * len(line_seg), linewidths=lw, @@ -1182,7 +1185,7 @@ def _construct_aline_collections(alines, dtix=None): ---------- alines : sequence sequences of segments, which are sequences of lines, - which are sequences of two or more points ( date[time], price ) or (x,y) + which are sequences of two or more points ( date[time], price ) or (x,y) date[time] may be (a) pandas.to_datetime parseable string, (b) pandas Timestamp, or @@ -1269,7 +1272,7 @@ def _construct_hline_collections(hlines,minx,maxx): #print('hconfig=',hconfig) #print('hlines=',hlines) - + lines = [] if not isinstance(hlines,(list,tuple)): hlines = [hlines,] # may be a single price value @@ -1332,7 +1335,7 @@ def _construct_vline_collections(vlines,dtix,miny,maxy): #print('vconfig=',vconfig) #print('vlines=',vlines) - + if not isinstance(vlines,(list,tuple)): vlines = [vlines,] @@ -1416,7 +1419,7 @@ def _tline_lsq(dfslice,tline_use): https://mmas.github.io/least-squares-fitting-numpy-scipy ''' si = dfslice[tline_use].mean(axis=1) - s = si.dropna() + s = si.dropna() if len(s) < 2: err = 'NOT enough data for Least Squares' if (len(si) > 2): @@ -1453,7 +1456,7 @@ def _tline_lsq(dfslice,tline_use): alines.append((p1,p2)) del tconfig['alines'] - alines = dict(alines=alines,**tconfig) + alines = dict(alines=alines,**tconfig) alines['tlines'] = None return _construct_aline_collections(alines, dtix) @@ -1471,7 +1474,7 @@ class IntegerIndexDateTimeFormatter(Formatter): you would otherwise plot on that axis. Construct this formatter by providing the arrange of datetimes (as matplotlib floats). When the formatter receives an integer in the range, it will look up the - datetime and format it. + datetime and format it. """ def __init__(self, dates, fmt='%b %d, %H:%M'): @@ -1485,7 +1488,7 @@ def __call__(self, x, pos=0): # not sure what 'pos' is for: see # https://matplotlib.org/gallery/ticks_and_spines/date_index_formatter.html ix = int(np.round(x)) - + if ix >= self.len or ix < 0: date = None dateformat = '' From c09265f2f375e2d584c565ab09557e7bec2c0dd5 Mon Sep 17 00:00:00 2001 From: nijek Date: Sun, 9 Oct 2022 10:25:39 -0300 Subject: [PATCH 2/3] mpf now accepts alpha parameter as list for [hvat]lines so it is possible to set a different alpha for each line. I also imported matplotlib.pyplot as plt to solve a unresolved reference on function _mscatter(x,y,ax=None, m=None, **kw) --- src/mplfinance/_utils.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/mplfinance/_utils.py b/src/mplfinance/_utils.py index 5d555ffa..a4f80dfc 100644 --- a/src/mplfinance/_utils.py +++ b/src/mplfinance/_utils.py @@ -438,7 +438,6 @@ def _valid_pnf_kwargs(): def _valid_lines_kwargs(): - ''' Construct and return the "valid lines (hlines,vlines,alines,tlines) kwargs table" for the mplfinance.plot() `[h|v|a|t]lines=` kwarg functions. @@ -504,12 +503,12 @@ def _valid_lines_kwargs(): or isinstance(value,(float,int)) or all([isinstance(v,(float,int)) for v in value]) }, - 'alpha' : { 'Default' : 1.0, - 'Description' : 'Opacity of [hvat]lines (or sequence of opacities,' - + 'if each line is to have a different opacity)' - + 'float from 0.0 to 1.0 '+' (1.0 means fully opaque; 0.0 means transparent.', - 'Validator' : lambda value: isinstance(value,(float,int)) - or all([isinstance(v, (float, int)) for v in value]) }, + 'alpha': {'Default': 1.0, + 'Description': 'Opacity of [hvat]lines (or sequence of opacities,' + + 'if each line is to have a different opacity)' + + 'float from 0.0 to 1.0 ' + ' (1.0 means fully opaque; 0.0 means transparent.', + 'Validator': lambda value: isinstance(value, (float, int)) + or all([isinstance(v, (float, int)) for v in value])}, 'tline_use' : { 'Default' : 'close', From 75b41f65a06fa6f87e44eb57166c04fb0bd4d80b Mon Sep 17 00:00:00 2001 From: Daniel Goldfarb Date: Thu, 13 Oct 2022 17:11:02 -0400 Subject: [PATCH 3/3] Update _version.py --- src/mplfinance/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mplfinance/_version.py b/src/mplfinance/_version.py index 9bca395f..44439d7d 100644 --- a/src/mplfinance/_version.py +++ b/src/mplfinance/_version.py @@ -1,4 +1,4 @@ -version_info = (0, 12, 9, 'beta', 1) +version_info = (0, 12, 9, 'beta', 2) _specifier_ = {'alpha': 'a','beta': 'b','candidate': 'rc','final': ''}