From 4b238987b0ae5bfc7ba0b5f8e274cffa4aec6699 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 2 Feb 2022 16:02:54 -0600 Subject: [PATCH 1/4] plot.generate_candlestick_graph Added short equivelent, separating plotting scatter creation to a function --- freqtrade/plot/plotting.py | 75 +++++++++++++++++++------------------- tests/test_plotting.py | 26 +++++++------ 2 files changed, 53 insertions(+), 48 deletions(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 9f6b193d3..1a603b051 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -1,6 +1,6 @@ import logging from pathlib import Path -from typing import Any, Dict, List +from typing import Any, Dict, List, Optional import pandas as pd @@ -385,6 +385,35 @@ def add_areas(fig, row: int, data: pd.DataFrame, indicators) -> make_subplots: return fig +def create_scatter( + data, + column_name, + color, + direction +) -> Optional[go.Scatter]: + + if column_name in data.columns: + df_short = data[data[column_name] == 1] + if len(df_short) > 0: + shorts = go.Scatter( + x=df_short.date, + y=df_short.close, + mode='markers', + name=column_name, + marker=dict( + symbol=f"triangle-{direction}-dot", + size=9, + line=dict(width=1), + color=color, + ) + ) + return shorts + else: + logger.warning(f"No {column_name}-signals found.") + + return None + + def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFrame = None, *, indicators1: List[str] = [], indicators2: List[str] = [], @@ -431,43 +460,15 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra ) fig.add_trace(candles, 1, 1) - if 'enter_long' in data.columns: - df_buy = data[data['enter_long'] == 1] - if len(df_buy) > 0: - buys = go.Scatter( - x=df_buy.date, - y=df_buy.close, - mode='markers', - name='buy', - marker=dict( - symbol='triangle-up-dot', - size=9, - line=dict(width=1), - color='green', - ) - ) - fig.add_trace(buys, 1, 1) - else: - logger.warning("No buy-signals found.") + longs = create_scatter(data, 'enter_long', 'green', 'up') + exit_longs = create_scatter(data, 'exit_long', 'red', 'down') + shorts = create_scatter(data, 'enter_short', 'blue', 'down') + exit_shorts = create_scatter(data, 'exit_short', 'violet', 'up') + + for scatter in [longs, exit_longs, shorts, exit_shorts]: + if scatter: + fig.add_trace(scatter, 1, 1) - if 'exit_long' in data.columns: - df_sell = data[data['exit_long'] == 1] - if len(df_sell) > 0: - sells = go.Scatter( - x=df_sell.date, - y=df_sell.close, - mode='markers', - name='sell', - marker=dict( - symbol='triangle-down-dot', - size=9, - line=dict(width=1), - color='red', - ) - ) - fig.add_trace(sells, 1, 1) - else: - logger.warning("No sell-signals found.") # Add Bollinger Bands fig = plot_area(fig, 1, data, 'bb_lowerband', 'bb_upperband', label="Bollinger Band") diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 5f8b20251..940639465 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -202,6 +202,8 @@ def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, t datadir=testdatadir, timerange=timerange) data['enter_long'] = 0 data['exit_long'] = 0 + data['enter_short'] = 0 + data['exit_short'] = 0 indicators1 = [] indicators2 = [] @@ -222,8 +224,10 @@ def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, t assert row_mock.call_count == 2 assert trades_mock.call_count == 1 - assert log_has("No buy-signals found.", caplog) - assert log_has("No sell-signals found.", caplog) + assert log_has("No enter_long-signals found.", caplog) + assert log_has("No exit_long-signals found.", caplog) + assert log_has("No enter_short-signals found.", caplog) + assert log_has("No exit_short-signals found.", caplog) def test_generate_candlestick_graph_no_trades(default_conf, mocker, testdatadir): @@ -249,7 +253,7 @@ def test_generate_candlestick_graph_no_trades(default_conf, mocker, testdatadir) assert fig.layout.title.text == pair figure = fig.layout.figure - assert len(figure.data) == 6 + assert len(figure.data) == 8 # Candlesticks are plotted first candles = find_trace_in_fig_data(figure.data, "Price") assert isinstance(candles, go.Candlestick) @@ -257,15 +261,15 @@ def test_generate_candlestick_graph_no_trades(default_conf, mocker, testdatadir) volume = find_trace_in_fig_data(figure.data, "Volume") assert isinstance(volume, go.Bar) - buy = find_trace_in_fig_data(figure.data, "buy") - assert isinstance(buy, go.Scatter) - # All entry-signals should be plotted - assert int(data['enter_long'].sum()) == len(buy.x) + enter_long = find_trace_in_fig_data(figure.data, "enter_long") + assert isinstance(enter_long, go.Scatter) + # All buy-signals should be plotted + assert int(data['enter_long'].sum()) == len(enter_long.x) - sell = find_trace_in_fig_data(figure.data, "sell") - assert isinstance(sell, go.Scatter) - # All entry-signals should be plotted - assert int(data['exit_long'].sum()) == len(sell.x) + exit_long = find_trace_in_fig_data(figure.data, "exit_long") + assert isinstance(exit_long, go.Scatter) + # All buy-signals should be plotted + assert int(data['exit_long'].sum()) == len(exit_long.x) assert find_trace_in_fig_data(figure.data, "Bollinger Band") From e3a624cf682370bc217614055ccd4e1429dce826 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 1 Apr 2022 06:44:17 +0200 Subject: [PATCH 2/4] Fix futures plotting --- freqtrade/plot/plotting.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 1a603b051..c8d32be9d 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -11,6 +11,7 @@ from freqtrade.data.btanalysis import (analyze_trade_parallelism, calculate_max_ from freqtrade.data.converter import trim_dataframe from freqtrade.data.dataprovider import DataProvider from freqtrade.data.history import get_timerange, load_data +from freqtrade.enums.candletype import CandleType from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_prev_date, timeframe_to_seconds from freqtrade.misc import pair_to_filename @@ -52,6 +53,7 @@ def init_plotscript(config, markets: List, startup_candles: int = 0): timerange=timerange, startup_candles=startup_candles, data_format=config.get('dataformat_ohlcv', 'json'), + candle_type=config.get('candle_type_def', CandleType.SPOT) ) if startup_candles and data: From fe3260aae2ebcc05f032d02e375d0b75ec665ac0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 1 Apr 2022 08:51:11 +0200 Subject: [PATCH 3/4] Remove false statement about plotting futures data --- docs/plotting.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/plotting.md b/docs/plotting.md index bbe9307c8..df988c578 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -276,10 +276,6 @@ def plot_config(self): !!! Note "Trade position adjustments" If `position_adjustment_enable` / `adjust_trade_position()` is used, the trade initial buy price is averaged over multiple orders and the trade start price will most likely appear outside the candle range. -!!! Note "Futures / Margin trading" - `plot-dataframe` does not support Futures / short trades, so these trades will simply be missing, and it's unlikely we'll be adding this functionality to this command. - Please use freqUI instead by starting freqtrade in [webserver mode](utils.md#webserver-mode) and use the Chart page to plot your dataframe. - ## Plot profit ![plot-profit](assets/plot-profit.png) From 775305e9c47809922347c71824a91f3ddb5f70cc Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 1 Apr 2022 08:57:58 +0200 Subject: [PATCH 4/4] Use correct candleconfig in plot endpoint --- freqtrade/plot/plotting.py | 2 +- freqtrade/rpc/rpc.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index c8d32be9d..5ef955354 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -11,7 +11,7 @@ from freqtrade.data.btanalysis import (analyze_trade_parallelism, calculate_max_ from freqtrade.data.converter import trim_dataframe from freqtrade.data.dataprovider import DataProvider from freqtrade.data.history import get_timerange, load_data -from freqtrade.enums.candletype import CandleType +from freqtrade.enums import CandleType from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_prev_date, timeframe_to_seconds from freqtrade.misc import pair_to_filename diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 94bc513fb..1388a9bda 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -18,7 +18,8 @@ from freqtrade import __version__ from freqtrade.configuration.timerange import TimeRange from freqtrade.constants import CANCEL_REASON, DATETIME_PRINT_FORMAT from freqtrade.data.history import load_data -from freqtrade.enums import ExitCheckTuple, ExitType, SignalDirection, State, TradingMode +from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, SignalDirection, State, + TradingMode) from freqtrade.exceptions import ExchangeError, PricingError from freqtrade.exchange import timeframe_to_minutes, timeframe_to_msecs from freqtrade.loggers import bufferHandler @@ -1057,6 +1058,7 @@ class RPC: timeframe=timeframe, timerange=timerange_parsed, data_format=config.get('dataformat_ohlcv', 'json'), + candle_type=config.get('candle_type_def', CandleType.SPOT) ) if pair not in _data: raise RPCException(f"No data for {pair}, {timeframe} in {timerange} found.")