freqtrade_origin/tests/test_plotting.py

569 lines
20 KiB
Python
Raw Normal View History

from copy import deepcopy
2019-06-10 18:17:23 +00:00
from unittest.mock import MagicMock
2020-03-03 19:18:38 +00:00
import pandas as pd
import plotly.graph_objects as go
2019-09-03 05:05:48 +00:00
import pytest
from plotly.subplots import make_subplots
2019-06-10 18:17:23 +00:00
2020-03-03 19:18:38 +00:00
from freqtrade.commands import start_plot_dataframe, start_plot_profit
from freqtrade.configuration import TimeRange
2019-06-10 18:17:23 +00:00
from freqtrade.data import history
2022-04-30 12:47:27 +00:00
from freqtrade.data.btanalysis import load_backtest_data
from freqtrade.data.metrics import create_cum_profit
from freqtrade.exceptions import OperationalException
2024-05-12 13:08:40 +00:00
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,
)
from freqtrade.resolvers import StrategyResolver
from tests.conftest import get_args, log_has, log_has_re, patch_exchange
2019-06-10 18:17:23 +00:00
2019-06-16 08:32:12 +00:00
2019-06-10 18:17:23 +00:00
def fig_generating_mock(fig, *args, **kwargs):
2024-05-12 13:29:14 +00:00
"""Return Fig - used to mock add_indicators and plot_trades"""
2019-06-10 18:17:23 +00:00
return fig
2019-06-11 04:45:36 +00:00
def find_trace_in_fig_data(data, search_string: str):
2019-07-14 18:14:35 +00:00
matches = (d for d in data if d.name == search_string)
2019-06-11 04:45:36 +00:00
return next(matches)
2019-09-05 20:00:16 +00:00
def generate_empty_figure():
return make_subplots(
2019-06-16 08:32:12 +00:00
rows=3,
cols=1,
shared_xaxes=True,
row_width=[1, 1, 4],
vertical_spacing=0.0001,
)
2019-06-16 12:03:55 +00:00
2019-09-07 18:56:03 +00:00
def test_init_plotscript(default_conf, mocker, testdatadir):
2024-05-12 13:29:14 +00:00
default_conf["timerange"] = "20180110-20180112"
default_conf["trade_source"] = "file"
default_conf["timeframe"] = "5m"
default_conf["exportfilename"] = testdatadir / "backtest-result.json"
2021-01-12 00:13:58 +00:00
supported_markets = ["TRX/BTC", "ADA/BTC"]
ret = init_plotscript(default_conf, supported_markets)
assert "ohlcv" in ret
2019-06-30 11:01:12 +00:00
assert "trades" in ret
assert "pairs" in ret
2024-05-12 13:29:14 +00:00
assert "timerange" in ret
2019-06-30 11:01:12 +00:00
2024-05-12 13:29:14 +00:00
default_conf["pairs"] = ["TRX/BTC", "ADA/BTC"]
2021-01-12 00:13:58 +00:00
ret = init_plotscript(default_conf, supported_markets, 20)
assert "ohlcv" in ret
assert "TRX/BTC" in ret["ohlcv"]
assert "ADA/BTC" in ret["ohlcv"]
2019-06-30 11:01:12 +00:00
2019-09-07 18:56:03 +00:00
def test_add_indicators(default_conf, testdatadir, caplog):
2019-06-16 08:32:12 +00:00
pair = "UNITTEST/BTC"
timerange = TimeRange()
2019-06-16 08:32:12 +00:00
2024-05-12 13:29:14 +00:00
data = history.load_pair_history(
pair=pair, timeframe="1m", datadir=testdatadir, timerange=timerange
)
indicators1 = {"ema10": {}}
indicators2 = {"macd": {"color": "red"}}
2019-06-16 08:32:12 +00:00
strategy = StrategyResolver.load_strategy(default_conf)
# Generate entry/exit signals and indicators
2024-05-12 13:29:14 +00:00
data = strategy.analyze_ticker(data, {"pair": pair})
2019-09-05 20:00:16 +00:00
fig = generate_empty_figure()
2019-06-16 08:32:12 +00:00
# Row 1
2019-06-30 07:44:50 +00:00
fig1 = add_indicators(fig=deepcopy(fig), row=1, indicators=indicators1, data=data)
2019-06-16 08:32:12 +00:00
figure = fig1.layout.figure
ema10 = find_trace_in_fig_data(figure.data, "ema10")
assert isinstance(ema10, go.Scatter)
assert ema10.yaxis == "y"
2019-06-30 07:44:50 +00:00
fig2 = add_indicators(fig=deepcopy(fig), row=3, indicators=indicators2, data=data)
2019-06-16 08:32:12 +00:00
figure = fig2.layout.figure
macd = find_trace_in_fig_data(figure.data, "macd")
assert isinstance(macd, go.Scatter)
assert macd.yaxis == "y3"
assert macd.line.color == "red"
2019-06-16 08:32:12 +00:00
# No indicator found
2024-05-12 13:29:14 +00:00
fig3 = add_indicators(fig=deepcopy(fig), row=3, indicators={"no_indicator": {}}, data=data)
2019-06-16 08:32:12 +00:00
assert fig == fig3
2019-08-11 18:17:39 +00:00
assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog)
2019-06-10 18:17:23 +00:00
2020-12-19 19:32:13 +00:00
def test_add_areas(default_conf, testdatadir, caplog):
pair = "UNITTEST/BTC"
2024-05-12 13:29:14 +00:00
timerange = TimeRange(None, "line", 0, -1000)
2020-12-19 19:32:13 +00:00
2024-05-12 13:29:14 +00:00
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",
}
}
2020-12-19 19:32:13 +00:00
2024-05-12 13:29:14 +00:00
ind_no_label = {"macd": {"fill_color": "red", "fill_to": "macdhist"}}
2020-12-19 20:47:11 +00:00
ind_plain = {"macd": {"fill_to": "macdhist"}}
2020-12-19 19:32:13 +00:00
strategy = StrategyResolver.load_strategy(default_conf)
# Generate entry/exit signals and indicators
2024-05-12 13:29:14 +00:00
data = strategy.analyze_ticker(data, {"pair": pair})
2020-12-19 19:32:13 +00:00
fig = generate_empty_figure()
# indicator mentioned in fill_to does not exist
2024-05-12 13:29:14 +00:00
fig1 = add_areas(fig, 1, data, {"ema10": {"fill_to": "no_fill_indicator"}})
2020-12-19 19:32:13 +00:00
assert fig == fig1
assert log_has_re(r'fill_to: "no_fill_indicator" ignored\..*', caplog)
# indicator does not exist
2024-05-12 13:29:14 +00:00
fig2 = add_areas(fig, 1, data, {"no_indicator": {"fill_to": "ema10"}})
2020-12-19 19:32:13 +00:00
assert fig == fig2
assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog)
2024-04-18 20:51:25 +00:00
# everything given in plot config, row 3
2020-12-19 19:32:13 +00:00
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"
2020-12-19 20:47:11 +00:00
# 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"
2020-12-19 19:32:13 +00:00
2019-09-07 18:56:03 +00:00
def test_plot_trades(testdatadir, caplog):
2019-09-06 22:59:42 +00:00
fig1 = generate_empty_figure()
2019-06-16 08:32:12 +00:00
# nothing happens when no trades are available
fig = plot_trades(fig1, None)
assert fig == fig1
2019-08-11 18:17:39 +00:00
assert log_has("No trades found.", caplog)
2019-06-16 17:53:48 +00:00
pair = "ADA/BTC"
filename = testdatadir / "backtest_results/backtest-result.json"
2019-06-16 17:53:48 +00:00
trades = load_backtest_data(filename)
2024-05-12 13:29:14 +00:00
trades = trades.loc[trades["pair"] == pair]
2019-06-16 08:32:12 +00:00
2019-06-16 17:53:48 +00:00
fig = plot_trades(fig, trades)
figure = fig1.layout.figure
# Check entry - color, should be in first graph, ...
2024-05-12 13:29:14 +00:00
trade_entries = find_trace_in_fig_data(figure.data, "Trade entry")
assert isinstance(trade_entries, go.Scatter)
2024-05-12 13:29:14 +00:00
assert trade_entries.yaxis == "y"
assert len(trades) == len(trade_entries.x)
2024-05-12 13:29:14 +00:00
assert trade_entries.marker.color == "cyan"
assert trade_entries.marker.symbol == "circle-open"
assert trade_entries.text[0] == "3.99%, buy_tag, roi, 15 min"
2024-05-12 13:29:14 +00:00
trade_exit = find_trace_in_fig_data(figure.data, "Exit - Profit")
assert isinstance(trade_exit, go.Scatter)
2024-05-12 13:29:14 +00:00
assert trade_exit.yaxis == "y"
assert len(trades.loc[trades["profit_ratio"] > 0]) == len(trade_exit.x)
assert trade_exit.marker.color == "green"
assert trade_exit.marker.symbol == "square-open"
assert trade_exit.text[0] == "3.99%, buy_tag, roi, 15 min"
2024-05-12 13:29:14 +00:00
trade_sell_loss = find_trace_in_fig_data(figure.data, "Exit - Loss")
assert isinstance(trade_sell_loss, go.Scatter)
2024-05-12 13:29:14 +00:00
assert trade_sell_loss.yaxis == "y"
assert len(trades.loc[trades["profit_ratio"] <= 0]) == len(trade_sell_loss.x)
assert trade_sell_loss.marker.color == "red"
assert trade_sell_loss.marker.symbol == "square-open"
assert trade_sell_loss.text[5] == "-10.45%, stop_loss, 720 min"
2019-06-10 18:17:23 +00:00
2019-09-07 18:56:03 +00:00
def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, testdatadir, caplog):
2024-05-12 13:29:14 +00:00
row_mock = mocker.patch(
"freqtrade.plot.plotting.add_indicators", MagicMock(side_effect=fig_generating_mock)
)
trades_mock = mocker.patch(
"freqtrade.plot.plotting.plot_trades", MagicMock(side_effect=fig_generating_mock)
)
2019-06-10 18:17:23 +00:00
2019-06-11 04:45:36 +00:00
pair = "UNITTEST/BTC"
2024-05-12 13:29:14 +00:00
timerange = TimeRange(None, "line", 0, -1000)
data = history.load_pair_history(
pair=pair, timeframe="1m", datadir=testdatadir, timerange=timerange
)
data["enter_long"] = 0
data["exit_long"] = 0
data["enter_short"] = 0
data["exit_short"] = 0
2019-06-11 04:45:36 +00:00
indicators1 = []
indicators2 = []
2024-05-12 13:29:14 +00:00
fig = generate_candlestick_graph(
pair=pair, data=data, trades=None, indicators1=indicators1, indicators2=indicators2
)
2019-06-11 04:45:36 +00:00
assert isinstance(fig, go.Figure)
assert fig.layout.title.text == pair
figure = fig.layout.figure
assert len(figure.data) == 2
# Candlesticks are plotted first
candles = find_trace_in_fig_data(figure.data, "Price")
assert isinstance(candles, go.Candlestick)
volume = find_trace_in_fig_data(figure.data, "Volume")
assert isinstance(volume, go.Bar)
assert row_mock.call_count == 2
assert trades_mock.call_count == 1
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)
2019-06-11 04:45:36 +00:00
2019-09-07 18:56:03 +00:00
def test_generate_candlestick_graph_no_trades(default_conf, mocker, testdatadir):
2024-05-12 13:29:14 +00:00
row_mock = mocker.patch(
"freqtrade.plot.plotting.add_indicators", MagicMock(side_effect=fig_generating_mock)
)
trades_mock = mocker.patch(
"freqtrade.plot.plotting.plot_trades", MagicMock(side_effect=fig_generating_mock)
)
pair = "UNITTEST/BTC"
timerange = TimeRange(None, "line", 0, -1000)
data = history.load_pair_history(
pair=pair, timeframe="1m", datadir=testdatadir, timerange=timerange
)
2019-06-10 18:17:23 +00:00
strategy = StrategyResolver.load_strategy(default_conf)
2019-06-11 04:45:36 +00:00
# Generate buy/sell signals and indicators
2024-05-12 13:29:14 +00:00
data = strategy.analyze_ticker(data, {"pair": pair})
2019-06-11 04:45:36 +00:00
2019-06-10 18:17:23 +00:00
indicators1 = []
indicators2 = []
2024-05-12 13:29:14 +00:00
fig = generate_candlestick_graph(
pair=pair, data=data, trades=None, indicators1=indicators1, indicators2=indicators2
)
2019-06-10 18:17:23 +00:00
assert isinstance(fig, go.Figure)
2019-06-11 04:45:36 +00:00
assert fig.layout.title.text == pair
2019-06-10 18:17:23 +00:00
figure = fig.layout.figure
2019-06-11 04:45:36 +00:00
assert len(figure.data) == 8
2019-06-10 18:17:23 +00:00
# Candlesticks are plotted first
2019-06-11 04:45:36 +00:00
candles = find_trace_in_fig_data(figure.data, "Price")
assert isinstance(candles, go.Candlestick)
volume = find_trace_in_fig_data(figure.data, "Volume")
assert isinstance(volume, go.Bar)
enter_long = find_trace_in_fig_data(figure.data, "enter_long")
assert isinstance(enter_long, go.Scatter)
# All buy-signals should be plotted
2024-05-12 13:29:14 +00:00
assert int(data["enter_long"].sum()) == len(enter_long.x)
2019-06-11 04:45:36 +00:00
exit_long = find_trace_in_fig_data(figure.data, "exit_long")
assert isinstance(exit_long, go.Scatter)
# All buy-signals should be plotted
2024-05-12 13:29:14 +00:00
assert int(data["exit_long"].sum()) == len(exit_long.x)
2019-06-10 18:17:23 +00:00
2019-10-05 08:32:42 +00:00
assert find_trace_in_fig_data(figure.data, "Bollinger Band")
2019-06-10 18:17:23 +00:00
assert row_mock.call_count == 2
assert trades_mock.call_count == 1
2019-06-16 12:03:55 +00:00
def test_generate_Plot_filename():
fn = generate_plot_filename("UNITTEST/BTC", "5m")
assert fn == "freqtrade-plot-UNITTEST_BTC-5m.html"
2023-05-11 18:09:24 +00:00
def test_generate_plot_file(mocker, caplog, user_dir):
2019-09-05 20:00:16 +00:00
fig = generate_empty_figure()
2019-06-16 12:03:55 +00:00
plot_mock = mocker.patch("freqtrade.plot.plotting.plot", MagicMock())
2024-05-12 13:29:14 +00:00
store_plot_file(
fig, filename="freqtrade-plot-UNITTEST_BTC-5m.html", directory=user_dir / "plot"
)
2019-06-16 12:03:55 +00:00
2023-05-11 18:09:24 +00:00
expected_fn = str(user_dir / "plot/freqtrade-plot-UNITTEST_BTC-5m.html")
2019-06-16 12:03:55 +00:00
assert plot_mock.call_count == 1
assert plot_mock.call_args[0][0] == fig
2024-05-12 13:29:14 +00:00
assert plot_mock.call_args_list[0][1]["filename"] == expected_fn
assert log_has(f"Stored plot as {expected_fn}", caplog)
2019-06-30 08:24:10 +00:00
2019-09-07 18:56:03 +00:00
def test_add_profit(testdatadir):
filename = testdatadir / "backtest_results/backtest-result.json"
2019-06-30 08:24:10 +00:00
bt_data = load_backtest_data(filename)
timerange = TimeRange.parse_timerange("20180110-20180112")
2019-06-30 08:24:10 +00:00
2024-05-12 13:29:14 +00:00
df = history.load_pair_history(
pair="TRX/BTC", timeframe="5m", datadir=testdatadir, timerange=timerange
)
2019-09-06 22:59:42 +00:00
fig = generate_empty_figure()
2019-06-30 08:24:10 +00:00
2024-05-12 13:29:14 +00:00
cum_profits = create_cum_profit(
df.set_index("date"), bt_data[bt_data["pair"] == "TRX/BTC"], "cum_profits", timeframe="5m"
)
2019-06-30 08:24:10 +00:00
2024-05-12 13:29:14 +00:00
fig1 = add_profit(fig, row=2, data=cum_profits, column="cum_profits", name="Profits")
2019-06-30 08:24:10 +00:00
figure = fig1.layout.figure
profits = find_trace_in_fig_data(figure.data, "Profits")
2019-10-05 08:32:42 +00:00
assert isinstance(profits, go.Scatter)
2019-06-30 08:24:10 +00:00
assert profits.yaxis == "y2"
2019-06-30 08:47:55 +00:00
2019-09-07 18:56:03 +00:00
def test_generate_profit_graph(testdatadir):
filename = testdatadir / "backtest_results/backtest-result.json"
2019-06-30 08:47:55 +00:00
trades = load_backtest_data(filename)
timerange = TimeRange.parse_timerange("20180110-20180112")
2020-04-05 12:43:01 +00:00
pairs = ["TRX/BTC", "XLM/BTC"]
2024-05-12 13:29:14 +00:00
trades = trades[trades["close_date"] < pd.Timestamp("2018-01-12", tz="UTC")]
2019-06-30 08:47:55 +00:00
2024-05-12 13:29:14 +00:00
data = history.load_data(datadir=testdatadir, pairs=pairs, timeframe="5m", timerange=timerange)
2024-05-12 13:29:14 +00:00
trades = trades[trades["pair"].isin(pairs)]
2019-06-30 08:47:55 +00:00
2022-04-24 20:37:09 +00:00
fig = generate_profit_graph(
2024-05-12 13:29:14 +00:00
pairs, data, trades, timeframe="5m", stake_currency="BTC", starting_balance=0
)
2019-06-30 08:47:55 +00:00
assert isinstance(fig, go.Figure)
2019-08-24 13:21:16 +00:00
assert fig.layout.title.text == "Freqtrade Profit plot"
2019-08-24 12:49:35 +00:00
assert fig.layout.yaxis.title.text == "Price"
2021-04-25 08:10:09 +00:00
assert fig.layout.yaxis2.title.text == "Profit BTC"
assert fig.layout.yaxis3.title.text == "Profit BTC"
2019-08-24 12:49:35 +00:00
2019-06-30 08:47:55 +00:00
figure = fig.layout.figure
2022-04-24 20:37:09 +00:00
assert len(figure.data) == 8
2019-06-30 08:47:55 +00:00
avgclose = find_trace_in_fig_data(figure.data, "Avg close price")
2019-10-05 08:32:42 +00:00
assert isinstance(avgclose, go.Scatter)
2019-06-30 08:47:55 +00:00
profit = find_trace_in_fig_data(figure.data, "Profit")
2019-10-05 08:32:42 +00:00
assert isinstance(profit, go.Scatter)
drawdown = find_trace_in_fig_data(figure.data, "Max drawdown 73.89%")
assert isinstance(drawdown, go.Scatter)
parallel = find_trace_in_fig_data(figure.data, "Parallel trades")
2022-01-01 15:40:18 +00:00
assert isinstance(parallel, go.Scatter)
2019-06-30 08:47:55 +00:00
2022-01-01 13:40:20 +00:00
underwater = find_trace_in_fig_data(figure.data, "Underwater Plot")
assert isinstance(underwater, go.Scatter)
2019-06-30 08:47:55 +00:00
2022-04-24 20:37:09 +00:00
underwater_relative = find_trace_in_fig_data(figure.data, "Underwater Plot (%)")
assert isinstance(underwater_relative, go.Scatter)
2019-06-30 08:47:55 +00:00
for pair in pairs:
profit_pair = find_trace_in_fig_data(figure.data, f"Profit {pair}")
2019-10-05 08:32:42 +00:00
assert isinstance(profit_pair, go.Scatter)
with pytest.raises(OperationalException, match=r"No trades found.*"):
# Pair cannot be empty - so it's an empty dataframe.
2024-05-12 13:29:14 +00:00
generate_profit_graph(
pairs,
data,
trades.loc[trades["pair"].isnull()],
timeframe="5m",
stake_currency="BTC",
starting_balance=0,
)
def test_start_plot_dataframe(mocker):
2019-09-06 22:59:42 +00:00
aup = mocker.patch("freqtrade.plot.plotting.load_and_plot_trades", MagicMock())
args = [
"plot-dataframe",
2024-05-12 13:29:14 +00:00
"--config",
"tests/testdata/testconfigs/main_test_config.json",
"--pairs",
"ETH/BTC",
]
start_plot_dataframe(get_args(args))
assert aup.call_count == 1
called_config = aup.call_args_list[0][0][0]
assert "pairs" in called_config
2024-05-12 13:29:14 +00:00
assert called_config["pairs"] == ["ETH/BTC"]
2019-08-22 14:43:28 +00:00
2019-09-06 22:59:42 +00:00
def test_load_and_plot_trades(default_conf, mocker, caplog, testdatadir):
patch_exchange(mocker)
2024-05-12 13:29:14 +00:00
default_conf["trade_source"] = "file"
default_conf["exportfilename"] = testdatadir / "backtest-result.json"
default_conf["indicators1"] = ["sma5", "ema10"]
default_conf["indicators2"] = ["macd"]
default_conf["pairs"] = ["ETH/BTC", "LTC/BTC"]
2019-08-22 14:43:28 +00:00
candle_mock = MagicMock()
store_mock = MagicMock()
mocker.patch.multiple(
"freqtrade.plot.plotting",
generate_candlestick_graph=candle_mock,
2024-05-12 13:29:14 +00:00
store_plot_file=store_mock,
)
2019-09-06 22:59:42 +00:00
load_and_plot_trades(default_conf)
2019-08-22 14:43:28 +00:00
# Both mocks should be called once per pair
assert candle_mock.call_count == 2
assert store_mock.call_count == 2
2024-05-12 13:29:14 +00:00
assert candle_mock.call_args_list[0][1]["indicators1"] == ["sma5", "ema10"]
assert candle_mock.call_args_list[0][1]["indicators2"] == ["macd"]
2019-08-22 14:43:28 +00:00
assert log_has("End of plotting process. 2 plots generated", caplog)
2019-08-22 14:51:09 +00:00
2019-08-24 13:35:43 +00:00
def test_start_plot_profit(mocker):
2019-08-22 14:51:09 +00:00
aup = mocker.patch("freqtrade.plot.plotting.plot_profit", MagicMock())
args = [
"plot-profit",
2024-05-12 13:29:14 +00:00
"--config",
"tests/testdata/testconfigs/main_test_config.json",
"--pairs",
"ETH/BTC",
2019-08-22 14:51:09 +00:00
]
start_plot_profit(get_args(args))
assert aup.call_count == 1
called_config = aup.call_args_list[0][0][0]
assert "pairs" in called_config
2024-05-12 13:29:14 +00:00
assert called_config["pairs"] == ["ETH/BTC"]
2019-08-22 15:02:22 +00:00
2019-09-03 05:05:48 +00:00
def test_start_plot_profit_error(mocker):
2024-05-12 13:29:14 +00:00
args = ["plot-profit", "--pairs", "ETH/BTC"]
argsp = get_args(args)
# Make sure we use no config. Details: #2241
# not resetting config causes random failures if config.json exists
2024-05-12 13:29:14 +00:00
argsp["config"] = []
2019-09-03 05:05:48 +00:00
with pytest.raises(OperationalException):
start_plot_profit(argsp)
2019-09-03 05:05:48 +00:00
def test_plot_profit(default_conf, mocker, testdatadir):
patch_exchange(mocker)
2024-05-12 13:29:14 +00:00
default_conf["trade_source"] = "file"
default_conf["exportfilename"] = testdatadir / "backtest-result_test_nofile.json"
default_conf["pairs"] = ["ETH/BTC", "LTC/BTC"]
2019-08-22 15:02:22 +00:00
profit_mock = MagicMock()
store_mock = MagicMock()
mocker.patch.multiple(
2024-05-12 13:29:14 +00:00
"freqtrade.plot.plotting", generate_profit_graph=profit_mock, store_plot_file=store_mock
2019-08-22 15:02:22 +00:00
)
2024-05-12 13:29:14 +00:00
with pytest.raises(
OperationalException, match=r"No trades found, cannot generate Profit-plot.*"
):
plot_profit(default_conf)
2024-05-12 13:29:14 +00:00
default_conf["exportfilename"] = testdatadir / "backtest_results/backtest-result.json"
2019-08-22 15:02:22 +00:00
plot_profit(default_conf)
# Plot-profit generates one combined plot
assert profit_mock.call_count == 1
assert store_mock.call_count == 1
2024-05-12 13:29:14 +00:00
assert profit_mock.call_args_list[0][0][0] == default_conf["pairs"]
assert store_mock.call_args_list[0][1]["auto_open"] is False
2024-05-12 13:29:14 +00:00
del default_conf["timeframe"]
with pytest.raises(OperationalException, match=r"Timeframe must be set.*--timeframe.*"):
plot_profit(default_conf)
2024-05-12 13:29:14 +00:00
@pytest.mark.parametrize(
"ind1,ind2,plot_conf,exp",
[
# No indicators, use plot_conf
(
[],
[],
{},
{
"main_plot": {"sma": {}, "ema3": {}, "ema5": {}},
"subplots": {"Other": {"macd": {}, "macdsignal": {}}},
},
),
# use indicators
(
["sma", "ema3"],
["macd"],
{},
{"main_plot": {"sma": {}, "ema3": {}}, "subplots": {"Other": {"macd": {}}}},
),
# only main_plot - adds empty subplots
([], [], {"main_plot": {"sma": {}}}, {"main_plot": {"sma": {}}, "subplots": {}}),
# Main and subplots
(
[],
[],
{"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}},
{"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}},
),
# no main_plot, adds empty main_plot
(
[],
[],
{"subplots": {"RSI": {"rsi": {"color": "red"}}}},
{"main_plot": {}, "subplots": {"RSI": {"rsi": {"color": "red"}}}},
),
# indicator 1 / 2 should have prevalence
(
["sma", "ema3"],
["macd"],
{"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}},
{"main_plot": {"sma": {}, "ema3": {}}, "subplots": {"Other": {"macd": {}}}},
),
# indicator 1 - overrides plot_config main_plot
(
["sma", "ema3"],
[],
{"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}},
{"main_plot": {"sma": {}, "ema3": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}},
),
# indicator 2 - overrides plot_config subplots
(
[],
["macd", "macd_signal"],
{"main_plot": {"sma": {}}, "subplots": {"RSI": {"rsi": {"color": "red"}}}},
{"main_plot": {"sma": {}}, "subplots": {"Other": {"macd": {}, "macd_signal": {}}}},
),
],
)
def test_create_plotconfig(ind1, ind2, plot_conf, exp):
res = create_plotconfig(ind1, ind2, plot_conf)
2024-05-12 13:29:14 +00:00
assert "main_plot" in res
assert "subplots" in res
assert isinstance(res["main_plot"], dict)
assert isinstance(res["subplots"], dict)
assert res == exp