Merge branch 'develop' into feat/dataprovider
This commit is contained in:
commit
02d13645b0
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
@ -101,11 +101,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():
|
||||
@ -117,6 +119,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]),
|
||||
@ -130,6 +133,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]),
|
||||
@ -154,11 +158,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,
|
||||
@ -166,6 +172,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]),
|
||||
@ -432,18 +439,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')
|
||||
|
||||
|
@ -346,6 +346,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(
|
||||
@ -361,13 +362,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
|
||||
|
||||
@ -403,6 +404,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(
|
||||
@ -430,13 +432,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
|
||||
|
@ -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:
|
||||
|
@ -1,4 +1,4 @@
|
||||
ccxt==1.18.144
|
||||
ccxt==1.18.152
|
||||
SQLAlchemy==1.2.16
|
||||
python-telegram-bot==11.1.0
|
||||
arrow==0.13.0
|
||||
@ -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
|
||||
|
@ -1,18 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Script to display when the bot will buy a specific pair
|
||||
Script to display when the bot will buy on specific pair(s)
|
||||
|
||||
Mandatory Cli parameters:
|
||||
-p / --pair: pair to examine
|
||||
-p / --pairs: pair(s) to examine
|
||||
|
||||
Option but recommended
|
||||
-s / --strategy: strategy to use
|
||||
|
||||
|
||||
Optional Cli parameters
|
||||
-d / --datadir: path to pair backtest data
|
||||
-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
|
||||
-l / --live: Live, to download the latest ticker for the pair(s)
|
||||
-db / --db-url: Show trades stored in database
|
||||
|
||||
|
||||
@ -21,8 +21,8 @@ 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
|
||||
> python3 scripts/plot_dataframe.py --pairs BTC/EUR,XRP/BTC -d user_data/data/
|
||||
--indicators1 sma,ema3 --indicators2 fastk,fastd
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
@ -65,7 +65,8 @@ def load_trades(args: Namespace, pair: str, timerange: TimeRange) -> pd.DataFram
|
||||
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)
|
||||
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)
|
||||
|
||||
@ -74,52 +75,66 @@ 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"]
|
||||
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]
|
||||
if file.exists():
|
||||
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)
|
||||
|
||||
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:
|
||||
def generate_plot_file(fig, pair, tick_interval, is_last) -> None:
|
||||
"""
|
||||
Calls analyze() and plots the returned dataframe
|
||||
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'
|
||||
|
||||
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:
|
||||
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)
|
||||
# Set the pair to audit
|
||||
pair = args.pair
|
||||
|
||||
if pair is None:
|
||||
logger.critical('Parameter --pair mandatory;. E.g --pair ETH/BTC')
|
||||
pairs = args.pairs.split(',')
|
||||
if pairs is None:
|
||||
logger.critical('Parameter --pairs mandatory;. E.g --pairs ETH/BTC,XRP/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
|
||||
@ -131,61 +146,84 @@ def plot_analyzed_dataframe(args: Namespace) -> None:
|
||||
)
|
||||
exit()
|
||||
|
||||
# Set the ticker to use
|
||||
tick_interval = strategy.ticker_interval
|
||||
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)
|
||||
|
||||
# Load pair tickers
|
||||
tickers = {}
|
||||
if args.live:
|
||||
logger.info('Downloading pair.')
|
||||
exchange.refresh_latest_ohlcv([(pair, tick_interval)])
|
||||
tickers[pair] = exchange.klines((pair, tick_interval))
|
||||
logger.info('Downloading pairs.')
|
||||
exchange.refresh_latest_ohlcv([(pair, tick_interval) for pair in pairs])
|
||||
for pair in pairs:
|
||||
tickers[pair] = exchange.klines((pair, tick_interval))
|
||||
else:
|
||||
tickers = history.load_data(
|
||||
datadir=Path(_CONF.get("datadir")),
|
||||
pairs=[pair],
|
||||
pairs=pairs,
|
||||
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()
|
||||
# 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
|
||||
|
||||
# Get trades already made from the DB
|
||||
trades = load_trades(args, pair, timerange)
|
||||
|
||||
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})
|
||||
|
||||
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)
|
||||
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']]
|
||||
fig = generate_graph(
|
||||
pair=pair,
|
||||
trades=trades,
|
||||
data=dataframe,
|
||||
args=args
|
||||
)
|
||||
|
||||
plot(fig, filename=str(Path('user_data').joinpath('freqtrade-plot.html')))
|
||||
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
|
||||
"""
|
||||
|
||||
@ -201,6 +239,7 @@ def generate_graph(pair, trades: pd.DataFrame, data: pd.DataFrame, args) -> tool
|
||||
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(
|
||||
@ -285,7 +324,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)
|
||||
@ -300,7 +339,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
|
||||
|
||||
@ -349,7 +388,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(
|
||||
@ -366,15 +405,58 @@ def plot_parse_args(args: List[str]) -> Namespace:
|
||||
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,
|
||||
indicators1=args.indicators1,
|
||||
indicators2=args.indicators2
|
||||
)
|
||||
|
||||
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')
|
||||
plot_analyzed_dataframe(
|
||||
analyse_and_plot_pairs(
|
||||
plot_parse_args(sysargv)
|
||||
)
|
||||
exit()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -108,8 +108,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']
|
||||
|
Loading…
Reference in New Issue
Block a user