From ecadfdd98ea7fcb22265d37a67a6494706047e8a Mon Sep 17 00:00:00 2001 From: Christof Date: Sun, 17 May 2020 12:24:04 +0200 Subject: [PATCH 01/19] fixed:advanced config. added. feature: fill area between traces by advanced configuration. --- freqtrade/plot/plotting.py | 40 +++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index f7d300593..ec0d53a0b 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -236,12 +236,20 @@ def create_plotconfig(indicators1: List[str], indicators2: List[str], :param plot_config: Dict of Dicts containing advanced plot configuration :return: plot_config - eventually with indicators 1 and 2 """ - if plot_config: if indicators1: - plot_config['main_plot'] = {ind: {} for ind in indicators1} + for ind in indicators1: + #add indicators with no advanced plot_config. + if ind not in plot_config['main_plot'].keys(): + plot_config['main_plot'][ind] = {} if indicators2: - plot_config['subplots'] = {'Other': {ind: {} for ind in indicators2}} + #'Other' key not provided in strategy.plot_config. + if 'Other' not in plot_config['subplots'].keys(): + plot_config['subplots'] = {'Other': plot_config['subplots']} + for ind in indicators2: + #add indicators with no advanced plot_config + if ind not in plot_config['subplots']['Other'].keys(): + plot_config['subplots']['Other'][ind] = {} if not plot_config: # If no indicators and no plot-config given, use defaults. @@ -255,6 +263,8 @@ def create_plotconfig(indicators1: List[str], indicators2: List[str], 'main_plot': {ind: {} for ind in indicators1}, 'subplots': {'Other': {ind: {} for ind in indicators2}}, } + + #!!!NON SENSE - isnt it? if 'main_plot' not in plot_config: plot_config['main_plot'] = {} @@ -280,6 +290,7 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra :return: Plotly figure """ plot_config = create_plotconfig(indicators1, indicators2, plot_config) + print(plot_config) rows = 2 + len(plot_config['subplots']) row_widths = [1 for _ in plot_config['subplots']] @@ -370,6 +381,29 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra del plot_config['main_plot']['bb_upperband'] del plot_config['main_plot']['bb_lowerband'] + #fill area betwenn traces i.e. for ichimoku + if 'fill_area' in plot_config.keys(): + for area in plot_config['fill_area']: + #!error: need exactly 2 trace + traces = area['traces'] + color = area['color'] + if traces[0] in data and traces[1] in data: + trace_b = go.Scatter( + x=data.date, + y=data.get(traces[0]), + showlegend=False, + line={'color': 'rgba(255,255,255,0)'}, + ) + trace_a = go.Scatter( + x=data.date, + y=data.get(traces[1]), + name=f'{traces[0]} * {traces[1]}', + fill="tonexty", + fillcolor=color, + line={'color': 'rgba(255,255,255,0)'}, + ) + fig.add_trace(trace_b) + fig.add_trace(trace_a) # Add indicators to main plot fig = add_indicators(fig=fig, row=1, indicators=plot_config['main_plot'], data=data) From 3fdfc06a1e6046d61bb420d7b52d2dd8d6427e4b Mon Sep 17 00:00:00 2001 From: Christof Date: Sun, 17 May 2020 12:43:38 +0200 Subject: [PATCH 02/19] label for fill_area added and documentation updated --- docs/plotting.md | 9 ++++++++- freqtrade/plot/plotting.py | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/plotting.md b/docs/plotting.md index 09eb6ddb5..f794cdedc 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -168,6 +168,7 @@ Additional features when using plot_config include: * Specify colors per indicator * Specify additional subplots +* Specify idicator pairs to fill area in between The sample plot configuration below specifies fixed colors for the indicators. Otherwise consecutive plots may produce different colorschemes each time, making comparisons difficult. It also allows multiple subplots to display both MACD and RSI at the same time. @@ -194,7 +195,13 @@ Sample configuration with inline comments explaining the process: "RSI": { 'rsi': {'color': 'red'}, } - } + }, + 'fill_area': { + "Ichimoku Cloud": { + 'traces': ('senkou_a', 'senkou_b'), + 'color': 'rgba(0,176,246,0.2)', + }, + } } ``` diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index ec0d53a0b..2d0d01388 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -383,7 +383,7 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra #fill area betwenn traces i.e. for ichimoku if 'fill_area' in plot_config.keys(): - for area in plot_config['fill_area']: + for label, area in plot_config['fill_area'].items(): #!error: need exactly 2 trace traces = area['traces'] color = area['color'] @@ -397,7 +397,7 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra trace_a = go.Scatter( x=data.date, y=data.get(traces[1]), - name=f'{traces[0]} * {traces[1]}', + name=label, fill="tonexty", fillcolor=color, line={'color': 'rgba(255,255,255,0)'}, From daa1727e2bfd641058728eb8161d615e2db1c66a Mon Sep 17 00:00:00 2001 From: Christof Date: Sun, 17 May 2020 13:13:32 +0200 Subject: [PATCH 03/19] Exeption for fill_area.traces --- freqtrade/plot/plotting.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 2d0d01388..aed752b94 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -290,7 +290,6 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra :return: Plotly figure """ plot_config = create_plotconfig(indicators1, indicators2, plot_config) - print(plot_config) rows = 2 + len(plot_config['subplots']) row_widths = [1 for _ in plot_config['subplots']] @@ -384,8 +383,13 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra #fill area betwenn traces i.e. for ichimoku if 'fill_area' in plot_config.keys(): for label, area in plot_config['fill_area'].items(): - #!error: need exactly 2 trace traces = area['traces'] + if len(traces) != 2: + raise Exception( + f"plot_config.fill_area.traces = {traces}: " \ + + f"needs exactly 2 indicators. " \ + + f"{len(traces)} is given." + ) color = area['color'] if traces[0] in data and traces[1] in data: trace_b = go.Scatter( From fdd4b40c3468d6cf8145202eeb058c6c36a50855 Mon Sep 17 00:00:00 2001 From: Christof Date: Thu, 21 May 2020 08:28:58 +0200 Subject: [PATCH 04/19] fixed subplots, empty create plot_config if its not given by strategie --- freqtrade/plot/plotting.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index aed752b94..ea18e7102 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -237,19 +237,24 @@ def create_plotconfig(indicators1: List[str], indicators2: List[str], :return: plot_config - eventually with indicators 1 and 2 """ if plot_config: + #maybe main or sub is given, not both. + if 'main_plot' not in plot_config.keys(): + plot_config['main_plot'] = {} + + if 'subplots' not in plot_config.keys(): + plot_config['subplots'] = {} if indicators1: for ind in indicators1: - #add indicators with no advanced plot_config. + #add indicators with NO advanced plot_config, only! to be sure + #indicator colors given in advanced plot_config will not be + #overwritten. if ind not in plot_config['main_plot'].keys(): plot_config['main_plot'][ind] = {} if indicators2: - #'Other' key not provided in strategy.plot_config. - if 'Other' not in plot_config['subplots'].keys(): - plot_config['subplots'] = {'Other': plot_config['subplots']} - for ind in indicators2: - #add indicators with no advanced plot_config - if ind not in plot_config['subplots']['Other'].keys(): - plot_config['subplots']['Other'][ind] = {} + #add other indicators given on cmd line to advanced plot_config. + plot_config['subplots'].update( + {'Other' : {ind : {} for ind in indicators2}} + ) if not plot_config: # If no indicators and no plot-config given, use defaults. @@ -264,12 +269,6 @@ def create_plotconfig(indicators1: List[str], indicators2: List[str], 'subplots': {'Other': {ind: {} for ind in indicators2}}, } - #!!!NON SENSE - isnt it? - if 'main_plot' not in plot_config: - plot_config['main_plot'] = {} - - if 'subplots' not in plot_config: - plot_config['subplots'] = {} return plot_config @@ -290,7 +289,7 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra :return: Plotly figure """ plot_config = create_plotconfig(indicators1, indicators2, plot_config) - + print(plot_config) rows = 2 + len(plot_config['subplots']) row_widths = [1 for _ in plot_config['subplots']] # Define the graph From fb3d82ccb9221a8928a9a81618bfade526853820 Mon Sep 17 00:00:00 2001 From: Christof Date: Thu, 21 May 2020 08:32:12 +0200 Subject: [PATCH 05/19] cleanup --- freqtrade/plot/plotting.py | 1 - 1 file changed, 1 deletion(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index ea18e7102..02636d831 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -289,7 +289,6 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra :return: Plotly figure """ plot_config = create_plotconfig(indicators1, indicators2, plot_config) - print(plot_config) rows = 2 + len(plot_config['subplots']) row_widths = [1 for _ in plot_config['subplots']] # Define the graph From 4531c924da55ecab7eb6a704d695a84c33e0aa19 Mon Sep 17 00:00:00 2001 From: Christof Date: Mon, 25 May 2020 09:05:24 +0200 Subject: [PATCH 06/19] PEP8 --- freqtrade/plot/plotting.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 02636d831..bed0319a6 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -237,7 +237,7 @@ def create_plotconfig(indicators1: List[str], indicators2: List[str], :return: plot_config - eventually with indicators 1 and 2 """ if plot_config: - #maybe main or sub is given, not both. + # maybe main or sub is given, not both. if 'main_plot' not in plot_config.keys(): plot_config['main_plot'] = {} @@ -245,15 +245,15 @@ def create_plotconfig(indicators1: List[str], indicators2: List[str], plot_config['subplots'] = {} if indicators1: for ind in indicators1: - #add indicators with NO advanced plot_config, only! to be sure - #indicator colors given in advanced plot_config will not be - #overwritten. + # add indicators with NO advanced plot_config, only! to be sure + # indicator colors given in advanced plot_config will not be + # overwritten. if ind not in plot_config['main_plot'].keys(): plot_config['main_plot'][ind] = {} if indicators2: - #add other indicators given on cmd line to advanced plot_config. + # add other indicators given on cmd line to advanced plot_config. plot_config['subplots'].update( - {'Other' : {ind : {} for ind in indicators2}} + {'Other': {ind: {} for ind in indicators2}} ) if not plot_config: @@ -378,15 +378,15 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra del plot_config['main_plot']['bb_upperband'] del plot_config['main_plot']['bb_lowerband'] - #fill area betwenn traces i.e. for ichimoku + # fill area betwenn traces i.e. for ichimoku if 'fill_area' in plot_config.keys(): for label, area in plot_config['fill_area'].items(): traces = area['traces'] if len(traces) != 2: raise Exception( - f"plot_config.fill_area.traces = {traces}: " \ - + f"needs exactly 2 indicators. " \ - + f"{len(traces)} is given." + f"plot_config.fill_area.traces = {traces}: " + + f"needs exactly 2 indicators. " + + f"{len(traces)} is given." ) color = area['color'] if traces[0] in data and traces[1] in data: @@ -406,6 +406,7 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra ) fig.add_trace(trace_b) fig.add_trace(trace_a) + # Add indicators to main plot fig = add_indicators(fig=fig, row=1, indicators=plot_config['main_plot'], data=data) From cc39cf97ddee25768ed26f6b8b8afc8f8fd52857 Mon Sep 17 00:00:00 2001 From: Christof Date: Sat, 19 Dec 2020 14:14:20 +0100 Subject: [PATCH 07/19] revert to former create_plotconfig behaviour --- freqtrade/plot/plotting.py | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index bed0319a6..7aa332501 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -226,7 +226,6 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots: logger.warning("No trades found.") return fig - def create_plotconfig(indicators1: List[str], indicators2: List[str], plot_config: Dict[str, Dict]) -> Dict[str, Dict]: """ @@ -236,25 +235,12 @@ def create_plotconfig(indicators1: List[str], indicators2: List[str], :param plot_config: Dict of Dicts containing advanced plot configuration :return: plot_config - eventually with indicators 1 and 2 """ - if plot_config: - # maybe main or sub is given, not both. - if 'main_plot' not in plot_config.keys(): - plot_config['main_plot'] = {} - if 'subplots' not in plot_config.keys(): - plot_config['subplots'] = {} + if plot_config: if indicators1: - for ind in indicators1: - # add indicators with NO advanced plot_config, only! to be sure - # indicator colors given in advanced plot_config will not be - # overwritten. - if ind not in plot_config['main_plot'].keys(): - plot_config['main_plot'][ind] = {} + plot_config['main_plot'] = {ind: {} for ind in indicators1} if indicators2: - # add other indicators given on cmd line to advanced plot_config. - plot_config['subplots'].update( - {'Other': {ind: {} for ind in indicators2}} - ) + plot_config['subplots'] = {'Other': {ind: {} for ind in indicators2}} if not plot_config: # If no indicators and no plot-config given, use defaults. @@ -268,10 +254,13 @@ def create_plotconfig(indicators1: List[str], indicators2: List[str], 'main_plot': {ind: {} for ind in indicators1}, 'subplots': {'Other': {ind: {} for ind in indicators2}}, } + if 'main_plot' not in plot_config: + plot_config['main_plot'] = {} + if 'subplots' not in plot_config: + plot_config['subplots'] = {} return plot_config - def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFrame = None, *, indicators1: List[str] = [], indicators2: List[str] = [], From 75e4758936ac99d3fc9f756730d6f678480c8035 Mon Sep 17 00:00:00 2001 From: Christof Date: Sat, 19 Dec 2020 17:06:21 +0100 Subject: [PATCH 08/19] changed config params, added fill area in subplots --- freqtrade/plot/plotting.py | 113 +++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 54 deletions(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 7aa332501..62a667e03 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -226,6 +226,7 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots: logger.warning("No trades found.") return fig + def create_plotconfig(indicators1: List[str], indicators2: List[str], plot_config: Dict[str, Dict]) -> Dict[str, Dict]: """ @@ -261,6 +262,34 @@ def create_plotconfig(indicators1: List[str], indicators2: List[str], plot_config['subplots'] = {} return plot_config + +def add_filled_traces(fig, row: int, data: pd.DataFrame, indicator_a: str, + indicator_b: str, label: str = "", + fill_color: str = "rgba(0,176,246,0.2)") -> make_subplots: + """ Adds plots for two traces, which are filled between to fig. + :param fig: Plot figure to append to + :param row: row number for this plot + :param data: candlestick DataFrame + :param indicator_a: indicator name as populated in stragetie + :param indicator_b: indicator name as populated in stragetie + :param label: label for the filled area + :param fill_color: color to be used for the filled area + :return: fig with added filled_traces plot + """ + if indicator_a in data and indicator_b in data: + # TODO: Figure out why scattergl causes problems plotly/plotly.js#2284 + trace_a = go.Scatter(x=data.date, y=data[indicator_a], + showlegend=False, + line={'color': 'rgba(255,255,255,0)'}) + + trace_b = go.Scatter(x=data.date, y=data[indicator_b], name=label, + fill="tonexty", fillcolor=fill_color, + line={'color': 'rgba(255,255,255,0)'}) + fig.add_trace(trace_a, row, 1) + fig.add_trace(trace_b, row, 1) + return fig + + def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFrame = None, *, indicators1: List[str] = [], indicators2: List[str] = [], @@ -344,61 +373,28 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra else: logger.warning("No sell-signals found.") - # TODO: Figure out why scattergl causes problems plotly/plotly.js#2284 - if 'bb_lowerband' in data and 'bb_upperband' in data: - bb_lower = go.Scatter( - x=data.date, - y=data.bb_lowerband, - showlegend=False, - line={'color': 'rgba(255,255,255,0)'}, - ) - bb_upper = go.Scatter( - x=data.date, - y=data.bb_upperband, - name='Bollinger Band', - fill="tonexty", - fillcolor="rgba(0,176,246,0.2)", - line={'color': 'rgba(255,255,255,0)'}, - ) - fig.add_trace(bb_lower, 1, 1) - fig.add_trace(bb_upper, 1, 1) - if ('bb_upperband' in plot_config['main_plot'] - and 'bb_lowerband' in plot_config['main_plot']): - del plot_config['main_plot']['bb_upperband'] - del plot_config['main_plot']['bb_lowerband'] - - # fill area betwenn traces i.e. for ichimoku - if 'fill_area' in plot_config.keys(): - for label, area in plot_config['fill_area'].items(): - traces = area['traces'] - if len(traces) != 2: - raise Exception( - f"plot_config.fill_area.traces = {traces}: " + - f"needs exactly 2 indicators. " + - f"{len(traces)} is given." - ) - color = area['color'] - if traces[0] in data and traces[1] in data: - trace_b = go.Scatter( - x=data.date, - y=data.get(traces[0]), - showlegend=False, - line={'color': 'rgba(255,255,255,0)'}, - ) - trace_a = go.Scatter( - x=data.date, - y=data.get(traces[1]), - name=label, - fill="tonexty", - fillcolor=color, - line={'color': 'rgba(255,255,255,0)'}, - ) - fig.add_trace(trace_b) - fig.add_trace(trace_a) + # Add Boilinger Bands + fig = add_filled_traces(fig, 1, data, 'bb_lowerband', 'bb_upperband', + label="Boillinger Band") + # prevent bb_lower and bb_upper from plotting + try: + del plot_config['main_plot']['bb_lowerband'] + del plot_config['main_plot']['bb_upperband'] + except KeyError: + pass # Add indicators to main plot fig = add_indicators(fig=fig, row=1, indicators=plot_config['main_plot'], data=data) + # fill area between indicators ( 'fill_to': 'other_indicator') + for indicator, ind_conf in plot_config['main_plot'].items(): + if 'fill_to' in ind_conf: + label = ind_conf.get('fill_label', '') + fill_color = ind_conf.get('fill_color', 'rgba(0,176,246,0.2)') + fig = add_filled_traces(fig, 1, data, indicator, + ind_conf['fill_to'], label=label, + fill_color=fill_color) + fig = plot_trades(fig, trades) # Volume goes to row 2 @@ -412,11 +408,20 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra fig.add_trace(volume, 2, 1) # Add indicators to separate row - for i, name in enumerate(plot_config['subplots']): - fig = add_indicators(fig=fig, row=3 + i, - indicators=plot_config['subplots'][name], + for i, label in enumerate(plot_config['subplots']): + sub_config = plot_config['subplots'][label] + row = 3 + i + fig = add_indicators(fig=fig, row=row, indicators=sub_config, data=data) + # fill area between indicators ( 'fill_to': 'other_indicator') + for indicator, ind_config in sub_config.items(): + if 'fill_to' in ind_config: + label = ind_config.get('fill_label', '') + fill_color = ind_config.get('fill_color', 'rgba(0,176,246,0.2)') + fig = add_filled_traces(fig, row, data, indicator, + ind_config['fill_to'], label=label, + fill_color=fill_color) return fig From d901a86165736df19eae7495f631de7f66b751d3 Mon Sep 17 00:00:00 2001 From: Christof Date: Sat, 19 Dec 2020 17:19:32 +0100 Subject: [PATCH 09/19] typo --- freqtrade/plot/plotting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 62a667e03..b77b76e1a 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -375,7 +375,7 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra # Add Boilinger Bands fig = add_filled_traces(fig, 1, data, 'bb_lowerband', 'bb_upperband', - label="Boillinger Band") + label="Bollinger Band") # prevent bb_lower and bb_upper from plotting try: del plot_config['main_plot']['bb_lowerband'] From 16baca5eeb2bca0ff79746d8dc420b6042428a7a Mon Sep 17 00:00:00 2001 From: Christof Date: Sat, 19 Dec 2020 17:42:22 +0100 Subject: [PATCH 10/19] fixed: too complex warning --- freqtrade/plot/plotting.py | 46 +++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index b77b76e1a..7d17585a1 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -263,10 +263,10 @@ def create_plotconfig(indicators1: List[str], indicators2: List[str], return plot_config -def add_filled_traces(fig, row: int, data: pd.DataFrame, indicator_a: str, +def plot_area_between(fig, row: int, data: pd.DataFrame, indicator_a: str, indicator_b: str, label: str = "", fill_color: str = "rgba(0,176,246,0.2)") -> make_subplots: - """ Adds plots for two traces, which are filled between to fig. + """ Plots the area between two traces and adds it to fig. :param fig: Plot figure to append to :param row: row number for this plot :param data: candlestick DataFrame @@ -290,6 +290,25 @@ def add_filled_traces(fig, row: int, data: pd.DataFrame, indicator_a: str, return fig +def add_areas(fig, row: int, data: pd.DataFrame, indicators) -> make_subplots: + """ Adds all areas (from plot_config) to fig. + :param fig: Plot figure to append to + :param row: row number for this plot + :param data: candlestick DataFrame + :param indicators: dict with indicators. ie.: plot_config['main_plot'] or + plot_config['subplots'][subplot_label] + :return: fig with added filled_traces plot + """ + for indicator, ind_conf in indicators.items(): + if 'fill_to' in ind_conf: + label = ind_conf.get('fill_label', '') + fill_color = ind_conf.get('fill_color', 'rgba(0,176,246,0.2)') + fig = plot_area_between(fig, row, data, indicator, + ind_conf['fill_to'], label=label, + fill_color=fill_color) + return fig + + def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFrame = None, *, indicators1: List[str] = [], indicators2: List[str] = [], @@ -373,8 +392,8 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra else: logger.warning("No sell-signals found.") - # Add Boilinger Bands - fig = add_filled_traces(fig, 1, data, 'bb_lowerband', 'bb_upperband', + # Add Bollinger Bands + fig = plot_area_between(fig, 1, data, 'bb_lowerband', 'bb_upperband', label="Bollinger Band") # prevent bb_lower and bb_upper from plotting try: @@ -385,15 +404,8 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra # Add indicators to main plot fig = add_indicators(fig=fig, row=1, indicators=plot_config['main_plot'], data=data) - # fill area between indicators ( 'fill_to': 'other_indicator') - for indicator, ind_conf in plot_config['main_plot'].items(): - if 'fill_to' in ind_conf: - label = ind_conf.get('fill_label', '') - fill_color = ind_conf.get('fill_color', 'rgba(0,176,246,0.2)') - fig = add_filled_traces(fig, 1, data, indicator, - ind_conf['fill_to'], label=label, - fill_color=fill_color) + fig = add_areas(fig, 1, data, plot_config['main_plot']) fig = plot_trades(fig, trades) @@ -413,15 +425,9 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra row = 3 + i fig = add_indicators(fig=fig, row=row, indicators=sub_config, data=data) - # fill area between indicators ( 'fill_to': 'other_indicator') - for indicator, ind_config in sub_config.items(): - if 'fill_to' in ind_config: - label = ind_config.get('fill_label', '') - fill_color = ind_config.get('fill_color', 'rgba(0,176,246,0.2)') - fig = add_filled_traces(fig, row, data, indicator, - ind_config['fill_to'], label=label, - fill_color=fill_color) + fig = add_areas(fig, row, data, sub_config) + return fig From 5b2902fcbcdd9ff4a3ed9510f2f064becaa0f3e7 Mon Sep 17 00:00:00 2001 From: Christof Date: Sat, 19 Dec 2020 17:48:08 +0100 Subject: [PATCH 11/19] cleanup --- freqtrade/plot/plotting.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 7d17585a1..0f8c99852 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -266,7 +266,7 @@ def create_plotconfig(indicators1: List[str], indicators2: List[str], def plot_area_between(fig, row: int, data: pd.DataFrame, indicator_a: str, indicator_b: str, label: str = "", fill_color: str = "rgba(0,176,246,0.2)") -> make_subplots: - """ Plots the area between two traces and adds it to fig. + """ Creates plot for the area between two traces and adds it to fig. :param fig: Plot figure to append to :param row: row number for this plot :param data: candlestick DataFrame @@ -277,21 +277,21 @@ def plot_area_between(fig, row: int, data: pd.DataFrame, indicator_a: str, :return: fig with added filled_traces plot """ if indicator_a in data and indicator_b in data: + line = {'color': 'rgba(255,255,255,0)'} # TODO: Figure out why scattergl causes problems plotly/plotly.js#2284 trace_a = go.Scatter(x=data.date, y=data[indicator_a], showlegend=False, - line={'color': 'rgba(255,255,255,0)'}) - + line=line) trace_b = go.Scatter(x=data.date, y=data[indicator_b], name=label, fill="tonexty", fillcolor=fill_color, - line={'color': 'rgba(255,255,255,0)'}) + line=line) fig.add_trace(trace_a, row, 1) fig.add_trace(trace_b, row, 1) return fig def add_areas(fig, row: int, data: pd.DataFrame, indicators) -> make_subplots: - """ Adds all areas (from plot_config) to fig. + """ Adds all area plots (specified in plot_config) to fig. :param fig: Plot figure to append to :param row: row number for this plot :param data: candlestick DataFrame From 8b248780231c9b5306a1f42ef0764402f0ffd12e Mon Sep 17 00:00:00 2001 From: Christof Date: Sat, 19 Dec 2020 18:21:26 +0100 Subject: [PATCH 12/19] plot_config documentation for fill_to, fill_label, fill_color --- docs/plotting.md | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/plotting.md b/docs/plotting.md index f794cdedc..30844606c 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -168,7 +168,7 @@ Additional features when using plot_config include: * Specify colors per indicator * Specify additional subplots -* Specify idicator pairs to fill area in between +* Specify indicator pairs to fill area in between The sample plot configuration below specifies fixed colors for the indicators. Otherwise consecutive plots may produce different colorschemes each time, making comparisons difficult. It also allows multiple subplots to display both MACD and RSI at the same time. @@ -184,29 +184,32 @@ Sample configuration with inline comments explaining the process: 'ema50': {'color': '#CCCCCC'}, # By omitting color, a random color is selected. 'sar': {}, + # fill area between senkou_a and senkou_b + 'senkou_a': { + 'color': 'green', #optional + 'fill_to': 'senkou_b', + 'fill_label': 'Ichimoku Cloud' #optional, + 'fill_color': 'rgba(255,76,46,0.2)', #optional + }, + # plot senkou_b, too. Not only the area to it. + 'senkou_b': {} }, 'subplots': { # Create subplot MACD "MACD": { - 'macd': {'color': 'blue'}, - 'macdsignal': {'color': 'orange'}, + 'macd': {'color': 'blue', 'fill_to': 'macdhist'}, + 'macdsignal': {'color': 'orange'} }, # Additional subplot RSI "RSI": { - 'rsi': {'color': 'red'}, + 'rsi': {'color': 'red'} } - }, - 'fill_area': { - "Ichimoku Cloud": { - 'traces': ('senkou_a', 'senkou_b'), - 'color': 'rgba(0,176,246,0.2)', - }, - } + } } -``` +``` !!! Note - The above configuration assumes that `ema10`, `ema50`, `macd`, `macdsignal` and `rsi` are columns in the DataFrame created by the strategy. + The above configuration assumes that `ema10`, `ema50`, `senkou_a`, `senkou_b`, `macd`, `macdsignal` and `rsi` are columns in the DataFrame created by the strategy. ## Plot profit From 43091a26ce19ce4bdfadf012ff6e9ff83b559101 Mon Sep 17 00:00:00 2001 From: Christof Date: Sat, 19 Dec 2020 20:32:13 +0100 Subject: [PATCH 13/19] simple tests --- freqtrade/plot/plotting.py | 21 +++++++++++++++----- tests/test_plotting.py | 40 +++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 0f8c99852..6e92bced8 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -301,11 +301,22 @@ def add_areas(fig, row: int, data: pd.DataFrame, indicators) -> make_subplots: """ for indicator, ind_conf in indicators.items(): if 'fill_to' in ind_conf: - label = ind_conf.get('fill_label', '') - fill_color = ind_conf.get('fill_color', 'rgba(0,176,246,0.2)') - fig = plot_area_between(fig, row, data, indicator, - ind_conf['fill_to'], label=label, - fill_color=fill_color) + indicator_b = ind_conf['fill_to'] + if indicator in data and indicator_b in data: + label = ind_conf.get('fill_label', '') + fill_color = ind_conf.get('fill_color', 'rgba(0,176,246,0.2)') + fig = plot_area_between(fig, row, data, indicator, indicator_b, + label=label, fill_color=fill_color) + elif indicator not in data: + logger.info( + 'Indicator "%s" ignored. Reason: This indicator is not ' + 'found in your strategy.', indicator + ) + elif indicator_b not in data: + logger.info( + 'fill_to: "%s" ignored. Reason: This indicator is not ' + 'in your strategy.', indicator_b + ) return fig diff --git a/tests/test_plotting.py b/tests/test_plotting.py index d3f97013d..96eff4c69 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -16,7 +16,8 @@ from freqtrade.exceptions import OperationalException from freqtrade.plot.plotting import (add_indicators, add_profit, create_plotconfig, generate_candlestick_graph, generate_plot_filename, generate_profit_graph, init_plotscript, load_and_plot_trades, - plot_profit, plot_trades, store_plot_file) + plot_profit, plot_trades, store_plot_file, + add_areas) from freqtrade.resolvers import StrategyResolver from tests.conftest import get_args, log_has, log_has_re, patch_exchange @@ -96,6 +97,42 @@ def test_add_indicators(default_conf, testdatadir, caplog): assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog) +def test_add_areas(default_conf, testdatadir, caplog): + pair = "UNITTEST/BTC" + timerange = TimeRange(None, 'line', 0, -1000) + + data = history.load_pair_history(pair=pair, timeframe='1m', + datadir=testdatadir, timerange=timerange) + indicators = {"macd": {"color": "red", + "fill_color": "black", + "fill_to": "macdhist", + "fill_label": "MACD Fill"}} + + default_conf.update({'strategy': 'DefaultStrategy'}) + strategy = StrategyResolver.load_strategy(default_conf) + + # Generate buy/sell signals and indicators + data = strategy.analyze_ticker(data, {'pair': pair}) + fig = generate_empty_figure() + + # indicator mentioned in fill_to does not exist + fig1 = add_areas(fig, 1, data, {'ema10': {'fill_to': 'no_fill_indicator'}}) + assert fig == fig1 + assert log_has_re(r'fill_to: "no_fill_indicator" ignored\..*', caplog) + + # indicator does not exist + fig2 = add_areas(fig, 1, data, {'no_indicator': {'fill_to': 'ema10'}}) + assert fig == fig2 + assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog) + + fig3 = add_areas(fig, 3, data, indicators) + figure = fig3.layout.figure + fill_macd = find_trace_in_fig_data(figure.data, "MACD Fill") + assert isinstance(fill_macd, go.Scatter) + assert fill_macd.yaxis == "y3" + assert fill_macd.fillcolor == "black" + + def test_plot_trades(testdatadir, caplog): fig1 = generate_empty_figure() # nothing happens when no trades are available @@ -136,6 +173,7 @@ def test_plot_trades(testdatadir, caplog): assert trade_sell_loss.text[5] == '-10.4%, stop_loss, 720 min' + def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, testdatadir, caplog): row_mock = mocker.patch('freqtrade.plot.plotting.add_indicators', MagicMock(side_effect=fig_generating_mock)) From f24626e13920377f0208292a4f5326ccf1eca023 Mon Sep 17 00:00:00 2001 From: Christof Date: Sat, 19 Dec 2020 20:42:19 +0100 Subject: [PATCH 14/19] removed too many blank lines --- tests/test_plotting.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 96eff4c69..379889ea1 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -173,7 +173,6 @@ def test_plot_trades(testdatadir, caplog): assert trade_sell_loss.text[5] == '-10.4%, stop_loss, 720 min' - def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, testdatadir, caplog): row_mock = mocker.patch('freqtrade.plot.plotting.add_indicators', MagicMock(side_effect=fig_generating_mock)) From f120c8d6c7c33419b315ebde415f17c1ec242f87 Mon Sep 17 00:00:00 2001 From: Christof Date: Sat, 19 Dec 2020 20:47:25 +0100 Subject: [PATCH 15/19] documentation --- docs/plotting.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/plotting.md b/docs/plotting.md index 30844606c..ed682e44b 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -209,7 +209,8 @@ Sample configuration with inline comments explaining the process: ``` !!! Note - The above configuration assumes that `ema10`, `ema50`, `senkou_a`, `senkou_b`, `macd`, `macdsignal` and `rsi` are columns in the DataFrame created by the strategy. + The above configuration assumes that `ema10`, `ema50`, `senkou_a`, `senkou_b`, + `macd`, `macdsignal`, `macdhist` and `rsi` are columns in the DataFrame created by the strategy. ## Plot profit From fabb31e1bc20a0040609ce180787a98f3d83870f Mon Sep 17 00:00:00 2001 From: Christof Date: Sat, 19 Dec 2020 20:50:15 +0100 Subject: [PATCH 16/19] imports order --- tests/test_plotting.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 379889ea1..5bd9f4f9d 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -13,11 +13,10 @@ from freqtrade.configuration import TimeRange from freqtrade.data import history from freqtrade.data.btanalysis import create_cum_profit, load_backtest_data from freqtrade.exceptions import OperationalException -from freqtrade.plot.plotting import (add_indicators, add_profit, create_plotconfig, +from freqtrade.plot.plotting import (add_areas, add_indicators, add_profit, create_plotconfig, generate_candlestick_graph, generate_plot_filename, generate_profit_graph, init_plotscript, load_and_plot_trades, - plot_profit, plot_trades, store_plot_file, - add_areas) + plot_profit, plot_trades, store_plot_file) from freqtrade.resolvers import StrategyResolver from tests.conftest import get_args, log_has, log_has_re, patch_exchange From c1b8ad723261d89157720d9bfe4c77e46c0bfe5f Mon Sep 17 00:00:00 2001 From: Christof Date: Sat, 19 Dec 2020 21:37:52 +0100 Subject: [PATCH 17/19] renaming, comments, cleanups --- freqtrade/plot/plotting.py | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 6e92bced8..b4adf4049 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -263,10 +263,10 @@ def create_plotconfig(indicators1: List[str], indicators2: List[str], return plot_config -def plot_area_between(fig, row: int, data: pd.DataFrame, indicator_a: str, - indicator_b: str, label: str = "", - fill_color: str = "rgba(0,176,246,0.2)") -> make_subplots: - """ Creates plot for the area between two traces and adds it to fig. +def plot_area(fig, row: int, data: pd.DataFrame, indicator_a: str, + indicator_b: str, label: str = "", + fill_color: str = "rgba(0,176,246,0.2)") -> make_subplots: + """ Creates a plot for the area between two traces and adds it to fig. :param fig: Plot figure to append to :param row: row number for this plot :param data: candlestick DataFrame @@ -277,6 +277,7 @@ def plot_area_between(fig, row: int, data: pd.DataFrame, indicator_a: str, :return: fig with added filled_traces plot """ if indicator_a in data and indicator_b in data: + # make lines invisible to get the area plotted, only. line = {'color': 'rgba(255,255,255,0)'} # TODO: Figure out why scattergl causes problems plotly/plotly.js#2284 trace_a = go.Scatter(x=data.date, y=data[indicator_a], @@ -303,10 +304,11 @@ def add_areas(fig, row: int, data: pd.DataFrame, indicators) -> make_subplots: if 'fill_to' in ind_conf: indicator_b = ind_conf['fill_to'] if indicator in data and indicator_b in data: - label = ind_conf.get('fill_label', '') + label = ind_conf.get('fill_label', + f'{indicator}<>{indicator_b}') fill_color = ind_conf.get('fill_color', 'rgba(0,176,246,0.2)') - fig = plot_area_between(fig, row, data, indicator, indicator_b, - label=label, fill_color=fill_color) + fig = plot_area(fig, row, data, indicator, indicator_b, + label=label, fill_color=fill_color) elif indicator not in data: logger.info( 'Indicator "%s" ignored. Reason: This indicator is not ' @@ -402,25 +404,20 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra fig.add_trace(sells, 1, 1) else: logger.warning("No sell-signals found.") - # Add Bollinger Bands - fig = plot_area_between(fig, 1, data, 'bb_lowerband', 'bb_upperband', - label="Bollinger Band") + fig = plot_area(fig, 1, data, 'bb_lowerband', 'bb_upperband', + label="Bollinger Band") # prevent bb_lower and bb_upper from plotting try: del plot_config['main_plot']['bb_lowerband'] del plot_config['main_plot']['bb_upperband'] except KeyError: pass - - # Add indicators to main plot + # main plot goes to row 1 fig = add_indicators(fig=fig, row=1, indicators=plot_config['main_plot'], data=data) - # fill area between indicators ( 'fill_to': 'other_indicator') fig = add_areas(fig, 1, data, plot_config['main_plot']) - fig = plot_trades(fig, trades) - - # Volume goes to row 2 + # sub plot: Volume goes to row 2 volume = go.Bar( x=data['date'], y=data['volume'], @@ -429,8 +426,7 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra marker_line_color='DarkSlateGrey' ) fig.add_trace(volume, 2, 1) - - # Add indicators to separate row + # Add each sub plot to a separate row for i, label in enumerate(plot_config['subplots']): sub_config = plot_config['subplots'][label] row = 3 + i @@ -438,7 +434,6 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra data=data) # fill area between indicators ( 'fill_to': 'other_indicator') fig = add_areas(fig, row, data, sub_config) - return fig From 3cb559994e108af03239ed4bce3f1769d3cd05b1 Mon Sep 17 00:00:00 2001 From: Christof Date: Sat, 19 Dec 2020 21:47:11 +0100 Subject: [PATCH 18/19] some more test --- tests/test_plotting.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 5bd9f4f9d..42847ca50 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -107,6 +107,10 @@ def test_add_areas(default_conf, testdatadir, caplog): "fill_to": "macdhist", "fill_label": "MACD Fill"}} + ind_no_label = {"macd": {"fill_color": "red", + "fill_to": "macdhist"}} + + ind_plain = {"macd": {"fill_to": "macdhist"}} default_conf.update({'strategy': 'DefaultStrategy'}) strategy = StrategyResolver.load_strategy(default_conf) @@ -124,6 +128,7 @@ def test_add_areas(default_conf, testdatadir, caplog): assert fig == fig2 assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog) + # everythin given in plot config, row 3 fig3 = add_areas(fig, 3, data, indicators) figure = fig3.layout.figure fill_macd = find_trace_in_fig_data(figure.data, "MACD Fill") @@ -131,6 +136,21 @@ def test_add_areas(default_conf, testdatadir, caplog): assert fill_macd.yaxis == "y3" assert fill_macd.fillcolor == "black" + # label missing, row 1 + fig4 = add_areas(fig, 1, data, ind_no_label) + figure = fig4.layout.figure + fill_macd = find_trace_in_fig_data(figure.data, "macd<>macdhist") + assert isinstance(fill_macd, go.Scatter) + assert fill_macd.yaxis == "y" + assert fill_macd.fillcolor == "red" + + # fit_to only + fig5 = add_areas(fig, 1, data, ind_plain) + figure = fig5.layout.figure + fill_macd = find_trace_in_fig_data(figure.data, "macd<>macdhist") + assert isinstance(fill_macd, go.Scatter) + assert fill_macd.yaxis == "y" + def test_plot_trades(testdatadir, caplog): fig1 = generate_empty_figure() From 18a24d75efcbdc19f162d9c4310ae257a5252a3a Mon Sep 17 00:00:00 2001 From: Christof Date: Sat, 19 Dec 2020 22:01:33 +0100 Subject: [PATCH 19/19] cleanup --- freqtrade/plot/plotting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index b4adf4049..497218deb 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -426,7 +426,7 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra marker_line_color='DarkSlateGrey' ) fig.add_trace(volume, 2, 1) - # Add each sub plot to a separate row + # add each sub plot to a separate row for i, label in enumerate(plot_config['subplots']): sub_config = plot_config['subplots'][label] row = 3 + i