Merge pull request #4094 from MrKrautee/plot_area
Plot area between traces
This commit is contained in:
commit
9d37ac9955
@ -168,6 +168,7 @@ Additional features when using plot_config include:
|
||||
|
||||
* Specify colors per indicator
|
||||
* Specify additional subplots
|
||||
* 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.
|
||||
@ -183,23 +184,33 @@ 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'}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
!!! 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`, `macdhist` and `rsi` are columns in the DataFrame created by the strategy.
|
||||
|
||||
## Plot profit
|
||||
|
||||
|
@ -263,6 +263,65 @@ def create_plotconfig(indicators1: List[str], indicators2: List[str],
|
||||
return plot_config
|
||||
|
||||
|
||||
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
|
||||
: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:
|
||||
# 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],
|
||||
showlegend=False,
|
||||
line=line)
|
||||
trace_b = go.Scatter(x=data.date, y=data[indicator_b], name=label,
|
||||
fill="tonexty", fillcolor=fill_color,
|
||||
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 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
|
||||
: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:
|
||||
indicator_b = ind_conf['fill_to']
|
||||
if indicator in data and indicator_b in data:
|
||||
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(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
|
||||
|
||||
|
||||
def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFrame = None, *,
|
||||
indicators1: List[str] = [],
|
||||
indicators2: List[str] = [],
|
||||
@ -280,7 +339,6 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra
|
||||
:return: Plotly figure
|
||||
"""
|
||||
plot_config = create_plotconfig(indicators1, indicators2, plot_config)
|
||||
|
||||
rows = 2 + len(plot_config['subplots'])
|
||||
row_widths = [1 for _ in plot_config['subplots']]
|
||||
# Define the graph
|
||||
@ -346,36 +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.")
|
||||
|
||||
# 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']
|
||||
|
||||
# Add indicators to main plot
|
||||
# Add Bollinger Bands
|
||||
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
|
||||
# main plot goes to row 1
|
||||
fig = add_indicators(fig=fig, row=1, indicators=plot_config['main_plot'], data=data)
|
||||
|
||||
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'],
|
||||
@ -384,13 +426,14 @@ 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
|
||||
for i, name in enumerate(plot_config['subplots']):
|
||||
fig = add_indicators(fig=fig, row=3 + i,
|
||||
indicators=plot_config['subplots'][name],
|
||||
# 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
|
||||
fig = add_indicators(fig=fig, row=row, indicators=sub_config,
|
||||
data=data)
|
||||
|
||||
# fill area between indicators ( 'fill_to': 'other_indicator')
|
||||
fig = add_areas(fig, row, data, sub_config)
|
||||
return fig
|
||||
|
||||
|
||||
|
@ -13,7 +13,7 @@ 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)
|
||||
@ -96,6 +96,62 @@ 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"}}
|
||||
|
||||
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)
|
||||
|
||||
# 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)
|
||||
|
||||
# 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")
|
||||
assert isinstance(fill_macd, go.Scatter)
|
||||
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()
|
||||
# nothing happens when no trades are available
|
||||
|
Loading…
Reference in New Issue
Block a user