stable/scripts/plot_dataframe.py

366 lines
11 KiB
Python
Raw Normal View History

2017-11-09 21:29:23 +00:00
#!/usr/bin/env python3
"""
Script to display when the bot will buy a specific pair
Mandatory Cli parameters:
-p / --pair: pair to examine
Option but recommended
-s / --strategy: strategy to use
Optional Cli parameters
-d / --datadir: path to pair backtest data
--timerange: specify what timerange of data to use.
-l / --live: Live, to download the latest ticker for the pair
-db / --db-url: Show trades stored in database
Indicators recommended
Row 1: sma, ema3, ema5, ema10, ema50
Row 3: macd, rsi, fisher_rsi, mfi, slowd, slowk, fastd, fastk
2018-06-05 03:42:34 +00:00
Example of usage:
> python3 scripts/plot_dataframe.py --pair BTC/EUR -d user_data/data/ --indicators1 sma,ema3
--indicators2 fastk,fastd
"""
2018-03-25 19:37:14 +00:00
import logging
2018-01-06 07:38:47 +00:00
import sys
2018-06-23 17:54:27 +00:00
import json
from pathlib import Path
2018-03-18 01:46:48 +00:00
from argparse import Namespace
from typing import Dict, List, Any
2018-06-23 17:54:27 +00:00
import pandas as pd
import plotly.graph_objs as go
from plotly import tools
from plotly.offline import plot
import freqtrade.optimize as optimize
from freqtrade import persistence
from freqtrade.analyze import Analyze
from freqtrade.arguments import Arguments
from freqtrade.exchange import Exchange
from freqtrade.optimize.backtesting import setup_configuration
from freqtrade.persistence import Trade
2017-11-09 21:29:23 +00:00
2018-03-25 19:37:14 +00:00
logger = logging.getLogger(__name__)
_CONF: Dict[str, Any] = {}
2018-01-06 07:38:47 +00:00
2018-03-18 01:46:48 +00:00
def plot_analyzed_dataframe(args: Namespace) -> None:
2017-11-09 21:29:23 +00:00
"""
Calls analyze() and plots the returned dataframe
:return: None
"""
global _CONF
# Load the configuration
_CONF.update(setup_configuration(args))
# Set the pair to audit
pair = args.pair
if pair is None:
logger.critical('Parameter --pair mandatory;. E.g --pair ETH/BTC')
exit()
if '/' not in pair:
logger.critical('--pair format must be XXX/YYY')
exit()
# Set timerange to use
timerange = Arguments.parse_timerange(args.timerange)
# Load the strategy
try:
analyze = Analyze(_CONF)
exchange = Exchange(_CONF)
except AttributeError:
logger.critical(
'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"',
args.strategy
)
exit()
# Set the ticker to use
tick_interval = analyze.get_ticker_interval()
2018-06-05 02:25:25 +00:00
# Load pair tickers
tickers = {}
if args.live:
logger.info('Downloading pair.')
tickers[pair] = exchange.get_ticker_history(pair, tick_interval)
else:
tickers = optimize.load_data(
2018-06-12 19:43:14 +00:00
datadir=_CONF.get("datadir"),
pairs=[pair],
ticker_interval=tick_interval,
refresh_pairs=_CONF.get('refresh_pairs', False),
timerange=timerange
)
2017-11-09 21:29:23 +00:00
# No ticker found, or impossible to download
if tickers == {}:
exit()
2018-06-23 18:14:15 +00:00
if args.db_url and args.exportfilename:
logger.critical("Can only specify --db-url or --export-filename")
# Get trades already made from the DB
2018-06-23 17:54:27 +00:00
trades: pd.DataFrame = pd.DataFrame()
if args.db_url:
persistence.init(_CONF)
2018-06-23 17:54:27 +00:00
columns = ["pair", "profit", "opents", "closets", "open_rate", "close_rate", "duration"]
trades = pd.DataFrame([(t.pair, t.calc_profit(),
t.open_date, t.close_date,
t.open_rate, t.close_rate,
t.close_date.timestamp() - t.open_date.timestamp())
2018-06-23 18:14:15 +00:00
for t in Trade.query.filter(Trade.pair.is_(pair)).all()],
columns=columns)
2018-06-23 17:54:27 +00:00
if args.exportfilename:
file = Path(args.exportfilename)
# must align with columns in backtest.py
columns = ["pair", "profit", "opents", "closets", "index", "duration",
"open_rate", "close_rate", "open_at_end"]
with file.open() as f:
data = json.load(f)
trades = pd.DataFrame(data, columns=columns)
trades = trades.loc[trades["pair"] == pair]
if timerange:
if timerange.starttype == 'date':
trades = trades.loc[trades["opents"] >= timerange.startts]
if timerange.stoptype == 'date':
trades = trades.loc[trades["opents"] <= timerange.stopts]
trades['opents'] = pd.to_datetime(trades['opents'],
unit='s',
utc=True,
infer_datetime_format=True)
trades['closets'] = pd.to_datetime(trades['closets'],
unit='s',
utc=True,
infer_datetime_format=True)
dataframes = analyze.tickerdata_to_dataframe(tickers)
dataframe = dataframes[pair]
dataframe = analyze.populate_buy_trend(dataframe)
dataframe = analyze.populate_sell_trend(dataframe)
2018-06-23 18:14:15 +00:00
if len(dataframe.index) > args.plot_limit:
logger.warning('Ticker contained more than %s candles as defined '
'with --plot-limit, clipping.', args.plot_limit)
dataframe = dataframe.tail(args.plot_limit)
trades = trades.loc[trades['opents'] >= dataframe.iloc[0]['date']]
2018-06-04 01:41:28 +00:00
fig = generate_graph(
pair=pair,
trades=trades,
2018-06-23 18:14:15 +00:00
data=dataframe,
args=args
)
2018-06-23 17:58:28 +00:00
plot(fig, filename=str(Path('user_data').joinpath('freqtrade-plot.html')))
2018-06-04 01:41:28 +00:00
2018-06-23 17:54:27 +00:00
def generate_graph(pair, trades: pd.DataFrame, data: pd.DataFrame, args) -> tools.make_subplots:
2018-06-04 01:41:28 +00:00
"""
Generate the graph from the data generated by Backtesting or from DB
:param pair: Pair to Display on the graph
:param trades: All trades created
:param data: Dataframe
2018-06-05 06:49:16 +00:00
:param args: sys.argv that contrains the two params indicators1, and indicators2
2018-06-04 01:41:28 +00:00
:return: None
"""
2018-06-04 01:41:28 +00:00
# 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')
2018-06-04 01:41:28 +00:00
# Common information
candles = go.Candlestick(
x=data.date,
open=data.open,
high=data.high,
low=data.low,
close=data.close,
name='Price'
)
df_buy = data[data['buy'] == 1]
buys = go.Scattergl(
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',
)
)
df_sell = data[data['sell'] == 1]
sells = go.Scattergl(
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',
)
)
trade_buys = go.Scattergl(
2018-06-23 17:54:27 +00:00
x=trades["opents"],
y=trades["open_rate"],
mode='markers',
name='trade_buy',
marker=dict(
symbol='square-open',
size=11,
line=dict(width=2),
color='green'
)
)
trade_sells = go.Scattergl(
2018-06-23 17:54:27 +00:00
x=trades["closets"],
y=trades["close_rate"],
mode='markers',
name='trade_sell',
marker=dict(
symbol='square-open',
size=11,
line=dict(width=2),
color='red'
)
)
2018-06-04 01:41:28 +00:00
# Row 1
fig.append_trace(candles, 1, 1)
2018-02-08 11:32:34 +00:00
2018-06-04 01:41:28 +00:00
if 'bb_lowerband' in data and 'bb_upperband' in data:
bb_lower = go.Scatter(
x=data.date,
y=data.bb_lowerband,
name='BB lower',
line={'color': "transparent"},
)
bb_upper = go.Scatter(
x=data.date,
y=data.bb_upperband,
name='BB upper',
fill="tonexty",
fillcolor="rgba(0,176,246,0.2)",
line={'color': "transparent"},
)
fig.append_trace(bb_lower, 1, 1)
fig.append_trace(bb_upper, 1, 1)
fig = generate_row(fig=fig, row=1, raw_indicators=args.indicators1, data=data)
fig.append_trace(buys, 1, 1)
fig.append_trace(sells, 1, 1)
fig.append_trace(trade_buys, 1, 1)
fig.append_trace(trade_sells, 1, 1)
2018-06-04 01:41:28 +00:00
# Row 2
volume = go.Bar(
x=data['date'],
y=data['volume'],
name='Volume'
)
fig.append_trace(volume, 2, 1)
# Row 3
fig = generate_row(fig=fig, row=3, raw_indicators=args.indicators2, data=data)
2018-06-04 01:41:28 +00:00
return fig
def generate_row(fig, row, raw_indicators, data) -> tools.make_subplots:
2018-06-04 01:41:28 +00:00
"""
Generator all the indicator selected by the user for a specific row
2018-06-04 01:41:28 +00:00
"""
for indicator in raw_indicators.split(','):
if indicator in data:
scattergl = go.Scattergl(
x=data['date'],
y=data[indicator],
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
2017-11-09 21:29:23 +00:00
2018-03-18 01:46:48 +00:00
def plot_parse_args(args: List[str]) -> Namespace:
"""
Parse args passed to the script
:param args: Cli arguments
:return: args: Array with all arguments
"""
arguments = Arguments(args, 'Graph dataframe')
arguments.scripts_options()
arguments.parser.add_argument(
'--indicators1',
help='Set indicators from your strategy you want in the first row of the graph. Separate '
'them with a coma. E.g: ema3,ema5 (default: %(default)s)',
type=str,
default='sma,ema3,ema5',
dest='indicators1',
)
arguments.parser.add_argument(
'--indicators2',
help='Set indicators from your strategy you want in the third row of the graph. Separate '
'them with a coma. E.g: fastd,fastk (default: %(default)s)',
type=str,
default='macd',
dest='indicators2',
)
2018-06-23 18:14:15 +00:00
arguments.parser.add_argument(
'--plot-limit',
help='Specify tick limit for plotting - too high values cause huge files - '
'Default: %(default)s',
dest='plot_limit',
default=750,
type=str,
)
arguments.common_args_parser()
arguments.optimizer_shared_options(arguments.parser)
arguments.backtesting_options(arguments.parser)
return arguments.parse_args()
2018-03-18 01:46:48 +00:00
def main(sysargv: List[str]) -> None:
"""
This function will initiate the bot and start the trading loop.
:return: None
"""
logger.info('Starting Plot Dataframe')
plot_analyzed_dataframe(
plot_parse_args(sysargv)
)
2017-11-09 21:29:23 +00:00
if __name__ == '__main__':
main(sys.argv[1:])