Merge pull request #6622 from freqtrade/short_plot

Short plot
This commit is contained in:
Matthias 2022-04-01 09:18:37 +02:00 committed by GitHub
commit d1ea5ea856
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 58 additions and 53 deletions

View File

@ -276,10 +276,6 @@ def plot_config(self):
!!! Note "Trade position adjustments" !!! 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. 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
![plot-profit](assets/plot-profit.png) ![plot-profit](assets/plot-profit.png)

View File

@ -1,6 +1,6 @@
import logging import logging
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List from typing import Any, Dict, List, Optional
import pandas as pd import pandas as pd
@ -11,6 +11,7 @@ from freqtrade.data.btanalysis import (analyze_trade_parallelism, calculate_max_
from freqtrade.data.converter import trim_dataframe from freqtrade.data.converter import trim_dataframe
from freqtrade.data.dataprovider import DataProvider from freqtrade.data.dataprovider import DataProvider
from freqtrade.data.history import get_timerange, load_data from freqtrade.data.history import get_timerange, load_data
from freqtrade.enums import CandleType
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.exchange import timeframe_to_prev_date, timeframe_to_seconds from freqtrade.exchange import timeframe_to_prev_date, timeframe_to_seconds
from freqtrade.misc import pair_to_filename from freqtrade.misc import pair_to_filename
@ -52,6 +53,7 @@ def init_plotscript(config, markets: List, startup_candles: int = 0):
timerange=timerange, timerange=timerange,
startup_candles=startup_candles, startup_candles=startup_candles,
data_format=config.get('dataformat_ohlcv', 'json'), data_format=config.get('dataformat_ohlcv', 'json'),
candle_type=config.get('candle_type_def', CandleType.SPOT)
) )
if startup_candles and data: if startup_candles and data:
@ -385,6 +387,35 @@ def add_areas(fig, row: int, data: pd.DataFrame, indicators) -> make_subplots:
return fig 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, *, def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFrame = None, *,
indicators1: List[str] = [], indicators1: List[str] = [],
indicators2: List[str] = [], indicators2: List[str] = [],
@ -431,43 +462,15 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra
) )
fig.add_trace(candles, 1, 1) fig.add_trace(candles, 1, 1)
if 'enter_long' in data.columns: longs = create_scatter(data, 'enter_long', 'green', 'up')
df_buy = data[data['enter_long'] == 1] exit_longs = create_scatter(data, 'exit_long', 'red', 'down')
if len(df_buy) > 0: shorts = create_scatter(data, 'enter_short', 'blue', 'down')
buys = go.Scatter( exit_shorts = create_scatter(data, 'exit_short', 'violet', 'up')
x=df_buy.date,
y=df_buy.close, for scatter in [longs, exit_longs, shorts, exit_shorts]:
mode='markers', if scatter:
name='buy', fig.add_trace(scatter, 1, 1)
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.")
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 # Add Bollinger Bands
fig = plot_area(fig, 1, data, 'bb_lowerband', 'bb_upperband', fig = plot_area(fig, 1, data, 'bb_lowerband', 'bb_upperband',
label="Bollinger Band") label="Bollinger Band")

View File

@ -18,7 +18,8 @@ from freqtrade import __version__
from freqtrade.configuration.timerange import TimeRange from freqtrade.configuration.timerange import TimeRange
from freqtrade.constants import CANCEL_REASON, DATETIME_PRINT_FORMAT from freqtrade.constants import CANCEL_REASON, DATETIME_PRINT_FORMAT
from freqtrade.data.history import load_data 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.exceptions import ExchangeError, PricingError
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_msecs from freqtrade.exchange import timeframe_to_minutes, timeframe_to_msecs
from freqtrade.loggers import bufferHandler from freqtrade.loggers import bufferHandler
@ -1057,6 +1058,7 @@ class RPC:
timeframe=timeframe, timeframe=timeframe,
timerange=timerange_parsed, timerange=timerange_parsed,
data_format=config.get('dataformat_ohlcv', 'json'), data_format=config.get('dataformat_ohlcv', 'json'),
candle_type=config.get('candle_type_def', CandleType.SPOT)
) )
if pair not in _data: if pair not in _data:
raise RPCException(f"No data for {pair}, {timeframe} in {timerange} found.") raise RPCException(f"No data for {pair}, {timeframe} in {timerange} found.")

View File

@ -202,6 +202,8 @@ def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, t
datadir=testdatadir, timerange=timerange) datadir=testdatadir, timerange=timerange)
data['enter_long'] = 0 data['enter_long'] = 0
data['exit_long'] = 0 data['exit_long'] = 0
data['enter_short'] = 0
data['exit_short'] = 0
indicators1 = [] indicators1 = []
indicators2 = [] 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 row_mock.call_count == 2
assert trades_mock.call_count == 1 assert trades_mock.call_count == 1
assert log_has("No buy-signals found.", caplog) assert log_has("No enter_long-signals found.", caplog)
assert log_has("No sell-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): 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 assert fig.layout.title.text == pair
figure = fig.layout.figure figure = fig.layout.figure
assert len(figure.data) == 6 assert len(figure.data) == 8
# Candlesticks are plotted first # Candlesticks are plotted first
candles = find_trace_in_fig_data(figure.data, "Price") candles = find_trace_in_fig_data(figure.data, "Price")
assert isinstance(candles, go.Candlestick) 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") volume = find_trace_in_fig_data(figure.data, "Volume")
assert isinstance(volume, go.Bar) assert isinstance(volume, go.Bar)
buy = find_trace_in_fig_data(figure.data, "buy") enter_long = find_trace_in_fig_data(figure.data, "enter_long")
assert isinstance(buy, go.Scatter) assert isinstance(enter_long, go.Scatter)
# All entry-signals should be plotted # All buy-signals should be plotted
assert int(data['enter_long'].sum()) == len(buy.x) assert int(data['enter_long'].sum()) == len(enter_long.x)
sell = find_trace_in_fig_data(figure.data, "sell") exit_long = find_trace_in_fig_data(figure.data, "exit_long")
assert isinstance(sell, go.Scatter) assert isinstance(exit_long, go.Scatter)
# All entry-signals should be plotted # All buy-signals should be plotted
assert int(data['exit_long'].sum()) == len(sell.x) assert int(data['exit_long'].sum()) == len(exit_long.x)
assert find_trace_in_fig_data(figure.data, "Bollinger Band") assert find_trace_in_fig_data(figure.data, "Bollinger Band")