From 5683f9e10e76c12a8bf1b91f84d3b07b3825e315 Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Sun, 3 Jun 2018 13:58:00 -0700 Subject: [PATCH 01/12] Remove hardcoded backtest-result.json in Plot scripts --- .gitignore | 1 + freqtrade/arguments.py | 13 +++++++------ scripts/plot_dataframe.py | 13 +++++-------- scripts/plot_profit.py | 33 +++++++++++++++++--------------- user_data/backtest_data/.gitkeep | 0 5 files changed, 31 insertions(+), 29 deletions(-) create mode 100644 user_data/backtest_data/.gitkeep diff --git a/.gitignore b/.gitignore index 219a9fb40..b52a31d8e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ logfile.txt hyperopt_trials.pickle user_data/ freqtrade-plot.html +freqtrade-profit-plot.html # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index a45dd20b3..c9cecb1f5 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -123,8 +123,8 @@ class Arguments(object): ) parser.add_argument( '-r', '--refresh-pairs-cached', - help='refresh the pairs files in tests/testdata with the latest data from the exchange. \ - Use it if you want to run your backtesting with up-to-date data.', + help='refresh the pairs files in tests/testdata with the latest data from the ' + 'exchange. Use it if you want to run your backtesting with up-to-date data.', action='store_true', dest='refresh_pairs', ) @@ -140,11 +140,12 @@ class Arguments(object): '--export-filename', help='Save backtest results to this filename \ requires --export to be set as well\ - Example --export-filename=backtest_today.json\ + Example --export-filename=user_data/backtest_data/backtest_today.json\ (default: %(default)s', type=str, - default='backtest-result.json', + default=os.path.join('user_data', 'backtest_data', 'backtest-result.json'), dest='exportfilename', + metavar='PATH', ) @staticmethod @@ -220,8 +221,8 @@ class Arguments(object): self.hyperopt_options(hyperopt_cmd) @staticmethod - def parse_timerange(text: Optional[str]) -> Optional[Tuple[Tuple, - Optional[int], Optional[int]]]: + def parse_timerange(text: Optional[str]) -> \ + Optional[Tuple[Tuple, Optional[int], Optional[int]]]: """ Parse the value of the argument --timerange to determine what is the range desired :param text: value from --timerange diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index 108c0b609..47ce0e746 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -13,18 +13,14 @@ Optional Cli parameters -db / --db-url: Show trades stored in database """ import logging +import os import sys from argparse import Namespace - -from typing import List - +from typing import Dict, List, Any +from sqlalchemy import create_engine from plotly import tools from plotly.offline import plot import plotly.graph_objs as go - -from typing import Dict, List, Any -from sqlalchemy import create_engine - from freqtrade.arguments import Arguments from freqtrade.analyze import Analyze from freqtrade import exchange @@ -35,6 +31,7 @@ from freqtrade.persistence import Trade logger = logging.getLogger(__name__) _CONF: Dict[str, Any] = {} + def plot_analyzed_dataframe(args: Namespace) -> None: """ Calls analyze() and plots the returned dataframe @@ -187,7 +184,7 @@ def plot_analyzed_dataframe(args: Namespace) -> None: fig['layout']['yaxis2'].update(title='Volume') fig['layout']['yaxis3'].update(title='MACD') - plot(fig, filename='freqtrade-plot.html') + plot(fig, filename=os.path.join('user_data', 'freqtrade-plot.html')) def plot_parse_args(args: List[str]) -> Namespace: diff --git a/scripts/plot_profit.py b/scripts/plot_profit.py index daa16ddc9..a5ac00169 100755 --- a/scripts/plot_profit.py +++ b/scripts/plot_profit.py @@ -8,9 +8,12 @@ Mandatory Cli parameters: Optional Cli parameters -c / --config: specify configuration file -s / --strategy: strategy to use ---timerange: specify what timerange of data to use. +-d / --datadir: path to pair backtest data +--timerange: specify what timerange of data to use +--export-filename: Specify where the backtest export is located. """ import logging +import os import sys import json from argparse import Namespace @@ -90,7 +93,18 @@ def plot_profit(args: Namespace) -> None: 'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"', config.get('strategy') ) - exit() + exit(0) + + # Load the profits results + try: + filename = args.exportfilename + with open(filename) as file: + data = json.load(file) + except FileNotFoundError: + logger.critical( + 'File "backtest-result.json" not found. This script require backtesting ' + 'results to run.\nPlease run a backtesting with the parameter --export.') + exit(0) # Take pairs from the cli otherwise switch to the pair in the config file if args.pair: @@ -140,18 +154,7 @@ def plot_profit(args: Namespace) -> None: num += 1 avgclose /= num - # Load the profits results - # And make an profits-growth array - - try: - filename = 'backtest-result.json' - with open(filename) as file: - data = json.load(file) - except FileNotFoundError: - logger.critical('File "backtest-result.json" not found. This script require backtesting ' - 'results to run.\nPlease run a backtesting with the parameter --export.') - exit(0) - + # make an profits-growth array pg = make_profit_array(data, num_iterations, min_date, tick_interval, filter_pairs) # @@ -184,7 +187,7 @@ def plot_profit(args: Namespace) -> None: ) fig.append_trace(pair_profit, 3, 1) - plot(fig, filename='freqtrade-profit-plot.html') + plot(fig, filename=os.path.join('user_data', 'freqtrade-profit-plot.html')) def define_index(min_date: int, max_date: int, interval: str) -> int: diff --git a/user_data/backtest_data/.gitkeep b/user_data/backtest_data/.gitkeep new file mode 100644 index 000000000..e69de29bb From af76d5f0e003fa0e3b3475228ef08360eb639cc7 Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Sun, 3 Jun 2018 14:36:22 -0700 Subject: [PATCH 02/12] Breakdown the script in functions the improve maintainability --- scripts/plot_dataframe.py | 46 +++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index 47ce0e746..b031118f0 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -23,6 +23,8 @@ from plotly.offline import plot import plotly.graph_objs as go from freqtrade.arguments import Arguments from freqtrade.analyze import Analyze +from freqtrade.optimize.backtesting import setup_configuration +from freqtrade.configuration import Configuration from freqtrade import exchange import freqtrade.optimize as optimize from freqtrade import persistence @@ -37,12 +39,19 @@ def plot_analyzed_dataframe(args: Namespace) -> None: Calls analyze() and plots the returned dataframe :return: None """ - pair = args.pair.replace('-', '_') + + # Load the configuration + config = setup_configuration(args) + + # Set the pair to audit + pair = args.pair + + # Set timerange to use timerange = Arguments.parse_timerange(args.timerange) - # Init strategy + # Load the strategy try: - analyze = Analyze({'strategy': args.strategy}) + analyze = Analyze(config) except AttributeError: logger.critical( 'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"', @@ -50,13 +59,15 @@ def plot_analyzed_dataframe(args: Namespace) -> None: ) exit() - tick_interval = analyze.strategy.ticker_interval + # Set the ticker to use + tick_interval = analyze.get_ticker_interval() + # Load pqir tickers tickers = {} if args.live: logger.info('Downloading pair.') # Init Bittrex to use public API - exchange.init({'key': '', 'secret': ''}) + exchange.init(config) tickers[pair] = exchange.get_ticker_history(pair, tick_interval) else: tickers = optimize.load_data( @@ -66,20 +77,31 @@ def plot_analyzed_dataframe(args: Namespace) -> None: refresh_pairs=False, timerange=timerange ) - dataframes = analyze.tickerdata_to_dataframe(tickers) - dataframe = dataframes[pair] - dataframe = analyze.populate_buy_trend(dataframe) - dataframe = analyze.populate_sell_trend(dataframe) + # Get trades already made from the DB trades = [] if args.db_url: engine = create_engine('sqlite:///' + args.db_url) persistence.init(_CONF, engine) trades = Trade.query.filter(Trade.pair.is_(pair)).all() + + dataframes = analyze.tickerdata_to_dataframe(tickers) + dataframe = dataframes[pair] + dataframe = analyze.populate_buy_trend(dataframe) + dataframe = analyze.populate_sell_trend(dataframe) + if len(dataframe.index) > 750: logger.warning('Ticker contained more than 750 candles, clipping.') - data = dataframe.tail(750) + + generate_graph( + pair=pair, + trades=trades, + data=dataframe.tail(750) + ) + + +def generate_graph(pair, trades, data): candles = go.Candlestick( x=data.date, @@ -168,6 +190,8 @@ def plot_analyzed_dataframe(args: Namespace) -> None: vertical_spacing=0.0001, ) + # Row 1 + fig.append_trace(candles, 1, 1) fig.append_trace(bb_lower, 1, 1) fig.append_trace(bb_upper, 1, 1) @@ -179,7 +203,7 @@ def plot_analyzed_dataframe(args: Namespace) -> None: fig.append_trace(trade_buys, 1, 1) fig.append_trace(trade_sells, 1, 1) - fig['layout'].update(title=args.pair) + fig['layout'].update(title=pair) fig['layout']['yaxis1'].update(title='Price') fig['layout']['yaxis2'].update(title='Volume') fig['layout']['yaxis3'].update(title='MACD') From 64504e67779afbba995f4d93d6fd86ffb6e8de72 Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Sun, 3 Jun 2018 15:53:15 -0700 Subject: [PATCH 03/12] Add support of --refresh-pairs-cached param --- scripts/plot_dataframe.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index b031118f0..94e72d2dd 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -24,7 +24,6 @@ import plotly.graph_objs as go from freqtrade.arguments import Arguments from freqtrade.analyze import Analyze from freqtrade.optimize.backtesting import setup_configuration -from freqtrade.configuration import Configuration from freqtrade import exchange import freqtrade.optimize as optimize from freqtrade import persistence @@ -52,6 +51,7 @@ def plot_analyzed_dataframe(args: Namespace) -> None: # Load the strategy try: analyze = Analyze(config) + exchange.init(config) except AttributeError: logger.critical( 'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"', @@ -67,17 +67,20 @@ def plot_analyzed_dataframe(args: Namespace) -> None: if args.live: logger.info('Downloading pair.') # Init Bittrex to use public API - exchange.init(config) tickers[pair] = exchange.get_ticker_history(pair, tick_interval) else: tickers = optimize.load_data( datadir=args.datadir, pairs=[pair], ticker_interval=tick_interval, - refresh_pairs=False, + refresh_pairs=config.get('refresh_pairs', False), timerange=timerange ) + # No ticker found, or impossible to download + if tickers == {}: + exit() + # Get trades already made from the DB trades = [] if args.db_url: From 1c75bfdddde8a7c59ee3fa0cec5fdfc87acf9bff Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Sun, 3 Jun 2018 18:41:28 -0700 Subject: [PATCH 04/12] Add more indicators --- scripts/plot_dataframe.py | 167 +++++++++++++++++++++++++++++--------- 1 file changed, 127 insertions(+), 40 deletions(-) diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index 94e72d2dd..247f530d2 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -33,6 +33,20 @@ logger = logging.getLogger(__name__) _CONF: Dict[str, Any] = {} +# Update the global variable TO_DISPLAY to select which indicator you want to display +TO_DISPLAY = { + 'sma': False, # On Row 1 + 'ema': True, # On Row 1 + + 'macd': True, # On Row 3 + 'rsi': False, # On Row 3 + 'fisher_rsi': False, # On Row 3 + 'mfi': False, # On Row 3 + 'slow': False, # On Row 3 + 'fast': False # On Row 3 +} + + def plot_analyzed_dataframe(args: Namespace) -> None: """ Calls analyze() and plots the returned dataframe @@ -88,7 +102,6 @@ def plot_analyzed_dataframe(args: Namespace) -> None: persistence.init(_CONF, engine) trades = Trade.query.filter(Trade.pair.is_(pair)).all() - dataframes = analyze.tickerdata_to_dataframe(tickers) dataframe = dataframes[pair] dataframe = analyze.populate_buy_trend(dataframe) @@ -97,15 +110,35 @@ def plot_analyzed_dataframe(args: Namespace) -> None: if len(dataframe.index) > 750: logger.warning('Ticker contained more than 750 candles, clipping.') - generate_graph( + fig = generate_graph( pair=pair, trades=trades, data=dataframe.tail(750) ) + plot(fig, filename=os.path.join('user_data', 'freqtrade-plot.html')) -def generate_graph(pair, trades, data): +def generate_graph(pair, trades, data) -> None: + """ + 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 + :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) + + # Common information candles = go.Candlestick( x=data.date, open=data.open, @@ -167,51 +200,105 @@ def generate_graph(pair, trades, 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"}, - ) - macd = go.Scattergl(x=data['date'], y=data['macd'], name='MACD') - macdsignal = go.Scattergl(x=data['date'], y=data['macdsignal'], name='MACD signal') - volume = go.Bar(x=data['date'], y=data['volume'], name='Volume') - - fig = tools.make_subplots( - rows=3, - cols=1, - shared_xaxes=True, - row_width=[1, 1, 4], - vertical_spacing=0.0001, - ) - # Row 1 - fig.append_trace(candles, 1, 1) - fig.append_trace(bb_lower, 1, 1) - fig.append_trace(bb_upper, 1, 1) + + 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) + + if TO_DISPLAY['sma'] and 'sma' in data: + sma = generate_scattergl(index='sma', name='SMA', data=data) + fig.append_trace(sma, 1, 1) + + if TO_DISPLAY['ema'] and 'ema10' in data and 'ema50' in data: + ema10 = generate_scattergl(index='ema10', name='EMA10', data=data) + ema50 = generate_scattergl(index='ema50', name='EMA50', data=data) + fig.append_trace(ema10, 1, 1) + fig.append_trace(ema50, 1, 1) + fig.append_trace(buys, 1, 1) fig.append_trace(sells, 1, 1) - fig.append_trace(volume, 2, 1) - fig.append_trace(macd, 3, 1) - fig.append_trace(macdsignal, 3, 1) fig.append_trace(trade_buys, 1, 1) fig.append_trace(trade_sells, 1, 1) - - fig['layout'].update(title=pair) fig['layout']['yaxis1'].update(title='Price') - fig['layout']['yaxis2'].update(title='Volume') - fig['layout']['yaxis3'].update(title='MACD') - plot(fig, filename=os.path.join('user_data', 'freqtrade-plot.html')) + # Row 2 + volume = go.Bar( + x=data['date'], + y=data['volume'], + name='Volume' + ) + fig.append_trace(volume, 2, 1) + fig['layout']['yaxis2'].update(title='Volume') + + # Row 3 (On Row 3, we can only display one indicator) + if TO_DISPLAY['macd'] and 'macd' in data and 'macdsignal' in data: + macd = generate_scattergl(index='macd', name='MACD', data=data) + macdsignal = generate_scattergl(index='macdsignal', name='MACD Signal', data=data) + fig.append_trace(macd, 3, 1) + fig.append_trace(macdsignal, 3, 1) + fig['layout']['yaxis3'].update(title='MACD') + + elif TO_DISPLAY['fast'] and 'fastd' in data and 'fastk' in data: + fastd = generate_scattergl(index='fastd', name='fastd', data=data) + fastk = generate_scattergl(index='fastk', name='fastk', data=data) + fig.append_trace(fastd, 3, 1) + fig.append_trace(fastk, 3, 1) + fig['layout']['yaxis3'].update(title='Stoch Fast') + + elif TO_DISPLAY['slow'] and 'slowd' in data and 'slowk' in data: + slowd = generate_scattergl(index='slowd', name='slowd', data=data) + slowk = generate_scattergl(index='slowk', name='slowk', data=data) + fig.append_trace(slowd, 3, 1) + fig.append_trace(slowk, 3, 1) + fig['layout']['yaxis3'].update(title='Stoch Slow') + + elif TO_DISPLAY['rsi'] and 'rsi' in data: + rsi = generate_scattergl(index='rsi', name='RSI', data=data) + fig.append_trace(rsi, 3, 1) + fig['layout']['yaxis3'].update(title='RSI') + + elif TO_DISPLAY['mfi'] and 'mfi' in data: + mfi = generate_scattergl(index='mfi', name='MFI', data=data) + fig.append_trace(mfi, 3, 1) + fig['layout']['yaxis3'].update(title='MFI') + + elif TO_DISPLAY['fisher_rsi'] and 'fisher_rsi' in data: + fisher_rsi = generate_scattergl(index='fisher_rsi', name='Fisher RSI', data=data) + fig.append_trace(fisher_rsi, 3, 1) + fig['layout']['yaxis3'].update(title='Fisher RSI') + + return fig + + +def generate_scattergl(index, name, data) -> go.Scattergl: + """ + Generate a Scattergl element + :param index: code of the Indicator to generate + :param name: Name that will be display in the graph legend + :param data: Dataframe + :return: Scattergl + """ + return go.Scattergl( + x=data['date'], + y=data[index], + name=name + ) def plot_parse_args(args: List[str]) -> Namespace: From e16fb45d84cc3d013669b00e8c2633f9c9bbfc36 Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Mon, 4 Jun 2018 19:25:25 -0700 Subject: [PATCH 05/12] Fix typo, remove Bittrex mention --- scripts/plot_dataframe.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index 247f530d2..99d17c0b6 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -76,11 +76,10 @@ def plot_analyzed_dataframe(args: Namespace) -> None: # Set the ticker to use tick_interval = analyze.get_ticker_interval() - # Load pqir tickers + # Load pair tickers tickers = {} if args.live: logger.info('Downloading pair.') - # Init Bittrex to use public API tickers[pair] = exchange.get_ticker_history(pair, tick_interval) else: tickers = optimize.load_data( From 662436acd291d4ca4bc385aa47c0f86844f5b74e Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Mon, 4 Jun 2018 20:40:36 -0700 Subject: [PATCH 06/12] Fix typo in Argument() --- freqtrade/arguments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index c9cecb1f5..a95b6ae52 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -141,7 +141,7 @@ class Arguments(object): help='Save backtest results to this filename \ requires --export to be set as well\ Example --export-filename=user_data/backtest_data/backtest_today.json\ - (default: %(default)s', + (default: %(default)s)', type=str, default=os.path.join('user_data', 'backtest_data', 'backtest-result.json'), dest='exportfilename', From 8edcef6d3215a215fd7821060e02783882e822ba Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Mon, 4 Jun 2018 20:41:16 -0700 Subject: [PATCH 07/12] Add two params to select what indicators to display --- scripts/plot_dataframe.py | 139 +++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 76 deletions(-) diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index 99d17c0b6..1a7b5b875 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -5,12 +5,20 @@ Script to display when the bot will buy a specific pair Mandatory Cli parameters: -p / --pair: pair to examine -Optional Cli parameters +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 """ import logging import os @@ -33,20 +41,6 @@ logger = logging.getLogger(__name__) _CONF: Dict[str, Any] = {} -# Update the global variable TO_DISPLAY to select which indicator you want to display -TO_DISPLAY = { - 'sma': False, # On Row 1 - 'ema': True, # On Row 1 - - 'macd': True, # On Row 3 - 'rsi': False, # On Row 3 - 'fisher_rsi': False, # On Row 3 - 'mfi': False, # On Row 3 - 'slow': False, # On Row 3 - 'fast': False # On Row 3 -} - - def plot_analyzed_dataframe(args: Namespace) -> None: """ Calls analyze() and plots the returned dataframe @@ -59,6 +53,15 @@ def plot_analyzed_dataframe(args: Namespace) -> None: # 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) @@ -112,13 +115,14 @@ def plot_analyzed_dataframe(args: Namespace) -> None: fig = generate_graph( pair=pair, trades=trades, - data=dataframe.tail(750) + data=dataframe.tail(750), + args=args ) plot(fig, filename=os.path.join('user_data', 'freqtrade-plot.html')) -def generate_graph(pair, trades, data) -> None: +def generate_graph(pair, trades, data, args) -> tools.make_subplots: """ Generate the graph from the data generated by Backtesting or from DB :param pair: Pair to Display on the graph @@ -136,6 +140,9 @@ def generate_graph(pair, trades, data) -> None: 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') # Common information candles = go.Candlestick( @@ -220,21 +227,11 @@ def generate_graph(pair, trades, data) -> None: fig.append_trace(bb_lower, 1, 1) fig.append_trace(bb_upper, 1, 1) - if TO_DISPLAY['sma'] and 'sma' in data: - sma = generate_scattergl(index='sma', name='SMA', data=data) - fig.append_trace(sma, 1, 1) - - if TO_DISPLAY['ema'] and 'ema10' in data and 'ema50' in data: - ema10 = generate_scattergl(index='ema10', name='EMA10', data=data) - ema50 = generate_scattergl(index='ema50', name='EMA50', data=data) - fig.append_trace(ema10, 1, 1) - fig.append_trace(ema50, 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) - fig['layout']['yaxis1'].update(title='Price') # Row 2 volume = go.Bar( @@ -243,61 +240,33 @@ def generate_graph(pair, trades, data) -> None: name='Volume' ) fig.append_trace(volume, 2, 1) - fig['layout']['yaxis2'].update(title='Volume') - # Row 3 (On Row 3, we can only display one indicator) - if TO_DISPLAY['macd'] and 'macd' in data and 'macdsignal' in data: - macd = generate_scattergl(index='macd', name='MACD', data=data) - macdsignal = generate_scattergl(index='macdsignal', name='MACD Signal', data=data) - fig.append_trace(macd, 3, 1) - fig.append_trace(macdsignal, 3, 1) - fig['layout']['yaxis3'].update(title='MACD') - - elif TO_DISPLAY['fast'] and 'fastd' in data and 'fastk' in data: - fastd = generate_scattergl(index='fastd', name='fastd', data=data) - fastk = generate_scattergl(index='fastk', name='fastk', data=data) - fig.append_trace(fastd, 3, 1) - fig.append_trace(fastk, 3, 1) - fig['layout']['yaxis3'].update(title='Stoch Fast') - - elif TO_DISPLAY['slow'] and 'slowd' in data and 'slowk' in data: - slowd = generate_scattergl(index='slowd', name='slowd', data=data) - slowk = generate_scattergl(index='slowk', name='slowk', data=data) - fig.append_trace(slowd, 3, 1) - fig.append_trace(slowk, 3, 1) - fig['layout']['yaxis3'].update(title='Stoch Slow') - - elif TO_DISPLAY['rsi'] and 'rsi' in data: - rsi = generate_scattergl(index='rsi', name='RSI', data=data) - fig.append_trace(rsi, 3, 1) - fig['layout']['yaxis3'].update(title='RSI') - - elif TO_DISPLAY['mfi'] and 'mfi' in data: - mfi = generate_scattergl(index='mfi', name='MFI', data=data) - fig.append_trace(mfi, 3, 1) - fig['layout']['yaxis3'].update(title='MFI') - - elif TO_DISPLAY['fisher_rsi'] and 'fisher_rsi' in data: - fisher_rsi = generate_scattergl(index='fisher_rsi', name='Fisher RSI', data=data) - fig.append_trace(fisher_rsi, 3, 1) - fig['layout']['yaxis3'].update(title='Fisher RSI') + # Row 3 + fig = generate_row(fig=fig, row=3, raw_indicators=args.indicators2, data=data) return fig -def generate_scattergl(index, name, data) -> go.Scattergl: +def generate_row(fig, row, raw_indicators, data) -> tools.make_subplots: """ - Generate a Scattergl element - :param index: code of the Indicator to generate - :param name: Name that will be display in the graph legend - :param data: Dataframe - :return: Scattergl + Generator all the indicator selected by the user for a specific row """ - return go.Scattergl( - x=data['date'], - y=data[index], - name=name - ) + 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 def plot_parse_args(args: List[str]) -> Namespace: @@ -308,6 +277,24 @@ def plot_parse_args(args: List[str]) -> Namespace: """ 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', + ) + arguments.common_args_parser() arguments.optimizer_shared_options(arguments.parser) arguments.backtesting_options(arguments.parser) From 1b071b1f4a812203507fd38260949c6361779048 Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Mon, 4 Jun 2018 20:42:34 -0700 Subject: [PATCH 08/12] Add example on how to start the script --- scripts/plot_dataframe.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index 1a7b5b875..c3fb1af18 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -19,6 +19,10 @@ Optional Cli parameters Indicators recommended Row 1: sma, ema3, ema5, ema10, ema50 Row 3: macd, rsi, fisher_rsi, mfi, slowd, slowk, fastd, fastk + +Example of usage: +> python3 scripts/plot_dataframe.py --pair BTC/EUR -d user_data/data/ --indicators1 sma,ema3 +--indicators2 fastk,fastd """ import logging import os From 3778bcda24343e1c6d7d23e60b060788478632d9 Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Mon, 4 Jun 2018 20:50:44 -0700 Subject: [PATCH 09/12] Ok! you won Flake8 --- freqtrade/arguments.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index a95b6ae52..bb59627de 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -173,7 +173,7 @@ class Arguments(object): @staticmethod def hyperopt_options(parser: argparse.ArgumentParser) -> None: """ - Parses given arguments for Hyperopt scripts. + Parses given arguments foér Hyperopt scripts. """ parser.add_argument( '-e', '--epochs', @@ -222,7 +222,7 @@ class Arguments(object): @staticmethod def parse_timerange(text: Optional[str]) -> \ - Optional[Tuple[Tuple, Optional[int], Optional[int]]]: + Optional[Tuple[Tuple, Optional[int], Optional[int]]]: """ Parse the value of the argument --timerange to determine what is the range desired :param text: value from --timerange From 947462e1342b50992072157d2bd293d3b24557e8 Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Mon, 4 Jun 2018 21:29:53 -0700 Subject: [PATCH 10/12] Add back 'import os' in Arguments() --- freqtrade/arguments.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index bb59627de..ee57dc1be 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -2,6 +2,7 @@ This module contains the argument manager class """ +import os import argparse import logging import re From c29c13dfd72e72832821f3b12aa1eb67432903b6 Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Mon, 4 Jun 2018 22:42:24 -0700 Subject: [PATCH 11/12] Fix a typo in Arguments() comment --- freqtrade/arguments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index ee57dc1be..d79a52af2 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -174,7 +174,7 @@ class Arguments(object): @staticmethod def hyperopt_options(parser: argparse.ArgumentParser) -> None: """ - Parses given arguments foér Hyperopt scripts. + Parses given arguments for Hyperopt scripts. """ parser.add_argument( '-e', '--epochs', From 5024cd52afd6d27d8192d5feffd367d99dedb3bf Mon Sep 17 00:00:00 2001 From: Gerald Lonlas Date: Mon, 4 Jun 2018 23:49:16 -0700 Subject: [PATCH 12/12] Update docstring for generate_graph() --- scripts/plot_dataframe.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index c3fb1af18..e7737a5c7 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -132,6 +132,7 @@ def generate_graph(pair, trades, data, args) -> tools.make_subplots: :param pair: Pair to Display on the graph :param trades: All trades created :param data: Dataframe + :param args: sys.argv that contrains the two params indicators1, and indicators2 :return: None """