2019-06-10 18:17:23 +00:00
|
|
|
|
2019-06-29 18:30:31 +00:00
|
|
|
from copy import deepcopy
|
2019-06-10 18:17:23 +00:00
|
|
|
from unittest.mock import MagicMock
|
|
|
|
|
|
|
|
import plotly.graph_objs as go
|
2019-06-29 18:30:31 +00:00
|
|
|
from plotly import tools
|
2019-06-10 18:17:23 +00:00
|
|
|
|
2019-06-16 17:53:48 +00:00
|
|
|
from freqtrade.arguments import TimeRange
|
2019-06-10 18:17:23 +00:00
|
|
|
from freqtrade.data import history
|
2019-06-16 17:53:48 +00:00
|
|
|
from freqtrade.data.btanalysis import load_backtest_data
|
2019-06-29 18:30:31 +00:00
|
|
|
from freqtrade.plot.plotting import (generate_candlestick_graph,
|
|
|
|
generate_plot_file,
|
|
|
|
generate_plot_filename, generate_row,
|
|
|
|
plot_trades)
|
2019-06-11 04:45:36 +00:00
|
|
|
from freqtrade.strategy.default_strategy import DefaultStrategy
|
|
|
|
from freqtrade.tests.conftest import log_has, log_has_re
|
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):
|
|
|
|
""" Return Fig - used to mock generate_row and plot_trades"""
|
|
|
|
return fig
|
|
|
|
|
|
|
|
|
2019-06-11 04:45:36 +00:00
|
|
|
def find_trace_in_fig_data(data, search_string: str):
|
|
|
|
matches = filter(lambda x: x.name == search_string, data)
|
|
|
|
return next(matches)
|
|
|
|
|
|
|
|
|
2019-06-16 08:32:12 +00:00
|
|
|
def generage_empty_figure():
|
|
|
|
return tools.make_subplots(
|
|
|
|
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-06-16 08:32:12 +00:00
|
|
|
def test_generate_row(default_conf, caplog):
|
|
|
|
pair = "UNITTEST/BTC"
|
|
|
|
timerange = TimeRange(None, 'line', 0, -1000)
|
|
|
|
|
|
|
|
data = history.load_pair_history(pair=pair, ticker_interval='1m',
|
|
|
|
datadir=None, timerange=timerange)
|
|
|
|
indicators1 = ["ema10"]
|
|
|
|
indicators2 = ["macd"]
|
|
|
|
|
|
|
|
# Generate buy/sell signals and indicators
|
|
|
|
strat = DefaultStrategy(default_conf)
|
|
|
|
data = strat.analyze_ticker(data, {'pair': pair})
|
|
|
|
fig = generage_empty_figure()
|
|
|
|
|
|
|
|
# Row 1
|
|
|
|
fig1 = generate_row(fig=deepcopy(fig), row=1, indicators=indicators1, data=data)
|
|
|
|
figure = fig1.layout.figure
|
|
|
|
ema10 = find_trace_in_fig_data(figure.data, "ema10")
|
|
|
|
assert isinstance(ema10, go.Scatter)
|
|
|
|
assert ema10.yaxis == "y"
|
|
|
|
|
|
|
|
fig2 = generate_row(fig=deepcopy(fig), row=3, indicators=indicators2, data=data)
|
|
|
|
figure = fig2.layout.figure
|
|
|
|
macd = find_trace_in_fig_data(figure.data, "macd")
|
|
|
|
assert isinstance(macd, go.Scatter)
|
|
|
|
assert macd.yaxis == "y3"
|
|
|
|
|
|
|
|
# No indicator found
|
|
|
|
fig3 = generate_row(fig=deepcopy(fig), row=3, indicators=['no_indicator'], data=data)
|
|
|
|
assert fig == fig3
|
|
|
|
assert log_has_re(r'Indicator "no_indicator" ignored\..*', caplog.record_tuples)
|
2019-06-10 18:17:23 +00:00
|
|
|
|
|
|
|
|
2019-06-22 13:45:20 +00:00
|
|
|
def test_plot_trades(caplog):
|
2019-06-16 08:32:12 +00:00
|
|
|
fig1 = generage_empty_figure()
|
|
|
|
# nothing happens when no trades are available
|
|
|
|
fig = plot_trades(fig1, None)
|
|
|
|
assert fig == fig1
|
2019-06-22 13:45:20 +00:00
|
|
|
assert log_has("No trades found.", caplog.record_tuples)
|
2019-06-16 17:53:48 +00:00
|
|
|
pair = "ADA/BTC"
|
|
|
|
filename = history.make_testdata_path(None) / "backtest-result_test.json"
|
|
|
|
trades = load_backtest_data(filename)
|
|
|
|
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 buys - color, should be in first graph, ...
|
|
|
|
trade_buy = find_trace_in_fig_data(figure.data, "trade_buy")
|
|
|
|
assert isinstance(trade_buy, go.Scatter)
|
|
|
|
assert trade_buy.yaxis == 'y'
|
|
|
|
assert len(trades) == len(trade_buy.x)
|
|
|
|
assert trade_buy.marker.color == 'green'
|
|
|
|
|
|
|
|
trade_sell = find_trace_in_fig_data(figure.data, "trade_sell")
|
|
|
|
assert isinstance(trade_sell, go.Scatter)
|
|
|
|
assert trade_sell.yaxis == 'y'
|
|
|
|
assert len(trades) == len(trade_sell.x)
|
|
|
|
assert trade_sell.marker.color == 'red'
|
2019-06-10 18:17:23 +00:00
|
|
|
|
|
|
|
|
2019-06-29 15:23:03 +00:00
|
|
|
def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, caplog):
|
2019-06-10 18:17:23 +00:00
|
|
|
row_mock = mocker.patch('freqtrade.plot.plotting.generate_row',
|
|
|
|
MagicMock(side_effect=fig_generating_mock))
|
|
|
|
trades_mock = mocker.patch('freqtrade.plot.plotting.plot_trades',
|
|
|
|
MagicMock(side_effect=fig_generating_mock))
|
|
|
|
|
2019-06-11 04:45:36 +00:00
|
|
|
pair = "UNITTEST/BTC"
|
|
|
|
timerange = TimeRange(None, 'line', 0, -1000)
|
|
|
|
data = history.load_pair_history(pair=pair, ticker_interval='1m',
|
|
|
|
datadir=None, timerange=timerange)
|
|
|
|
data['buy'] = 0
|
|
|
|
data['sell'] = 0
|
|
|
|
|
|
|
|
indicators1 = []
|
|
|
|
indicators2 = []
|
2019-06-29 15:23:03 +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 buy-signals found.", caplog.record_tuples)
|
|
|
|
assert log_has("No sell-signals found.", caplog.record_tuples)
|
|
|
|
|
|
|
|
|
2019-06-29 15:23:03 +00:00
|
|
|
def test_generate_candlestick_graph_no_trades(default_conf, mocker):
|
2019-06-11 04:45:36 +00:00
|
|
|
row_mock = mocker.patch('freqtrade.plot.plotting.generate_row',
|
|
|
|
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, ticker_interval='1m',
|
2019-06-10 18:17:23 +00:00
|
|
|
datadir=None, timerange=timerange)
|
|
|
|
|
2019-06-11 04:45:36 +00:00
|
|
|
# Generate buy/sell signals and indicators
|
|
|
|
strat = DefaultStrategy(default_conf)
|
|
|
|
data = strat.analyze_ticker(data, {'pair': pair})
|
|
|
|
|
2019-06-10 18:17:23 +00:00
|
|
|
indicators1 = []
|
|
|
|
indicators2 = []
|
2019-06-29 15:23:03 +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) == 6
|
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)
|
|
|
|
|
|
|
|
buy = find_trace_in_fig_data(figure.data, "buy")
|
|
|
|
assert isinstance(buy, go.Scatter)
|
|
|
|
# All buy-signals should be plotted
|
|
|
|
assert int(data.buy.sum()) == len(buy.x)
|
|
|
|
|
|
|
|
sell = find_trace_in_fig_data(figure.data, "sell")
|
|
|
|
assert isinstance(sell, go.Scatter)
|
|
|
|
# All buy-signals should be plotted
|
|
|
|
assert int(data.sell.sum()) == len(sell.x)
|
2019-06-10 18:17:23 +00:00
|
|
|
|
2019-06-11 04:45:36 +00:00
|
|
|
assert find_trace_in_fig_data(figure.data, "BB lower")
|
|
|
|
assert find_trace_in_fig_data(figure.data, "BB upper")
|
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
|
|
|
|
|
|
|
|
2019-06-29 18:30:31 +00:00
|
|
|
def test_generate_Plot_filename():
|
|
|
|
fn = generate_plot_filename("UNITTEST/BTC", "5m")
|
|
|
|
assert fn == "freqtrade-plot-UNITTEST_BTC-5m.html"
|
|
|
|
|
|
|
|
|
2019-06-16 12:03:55 +00:00
|
|
|
def test_generate_plot_file(mocker, caplog):
|
|
|
|
fig = generage_empty_figure()
|
|
|
|
plot_mock = mocker.patch("freqtrade.plot.plotting.plot", MagicMock())
|
2019-06-29 18:30:31 +00:00
|
|
|
generate_plot_file(fig, filename="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
|
|
|
|
assert (plot_mock.call_args_list[0][1]['filename']
|
|
|
|
== "user_data/plots/freqtrade-plot-UNITTEST_BTC-5m.html")
|