|
8 | 8 | import datetime
|
9 | 9 |
|
10 | 10 | from matplotlib import colors as mcolors
|
11 |
| -from matplotlib.collections import LineCollection, PolyCollection |
| 11 | +from matplotlib.patches import Ellipse |
| 12 | +from matplotlib.collections import LineCollection, PolyCollection, CircleCollection, PatchCollection |
12 | 13 | from mplfinance._arg_validators import _process_kwargs, _validate_vkwargs_dict
|
13 | 14 |
|
14 | 15 | from six.moves import zip
|
@@ -341,9 +342,8 @@ def _construct_renko_collections(dates, highs, lows, volumes, config_renko_param
|
341 | 342 | lows : sequence
|
342 | 343 | sequence of low values
|
343 | 344 | renko_params : dictionary
|
344 |
| - type : type of renko chart |
345 | 345 | brick_size : size of each brick
|
346 |
| - atr_legnth : length of time used for calculating atr |
| 346 | + atr_length : length of time used for calculating atr |
347 | 347 | closes : sequence
|
348 | 348 | sequence of closing values
|
349 | 349 | marketcolors : dict of colors: up, down, edge, wick, alpha
|
@@ -436,14 +436,122 @@ def _construct_renko_collections(dates, highs, lows, volumes, config_renko_param
|
436 | 436 | useAA = 0, # use tuple here
|
437 | 437 | lw = None
|
438 | 438 | rectCollection = PolyCollection(verts,
|
439 |
| - facecolors=colors, |
440 |
| - antialiaseds=useAA, |
441 |
| - edgecolors=edge_colors, |
442 |
| - linewidths=lw |
443 |
| - ) |
| 439 | + facecolors=colors, |
| 440 | + antialiaseds=useAA, |
| 441 | + edgecolors=edge_colors, |
| 442 | + linewidths=lw |
| 443 | + ) |
444 | 444 |
|
445 | 445 | return (rectCollection, ), new_dates, new_volumes, brick_values
|
446 | 446 |
|
| 447 | +def _construct_pf_collections(dates, highs, lows, volumes, config_renko_params, closes, marketcolors=None): |
| 448 | + """Represent the price change with Xs and Os |
| 449 | +
|
| 450 | + Parameters |
| 451 | + ---------- |
| 452 | + dates : sequence |
| 453 | + sequence of dates |
| 454 | + highs : sequence |
| 455 | + sequence of high values |
| 456 | + lows : sequence |
| 457 | + sequence of low values |
| 458 | + renko_params : dictionary |
| 459 | + brick_size : size of each brick/box |
| 460 | + atr_length : length of time used for calculating atr |
| 461 | + closes : sequence |
| 462 | + sequence of closing values |
| 463 | + marketcolors : dict of colors: up, down, edge, wick, alpha |
| 464 | +
|
| 465 | + Returns |
| 466 | + ------- |
| 467 | + ret : tuple |
| 468 | + rectCollection |
| 469 | + """ |
| 470 | + renko_params = _process_kwargs(config_renko_params, _valid_renko_kwargs()) |
| 471 | + if marketcolors is None: |
| 472 | + marketcolors = _get_mpfstyle('classic')['marketcolors'] |
| 473 | + print('default market colors:',marketcolors) |
| 474 | + |
| 475 | + box_size = renko_params['brick_size'] |
| 476 | + atr_length = renko_params['atr_length'] |
| 477 | + |
| 478 | + |
| 479 | + if box_size == 'atr': |
| 480 | + box_size = _calculate_atr(atr_length, highs, lows, closes) |
| 481 | + else: # is an integer or float |
| 482 | + total_atr = _calculate_atr(len(closes)-1, highs, lows, closes) |
| 483 | + upper_limit = 1.5*total_atr |
| 484 | + lower_limit = 0.01*total_atr |
| 485 | + if box_size > upper_limit: |
| 486 | + raise ValueError("Specified brick_size may not be larger than (1.5* the Average True Value of the dataset) which has value: "+ str(upper_limit)) |
| 487 | + elif box_size < lower_limit: |
| 488 | + raise ValueError("Specified brick_size may not be smaller than (0.01* the Average True Value of the dataset) which has value: "+ str(lower_limit)) |
| 489 | + |
| 490 | + alpha = marketcolors['alpha'] |
| 491 | + |
| 492 | + uc = mcolors.to_rgba(marketcolors['candle'][ 'up' ], alpha) |
| 493 | + dc = mcolors.to_rgba(marketcolors['candle']['down'], alpha) |
| 494 | + tfc = mcolors.to_rgba(marketcolors['edge']['down'], 0) # transparent face color |
| 495 | + |
| 496 | + cdiff = [(closes[i+1] - closes[i])/box_size for i in range(len(closes)-1)] # fill cdiff with close price change |
| 497 | + curr_price = closes[0] |
| 498 | + new_dates = [] # holds the dates corresponding with the index |
| 499 | + new_volumes = [] # holds the volumes corresponding with the index. If more than one index for the same day then they all have the same volume. |
| 500 | + box_values = [] # y values for the boxes |
| 501 | + circle_patches = [] |
| 502 | + line_seg = [] # line segments that make up the Xs |
| 503 | + |
| 504 | + volume_cache = 0 # holds the volumes for the dates that were skipped |
| 505 | + index = -1 |
| 506 | + last_trend_positive = True |
| 507 | + for diff_index, difference in enumerate(cdiff): |
| 508 | + diff = abs(int(round(difference, 0))) |
| 509 | + if volumes is not None: # only adds volumes if there are volume values when volume=True |
| 510 | + if diff != 0: |
| 511 | + new_volumes.extend([volumes[diff_index] + volume_cache]*diff) |
| 512 | + volume_cache = 0 |
| 513 | + else: |
| 514 | + volume_cache += volumes[diff_index] |
| 515 | + |
| 516 | + if diff != 0: |
| 517 | + index += 1 |
| 518 | + new_dates.append(dates[diff_index]) |
| 519 | + else: |
| 520 | + continue |
| 521 | + |
| 522 | + |
| 523 | + sign = (difference / abs(difference)) # -1 or 1 |
| 524 | + x = [index] * (diff) |
| 525 | + start_iteration = 0 if sign > 0 else 1 |
| 526 | + start_iteration += 0 if (last_trend_positive and sign > 0) or (not last_trend_positive and sign < 0) else 1 |
| 527 | + |
| 528 | + y = [(curr_price + (i * box_size * sign)) for i in range(start_iteration, diff+start_iteration)] |
| 529 | + box_values.extend(y) |
| 530 | + |
| 531 | + spacing = 0.1 * box_size |
| 532 | + curr_price += (box_size * sign * (diff)) + spacing |
| 533 | + for i in range(len(x)): # x and y have the same length |
| 534 | + if sign == 1: # X |
| 535 | + line_seg.append([(x[i]-0.25, y[i]-(box_size/2)), (x[i]+0.25, y[i]+(box_size/2))]) # create / part of the X |
| 536 | + line_seg.append([(x[i]-0.25, y[i]+(box_size/2)), (x[i]+0.25, y[i]-(box_size/2))]) # create \ part of the X |
| 537 | + else: # O |
| 538 | + circle_patches.append(Ellipse((x[i], y[i]), 0.5, box_size/0.9)) |
| 539 | + useAA = 0, # use tuple here |
| 540 | + lw = 0.5 |
| 541 | + |
| 542 | + cirCollection = PatchCollection(circle_patches) |
| 543 | + cirCollection.set_facecolor([tfc] * len(circle_patches)) |
| 544 | + cirCollection.set_edgecolor([dc] * len(circle_patches)) |
| 545 | + xCollection = LineCollection(line_seg, |
| 546 | + colors=[uc] * len(line_seg), |
| 547 | + linewidths=lw, |
| 548 | + antialiaseds=useAA |
| 549 | + ) |
| 550 | + |
| 551 | + return (cirCollection, xCollection), new_dates, new_volumes, box_values |
| 552 | + |
| 553 | + |
| 554 | + |
447 | 555 | from matplotlib.ticker import Formatter
|
448 | 556 | class IntegerIndexDateTimeFormatter(Formatter):
|
449 | 557 | """
|
|
0 commit comments