@@ -378,6 +378,37 @@ def _construct_candlestick_collections(dates, opens, highs, lows, closes, market
378378def _construct_renko_collections (dates , highs , lows , volumes , config_renko_params , closes , marketcolors = None ):
379379 """Represent the price change with bricks
380380
381+ NOTE: this code assumes if any value open, low, high, close is
382+ missing they all are missing
383+
384+ Algorithm Explanation
385+ ---------------------
386+ In the first part of the algorithm, we populate the cdiff array
387+ along with adjusting the dates and volumes arrays into the new_dates and
388+ new_volumes arrays. A single date includes a range from no bricks to many
389+ bricks, if a date has no bricks it shall not be included in new_dates,
390+ and if it has n bricks then it will be included n times. Volumes use a
391+ volume cache to save volume amounts for dates that do not have any bricks
392+ before adding the cache to the next date that has at least one brick.
393+ We populate the cdiff array with each close values difference from the
394+ previously created brick divided by the brick size.
395+
396+ In the second part of the algorithm, we iterate through the values in cdiff
397+ and add 1s or -1s to the bricks array depending on whether the value is
398+ positive or negative. Every time there is a trend change (ex. previous brick is
399+ an upbrick, current brick is a down brick) we draw one less brick to account
400+ for the price having to move the previous bricks amount before creating a
401+ brick in the opposite direction.
402+
403+ In the final part of the algorithm, we enumerate through the bricks array and
404+ assign up-colors or down-colors to the associated index in the color array and
405+ populate the verts list with each bricks vertice to be used to create the matplotlib
406+ PolyCollection.
407+
408+ Useful sources:
409+ https://avilpage.com/2018/01/how-to-plot-renko-charts-with-python.html
410+ https://school.stockcharts.com/doku.php?id=chart_analysis:renko
411+
381412 Parameters
382413 ----------
383414 dates : sequence
@@ -413,12 +444,12 @@ def _construct_renko_collections(dates, highs, lows, volumes, config_renko_param
413444 else :
414445 brick_size = _calculate_atr (atr_length , highs , lows , closes )
415446 else : # is an integer or float
416- upper_limit = (max (closes ) - min (closes )) / 5
417- lower_limit = ( max (closes ) - min ( closes )) / 32
447+ upper_limit = (max (closes ) - min (closes )) / 2
448+ lower_limit = 0.01 * _calculate_atr ( len (closes )- 1 , highs , lows , closes )
418449 if brick_size > upper_limit :
419- raise ValueError ("Specified brick_size may not be larger than (20 % of the close price range of the dataset) which has value: " + str (upper_limit ))
450+ raise ValueError ("Specified brick_size may not be larger than (50 % of the close price range of the dataset) which has value: " + str (upper_limit ))
420451 elif brick_size < lower_limit :
421- raise ValueError ("Specified brick_size may not be smaller than (3.125% of the close price range of the dataset) which has value: " + str (lower_limit ))
452+ 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 ))
422453
423454 alpha = marketcolors ['alpha' ]
424455
@@ -427,7 +458,7 @@ def _construct_renko_collections(dates, highs, lows, volumes, config_renko_param
427458 euc = mcolors .to_rgba (marketcolors ['edge' ][ 'up' ], 1.0 )
428459 edc = mcolors .to_rgba (marketcolors ['edge' ]['down' ], 1.0 )
429460
430- cdiff = []
461+ cdiff = [] # holds the differences between each close and the previously created brick / the brick size
431462 prev_close_brick = closes [0 ]
432463 volume_cache = 0 # holds the volumes for the dates that were skipped
433464 new_dates = [] # holds the dates corresponding with the index
@@ -447,7 +478,7 @@ def _construct_renko_collections(dates, highs, lows, volumes, config_renko_param
447478 new_dates .extend ([dates [i ]] * abs (brick_diff ))
448479 prev_close_brick += brick_diff * brick_size
449480
450- bricks = [] # holds bricks, 1 for down bricks, - 1 for up bricks
481+ bricks = [] # holds bricks, - 1 for down bricks, 1 for up bricks
451482 curr_price = closes [0 ]
452483
453484 last_diff_sign = 0 # direction the bricks were last going in -1 -> down, 1 -> up
@@ -511,6 +542,47 @@ def _construct_renko_collections(dates, highs, lows, volumes, config_renko_param
511542def _construct_pointnfig_collections (dates , highs , lows , volumes , config_pointnfig_params , closes , marketcolors = None ):
512543 """Represent the price change with Xs and Os
513544
545+ NOTE: this code assumes if any value open, low, high, close is
546+ missing they all are missing
547+
548+ Algorithm Explanation
549+ ---------------------
550+ In the first part of the algorithm, we populate the boxes array
551+ along with adjusting the dates and volumes arrays into the new_dates and
552+ new_volumes arrays. A single date includes a range from no boxes to many
553+ boxes, if a date has no boxes it shall not be included in new_dates,
554+ and if it has n boxes then it will be included n times. Volumes use a
555+ volume cache to save volume amounts for dates that do not have any boxes
556+ before adding the cache to the next date that has at least one box.
557+ We populate the boxes array with each close values difference from the
558+ previously created brick divided by the box size.
559+
560+ The second part of the algorithm has a series of step. First we combine the
561+ adjacent like signed values in the boxes array (ex. [-1, -2, 3, -4] -> [-3, 3, -4]).
562+ Next we subtract 1 from the absolute value of each element in boxes except the
563+ first to ensure every time there is a trend change (ex. previous box is
564+ an X, current brick is a O) we draw one less box to account for the price
565+ having to move the previous box's amount before creating a box in the
566+ opposite direction. Next we adjust volume and dates to combine volume into
567+ non 0 box indexes and to only use dates from non 0 box indexes. We then
568+ remove all 0s from the boxes array and once again combine adjacent similarly
569+ signed differences in boxes.
570+
571+ Lastly, we enumerate through the boxes to populate the line_seg and circle_patches
572+ arrays. line_seg holds the / and \ line segments that make up an X and
573+ circle_patches holds matplotlib.patches Ellipse objects for each O. We start
574+ by filling an x and y array each iteration which contain the x and y
575+ coordinates for each box in the column. Then for each coordinate pair in
576+ x, y we add to either the line_seg array or the circle_patches array
577+ depending on the value of sign for the current column (1 indicates
578+ line_seg, -1 indicates circle_patches). The height of the boxes take
579+ into account padding which separates each box by a small margin in
580+ order to increase readability.
581+
582+ Useful sources:
583+ https://stackoverflow.com/questions/8750648/point-and-figure-chart-with-matplotlib
584+ https://www.investopedia.com/articles/technical/03/081303.asp
585+
514586 Parameters
515587 ----------
516588 dates : sequence
@@ -546,12 +618,12 @@ def _construct_pointnfig_collections(dates, highs, lows, volumes, config_pointnf
546618 else :
547619 box_size = _calculate_atr (atr_length , highs , lows , closes )
548620 else : # is an integer or float
549- upper_limit = (max (closes ) - min (closes )) / 5
550- lower_limit = ( max (closes ) - min ( closes )) / 32
621+ upper_limit = (max (closes ) - min (closes )) / 2
622+ lower_limit = 0.01 * _calculate_atr ( len (closes )- 1 , highs , lows , closes )
551623 if box_size > upper_limit :
552- raise ValueError ("Specified box_size may not be larger than (20 % of the close price range of the dataset) which has value: " + str (upper_limit ))
624+ raise ValueError ("Specified box_size may not be larger than (50 % of the close price range of the dataset) which has value: " + str (upper_limit ))
553625 elif box_size < lower_limit :
554- raise ValueError ("Specified box_size may not be smaller than (3.125% of the close price range of the dataset) which has value: " + str (lower_limit ))
626+ raise ValueError ("Specified box_size may not be smaller than (0.01* the Average True Value of the dataset) which has value: " + str (lower_limit ))
555627
556628 alpha = marketcolors ['alpha' ]
557629
0 commit comments