From 896c9d34fdc5b4e13f158217ac6432184ace4c76 Mon Sep 17 00:00:00 2001 From: Gianluca Puglia Date: Tue, 22 Jan 2019 22:41:53 +0100 Subject: [PATCH 01/11] Added total profit column do backtest result --- freqtrade/optimize/backtesting.py | 23 +++++++++++++------- freqtrade/tests/optimize/test_backtesting.py | 22 ++++++++++--------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 38bbe13d4..05624d9bd 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -100,11 +100,13 @@ class Backtesting(object): :return: pretty printed table with tabulate as str """ stake_currency = str(self.config.get('stake_currency')) + max_open_trades = self.config.get('max_open_trades') - floatfmt = ('s', 'd', '.2f', '.2f', '.8f', 'd', '.1f', '.1f') + floatfmt = ('s', 'd', '.2f', '.2f', '.8f', '.2f', 'd', '.1f', '.1f') tabular_data = [] headers = ['pair', 'buy count', 'avg profit %', 'cum profit %', - 'total profit ' + stake_currency, 'avg duration', 'profit', 'loss'] + 'tot profit ' + stake_currency, 'tot profit %', 'avg duration', + 'profit', 'loss'] for pair in data: result = results[results.pair == pair] if skip_nan and result.profit_abs.isnull().all(): @@ -116,6 +118,7 @@ class Backtesting(object): result.profit_percent.mean() * 100.0, result.profit_percent.sum() * 100.0, result.profit_abs.sum(), + result.profit_percent.sum() * 100.0 / max_open_trades, str(timedelta( minutes=round(result.trade_duration.mean()))) if not result.empty else '0:00', len(result[result.profit_abs > 0]), @@ -129,6 +132,7 @@ class Backtesting(object): results.profit_percent.mean() * 100.0, results.profit_percent.sum() * 100.0, results.profit_abs.sum(), + results.profit_percent.sum() * 100.0 / max_open_trades, str(timedelta( minutes=round(results.trade_duration.mean()))) if not results.empty else '0:00', len(results[results.profit_abs > 0]), @@ -153,11 +157,13 @@ class Backtesting(object): Generate summary table per strategy """ stake_currency = str(self.config.get('stake_currency')) + max_open_trades = self.config.get('max_open_trades') - floatfmt = ('s', 'd', '.2f', '.2f', '.8f', 'd', '.1f', '.1f') + floatfmt = ('s', 'd', '.2f', '.2f', '.8f', '.2f', 'd', '.1f', '.1f') tabular_data = [] headers = ['Strategy', 'buy count', 'avg profit %', 'cum profit %', - 'total profit ' + stake_currency, 'avg duration', 'profit', 'loss'] + 'tot profit ' + stake_currency, 'tot profit %', 'avg duration', + 'profit', 'loss'] for strategy, results in all_results.items(): tabular_data.append([ strategy, @@ -165,6 +171,7 @@ class Backtesting(object): results.profit_percent.mean() * 100.0, results.profit_percent.sum() * 100.0, results.profit_abs.sum(), + results.profit_percent.sum() * 100.0 / max_open_trades, str(timedelta( minutes=round(results.trade_duration.mean()))) if not results.empty else '0:00', len(results[results.profit_abs > 0]), @@ -430,18 +437,18 @@ class Backtesting(object): strategy if len(self.strategylist) > 1 else None) print(f"Result for strategy {strategy}") - print(' BACKTESTING REPORT '.center(119, '=')) + print(' BACKTESTING REPORT '.center(133, '=')) print(self._generate_text_table(data, results)) - print(' SELL REASON STATS '.center(119, '=')) + print(' SELL REASON STATS '.center(133, '=')) print(self._generate_text_table_sell_reason(data, results)) - print(' LEFT OPEN TRADES REPORT '.center(119, '=')) + print(' LEFT OPEN TRADES REPORT '.center(133, '=')) print(self._generate_text_table(data, results.loc[results.open_at_end], True)) print() if len(all_results) > 1: # Print Strategy summary table - print(' Strategy Summary '.center(119, '=')) + print(' Strategy Summary '.center(133, '=')) print(self._generate_text_table_strategy(all_results)) print('\nFor more details, please look at the detail tables above') diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 5ab44baad..fbcbe4c55 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -341,6 +341,7 @@ def test_tickerdata_to_dataframe_bt(default_conf, mocker) -> None: def test_generate_text_table(default_conf, mocker): patch_exchange(mocker) + default_conf['max_open_trades'] = 2 backtesting = Backtesting(default_conf) results = pd.DataFrame( @@ -356,13 +357,13 @@ def test_generate_text_table(default_conf, mocker): result_str = ( '| pair | buy count | avg profit % | cum profit % | ' - 'total profit BTC | avg duration | profit | loss |\n' + 'tot profit BTC | tot profit % | avg duration | profit | loss |\n' '|:--------|------------:|---------------:|---------------:|' - '-------------------:|:---------------|---------:|-------:|\n' - '| ETH/BTC | 2 | 15.00 | 30.00 | ' - '0.60000000 | 0:20:00 | 2 | 0 |\n' - '| TOTAL | 2 | 15.00 | 30.00 | ' - '0.60000000 | 0:20:00 | 2 | 0 |' + '-----------------:|---------------:|:---------------|---------:|-------:|\n' + '| ETH/BTC | 2 | 15.00 | 30.00 | ' + '0.60000000 | 15.00 | 0:20:00 | 2 | 0 |\n' + '| TOTAL | 2 | 15.00 | 30.00 | ' + '0.60000000 | 15.00 | 0:20:00 | 2 | 0 |' ) assert backtesting._generate_text_table(data={'ETH/BTC': {}}, results=results) == result_str @@ -398,6 +399,7 @@ def test_generate_text_table_strategyn(default_conf, mocker): Test Backtesting.generate_text_table_sell_reason() method """ patch_exchange(mocker) + default_conf['max_open_trades'] = 2 backtesting = Backtesting(default_conf) results = {} results['ETH/BTC'] = pd.DataFrame( @@ -425,13 +427,13 @@ def test_generate_text_table_strategyn(default_conf, mocker): result_str = ( '| Strategy | buy count | avg profit % | cum profit % ' - '| total profit BTC | avg duration | profit | loss |\n' + '| tot profit BTC | tot profit % | avg duration | profit | loss |\n' '|:-----------|------------:|---------------:|---------------:' - '|-------------------:|:---------------|---------:|-------:|\n' + '|-----------------:|---------------:|:---------------|---------:|-------:|\n' '| ETH/BTC | 3 | 20.00 | 60.00 ' - '| 1.10000000 | 0:17:00 | 3 | 0 |\n' + '| 1.10000000 | 30.00 | 0:17:00 | 3 | 0 |\n' '| LTC/BTC | 3 | 30.00 | 90.00 ' - '| 1.30000000 | 0:20:00 | 3 | 0 |' + '| 1.30000000 | 45.00 | 0:20:00 | 3 | 0 |' ) print(backtesting._generate_text_table_strategy(all_results=results)) assert backtesting._generate_text_table_strategy(all_results=results) == result_str From dcceb40fab456ddb742344126c3e7972c6753ff4 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 24 Jan 2019 13:33:07 +0100 Subject: [PATCH 02/11] Update ccxt from 1.18.144 to 1.18.146 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 25e337306..15132a026 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.144 +ccxt==1.18.146 SQLAlchemy==1.2.16 python-telegram-bot==11.1.0 arrow==0.13.0 From 38d293cc267002bc36bdfd741dfacc291df65885 Mon Sep 17 00:00:00 2001 From: Gianluca Puglia Date: Thu, 24 Jan 2019 15:24:38 +0100 Subject: [PATCH 03/11] Updated doc --- docs/backtesting.md | 92 +++++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 0a60d2db3..f6c9dd4d1 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -166,53 +166,65 @@ The most important in the backtesting is to understand the result. A backtesting result will look like that: ``` -======================================== BACKTESTING REPORT ========================================= -| pair | buy count | avg profit % | total profit BTC | avg duration | profit | loss | -|:---------|------------:|---------------:|-------------------:|---------------:|---------:|-------:| -| ETH/BTC | 44 | 0.18 | 0.00159118 | 50.9 | 44 | 0 | -| LTC/BTC | 27 | 0.10 | 0.00051931 | 103.1 | 26 | 1 | -| ETC/BTC | 24 | 0.05 | 0.00022434 | 166.0 | 22 | 2 | -| DASH/BTC | 29 | 0.18 | 0.00103223 | 192.2 | 29 | 0 | -| ZEC/BTC | 65 | -0.02 | -0.00020621 | 202.7 | 62 | 3 | -| XLM/BTC | 35 | 0.02 | 0.00012877 | 242.4 | 32 | 3 | -| BCH/BTC | 12 | 0.62 | 0.00149284 | 50.0 | 12 | 0 | -| POWR/BTC | 21 | 0.26 | 0.00108215 | 134.8 | 21 | 0 | -| ADA/BTC | 54 | -0.19 | -0.00205202 | 191.3 | 47 | 7 | -| XMR/BTC | 24 | -0.43 | -0.00206013 | 120.6 | 20 | 4 | -| TOTAL | 335 | 0.03 | 0.00175246 | 157.9 | 315 | 20 | -2018-06-13 06:57:27,347 - freqtrade.optimize.backtesting - INFO - -====================================== LEFT OPEN TRADES REPORT ====================================== -| pair | buy count | avg profit % | total profit BTC | avg duration | profit | loss | -|:---------|------------:|---------------:|-------------------:|---------------:|---------:|-------:| -| ETH/BTC | 3 | 0.16 | 0.00009619 | 25.0 | 3 | 0 | -| LTC/BTC | 1 | -1.00 | -0.00020118 | 1085.0 | 0 | 1 | -| ETC/BTC | 2 | -1.80 | -0.00071933 | 1092.5 | 0 | 2 | -| DASH/BTC | 0 | nan | 0.00000000 | nan | 0 | 0 | -| ZEC/BTC | 3 | -4.27 | -0.00256826 | 1301.7 | 0 | 3 | -| XLM/BTC | 3 | -1.11 | -0.00066744 | 965.0 | 0 | 3 | -| BCH/BTC | 0 | nan | 0.00000000 | nan | 0 | 0 | -| POWR/BTC | 0 | nan | 0.00000000 | nan | 0 | 0 | -| ADA/BTC | 7 | -3.58 | -0.00503604 | 850.0 | 0 | 7 | -| XMR/BTC | 4 | -3.79 | -0.00303456 | 291.2 | 0 | 4 | -| TOTAL | 23 | -2.63 | -0.01213062 | 750.4 | 3 | 20 | - +========================================================= BACKTESTING REPORT ======================================================== +| pair | buy count | avg profit % | cum profit % | tot profit BTC | tot profit % | avg duration | profit | loss | +|:---------|------------:|---------------:|---------------:|-----------------:|---------------:|:---------------|---------:|-------:| +| ADA/BTC | 35 | -0.11 | -3.88 | -0.00019428 | -1.94 | 4:35:00 | 14 | 21 | +| ARK/BTC | 11 | -0.41 | -4.52 | -0.00022647 | -2.26 | 2:03:00 | 3 | 8 | +| BTS/BTC | 32 | 0.31 | 9.78 | 0.00048938 | 4.89 | 5:05:00 | 18 | 14 | +| DASH/BTC | 13 | -0.08 | -1.07 | -0.00005343 | -0.53 | 4:39:00 | 6 | 7 | +| ENG/BTC | 18 | 1.36 | 24.54 | 0.00122807 | 12.27 | 2:50:00 | 8 | 10 | +| EOS/BTC | 36 | 0.08 | 3.06 | 0.00015304 | 1.53 | 3:34:00 | 16 | 20 | +| ETC/BTC | 26 | 0.37 | 9.51 | 0.00047576 | 4.75 | 6:14:00 | 11 | 15 | +| ETH/BTC | 33 | 0.30 | 9.96 | 0.00049856 | 4.98 | 7:31:00 | 16 | 17 | +| IOTA/BTC | 32 | 0.03 | 1.09 | 0.00005444 | 0.54 | 3:12:00 | 14 | 18 | +| LSK/BTC | 15 | 1.75 | 26.26 | 0.00131413 | 13.13 | 2:58:00 | 6 | 9 | +| LTC/BTC | 32 | -0.04 | -1.38 | -0.00006886 | -0.69 | 4:49:00 | 11 | 21 | +| NANO/BTC | 17 | 1.26 | 21.39 | 0.00107058 | 10.70 | 1:55:00 | 10 | 7 | +| NEO/BTC | 23 | 0.82 | 18.97 | 0.00094936 | 9.48 | 2:59:00 | 10 | 13 | +| REQ/BTC | 9 | 1.17 | 10.54 | 0.00052734 | 5.27 | 3:47:00 | 4 | 5 | +| XLM/BTC | 16 | 1.22 | 19.54 | 0.00097800 | 9.77 | 3:15:00 | 7 | 9 | +| XMR/BTC | 23 | -0.18 | -4.13 | -0.00020696 | -2.07 | 5:30:00 | 12 | 11 | +| XRP/BTC | 35 | 0.66 | 22.96 | 0.00114897 | 11.48 | 3:49:00 | 12 | 23 | +| ZEC/BTC | 22 | -0.46 | -10.18 | -0.00050971 | -5.09 | 2:22:00 | 7 | 15 | +| TOTAL | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 | 243 | +========================================================= SELL REASON STATS ========================================================= +| Sell Reason | Count | +|:-------------------|--------:| +| trailing_stop_loss | 205 | +| stop_loss | 166 | +| sell_signal | 56 | +| force_sell | 2 | +====================================================== LEFT OPEN TRADES REPORT ====================================================== +| pair | buy count | avg profit % | cum profit % | tot profit BTC | tot profit % | avg duration | profit | loss | +|:---------|------------:|---------------:|---------------:|-----------------:|---------------:|:---------------|---------:|-------:| +| ADA/BTC | 1 | 0.89 | 0.89 | 0.00004434 | 0.44 | 6:00:00 | 1 | 0 | +| LTC/BTC | 1 | 0.68 | 0.68 | 0.00003421 | 0.34 | 2:00:00 | 1 | 0 | +| TOTAL | 2 | 0.78 | 1.57 | 0.00007855 | 0.78 | 4:00:00 | 2 | 0 | ``` The 1st table will contain all trades the bot made. -The 2nd table will contain all trades the bot had to `forcesell` at the end of the backtest period to present a full picture. +The 2nd table will contain a recap of sell reasons. + +The 3rd table will contain all trades the bot had to `forcesell` at the end of the backtest period to present a full picture. These trades are also included in the first table, but are extracted separately for clarity. The last line will give you the overall performance of your strategy, here: ``` -TOTAL 419 -0.41 -0.00348593 52.9 +| TOTAL | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 | 243 | ``` -We understand the bot has made `419` trades for an average duration of -`52.9` min, with a performance of `-0.41%` (loss), that means it has -lost a total of `-0.00348593 BTC`. +We understand the bot has made `429` trades for an average duration of +`4:12:00`, with a performance of `76.20%` (profit), that means it has +earned a total of `0.00762792 BTC` starting with a capital of 0.01 BTC. + +The column `avg profit %` shows the average profit for all trades made while the column `cum profit %` sums all the profits/losses. +The column `tot profit %` shows instead the total profit % in relation to allocated capital +(`max_open_trades * stake_amount`). In the above results we have `max_open_trades=2 stake_amount=0.005` in config +so `(76.20/100) * (0.005 * 2) =~ 0.00762792 BTC`. As you will see your strategy performance will be influenced by your buy strategy, your sell strategy, and also by the `minimal_roi` and @@ -251,11 +263,11 @@ There will be an additional table comparing win/losses of the different strategi Detailed output for all strategies one after the other will be available, so make sure to scroll up. ``` -=================================================== Strategy Summary ==================================================== -| Strategy | buy count | avg profit % | cum profit % | total profit ETH | avg duration | profit | loss | -|:-----------|------------:|---------------:|---------------:|-------------------:|:----------------|---------:|-------:| -| Strategy1 | 19 | -0.76 | -14.39 | -0.01440287 | 15:48:00 | 15 | 4 | -| Strategy2 | 6 | -2.73 | -16.40 | -0.01641299 | 1 day, 14:12:00 | 3 | 3 | +=========================================================== Strategy Summary =========================================================== +| Strategy | buy count | avg profit % | cum profit % | tot profit BTC | tot profit % | avg duration | profit | loss | +|:------------|------------:|---------------:|---------------:|-----------------:|---------------:|:---------------|---------:|-------:| +| Strategy1 | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 | 243 | +| Strategy2 | 1487 | -0.13 | -197.58 | -0.00988917 | -98.79 | 4:43:00 | 662 | 825 | ``` ## Next step From a97b3ab04a88513c9a7a81613e6ff1da7985d3df Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 25 Jan 2019 13:33:29 +0100 Subject: [PATCH 04/11] Update ccxt from 1.18.146 to 1.18.152 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 15132a026..24d1996e5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.146 +ccxt==1.18.152 SQLAlchemy==1.2.16 python-telegram-bot==11.1.0 arrow==0.13.0 From bd24646822c1b7c0fb4e1355dcddf3a05c7d9887 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 25 Jan 2019 13:33:30 +0100 Subject: [PATCH 05/11] Update tabulate from 0.8.2 to 0.8.3 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 24d1996e5..eb1054f62 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ scipy==1.2.0 jsonschema==2.6.0 numpy==1.16.0 TA-Lib==0.4.17 -tabulate==0.8.2 +tabulate==0.8.3 coinmarketcap==5.0.3 # Required for hyperopt From eec727639358dd8c2ca6c95475dfa38005cd4d9c Mon Sep 17 00:00:00 2001 From: AxelCh Date: Wed, 23 Jan 2019 14:11:05 -0400 Subject: [PATCH 06/11] fix crash when backtest-result.json not exist --- docs/plotting.md | 11 +- freqtrade/arguments.py | 4 +- freqtrade/tests/test_arguments.py | 2 +- scripts/plot_dataframe.py | 841 ++++++++++++++++-------------- scripts/plot_profit.py | 4 +- 5 files changed, 473 insertions(+), 389 deletions(-) diff --git a/docs/plotting.md b/docs/plotting.md index 3f2d38d0b..a9b191e75 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -15,7 +15,7 @@ At least version 2.3.0 is required. Usage for the price plotter: ``` -script/plot_dataframe.py [-h] [-p pair] [--live] +script/plot_dataframe.py [-h] [-p pairs] [--live] ``` Example @@ -23,11 +23,16 @@ Example python scripts/plot_dataframe.py -p BTC/ETH ``` -The `-p` pair argument, can be used to specify what -pair you would like to plot. +The `-p` pairs argument, can be used to specify +pairs you would like to plot. **Advanced use** +To plot multiple pairs, separate them with a comma: +``` +python scripts/plot_dataframe.py -p BTC/ETH,XRP/ETH +``` + To plot the current live price use the `--live` flag: ``` python scripts/plot_dataframe.py -p BTC/ETH --live diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index b86d3502a..9b1b9a925 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -352,9 +352,9 @@ class Arguments(object): Parses given arguments for scripts. """ self.parser.add_argument( - '-p', '--pair', + '-p', '--pairs', help='Show profits for only this pairs. Pairs are comma-separated.', - dest='pair', + dest='pairs', default=None ) diff --git a/freqtrade/tests/test_arguments.py b/freqtrade/tests/test_arguments.py index d28ab30af..042d43ed2 100644 --- a/freqtrade/tests/test_arguments.py +++ b/freqtrade/tests/test_arguments.py @@ -47,7 +47,7 @@ def test_scripts_options() -> None: arguments = Arguments(['-p', 'ETH/BTC'], '') arguments.scripts_options() args = arguments.get_parsed_arg() - assert args.pair == 'ETH/BTC' + assert args.pairs == 'ETH/BTC' def test_parse_args_version() -> None: diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index ae9cd7f1d..b1c792a22 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -1,381 +1,460 @@ -#!/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 - -Example of usage: -> python3 scripts/plot_dataframe.py --pair BTC/EUR -d user_data/data/ --indicators1 sma,ema3 ---indicators2 fastk,fastd -""" -import json -import logging -import sys -from argparse import Namespace -from pathlib import Path -from typing import Dict, List, Any - -import pandas as pd -import plotly.graph_objs as go -import pytz - -from plotly import tools -from plotly.offline import plot - -from freqtrade import persistence -from freqtrade.arguments import Arguments, TimeRange -from freqtrade.data import history -from freqtrade.exchange import Exchange -from freqtrade.optimize.backtesting import setup_configuration -from freqtrade.persistence import Trade -from freqtrade.resolvers import StrategyResolver - -logger = logging.getLogger(__name__) -_CONF: Dict[str, Any] = {} - -timeZone = pytz.UTC - - -def load_trades(args: Namespace, pair: str, timerange: TimeRange) -> pd.DataFrame: - trades: pd.DataFrame = pd.DataFrame() - if args.db_url: - persistence.init(_CONF) - columns = ["pair", "profit", "opents", "closets", "open_rate", "close_rate", "duration"] - - for x in Trade.query.all(): - print("date: {}".format(x.open_date)) - - trades = pd.DataFrame([(t.pair, t.calc_profit(), - t.open_date.replace(tzinfo=timeZone), - t.close_date.replace(tzinfo=timeZone) if t.close_date else None, - t.open_rate, t.close_rate, - t.close_date.timestamp() - t.open_date.timestamp() if t.close_date else None) - for t in Trade.query.filter(Trade.pair.is_(pair)).all()], - columns=columns) - - elif 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", "sell_reason"] - 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) - return trades - - -def plot_analyzed_dataframe(args: Namespace) -> None: - """ - Calls analyze() and plots the returned dataframe - :return: None - """ - global _CONF - - # Load the configuration - _CONF.update(setup_configuration(args)) - - print(_CONF) - # 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: - strategy = StrategyResolver(_CONF).strategy - 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 = strategy.ticker_interval - - # Load pair tickers - tickers = {} - if args.live: - logger.info('Downloading pair.') - exchange.refresh_tickers([pair], tick_interval) - tickers[pair] = exchange.klines(pair) - else: - tickers = history.load_data( - datadir=Path(_CONF.get("datadir")), - pairs=[pair], - ticker_interval=tick_interval, - refresh_pairs=_CONF.get('refresh_pairs', False), - timerange=timerange, - exchange=Exchange(_CONF) - ) - - # No ticker found, or impossible to download - if tickers == {}: - exit() - - # Get trades already made from the DB - trades = load_trades(args, pair, timerange) - - dataframes = strategy.tickerdata_to_dataframe(tickers) - - dataframe = dataframes[pair] - dataframe = strategy.advise_buy(dataframe, {'pair': pair}) - dataframe = strategy.advise_sell(dataframe, {'pair': pair}) - - 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']] - fig = generate_graph( - pair=pair, - trades=trades, - data=dataframe, - args=args - ) - - plot(fig, filename=str(Path('user_data').joinpath('freqtrade-plot.html'))) - - -def generate_graph(pair, trades: pd.DataFrame, data: pd.DataFrame, args) -> tools.make_subplots: - """ - 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 - :param args: sys.argv that contrains the two params indicators1, and indicators2 - :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') - - # 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( - 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( - 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' - ) - ) - - # Row 1 - fig.append_trace(candles, 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': 'rgba(255,255,255,0)'}, - ) - 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': 'rgba(255,255,255,0)'}, - ) - 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) - - # 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) - - return fig - - -def generate_row(fig, row, raw_indicators, data) -> tools.make_subplots: - """ - Generator all the indicator selected by the user for a specific row - """ - 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: - """ - 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', - ) - 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=int, - ) - arguments.common_args_parser() - arguments.optimizer_shared_options(arguments.parser) - arguments.backtesting_options(arguments.parser) - return arguments.parse_args() - - -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) - ) - - -if __name__ == '__main__': - main(sys.argv[1:]) +#!/usr/bin/env python3 +""" +Script to display when the bot will buy on specific pair(s) + +Mandatory Cli parameters: +-p / --pairs: pair(s) to examine + +Option but recommended +-s / --strategy: strategy to use + + +Optional Cli parameters +-d / --datadir: path to pair(s) backtest data +--timerange: specify what timerange of data to use. +-l / --live: Live, to download the latest ticker for the pair(s) +-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 + +Example of usage: +> python3 scripts/plot_dataframe.py --pairs BTC/EUR,XRP/BTC -d user_data/data/ --indicators1 sma,ema3 +--indicators2 fastk,fastd +""" +import json +import logging +import sys +import os +from argparse import Namespace +from pathlib import Path +from typing import Dict, List, Any + +import pandas as pd +import plotly.graph_objs as go +import pytz + +from plotly import tools +from plotly.offline import plot + +from freqtrade import persistence +from freqtrade.arguments import Arguments, TimeRange +from freqtrade.data import history +from freqtrade.exchange import Exchange +from freqtrade.optimize.backtesting import setup_configuration +from freqtrade.persistence import Trade +from freqtrade.resolvers import StrategyResolver + +logger = logging.getLogger(__name__) +_CONF: Dict[str, Any] = {} + +timeZone = pytz.UTC + + +def load_trades(args: Namespace, pair: str, timerange: TimeRange) -> pd.DataFrame: + trades: pd.DataFrame = pd.DataFrame() + if args.db_url: + persistence.init(_CONF) + columns = ["pair", "profit", "opents", "closets", "open_rate", "close_rate", "duration"] + + for x in Trade.query.all(): + print("date: {}".format(x.open_date)) + + trades = pd.DataFrame([(t.pair, t.calc_profit(), + t.open_date.replace(tzinfo=timeZone), + t.close_date.replace(tzinfo=timeZone) if t.close_date else None, + t.open_rate, t.close_rate, + t.close_date.timestamp() - t.open_date.timestamp() + if t.close_date else None) + for t in Trade.query.filter(Trade.pair.is_(pair)).all()], + columns=columns) + + elif 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", "sell_reason"] + if os.path.exists(file): + 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) + else: + trades = pd.DataFrame([], columns=columns) + + return trades + + +def generate_plot_file(fig, pair, tick_interval, is_last) -> None: + """ + Generate a plot html file from pre populated fig plotly object + :return: None + """ + logger.info('Generate plot file for %s', pair) + + pair_name = pair.replace("/", "_") + file_name = 'freqtrade-plot-' + pair_name + '-' + tick_interval + '.html' + + if not os.path.exists('user_data/plots'): + try: + os.makedirs('user_data/plots') + except OSError as e: + raise + + plot(fig, filename=str(Path('user_data/plots').joinpath(file_name)), auto_open=False) + if is_last: + plot(fig, filename=str(Path('user_data').joinpath('freqtrade-plot.html')), auto_open=False) + + +def get_trading_env(args: Namespace): + """ + Initalize freqtrade Exchange and Strategy, split pairs recieved in parameter + :return: Strategy + """ + global _CONF + + # Load the configuration + _CONF.update(setup_configuration(args)) + print(_CONF) + + pairs = args.pairs.split(',') + if pairs is None: + logger.critical('Parameter --pairs mandatory;. E.g --pairs ETH/BTC,XRP/BTC') + exit() + + # Load the strategy + try: + strategy = StrategyResolver(_CONF).strategy + exchange = Exchange(_CONF) + except AttributeError: + logger.critical( + 'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"', + args.strategy + ) + exit() + + return [strategy, exchange, pairs] + + +def get_tickers_data(strategy, exchange, pairs: List[str], args): + """ + Get tickers data for each pairs on live or local, option defined in args + :return: dictinnary of tickers. output format: {'pair': tickersdata} + """ + + tick_interval = strategy.ticker_interval + timerange = Arguments.parse_timerange(args.timerange) + + tickers = {} + if args.live: + logger.info('Downloading pairs.') + exchange.refresh_tickers(pairs, tick_interval) + for pair in pairs: + tickers[pair] = exchange.klines(pair) + else: + tickers = history.load_data( + datadir=Path(_CONF.get("datadir")), + pairs=pairs, + ticker_interval=tick_interval, + refresh_pairs=_CONF.get('refresh_pairs', False), + timerange=timerange, + exchange=Exchange(_CONF) + ) + + # No ticker found, impossible to download, len mismatch + for pair, data in tickers.copy().items(): + logger.debug("checking tickers data of pair: %s", pair) + logger.debug("data.empty: %s", data.empty) + logger.debug("len(data): %s", len(data)) + if data.empty: + del tickers[pair] + logger.info( + 'An issue occured while retreiving datas of %s pair, please retry ' + 'using -l option for live or --refresh-pairs-cached', pair) + return tickers + + +def generate_dataframe(strategy, tickers, pair) -> pd.DataFrame: + """ + Get tickers then Populate strategy indicators and signals, then return the full dataframe + :return: the DataFrame of a pair + """ + + dataframes = strategy.tickerdata_to_dataframe(tickers) + dataframe = dataframes[pair] + dataframe = strategy.advise_buy(dataframe, {'pair': pair}) + dataframe = strategy.advise_sell(dataframe, {'pair': pair}) + + return dataframe + + +def extract_trades_of_period(dataframe, trades) -> pd.DataFrame: + """ + Compare trades and backtested pair DataFrames to get trades performed on backtested period + :return: the DataFrame of a trades of period + """ + trades = trades.loc[trades['opents'] >= dataframe.iloc[0]['date']] + return trades + + +def generate_graph(pair, trades: pd.DataFrame, data: pd.DataFrame, args) -> tools.make_subplots: + """ + 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 + :param args: sys.argv that contrains the two params indicators1, and indicators2 + :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' + ) + + 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( + 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( + 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' + ) + ) + + # Row 1 + fig.append_trace(candles, 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': 'rgba(255,255,255,0)'}, + ) + 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': 'rgba(255,255,255,0)'}, + ) + 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) + + # 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) + + return fig + + +def generate_row(fig, row, raw_indicators, data) -> tools.make_subplots: + """ + Generator all the indicator selected by the user for a specific row + """ + 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: + """ + 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', + ) + 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=int, + ) + arguments.common_args_parser() + arguments.optimizer_shared_options(arguments.parser) + arguments.backtesting_options(arguments.parser) + return arguments.parse_args() + + +def analyse_and_plot_pairs(args: Namespace): + """ + From arguments provided in cli: + -Initialise backtest env + -Get tickers data + -Generate Dafaframes populated with indicators and signals + -Load trades excecuted on same periods + -Generate Plotly plot objects + -Generate plot files + :return: None + """ + strategy, exchange, pairs = get_trading_env(args) + # Set timerange to use + timerange = Arguments.parse_timerange(args.timerange) + tick_interval = strategy.ticker_interval + + tickers = get_tickers_data(strategy, exchange, pairs, args) + pair_counter = 0 + for pair, data in tickers.items(): + pair_counter += 1 + logger.info("analyse pair %s", pair) + tickers = {} + tickers[pair] = data + dataframe = generate_dataframe(strategy, tickers, pair) + + trades = load_trades(args, pair, timerange) + trades = extract_trades_of_period(dataframe, trades) + + fig = generate_graph( + pair=pair, + trades=trades, + data=dataframe, + args=args + ) + + is_last = (False, True)[pair_counter == len(tickers)] + generate_plot_file(fig, pair, tick_interval, is_last) + + logger.info('End of ploting process %s plots generated', pair_counter) + + +def main(sysargv: List[str]) -> None: + """ + This function will initiate the bot and start the trading loop. + :return: None + """ + logger.info('Starting Plot Dataframe') + analyse_and_plot_pairs( + plot_parse_args(sysargv) + ) + exit() + + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/scripts/plot_profit.py b/scripts/plot_profit.py index a1561bc89..c12bd966d 100755 --- a/scripts/plot_profit.py +++ b/scripts/plot_profit.py @@ -107,8 +107,8 @@ def plot_profit(args: Namespace) -> None: exit(1) # Take pairs from the cli otherwise switch to the pair in the config file - if args.pair: - filter_pairs = args.pair + if args.pairs: + filter_pairs = args.pairs filter_pairs = filter_pairs.split(',') else: filter_pairs = config['exchange']['pair_whitelist'] From b840b9f53a3557f2e6930116b76123b322a01421 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 24 Jan 2019 13:33:07 +0100 Subject: [PATCH 07/11] Update ccxt from 1.18.144 to 1.18.146 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 25e337306..15132a026 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.144 +ccxt==1.18.146 SQLAlchemy==1.2.16 python-telegram-bot==11.1.0 arrow==0.13.0 From 22e7ad8ec1e476e4190a38e18e28a020b4f8530d Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 25 Jan 2019 06:42:29 +0100 Subject: [PATCH 08/11] Change back to LF lineendings --- scripts/plot_dataframe.py | 920 +++++++++++++++++++------------------- 1 file changed, 460 insertions(+), 460 deletions(-) diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index b1c792a22..bb9c04206 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -1,460 +1,460 @@ -#!/usr/bin/env python3 -""" -Script to display when the bot will buy on specific pair(s) - -Mandatory Cli parameters: --p / --pairs: pair(s) to examine - -Option but recommended --s / --strategy: strategy to use - - -Optional Cli parameters --d / --datadir: path to pair(s) backtest data ---timerange: specify what timerange of data to use. --l / --live: Live, to download the latest ticker for the pair(s) --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 - -Example of usage: -> python3 scripts/plot_dataframe.py --pairs BTC/EUR,XRP/BTC -d user_data/data/ --indicators1 sma,ema3 ---indicators2 fastk,fastd -""" -import json -import logging -import sys -import os -from argparse import Namespace -from pathlib import Path -from typing import Dict, List, Any - -import pandas as pd -import plotly.graph_objs as go -import pytz - -from plotly import tools -from plotly.offline import plot - -from freqtrade import persistence -from freqtrade.arguments import Arguments, TimeRange -from freqtrade.data import history -from freqtrade.exchange import Exchange -from freqtrade.optimize.backtesting import setup_configuration -from freqtrade.persistence import Trade -from freqtrade.resolvers import StrategyResolver - -logger = logging.getLogger(__name__) -_CONF: Dict[str, Any] = {} - -timeZone = pytz.UTC - - -def load_trades(args: Namespace, pair: str, timerange: TimeRange) -> pd.DataFrame: - trades: pd.DataFrame = pd.DataFrame() - if args.db_url: - persistence.init(_CONF) - columns = ["pair", "profit", "opents", "closets", "open_rate", "close_rate", "duration"] - - for x in Trade.query.all(): - print("date: {}".format(x.open_date)) - - trades = pd.DataFrame([(t.pair, t.calc_profit(), - t.open_date.replace(tzinfo=timeZone), - t.close_date.replace(tzinfo=timeZone) if t.close_date else None, - t.open_rate, t.close_rate, - t.close_date.timestamp() - t.open_date.timestamp() - if t.close_date else None) - for t in Trade.query.filter(Trade.pair.is_(pair)).all()], - columns=columns) - - elif 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", "sell_reason"] - if os.path.exists(file): - 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) - else: - trades = pd.DataFrame([], columns=columns) - - return trades - - -def generate_plot_file(fig, pair, tick_interval, is_last) -> None: - """ - Generate a plot html file from pre populated fig plotly object - :return: None - """ - logger.info('Generate plot file for %s', pair) - - pair_name = pair.replace("/", "_") - file_name = 'freqtrade-plot-' + pair_name + '-' + tick_interval + '.html' - - if not os.path.exists('user_data/plots'): - try: - os.makedirs('user_data/plots') - except OSError as e: - raise - - plot(fig, filename=str(Path('user_data/plots').joinpath(file_name)), auto_open=False) - if is_last: - plot(fig, filename=str(Path('user_data').joinpath('freqtrade-plot.html')), auto_open=False) - - -def get_trading_env(args: Namespace): - """ - Initalize freqtrade Exchange and Strategy, split pairs recieved in parameter - :return: Strategy - """ - global _CONF - - # Load the configuration - _CONF.update(setup_configuration(args)) - print(_CONF) - - pairs = args.pairs.split(',') - if pairs is None: - logger.critical('Parameter --pairs mandatory;. E.g --pairs ETH/BTC,XRP/BTC') - exit() - - # Load the strategy - try: - strategy = StrategyResolver(_CONF).strategy - exchange = Exchange(_CONF) - except AttributeError: - logger.critical( - 'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"', - args.strategy - ) - exit() - - return [strategy, exchange, pairs] - - -def get_tickers_data(strategy, exchange, pairs: List[str], args): - """ - Get tickers data for each pairs on live or local, option defined in args - :return: dictinnary of tickers. output format: {'pair': tickersdata} - """ - - tick_interval = strategy.ticker_interval - timerange = Arguments.parse_timerange(args.timerange) - - tickers = {} - if args.live: - logger.info('Downloading pairs.') - exchange.refresh_tickers(pairs, tick_interval) - for pair in pairs: - tickers[pair] = exchange.klines(pair) - else: - tickers = history.load_data( - datadir=Path(_CONF.get("datadir")), - pairs=pairs, - ticker_interval=tick_interval, - refresh_pairs=_CONF.get('refresh_pairs', False), - timerange=timerange, - exchange=Exchange(_CONF) - ) - - # No ticker found, impossible to download, len mismatch - for pair, data in tickers.copy().items(): - logger.debug("checking tickers data of pair: %s", pair) - logger.debug("data.empty: %s", data.empty) - logger.debug("len(data): %s", len(data)) - if data.empty: - del tickers[pair] - logger.info( - 'An issue occured while retreiving datas of %s pair, please retry ' - 'using -l option for live or --refresh-pairs-cached', pair) - return tickers - - -def generate_dataframe(strategy, tickers, pair) -> pd.DataFrame: - """ - Get tickers then Populate strategy indicators and signals, then return the full dataframe - :return: the DataFrame of a pair - """ - - dataframes = strategy.tickerdata_to_dataframe(tickers) - dataframe = dataframes[pair] - dataframe = strategy.advise_buy(dataframe, {'pair': pair}) - dataframe = strategy.advise_sell(dataframe, {'pair': pair}) - - return dataframe - - -def extract_trades_of_period(dataframe, trades) -> pd.DataFrame: - """ - Compare trades and backtested pair DataFrames to get trades performed on backtested period - :return: the DataFrame of a trades of period - """ - trades = trades.loc[trades['opents'] >= dataframe.iloc[0]['date']] - return trades - - -def generate_graph(pair, trades: pd.DataFrame, data: pd.DataFrame, args) -> tools.make_subplots: - """ - 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 - :param args: sys.argv that contrains the two params indicators1, and indicators2 - :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' - ) - - 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( - 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( - 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' - ) - ) - - # Row 1 - fig.append_trace(candles, 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': 'rgba(255,255,255,0)'}, - ) - 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': 'rgba(255,255,255,0)'}, - ) - 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) - - # 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) - - return fig - - -def generate_row(fig, row, raw_indicators, data) -> tools.make_subplots: - """ - Generator all the indicator selected by the user for a specific row - """ - 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: - """ - 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', - ) - 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=int, - ) - arguments.common_args_parser() - arguments.optimizer_shared_options(arguments.parser) - arguments.backtesting_options(arguments.parser) - return arguments.parse_args() - - -def analyse_and_plot_pairs(args: Namespace): - """ - From arguments provided in cli: - -Initialise backtest env - -Get tickers data - -Generate Dafaframes populated with indicators and signals - -Load trades excecuted on same periods - -Generate Plotly plot objects - -Generate plot files - :return: None - """ - strategy, exchange, pairs = get_trading_env(args) - # Set timerange to use - timerange = Arguments.parse_timerange(args.timerange) - tick_interval = strategy.ticker_interval - - tickers = get_tickers_data(strategy, exchange, pairs, args) - pair_counter = 0 - for pair, data in tickers.items(): - pair_counter += 1 - logger.info("analyse pair %s", pair) - tickers = {} - tickers[pair] = data - dataframe = generate_dataframe(strategy, tickers, pair) - - trades = load_trades(args, pair, timerange) - trades = extract_trades_of_period(dataframe, trades) - - fig = generate_graph( - pair=pair, - trades=trades, - data=dataframe, - args=args - ) - - is_last = (False, True)[pair_counter == len(tickers)] - generate_plot_file(fig, pair, tick_interval, is_last) - - logger.info('End of ploting process %s plots generated', pair_counter) - - -def main(sysargv: List[str]) -> None: - """ - This function will initiate the bot and start the trading loop. - :return: None - """ - logger.info('Starting Plot Dataframe') - analyse_and_plot_pairs( - plot_parse_args(sysargv) - ) - exit() - - -if __name__ == '__main__': - main(sys.argv[1:]) +#!/usr/bin/env python3 +""" +Script to display when the bot will buy on specific pair(s) + +Mandatory Cli parameters: +-p / --pairs: pair(s) to examine + +Option but recommended +-s / --strategy: strategy to use + + +Optional Cli parameters +-d / --datadir: path to pair(s) backtest data +--timerange: specify what timerange of data to use. +-l / --live: Live, to download the latest ticker for the pair(s) +-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 + +Example of usage: +> python3 scripts/plot_dataframe.py --pairs BTC/EUR,XRP/BTC -d user_data/data/ --indicators1 sma,ema3 +--indicators2 fastk,fastd +""" +import json +import logging +import sys +import os +from argparse import Namespace +from pathlib import Path +from typing import Dict, List, Any + +import pandas as pd +import plotly.graph_objs as go +import pytz + +from plotly import tools +from plotly.offline import plot + +from freqtrade import persistence +from freqtrade.arguments import Arguments, TimeRange +from freqtrade.data import history +from freqtrade.exchange import Exchange +from freqtrade.optimize.backtesting import setup_configuration +from freqtrade.persistence import Trade +from freqtrade.resolvers import StrategyResolver + +logger = logging.getLogger(__name__) +_CONF: Dict[str, Any] = {} + +timeZone = pytz.UTC + + +def load_trades(args: Namespace, pair: str, timerange: TimeRange) -> pd.DataFrame: + trades: pd.DataFrame = pd.DataFrame() + if args.db_url: + persistence.init(_CONF) + columns = ["pair", "profit", "opents", "closets", "open_rate", "close_rate", "duration"] + + for x in Trade.query.all(): + print("date: {}".format(x.open_date)) + + trades = pd.DataFrame([(t.pair, t.calc_profit(), + t.open_date.replace(tzinfo=timeZone), + t.close_date.replace(tzinfo=timeZone) if t.close_date else None, + t.open_rate, t.close_rate, + t.close_date.timestamp() - t.open_date.timestamp() + if t.close_date else None) + for t in Trade.query.filter(Trade.pair.is_(pair)).all()], + columns=columns) + + elif 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", "sell_reason"] + if os.path.exists(file): + 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) + else: + trades = pd.DataFrame([], columns=columns) + + return trades + + +def generate_plot_file(fig, pair, tick_interval, is_last) -> None: + """ + Generate a plot html file from pre populated fig plotly object + :return: None + """ + logger.info('Generate plot file for %s', pair) + + pair_name = pair.replace("/", "_") + file_name = 'freqtrade-plot-' + pair_name + '-' + tick_interval + '.html' + + if not os.path.exists('user_data/plots'): + try: + os.makedirs('user_data/plots') + except OSError as e: + raise + + plot(fig, filename=str(Path('user_data/plots').joinpath(file_name)), auto_open=False) + if is_last: + plot(fig, filename=str(Path('user_data').joinpath('freqtrade-plot.html')), auto_open=False) + + +def get_trading_env(args: Namespace): + """ + Initalize freqtrade Exchange and Strategy, split pairs recieved in parameter + :return: Strategy + """ + global _CONF + + # Load the configuration + _CONF.update(setup_configuration(args)) + print(_CONF) + + pairs = args.pairs.split(',') + if pairs is None: + logger.critical('Parameter --pairs mandatory;. E.g --pairs ETH/BTC,XRP/BTC') + exit() + + # Load the strategy + try: + strategy = StrategyResolver(_CONF).strategy + exchange = Exchange(_CONF) + except AttributeError: + logger.critical( + 'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"', + args.strategy + ) + exit() + + return [strategy, exchange, pairs] + + +def get_tickers_data(strategy, exchange, pairs: List[str], args): + """ + Get tickers data for each pairs on live or local, option defined in args + :return: dictinnary of tickers. output format: {'pair': tickersdata} + """ + + tick_interval = strategy.ticker_interval + timerange = Arguments.parse_timerange(args.timerange) + + tickers = {} + if args.live: + logger.info('Downloading pairs.') + exchange.refresh_tickers(pairs, tick_interval) + for pair in pairs: + tickers[pair] = exchange.klines(pair) + else: + tickers = history.load_data( + datadir=Path(_CONF.get("datadir")), + pairs=pairs, + ticker_interval=tick_interval, + refresh_pairs=_CONF.get('refresh_pairs', False), + timerange=timerange, + exchange=Exchange(_CONF) + ) + + # No ticker found, impossible to download, len mismatch + for pair, data in tickers.copy().items(): + logger.debug("checking tickers data of pair: %s", pair) + logger.debug("data.empty: %s", data.empty) + logger.debug("len(data): %s", len(data)) + if data.empty: + del tickers[pair] + logger.info( + 'An issue occured while retreiving datas of %s pair, please retry ' + 'using -l option for live or --refresh-pairs-cached', pair) + return tickers + + +def generate_dataframe(strategy, tickers, pair) -> pd.DataFrame: + """ + Get tickers then Populate strategy indicators and signals, then return the full dataframe + :return: the DataFrame of a pair + """ + + dataframes = strategy.tickerdata_to_dataframe(tickers) + dataframe = dataframes[pair] + dataframe = strategy.advise_buy(dataframe, {'pair': pair}) + dataframe = strategy.advise_sell(dataframe, {'pair': pair}) + + return dataframe + + +def extract_trades_of_period(dataframe, trades) -> pd.DataFrame: + """ + Compare trades and backtested pair DataFrames to get trades performed on backtested period + :return: the DataFrame of a trades of period + """ + trades = trades.loc[trades['opents'] >= dataframe.iloc[0]['date']] + return trades + + +def generate_graph(pair, trades: pd.DataFrame, data: pd.DataFrame, args) -> tools.make_subplots: + """ + 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 + :param args: sys.argv that contrains the two params indicators1, and indicators2 + :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' + ) + + 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( + 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( + 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' + ) + ) + + # Row 1 + fig.append_trace(candles, 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': 'rgba(255,255,255,0)'}, + ) + 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': 'rgba(255,255,255,0)'}, + ) + 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) + + # 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) + + return fig + + +def generate_row(fig, row, raw_indicators, data) -> tools.make_subplots: + """ + Generator all the indicator selected by the user for a specific row + """ + 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: + """ + 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', + ) + 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=int, + ) + arguments.common_args_parser() + arguments.optimizer_shared_options(arguments.parser) + arguments.backtesting_options(arguments.parser) + return arguments.parse_args() + + +def analyse_and_plot_pairs(args: Namespace): + """ + From arguments provided in cli: + -Initialise backtest env + -Get tickers data + -Generate Dafaframes populated with indicators and signals + -Load trades excecuted on same periods + -Generate Plotly plot objects + -Generate plot files + :return: None + """ + strategy, exchange, pairs = get_trading_env(args) + # Set timerange to use + timerange = Arguments.parse_timerange(args.timerange) + tick_interval = strategy.ticker_interval + + tickers = get_tickers_data(strategy, exchange, pairs, args) + pair_counter = 0 + for pair, data in tickers.items(): + pair_counter += 1 + logger.info("analyse pair %s", pair) + tickers = {} + tickers[pair] = data + dataframe = generate_dataframe(strategy, tickers, pair) + + trades = load_trades(args, pair, timerange) + trades = extract_trades_of_period(dataframe, trades) + + fig = generate_graph( + pair=pair, + trades=trades, + data=dataframe, + args=args + ) + + is_last = (False, True)[pair_counter == len(tickers)] + generate_plot_file(fig, pair, tick_interval, is_last) + + logger.info('End of ploting process %s plots generated', pair_counter) + + +def main(sysargv: List[str]) -> None: + """ + This function will initiate the bot and start the trading loop. + :return: None + """ + logger.info('Starting Plot Dataframe') + analyse_and_plot_pairs( + plot_parse_args(sysargv) + ) + exit() + + +if __name__ == '__main__': + main(sys.argv[1:]) From 422a0ce114fb2b1654b27a3715e1e4ba5b901392 Mon Sep 17 00:00:00 2001 From: Axel Cherubin Date: Fri, 25 Jan 2019 13:48:22 -0400 Subject: [PATCH 09/11] better Path usage, remove arg parameter in generate_graph --- scripts/plot_dataframe.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index bb9c04206..881bf813b 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -76,7 +76,7 @@ def load_trades(args: Namespace, pair: str, timerange: TimeRange) -> pd.DataFram # must align with columns in backtest.py columns = ["pair", "profit", "opents", "closets", "index", "duration", "open_rate", "close_rate", "open_at_end", "sell_reason"] - if os.path.exists(file): + if file.exists(): with file.open() as f: data = json.load(f) trades = pd.DataFrame(data, columns=columns) @@ -113,11 +113,7 @@ def generate_plot_file(fig, pair, tick_interval, is_last) -> None: pair_name = pair.replace("/", "_") file_name = 'freqtrade-plot-' + pair_name + '-' + tick_interval + '.html' - if not os.path.exists('user_data/plots'): - try: - os.makedirs('user_data/plots') - except OSError as e: - raise + Path("user_data/plots").mkdir(parents=True, exist_ok=True) plot(fig, filename=str(Path('user_data/plots').joinpath(file_name)), auto_open=False) if is_last: @@ -215,13 +211,20 @@ def extract_trades_of_period(dataframe, trades) -> pd.DataFrame: return trades -def generate_graph(pair, trades: pd.DataFrame, data: pd.DataFrame, args) -> tools.make_subplots: +def generate_graph( + pair: str, + trades: pd.DataFrame, + data: pd.DataFrame, + indicators1: str, + indicators2: str + ) -> tools.make_subplots: """ 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 - :param args: sys.argv that contrains the two params indicators1, and indicators2 + :indicators1: String Main plot indicators + :indicators2: String Sub plot indicators :return: None """ @@ -322,7 +325,7 @@ def generate_graph(pair, trades: pd.DataFrame, data: pd.DataFrame, args) -> tool 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 = generate_row(fig=fig, row=1, raw_indicators=indicators1, data=data) fig.append_trace(buys, 1, 1) fig.append_trace(sells, 1, 1) fig.append_trace(trade_buys, 1, 1) @@ -337,7 +340,7 @@ def generate_graph(pair, trades: pd.DataFrame, data: pd.DataFrame, args) -> tool fig.append_trace(volume, 2, 1) # Row 3 - fig = generate_row(fig=fig, row=3, raw_indicators=args.indicators2, data=data) + fig = generate_row(fig=fig, row=3, raw_indicators=indicators2, data=data) return fig @@ -435,7 +438,8 @@ def analyse_and_plot_pairs(args: Namespace): pair=pair, trades=trades, data=dataframe, - args=args + indicators1=args.indicators1, + indicators2=args.indicators2 ) is_last = (False, True)[pair_counter == len(tickers)] From e43aaaef9c1f9655102a00bc7bce6fbbd384e323 Mon Sep 17 00:00:00 2001 From: Axel Cherubin Date: Fri, 25 Jan 2019 14:04:39 -0400 Subject: [PATCH 10/11] add macd signal as default indicator2 --- scripts/plot_dataframe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index 881bf813b..719653eb0 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -389,7 +389,7 @@ def plot_parse_args(args: List[str]) -> Namespace: 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', + default='macd,macdsignal', dest='indicators2', ) arguments.parser.add_argument( From e5b022405045fd19590741399cd4260eeb9c8ef8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 26 Jan 2019 10:56:29 +0100 Subject: [PATCH 11/11] remove unused import --- scripts/plot_dataframe.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index 719653eb0..4470213ef 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -21,13 +21,12 @@ 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 --pairs BTC/EUR,XRP/BTC -d user_data/data/ --indicators1 sma,ema3 ---indicators2 fastk,fastd +> python3 scripts/plot_dataframe.py --pairs BTC/EUR,XRP/BTC -d user_data/data/ + --indicators1 sma,ema3 --indicators2 fastk,fastd """ import json import logging import sys -import os from argparse import Namespace from pathlib import Path from typing import Dict, List, Any