stable/freqtrade/plot/plotting.py

266 lines
8.2 KiB
Python
Raw Normal View History

2018-06-23 12:18:30 +00:00
import logging
from pathlib import Path
from typing import Any, Dict, List, Optional
2019-05-29 05:19:21 +00:00
2019-05-28 05:00:57 +00:00
import pandas as pd
from freqtrade.arguments import Arguments
from frqtrade.exchange import Exchange
from freqtrade.data import history
from freqtrade.data.btanalysis import load_trades
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
2018-06-23 12:18:30 +00:00
logger = logging.getLogger(__name__)
try:
from plotly import tools
from plotly.offline import plot
import plotly.graph_objs as go
except ImportError:
logger.exception("Module plotly not found \n Please install using `pip install plotly`")
2019-05-29 05:19:21 +00:00
exit(1)
2019-05-28 05:00:57 +00:00
class FTPlots():
def __init__(self, config: Dict[str, Any]):
self._config = config
self.exchange: Optional[Exchange] = None
if self._config.get("live", False) or self._config.get("refresh_pairs", False):
self.exchange = ExchangeResolver(self._config.get('exchange', {}).get('name'),
self._config).exchange
self.strategy = StrategyResolver(self._config).strategy
if "pairs" in self._config:
self.pairs = self._config["pairs"].split(',')
else:
self.pairs = self._config["exchange"]["pair_whitelist"]
# Set timerange to use
self.timerange = Arguments.parse_timerange(self._config["timerange"])
self.tickers = history.load_data(
datadir=Path(str(self._config.get("datadir"))),
pairs=self.pairs,
ticker_interval=self._config['ticker_interval'],
refresh_pairs=self._config.get('refresh_pairs', False),
timerange=self.timerange,
exchange=self.exchange,
live=self._config.get("live", False),
)
self.trades = load_trades(self._config)
2019-06-30 07:44:50 +00:00
def add_indicators(fig, row, indicators: List[str], data: pd.DataFrame) -> tools.make_subplots:
2019-05-28 05:00:57 +00:00
"""
Generator all the indicator selected by the user for a specific row
:param fig: Plot figure to append to
:param row: row number for this plot
:param indicators: List of indicators present in the dataframe
:param data: candlestick DataFrame
"""
for indicator in indicators:
if indicator in data:
2019-05-29 05:19:21 +00:00
# TODO: Figure out why scattergl causes problems
scattergl = go.Scatter(
2019-05-28 05:00:57 +00:00
x=data['date'],
2019-05-29 05:19:21 +00:00
y=data[indicator].values,
2019-05-28 05:00:57 +00:00
mode='lines',
name=indicator
)
fig.append_trace(scattergl, row, 1)
else:
logger.info(
'Indicator "%s" ignored. Reason: This indicator is not found '
'in your strategy.',
indicator
)
return fig
def plot_trades(fig, trades: pd.DataFrame):
"""
2019-06-30 07:47:07 +00:00
Add trades to "fig"
2019-05-28 05:00:57 +00:00
"""
# Trades can be empty
2019-05-28 18:23:16 +00:00
if trades is not None and len(trades) > 0:
2019-05-28 05:00:57 +00:00
trade_buys = go.Scatter(
x=trades["open_time"],
y=trades["open_rate"],
mode='markers',
name='trade_buy',
marker=dict(
symbol='square-open',
size=11,
line=dict(width=2),
color='green'
)
)
2019-05-29 05:19:21 +00:00
# Create description for sell summarizing the trade
desc = trades.apply(lambda row: f"{round(row['profitperc'], 3)}%, {row['sell_reason']}, "
f"{row['duration']}min",
axis=1)
2019-05-28 05:00:57 +00:00
trade_sells = go.Scatter(
x=trades["close_time"],
y=trades["close_rate"],
2019-05-29 05:19:21 +00:00
text=desc,
2019-05-28 05:00:57 +00:00
mode='markers',
name='trade_sell',
marker=dict(
symbol='square-open',
size=11,
line=dict(width=2),
color='red'
)
)
fig.append_trace(trade_buys, 1, 1)
fig.append_trace(trade_sells, 1, 1)
2019-06-22 13:45:20 +00:00
else:
logger.warning("No trades found.")
2019-05-28 05:00:57 +00:00
return fig
2019-06-30 07:47:07 +00:00
def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFrame = None,
indicators1: List[str] = [],
indicators2: List[str] = [],) -> go.Figure:
2019-05-28 05:00:57 +00:00
"""
Generate the graph from the data generated by Backtesting or from DB
2019-06-16 18:14:31 +00:00
Volume will always be ploted in row2, so Row 1 and 3 are to our disposal for custom indicators
2019-05-28 05:00:57 +00:00
:param pair: Pair to Display on the graph
:param data: OHLCV DataFrame containing indicators and buy/sell signals
:param trades: All trades created
:param indicators1: List containing Main plot indicators
:param indicators2: List containing Sub plot indicators
:return: None
"""
# Define the graph
fig = tools.make_subplots(
rows=3,
cols=1,
shared_xaxes=True,
row_width=[1, 1, 4],
vertical_spacing=0.0001,
)
fig['layout'].update(title=pair)
fig['layout']['yaxis1'].update(title='Price')
fig['layout']['yaxis2'].update(title='Volume')
fig['layout']['yaxis3'].update(title='Other')
fig['layout']['xaxis']['rangeslider'].update(visible=False)
# Common information
candles = go.Candlestick(
x=data.date,
open=data.open,
high=data.high,
low=data.low,
close=data.close,
name='Price'
)
fig.append_trace(candles, 1, 1)
if 'buy' in data.columns:
df_buy = data[data['buy'] == 1]
2019-05-28 18:23:16 +00:00
if len(df_buy) > 0:
2019-05-29 05:19:21 +00:00
buys = go.Scatter(
2019-05-28 18:23:16 +00:00
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',
)
2019-05-28 05:00:57 +00:00
)
2019-05-28 18:23:16 +00:00
fig.append_trace(buys, 1, 1)
else:
logger.warning("No buy-signals found.")
2019-05-28 05:00:57 +00:00
if 'sell' in data.columns:
df_sell = data[data['sell'] == 1]
2019-05-28 18:23:16 +00:00
if len(df_sell) > 0:
2019-05-29 05:19:21 +00:00
sells = go.Scatter(
2019-05-28 18:23:16 +00:00
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',
)
2019-05-28 05:00:57 +00:00
)
2019-05-28 18:23:16 +00:00
fig.append_trace(sells, 1, 1)
else:
logger.warning("No sell-signals found.")
2019-05-28 05:00:57 +00:00
if 'bb_lowerband' in data and 'bb_upperband' in data:
2019-05-28 18:23:16 +00:00
bb_lower = go.Scattergl(
2019-05-28 05:00:57 +00:00
x=data.date,
y=data.bb_lowerband,
name='BB lower',
line={'color': 'rgba(255,255,255,0)'},
)
2019-05-28 18:23:16 +00:00
bb_upper = go.Scattergl(
2019-05-28 05:00:57 +00:00
x=data.date,
y=data.bb_upperband,
name='BB upper',
fill="tonexty",
fillcolor="rgba(0,176,246,0.2)",
line={'color': 'rgba(255,255,255,0)'},
)
fig.append_trace(bb_lower, 1, 1)
fig.append_trace(bb_upper, 1, 1)
# Add indicators to main plot
2019-06-30 07:44:50 +00:00
fig = add_indicators(fig=fig, row=1, indicators=indicators1, data=data)
2019-05-28 05:00:57 +00:00
fig = plot_trades(fig, trades)
# Volume goes to row 2
volume = go.Bar(
x=data['date'],
y=data['volume'],
name='Volume'
)
fig.append_trace(volume, 2, 1)
# Add indicators to seperate row
2019-06-30 07:44:50 +00:00
fig = add_indicators(fig=fig, row=3, indicators=indicators2, data=data)
2019-05-28 05:00:57 +00:00
return fig
2019-05-31 04:41:55 +00:00
def generate_plot_filename(pair, ticker_interval) -> str:
2019-06-30 07:47:07 +00:00
"""
Generate filenames per pair/ticker_interval to be used for storing plots
"""
pair_name = pair.replace("/", "_")
file_name = 'freqtrade-plot-' + pair_name + '-' + ticker_interval + '.html'
logger.info('Generate plot file for %s', pair)
return file_name
def store_plot_file(fig, filename: str, auto_open: bool = False) -> None:
2019-05-31 04:41:55 +00:00
"""
Generate a plot html file from pre populated fig plotly object
:param fig: Plotly Figure to plot
:param pair: Pair to plot (used as filename and Plot title)
:param ticker_interval: Used as part of the filename
:return: None
"""
Path("user_data/plots").mkdir(parents=True, exist_ok=True)
plot(fig, filename=str(Path('user_data/plots').joinpath(filename)),
auto_open=auto_open)