From 8137d2461208b4f4f59eee12809a6e1860da93ee Mon Sep 17 00:00:00 2001 From: Chelsea Date: Thu, 6 Aug 2015 15:42:56 -0400 Subject: [PATCH] candlestick with boxplots - re https://github.com/plotly/streambed/pull/3291#issuecomment-128185914 if we want to use the box plot solution for candles this seems simpler @jackparmer @chriddyp --- .../test_tools/test_figure_factory.py | 284 ++++++------------ plotly/tools.py | 190 ++++-------- 2 files changed, 143 insertions(+), 331 deletions(-) diff --git a/plotly/tests/test_core/test_tools/test_figure_factory.py b/plotly/tests/test_core/test_tools/test_figure_factory.py index e4852ac816e..658a48cecfc 100644 --- a/plotly/tests/test_core/test_tools/test_figure_factory.py +++ b/plotly/tests/test_core/test_tools/test_figure_factory.py @@ -325,63 +325,25 @@ def test_one_candlestick(self): low=[32.7], close=[33.1]) - exp_can_inc = {'data': [{'hoverinfo': 'none', - 'legendgroup': 'Increasing', - 'marker': {'color': 'rgba(0, 0, 0, 0)'}, - 'showlegend': False, - 'type': 'bar', - 'x': [0], - 'y': [33.0]}, - {'hoverinfo': 'none', - 'legendgroup': 'Increasing', - 'line': {'color': '#3D9970'}, - 'marker': {'color': '#3D9970'}, - 'name': 'Increasing', - 'showlegend': False, - 'type': 'bar', - 'x': [0], - 'y': [0.10000000000000142]}, - {'legendgroup': 'Increasing', + exp_can_inc = {'data': [{'boxpoints': False, + 'fillcolor': '#3D9970', 'line': {'color': '#3D9970'}, - 'marker': {'color': '#3D9970'}, - 'mode': 'lines', 'name': 'Increasing', 'showlegend': False, - 'text': ('Low', 'Open', 'Close', 'High', ''), - 'type': 'scatter', - 'x': [0, 0, 0, 0, None], - 'y': [32.7, 33.0, 33.1, 33.2, None]}, - {'hoverinfo': 'none', - 'legendgroup': 'Decreasing', - 'marker': {'color': 'rgba(0, 0, 0, 0)'}, - 'showlegend': False, - 'type': 'bar', - 'x': [], - 'y': []}, - {'hoverinfo': 'none', - 'legendgroup': 'Decreasing', + 'type': 'box', + 'whiskerwidth': 0, + 'x': [0, 0, 0, 0, 0, 0], + 'y': [32.7, 33.0, 33.1, 33.1, 33.1, 33.2]}, + {'boxpoints': False, + 'fillcolor': '#FF4136', 'line': {'color': '#FF4136'}, - 'marker': {'color': '#FF4136'}, 'name': 'Decreasing', 'showlegend': False, - 'type': 'bar', - 'x': [], - 'y': []}, - {'legendgroup': 'Decreasing', - 'line': {'color': '#FF4136'}, - 'marker': {'color': '#FF4136'}, - 'mode': 'lines', - 'name': 'Decreasing', - 'showlegend': False, - 'text': (), - 'type': 'scatter', + 'type': 'box', + 'whiskerwidth': 0, 'x': [], 'y': []}], - 'layout': {'bargroupgap': 0.2, - 'barmode': 'stack', - 'yaxis': {'fixedrange': True, - 'range': [32.650000000000006, - 33.25]}}} + 'layout': {}} self.assertEqual(can_inc, exp_can_inc) @@ -610,195 +572,117 @@ def test_datetime_candlestick(self): candle = tls.FigureFactory.create_candlestick(open_data, high_data, low_data, close_data, dates=x) - exp_candle = {'data': [{'hoverinfo': 'none', - 'legendgroup': 'Increasing', - 'marker': {'color': 'rgba(0, 0, 0, 0)'}, - 'showlegend': False, - 'type': 'bar', - 'x': [datetime.datetime(2013, 3, 4, 0, 0), - datetime.datetime(2013, 12, 4, 0, 0), - datetime.datetime(2014, 6, 6, 0, 0), - datetime.datetime(2014, 12, 5, 0, 0)], - 'y': [33.01, 32.06, 33.05, 33.5]}, - {'hoverinfo': 'none', - 'legendgroup': 'Increasing', - 'line': {'color': '#3D9970'}, - 'marker': {'color': '#3D9970'}, - 'name': 'Increasing', - 'showlegend': False, - 'type': 'bar', - 'x': [datetime.datetime(2013, 3, 4, 0, 0), - datetime.datetime(2013, 12, 4, 0, 0), - datetime.datetime(2014, 6, 6, 0, 0), - datetime.datetime(2014, 12, 5, 0, 0)], - 'y': [1.0900000000000034, - 1.1199999999999974, - 0.05000000000000426, - 0.20000000000000284]}, - {'legendgroup': 'Increasing', + exp_candle = {'data': [{'boxpoints': False, + 'fillcolor': '#3D9970', 'line': {'color': '#3D9970'}, - 'marker': {'color': '#3D9970'}, - 'mode': 'lines', 'name': 'Increasing', 'showlegend': False, - 'text': ('Low', - 'Open', - 'Close', - 'High', - '', - 'Low', - 'Open', - 'Close', - 'High', - '', - 'Low', - 'Open', - 'Close', - 'High', - '', - 'Low', - 'Open', - 'Close', - 'High', - ''), - 'type': 'scatter', + 'type': 'box', + 'whiskerwidth': 0, 'x': [datetime.datetime(2013, 3, 4, 0, 0), datetime.datetime(2013, 3, 4, 0, 0), datetime.datetime(2013, 3, 4, 0, 0), datetime.datetime(2013, 3, 4, 0, 0), - None, + datetime.datetime(2013, 3, 4, 0, 0), + datetime.datetime(2013, 3, 4, 0, 0), + datetime.datetime(2013, 12, 4, 0, 0), datetime.datetime(2013, 12, 4, 0, 0), datetime.datetime(2013, 12, 4, 0, 0), datetime.datetime(2013, 12, 4, 0, 0), datetime.datetime(2013, 12, 4, 0, 0), - None, + datetime.datetime(2013, 12, 4, 0, 0), + datetime.datetime(2014, 6, 6, 0, 0), + datetime.datetime(2014, 6, 6, 0, 0), datetime.datetime(2014, 6, 6, 0, 0), datetime.datetime(2014, 6, 6, 0, 0), datetime.datetime(2014, 6, 6, 0, 0), datetime.datetime(2014, 6, 6, 0, 0), - None, datetime.datetime(2014, 12, 5, 0, 0), datetime.datetime(2014, 12, 5, 0, 0), datetime.datetime(2014, 12, 5, 0, 0), datetime.datetime(2014, 12, 5, 0, 0), - None], - 'y': [31.7, - 33.01, - 34.1, - 34.2, - None, - 31.62, - 32.06, - 33.18, - 34.25, - None, - 32.75, - 33.05, - 33.1, - 33.25, - None, - 32.87, - 33.5, - 33.7, - 34.62, - None]}, - {'hoverinfo': 'none', - 'legendgroup': 'Decreasing', - 'marker': {'color': 'rgba(0, 0, 0, 0)'}, - 'showlegend': False, - 'type': 'bar', - 'x': [datetime.datetime(2013, 6, 5, 0, 0), - datetime.datetime(2013, 9, 6, 0, 0), - datetime.datetime(2014, 3, 5, 0, 0), - datetime.datetime(2014, 9, 4, 0, 0)], - 'y': [31.93, 33.37, 31.18, 32.93]}, - {'hoverinfo': 'none', - 'legendgroup': 'Decreasing', - 'line': {'color': '#FF4136'}, - 'marker': {'color': '#FF4136'}, - 'name': 'Decreasing', - 'showlegend': False, - 'type': 'bar', - 'x': [datetime.datetime(2013, 6, 5, 0, 0), - datetime.datetime(2013, 9, 6, 0, 0), - datetime.datetime(2014, 3, 5, 0, 0), - datetime.datetime(2014, 9, 4, 0, 0)], - 'y': [1.3800000000000026, - 0.13000000000000256, - 2.9399999999999977, - 0.38000000000000256]}, - {'legendgroup': 'Decreasing', + datetime.datetime(2014, 12, 5, 0, 0), + datetime.datetime(2014, 12, 5, 0, 0)], + 'y': [31.7, + 33.01, + 34.1, + 34.1, + 34.1, + 34.2, + 31.62, + 32.06, + 33.18, + 33.18, + 33.18, + 34.25, + 32.75, + 33.05, + 33.1, + 33.1, + 33.1, + 33.25, + 32.87, + 33.5, + 33.7, + 33.7, + 33.7, + 34.62]}, + {'boxpoints': False, + 'fillcolor': '#FF4136', 'line': {'color': '#FF4136'}, - 'marker': {'color': '#FF4136'}, - 'mode': 'lines', 'name': 'Decreasing', 'showlegend': False, - 'text': ('Low', - 'Close', - 'Open', - 'High', - '', - 'Low', - 'Close', - 'Open', - 'High', - '', - 'Low', - 'Close', - 'Open', - 'High', - '', - 'Low', - 'Close', - 'Open', - 'High', - ''), - 'type': 'scatter', + 'type': 'box', + 'whiskerwidth': 0, 'x': [datetime.datetime(2013, 6, 5, 0, 0), datetime.datetime(2013, 6, 5, 0, 0), datetime.datetime(2013, 6, 5, 0, 0), datetime.datetime(2013, 6, 5, 0, 0), - None, + datetime.datetime(2013, 6, 5, 0, 0), + datetime.datetime(2013, 6, 5, 0, 0), + datetime.datetime(2013, 9, 6, 0, 0), datetime.datetime(2013, 9, 6, 0, 0), datetime.datetime(2013, 9, 6, 0, 0), datetime.datetime(2013, 9, 6, 0, 0), datetime.datetime(2013, 9, 6, 0, 0), - None, + datetime.datetime(2013, 9, 6, 0, 0), + datetime.datetime(2014, 3, 5, 0, 0), + datetime.datetime(2014, 3, 5, 0, 0), datetime.datetime(2014, 3, 5, 0, 0), datetime.datetime(2014, 3, 5, 0, 0), datetime.datetime(2014, 3, 5, 0, 0), datetime.datetime(2014, 3, 5, 0, 0), - None, datetime.datetime(2014, 9, 4, 0, 0), datetime.datetime(2014, 9, 4, 0, 0), datetime.datetime(2014, 9, 4, 0, 0), datetime.datetime(2014, 9, 4, 0, 0), - None], - 'y': [30.75, - 31.93, - 33.31, - 34.37, - None, - 32.87, - 33.37, - 33.5, - 33.62, - None, - 30.81, - 31.18, - 34.12, - 35.18, - None, - 32.75, - 32.93, - 33.31, - 35.37, - None]}], - 'layout': {'bargroupgap': 0.2, - 'barmode': 'stack', - 'yaxis': {'fixedrange': True, - 'range': [30.288, - 35.831999999999994]}}} + datetime.datetime(2014, 9, 4, 0, 0), + datetime.datetime(2014, 9, 4, 0, 0)], + 'y': [30.75, + 33.31, + 31.93, + 31.93, + 31.93, + 34.37, + 32.87, + 33.5, + 33.37, + 33.37, + 33.37, + 33.62, + 30.81, + 34.12, + 31.18, + 31.18, + 31.18, + 35.18, + 32.75, + 33.31, + 32.93, + 32.93, + 32.93, + 35.37]}], + 'layout': {}} self.assertEqual(candle, exp_candle) diff --git a/plotly/tools.py b/plotly/tools.py index 590166906d6..f77b7f92f2a 100644 --- a/plotly/tools.py +++ b/plotly/tools.py @@ -2003,7 +2003,7 @@ def create_ohlc(open, high, low, close, @staticmethod def _make_increasing_candle(open, high, low, close, dates, **kwargs): """ - Makes stacked bar and vertical line for increasing candlesticks + Makes boxplot trace for increasing candlesticks _make_increasing_candle() and _make_decreasing_candle separate the increasing traces from the decreasing traces so kwargs (such as @@ -2019,57 +2019,36 @@ def _make_increasing_candle(open, high, low, close, dates, **kwargs): :param kwargs: kwargs to be passed to increasing trace via plotly.graph_objs.Scatter. - :rtype (list) candle_incr_data: list of three traces: hidden_bar_incr, - candle_bar_incr, candle_line_incr: returns the first (invisible) - stacked bar, second (visible) stacked bar, and trace composed of - vertical lines for each increasing candlestick. + :rtype (list) candle_incr_data: list of the box trace for + increasing candlesticks. """ - (increase_x, - increase_open, - increase_dif, - stick_increase_y, - stick_increase_x) = (_Candlestick(open, high, low, close, dates, - **kwargs).get_candle_increase()) + increase_x, increase_y = _Candlestick( + open, high, low, close, dates, **kwargs).get_candle_increase() + if 'line' in kwargs: + kwargs.setdefault('fillcolor', kwargs['line']['color']) + else: + kwargs.setdefault('fillcolor', _DEFAULT_INCREASING_COLOR) if 'name' in kwargs: - showlegend = True + kwargs.setdefault('showlegend', True) else: - kwargs.setdefault('name', 'Increasing') - showlegend = False - - kwargs.setdefault('marker', dict(color=_DEFAULT_INCREASING_COLOR)) + kwargs.setdefault('showlegend', False) + kwargs.setdefault('name', 'Increasing') kwargs.setdefault('line', dict(color=_DEFAULT_INCREASING_COLOR)) - hidden_bar_incr = dict(type='bar', - x=increase_x, - y=increase_open, - marker=Marker(color='rgba(0, 0, 0, 0)'), - legendgroup='Increasing', - showlegend=False, - hoverinfo='none') - candle_bar_incr = dict(type='bar', - x=increase_x, - y=increase_dif, - legendgroup='Increasing', - showlegend=False, - hoverinfo='none', - **kwargs) - candle_line_incr = dict(type='scatter', - x=stick_increase_x, - y=stick_increase_y, - mode='lines', - legendgroup='Increasing', - text=('Low', 'Open', 'Close', - 'High', '') * len(increase_x), - showlegend=showlegend, + candle_incr_data = dict(type='box', + x=increase_x, + y=increase_y, + whiskerwidth=0, + boxpoints=False, **kwargs) - candle_incr_data = [hidden_bar_incr, candle_bar_incr, candle_line_incr] - return candle_incr_data + + return [candle_incr_data] @staticmethod def _make_decreasing_candle(open, high, low, close, dates, **kwargs): """ - Makes stacked bar and vertical line for decreasing candlesticks + Makes boxplot trace for decreasing candlesticks :param (list) open: opening values :param (list) high: high values @@ -2079,48 +2058,29 @@ def _make_decreasing_candle(open, high, low, close, dates, **kwargs): :param kwargs: kwargs to be passed to decreasing trace via plotly.graph_objs.Scatter. - :rtype (list) candle_decr_data: list of three traces: hidden_bar_decr, - candle_bar_decr, candle_line_decr: returns the first (invisible) - stacked bar, second (visible) stacked bar, and trace composed of - vertical lines for each decreasing candlestick. + :rtype (list) candle_decr_data: list of the box trace for + decreasing candlesticks. """ - (decrease_x, - decrease_close, - decrease_dif, - stick_decrease_y, - stick_decrease_x) = (_Candlestick(open, high, low, close, dates, - **kwargs).get_candle_decrease()) + decrease_x, decrease_y = _Candlestick( + open, high, low, close, dates, **kwargs).get_candle_decrease() - kwargs.setdefault('marker', dict(color=_DEFAULT_DECREASING_COLOR)) + if 'line' in kwargs: + kwargs.setdefault('fillcolor', kwargs['line']['color']) + else: + kwargs.setdefault('fillcolor', _DEFAULT_DECREASING_COLOR) + kwargs.setdefault('showlegend', False) kwargs.setdefault('line', dict(color=_DEFAULT_DECREASING_COLOR)) kwargs.setdefault('name', 'Decreasing') - hidden_bar_decr = dict(type='bar', - x=decrease_x, - y=decrease_close, - marker=Marker(color='rgba(0, 0, 0, 0)'), - legendgroup='Decreasing', - showlegend=False, - hoverinfo='none') - candle_bar_decr = dict(type='bar', - x=decrease_x, - y=decrease_dif, - legendgroup='Decreasing', - showlegend=False, - hoverinfo='none', - **kwargs) - candle_line_decr = dict(type='scatter', - x=stick_decrease_x, - y=stick_decrease_y, - mode='lines', - legendgroup='Decreasing', - showlegend=False, - text=('Low', 'Close', 'Open', - 'High', '') * len(decrease_x), + candle_decr_data = dict(type='box', + x=decrease_x, + y=decrease_y, + whiskerwidth=0, + boxpoints=False, **kwargs) - candle_decr_data = [hidden_bar_decr, candle_bar_decr, candle_line_decr] - return candle_decr_data + + return [candle_decr_data] @staticmethod def create_candlestick(open, high, low, close, @@ -2192,15 +2152,14 @@ def create_candlestick(open, high, low, close, import pandas.io.data as web df = web.DataReader("aapl", 'yahoo', datetime(2008, 1, 1), datetime(2009, 4, 1)) - fig = FF.create_candlestick(df.Open, df.High, df.Low, df.Close, dates=df.index) - # Make increasing ohlc sticks and customize their color and name + # Make increasing candlesticks and customize their color and name fig_increasing = FF.create_candlestick(df.Open, df.High, df.Low, df.Close, dates=df.index, direction='increasing', name='AAPL', marker=Marker(color='rgb(150, 200, 250)'), line=Line(color='rgb(150, 200, 250)')) - # Make decreasing ohlc sticks and customize their color and name + # Make decreasing candlesticks and customize their color and name fig_decreasing = FF.create_candlestick(df.Open, df.High, df.Low, df.Close, dates=df.index, direction='decreasing', marker=Marker(color='rgb(128, 128, 128)'), @@ -2262,16 +2221,7 @@ def create_candlestick(open, high, low, close, open, high, low, close, dates, **kwargs) data = candle_incr_data + candle_decr_data - layout = graph_objs.Layout(barmode='stack', - bargroupgap=0.2, - yaxis=dict(range=[(min(low) - - ((max(high) - min(low)) * - .1)), - (max(high) + ((max(high) - - min(low)) * - .1))])) - layout['yaxis']['fixedrange'] = True - + layout = graph_objs.Layout() return dict(data=data, layout=layout) @@ -2808,32 +2758,22 @@ def get_candle_increase(self): The data is increasing when close value > open value and decreasing when the close value <= open value. """ - increase_open = [] - increase_high = [] - increase_low = [] - increase_close = [] + increase_y = [] increase_x = [] for index in range(len(self.open)): if self.close[index] > self.open[index]: - increase_open.append(self.open[index]) - increase_high.append(self.high[index]) - increase_low.append(self.low[index]) - increase_close.append(self.close[index]) + increase_y.append(self.low[index]) + increase_y.append(self.open[index]) + increase_y.append(self.close[index]) + increase_y.append(self.close[index]) + increase_y.append(self.close[index]) + increase_y.append(self.high[index]) increase_x.append(self.x[index]) - increase_dif = [cl - op for (cl, op) - in zip(increase_close, increase_open)] + increase_x = [[x, x, x, x, x, x] for x in increase_x] + increase_x = FigureFactory.flatten(increase_x) - increase_empty = [None] * len(increase_open) - stick_increase_y = list(zip(increase_low, increase_open, - increase_close, increase_high, - increase_empty)) - stick_increase_x = [[x, x, x, x, None] for x in increase_x] - stick_increase_y = FigureFactory.flatten(stick_increase_y) - stick_increase_x = FigureFactory.flatten(stick_increase_x) - - return (increase_x, increase_open, increase_dif, - stick_increase_y, stick_increase_x) + return increase_x, increase_y def get_candle_decrease(self): """ @@ -2842,32 +2782,20 @@ def get_candle_decrease(self): The data is increasing when close value > open value and decreasing when the close value <= open value. """ - decrease_open = [] - decrease_high = [] - decrease_low = [] - decrease_close = [] + decrease_y = [] decrease_x = [] - for index in range(len(self.open)): if self.close[index] <= self.open[index]: - decrease_open.append(self.open[index]) - decrease_high.append(self.high[index]) - decrease_low.append(self.low[index]) - decrease_close.append(self.close[index]) + decrease_y.append(self.low[index]) + decrease_y.append(self.open[index]) + decrease_y.append(self.close[index]) + decrease_y.append(self.close[index]) + decrease_y.append(self.close[index]) + decrease_y.append(self.high[index]) decrease_x.append(self.x[index]) - decrease_dif = [op - cl for (op, cl) - in zip(decrease_open, decrease_close)] - - decrease_empty = [None] * len(decrease_open) - stick_decrease_y = list(zip(decrease_low, decrease_close, - decrease_open, decrease_high, - decrease_empty)) - stick_decrease_x = [[x, x, x, x, None] for x in decrease_x] - - stick_decrease_y = FigureFactory.flatten(stick_decrease_y) - stick_decrease_x = FigureFactory.flatten(stick_decrease_x) + decrease_x = [[x, x, x, x, x, x] for x in decrease_x] + decrease_x = FigureFactory.flatten(decrease_x) - return (decrease_x, decrease_close, decrease_dif, - stick_decrease_y, stick_decrease_x) + return decrease_x, decrease_y