From ecb5cdc9e379b1d924f7538aa5d445cf46abbc0c Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 27 Dec 2018 11:47:00 +0100 Subject: [PATCH 001/358] Version bump to 0.18.1-dev --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 2319135c6..ca148f518 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ FreqTrade bot """ -__version__ = '0.18.0' +__version__ = '0.18.1-dev' class DependencyException(BaseException): From cb654a82dbe9c2541e8d95986b80b83993f919b0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 27 Dec 2018 12:56:56 +0100 Subject: [PATCH 002/358] Add release documentation --- docs/developer.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/developer.md b/docs/developer.md index 9137f16ca..64f3b9a52 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -68,3 +68,38 @@ Please also run `self._validate_whitelist(pairs)` and to check and remove pairs This is a simple method used by `VolumePairList` - however serves as a good example. It implements caching (`@cached(TTLCache(maxsize=1, ttl=1800))`) as well as a configuration option to allow different (but similar) strategies to work with the same PairListProvider. + +## Creating a release + +This part of the documentation is aimed at maintainers, and shows how to create a release. + +### create release branch + +``` bash +# make sure you're in develop branch +git checkout develop + +# create new branch +git checkout -b new_release +``` + +* edit `freqtrade/__init__.py` and add the desired version (for example `0.18.0`) +* Commit this part +* push that branch to the remote and create a PR + +### create changelog from git commits + +``` bash +# Needs to be done before merging / pulling that branch. +git log --oneline --no-decorate --no-merges master..develop +``` + +### Create github release / tag + +* Use the version-number specified as tag. +* Use "master" as reference (this step comes after the above PR is merged). +* use the above changelog as release comment (as codeblock) + +### After-release + +* update version in develop to next valid version and postfix that with `-dev` (`0.18.0 -> 0.18.1-dev`) From 1167b24eeb29b03ea29026c8bf4389625d163355 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 27 Dec 2018 13:34:07 +0100 Subject: [PATCH 003/358] Update ccxt from 1.18.71 to 1.18.74 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index aa1147f9a..d0fe69970 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.71 +ccxt==1.18.74 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.12.1 From 1ce8f416ca6dea2b4aca539b2c822114d3cbd8e3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 27 Dec 2018 14:29:26 +0100 Subject: [PATCH 004/358] Reset stake-currency when using config to download pairs --- scripts/download_backtest_data.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/download_backtest_data.py b/scripts/download_backtest_data.py index c4a1b59c5..c8fd08747 100755 --- a/scripts/download_backtest_data.py +++ b/scripts/download_backtest_data.py @@ -31,6 +31,7 @@ if args.config: configuration = Configuration(args) config = configuration._load_config_file(args.config) + config['stake_currency'] = '' # Ensure we do not use Exchange credentials config['exchange']['key'] = '' config['exchange']['secret'] = '' From c955415cc3b13a9cda40a10b13f898fbf9342b81 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 28 Dec 2018 10:01:16 +0100 Subject: [PATCH 005/358] Switch from ujson to rapidjson --- freqtrade/data/history.py | 8 ++++---- freqtrade/misc.py | 6 +++--- freqtrade/tests/test_misc.py | 4 ++-- requirements.txt | 2 +- setup.py | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index f4ff46a1a..54d9abf2b 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -13,7 +13,7 @@ from typing import Optional, List, Dict, Tuple, Any import arrow from pandas import DataFrame -import ujson +import rapidjson from freqtrade import misc, constants, OperationalException from freqtrade.data.converter import parse_ticker_dataframe @@ -25,11 +25,11 @@ logger = logging.getLogger(__name__) def json_load(data): """ - load data with ujson + load data with rapidjson Use this to have a consistent experience, - otherwise "precise_float" needs to be passed to all load operations + sete number_mode to "NM_NATIVE" for greatest speed """ - return ujson.load(data, precise_float=True) + return rapidjson.load(data, number_mode=rapidjson.NM_NATIVE) def trim_tickerlist(tickerlist: List[Dict], timerange: TimeRange) -> List[Dict]: diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 832437951..7bb9ddced 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -3,7 +3,6 @@ Various tool function for Freqtrade and scripts """ import gzip -import json import logging import re from datetime import datetime @@ -11,6 +10,7 @@ from typing import Dict import numpy as np from pandas import DataFrame +import rapidjson logger = logging.getLogger(__name__) @@ -77,10 +77,10 @@ def file_dump_json(filename, data, is_zip=False) -> None: if not filename.endswith('.gz'): filename = filename + '.gz' with gzip.open(filename, 'w') as fp: - json.dump(data, fp, default=str) + rapidjson.dump(data, fp, default=str, number_mode=rapidjson.NM_NATIVE) else: with open(filename, 'w') as fp: - json.dump(data, fp, default=str) + rapidjson.dump(data, fp, default=str, number_mode=rapidjson.NM_NATIVE) def format_ms_time(date: int) -> str: diff --git a/freqtrade/tests/test_misc.py b/freqtrade/tests/test_misc.py index 017bf372f..991e03fa0 100644 --- a/freqtrade/tests/test_misc.py +++ b/freqtrade/tests/test_misc.py @@ -46,12 +46,12 @@ def test_common_datearray(default_conf) -> None: def test_file_dump_json(mocker) -> None: file_open = mocker.patch('freqtrade.misc.open', MagicMock()) - json_dump = mocker.patch('json.dump', MagicMock()) + json_dump = mocker.patch('rapidjson.dump', MagicMock()) file_dump_json('somefile', [1, 2, 3]) assert file_open.call_count == 1 assert json_dump.call_count == 1 file_open = mocker.patch('freqtrade.misc.gzip.open', MagicMock()) - json_dump = mocker.patch('json.dump', MagicMock()) + json_dump = mocker.patch('rapidjson.dump', MagicMock()) file_dump_json('somefile', [1, 2, 3], True) assert file_open.call_count == 1 assert json_dump.call_count == 1 diff --git a/requirements.txt b/requirements.txt index d0fe69970..d7a6f748e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,4 +23,4 @@ scikit-optimize==0.5.2 py_find_1st==1.1.3 #Load ticker files 30% faster -ujson==1.35 +python-rapidjson==0.6.3 diff --git a/setup.py b/setup.py index b9e3620df..11f957e67 100644 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ setup(name='freqtrade', 'cachetools', 'coinmarketcap', 'scikit-optimize', - 'ujson', + 'python-rapidjson', 'py_find_1st' ], include_package_data=True, From 065b469a106d0cb978667ca9a82173d1d31aa89a Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 28 Dec 2018 10:04:07 +0100 Subject: [PATCH 006/358] rename test to avoid naming collision --- freqtrade/tests/data/test_history.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/tests/data/test_history.py b/freqtrade/tests/data/test_history.py index 8923a60b6..bc859b325 100644 --- a/freqtrade/tests/data/test_history.py +++ b/freqtrade/tests/data/test_history.py @@ -450,7 +450,7 @@ def test_trim_tickerlist() -> None: assert not ticker -def test_file_dump_json() -> None: +def test_file_dump_json_tofile() -> None: file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'test_{id}.json'.format(id=str(uuid.uuid4()))) data = {'bar': 'foo'} From 27abdd97888524ca76634e0f7a86bb8f2e3f055e Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 28 Dec 2018 10:04:28 +0100 Subject: [PATCH 007/358] Move load_json to misc --- freqtrade/data/history.py | 16 +++------------- freqtrade/misc.py | 9 +++++++++ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index 54d9abf2b..e54b5a657 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -13,7 +13,6 @@ from typing import Optional, List, Dict, Tuple, Any import arrow from pandas import DataFrame -import rapidjson from freqtrade import misc, constants, OperationalException from freqtrade.data.converter import parse_ticker_dataframe @@ -23,15 +22,6 @@ from freqtrade.arguments import TimeRange logger = logging.getLogger(__name__) -def json_load(data): - """ - load data with rapidjson - Use this to have a consistent experience, - sete number_mode to "NM_NATIVE" for greatest speed - """ - return rapidjson.load(data, number_mode=rapidjson.NM_NATIVE) - - def trim_tickerlist(tickerlist: List[Dict], timerange: TimeRange) -> List[Dict]: """ Trim tickerlist based on given timerange @@ -83,11 +73,11 @@ def load_tickerdata_file( if gzipfile.is_file(): logger.debug('Loading ticker data from file %s', gzipfile) with gzip.open(gzipfile) as tickerdata: - pairdata = json_load(tickerdata) + pairdata = misc.json_load(tickerdata) elif file.is_file(): logger.debug('Loading ticker data from file %s', file) with open(file) as tickerdata: - pairdata = json_load(tickerdata) + pairdata = misc.json_load(tickerdata) else: return None @@ -185,7 +175,7 @@ def load_cached_data_for_updating(filename: Path, tick_interval: str, # read the cached file if filename.is_file(): with open(filename, "rt") as file: - data = json_load(file) + data = misc.json_load(file) # remove the last item, could be incomplete candle if data: data.pop() diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 7bb9ddced..7fcfeec44 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -83,6 +83,15 @@ def file_dump_json(filename, data, is_zip=False) -> None: rapidjson.dump(data, fp, default=str, number_mode=rapidjson.NM_NATIVE) +def json_load(data): + """ + load data with rapidjson + Use this to have a consistent experience, + sete number_mode to "NM_NATIVE" for greatest speed + """ + return rapidjson.load(data, number_mode=rapidjson.NM_NATIVE) + + def format_ms_time(date: int) -> str: """ convert MS date to readable format. From 7dc40cdac53faee936910c317f08ab0acc61a86c Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 28 Dec 2018 10:25:12 +0100 Subject: [PATCH 008/358] refactor file_load_json to be standalone --- freqtrade/data/history.py | 14 +++----------- freqtrade/misc.py | 22 ++++++++++++++++++++-- freqtrade/tests/test_misc.py | 18 ++++++++++++++++-- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index e54b5a657..70c76536a 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -67,18 +67,10 @@ def load_tickerdata_file( path = make_testdata_path(datadir) pair_s = pair.replace('/', '_') file = path.joinpath(f'{pair_s}-{ticker_interval}.json') - gzipfile = file.with_suffix(file.suffix + '.gz') - # Try gzip file first, otherwise regular json file. - if gzipfile.is_file(): - logger.debug('Loading ticker data from file %s', gzipfile) - with gzip.open(gzipfile) as tickerdata: - pairdata = misc.json_load(tickerdata) - elif file.is_file(): - logger.debug('Loading ticker data from file %s', file) - with open(file) as tickerdata: - pairdata = misc.json_load(tickerdata) - else: + pairdata = misc.file_load_json(file) + + if not pairdata: return None if timerange: diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 7fcfeec44..77b901be0 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -83,13 +83,31 @@ def file_dump_json(filename, data, is_zip=False) -> None: rapidjson.dump(data, fp, default=str, number_mode=rapidjson.NM_NATIVE) -def json_load(data): +def json_load(datafile): """ load data with rapidjson Use this to have a consistent experience, sete number_mode to "NM_NATIVE" for greatest speed """ - return rapidjson.load(data, number_mode=rapidjson.NM_NATIVE) + return rapidjson.load(datafile, number_mode=rapidjson.NM_NATIVE) + + +def file_load_json(file): + + gzipfile = file.with_suffix(file.suffix + '.gz') + + # Try gzip file first, otherwise regular json file. + if gzipfile.is_file(): + logger.debug('Loading ticker data from file %s', gzipfile) + with gzip.open(gzipfile) as tickerdata: + pairdata = json_load(tickerdata) + elif file.is_file(): + logger.debug('Loading ticker data from file %s', file) + with open(file) as tickerdata: + pairdata = json_load(tickerdata) + else: + return None + return pairdata def format_ms_time(date: int) -> str: diff --git a/freqtrade/tests/test_misc.py b/freqtrade/tests/test_misc.py index 991e03fa0..4a630af14 100644 --- a/freqtrade/tests/test_misc.py +++ b/freqtrade/tests/test_misc.py @@ -2,11 +2,12 @@ import datetime from unittest.mock import MagicMock +from pathlib import Path from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.misc import (common_datearray, datesarray_to_datetimearray, - file_dump_json, format_ms_time, shorten_date) -from freqtrade.data.history import load_tickerdata_file + file_dump_json, file_load_json, format_ms_time, shorten_date) +from freqtrade.data.history import load_tickerdata_file, make_testdata_path from freqtrade.strategy.default_strategy import DefaultStrategy @@ -57,6 +58,19 @@ def test_file_dump_json(mocker) -> None: assert json_dump.call_count == 1 +def test_file_load_json(mocker) -> None: + + # 7m .json does not exist + ret = file_load_json(make_testdata_path(None).joinpath('UNITTEST_BTC-7m.json')) + assert not ret + # 1m json exists (but no .gz exists) + ret = file_load_json(make_testdata_path(None).joinpath('UNITTEST_BTC-1m.json')) + assert ret + # 8 .json is empty and will fail if it's loaded. .json.gz is a copy of 1.json + ret = file_load_json(make_testdata_path(None).joinpath('UNITTEST_BTC-8m.json')) + assert ret + + def test_format_ms_time() -> None: # Date 2018-04-10 18:02:01 date_in_epoch_ms = 1523383321000 From 0f86e218c15c76f10d7fb288ac9fdc830f5ba3bf Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 28 Dec 2018 10:41:54 +0100 Subject: [PATCH 009/358] Add plotly requirements file --- .pyup.yml | 1 + requirements-plot.txt | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 requirements-plot.txt diff --git a/.pyup.yml b/.pyup.yml index 74c1456ce..01d4bba2a 100644 --- a/.pyup.yml +++ b/.pyup.yml @@ -21,6 +21,7 @@ search: False requirements: - requirements.txt - requirements-dev.txt + - requirements-plot.txt # configure the branch prefix the bot is using diff --git a/requirements-plot.txt b/requirements-plot.txt new file mode 100644 index 000000000..c1cc409b0 --- /dev/null +++ b/requirements-plot.txt @@ -0,0 +1,5 @@ +# Include all requirements to run the bot. +-r requirements.txt + +plotly==3.4.1 + From 61f8ce5c0e552cdf2644e094066b4bfbaa2f0dbf Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 28 Dec 2018 10:44:24 +0100 Subject: [PATCH 010/358] remove unused imports --- freqtrade/data/history.py | 2 -- freqtrade/tests/test_misc.py | 1 - 2 files changed, 3 deletions(-) diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index 70c76536a..ae56aa6c7 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -5,8 +5,6 @@ includes: * download data from exchange and store to disk """ -import gzip - import logging from pathlib import Path from typing import Optional, List, Dict, Tuple, Any diff --git a/freqtrade/tests/test_misc.py b/freqtrade/tests/test_misc.py index 4a630af14..33a59effd 100644 --- a/freqtrade/tests/test_misc.py +++ b/freqtrade/tests/test_misc.py @@ -2,7 +2,6 @@ import datetime from unittest.mock import MagicMock -from pathlib import Path from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.misc import (common_datearray, datesarray_to_datetimearray, From fab7663ab3810ae0ba2722cf855fcf618a9236cf Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 28 Dec 2018 10:46:48 +0100 Subject: [PATCH 011/358] Log when dumping to file (instead of print) --- freqtrade/misc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 77b901be0..23d8732a0 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -71,7 +71,7 @@ def file_dump_json(filename, data, is_zip=False) -> None: :param data: JSON Data to save :return: """ - print(f'dumping json to "{filename}"') + logger.info(f'dumping json to "{filename}"') if is_zip: if not filename.endswith('.gz'): @@ -82,6 +82,8 @@ def file_dump_json(filename, data, is_zip=False) -> None: with open(filename, 'w') as fp: rapidjson.dump(data, fp, default=str, number_mode=rapidjson.NM_NATIVE) + logger.debug(f'done json to "{filename}"') + def json_load(datafile): """ From 305f2b74e8c43b008ebab7e6bd3db92e3ad3c56f Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 28 Dec 2018 13:34:05 +0100 Subject: [PATCH 012/358] Update ccxt from 1.18.74 to 1.18.78 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d7a6f748e..60cf407a0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.74 +ccxt==1.18.78 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.12.1 From cf0e31c5a27b620967da490fa95e312eb580dfc5 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 28 Dec 2018 13:34:07 +0100 Subject: [PATCH 013/358] Update plotly from 3.4.1 to 3.4.2 --- requirements-plot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-plot.txt b/requirements-plot.txt index c1cc409b0..fa6827211 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,5 +1,5 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==3.4.1 +plotly==3.4.2 From b45af561995447d849ccf20e9f015e3650176af2 Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 28 Dec 2018 13:37:44 +0100 Subject: [PATCH 014/358] initial commit for docs --- mkdocs.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 mkdocs.yml diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 000000000..c97182f51 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1 @@ +site_name: My Docs From 9c442455a298f1bf0ccb2c2577f86b89673d088a Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 28 Dec 2018 14:11:22 +0100 Subject: [PATCH 015/358] adding readdoc config --- .readthedocs.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .readthedocs.yml diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 000000000..dec7b44d7 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,8 @@ +# .readthedocs.yml + +build: + image: latest + +python: + version: 3.6 + setup_py_install: false \ No newline at end of file From ffbe95ef02ae1ffbefc08186f38aa709cfce481a Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 28 Dec 2018 14:18:34 +0100 Subject: [PATCH 016/358] adding requirements for docs --- requirements-docs.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 requirements-docs.txt diff --git a/requirements-docs.txt b/requirements-docs.txt new file mode 100644 index 000000000..a56501a20 --- /dev/null +++ b/requirements-docs.txt @@ -0,0 +1,23 @@ +ccxt==1.18.74 +SQLAlchemy==1.2.15 +python-telegram-bot==11.1.0 +arrow==0.12.1 +cachetools==3.0.0 +requests==2.21.0 +urllib3==1.24.1 +wrapt==1.10.11 +pandas==0.23.4 +scikit-learn==0.20.2 +joblib==0.13.0 +scipy==1.2.0 +jsonschema==2.6.0 +numpy==1.15.4 +TA-Lib==0.4.17 +tabulate==0.8.2 +coinmarketcap==5.0.3 + +# Required for hyperopt +scikit-optimize==0.5.2 + +#Load ticker files 30% faster +python-rapidjson==0.6.3 From 793dd38445119d8d7b3e2c270a36cda78d900577 Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 28 Dec 2018 14:20:43 +0100 Subject: [PATCH 017/358] removing unnecessary packages --- requirements-docs.txt | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/requirements-docs.txt b/requirements-docs.txt index a56501a20..b01e66918 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,23 +1 @@ -ccxt==1.18.74 -SQLAlchemy==1.2.15 -python-telegram-bot==11.1.0 -arrow==0.12.1 -cachetools==3.0.0 -requests==2.21.0 -urllib3==1.24.1 -wrapt==1.10.11 -pandas==0.23.4 -scikit-learn==0.20.2 -joblib==0.13.0 -scipy==1.2.0 -jsonschema==2.6.0 -numpy==1.15.4 -TA-Lib==0.4.17 -tabulate==0.8.2 -coinmarketcap==5.0.3 - -# Required for hyperopt -scikit-optimize==0.5.2 - -#Load ticker files 30% faster -python-rapidjson==0.6.3 +numpy==1.15.4 \ No newline at end of file From ad696a9d125af249144fb311f2cbcdb65331eb7c Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 28 Dec 2018 14:22:17 +0100 Subject: [PATCH 018/358] removing numpy from docs --- requirements-docs.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements-docs.txt b/requirements-docs.txt index b01e66918..e69de29bb 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1 +0,0 @@ -numpy==1.15.4 \ No newline at end of file From dcf0feefb9232f36accf3c0233c28d85d6d89dc0 Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 28 Dec 2018 14:28:09 +0100 Subject: [PATCH 019/358] move requirements to docs --- requirements-docs.txt => docs/requirements-docs.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename requirements-docs.txt => docs/requirements-docs.txt (100%) diff --git a/requirements-docs.txt b/docs/requirements-docs.txt similarity index 100% rename from requirements-docs.txt rename to docs/requirements-docs.txt From 71de820adf8666e625e978adf496c29a2dc2bdd5 Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 28 Dec 2018 14:52:23 +0100 Subject: [PATCH 020/358] changing doc theme --- docs/requirements-docs.txt | 1 + mkdocs.yml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index e69de29bb..c4d8c2cae 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -0,0 +1 @@ +mkdocs-material==3.1.0 \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index c97182f51..62fef3a51 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1 +1,2 @@ -site_name: My Docs +site_name: Freqtrade +theme: material From 78f29a745430ea32ef58f1f908f99f147227f381 Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 28 Dec 2018 15:37:14 +0100 Subject: [PATCH 021/358] added theme configuration --- docs/images/logo.png | Bin 0 -> 12436 bytes mkdocs.yml | 13 ++++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 docs/images/logo.png diff --git a/docs/images/logo.png b/docs/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5e9541bccc8010637f99085c07470fd6e293bf25 GIT binary patch literal 12436 zcmaJ|Wmr{Fm!`W@K|)%QknTk&b{aCz1O?mScI#p$m3#BU?CtN;3_J}sKeg_w|_9u;A@3WWex%Yr@o?$wp`^IG~>HU=Mo}c~Vkyp+a=~Kh8)2!1A!F}(gRLnQD5`jpluH%Dz z0Z6E_Xp96{B#si8Z;?>%sUqb|AfhKpu&8z-{vR(V+9I>K%IWFNRga83t*fu+$&84= zdMPUU$%9@#<|&1k&spY=p3ql`cDa*?D3TFaoij5!==}VRmZb`uGeTCA@3vJ%YV9dh+>H)0`ZNGj*@N=qY|i4E(a;A|)lIrTFC`mZ5|@X>k+@21-CpjT4^M z(csh+@zWEP|}?cvc)Oim)j zQ1atxFr=raUk$dG!zCI;)M@R@-rR$@@02D?7qP(gq;}}x+(>=rK<+{2W?eseo6e$Y* zTv!UKJ#7qmwYBPX&vM1geZ-7W?^>9cAc(v^!MP*gxKgq_IW>jI%*>qE&_Kr9(b6JR z+0x>jXj`GBqKb4M6COUM&HHZ|zFt~c*~4njKc*%#)h{HTJi&6jJafwX_>q*C1{*uF z!}yaouim$lhin7XjH+5j&Ccc#D0Q2;o_~J!>Y6PpJ!8@giVLfG*}oJW8JYRW-Wwe~ zwEtO-$lZPulauoY3K(UHDs*ExqOAinGXl@D>bSiXRFQYs2c%+fItTQ)2B~g zib_iKRe#125A30+UB)^&IThZ;k>l2TDqN1X|pdr~kLFcu)fPjGN*}7N8 zj~_pNkUZt~nrR^C{Z4pPL_~&|`B0LLosv9*(Ih*&;60Va1V$x1b2Bp>ri*k}?F4&? zXFo?X`I&L=a;-6@7j(`APfSf6nfm#)Kc4+k+8XbQ?2Uwth4pVNlYc)72?H_JfW6Tj zs!P&GELZM?dz+nMF<@`3x|)*)3Z1An+n8i-W`?27?`ke-=hb&A1-Hpw8EI+cS6|CC zFH~|wn$?qB4cN&YU0jf(qN9H}jG^OG<7{qkBU@WrBP1${{9c-w!I++&M%Z8O2<3Ny zxdc6BF7y zgoKFSzJ7I^6;z{8P*g5ksl2?9A3l@@29msiv^$H{|YYl$G8-<>%U3F2mQaFLdZar^d$-Ja*?$ z`FME;HSZ`eFf#tw+nSHx{$91La&n|81Iy}hKeoScer`^T(V0hETf4ftI_~)kR~Z)< zZciwvrgZt~g@1qgX}-r_Wsyd|qZ~KfTwNs&*)ZXuz}-V;d01Ol*F{J#M-ah4N7tRW z^QuDO_7*y&L-Bl31@Ti}zAr5eh>wpqvk4&R?d@H8rH?Y{uHJ?8Z$n`zByYs=5mmqNvh6qzPE9Gb&-abnvsF! zu@IisNblz6)&;nn|Cx}62LJcgR(O*Ca=Y)3SvQ`8bD#0CF+6TNIB*!Vza*KG{b~TC zDy$_XCH<*mPq#)6KYy0VG#^gMmW&klyYyUv&R6PXZf=f9%&dYE5h*MFeR+DCM9i;Z zbx>UaPBd~uLxY5y+sA=(%DZ&;`NjI; z+}uN_YXHu)bA5gN$RzUP1zS72+?(U6XXSYmuw}`l&_b7>U3~DUGWs;s>%{r$)vKrI z*!cLV177ptytY2SrysmuqoJaT&JaItZSsN@Azf^7vmu%_SHhH$$`SE$4rEvk&lP=5 zra>DM6Vqh%YAg%Wezq1hi_b1Y=VMr6BC!PpMKs#8YrCbkyICS$T=v&48)LGg9ng|? zrb=RG=w8(5;HI)0Iy1LMJqQdN1PqKSoUrt9q6!!^dm}=P|Fs$Lb;YsXb#88M1$vfH zZ)k><4wV;+m^0}?CclFi?ke5;;^J@SO=lB@@@YMdaoI?L2!F-+?B*D(a2XYplqjX# zbv2L(gDfm89wd-lCq2;Mw26f)O^Yvngwm)^eK2e#AiO`0WGB8FPD1&P)6cyppN zo?brS;K0#jckW}FS6W7fX$X|MNl0>Xa{jfEre<(@$fyN&wm)F%?|oPhZrsRTsVgd? zeyBvXp8ERw09cjoFAo?PP&oPd`-gH)j<=?2(#ZGiXdEu7v%KQK3|k#5XBU^?G5@hF!P0X;SJUD^p*5X@g98cW9urQX zqM(0=w$KKG3gx2zQC84)TpkXqjMcbqsZS&Eb8>d>mPIX`}B0Z5_3ysCEj%uIqPVdW_f2@n}~p+yE|Xo2r4VP z88h{pH*Ywnq#8Uvr#*hDx;i^Q-yaVjSfI{RQ!PKG@&RU~F>ERjDh4HT&J)zkdhh=F(VX1fk<21)<@-$uxy7i%3UD$GQ6N z;^Jk9Lh@L+V7FXh@*NUYje9rF)Ddl32T7cjEKeDyYYNUo8ASgdSL6n5^; zHwHd_*|*Y{Dd0rc_L=H;2L`r+Oeh`|QbA!MB7i~nC3Izc6m)d#Z1Lc+yfKy}LZ)Xq zINYM5R8UyRM8w2D)M$~BkvqG(5UcC!Q9|&j9=cysQ&V&1UuvkU2du4`dCDyVZs%Oq z*VF{^_Z8FLWeFU(v%bF0>Feujo^f+|eq^4|Q5$4!ss;qChz#BqdzQ)7X8Qa0!s{^; zPVW)VYITgaT_$;Bd5g_1=mUd;@g0()2;(k6|3ob}0YWxuezCG772un`coClv7q=32 zN%fltjxds#m{>qV1D}sIF{*7AKvY<4Ecz2ArNBsX4%12;M|XEWCXZD!)LQbO!65&e ztHxvo8pPq}|h%BA=)QMN#lR9ED!#BqjBR_3n2EOUnTA zN5w@&UHYfMUKDu!f!ODI%``d~d&>DiS*wp4lZ@nuUObbR?QOyT%DS!`_Vvk@t;@3e4cHDSg1XNyDl*U|O?+-?_#}z{XZFBp*vHm|)nKB*Cx-ARw2KkW+sUHmu8}f=Yhl= z65;XQV(UsIN4o$OHT9mXyO%mr{9{l+&9btx-a4VK5p;2^+LdwaSPWEDRF6-)iDYSi z{PugTVRFUH8V9hyu!rY1D(T@NA4Sb+coEJh$LNajq&nz!L zKky@v`D9qz>*WT!c_4qhiXSy)cx%>B;GfL~=;i)X)6)#8+rz{_z4{Ol5nsV)GE4b` zX!?4w*y_>8{4B?Sy~2jY05>5m&2snGFE@bnk1;AGL1<`bjFfz~Gg?MQm2BHKQ=}_|3fPl!C!NDz#;qC3s&AdIL?A+Wcp5+)bbMqG=pjwiL zzTP2`P%i6@B&WHC3pg6<>#O5`e0qJFQWlLxANRFf8+{+N1uMpzQvKH?BqTWLK-zlO z*Dca{vDqFynjH75Dl6;0=eKLsNfEs2C_n?8K8wR6NGRaT##D()w)fHWdyP_M7CXD$ zAs~ZFAL~j8kOYBeoWjnx#IqPVJJVAFcrE945A}%KfPsH~h z39s!!lM}w+J-q90aa?@>m)iZkz0LW-qZz!cbfTiZX&*oGx|6ctf#_K~I_Hs=?m=4n zz!oh>Y-wqE8FYE8@%YWmm4JH(4Vv*4j?%&MvNOxxcl!Amb3}KupnxvlqP9jQL9flT zMsdPvChO_W)mcsy6zZrHR8%bfYO2ejC7_N)_q+Q0-E&w| zFES$H__J3IDk`dPu85Z@4LSL%8)(bIP1=lP*QG7luYSYXUPID5b-`{x$N7;MDqIkn{;z; zFQzqRxTi;Y?MMs@8ylDWk&ffN^EZ@_O+VKG_KkrTQ1UEy?*8uK-@l^FHZ+WG)N5;l ziIGbk{tgR&_+is(;RMiF-LwKjeb(6GQXMw8wz`yL)HO67A4P|UBf7e}wp0a4JWxsN3)9?*{DJr9ZkFV#kDl*fiy`xQC`C{xo=|*$wxPP93{_whO^i>k zfy3eFYrW+>2X_6NGkv5lW^2~g{iV0}KNH=5NLEO*^;T14YslPueC)T1q(pQKS}8N_ z>e=~u@KBFfsa<(x<&t@vM0%6iU;=W17=LNi{UKi9iT)++?Kx~um-hDdQscM8FLvt0 zy|W~ShK5`%c_{F`>N;+&TaEiYft@ZzL=h7a)fyPNGchnk1!VY`2|vWNz52IZCH>jU z0)QV^X8o19dGgx!Hf7&0=k4*zjh{cAfmdrqv+S3amTnD*Q9wPkdH(5clgC_}cj&mf zdmaZ8{3wq89spJTfG4QQrNeq&e*W*DLE8M4F3VAXRd+mC>FWI{R&{l8$|1e(1?~i? z$^3~D6BARj<@anc-+y2G`sB!rmQ;-gzkNFqT zsf%&ibQS8z%gbwQv$MCfGzkp}VTTq`#V{XeKJYD_`<2lMz!dXfosjqOCXw0FvhV5Q z(#b(oRFovxM2wbvogoG_3e0$$)%uyrmIlk=WY$nq-nZ?jFDWf}L55)l{+J?g^6D#Yx3| zXM=k`n_V_qcry1EnpY#DHda^ZkDMG{3~U@(L3_HgzzdyX2@jImwR-vT<(cgvm@U%E zgrziSA;H0c&?i@*j8^==WCWSSF>7{gltaB+5i`tEO(JIE8fBCl97=;VZfkU`}HgER{hII-ML?@Kr0^|7Iu)-=TcT#x$Z2G<-NvRY1|=3 zc=z=DXakD3rPP;5T)g9@j*Vs>5ripp(=b-94-j|h0WuN^yJQNCT zy9*qw1MXN=*c_hd&%l|P>-c?W<~5g>mn~7HKjNVsA8mXIZA6gOPoYzi_#PP*f5)$=gv(WFdS++Ouc7wH&h{3CXYw0uXKOKkt)qUgG|Y@hVLtw5!j+yawE2jv zJQ&A`gq(c)R`FnMo7wVF8q}Dgo?TzPZjM00R;~ECv4QSD%e*7{G@XmF<=s1(553{L zj~+eZF|C`$#Xx*>`-V|uH(OIB^uSEi-VG!sI$ogKTq7dl=kNlC|2ef<3V&k`C1VD(nIg1FzheSq@(S$V=ptQ!6E=TEwN zgbeCOF@I2lLu|Ln$oMZLpoK8fXI4RD_y)8cZ z>tMOWfLl;{)doj&Uz%_5=P->*^%V-3F16CD-x*7;q7b&CJmKs?8v z!^@L-hf~_A;NalVo(OzQll~a0o6szNtEz9{P&Gt4KlAeP>XVa`JMUhIjg7_E(bAG@ zSdM(#-7PgfI;vcHNb%TIMhcvOn5Jt`Q&B8tIiUB>G23_m#j}8K+Dz=sR=n zykKsR`%oNEt6ieAVPA;dxLpKbYSrIP*w`duHCFtZ)|RHFsYyhp*!TMK-15U2Fzp@n zWcp?>;~d^Lr%q0)8)|51biXh_3qst_S^|@Yv?Tc7Bk+M&z=dDomer~>!1PB(x@vXX z*7BrY11c}&uPyZ!>fF;z`A)o!t}Y{(fRSI#6rVob;hIDFt1&z@W3Rvzp zkEjdqt1WAFKCnB{)6>6t@K!>owxJ>AsGr9F(ecTNSXE)6Mz=-XHn`ns&2XaQU?O2gKb(PA%=oS6oLR(Iu6rtjcuaCnd?T3D>- z;R16G&3FMdO$u7_!ENK%gXYi9!3Cv`&Z5#DIDiitwgjF5jpsF(3Jva27gwjt9=cR} z8yn1jo*2K!en;;mLS6Cxefa$+G}6$xQ!+C2Qcsx4Ztdg+ zJ+rPisBf>{cLkvb!LyhO0vQlGTl83#{Mw=LhPL4S`v+ML?(Pjb?u-l!oyPZw3||3P z70%xvAtPH~1SyUcm6448S1CVL`NBoi|N8QtnFtL{uH%c#A5bE7YX2T{dh9Pd{8{af)NK9ab%bVgBISy!t^J*S)c7`Q zVfskZuVrlfMULIAvASA;>>D#3Y&h2)9G-ZL_M7XA(OurmoE)Q)B33{=K~WW2VEQ|? z2Hs#NiQiv^=kQ@j9NanF^|Z9yL^@cmLYf`m1j9Vk@;iiPwVh={^MYU!$8YedtE=Y~ zhmQmY$~ibVH1!do4h{}V#X4e2msb#Nb|shu%VHJS5*eGGE6O(N50)MnG$R881alJ^nVI=~Tm!$H6$#~qzfMo*HMEb1Zb&FRdGh9;51k>{ zN~$))jj!5D4a+1ueB$6_{=s=vSY^iTzlD$9-3`WYV9Dx^Bkl(3ocnxnb2YNvg(WWr+ZHL;0P@eJ4tVU)*v$ZzRSE z^y+h<*F*96W&jW+!hPV^o=Wx2w)?(T?HBm-=TG5`t$V7h8`U{*KFnPmFjtYl*H$D) zp>T6`?R9+sfZZAlS18JQ8O`RCR2 zK!2JvDZ~_8ScDFt_ZpsvQ3$)+mY~{~YgNoeMioK;0mNz1D*hH(MdSVe_%t`Zyhl!+ z`q~r!yzYw_@mxOMJpqXpj4+I^Sl~Ml{L6tz&G9G`06F!=^V0BEpvVMhL4hreY3b>@ zzd_^TGX6~ln4>C9{|1GlBD{LM{hj0VblJZ@J8Z83DR2pgMdixMZ416RGCo&#+d^VT zSb@f3+^47BdtV^n~-#M8s24sP`OK6~X_iJ~D!JJLBv9VEmW+*#9Oj-an^d7vZ-vWNZ zfa7dQ#Jh{Fjq_oJ*^kJSEaUG9yW{XzfgqNP-{r4m*g|t%11KlEk9}*K8leVf3%c~7 zR|Hh2JPW#UU+*C35A^Z%4R+2M7*KdG@S&#W@fn2Qo^n1w(G0r}kB)v+LyD!K%FIy>wCF>V3>;qOVY(#*rCpHjA+L|6*rv&OTUh_T~@;i zOiE3)DK9J2!q#YtGl71OPWWqZ_0Q(A&(SC~dn)U6Nv+Md5BFWuKtXtVh1TD5`;sG? zdl+J!5Oajp>aoA)YF8kLt|n-|AaLdvwz9h$txd_Rc$Dcb3yw2wv)|Rf>~4}BXlNaz zjLU6pZGQ7`Zw27eJdhYT2Wt;A+x+kEuj+23{P|n&$17CTU_vFy z*Kg^2yz8by2H$QXEiG-c$>;nq4)`pjJ|&aXVBl1z>I3Z&1WXt29lRJx=f*}sLR=TE z(huzz7SH{H4&5gJyb~vO{BcX3&f#I~O_0|m%iso!T^TDY0Nbcl-JR4r^kL75F{;&q{w?K6ow)yK9Y2aTp*k(q9I@hCh>~Gw49u(Yl|zS+3zT6IkYr2-xuzFefw>s>kZV4jEd*> zH?{|Z1+1{WT|l&z%$7gs)?riy@lSuKtTa+|7lf=Ok(}M&pzom$SaaQcXHXK1_S@LP z=Pc}4SXc~1nA07D!^1tFR{>SXWOE3GApoJY#{yn_s?{qvlF4g3p%ER)t3LpI)v#o4aC256| zZQBfYbCN}!#Y!dz&nlsM$r}btx_xcnfdW5y|4p#QC8DBYbMo8$NaHNn&Ol?ewY^Pz z()-^5gwm=ogsYR&w7^!s>Ac{6HTGHg^~M-gUQ3G}nT28$jAzi&(jt^rR9qd;1KjkE zRTUH<4UCL@j(%xxA9FV)EloguCz6#mNs3lbP_XZIV4$bfRfwvw^Zw`F-oYWB_q4*o zlua;1QPNSOoXMo-2P@Wz>*3_oW!nc?y^inMfusf8)Q*1t-Tva{#f#wg@8AC@9tCqgSWz97 z0gt2VMoxQwv6a~=2bQqu2P7|DIIL`Jy)@kWa&QsEshS@+p)D=cgB@1Osc6l~#f5Zz z+HvF75)0;4Cx-3@SmRqowP2zkEjYG?f5oQ}+f=j-cq8!uLa0DT+$?M1y-` zex4Eo6B7~o>4O^Iva+;Q3Qh_N3eFw@@I<9|Z`ZKi?o~tt2n}?8wk6LF1{QYqy?POu zdlE>Qf-X#sCp)?l;Amd6adHv}cplImtdCNLA2OANA7nTKxzr>wGziQiQU1`7t*$CL z-QP*MuZ5_-mMPEY;GrsbefuH z9Kvh-KO(|FpSbFAG?;WDg&*ylWs6W8FcB#~eaaH(%8WxJCPKx!1E;CU3LTdMrRq%6 z^<8nX-#_u=lhk*0cty-*f=5S3k8e}VYGIZ|oCxDX^?;%x#$v3WtE)~^QdFh|fAt~) zA|fJG{FUf_1C1rFMC;oIin2Xy;hu~FL8@FdMeZjf-k~^Ps+52Fa##L2_s zgDq!In9^42A$8!4Z)@vKI*gX$%h;LNcnk< zm8*hN;8tQV`Hn=xkR%DS0SJ<@nG$E; zZlI{w@Bh1R~Me;W(nOsOFdbF|;D`0oK7C z*Z-6(`@v(U+nFB9gqKkvk9gZ28Wx7pjX4PaKTkAtPm);c1d_IXYn~otb$$Mf;&b)y z<2as{zCL#DD*DIMK@zmg(GYY((2v0lqKu5e)nu+tPF^$MXwbIqK&TxKGc**p16e^E zB&sEbNsXK{?vIWjSTU~^L5Usz3v)fpwV%e7B;@7M`5maJvTM7$yO+#T?=lhy-u~Fp zpLID1L1wbD?!{-}+3KV2tgkbt2d*<))@k;W? zIF=l7fA(Wl9*XXso?r^E+l<0$^E}Q_?vTl+hvd2kNE*)K;$l^Y_g}1wp16knX#fe_ z4|*S;{ImhXsb@QTsm%-dxt3P9aEWw>suHMn@wfI38z>*d(2dZWnrs-PD zQc=^P9%L=l)YSF^kh~wkqsK6D{UfaBI zf^6d;MSOO4mW#9(2<{JMY8+f#*;;(d%=;a#TM5FmUvwrES`Zhbt*opp01*@jz9I%T zFw&l!oE-cs-Q2Xi{b)K#&^G~qN^i86^mKK@ZJ!An8W>Qwy*^d3N{J1VvZj{7xy@Wo zcV>@vp{(5hocqKyodeY}S?LTs=$ip97r4QHJIr~G2V3E}7u3Jng>I3OoZQu)15~u@ z;>AZANeR~{9Mn3Gw5IZKF_I->x1onYaQKlPDKUO{LVZiro`V%)HbD<5%3XUn*MTU`UndrKzcb zfuZ3(Q3X~CzwRTg!pzM1zwh2LxYXvVdC+g69m|M;mwZxQ)h<-_{{4C2(-(K{+-bX6 z&vZB`DK5ri{GVx93pg!vw~}lWOme!Qu9ZP)64)0S7*Kh(-0JbAsIoF9V<$0gnk6NR z783c};bvbjzyv`KA%o%)f~cv!>fJd7C1nRN+AAAaCI@IV>@XMGwujfRQ#vhn{V?lvbYjw4wgt>W<3Vghuot<4(aWPX%M8uQwloYMth6Z=|So=|MubjcP zvAzwZxZw235ECb=KOv}v5uV#Y{ZHQi&vgF(KD6I;gN}f3bMuQQf_+yNy#RjO06|e! LMW#~9 Date: Fri, 28 Dec 2018 15:55:51 +0100 Subject: [PATCH 022/358] bigger logo --- docs/images/logo.png | Bin 12436 -> 12030 bytes mkdocs.yml | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/images/logo.png b/docs/images/logo.png index 5e9541bccc8010637f99085c07470fd6e293bf25..c7138e84b7e6f06a523e1d1a27b21691c6793288 100644 GIT binary patch literal 12030 zcmY*<1ymeCvn~X8cMb0DED$8PyE|-fw_w5DgIfsB;_mM5?h=B#+ui*4zWd(Wb7rTf zr>CS(bywF{6``ysg@Q+eVU{C&! zu8FCGo2w87#Xm;>`}=P>U9BwtFDHAK|4|F9AnU(3tn4gotp7_J{3+mHuYi)Xl?9md zKl;M#fd7pA|N8!i4}kR_^Z!Sf|JL+Byb#+BF_CRMw|y{SrYTB?)v92M(rz(qGJe8i&K+Ai1lUMP&5D z2I7?0P|>7lv;8u~PAIy*|9IPI?^xmBY-{RxId2opo$~hZ7)?*-1Og|s_z$BG5Aw?J zJ#S~En$~QpI?f?W9G+|l_#C%8Mzt0qU5xo*WqZ0yi*p3Z)QT02lm{hC1|ADjbggHs zinH%Qs@sQNF39T9kXsLeB)+#Zsh>)uqjhrO&`1HDyRiEZ&dBBi-535JFMA_;9C?i? zR!?UR{(@XK240=SP!?v}TOaQa*4cs{@5B%Xm>|}a?QD-to#InTE}0${h1sbLx4ObY zi^n7-;TAn=90)V|QU9lTH6sOu?w!cfKGsLe zVb0CNrniHcaPq}!!wCTR)yp(CK$eZoY%rRc&Vj1i_hBQ|SBuN%{ps-CzU|C%oR!c2 zWo>J*sz8XO!}c(Cvq$&-;-B1uJdfcum@;y#9rYgj2~sq>LE--IH@R;?hp)U50a@Bf zd?UnARaOU2ONQQdlv&AIhAa40!!Ss=u5~nj5DXQ|anO5?K~?Vo6^~G80Au;@XAMiy zSCRTI6Wl-@teaqBsAS{q{5WA<$eKAZW2(n#?nUiB|CR!Z%c2O@5 zRy`T->so%;EPTGdzk=+kgAyQ8_3Zimm#aVTv{)|Jn?M5>CJuY>E z#U@%Uc;391pG2#)S!$hp@WUBdJrXij^AL(sw0wxfWBnkx*M9N(2|=?i)(7fdkaiK4 zxZcvvS+RUV;2LH5B1@adaXO=6gujO8WSG~u%i<*|43X`JZ3B|*a>q=yuE%D(7vn^( zi2oC7*Q;}Bo_H9cn?Yfn49#d{g3QUI7A!}GmODvne@d00F);MnBL zV)@=+jNne@*6W{*O#9X&BpwVLZkm40%H}G+JCIA`=J%7Mt+#Uz+D*TgvzHqT*9t?B zi?l)zWVvF~`vI#zKk?Z|(_dE^32o8$6y~P&>xd|p*1y62Zzo1J+$gm-u_9&GPSdeH|*!wD&e`EVMjvh{cf6+oSswgGsp{!VA$?fY_n_k4S_ zmp66nd%$RP#YQ2&A1B46-#ncu=uye*@<$%+5O?$CqU&O(Zb6)tCuiXaRYdUPdW==% zPf31kBC|HB00S)fyoEb>s^N}|iVlc->G0o8SPv-G3O(6)>*1B9%Cx>r+GnUu=jGg~xhNE1yR05HyJv-B}+^D)d}mM6w+_f7OR7 zcq)M91nabppuA0R^7W_H9fjE)@0QPvdSJ)XpG>sB(tC@pzB_-fwFNdX(Jf!`4lq9! z^&AxPQ(KGs9A4HRcd4|0n*aF|28SZMTh;ac9W7Px?jY4@Gnd0cb_0Rk1}cR~XZNCM z%TF;dWXc!9^T@d#mShff&6Y=fZkN;QGM2jRV_F=|qA{M*M~GHq*R66oqNlD0_~dU|~!OTQ2kK$9gT#jP*l={Lxc!p)@K zO$nSZeLNlJdi=aU*xaCxI&fv|vH@X)FStqJJeR%$bA2tTgV97@r=J{ zL?6lgUu$qu=y!t9gfFkgRbL^X$8KqRlgH#^feOZmPWh5C94R<}$E&!T3EZ@AQvoWc z(KnlDR{2ikWPXn(C);~LvO%^?z@V^*$hehA9811{lW-qwplsk)hqs4Ov2M{1`WO-8 zZU{zzV$)#5VU2ZJam?FZ8}%xJ0*zBVXh zunGO4SmbR4(W5^cW6*h>>KijwYTRzqLdJtf*Nef(Wep2kN(=a8QgLTKK*IUbksPhY zJj#PDK2hE}C-Qz3H^C^{FZ^<9#vHpomtn~mrrx6qi_<4fh=(e**~eN9x3jd%35Pat z_y`3XdsWigHg3n)!ck-q<8#>q_h4bk%gewR&v0l%M-68i6T14`HNmsyDMjX6AC~JE zM^qvtFCMLf!A!7+6S0`gt-zyN)Ghjhz7;(z)SqesCKtp;=@o>howCh-1!^-^5Di|f zG7>?%Qm4>)-0@u5`_;2ovItcSLsTsxIdyW|kEL`f_L>x04`4ung6{yv)7JD?G;MzO z^OR}7>_x)z3UpSjs)I48Bi)bs_zh>nMAJx-!|Un47sE?op?`@c{J1T>x&2jk8?ybe zibt*Svckc4Oj}V_aL0Lf&JJUYhq*bO8i5)aw#W4dEgmYix}xl& zz;VNYhM<9HD{Nz}wKt#RQ~*2cgNqPsv^y4IB(T8*<>(95$l%yfk+u6wLi-LLbBy?Z)E{ZZ7PFwnC+2+NBizc zVA<)ZzMoyV__9#thZBKhs4VT|$2NMntCq~N1^w7Lo6kKQ7QnpxXr}jmkGb_ZBAI$J z+(^%dkMp%EEQ^C!|9B(g2THLiKV%_53mm1i66%D<%<8kJDMQQ zI{X1uraak{ltlHS8)~fW;o)WLqw^st_qBfodb~|Yu`J8WHsbPdGtTdpQqP%P*v^g4 z()L$amNrTdb;2C@40=$bB6pb`dKyA^)oW`FaK#1GKvt3);`Ver?2!Q#39!+ z`J8?uxrQl@MJ%wl?+wL0ykDnwd7h?_v;Z8y`@}6@^=1EsB?`s2ayubxB>=Wf)&O*F zz5&_<9k4St=i3&c$88Sqlx!$A=Y1c5;;VCURGfQXpDBs_=kw~7veIoc7CB|TInRDx z&O0=jh!&|-eU9QRRm-#}T+SyX#jQl@Oja7&#b~qGx8&lk3m>V*umv>8*9+eZ?*+I` zQSv{wU*HIaE&ob}k!!ZUaQA61l*c`ivGs;l*=uoBhEiz81Lu#MKi*%2Vnt)%rzkh@ z(Kq{PGe1eSe(($|F;?bzYCg8&S&paG_!8@^UjDk=$+O+fyVbv>A(=|*`apxymi5$L z`i@WfJ&8u)+LqQ?@S^=j&Hpu@1vDZ)g6|35M+(-h)LVx!Swmth5m>%nGc{98Vg4Lw z+HTqi4$aU}!SIn02BjHzRKUTy6|q?0MpQRTS2)ss;a>Y3g1MnWPK})%EaR$C^<(I4 zE#YI7ffDV&6hg3MLb)z!&}D?vwuO^XLYED5{FtR9t1*4__F`*mi3I8(Sk_V#+fO+3 zt9Jf6`{-B*+a-7bnTYV{Q9mJ|v;Pz?AOD53_5?b2{z$X)rB6yF9HrJ|LJ-aXJ8@GZ zbTR*TZ3bAjmSxP1*;KjDfvOQ2oh1bl2C^g#qc)si-|EEQKMH%lvR>ApF3a>;1h9{> zj_AqxoLQUW@{CEBtX*d)pu+eGrEdXQ(CgZq$c|>06Z0ey>koAp z2Ow}iZMa&*p9My#hT~L#+}ZO(5j0Wxc8fR=80=ykF-{bCH(cW{B{bVY%nO55g04}h zQKV1&<4A>Y6M>&CCvbAx^_>QACXWlc1DC$oJSX}rMN~8~{smqG{npUy5D6%5>#LfN^CV@BY9vg`Y6uO9?#IxvL}q{u6dT0L zilVg=?a6Tk=IasYW10BTDW+C;y4vtd2_@wm=iBQ~|ZA|jHK5@nzHPai3 zd{bxkTR9^X`gH{8(s4DqYBl}JocLp9v7bkFy4L@+X2>vFD^n0Z$zpeb;-kWsh+SMb zH8Pt+7G5(}(Pl94vL?E=0kTiH%smVo`^~PtgQs%JpqX0(6j6%eEc%n0*kzo3@R~Tx zlKy-Rm8&{28xErB$u7&e>{anCTSdfYI+$#rW+bZ*hKfk5WtEPPMT*GW<_jQaj6OCd zrE-&qW%l50l5g2IUKOrwCgKhptzNn9TJ%B^HMKI9?DFoYtF2)V!Jb%>XkY5LLBP0* zZNK4iOT_mh*FiJWKix$1@2MSk>NZGn!nwaj?YSKraFV{(Cm9o^Yr74^>yKnUSMs$_ zL~l3QL-w-p$UbNI7U^N7F#u|WV1q- zqOwt@RnGd#gm>&LVuchVTYeqSXFiN>maF%&rFvdIaNaIBD!z6c*Vf6y;Bm^9mmua* z5OZ0~sfTki29w!A46lt^xYNHBNR-3*iLN4&W+Evtf!GFcTeh7|2uGchs2iTI+ z9}!ZOPDe_Zpl#&$WZ}Q%`@ntA{g;>1e&k&<;iS^HA45t$T!|;sq4P;Ct)E!^xLfFc z*$gW19=a>7X)7K^QKLv7!9|Jx;Ad1Xj`P{n%kUIWurv~;JN^qRE~}ybx_aJld$57= zm5_d;?$l**>W=-PCtmyAlF=3spQG^(d=khF@jOr)1#weV{$Rfo&Ptp@2KP57aFU4+ zWQ?VGIh{Bx0WIlC3UxLDnYC^?%F(^AYH-fF(}6|)lh-kX{_3auo4`UKJRr4?J7pzA zUvjBQDOdP{>W41Hw3;@QQ1GEjvi?xwS3m5^Vj=M~A1?L+*mkEq%cgB$Q(Ef8Vt#$s z{UVvJ=A|}mVfe6?(D&QFA4s}E%g3NASJ>k!(eD+0>FjNy*c{$5^%V$PdX5vq1?qsh zsz(=G{m(^NzZbZXe3m0LmX)O8^@*`)Dnrr`??zNRe*@S~`}%NB(pPp#;0tXa@cmW1 z`){_@A0o*+Qo43Sl!aw%FL%$5hA$qSJF3-*qj;HNXv0U(f#X=$s%jnjG|p1QJlBgL z1uqb8aaHhL zvX%uuG#Bp$y9ck+BG+Q2OR+fdimSuS_>rwquk>(+824?^3-^l!=Q~Buu_LB5p9BE#C(mZ+yAax;2wb?x;{U@voD|J$HkjT3;dtPOtJwd zC`NYm%X-~B!Bv2 zxLgmph=*ptCu*rmo57ofXM-{9)&i+s+~HC`Xcdyb9U%rERQ8cNRhT}8-;WB12Aq2L zT}2_1)-b*6K4;@h<$D}=!RUJ+Txa+>?X=|Qonn@Iy<}c%VGp&OQ(uiXCp_s863p-# z)3yMNy!tVdNsh*FGz?8l8dP|sq>(C(si$GfsW($4zo&j7saY^MC7YCV^0`BtQKPe` zps~~J8w1RS7)tX}8A$s))*nvtG54>96fGF-TC`w2oO^agZc=+kjcurx+Ly&bN$&Y+ z6^4AUU5-&{MfH{43l5(`i@eG8TPJK-HCO254ET%QcAn?VQupv7+}F@tR<)fA7&Y1E z>!?2-@U@~>xpGY-rXso=KhM<^wc8QUennV)m2J1v+2r@V*=g}%slDX)xlF+0q&a}wpAxoY61HFS{psVQ z{z&mSI!kkqkXPX8k^{ozdk3I=NoZti>y%WYBnxqj2#itL?HzDP8aDcgJV0dmo0`Wu zY42(DJDyU2K9kdVB}op#whV1HSLma3m;z^YscpsXEhKxUAFhMes$gZ*t4hA))B1?6 zu!SWZg2;u-^P!6;dXJv&%TJcyCNv^H4kMvvbL_XRgnv++bvwIH@i`KA*oE{qCxm^K z_tF9QU)tXor5t~Q`nZQ07}|l{r^<}SGvi_2Nf_L#^kFbFHZOTv&5b^+8u}wUoEIb4 zH0nWl7T3i3irThkMy&0)XC{y_@x|K$sN$P-`WQy~{o9}WvNK)TmYIVlfEKYf%oL<_ zf#0Ba212z&FflrTfuPez>iVdJfb+ChBE(4a^E_$1~gC_+s=~pVp;Z0kQEM1!7VW@Zf zV$7M#Wje}r%w!oFcT@AM^N<%RU5q`9pkaY+u_ki7D=JcwxugyS#{+t}KyF#RX831k zHGpCh z{``i3L{BDIZv&b-XOk-3YU8r(nBzZb!`&iO*2%5`2Y%05nGqd zNMK_}6swpcfDe)mo85M~`eyIos`d3uIi;VHC~a=fS*K@umU6990#YitoaSh*OI`R( zNvl4rD!>6ca6vK-hrPk!STM7Jz~yjKp)8935>GL4ohw${{;ZQDi}oGayBQ8tdz*q~ zC*jIVkwcc08+_X7_PVPhq?F0fk|bg1sRx2F|Bb1^B-ZhK44QbAfM*(8z&=3x*n#)V zR8ASDObqIG#$o{-{zLJw>CL?Ymh_Ves5Q8#*%JV_N4odc&{xeR__89ux92o==~fJm zv;=)dj4a?%7E%~16fiSo#4v`EjmR+qL#CChWTjSE}b6k=oi zfBd%0f!Mb9hoLD89)AI2euC4w0hoW)wRjAqP{ANG&Cf5EHTzS0&TCrfy+e88D;qk^ z+!zKkS}+WcNu?1LF6epKdXZjsQ_rey^Kz}+)AxmX9_5OjX{2h#_ zYLUy1NQu_h2@?{_X!$8R`Pe4<@T(>j`L{rBeWWjc%I;E=N-%b_9p%oX9AF^7e^!3r zA&2Bc!#j(Rff;g4^p|{w=y@9#K(9bp{V}&ROvC)6&)Q3~xm_j-4fpQYk@S!=`=s9} zc*qf#T!oXUpI~PAc&Td3rN~*orITWk67v<&i!vNxbIwxBsu9l1u8a8T*~isl2G*x_ zioazF)512lVzS;7z6b*EzOdZA##&}FW3>pJ5plz|yE5O7NPG<|a<{Tr5DpDxs%S^7 zy*i}*M0S5PC}uh;;>8kdyyxQpiA(c5u%Mc0VreTA9eMXHDeY5dt@$znLQxdXF@eah zrLU{W@3D9yWGTf|f$<<2=8JDFQmdF@ z1~DF2hiw;~-a2){oEAHOc`HcTz!3Y-dVuUwEU{HDA;6}=fTof(JSg%oIfc4-A1svGEpLoWshqO|2I_}F+#Ie%$j50zW z(Y_81po_Uu{B@mZT#s*o$Olg4P>(4dEae_>&W1u5R`{Fr`AqB=LPuY7)IjU3vD~A< zJR4Be#WS&~y?6P*8ZX4^2SgDhdYw%@7*ge3$djL|p`)UW!)7kWVe&O5Gvf z87YE!&xos9kITrW1K|U|44Wb!xWns)28Q9kNs}syVa7P%@JL;Uj9G0{DNhl`Ge5U7 zQzPeZPMM03FKB27VWe78W@O2E*-!4oa!HpXU+XeDyk<)W5 zJe+UnB?g#6?2P?>yoAKSFgK0jtVxRq(=>WW08mQB0w}ZKigEPB_irMi_K~_c@pXol z!?y0NGI2EV?e#<`)32V=I3K?5*4Fcg&3h*l$r0NSiYj#iBb!z%?!*{o|g#^l?cuvj?J&^_Y>nwaI3w| z@T#O6j#Vm$R*eV`;VKJvAWtII$GBt)xf_UQ4g94U;GfqVdm;ZC7puK@>4uCKM(y>h z0EN8X2GpusQ0eFwk;(=$#Z~#&0atOf_h9X$jtaI~CH;BeszI%wP@I(c{xuIANK zbK4euj)A}=2dW;aes&_QFOvKw$|7yc@=)Js&_ z+;^xDd%U5GW~ap%3-YW9WH@_935#RQaCvjYpjEMf9o*a@MdD{1-vo+HF#5?gzmveM z%gJZ(!IzM^x)}!oYE>i9=GJLW%$1(<@4Z`ms?@7!iW}|>6Qi6Qc6a{Cn7=< zDI#6}d;#6dl~OZHzaN)DVf|c_cBmxRIz=C&y9g7j9 zUKFREg1+M(mYa~%j&Lrks>=XG9-~)NCZ;Pk)+OjPtdLoAS>;0Qc~>dv!tr>{qU{Rz zeWR<-?+;5MR(=-#G=OQd#-8Bb`_o3+(l2gL9Vo0v;mf%GDcqJV5yp3Bx2=+VH$TB% z{4?rjZ*~PV(F~TbAd5@3Blp(wXza%JzdVG#k4hGrdX|ZU0{dlmlp}hZTs`JIJ&ukuo{O6kPjl#| zar1U8!T56i+A(?x`bh9*Vw`8i4PUi1O?^-rI1}$Ft=O(4l6-LO4<(EnIF#tGvU#=b zpM9{sQE^neT}`t{pO+V|;Rm6;8%@FZ$Tbz>vy++*Qv5cl$^4dN7H?|Fl^r>C0O;Nl zjLOrsvf!>RdDI0Zhhjj2kDen2R}L&0*YlyZ)IzjdL-)VK-jKk!|yBiim&vg7Yk8YyQo*h zAN+!}W3zGF2BmV>pdz4rEnoi)Y#5L+gCxJ*wNfRy%xaO9feZYFMZfgbR#bSciV8Wr z+IA$w^VM5tZYMvrfoAbDSS1`I^;$wUSQ3ZUPpcA@`|b%eVh663%eZqWK?D8KXqP#Y zS)Lz_ML`Zt#M8R9JpcHNb6~ubv&r(gA#~B?VARv@uBt&JQjrR0w>ygP#ekY4^uu?8 zcTAx!hb(g4vz2Xg*fR4LId0KKU3YsXTz~y@eF@aE}qJY#24nnesT|gd%fZnyBT% zr61l(XmF$<0J<+@zQtkeg*Ebv!(6xn1NO$;FHVc!S1EFR%dDeT<;wFHOc%8oVrbn) zuwo$A_*A8O)VXqk{6y_sIU2H@Ymja{T$)BGe?S@Qi3jIZyx4X z+;c_vAX&f(-J%onMj+gEF;>Vi{)}=zBHA4p52@w~S?5P~2{D`T56+Iw2!U1i^*kpTAj9&72h@E2EnM={*T$Ssj}WhaRo<|uTeL4mY)>Y{0$EoCl<96=*vqFkUs!2Fg&jqK-xQh%-04nlmm(9& zvI@@U6p;%gKMI%kwcQ!OGrDb_T1YwV3AFvOeB#f;vzX--hy#_zz&b`hC1uW*fl-`p zHa4%LrL{T=j6lmY4O1uvT`@H!KR#BK_?DBFDkKbv>KUn+dmvTNr-eo18?#kWgD_TF zPd`r>8s_}AJE-eVb-s~fSP14Y<~*P@1eMr@xBB#ZelGbVwV`Ko!boeiCu8H0x3dgk zqO4`UJJ3JsxI;+mR=QM~dBQVBi0)VLOaT;z{%Y> z-!w;2tj1XfHO_{~At)1<%pGxQF{f&Q=P5J9AN6kbH9R}DVTXT1u+k3t$Y4f!1xi+E%!~mS>X=0qR6hml}y@ z_z7|H3qw<1R+pbz^Kqh7DUpe6^#GiDHNq`f=AjI|F>>_1Ry>|MdkcQZ6j5$#q`_!f z9SYufxZ#yv-#P>toi})saS^DZ;H99(YX1`Vv&k=UI?ItwynL!~#M7=JVF)r5XFm%i zvJWq4F)k`R%8&LY~^uak;@5G zG`*C^NJ}tpy8hA>4ME3?gjR5Ww|)w1YTA{wld|;X@ACSFg#3$eIX7K0C2dg@11+8V zFN;o9s#7pZO$c2`i|5a$6}PI)7nYF3&6#GlG5a- z#dIl_Xmdr14|V;qy%F);-CM>aBK1eaP2@tfvAg>m+bV1$nRvJ3zccwAbyLEE3z)bk zn?o%D7UYQu`k*q*f~DU~EFqevElU=kCJ8i;=*)n#Fx(T?cah>&YfH@AI?qEjx5x&w z`bdC7Hc&9+tyAOpi*jB*AwX~ z!wy4~aE>#)DP{}1&eg!-hBYAn6jx?h?jx!vW~zkBOQtS`V3+|vEO~)_7KBhaQ$xFG!yIQX{(YH!sZ~kDOAqe31m!fJH;xGk~VK1qGQm=wBGez-h|@T}i(o z8ygkeJCwsP$AE5MtXNe2)9!=x?=kmdj5g~ZCZhZm5lX0Bf_Z@HawUVwOU`YQG!A7W-v(O#b7HT+Q^^-V#$mbQE!n+X1dGRaWF2%~?2B(IkAooYUGjEsd2;kM-Muw<8~G)3qq}Tjr@%QFb=(`_roJ{O!!YX!3zduN3mq4c{%z?g=*4 zXslC!lxax!qONLkP@_+ck&b{aCz1O?mScI#p$m3#BU?CtN;3_J}sKeg_w|_9u;A@3WWex%Yr@o?$wp`^IG~>HU=Mo}c~Vkyp+a=~Kh8)2!1A!F}(gRLnQD5`jpluH%Dz z0Z6E_Xp96{B#si8Z;?>%sUqb|AfhKpu&8z-{vR(V+9I>K%IWFNRga83t*fu+$&84= zdMPUU$%9@#<|&1k&spY=p3ql`cDa*?D3TFaoij5!==}VRmZb`uGeTCA@3vJ%YV9dh+>H)0`ZNGj*@N=qY|i4E(a;A|)lIrTFC`mZ5|@X>k+@21-CpjT4^M z(csh+@zWEP|}?cvc)Oim)j zQ1atxFr=raUk$dG!zCI;)M@R@-rR$@@02D?7qP(gq;}}x+(>=rK<+{2W?eseo6e$Y* zTv!UKJ#7qmwYBPX&vM1geZ-7W?^>9cAc(v^!MP*gxKgq_IW>jI%*>qE&_Kr9(b6JR z+0x>jXj`GBqKb4M6COUM&HHZ|zFt~c*~4njKc*%#)h{HTJi&6jJafwX_>q*C1{*uF z!}yaouim$lhin7XjH+5j&Ccc#D0Q2;o_~J!>Y6PpJ!8@giVLfG*}oJW8JYRW-Wwe~ zwEtO-$lZPulauoY3K(UHDs*ExqOAinGXl@D>bSiXRFQYs2c%+fItTQ)2B~g zib_iKRe#125A30+UB)^&IThZ;k>l2TDqN1X|pdr~kLFcu)fPjGN*}7N8 zj~_pNkUZt~nrR^C{Z4pPL_~&|`B0LLosv9*(Ih*&;60Va1V$x1b2Bp>ri*k}?F4&? zXFo?X`I&L=a;-6@7j(`APfSf6nfm#)Kc4+k+8XbQ?2Uwth4pVNlYc)72?H_JfW6Tj zs!P&GELZM?dz+nMF<@`3x|)*)3Z1An+n8i-W`?27?`ke-=hb&A1-Hpw8EI+cS6|CC zFH~|wn$?qB4cN&YU0jf(qN9H}jG^OG<7{qkBU@WrBP1${{9c-w!I++&M%Z8O2<3Ny zxdc6BF7y zgoKFSzJ7I^6;z{8P*g5ksl2?9A3l@@29msiv^$H{|YYl$G8-<>%U3F2mQaFLdZar^d$-Ja*?$ z`FME;HSZ`eFf#tw+nSHx{$91La&n|81Iy}hKeoScer`^T(V0hETf4ftI_~)kR~Z)< zZciwvrgZt~g@1qgX}-r_Wsyd|qZ~KfTwNs&*)ZXuz}-V;d01Ol*F{J#M-ah4N7tRW z^QuDO_7*y&L-Bl31@Ti}zAr5eh>wpqvk4&R?d@H8rH?Y{uHJ?8Z$n`zByYs=5mmqNvh6qzPE9Gb&-abnvsF! zu@IisNblz6)&;nn|Cx}62LJcgR(O*Ca=Y)3SvQ`8bD#0CF+6TNIB*!Vza*KG{b~TC zDy$_XCH<*mPq#)6KYy0VG#^gMmW&klyYyUv&R6PXZf=f9%&dYE5h*MFeR+DCM9i;Z zbx>UaPBd~uLxY5y+sA=(%DZ&;`NjI; z+}uN_YXHu)bA5gN$RzUP1zS72+?(U6XXSYmuw}`l&_b7>U3~DUGWs;s>%{r$)vKrI z*!cLV177ptytY2SrysmuqoJaT&JaItZSsN@Azf^7vmu%_SHhH$$`SE$4rEvk&lP=5 zra>DM6Vqh%YAg%Wezq1hi_b1Y=VMr6BC!PpMKs#8YrCbkyICS$T=v&48)LGg9ng|? zrb=RG=w8(5;HI)0Iy1LMJqQdN1PqKSoUrt9q6!!^dm}=P|Fs$Lb;YsXb#88M1$vfH zZ)k><4wV;+m^0}?CclFi?ke5;;^J@SO=lB@@@YMdaoI?L2!F-+?B*D(a2XYplqjX# zbv2L(gDfm89wd-lCq2;Mw26f)O^Yvngwm)^eK2e#AiO`0WGB8FPD1&P)6cyppN zo?brS;K0#jckW}FS6W7fX$X|MNl0>Xa{jfEre<(@$fyN&wm)F%?|oPhZrsRTsVgd? zeyBvXp8ERw09cjoFAo?PP&oPd`-gH)j<=?2(#ZGiXdEu7v%KQK3|k#5XBU^?G5@hF!P0X;SJUD^p*5X@g98cW9urQX zqM(0=w$KKG3gx2zQC84)TpkXqjMcbqsZS&Eb8>d>mPIX`}B0Z5_3ysCEj%uIqPVdW_f2@n}~p+yE|Xo2r4VP z88h{pH*Ywnq#8Uvr#*hDx;i^Q-yaVjSfI{RQ!PKG@&RU~F>ERjDh4HT&J)zkdhh=F(VX1fk<21)<@-$uxy7i%3UD$GQ6N z;^Jk9Lh@L+V7FXh@*NUYje9rF)Ddl32T7cjEKeDyYYNUo8ASgdSL6n5^; zHwHd_*|*Y{Dd0rc_L=H;2L`r+Oeh`|QbA!MB7i~nC3Izc6m)d#Z1Lc+yfKy}LZ)Xq zINYM5R8UyRM8w2D)M$~BkvqG(5UcC!Q9|&j9=cysQ&V&1UuvkU2du4`dCDyVZs%Oq z*VF{^_Z8FLWeFU(v%bF0>Feujo^f+|eq^4|Q5$4!ss;qChz#BqdzQ)7X8Qa0!s{^; zPVW)VYITgaT_$;Bd5g_1=mUd;@g0()2;(k6|3ob}0YWxuezCG772un`coClv7q=32 zN%fltjxds#m{>qV1D}sIF{*7AKvY<4Ecz2ArNBsX4%12;M|XEWCXZD!)LQbO!65&e ztHxvo8pPq}|h%BA=)QMN#lR9ED!#BqjBR_3n2EOUnTA zN5w@&UHYfMUKDu!f!ODI%``d~d&>DiS*wp4lZ@nuUObbR?QOyT%DS!`_Vvk@t;@3e4cHDSg1XNyDl*U|O?+-?_#}z{XZFBp*vHm|)nKB*Cx-ARw2KkW+sUHmu8}f=Yhl= z65;XQV(UsIN4o$OHT9mXyO%mr{9{l+&9btx-a4VK5p;2^+LdwaSPWEDRF6-)iDYSi z{PugTVRFUH8V9hyu!rY1D(T@NA4Sb+coEJh$LNajq&nz!L zKky@v`D9qz>*WT!c_4qhiXSy)cx%>B;GfL~=;i)X)6)#8+rz{_z4{Ol5nsV)GE4b` zX!?4w*y_>8{4B?Sy~2jY05>5m&2snGFE@bnk1;AGL1<`bjFfz~Gg?MQm2BHKQ=}_|3fPl!C!NDz#;qC3s&AdIL?A+Wcp5+)bbMqG=pjwiL zzTP2`P%i6@B&WHC3pg6<>#O5`e0qJFQWlLxANRFf8+{+N1uMpzQvKH?BqTWLK-zlO z*Dca{vDqFynjH75Dl6;0=eKLsNfEs2C_n?8K8wR6NGRaT##D()w)fHWdyP_M7CXD$ zAs~ZFAL~j8kOYBeoWjnx#IqPVJJVAFcrE945A}%KfPsH~h z39s!!lM}w+J-q90aa?@>m)iZkz0LW-qZz!cbfTiZX&*oGx|6ctf#_K~I_Hs=?m=4n zz!oh>Y-wqE8FYE8@%YWmm4JH(4Vv*4j?%&MvNOxxcl!Amb3}KupnxvlqP9jQL9flT zMsdPvChO_W)mcsy6zZrHR8%bfYO2ejC7_N)_q+Q0-E&w| zFES$H__J3IDk`dPu85Z@4LSL%8)(bIP1=lP*QG7luYSYXUPID5b-`{x$N7;MDqIkn{;z; zFQzqRxTi;Y?MMs@8ylDWk&ffN^EZ@_O+VKG_KkrTQ1UEy?*8uK-@l^FHZ+WG)N5;l ziIGbk{tgR&_+is(;RMiF-LwKjeb(6GQXMw8wz`yL)HO67A4P|UBf7e}wp0a4JWxsN3)9?*{DJr9ZkFV#kDl*fiy`xQC`C{xo=|*$wxPP93{_whO^i>k zfy3eFYrW+>2X_6NGkv5lW^2~g{iV0}KNH=5NLEO*^;T14YslPueC)T1q(pQKS}8N_ z>e=~u@KBFfsa<(x<&t@vM0%6iU;=W17=LNi{UKi9iT)++?Kx~um-hDdQscM8FLvt0 zy|W~ShK5`%c_{F`>N;+&TaEiYft@ZzL=h7a)fyPNGchnk1!VY`2|vWNz52IZCH>jU z0)QV^X8o19dGgx!Hf7&0=k4*zjh{cAfmdrqv+S3amTnD*Q9wPkdH(5clgC_}cj&mf zdmaZ8{3wq89spJTfG4QQrNeq&e*W*DLE8M4F3VAXRd+mC>FWI{R&{l8$|1e(1?~i? z$^3~D6BARj<@anc-+y2G`sB!rmQ;-gzkNFqT zsf%&ibQS8z%gbwQv$MCfGzkp}VTTq`#V{XeKJYD_`<2lMz!dXfosjqOCXw0FvhV5Q z(#b(oRFovxM2wbvogoG_3e0$$)%uyrmIlk=WY$nq-nZ?jFDWf}L55)l{+J?g^6D#Yx3| zXM=k`n_V_qcry1EnpY#DHda^ZkDMG{3~U@(L3_HgzzdyX2@jImwR-vT<(cgvm@U%E zgrziSA;H0c&?i@*j8^==WCWSSF>7{gltaB+5i`tEO(JIE8fBCl97=;VZfkU`}HgER{hII-ML?@Kr0^|7Iu)-=TcT#x$Z2G<-NvRY1|=3 zc=z=DXakD3rPP;5T)g9@j*Vs>5ripp(=b-94-j|h0WuN^yJQNCT zy9*qw1MXN=*c_hd&%l|P>-c?W<~5g>mn~7HKjNVsA8mXIZA6gOPoYzi_#PP*f5)$=gv(WFdS++Ouc7wH&h{3CXYw0uXKOKkt)qUgG|Y@hVLtw5!j+yawE2jv zJQ&A`gq(c)R`FnMo7wVF8q}Dgo?TzPZjM00R;~ECv4QSD%e*7{G@XmF<=s1(553{L zj~+eZF|C`$#Xx*>`-V|uH(OIB^uSEi-VG!sI$ogKTq7dl=kNlC|2ef<3V&k`C1VD(nIg1FzheSq@(S$V=ptQ!6E=TEwN zgbeCOF@I2lLu|Ln$oMZLpoK8fXI4RD_y)8cZ z>tMOWfLl;{)doj&Uz%_5=P->*^%V-3F16CD-x*7;q7b&CJmKs?8v z!^@L-hf~_A;NalVo(OzQll~a0o6szNtEz9{P&Gt4KlAeP>XVa`JMUhIjg7_E(bAG@ zSdM(#-7PgfI;vcHNb%TIMhcvOn5Jt`Q&B8tIiUB>G23_m#j}8K+Dz=sR=n zykKsR`%oNEt6ieAVPA;dxLpKbYSrIP*w`duHCFtZ)|RHFsYyhp*!TMK-15U2Fzp@n zWcp?>;~d^Lr%q0)8)|51biXh_3qst_S^|@Yv?Tc7Bk+M&z=dDomer~>!1PB(x@vXX z*7BrY11c}&uPyZ!>fF;z`A)o!t}Y{(fRSI#6rVob;hIDFt1&z@W3Rvzp zkEjdqt1WAFKCnB{)6>6t@K!>owxJ>AsGr9F(ecTNSXE)6Mz=-XHn`ns&2XaQU?O2gKb(PA%=oS6oLR(Iu6rtjcuaCnd?T3D>- z;R16G&3FMdO$u7_!ENK%gXYi9!3Cv`&Z5#DIDiitwgjF5jpsF(3Jva27gwjt9=cR} z8yn1jo*2K!en;;mLS6Cxefa$+G}6$xQ!+C2Qcsx4Ztdg+ zJ+rPisBf>{cLkvb!LyhO0vQlGTl83#{Mw=LhPL4S`v+ML?(Pjb?u-l!oyPZw3||3P z70%xvAtPH~1SyUcm6448S1CVL`NBoi|N8QtnFtL{uH%c#A5bE7YX2T{dh9Pd{8{af)NK9ab%bVgBISy!t^J*S)c7`Q zVfskZuVrlfMULIAvASA;>>D#3Y&h2)9G-ZL_M7XA(OurmoE)Q)B33{=K~WW2VEQ|? z2Hs#NiQiv^=kQ@j9NanF^|Z9yL^@cmLYf`m1j9Vk@;iiPwVh={^MYU!$8YedtE=Y~ zhmQmY$~ibVH1!do4h{}V#X4e2msb#Nb|shu%VHJS5*eGGE6O(N50)MnG$R881alJ^nVI=~Tm!$H6$#~qzfMo*HMEb1Zb&FRdGh9;51k>{ zN~$))jj!5D4a+1ueB$6_{=s=vSY^iTzlD$9-3`WYV9Dx^Bkl(3ocnxnb2YNvg(WWr+ZHL;0P@eJ4tVU)*v$ZzRSE z^y+h<*F*96W&jW+!hPV^o=Wx2w)?(T?HBm-=TG5`t$V7h8`U{*KFnPmFjtYl*H$D) zp>T6`?R9+sfZZAlS18JQ8O`RCR2 zK!2JvDZ~_8ScDFt_ZpsvQ3$)+mY~{~YgNoeMioK;0mNz1D*hH(MdSVe_%t`Zyhl!+ z`q~r!yzYw_@mxOMJpqXpj4+I^Sl~Ml{L6tz&G9G`06F!=^V0BEpvVMhL4hreY3b>@ zzd_^TGX6~ln4>C9{|1GlBD{LM{hj0VblJZ@J8Z83DR2pgMdixMZ416RGCo&#+d^VT zSb@f3+^47BdtV^n~-#M8s24sP`OK6~X_iJ~D!JJLBv9VEmW+*#9Oj-an^d7vZ-vWNZ zfa7dQ#Jh{Fjq_oJ*^kJSEaUG9yW{XzfgqNP-{r4m*g|t%11KlEk9}*K8leVf3%c~7 zR|Hh2JPW#UU+*C35A^Z%4R+2M7*KdG@S&#W@fn2Qo^n1w(G0r}kB)v+LyD!K%FIy>wCF>V3>;qOVY(#*rCpHjA+L|6*rv&OTUh_T~@;i zOiE3)DK9J2!q#YtGl71OPWWqZ_0Q(A&(SC~dn)U6Nv+Md5BFWuKtXtVh1TD5`;sG? zdl+J!5Oajp>aoA)YF8kLt|n-|AaLdvwz9h$txd_Rc$Dcb3yw2wv)|Rf>~4}BXlNaz zjLU6pZGQ7`Zw27eJdhYT2Wt;A+x+kEuj+23{P|n&$17CTU_vFy z*Kg^2yz8by2H$QXEiG-c$>;nq4)`pjJ|&aXVBl1z>I3Z&1WXt29lRJx=f*}sLR=TE z(huzz7SH{H4&5gJyb~vO{BcX3&f#I~O_0|m%iso!T^TDY0Nbcl-JR4r^kL75F{;&q{w?K6ow)yK9Y2aTp*k(q9I@hCh>~Gw49u(Yl|zS+3zT6IkYr2-xuzFefw>s>kZV4jEd*> zH?{|Z1+1{WT|l&z%$7gs)?riy@lSuKtTa+|7lf=Ok(}M&pzom$SaaQcXHXK1_S@LP z=Pc}4SXc~1nA07D!^1tFR{>SXWOE3GApoJY#{yn_s?{qvlF4g3p%ER)t3LpI)v#o4aC256| zZQBfYbCN}!#Y!dz&nlsM$r}btx_xcnfdW5y|4p#QC8DBYbMo8$NaHNn&Ol?ewY^Pz z()-^5gwm=ogsYR&w7^!s>Ac{6HTGHg^~M-gUQ3G}nT28$jAzi&(jt^rR9qd;1KjkE zRTUH<4UCL@j(%xxA9FV)EloguCz6#mNs3lbP_XZIV4$bfRfwvw^Zw`F-oYWB_q4*o zlua;1QPNSOoXMo-2P@Wz>*3_oW!nc?y^inMfusf8)Q*1t-Tva{#f#wg@8AC@9tCqgSWz97 z0gt2VMoxQwv6a~=2bQqu2P7|DIIL`Jy)@kWa&QsEshS@+p)D=cgB@1Osc6l~#f5Zz z+HvF75)0;4Cx-3@SmRqowP2zkEjYG?f5oQ}+f=j-cq8!uLa0DT+$?M1y-` zex4Eo6B7~o>4O^Iva+;Q3Qh_N3eFw@@I<9|Z`ZKi?o~tt2n}?8wk6LF1{QYqy?POu zdlE>Qf-X#sCp)?l;Amd6adHv}cplImtdCNLA2OANA7nTKxzr>wGziQiQU1`7t*$CL z-QP*MuZ5_-mMPEY;GrsbefuH z9Kvh-KO(|FpSbFAG?;WDg&*ylWs6W8FcB#~eaaH(%8WxJCPKx!1E;CU3LTdMrRq%6 z^<8nX-#_u=lhk*0cty-*f=5S3k8e}VYGIZ|oCxDX^?;%x#$v3WtE)~^QdFh|fAt~) zA|fJG{FUf_1C1rFMC;oIin2Xy;hu~FL8@FdMeZjf-k~^Ps+52Fa##L2_s zgDq!In9^42A$8!4Z)@vKI*gX$%h;LNcnk< zm8*hN;8tQV`Hn=xkR%DS0SJ<@nG$E; zZlI{w@Bh1R~Me;W(nOsOFdbF|;D`0oK7C z*Z-6(`@v(U+nFB9gqKkvk9gZ28Wx7pjX4PaKTkAtPm);c1d_IXYn~otb$$Mf;&b)y z<2as{zCL#DD*DIMK@zmg(GYY((2v0lqKu5e)nu+tPF^$MXwbIqK&TxKGc**p16e^E zB&sEbNsXK{?vIWjSTU~^L5Usz3v)fpwV%e7B;@7M`5maJvTM7$yO+#T?=lhy-u~Fp zpLID1L1wbD?!{-}+3KV2tgkbt2d*<))@k;W? zIF=l7fA(Wl9*XXso?r^E+l<0$^E}Q_?vTl+hvd2kNE*)K;$l^Y_g}1wp16knX#fe_ z4|*S;{ImhXsb@QTsm%-dxt3P9aEWw>suHMn@wfI38z>*d(2dZWnrs-PD zQc=^P9%L=l)YSF^kh~wkqsK6D{UfaBI zf^6d;MSOO4mW#9(2<{JMY8+f#*;;(d%=;a#TM5FmUvwrES`Zhbt*opp01*@jz9I%T zFw&l!oE-cs-Q2Xi{b)K#&^G~qN^i86^mKK@ZJ!An8W>Qwy*^d3N{J1VvZj{7xy@Wo zcV>@vp{(5hocqKyodeY}S?LTs=$ip97r4QHJIr~G2V3E}7u3Jng>I3OoZQu)15~u@ z;>AZANeR~{9Mn3Gw5IZKF_I->x1onYaQKlPDKUO{LVZiro`V%)HbD<5%3XUn*MTU`UndrKzcb zfuZ3(Q3X~CzwRTg!pzM1zwh2LxYXvVdC+g69m|M;mwZxQ)h<-_{{4C2(-(K{+-bX6 z&vZB`DK5ri{GVx93pg!vw~}lWOme!Qu9ZP)64)0S7*Kh(-0JbAsIoF9V<$0gnk6NR z783c};bvbjzyv`KA%o%)f~cv!>fJd7C1nRN+AAAaCI@IV>@XMGwujfRQ#vhn{V?lvbYjw4wgt>W<3Vghuot<4(aWPX%M8uQwloYMth6Z=|So=|MubjcP zvAzwZxZw235ECb=KOv}v5uV#Y{ZHQi&vgF(KD6I;gN}f3bMuQQf_+yNy#RjO06|e! LMW#~9 Date: Fri, 28 Dec 2018 16:16:04 +0100 Subject: [PATCH 023/358] changed to introduction --- docs/index.md | 2 +- docs/pre-requisite.md | 20 +++++++++----------- mkdocs.yml | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/docs/index.md b/docs/index.md index 8fa24e996..afbc0a73a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,4 @@ -# freqtrade documentation +# Introduction Welcome to freqtrade documentation. Please feel free to contribute to this documentation if you see it became outdated by sending us a diff --git a/docs/pre-requisite.md b/docs/pre-requisite.md index 79232a89d..2ff3f08b5 100644 --- a/docs/pre-requisite.md +++ b/docs/pre-requisite.md @@ -3,7 +3,6 @@ Before running your bot in production you will need to setup few external API. In production mode, the bot required valid Bittrex API credentials and a Telegram bot (optional but recommended). -## Table of Contents - [Setup your Bittrex account](#setup-your-bittrex-account) - [Backtesting commands](#setup-your-telegram-bot) @@ -16,20 +15,20 @@ Below we explain how to create your Telegram Bot, and how to get your Telegram user id. ### 1. Create your Telegram bot -**1.1. Start a chat with https://telegram.me/BotFather** -**1.2. Send the message** `/newbot` +**1.1. Start a chat with https://telegram.me/BotFather** +**1.2. Send the message** `/newbot` *BotFather response:* ``` Alright, a new bot. How are we going to call it? Please choose a name for your bot. ``` -**1.3. Choose the public name of your bot (e.g "`Freqtrade bot`")** +**1.3. Choose the public name of your bot (e.g "`Freqtrade bot`")** *BotFather response:* ``` Good. Now let's choose a username for your bot. It must end in `bot`. Like this, for example: TetrisBot or tetris_bot. ``` -**1.4. Choose the name id of your bot (e.g "`My_own_freqtrade_bot`")** -**1.5. Father bot will return you the token (API key)** -Copy it and keep it you will use it for the config parameter `token`. +**1.4. Choose the name id of your bot (e.g "`My_own_freqtrade_bot`")** +**1.5. Father bot will return you the token (API key)** +Copy it and keep it you will use it for the config parameter `token`. *BotFather response:* ``` Done! Congratulations on your new bot. You will find it at t.me/My_own_freqtrade_bot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it. Just make sure the bot is fully operational before you do this. @@ -39,10 +38,9 @@ Use this token to access the HTTP API: For a description of the Bot API, see this page: https://core.telegram.org/bots/api ``` -**1.6. Don't forget to start the conversation with your bot, by clicking /START button** +**1.6. Don't forget to start the conversation with your bot, by clicking /START button** ### 2. Get your user id -**2.1. Talk to https://telegram.me/userinfobot** -**2.2. Get your "Id", you will use it for the config parameter +**2.1. Talk to https://telegram.me/userinfobot** +**2.2. Get your "Id", you will use it for the config parameter `chat_id`.** - diff --git a/mkdocs.yml b/mkdocs.yml index db01194cc..7ad68178b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -4,7 +4,7 @@ theme: logo: 'images/logo.png' palette: primary: 'blue grey' - accent: 'deep orange' + accent: 'tear' markdown_extensions: - admonition - codehilite: From 460900ddd7c15a04009232bd04991b24e5532055 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 29 Dec 2018 09:01:58 +0100 Subject: [PATCH 024/358] Remove unused and duplicate datastructure --- freqtrade/wallets.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index 59d8fa3da..1f1d2c511 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -2,12 +2,12 @@ """ Wallet """ import logging from typing import Dict, Any, NamedTuple -from collections import namedtuple from freqtrade.exchange import Exchange logger = logging.getLogger(__name__) +# wallet data structure class Wallet(NamedTuple): exchange: str currency: str @@ -18,12 +18,6 @@ class Wallet(NamedTuple): class Wallets(object): - # wallet data structure - wallet = namedtuple( - 'wallet', - ['exchange', 'currency', 'free', 'used', 'total'] - ) - def __init__(self, exchange: Exchange) -> None: self.exchange = exchange self.wallets: Dict[str, Any] = {} From af850801135449f9e9b4272223684bf54ddc0eed Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 29 Dec 2018 13:34:05 +0100 Subject: [PATCH 025/358] Update ccxt from 1.18.78 to 1.18.84 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 60cf407a0..160bbcd7c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.78 +ccxt==1.18.84 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.12.1 From f32dfc57cae376cf0cacc12f679341f2081cd902 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sun, 30 Dec 2018 13:34:05 +0100 Subject: [PATCH 026/358] Update ccxt from 1.18.84 to 1.18.86 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 160bbcd7c..4c6b0799f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.84 +ccxt==1.18.86 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.12.1 From cb1d9b620004c97777131ea7bb94381232690f71 Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 15:22:19 +0100 Subject: [PATCH 027/358] restructuring docs content --- docs/about.md | 2 + docs/configuration.md | 10 +- docs/pre-requisite.md | 452 +++++++++++++++++++++++++++++++++++++++++- mkdocs.yml | 10 + 4 files changed, 455 insertions(+), 19 deletions(-) create mode 100644 docs/about.md diff --git a/docs/about.md b/docs/about.md new file mode 100644 index 000000000..0fbc0321e --- /dev/null +++ b/docs/about.md @@ -0,0 +1,2 @@ +# Freqtrade +Freqtrade is a cryptocurrency trading bot written in Python. diff --git a/docs/configuration.md b/docs/configuration.md index 6f54cfa8d..7714eaba9 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -2,12 +2,6 @@ This page explains how to configure your `config.json` file. -## Table of Contents - -- [Bot commands](#bot-commands) -- [Backtesting commands](#backtesting-commands) -- [Hyperopt commands](#hyperopt-commands) - ## Setup config.json We recommend to copy and use the `config.json.example` as a template @@ -209,8 +203,6 @@ behave and how is the performance of your strategy. In Dry-run mode the bot does not engage your money. It only runs a live simulation without creating trades. -### To switch your bot in Dry-run mode: - 1. Edit your `config.json` file 2. Switch dry-run to true and specify db_url for a persistent db @@ -240,7 +232,7 @@ The bot runs against all pairs (with that stake) on the exchange, and a number o By *default*, a Static Pairlist is used (configured as `"pair_whitelist"` under the `"exchange"` section of this configuration). -#### Available Pairlist methods +** Available Pairlist methods ** * `"StaticPairList"` * uses configuration from `exchange.pair_whitelist` and `exchange.pair_blacklist` diff --git a/docs/pre-requisite.md b/docs/pre-requisite.md index 2ff3f08b5..1dd688ef0 100644 --- a/docs/pre-requisite.md +++ b/docs/pre-requisite.md @@ -1,36 +1,42 @@ -# Pre-requisite +# Installation +This page explains how to prepare your environment for running the bot. + +## Pre-requisite Before running your bot in production you will need to setup few external API. In production mode, the bot required valid Bittrex API credentials and a Telegram bot (optional but recommended). -- [Setup your Bittrex account](#setup-your-bittrex-account) +- [Setup your exchange account](#setup-your-exchange-account) - [Backtesting commands](#setup-your-telegram-bot) -## Setup your Bittrex account +### Setup your exchange account *To be completed, please feel free to complete this section.* -## Setup your Telegram bot +### Setup your Telegram bot The only things you need is a working Telegram bot and its API token. Below we explain how to create your Telegram Bot, and how to get your Telegram user id. ### 1. Create your Telegram bot + **1.1. Start a chat with https://telegram.me/BotFather** -**1.2. Send the message** `/newbot` -*BotFather response:* + +**1.2. Send the message `/newbot`. ** *BotFather response:* ``` Alright, a new bot. How are we going to call it? Please choose a name for your bot. ``` -**1.3. Choose the public name of your bot (e.g "`Freqtrade bot`")** + +**1.3. Choose the public name of your bot (e.x. `Freqtrade bot`)** *BotFather response:* ``` Good. Now let's choose a username for your bot. It must end in `bot`. Like this, for example: TetrisBot or tetris_bot. ``` -**1.4. Choose the name id of your bot (e.g "`My_own_freqtrade_bot`")** -**1.5. Father bot will return you the token (API key)** +**1.4. Choose the name id of your bot (e.x "`My_own_freqtrade_bot`")** + +**1.5. Father bot will return you the token (API key)**
Copy it and keep it you will use it for the config parameter `token`. *BotFather response:* -``` +```hl_lines="4" Done! Congratulations on your new bot. You will find it at t.me/My_own_freqtrade_bot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it. Just make sure the bot is fully operational before you do this. Use this token to access the HTTP API: @@ -42,5 +48,431 @@ For a description of the Bot API, see this page: https://core.telegram.org/bots/ ### 2. Get your user id **2.1. Talk to https://telegram.me/userinfobot** + **2.2. Get your "Id", you will use it for the config parameter `chat_id`.** +
+## Quick start +Freqtrade provides a Linux/MacOS script to install all dependencies and help you to configure the bot. + +```bash +git clone git@github.com:freqtrade/freqtrade.git +cd freqtrade +git checkout develop +./setup.sh --install +``` +!!! Note + Windows installation is explained [here](/#windows). +
+## Easy Installation - Linux Script + +If you are on Debian, Ubuntu or MacOS a freqtrade provides a script to Install, Update, Configure, and Reset your bot. + +```bash +$ ./setup.sh +usage: + -i,--install Install freqtrade from scratch + -u,--update Command git pull to update. + -r,--reset Hard reset your develop/master branch. + -c,--config Easy config generator (Will override your existing file). +``` + +** --install ** + +This script will install everything you need to run the bot: + +* Mandatory software as: `Python3`, `ta-lib`, `wget` +* Setup your virtualenv +* Configure your `config.json` file + +This script is a combination of `install script` `--reset`, `--config` + +** --update ** + +Update parameter will pull the last version of your current branch and update your virtualenv. + +** --reset ** + +Reset parameter will hard reset your branch (only if you are on `master` or `develop`) and recreate your virtualenv. + +** --config ** + +Config parameter is a `config.json` configurator. This script will ask you questions to setup your bot and create your `config.json`. + +------ + +## Automatic Installation - Docker + +Start by downloading Docker for your platform: + +* [Mac](https://www.docker.com/products/docker#/mac) +* [Windows](https://www.docker.com/products/docker#/windows) +* [Linux](https://www.docker.com/products/docker#/linux) + +Once you have Docker installed, simply create the config file (e.g. `config.json`) and then create a Docker image for `freqtrade` using the Dockerfile in this repo. + +### 1. Prepare the Bot + +**1.1. Clone the git repository** + +```bash +git clone https://github.com/freqtrade/freqtrade.git +``` + +**1.2. (Optional) Checkout the develop branch** + +```bash +git checkout develop +``` + +**1.3. Go into the new directory** + +```bash +cd freqtrade +``` + +**1.4. Copy `config.json.example` to `config.json`** + +```bash +cp -n config.json.example config.json +``` + +> To edit the config please refer to the [Bot Configuration](/configuration.md) page. + +**1.5. Create your database file *(optional - the bot will create it if it is missing)** + +Production + +```bash +touch tradesv3.sqlite +```` + +Dry-Run + +```bash +touch tradesv3.dryrun.sqlite +``` + +### 2. Download or build the docker image + +Either use the prebuilt image from docker hub - or build the image yourself if you would like more control on which version is used. + +Branches / tags available can be checked out on [Dockerhub](https://hub.docker.com/r/freqtradeorg/freqtrade/tags/). + +**2.1. Download the docker image** + +Pull the image from docker hub and (optionally) change the name of the image + +```bash +docker pull freqtradeorg/freqtrade:develop +# Optionally tag the repository so the run-commands remain shorter +docker tag freqtradeorg/freqtrade:develop freqtrade +``` + +To update the image, simply run the above commands again and restart your running container. + +**2.2. Build the Docker image** + +```bash +cd freqtrade +docker build -t freqtrade . +``` + +If you are developing using Docker, use `Dockerfile.develop` to build a dev Docker image, which will also set up develop dependencies: + +```bash +docker build -f ./Dockerfile.develop -t freqtrade-dev . +``` + +For security reasons, your configuration file will not be included in the image, you will need to bind mount it. It is also advised to bind mount an SQLite database file (see the "5. Run a restartable docker image" section) to keep it between updates. + +### 3. Verify the Docker image + +After the build process you can verify that the image was created with: + +```bash +docker images +``` + +### 4. Run the Docker image + +You can run a one-off container that is immediately deleted upon exiting with the following command (`config.json` must be in the current working directory): + +```bash +docker run --rm -v /etc/localtime:/etc/localtime:ro -v `pwd`/config.json:/freqtrade/config.json -it freqtrade +``` + +There is known issue in OSX Docker versions after 17.09.1, whereby /etc/localtime cannot be shared causing Docker to not start. A work-around for this is to start with the following cmd. + +```bash +docker run --rm -e TZ=`ls -la /etc/localtime | cut -d/ -f8-9` -v `pwd`/config.json:/freqtrade/config.json -it freqtrade +``` + +More information on this docker issue and work-around can be read [here](https://github.com/docker/for-mac/issues/2396). + +In this example, the database will be created inside the docker instance and will be lost when you will refresh your image. + +### 5. Run a restartable docker image + +To run a restartable instance in the background (feel free to place your configuration and database files wherever it feels comfortable on your filesystem). + +**5.1. Move your config file and database** + +```bash +mkdir ~/.freqtrade +mv config.json ~/.freqtrade +mv tradesv3.sqlite ~/.freqtrade +``` + +**5.2. Run the docker image** + +```bash +docker run -d \ + --name freqtrade \ + -v /etc/localtime:/etc/localtime:ro \ + -v ~/.freqtrade/config.json:/freqtrade/config.json \ + -v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \ + freqtrade --db-url sqlite:///tradesv3.sqlite +``` + +!!! Note + db-url defaults to `sqlite:///tradesv3.sqlite` but it defaults to `sqlite://` if `dry_run=True` is being used. + To override this behaviour use a custom db-url value: i.e.: `--db-url sqlite:///tradesv3.dryrun.sqlite` + +### 6. Monitor your Docker instance + +You can then use the following commands to monitor and manage your container: + +```bash +docker logs freqtrade +docker logs -f freqtrade +docker restart freqtrade +docker stop freqtrade +docker start freqtrade +``` + +For more information on how to operate Docker, please refer to the [official Docker documentation](https://docs.docker.com/). + +!!! Note + You do not need to rebuild the image for configuration changes, it will suffice to edit `config.json` and restart the container. + +### 7. Backtest with docker + +The following assumes that the above steps (1-4) have been completed successfully. +Also, backtest-data should be available at `~/.freqtrade/user_data/`. + +```bash +docker run -d \ + --name freqtrade \ + -v /etc/localtime:/etc/localtime:ro \ + -v ~/.freqtrade/config.json:/freqtrade/config.json \ + -v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \ + -v ~/.freqtrade/user_data/:/freqtrade/user_data/ \ + freqtrade --strategy AwsomelyProfitableStrategy backtesting +``` + +Head over to the [Backtesting Documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md) for more details. + +!!! Note + Additional parameters can be appended after the image name (`freqtrade` in the above example). + +------ + +## Custom Installation + +We've included/collected install instructions for Ubuntu 16.04, MacOS, and Windows. These are guidelines and your success may vary with other distros. +OS Specific steps are listed first, the [Common](#common) section below is necessary for all systems. + +### Requirements + +Click each one for install guide: + +* [Python >= 3.6.x](http://docs.python-guide.org/en/latest/starting/installation/) +* [pip](https://pip.pypa.io/en/stable/installing/) +* [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [virtualenv](https://virtualenv.pypa.io/en/stable/installation/) (Recommended) +* [TA-Lib](https://mrjbq7.github.io/ta-lib/install.html) + +### Linux - Ubuntu 16.04 + +#### Install Python 3.6, Git, and wget + +```bash +sudo add-apt-repository ppa:jonathonf/python-3.6 +sudo apt-get update +sudo apt-get install python3.6 python3.6-venv python3.6-dev build-essential autoconf libtool pkg-config make wget git +``` + +#### Raspberry Pi / Raspbian + +Before installing FreqTrade on a Raspberry Pi running the official Raspbian Image, make sure you have at least Python 3.6 installed. The default image only provides Python 3.5. Probably the easiest way to get a recent version of python is [miniconda](https://repo.continuum.io/miniconda/). + +The following assumes that miniconda3 is installed and available in your environment. Last miniconda3 installation file use python 3.4, we will update to python 3.6 on this installation. +It's recommended to use (mini)conda for this as installation/compilation of `numpy`, `scipy` and `pandas` takes a long time. +If you have installed it from (mini)conda, you can remove `numpy`, `scipy`, and `pandas` from `requirements.txt` before you install it with `pip`. + +Additional package to install on your Raspbian, `libffi-dev` required by cryptography (from python-telegram-bot). + +``` bash +conda config --add channels rpi +conda install python=3.6 +conda create -n freqtrade python=3.6 +conda activate freqtrade +conda install scipy pandas numpy + +sudo apt install libffi-dev +python3 -m pip install -r requirements.txt +python3 -m pip install -e . +``` + +### MacOS + +#### Install Python 3.6, git and wget + +```bash +brew install python3 git wget +``` + +### Common + +#### 1. Install TA-Lib + +Official webpage: https://mrjbq7.github.io/ta-lib/install.html + +```bash +wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz +tar xvzf ta-lib-0.4.0-src.tar.gz +cd ta-lib +sed -i.bak "s|0.00000001|0.000000000000000001 |g" src/ta_func/ta_utility.h +./configure --prefix=/usr/local +make +sudo make install +cd .. +rm -rf ./ta-lib* +``` + +!!! Note + An already downloaded version of ta-lib is included in the repository, as the sourceforge.net source seems to have problems frequently. + +#### 2. Setup your Python virtual environment (virtualenv) + +!!! Note + This step is optional but strongly recommended to keep your system organized + +```bash +python3 -m venv .env +source .env/bin/activate +``` + +#### 3. Install FreqTrade + +Clone the git repository: + +```bash +git clone https://github.com/freqtrade/freqtrade.git + +``` + +Optionally checkout the stable/master branch: + +```bash +git checkout master +``` + +#### 4. Initialize the configuration + +```bash +cd freqtrade +cp config.json.example config.json +``` + +> *To edit the config please refer to [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md).* + +#### 5. Install python dependencies + +``` bash +pip3 install --upgrade pip +pip3 install -r requirements.txt +pip3 install -e . +``` + +#### 6. Run the Bot + +If this is the first time you run the bot, ensure you are running it in Dry-run `"dry_run": true,` otherwise it will start to buy and sell coins. + +```bash +python3.6 ./freqtrade/main.py -c config.json +``` + +*Note*: If you run the bot on a server, you should consider using [Docker](#automatic-installation---docker) a terminal multiplexer like `screen` or [`tmux`](https://en.wikipedia.org/wiki/Tmux) to avoid that the bot is stopped on logout. + +#### 7. [Optional] Configure `freqtrade` as a `systemd` service + +From the freqtrade repo... copy `freqtrade.service` to your systemd user directory (usually `~/.config/systemd/user`) and update `WorkingDirectory` and `ExecStart` to match your setup. + +After that you can start the daemon with: + +```bash +systemctl --user start freqtrade +``` + +For this to be persistent (run when user is logged out) you'll need to enable `linger` for your freqtrade user. + +```bash +sudo loginctl enable-linger "$USER" +``` + +------ + +## Windows + +We recommend that Windows users use [Docker](#docker) as this will work much easier and smoother (also more secure). + +If that is not possible, try using the Windows Linux subsystem (WSL) - for which the Ubuntu instructions should work. +If that is not available on your system, feel free to try the instructions below, which led to success for some. + +### Install freqtrade manually + +#### Clone the git repository + +```bash +git clone https://github.com/freqtrade/freqtrade.git +``` + +copy paste `config.json` to ``\path\freqtrade-develop\freqtrade` + +#### Install ta-lib + +Install ta-lib according to the [ta-lib documentation](https://github.com/mrjbq7/ta-lib#windows). + +As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), there is also a repository of inofficial precompiled windows Wheels [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib), which needs to be downloaded and installed using `pip install TA_Lib‑0.4.17‑cp36‑cp36m‑win32.whl` (make sure to use the version matching your python version) + +```cmd +>cd \path\freqtrade-develop +>python -m venv .env +>cd .env\Scripts +>activate.bat +>cd \path\freqtrade-develop +REM optionally install ta-lib from wheel +REM >pip install TA_Lib‑0.4.17‑cp36‑cp36m‑win32.whl +>pip install -r requirements.txt +>pip install -e . +>python freqtrade\main.py +``` + +> Thanks [Owdr](https://github.com/Owdr) for the commands. Source: [Issue #222](https://github.com/freqtrade/freqtrade/issues/222) + +#### Error during installation under Windows + +``` bash +error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools +``` + +Unfortunately, many packages requiring compilation don't provide a pre-build wheel. It is therefore mandatory to have a C/C++ compiler installed and available for your python environment to use. + +The easiest way is to download install Microsoft Visual Studio Community [here](https://visualstudio.microsoft.com/downloads/) and make sure to install "Common Tools for Visual C++" to enable building c code on Windows. Unfortunately, this is a heavy download / dependency (~4Gb) so you might want to consider WSL or docker first. + +--- + +Now you have an environment ready, the next step is +[Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md)... diff --git a/mkdocs.yml b/mkdocs.yml index 7ad68178b..7d3ed3366 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,4 +1,14 @@ site_name: Freqtrade +nav: + - About: about.md + - Installation: pre-requisite.md + - Configuratioon: configuration.md + - Start the bot: bot-usage.md + - Bot optimization: bot-optimization.md + - Backtesting: backtesting.md + - Hyperopt: hyperopt.md + - Plotting: plotting.md + - Telegram usage: telegram-usage.md theme: name: material logo: 'images/logo.png' From 016522b1514b9c3f8d26276a5852ca3efa62df7b Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 16:56:13 +0100 Subject: [PATCH 028/358] configuration reformatetd --- docs/configuration.md | 213 ++++++++++++++++++++++-------------------- 1 file changed, 114 insertions(+), 99 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 7714eaba9..2f7486ec7 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -10,77 +10,83 @@ for your bot configuration. The table below will list all configuration parameters. | Command | Default | Mandatory | Description | -|----------|---------|----------|-------------| -| `max_open_trades` | 3 | Yes | Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades) -| `stake_currency` | BTC | Yes | Crypto-currency used for trading. -| `stake_amount` | 0.05 | Yes | Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to 'unlimited' to allow the bot to use all avaliable balance. -| `ticker_interval` | [1m, 5m, 30m, 1h, 1d] | No | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes -| `fiat_display_currency` | USD | Yes | Fiat currency used to show your profits. More information below. -| `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode. -| `process_only_new_candles` | false | No | If set to true indicators are processed only once a new candle arrives. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. Can be set either in Configuration or in the strategy. -| `minimal_roi` | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. If set, this parameter will override `minimal_roi` from your strategy file. -| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. If set, this parameter will override `stoploss` from your strategy file. -| `trailing_stop` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file). -| `trailing_stop_positve` | 0 | No | Changes stop-loss once profit has been reached. -| `trailing_stop_positve_offset` | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. -| `unfilledtimeout.buy` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled. -| `unfilledtimeout.sell` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled. -| `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. More information below. -| `bid_strategy.use_order_book` | false | No | Allows buying of pair using the rates in Order Book Bids. -| `bid_strategy.order_book_top` | 0 | No | Bot will use the top N rate in Order Book Bids. Ie. a value of 2 will allow the bot to pick the 2nd bid rate in Order Book Bids. -| `bid_strategy. check_depth_of_market.enabled` | false | No | Does not buy if the % difference of buy orders and sell orders is met in Order Book. -| `bid_strategy. check_depth_of_market.bids_to_ask_delta` | 0 | No | The % difference of buy orders and sell orders found in Order Book. A value lesser than 1 means sell orders is greater, while value greater than 1 means buy orders is higher. -| `ask_strategy.use_order_book` | false | No | Allows selling of open traded pair using the rates in Order Book Asks. -| `ask_strategy.order_book_min` | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. -| `ask_strategy.order_book_max` | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. -| `order_types` | None | No | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). -| `order_time_in_force` | None | No | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). -| `exchange.name` | bittrex | Yes | Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). -| `exchange.key` | key | No | API key to use for the exchange. Only required when you are in production mode. -| `exchange.secret` | secret | No | API secret to use for the exchange. Only required when you are in production mode. -| `exchange.pair_whitelist` | [] | No | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param. -| `exchange.pair_blacklist` | [] | No | List of currency the bot must avoid. Useful when using `--dynamic-whitelist` param. -| `exchange.ccxt_rate_limit` | True | No | DEPRECATED!! Have CCXT handle Exchange rate limits. Depending on the exchange, having this to false can lead to temporary bans from the exchange. -| `exchange.ccxt_config` | None | No | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) -| `exchange.ccxt_async_config` | None | No | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) -| `edge` | false | No | Please refer to [edge configuration document](edge.md) for detailed explanation. -| `experimental.use_sell_signal` | false | No | Use your sell strategy in addition of the `minimal_roi`. -| `experimental.sell_profit_only` | false | No | waits until you have made a positive profit before taking a sell decision. -| `experimental.ignore_roi_if_buy_signal` | false | No | Does not sell if the buy-signal is still active. Takes preference over `minimal_roi` and `use_sell_signal` -| `pairlist.method` | StaticPairList | No | Use Static whitelist. [More information below](#dynamic-pairlists). -| `pairlist.config` | None | No | Additional configuration for dynamic pairlists. [More information below](#dynamic-pairlists). -| `telegram.enabled` | true | Yes | Enable or not the usage of Telegram. -| `telegram.token` | token | No | Your Telegram bot token. Only required if `telegram.enabled` is `true`. -| `telegram.chat_id` | chat_id | No | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. -| `webhook.enabled` | false | No | Enable usage of Webhook notifications -| `webhook.url` | false | No | URL for the webhook. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details. -| `webhook.webhookbuy` | false | No | Payload to send on buy. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. -| `webhook.webhooksell` | false | No | Payload to send on sell. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. -| `webhook.webhookstatus` | false | No | Payload to send on status calls. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. -| `db_url` | `sqlite:///tradesv3.sqlite` | No | Declares database URL to use. NOTE: This defaults to `sqlite://` if `dry_run` is `True`. -| `initial_state` | running | No | Defines the initial application state. More information below. -| `forcebuy_enable` | false | No | Enables the RPC Commands to force a buy. More information below. -| `strategy` | DefaultStrategy | No | Defines Strategy class to use. -| `strategy_path` | null | No | Adds an additional strategy lookup path (must be a folder). -| `internals.process_throttle_secs` | 5 | Yes | Set the process throttle. Value in second. +|----------|---------|-----------|-------------| +| ==`max_open_trades`== | 3 | Yes | Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades) +| ==`stake_currency`== | BTC | Yes | Crypto-currency used for trading. +| ==`stake_amount`== | 0.05 | Yes | Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to 'unlimited' to allow the bot to use all avaliable balance. +| ==`ticker_interval`== | [1m, 5m, 30m, 1h, 1d] | No | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes +| ==`fiat_display_currency`== | USD | Yes | Fiat currency used to show your profits. More information below. +| ==`dry_run`== | true | Yes | Define if the bot must be in Dry-run or production mode. +| ==`process_only_new_candles`== | false | No | If set to true indicators are processed only once a new candle arrives. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. Can be set either in Configuration or in the strategy. +| ==`minimal_roi`== | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. If set, this parameter will override `minimal_roi` from your strategy file. +| ==`stoploss`== | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. If set, this parameter will override `stoploss` from your strategy file. +| ==`trailing_stop`== | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file). +| ==`trailing_stop_positve`== | 0 | No | Changes stop-loss once profit has been reached. +| ==`trailing_stop_positve_offset`== | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. +| ==`unfilledtimeout.buy`== | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled. +| ==`unfilledtimeout.sell`== | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled. +| ==`bid_strategy.ask_last_balance`== | 0.0 | Yes | Set the bidding price. More information below. +| ==`bid_strategy.use_order_book`== | false | No | Allows buying of pair using the rates in Order Book Bids. +| ==`bid_strategy.order_book_top`== | 0 | No | Bot will use the top N rate in Order Book Bids. Ie. a value of 2 will allow the bot to pick the 2nd bid rate in Order Book Bids. +| ==`bid_strategy. check_depth_of_market.enabled`== | false | No | Does not buy if the % difference of buy orders and sell orders is met in Order Book. +| ==`bid_strategy. check_depth_of_market.bids_to_ask_delta`== | 0 | No | The % difference of buy orders and sell orders found in Order Book. A value lesser than 1 means sell orders is greater, while value greater than 1 means buy orders is higher. +| ==`ask_strategy.use_order_book`== | false | No | Allows selling of open traded pair using the rates in Order Book Asks. +| ==`ask_strategy.order_book_min`== | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. +| ==`ask_strategy.order_book_max`== | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. +| ==`order_types`== | None | No | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). +| ==`order_time_in_force`== | None | No | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). +| ==`exchange.name`== | bittrex | Yes | Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). +| ==`exchange.key`== | key | No | API key to use for the exchange. Only required when you are in production mode. +| ==`exchange.secret`== | secret | No | API secret to use for the exchange. Only required when you are in production mode. +| ==`exchange.pair_whitelist`== | [] | No | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param. +| ==`exchange.pair_blacklist`== | [] | No | List of currency the bot must avoid. Useful when using `--dynamic-whitelist` param. +| ==`exchange.ccxt_rate_limit`== | True | No | DEPRECATED!! Have CCXT handle Exchange rate limits. Depending on the exchange, having this to false can lead to temporary bans from the exchange. +| ==`exchange.ccxt_config`== | None | No | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) +| ==`exchange.ccxt_async_config`== | None | No | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) +| ==`edge`== | false | No | Please refer to [edge configuration document](edge.md) for detailed explanation. +| ==`experimental.use_sell_signal`== | false | No | Use your sell strategy in addition of the `minimal_roi`. +| ==`experimental.sell_profit_only`== | false | No | waits until you have made a positive profit before taking a sell decision. +| ==`experimental.ignore_roi_if_buy_signal`== | false | No | Does not sell if the buy-signal is still active. Takes preference over `minimal_roi` and `use_sell_signal` +| ==`pairlist.method`== | StaticPairList | No | Use Static whitelist. [More information below](#dynamic-pairlists). +| ==`pairlist.config`== | None | No | Additional configuration for dynamic pairlists. [More information below](#dynamic-pairlists). +| ==`telegram.enabled`== | true | Yes | Enable or not the usage of Telegram. +| ==`telegram.token`== | token | No | Your Telegram bot token. Only required if `telegram.enabled` is `true`. +| ==`telegram.chat_id`== | chat_id | No | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. +| ==`webhook.enabled`== | false | No | Enable usage of Webhook notifications +| ==`webhook.url`== | false | No | URL for the webhook. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details. +| ==`webhook.webhookbuy`== | false | No | Payload to send on buy. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. +| ==`webhook.webhooksell`== | false | No | Payload to send on sell. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. +| ==`webhook.webhookstatus`== | false | No | Payload to send on status calls. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. +| ==`db_url`== | `sqlite:///tradesv3.sqlite`| No | Declares database URL to use. NOTE: This defaults to `sqlite://` if `dry_run` is `True`. +| ==`initial_state`== | running | No | Defines the initial application state. More information below. +| ==`forcebuy_enable`== | false | No | Enables the RPC Commands to force a buy. More information below. +| ==`strategy`== | DefaultStrategy | No | Defines Strategy class to use. +| ==`strategy_path`== | null | No | Adds an additional strategy lookup path (must be a folder). +| ==`internals.process_throttle_secs`== | 5 | Yes | Set the process throttle. Value in second. The definition of each config parameters is in [misc.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/misc.py#L205). ### Understand stake_amount -`stake_amount` is an amount of crypto-currency your bot will use for each trade. +==`stake_amount`== is an amount of crypto-currency your bot will use for each trade. The minimal value is 0.0005. If there is not enough crypto-currency in the account an exception is generated. -To allow the bot to trade all the avaliable `stake_currency` in your account set `stake_amount` = `unlimited`. -In this case a trade amount is calclulated as `currency_balanse / (max_open_trades - current_open_trades)`. +To allow the bot to trade all the avaliable ==`stake_currency`== in your account set
+```json +"stake_amount" : "unlimited", +``` +In this case a trade amount is calclulated as:
+```python +currency_balanse / (max_open_trades - current_open_trades) +``` ### Understand minimal_roi -`minimal_roi` is a JSON object where the key is a duration +==`minimal_roi`== is a JSON object where the key is a duration in minutes and the value is the minimum ROI in percent. See the example below: -``` +```json "minimal_roi": { "40": 0.0, # Sell after 40 minutes if the profit is not negative "30": 0.01, # Sell after 30 minutes if there is at least 1% profit @@ -89,19 +95,19 @@ See the example below: }, ``` -Most of the strategy files already include the optimal `minimal_roi` +Most of the strategy files already include the optimal ==`minimal_roi`== value. This parameter is optional. If you use it, it will take over the -`minimal_roi` value from the strategy file. +==`minimal_roi`== value from the strategy file. ### Understand stoploss -`stoploss` is loss in percentage that should trigger a sale. +==`stoploss`== is loss in percentage that should trigger a sale. For example value `-0.10` will cause immediate sell if the profit dips below -10% for a given trade. This parameter is optional. -Most of the strategy files already include the optimal `stoploss` +Most of the strategy files already include the optimal ==`stoploss`== value. This parameter is optional. If you use it, it will take over the -`stoploss` value from the strategy file. +==`stoploss`== value from the strategy file. ### Understand trailing stoploss @@ -109,13 +115,13 @@ Go to the [trailing stoploss Documentation](stoploss.md) for details on trailing ### Understand initial_state -`initial_state` is an optional field that defines the initial application state. +==`initial_state`== is an optional field that defines the initial application state. Possible values are `running` or `stopped`. (default=`running`) If the value is `stopped` the bot has to be started with `/start` first. ### Understand forcebuy_enable -`forcebuy_enable` enables the usage of forcebuy commands via Telegram. +==`forcebuy_enable`== enables the usage of forcebuy commands via Telegram. This is disabled for security reasons by default, and will show a warning message on startup if enabled. You send `/forcebuy ETH/BTC` to the bot, who buys the pair and holds it until a regular sell-signal appears (ROI, stoploss, /forcesell). @@ -124,40 +130,41 @@ See [the telegram documentation](telegram-usage.md) for details on usage. ### Understand process_throttle_secs -`process_throttle_secs` is an optional field that defines in seconds how long the bot should wait +==`process_throttle_secs`== is an optional field that defines in seconds how long the bot should wait before asking the strategy if we should buy or a sell an asset. After each wait period, the strategy is asked again for every opened trade wether or not we should sell, and for all the remaining pairs (either the dynamic list of pairs or the static list of pairs) if we should buy. ### Understand ask_last_balance -`ask_last_balance` sets the bidding price. Value `0.0` will use `ask` price, `1.0` will +==`ask_last_balance`== sets the bidding price. Value `0.0` will use `ask` price, `1.0` will use the `last` price and values between those interpolate between ask and last price. Using `ask` price will guarantee quick success in bid, but bot will also end up paying more then would probably have been necessary. ### Understand order_types -`order_types` contains a dict mapping order-types to market-types as well as stoploss on or off exchange type. This allows to buy using limit orders, sell using limit-orders, and create stoploss orders using market. It also allows to set the stoploss "on exchange" which means stoploss order would be placed immediately once the buy order is fulfilled. +==`order_types`== contains a dict mapping order-types to market-types as well as stoploss on or off exchange type. This allows to buy using limit orders, sell using limit-orders, and create stoploss orders using market. It also allows to set the stoploss "on exchange" which means stoploss order would be placed immediately once the buy order is fulfilled. This can be set in the configuration or in the strategy. Configuration overwrites strategy configurations. If this is configured, all 4 values (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`) need to be present, otherwise the bot warn about it and will fail to start. The below is the default which is used if this is not configured in either Strategy or configuration. -``` python - "order_types": { - "buy": "limit", - "sell": "limit", - "stoploss": "market", - "stoploss_on_exchange": False - }, +```python +"order_types": { + "buy": "limit", + "sell": "limit", + "stoploss": "market", + "stoploss_on_exchange": False +}, ``` -**NOTE**: Not all exchanges support "market" orders. -The following message will be shown if your exchange does not support market orders: `"Exchange does not support market orders."` +!!! Note + Not all exchanges support "market" orders. + The following message will be shown if your exchange does not support market orders: `"Exchange does not support market orders."` ### Understand order_time_in_force -Order time in force defines the policy by which the order is executed on the exchange. Three commonly used time in force are:
+==`order_time_in_force`== defines the policy by which the order is executed on the exchange. Three commonly used time in force are:
**GTC (Goog Till Canceled):** This is most of the time the default time in force. It means the order will remain on exchange till it is canceled by user. It can be fully or partially fulfilled. If partially fulfilled, the remaining will stay on the exchange till cancelled.
**FOK (Full Or Kill):** @@ -165,15 +172,17 @@ It means if the order is not executed immediately AND fully then it is canceled **IOC (Immediate Or Canceled):** It is the same as FOK (above) except it can be partially fulfilled. The remaining part is automatically cancelled by the exchange.
-`order_time_in_force` contains a dict buy and sell time in force policy. This can be set in the configuration or in the strategy. Configuration overwrites strategy configurations.
+==`order_time_in_force`== contains a dict buy and sell time in force policy. This can be set in the configuration or in the strategy. Configuration overwrites strategy configurations.
possible values are: `gtc` (default), `fok` or `ioc`.
``` python - "order_time_in_force": { - "buy": "gtc", - "sell": "gtc" - }, +"order_time_in_force": { + "buy": "gtc", + "sell": "gtc" +}, ``` -**NOTE**: This is an ongoing work. For now it is supported only for binance and only for buy orders. Please don't change the default value unless you know what you are doing.
+ +!!! Warning + This is an ongoing work. For now it is supported only for binance and only for buy orders. Please don't change the default value unless you know what you are doing. ### What values for exchange.name? @@ -191,10 +200,16 @@ Feel free to test other exchanges and submit your PR to improve the bot. ### What values for fiat_display_currency? -`fiat_display_currency` set the base currency to use for the conversion from coin to fiat in Telegram. -The valid values are: "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK", "EUR", "GBP", "HKD", "HUF", "IDR", "ILS", "INR", "JPY", "KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN", "RUB", "SEK", "SGD", "THB", "TRY", "TWD", "ZAR", "USD". -In addition to central bank currencies, a range of cryto currencies are supported. -The valid values are: "BTC", "ETH", "XRP", "LTC", "BCH", "USDT". +==`fiat_display_currency`== set the base currency to use for the conversion from coin to fiat in Telegram. +The valid values are:
+```json +"AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK", "EUR", "GBP", "HKD", "HUF", "IDR", "ILS", "INR", "JPY", "KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN", "RUB", "SEK", "SGD", "THB", "TRY", "TWD", "ZAR", "USD" +``` +In addition to FIAT currencies, a range of cryto currencies are supported. +The valid values are: +```json +"BTC", "ETH", "XRP", "LTC", "BCH", "USDT" +``` ## Switch to dry-run mode @@ -228,17 +243,17 @@ production mode. ### Dynamic Pairlists Dynamic pairlists select pairs for you based on the logic configured. -The bot runs against all pairs (with that stake) on the exchange, and a number of assets (`number_assets`) is selected based on the selected criteria. +The bot runs against all pairs (with that stake) on the exchange, and a number of assets (==`number_assets`==) is selected based on the selected criteria. -By *default*, a Static Pairlist is used (configured as `"pair_whitelist"` under the `"exchange"` section of this configuration). +By default, a Static Pairlist is used (configured as ==`"pair_whitelist"`== under the ==`"exchange"`== section of this configuration). -** Available Pairlist methods ** +**Available Pairlist methods:** * `"StaticPairList"` - * uses configuration from `exchange.pair_whitelist` and `exchange.pair_blacklist` + * uses configuration from ==`exchange.pair_whitelist`== and ==`exchange.pair_blacklist`== * `"VolumePairList"` * Formerly available as `--dynamic-whitelist []` - * Selects `number_assets` top pairs based on `sort_key`, which can be one of `askVolume`, `bidVolume` and `quoteVolume`, defaults to `quoteVolume`. + * Selects ==`number_assets`== top pairs based on ==`sort_key`==, which can be one of `askVolume`, `bidVolume` and `quoteVolume`, defaults to `quoteVolume`. ```json "pairlist": { @@ -258,15 +273,15 @@ you run it in production mode. ### To switch your bot in production mode -1. Edit your `config.json` file +**Edit your `config.json` file.** -2. Switch dry-run to false and don't forget to adapt your database URL if set +**Switch dry-run to false and don't forget to adapt your database URL if set:** ```json "dry_run": false, ``` -3. Insert your Exchange API key (change them by fake api keys) +**Insert your Exchange API key (change them by fake api keys):** ```json "exchange": { @@ -277,12 +292,12 @@ you run it in production mode. } ``` - -If you have not your Bittrex API key yet, [see our tutorial](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md). +!!! Note + If you have an exchange API key yet, [see our tutorial](/pre-requisite). ### Using proxy with FreqTrade -To use a proxy with freqtrade, add the kwarg `"aiohttp_trust_env"=true` to the `"ccxt_async_kwargs"` dict in the exchange section of the configuration. +To use a proxy with freqtrade, add the kwarg ==`"aiohttp_trust_env"=true`== to the ==`"ccxt_async_kwargs"`== dict in the exchange section of the configuration. An example for this can be found in `config_full.json.example` From 83b9732106a159383ea4b837b94432d8ecbac69a Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 16:56:27 +0100 Subject: [PATCH 029/358] permalink installed --- mkdocs.yml | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 7d3ed3366..ae66d098b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -16,8 +16,24 @@ theme: primary: 'blue grey' accent: 'tear' markdown_extensions: - - admonition - - codehilite: - guess_lang: false - - toc: - permalink: false \ No newline at end of file + - admonition + - codehilite: + guess_lang: false + - toc: + permalink: true + - pymdownx.arithmatex + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret + - pymdownx.critic + - pymdownx.details + - pymdownx.emoji: + emoji_generator: !!python/name:pymdownx.emoji.to_svg + - pymdownx.inlinehilite + - pymdownx.magiclink + - pymdownx.mark + - pymdownx.smartsymbols + - pymdownx.superfences + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde \ No newline at end of file From da380e6a0deb35a2cd8d135137b268955c98d83c Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 17:08:21 +0100 Subject: [PATCH 030/358] start the bot documentation reformatted --- docs/bot-usage.md | 62 +++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 5451c8459..a26a2eb6f 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -1,32 +1,28 @@ -# Bot usage +# Start the bot -This page explains the difference parameters of the bot and how to run it. +This page explains the different parameters of the bot and how to run it. -## Table of Contents - -- [Bot commands](#bot-commands) -- [Backtesting commands](#backtesting-commands) -- [Hyperopt commands](#hyperopt-commands) ## Bot commands ``` -usage: freqtrade [-h] [-v] [--version] [-c PATH] [-d PATH] [-s NAME] - [--strategy-path PATH] [--dynamic-whitelist [INT]] - [--db-url PATH] - {backtesting,hyperopt} ... +usage: main.py [-h] [-v] [--version] [-c PATH] [-d PATH] [-s NAME] + [--strategy-path PATH] [--customhyperopt NAME] + [--dynamic-whitelist [INT]] [--db-url PATH] + {backtesting,edge,hyperopt} ... Simple High Frequency Trading Bot for crypto currencies positional arguments: - {backtesting,hyperopt} + {backtesting,edge,hyperopt} backtesting backtesting module + edge edge module hyperopt hyperopt module optional arguments: -h, --help show this help message and exit - -v, --verbose be verbose - --version show program's version number and exit + -v, --verbose verbose mode (-vv for more, -vvv to get all messages) + --version show program\'s version number and exit -c PATH, --config PATH specify configuration file (default: config.json) -d PATH, --datadir PATH @@ -34,12 +30,15 @@ optional arguments: -s NAME, --strategy NAME specify strategy class name (default: DefaultStrategy) --strategy-path PATH specify additional strategy lookup path + --customhyperopt NAME + specify hyperopt class name (default: + DefaultHyperOpts) --dynamic-whitelist [INT] dynamically generate and update whitelist based on 24h - BaseVolume (default: 20) DEPRECATED + BaseVolume (default: 20) DEPRECATED. --db-url PATH Override trades database URL, this is useful if dry_run is enabled or in custom deployments (default: - sqlite:///tradesv3.sqlite) + None) ``` ### How to use a different config file? @@ -51,7 +50,7 @@ default, the bot will load the file `./config.json` python3 ./freqtrade/main.py -c path/far/far/away/config.json ``` -### How to use --strategy? +### How to use **--strategy**? This parameter will allow you to load your custom strategy class. Per default without `--strategy` or `-s` the bot will load the @@ -74,7 +73,7 @@ message the reason (File not found, or errors in your code). Learn more about strategy file in [optimize your bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md). -### How to use --strategy-path? +### How to use **--strategy-path**? This parameter allows you to add an additional strategy lookup path, which gets checked before the default locations (The passed path must be a folder!): @@ -87,9 +86,10 @@ python3 ./freqtrade/main.py --strategy AwesomeStrategy --strategy-path /some/fol This is very simple. Copy paste your strategy file into the folder `user_data/strategies` or use `--strategy-path`. And voila, the bot is ready to use it. -### How to use --dynamic-whitelist? +### How to use **--dynamic-whitelist**? -> Dynamic-whitelist is deprecated. Please move your configurations to the configuration as outlined [here](docs/configuration.md#Dynamic-Pairlists) +!!! danger "DEPRECATED" + Dynamic-whitelist is deprecated. Please move your configurations to the configuration as outlined [here](docs/configuration.md#Dynamic-Pairlists) Per default `--dynamic-whitelist` will retrieve the 20 currencies based on BaseVolume. This value can be changed when you run the script. @@ -113,7 +113,7 @@ python3 ./freqtrade/main.py --dynamic-whitelist 30 negative value (e.g -2), `--dynamic-whitelist` will use the default value (20). -### How to use --db-url? +### How to use **--db-url**? When you run the bot in Dry-run mode, per default no transactions are stored in a database. If you want to store your bot actions in a DB @@ -129,15 +129,17 @@ python3 ./freqtrade/main.py -c config.json --db-url sqlite:///tradesv3.dry_run.s Backtesting also uses the config specified via `-c/--config`. ``` -usage: freqtrade backtesting [-h] [-i TICKER_INTERVAL] [--eps] [--dmmp] - [--timerange TIMERANGE] [-l] [-r] - [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] - [--export EXPORT] [--export-filename PATH] +usage: main.py backtesting [-h] [-i TICKER_INTERVAL] [--timerange TIMERANGE] + [--eps] [--dmmp] [-l] [-r] + [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] + [--export EXPORT] [--export-filename PATH] optional arguments: -h, --help show this help message and exit -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL specify ticker interval (1m, 5m, 30m, 1h, 1d) + --timerange TIMERANGE + specify what timerange of data to use. --eps, --enable-position-stacking Allow buying the same pair multiple times (position stacking) @@ -145,8 +147,6 @@ optional arguments: Disable applying `max_open_trades` during backtest (same as setting `max_open_trades` to a very high number) - --timerange TIMERANGE - specify what timerange of data to use. -l, --live using live data -r, --refresh-pairs-cached refresh the pairs files in tests/testdata with the @@ -167,18 +167,18 @@ optional arguments: filename=user_data/backtest_data/backtest_today.json (default: user_data/backtest_data/backtest- result.json) - ``` -### How to use --refresh-pairs-cached parameter? +### How to use **--refresh-pairs-cached** parameter? The first time your run Backtesting, it will take the pairs you have set in your config file and download data from Bittrex. If for any reason you want to update your data set, you use `--refresh-pairs-cached` to force Backtesting to update the data it has. -**Use it only if you want to update your data set. You will not be able -to come back to the previous version.** + +!!! Note + Use it only if you want to update your data set. You will not be able to come back to the previous version. To test your strategy with latest data, we recommend continuing using the parameter `-l` or `--live`. From 219e9d9e2b747a3df8e8d94ea3aab83023ffcd65 Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 17:14:03 +0100 Subject: [PATCH 031/358] optimization page reformatted --- docs/bot-optimization.md | 34 +++++++--------------------------- mkdocs.yml | 2 +- 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/docs/bot-optimization.md b/docs/bot-optimization.md index 0fc41a2e3..d4402d157 100644 --- a/docs/bot-optimization.md +++ b/docs/bot-optimization.md @@ -1,28 +1,8 @@ -# Bot Optimization +# Optimization This page explains where to customize your strategies, and add new indicators. -## Table of Contents - -- [Install a custom strategy file](#install-a-custom-strategy-file) -- [Customize your strategy](#change-your-strategy) - - [Anatomy of a strategy](#anatomy-of-a-strategy) - - [Customize indicators](#customize-indicators) - - [Buy signal rules](#buy-signal-rules) - - [Sell signal rules](#sell-signal-rules) - - [Minimal ROI](#minimal-roi) - - [Stoploss](#stoploss) - - [Ticker interval](#ticker-interval) - - [Metadata dict](#metadata-dict) - - [Where is the default strategy](#where-is-the-default-strategy) - - [Specify custom strategy location](#specify-custom-strategy-location) - - [Further strategy ideas](#further-strategy-ideas) - -- [Where is the default strategy](#where-is-the-default-strategy) - -Since the version `0.16.0` the bot allows using custom strategy file. - ## Install a custom strategy file This is very simple. Copy paste your strategy file into the folder @@ -60,7 +40,7 @@ A strategy file contains all the information needed to build a good strategy: The bot also include a sample strategy called `TestStrategy` you can update: `user_data/strategies/test_strategy.py`. You can test it with the parameter: `--strategy TestStrategy` -``` bash +```bash python3 ./freqtrade/main.py --strategy AwesomeStrategy ``` @@ -118,10 +98,10 @@ def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame return dataframe ``` -#### Want more indicator examples -Look into the [user_data/strategies/test_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/test_strategy.py). -Then uncomment indicators you need. +!!! Note "Want more indicator examples?" + Look into the [user_data/strategies/test_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/test_strategy.py).
+ Then uncomment indicators you need. ### Buy signal rules @@ -187,7 +167,7 @@ This dict defines the minimal Return On Investment (ROI) a trade should reach be It is of the following format, with the dict key (left side of the colon) being the minutes passed since the trade opened, and the value (right side of the colon) being the percentage. -```python +```python minimal_roi = { "40": 0.0, "30": 0.01, @@ -227,7 +207,7 @@ stoploss = -0.10 ``` This would signify a stoploss of -10%. -If your exchange supports it, it's recommended to also set `"stoploss_on_exchange"` in the order dict, so your stoploss is on the exchange and cannot be missed for network-problems (or other problems). +If your exchange supports it, it's recommended to also set `"stoploss_on_exchange"` in the order dict, so your stoploss is on the exchange and cannot be missed for network-problems (or other problems). For more information on order_types please look [here](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md#understand-order_types). diff --git a/mkdocs.yml b/mkdocs.yml index ae66d098b..521528e4b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -4,7 +4,7 @@ nav: - Installation: pre-requisite.md - Configuratioon: configuration.md - Start the bot: bot-usage.md - - Bot optimization: bot-optimization.md + - Optimization: bot-optimization.md - Backtesting: backtesting.md - Hyperopt: hyperopt.md - Plotting: plotting.md From b029a98980f441f3a6434c13d60266db2edee31c Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 17:14:56 +0100 Subject: [PATCH 032/358] backtesting reformatted --- docs/backtesting.md | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index cc8ecd6c7..52b91c580 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -1,24 +1,19 @@ # Backtesting -This page explains how to validate your strategy performance by using +This page explains how to validate your strategy performance by using Backtesting. -## Table of Contents - -- [Test your strategy with Backtesting](#test-your-strategy-with-backtesting) -- [Understand the backtesting result](#understand-the-backtesting-result) - ## Test your strategy with Backtesting Now you have good Buy and Sell strategies, you want to test it against -real data. This is what we call +real data. This is what we call [backtesting](https://en.wikipedia.org/wiki/Backtesting). Backtesting will use the crypto-currencies (pair) from your config file -and load static tickers located in -[/freqtrade/tests/testdata](https://github.com/freqtrade/freqtrade/tree/develop/freqtrade/tests/testdata). -If the 5 min and 1 min ticker for the crypto-currencies to test is not -already in the `testdata` folder, backtesting will download them +and load static tickers located in +[/freqtrade/tests/testdata](https://github.com/freqtrade/freqtrade/tree/develop/freqtrade/tests/testdata). +If the 5 min and 1 min ticker for the crypto-currencies to test is not +already in the `testdata` folder, backtesting will download them automatically. Testdata files will not be updated until you specify it. The result of backtesting will confirm you if your bot has better odds of making a profit than a loss. @@ -216,15 +211,15 @@ TOTAL 419 -0.41 -0.00348593 52.9 ``` 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 +`52.9` min, with a performance of `-0.41%` (loss), that means it has lost a total of `-0.00348593 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 -`stop_loss` you have set. + +As you will see your strategy performance will be influenced by your buy +strategy, your sell strategy, and also by the `minimal_roi` and +`stop_loss` you have set. As for an example if your minimal_roi is only `"0": 0.01`. You cannot -expect the bot to make more profit than 1% (because it will sell every +expect the bot to make more profit than 1% (because it will sell every time a trade will reach 1%). ```json @@ -234,21 +229,21 @@ time a trade will reach 1%). ``` On the other hand, if you set a too high `minimal_roi` like `"0": 0.55` -(55%), there is a lot of chance that the bot will never reach this -profit. Hence, keep in mind that your performance is a mix of your +(55%), there is a lot of chance that the bot will never reach this +profit. Hence, keep in mind that your performance is a mix of your strategies, your configuration, and the crypto-currency you have set up. ## Backtesting multiple strategies To backtest multiple strategies, a list of Strategies can be provided. -This is limited to 1 ticker-interval per run, however, data is only loaded once from disk so if you have multiple +This is limited to 1 ticker-interval per run, however, data is only loaded once from disk so if you have multiple strategies you'd like to compare, this should give a nice runtime boost. All listed Strategies need to be in the same folder. ``` bash -freqtrade backtesting --timerange 20180401-20180410 --ticker-interval 5m --strategy-list Strategy001 Strategy002 --export trades +freqtrade backtesting --timerange 20180401-20180410 --ticker-interval 5m --strategy-list Strategy001 Strategy002 --export trades ``` This will save the results to `user_data/backtest_data/backtest-result-.json`, injecting the strategy-name into the target filename. @@ -266,5 +261,5 @@ Detailed output for all strategies one after the other will be available, so mak ## Next step Great, your strategy is profitable. What if the bot can give your the -optimal parameters to use for your strategy? +optimal parameters to use for your strategy? Your next step is to learn [how to find optimal parameters with Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md) From 863cf303e30758d27fa54cfcdc4290508f43ba5c Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 17:17:52 +0100 Subject: [PATCH 033/358] hyperopt reformatted --- docs/hyperopt.md | 78 ++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 43 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index dffe84d1d..76858aa53 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -1,64 +1,56 @@ # Hyperopt -This page explains how to tune your strategy by finding the optimal -parameters, a process called hyperparameter optimization. The bot uses several +This page explains how to tune your strategy by finding the optimal +parameters, a process called hyperparameter optimization. The bot uses several algorithms included in the `scikit-optimize` package to accomplish this. The search will burn all your CPU cores, make your laptop sound like a fighter jet and still take a long time. -*Note:* Hyperopt will crash when used with only 1 CPU Core as found out in [Issue #1133](https://github.com/freqtrade/freqtrade/issues/1133) - -## Table of Contents - -- [Prepare your Hyperopt](#prepare-hyperopt) -- [Configure your Guards and Triggers](#configure-your-guards-and-triggers) -- [Solving a Mystery](#solving-a-mystery) -- [Adding New Indicators](#adding-new-indicators) -- [Execute Hyperopt](#execute-hyperopt) -- [Understand the hyperopt result](#understand-the-hyperopt-result) +!!! Bug + Hyperopt will crash when used with only 1 CPU Core as found out in [Issue #1133](https://github.com/freqtrade/freqtrade/issues/1133) ## Prepare Hyperopting -Before we start digging in Hyperopt, we recommend you to take a look at +Before we start digging in Hyperopt, we recommend you to take a look at an example hyperopt file located into [user_data/hyperopts/](https://github.com/gcarq/freqtrade/blob/develop/user_data/hyperopts/test_hyperopt.py) ### 1. Install a Custom Hyperopt File -This is very simple. Put your hyperopt file into the folder +This is very simple. Put your hyperopt file into the folder `user_data/hyperopts`. -Let assume you want a hyperopt file `awesome_hyperopt.py`: -1. Copy the file `user_data/hyperopts/sample_hyperopt.py` into `user_data/hyperopts/awesome_hyperopt.py` +Let assume you want a hyperopt file `awesome_hyperopt.py`:
+Copy the file `user_data/hyperopts/sample_hyperopt.py` into `user_data/hyperopts/awesome_hyperopt.py` + - ### 2. Configure your Guards and Triggers -There are two places you need to change in your hyperopt file to add a +There are two places you need to change in your hyperopt file to add a new buy hyperopt for testing: - Inside [populate_buy_trend()](https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/test_hyperopt.py#L230-L251). - Inside [indicator_space()](https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/test_hyperopt.py#L207-L223). There you have two different types of indicators: 1. `guards` and 2. `triggers`. -1. Guards are conditions like "never buy if ADX < 10", or never buy if +1. Guards are conditions like "never buy if ADX < 10", or never buy if current price is over EMA10. -2. Triggers are ones that actually trigger buy in specific moment, like -"buy when EMA5 crosses over EMA10" or "buy when close price touches lower +2. Triggers are ones that actually trigger buy in specific moment, like +"buy when EMA5 crosses over EMA10" or "buy when close price touches lower bollinger band". -Hyperoptimization will, for each eval round, pick one trigger and possibly -multiple guards. The constructed strategy will be something like -"*buy exactly when close price touches lower bollinger band, BUT only if +Hyperoptimization will, for each eval round, pick one trigger and possibly +multiple guards. The constructed strategy will be something like +"*buy exactly when close price touches lower bollinger band, BUT only if ADX > 10*". If you have updated the buy strategy, ie. changed the contents of -`populate_buy_trend()` method you have to update the `guards` and +`populate_buy_trend()` method you have to update the `guards` and `triggers` hyperopts must use. ## Solving a Mystery -Let's say you are curious: should you use MACD crossings or lower Bollinger -Bands to trigger your buys. And you also wonder should you use RSI or ADX to -help with those buy decisions. If you decide to use RSI or ADX, which values -should I use for them? So let's use hyperparameter optimization to solve this +Let's say you are curious: should you use MACD crossings or lower Bollinger +Bands to trigger your buys. And you also wonder should you use RSI or ADX to +help with those buy decisions. If you decide to use RSI or ADX, which values +should I use for them? So let's use hyperparameter optimization to solve this mystery. We will start by defining a search space: @@ -77,8 +69,8 @@ We will start by defining a search space: ] ``` -Above definition says: I have five parameters I want you to randomly combine -to find the best combination. Two of them are integer values (`adx-value` +Above definition says: I have five parameters I want you to randomly combine +to find the best combination. Two of them are integer values (`adx-value` and `rsi-value`) and I want you test in the range of values 20 to 40. Then we have three category variables. First two are either `True` or `False`. We use these to either enable or disable the ADX and RSI guards. The last @@ -117,12 +109,12 @@ with different value combinations. It will then use the given historical data an buys based on the buy signals generated with the above function and based on the results it will end with telling you which paramter combination produced the best profits. -The search for best parameters starts with a few random combinations and then uses a +The search for best parameters starts with a few random combinations and then uses a regressor algorithm (currently ExtraTreesRegressor) to quickly find a parameter combination that minimizes the value of the objective function `calculate_loss` in `hyperopt.py`. The above setup expects to find ADX, RSI and Bollinger Bands in the populated indicators. -When you want to test an indicator that isn't used by the bot currently, remember to +When you want to test an indicator that isn't used by the bot currently, remember to add it to the `populate_indicators()` method in `hyperopt.py`. ## Execute Hyperopt @@ -136,7 +128,7 @@ We strongly recommend to use `screen` or `tmux` to prevent any connection loss. python3 ./freqtrade/main.py -s --hyperopt -c config.json hyperopt -e 5000 ``` -Use `` and `` as the names of the custom strategy +Use `` and `` as the names of the custom strategy (only required for generating sells) and the custom hyperopt used. The `-e` flag will set how many evaluations hyperopt will do. We recommend @@ -161,9 +153,9 @@ python3 ./freqtrade/main.py hyperopt --timerange -200 ### Running Hyperopt with Smaller Search Space Use the `--spaces` argument to limit the search space used by hyperopt. -Letting Hyperopt optimize everything is a huuuuge search space. Often it -might make more sense to start by just searching for initial buy algorithm. -Or maybe you just want to optimize your stoploss or roi table for that awesome +Letting Hyperopt optimize everything is a huuuuge search space. Often it +might make more sense to start by just searching for initial buy algorithm. +Or maybe you just want to optimize your stoploss or roi table for that awesome new buy strategy you have. Legal values are: @@ -189,19 +181,19 @@ with values: You should understand this result like: - The buy trigger that worked best was `bb_lower`. -- You should not use ADX because `adx-enabled: False`) +- You should not use ADX because `adx-enabled: False`) - You should **consider** using the RSI indicator (`rsi-enabled: True` and the best value is `29.0` (`rsi-value: 29.0`) -You have to look inside your strategy file into `buy_strategy_generator()` +You have to look inside your strategy file into `buy_strategy_generator()` method, what those values match to. - + So for example you had `rsi-value: 29.0` so we would look at `rsi`-block, that translates to the following code block: ``` (dataframe['rsi'] < 29.0) ``` - -Translating your whole hyperopt result as the new buy-signal + +Translating your whole hyperopt result as the new buy-signal would then look like: ```python @@ -245,7 +237,7 @@ Once the optimized strategy has been implemented into your strategy, you should To archive the same results (number of trades, ...) than during hyperopt, please use the command line flag `--disable-max-market-positions`. This setting is the default for hyperopt for speed reasons. You can overwrite this in the configuration by setting `"position_stacking"=false` or by changing the relevant line in your hyperopt file [here](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/optimize/hyperopt.py#L283). -Dry/live runs will **NOT** use position stacking - therefore it does make sense to also validate the strategy without this as it's closer to reality. +Dry/live runs will **NOT** use position stacking - therefore it does make sense to also validate the strategy without this as it's closer to reality. ## Next Step From 87cbf6aaaa33d241b24081176cb9db0544b25e5c Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 17:20:16 +0100 Subject: [PATCH 034/358] Plotting reformatted --- docs/plotting.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/plotting.md b/docs/plotting.md index 54a4bb4b8..3f2d38d0b 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -1,10 +1,6 @@ # Plotting This page explains how to plot prices, indicator, profits. -## Table of Contents -- [Plot price and indicators](#plot-price-and-indicators) -- [Plot profit](#plot-profit) - ## Installation Plotting scripts use Plotly library. Install/upgrade it with: @@ -48,7 +44,7 @@ To plot trades stored in a database use `--db-url` argument: python scripts/plot_dataframe.py --db-url sqlite:///tradesv3.dry_run.sqlite -p BTC/ETH ``` -To plot a test strategy the strategy should have first be backtested. +To plot a test strategy the strategy should have first be backtested. The results may then be plotted with the -s argument: ``` python scripts/plot_dataframe.py -s Strategy_Name -p BTC/ETH --datadir user_data/data// From 808ce3e7baa041a9784c82c180c3d93c493cad72 Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 17:20:28 +0100 Subject: [PATCH 035/358] edge added to content bar --- mkdocs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 521528e4b..45ae02cfa 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,8 +7,9 @@ nav: - Optimization: bot-optimization.md - Backtesting: backtesting.md - Hyperopt: hyperopt.md - - Plotting: plotting.md - Telegram usage: telegram-usage.md + - Plotting: plotting.md + - Edge positioning: edge.md theme: name: material logo: 'images/logo.png' From f42df56a88a5ce66a729553710ea7aa0a151f718 Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 17:21:54 +0100 Subject: [PATCH 036/358] edge reformatted --- docs/edge.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/edge.md b/docs/edge.md index 829910484..61abf354b 100644 --- a/docs/edge.md +++ b/docs/edge.md @@ -2,15 +2,11 @@ This page explains how to use Edge Positioning module in your bot in order to enter into a trade only if the trade has a reasonable win rate and risk reward ratio, and consequently adjust your position size and stoploss. -**NOTICE:** Edge positioning is not compatible with dynamic whitelist. it overrides dynamic whitelist. -**NOTICE2:** Edge won't consider anything else than buy/sell/stoploss signals. So trailing stoploss, ROI, and everything else will be ignored in its calculation. +!!! Warning + Edge positioning is not compatible with dynamic whitelist. it overrides dynamic whitelist. -## Table of Contents - -- [Introduction](#introduction) -- [How does it work?](#how-does-it-work?) -- [Configurations](#configurations) -- [Running Edge independently](#running-edge-independently) +!!! Note + Edge won't consider anything else than buy/sell/stoploss signals. So trailing stoploss, ROI, and everything else will be ignored in its calculation. ## Introduction Trading is all about probability. No one can claim that he has a strategy working all the time. You have to assume that sometimes you lose.

From 49c37692f3afea0bb4086089cf07bfa479846363 Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 17:29:07 +0100 Subject: [PATCH 037/358] content bar completed --- mkdocs.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 45ae02cfa..3f9690bc3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,9 +7,15 @@ nav: - Optimization: bot-optimization.md - Backtesting: backtesting.md - Hyperopt: hyperopt.md - - Telegram usage: telegram-usage.md + - Stoploss: stoploss.md + - Telegram: telegrame-usage.md - Plotting: plotting.md - Edge positioning: edge.md + - Web Hook: webhook-config.md + - FAQ: faq.md + - Contributors guide: developer.md + - SQL Cheatsheeet: sql_cheatsheet.md + - Sanbox testing: sandbox-testing.md theme: name: material logo: 'images/logo.png' From aa542784abcc1445535bf19c233852bb5dafb1b4 Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 17:32:03 +0100 Subject: [PATCH 038/358] unnecessary config removed --- mkdocs.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 3f9690bc3..8f3e399a3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -29,13 +29,9 @@ markdown_extensions: - toc: permalink: true - pymdownx.arithmatex - - pymdownx.betterem: - smart_enable: all - pymdownx.caret - pymdownx.critic - pymdownx.details - - pymdownx.emoji: - emoji_generator: !!python/name:pymdownx.emoji.to_svg - pymdownx.inlinehilite - pymdownx.magiclink - pymdownx.mark From c357483eef3befda800e3caf32d2090b9f95c856 Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 17:47:41 +0100 Subject: [PATCH 039/358] typo --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 8f3e399a3..6d59d557a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,7 +8,7 @@ nav: - Backtesting: backtesting.md - Hyperopt: hyperopt.md - Stoploss: stoploss.md - - Telegram: telegrame-usage.md + - Telegram: telegram-usage.md - Plotting: plotting.md - Edge positioning: edge.md - Web Hook: webhook-config.md From 4d415205d1a38e4501213e8c49fc796268c6107f Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 17:53:33 +0100 Subject: [PATCH 040/358] about document enriched --- docs/about.md | 23 +++++++++++++++++++++++ mkdocs.yml | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/about.md b/docs/about.md index 0fbc0321e..8edff2821 100644 --- a/docs/about.md +++ b/docs/about.md @@ -1,2 +1,25 @@ # Freqtrade +## Introduction Freqtrade is a cryptocurrency trading bot written in Python. + +!!! Danger "DISCLAIMER" + This software is for educational purposes only. Do not risk money which you are afraid to lose. USE THE SOFTWARE AT YOUR OWN RISK. THE AUTHORS AND ALL AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR TRADING RESULTS. + + Always start by running a trading bot in Dry-run and do not engage money before you understand how it works and what profit/loss you should expect. + + We strongly recommend you to have coding and Python knowledge. Do not hesitate to read the source code and understand the mechanism of this bot. + + +## Features + - Based on Python 3.6+: For botting on any operating system - Windows, macOS and Linux + - Persistence: Persistence is achieved through sqlite + - Dry-run: Run the bot without playing money. + - Backtesting: Run a simulation of your buy/sell strategy. + - Strategy Optimization by machine learning: Use machine learning to optimize your buy/sell strategy parameters with real exchange data. + - Edge position sizing Calculate your win rate, risk reward ratio, the best stoploss and adjust your position size before taking a position for each specific market. Learn more + - Whitelist crypto-currencies: Select which crypto-currency you want to trade or use dynamic whitelists. + - Blacklist crypto-currencies: Select which crypto-currency you want to avoid. + - Manageable via Telegram: Manage the bot with Telegram + - Display profit/loss in fiat: Display your profit/loss in 33 fiat. + - Daily summary of profit/loss: Provide a daily summary of your profit/loss. + - Performance status report: Provide a performance status of your current trades. diff --git a/mkdocs.yml b/mkdocs.yml index 6d59d557a..eea4be90a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -2,7 +2,7 @@ site_name: Freqtrade nav: - About: about.md - Installation: pre-requisite.md - - Configuratioon: configuration.md + - Configuration: configuration.md - Start the bot: bot-usage.md - Optimization: bot-optimization.md - Backtesting: backtesting.md From faad07aa3d26ffe1d8c423aaf1b4d31cc23a9650 Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 17:58:06 +0100 Subject: [PATCH 041/358] changing index to about page --- docs/about.md | 25 ------------------- docs/index-2.md | 37 ++++++++++++++++++++++++++++ docs/index.md | 64 ++++++++++++++++++++++--------------------------- mkdocs.yml | 2 +- 4 files changed, 66 insertions(+), 62 deletions(-) delete mode 100644 docs/about.md create mode 100644 docs/index-2.md diff --git a/docs/about.md b/docs/about.md deleted file mode 100644 index 8edff2821..000000000 --- a/docs/about.md +++ /dev/null @@ -1,25 +0,0 @@ -# Freqtrade -## Introduction -Freqtrade is a cryptocurrency trading bot written in Python. - -!!! Danger "DISCLAIMER" - This software is for educational purposes only. Do not risk money which you are afraid to lose. USE THE SOFTWARE AT YOUR OWN RISK. THE AUTHORS AND ALL AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR TRADING RESULTS. - - Always start by running a trading bot in Dry-run and do not engage money before you understand how it works and what profit/loss you should expect. - - We strongly recommend you to have coding and Python knowledge. Do not hesitate to read the source code and understand the mechanism of this bot. - - -## Features - - Based on Python 3.6+: For botting on any operating system - Windows, macOS and Linux - - Persistence: Persistence is achieved through sqlite - - Dry-run: Run the bot without playing money. - - Backtesting: Run a simulation of your buy/sell strategy. - - Strategy Optimization by machine learning: Use machine learning to optimize your buy/sell strategy parameters with real exchange data. - - Edge position sizing Calculate your win rate, risk reward ratio, the best stoploss and adjust your position size before taking a position for each specific market. Learn more - - Whitelist crypto-currencies: Select which crypto-currency you want to trade or use dynamic whitelists. - - Blacklist crypto-currencies: Select which crypto-currency you want to avoid. - - Manageable via Telegram: Manage the bot with Telegram - - Display profit/loss in fiat: Display your profit/loss in 33 fiat. - - Daily summary of profit/loss: Provide a daily summary of your profit/loss. - - Performance status report: Provide a performance status of your current trades. diff --git a/docs/index-2.md b/docs/index-2.md new file mode 100644 index 000000000..c0679f245 --- /dev/null +++ b/docs/index-2.md @@ -0,0 +1,37 @@ +# Introduction +Freqtrade is a cryptocurrency trading bot written in Python. + +[Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) + if you do not find the answer to your questions. + +## Table of Contents + +- [Pre-requisite](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md) + - [Setup your Bittrex account](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md#setup-your-bittrex-account) + - [Setup your Telegram bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md#setup-your-telegram-bot) +- [Bot Installation](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md) + - [Install with Docker (all platforms)](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#docker) + - [Install on Linux Ubuntu](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#21-linux---ubuntu-1604) + - [Install on MacOS](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#23-macos-installation) + - [Install on Windows](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#windows) +- [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md) +- [Bot usage (Start your bot)](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md) + - [Bot commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#bot-commands) + - [Backtesting commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#backtesting-commands) + - [Hyperopt commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#hyperopt-commands) + - [Edge commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#edge-commands) +- [Bot Optimization](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md) + - [Change your strategy](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md#change-your-strategy) + - [Add more Indicator](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md#add-more-indicator) + - [Test your strategy with Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md) + - [Edge positioning](https://github.com/freqtrade/freqtrade/blob/develop/docs/edge.md) + - [Find optimal parameters with Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md) +- [Control the bot with telegram](https://github.com/freqtrade/freqtrade/blob/develop/docs/telegram-usage.md) +- [Receive notifications via webhook](https://github.com/freqtrade/freqtrade/blob/develop/docs/webhook-config.md) +- [Contribute to the project](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md) + - [How to contribute](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md) + - [Run tests & Check PEP8 compliance](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md) +- [FAQ](https://github.com/freqtrade/freqtrade/blob/develop/docs/faq.md) + - [SQL cheatsheet](https://github.com/freqtrade/freqtrade/blob/develop/docs/sql_cheatsheet.md) +- [Sandbox Testing](https://github.com/freqtrade/freqtrade/blob/develop/docs/sandbox-testing.md) +- [Developer Docs](https://github.com/freqtrade/freqtrade/blob/develop/docs/developer.md) diff --git a/docs/index.md b/docs/index.md index afbc0a73a..0fca78f7e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,39 +1,31 @@ -# Introduction +# Freqtrade +## Introduction +Freqtrade is a cryptocurrency trading bot written in Python. -Welcome to freqtrade documentation. Please feel free to contribute to -this documentation if you see it became outdated by sending us a -Pull-request. Do not hesitate to reach us on -[Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) - if you do not find the answer to your questions. +!!! Danger "DISCLAIMER" + This software is for educational purposes only. Do not risk money which you are afraid to lose. USE THE SOFTWARE AT YOUR OWN RISK. THE AUTHORS AND ALL AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR TRADING RESULTS. -## Table of Contents + Always start by running a trading bot in Dry-run and do not engage money before you understand how it works and what profit/loss you should expect. -- [Pre-requisite](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md) - - [Setup your Bittrex account](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md#setup-your-bittrex-account) - - [Setup your Telegram bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md#setup-your-telegram-bot) -- [Bot Installation](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md) - - [Install with Docker (all platforms)](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#docker) - - [Install on Linux Ubuntu](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#21-linux---ubuntu-1604) - - [Install on MacOS](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#23-macos-installation) - - [Install on Windows](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#windows) -- [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md) -- [Bot usage (Start your bot)](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md) - - [Bot commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#bot-commands) - - [Backtesting commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#backtesting-commands) - - [Hyperopt commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#hyperopt-commands) - - [Edge commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#edge-commands) -- [Bot Optimization](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md) - - [Change your strategy](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md#change-your-strategy) - - [Add more Indicator](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md#add-more-indicator) - - [Test your strategy with Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md) - - [Edge positioning](https://github.com/freqtrade/freqtrade/blob/develop/docs/edge.md) - - [Find optimal parameters with Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md) -- [Control the bot with telegram](https://github.com/freqtrade/freqtrade/blob/develop/docs/telegram-usage.md) -- [Receive notifications via webhook](https://github.com/freqtrade/freqtrade/blob/develop/docs/webhook-config.md) -- [Contribute to the project](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md) - - [How to contribute](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md) - - [Run tests & Check PEP8 compliance](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md) -- [FAQ](https://github.com/freqtrade/freqtrade/blob/develop/docs/faq.md) - - [SQL cheatsheet](https://github.com/freqtrade/freqtrade/blob/develop/docs/sql_cheatsheet.md) -- [Sandbox Testing](https://github.com/freqtrade/freqtrade/blob/develop/docs/sandbox-testing.md) -- [Developer Docs](https://github.com/freqtrade/freqtrade/blob/develop/docs/developer.md) + We strongly recommend you to have coding and Python knowledge. Do not hesitate to read the source code and understand the mechanism of this bot. + + +## Features + - Based on Python 3.6+: For botting on any operating system - Windows, macOS and Linux + - Persistence: Persistence is achieved through sqlite + - Dry-run: Run the bot without playing money. + - Backtesting: Run a simulation of your buy/sell strategy. + - Strategy Optimization by machine learning: Use machine learning to optimize your buy/sell strategy parameters with real exchange data. + - Edge position sizing Calculate your win rate, risk reward ratio, the best stoploss and adjust your position size before taking a position for each specific market. Learn more + - Whitelist crypto-currencies: Select which crypto-currency you want to trade or use dynamic whitelists. + - Blacklist crypto-currencies: Select which crypto-currency you want to avoid. + - Manageable via Telegram: Manage the bot with Telegram + - Display profit/loss in fiat: Display your profit/loss in 33 fiat. + - Daily summary of profit/loss: Provide a daily summary of your profit/loss. + - Performance status report: Provide a performance status of your current trades. + +## Support +Help / Slack +For any questions not covered by the documentation or for further information about the bot, we encourage you to join our slack channel. + +Click [here](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) to join Slack channel. \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index eea4be90a..0a4701e1d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,6 +1,6 @@ site_name: Freqtrade nav: - - About: about.md + - About: index.md - Installation: pre-requisite.md - Configuration: configuration.md - Start the bot: bot-usage.md From 034bcd64d5817105f49760abcc70ecc79122e81a Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 18:03:09 +0100 Subject: [PATCH 042/358] =?UTF-8?q?requirements=20added=20to=20=E2=80=9Cab?= =?UTF-8?q?out=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/index.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 0fca78f7e..b5544522f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -24,8 +24,29 @@ Freqtrade is a cryptocurrency trading bot written in Python. - Daily summary of profit/loss: Provide a daily summary of your profit/loss. - Performance status report: Provide a performance status of your current trades. + +## Requirements +### Uptodate clock +The clock must be accurate, syncronized to a NTP server very frequently to avoid problems with communication to the exchanges. + +### Hardware requirements +To run this bot we recommend you a cloud instance with a minimum of: + +- 2GB RAM +- 1GB disk space +- 2vCPU + +### Software requirements +- Python 3.6.x +- pip +- git +- TA-Lib +- virtualenv (Recommended) +- Docker (Recommended) + + ## Support Help / Slack For any questions not covered by the documentation or for further information about the bot, we encourage you to join our slack channel. -Click [here](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) to join Slack channel. \ No newline at end of file +Click [here](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) to join Slack channel. From 689ca7645612f472ee3143cc38d1c4df1a72c0fd Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 18:06:09 +0100 Subject: [PATCH 043/358] =?UTF-8?q?added=20=E2=80=9Cready=20to=20try=3F?= =?UTF-8?q?=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/index.md b/docs/index.md index b5544522f..161d4c6ef 100644 --- a/docs/index.md +++ b/docs/index.md @@ -50,3 +50,6 @@ Help / Slack For any questions not covered by the documentation or for further information about the bot, we encourage you to join our slack channel. Click [here](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) to join Slack channel. + +## Ready to try? +Begin buy reading our installation guide [here](pre-requisite). \ No newline at end of file From be1969adc881213d984ea158ea4f85cddf4cd153 Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 30 Dec 2018 18:22:28 +0100 Subject: [PATCH 044/358] added widgets to index documents --- docs/index.md | 12 +++++++++ docs/partials/header.html | 52 +++++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 3 files changed, 65 insertions(+) create mode 100644 docs/partials/header.html diff --git a/docs/index.md b/docs/index.md index 161d4c6ef..fedbb98f5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,16 @@ # Freqtrade +[![Build Status](https://travis-ci.org/freqtrade/freqtrade.svg?branch=develop)](https://travis-ci.org/freqtrade/freqtrade) +[![Coverage Status](https://coveralls.io/repos/github/freqtrade/freqtrade/badge.svg?branch=develop&service=github)](https://coveralls.io/github/freqtrade/freqtrade?branch=develop) +[![Maintainability](https://api.codeclimate.com/v1/badges/5737e6d668200b7518ff/maintainability)](https://codeclimate.com/github/freqtrade/freqtrade/maintainability) + + +Star + +Fork + +Download + +Follow @freqtrade ## Introduction Freqtrade is a cryptocurrency trading bot written in Python. diff --git a/docs/partials/header.html b/docs/partials/header.html new file mode 100644 index 000000000..84c254b5a --- /dev/null +++ b/docs/partials/header.html @@ -0,0 +1,52 @@ +
+ + + +
\ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 0a4701e1d..c553d1fc7 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -19,6 +19,7 @@ nav: theme: name: material logo: 'images/logo.png' + custom_dir: 'docs' palette: primary: 'blue grey' accent: 'tear' From a021cd3ae2755f889c0fd47af25b202cc0326ce9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 30 Dec 2018 16:07:47 +0100 Subject: [PATCH 045/358] Add ohlcv data interpolator --- freqtrade/data/converter.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py index ee915ec19..71a7e35a4 100644 --- a/freqtrade/data/converter.py +++ b/freqtrade/data/converter.py @@ -5,6 +5,8 @@ import logging import pandas as pd from pandas import DataFrame, to_datetime +from freqtrade.constants import TICKER_INTERVAL_MINUTES + logger = logging.getLogger(__name__) @@ -36,6 +38,35 @@ def parse_ticker_dataframe(ticker: list) -> DataFrame: return frame +def ohlcv_fill_up_missing_data(dataframe: DataFrame, ticker_interval: str) -> DataFrame: + """ + Fills up missing data with 0 volume rows, + using the previous close as price for "open", "high" "low" and "close", volume is set to 0 + + """ + ohlc_dict = { + 'open': 'first', + 'high': 'max', + 'low': 'min', + 'close': 'last', + 'volume': 'sum' + } + tick_mins = TICKER_INTERVAL_MINUTES[ticker_interval] + # Resample to create "NAN" values + df = dataframe.resample(f'{tick_mins}min', on='date').agg(ohlc_dict) + + # Forwardfill close for missing columns + df['close'] = df['close'].fillna(method='ffill') + # Use close for "open, high, low" + df.loc[:, ['open', 'high', 'low']] = df[['open', 'high', 'low']].fillna( + value={'open': df['close'], + 'high': df['close'], + 'low': df['close'], + }) + df.reset_index(inplace=True) + return df + + def order_book_to_dataframe(bids: list, asks: list) -> DataFrame: """ Gets order book list, returns dataframe with below format per suggested by creslin From 03389d961fe7c6fc38a4fed630f86e69cc8d736b Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 31 Dec 2018 09:18:22 +0100 Subject: [PATCH 046/358] ADd test for data_interpolate --- freqtrade/data/converter.py | 1 + freqtrade/tests/data/test_converter.py | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py index 71a7e35a4..c1462e351 100644 --- a/freqtrade/data/converter.py +++ b/freqtrade/data/converter.py @@ -64,6 +64,7 @@ def ohlcv_fill_up_missing_data(dataframe: DataFrame, ticker_interval: str) -> Da 'low': df['close'], }) df.reset_index(inplace=True) + logger.debug(f"Missing data fillup: before: {len(dataframe)} - after: {len(df)}") return df diff --git a/freqtrade/tests/data/test_converter.py b/freqtrade/tests/data/test_converter.py index 54f32b341..8c60acbeb 100644 --- a/freqtrade/tests/data/test_converter.py +++ b/freqtrade/tests/data/test_converter.py @@ -1,7 +1,8 @@ # pragma pylint: disable=missing-docstring, C0103 import logging -from freqtrade.data.converter import parse_ticker_dataframe +from freqtrade.data.converter import parse_ticker_dataframe, ohlcv_fill_up_missing_data +from freqtrade.data.history import load_pair_history from freqtrade.tests.conftest import log_has @@ -11,8 +12,7 @@ def test_dataframe_correct_length(result): def test_dataframe_correct_columns(result): - assert result.columns.tolist() == \ - ['date', 'open', 'high', 'low', 'close', 'volume'] + assert result.columns.tolist() == ['date', 'open', 'high', 'low', 'close', 'volume'] def test_parse_ticker_dataframe(ticker_history, caplog): @@ -23,3 +23,18 @@ def test_parse_ticker_dataframe(ticker_history, caplog): dataframe = parse_ticker_dataframe(ticker_history) assert dataframe.columns.tolist() == columns assert log_has('Parsing tickerlist to dataframe', caplog.record_tuples) + + +def test_ohlcv_fill_up_missing_data(caplog): + data = load_pair_history(datadir=None, + ticker_interval='1m', + refresh_pairs=False, + pair='UNITTEST/BTC') + caplog.set_level(logging.DEBUG) + data2 = ohlcv_fill_up_missing_data(data, '1m') + assert len(data2) > len(data) + # Column names should not change + assert (data.columns == data2.columns).all() + + assert log_has(f"Missing data fillup: before: {len(data)} - after: {len(data2)}", + caplog.record_tuples) From d40921190821bbf591490a8684d819c9c622f4fe Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 31 Dec 2018 09:24:04 +0100 Subject: [PATCH 047/358] add test to verify data does not contain missing data afterwards --- freqtrade/tests/data/test_converter.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/freqtrade/tests/data/test_converter.py b/freqtrade/tests/data/test_converter.py index 8c60acbeb..aa4a161a7 100644 --- a/freqtrade/tests/data/test_converter.py +++ b/freqtrade/tests/data/test_converter.py @@ -3,6 +3,7 @@ import logging from freqtrade.data.converter import parse_ticker_dataframe, ohlcv_fill_up_missing_data from freqtrade.data.history import load_pair_history +from freqtrade.optimize import validate_backtest_data, get_timeframe from freqtrade.tests.conftest import log_has @@ -38,3 +39,8 @@ def test_ohlcv_fill_up_missing_data(caplog): assert log_has(f"Missing data fillup: before: {len(data)} - after: {len(data2)}", caplog.record_tuples) + + # Test fillup actually fixes invalid backtest data + min_date, max_date = get_timeframe({'UNITTEST/BTC': data}) + assert validate_backtest_data({'UNITTEST/BTC': data}, min_date, max_date, 1) + assert not validate_backtest_data({'UNITTEST/BTC': data2}, min_date, max_date, 1) From 04483da8df77be18f1264061d445b69591b6b2cf Mon Sep 17 00:00:00 2001 From: misagh Date: Mon, 31 Dec 2018 10:31:28 +0100 Subject: [PATCH 048/358] typo corrected --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index fedbb98f5..a9761fc8b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -64,4 +64,4 @@ For any questions not covered by the documentation or for further information ab Click [here](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) to join Slack channel. ## Ready to try? -Begin buy reading our installation guide [here](pre-requisite). \ No newline at end of file +Begin by reading our installation guide [here](pre-requisite). \ No newline at end of file From 79ac20636f303838e3b42350f953e746b76052b7 Mon Sep 17 00:00:00 2001 From: misagh Date: Mon, 31 Dec 2018 10:49:14 +0100 Subject: [PATCH 049/358] typo + broken link --- docs/backtesting.md | 2 +- docs/bot-usage.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 52b91c580..505b50e88 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -200,7 +200,7 @@ A backtesting result will look like that: 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 prsent a full picture. +The 2nd 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, diff --git a/docs/bot-usage.md b/docs/bot-usage.md index a26a2eb6f..1c21283ad 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -250,4 +250,4 @@ in [misc.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/misc. ## Next step The optimal strategy of the bot will change with time depending of the market trends. The next step is to -[optimize your bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md). +[optimize your bot](bot-optimization.md). From db1c9b8edfcf58287afeacf945f5521b1ed8befd Mon Sep 17 00:00:00 2001 From: misagh Date: Mon, 31 Dec 2018 11:04:22 +0100 Subject: [PATCH 050/358] relative links --- docs/backtesting.md | 2 +- docs/bot-optimization.md | 2 +- docs/bot-usage.md | 2 +- docs/configuration.md | 2 +- docs/hyperopt.md | 2 +- docs/index.md | 2 +- docs/{pre-requisite.md => installation-2.md} | 125 ++++++------------ docs/installation.md | 127 +++++++++++++------ mkdocs.yml | 2 +- 9 files changed, 133 insertions(+), 133 deletions(-) rename docs/{pre-requisite.md => installation-2.md} (76%) diff --git a/docs/backtesting.md b/docs/backtesting.md index 505b50e88..3111a1a39 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -262,4 +262,4 @@ Detailed output for all strategies one after the other will be available, so mak Great, your strategy is profitable. What if the bot can give your the optimal parameters to use for your strategy? -Your next step is to learn [how to find optimal parameters with Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md) +Your next step is to learn [how to find optimal parameters with Hyperopt](/hyperopt) diff --git a/docs/bot-optimization.md b/docs/bot-optimization.md index d4402d157..cf40cb884 100644 --- a/docs/bot-optimization.md +++ b/docs/bot-optimization.md @@ -247,4 +247,4 @@ We also got a *strategy-sharing* channel in our [Slack community](https://join.s ## Next step Now you have a perfect strategy you probably want to backtest it. -Your next step is to learn [How to use the Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md). +Your next step is to learn [How to use the Backtesting](/backtesting). diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 1c21283ad..bc5e7f0b4 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -89,7 +89,7 @@ This is very simple. Copy paste your strategy file into the folder ### How to use **--dynamic-whitelist**? !!! danger "DEPRECATED" - Dynamic-whitelist is deprecated. Please move your configurations to the configuration as outlined [here](docs/configuration.md#Dynamic-Pairlists) + Dynamic-whitelist is deprecated. Please move your configurations to the configuration as outlined [here](/configuration/#dynamic-pairlists) Per default `--dynamic-whitelist` will retrieve the 20 currencies based on BaseVolume. This value can be changed when you run the script. diff --git a/docs/configuration.md b/docs/configuration.md index 2f7486ec7..530cedc9c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -344,4 +344,4 @@ Please ensure that 'NameOfStrategy' is identical to the strategy name! ## Next step -Now you have configured your config.json, the next step is to [start your bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md). +Now you have configured your config.json, the next step is to [start your bot](/bot-usage). diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 76858aa53..54f5d2a6d 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -242,4 +242,4 @@ Dry/live runs will **NOT** use position stacking - therefore it does make sense ## Next Step Now you have a perfect bot and want to control it from Telegram. Your -next step is to learn the [Telegram usage](https://github.com/freqtrade/freqtrade/blob/develop/docs/telegram-usage.md). +next step is to learn the [Telegram usage](/telegram-usage). diff --git a/docs/index.md b/docs/index.md index a9761fc8b..cb2d63408 100644 --- a/docs/index.md +++ b/docs/index.md @@ -64,4 +64,4 @@ For any questions not covered by the documentation or for further information ab Click [here](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) to join Slack channel. ## Ready to try? -Begin by reading our installation guide [here](pre-requisite). \ No newline at end of file +Begin by reading our installation guide [here](/installation). \ No newline at end of file diff --git a/docs/pre-requisite.md b/docs/installation-2.md similarity index 76% rename from docs/pre-requisite.md rename to docs/installation-2.md index 1dd688ef0..71ae5a6b5 100644 --- a/docs/pre-requisite.md +++ b/docs/installation-2.md @@ -1,69 +1,25 @@ # Installation + This page explains how to prepare your environment for running the bot. -## Pre-requisite -Before running your bot in production you will need to setup few -external API. In production mode, the bot required valid Bittrex API -credentials and a Telegram bot (optional but recommended). +To understand how to set up the bot please read the [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md) page. -- [Setup your exchange account](#setup-your-exchange-account) -- [Backtesting commands](#setup-your-telegram-bot) +## Table of Contents -### Setup your exchange account -*To be completed, please feel free to complete this section.* +* [Table of Contents](#table-of-contents) +* [Easy Installation - Linux Script](#easy-installation---linux-script) +* [Automatic Installation - Docker](#automatic-installation---docker) +* [Custom Linux MacOS Installation](#custom-installation) + - [Requirements](#requirements) + - [Linux - Ubuntu 16.04](#linux---ubuntu-1604) + - [MacOS](#macos) + - [Setup Config and virtual env](#setup-config-and-virtual-env) +* [Windows](#windows) -### Setup your Telegram bot -The only things you need is a working Telegram bot and its API token. -Below we explain how to create your Telegram Bot, and how to get your -Telegram user id. + -### 1. Create your Telegram bot +------ -**1.1. Start a chat with https://telegram.me/BotFather** - -**1.2. Send the message `/newbot`. ** *BotFather response:* -``` -Alright, a new bot. How are we going to call it? Please choose a name for your bot. -``` - -**1.3. Choose the public name of your bot (e.x. `Freqtrade bot`)** -*BotFather response:* -``` -Good. Now let's choose a username for your bot. It must end in `bot`. Like this, for example: TetrisBot or tetris_bot. -``` -**1.4. Choose the name id of your bot (e.x "`My_own_freqtrade_bot`")** - -**1.5. Father bot will return you the token (API key)**
-Copy it and keep it you will use it for the config parameter `token`. -*BotFather response:* -```hl_lines="4" -Done! Congratulations on your new bot. You will find it at t.me/My_own_freqtrade_bot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it. Just make sure the bot is fully operational before you do this. - -Use this token to access the HTTP API: -521095879:AAEcEZEL7ADJ56FtG_qD0bQJSKETbXCBCi0 - -For a description of the Bot API, see this page: https://core.telegram.org/bots/api -``` -**1.6. Don't forget to start the conversation with your bot, by clicking /START button** - -### 2. Get your user id -**2.1. Talk to https://telegram.me/userinfobot** - -**2.2. Get your "Id", you will use it for the config parameter -`chat_id`.** -
-## Quick start -Freqtrade provides a Linux/MacOS script to install all dependencies and help you to configure the bot. - -```bash -git clone git@github.com:freqtrade/freqtrade.git -cd freqtrade -git checkout develop -./setup.sh --install -``` -!!! Note - Windows installation is explained [here](/#windows). -
## Easy Installation - Linux Script If you are on Debian, Ubuntu or MacOS a freqtrade provides a script to Install, Update, Configure, and Reset your bot. @@ -77,7 +33,7 @@ usage: -c,--config Easy config generator (Will override your existing file). ``` -** --install ** +### --install This script will install everything you need to run the bot: @@ -87,15 +43,15 @@ This script will install everything you need to run the bot: This script is a combination of `install script` `--reset`, `--config` -** --update ** +### --update Update parameter will pull the last version of your current branch and update your virtualenv. -** --reset ** +### --reset Reset parameter will hard reset your branch (only if you are on `master` or `develop`) and recreate your virtualenv. -** --config ** +### --config Config parameter is a `config.json` configurator. This script will ask you questions to setup your bot and create your `config.json`. @@ -113,33 +69,33 @@ Once you have Docker installed, simply create the config file (e.g. `config.json ### 1. Prepare the Bot -**1.1. Clone the git repository** +#### 1.1. Clone the git repository ```bash git clone https://github.com/freqtrade/freqtrade.git ``` -**1.2. (Optional) Checkout the develop branch** +#### 1.2. (Optional) Checkout the develop branch ```bash git checkout develop ``` -**1.3. Go into the new directory** +#### 1.3. Go into the new directory ```bash cd freqtrade ``` -**1.4. Copy `config.json.example` to `config.json`** +#### 1.4. Copy `config.json.example` to `config.json` ```bash cp -n config.json.example config.json ``` -> To edit the config please refer to the [Bot Configuration](/configuration.md) page. +> To edit the config please refer to the [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md) page. -**1.5. Create your database file *(optional - the bot will create it if it is missing)** +#### 1.5. Create your database file *(optional - the bot will create it if it is missing)* Production @@ -159,7 +115,7 @@ Either use the prebuilt image from docker hub - or build the image yourself if y Branches / tags available can be checked out on [Dockerhub](https://hub.docker.com/r/freqtradeorg/freqtrade/tags/). -**2.1. Download the docker image** +#### 2.1. Download the docker image Pull the image from docker hub and (optionally) change the name of the image @@ -171,7 +127,7 @@ docker tag freqtradeorg/freqtrade:develop freqtrade To update the image, simply run the above commands again and restart your running container. -**2.2. Build the Docker image** +#### 2.2. Build the Docker image ```bash cd freqtrade @@ -208,7 +164,7 @@ There is known issue in OSX Docker versions after 17.09.1, whereby /etc/localtim docker run --rm -e TZ=`ls -la /etc/localtime | cut -d/ -f8-9` -v `pwd`/config.json:/freqtrade/config.json -it freqtrade ``` -More information on this docker issue and work-around can be read [here](https://github.com/docker/for-mac/issues/2396). +More information on this docker issue and work-around can be read [here](https://github.com/docker/for-mac/issues/2396) In this example, the database will be created inside the docker instance and will be lost when you will refresh your image. @@ -216,7 +172,7 @@ In this example, the database will be created inside the docker instance and wil To run a restartable instance in the background (feel free to place your configuration and database files wherever it feels comfortable on your filesystem). -**5.1. Move your config file and database** +#### 5.1. Move your config file and database ```bash mkdir ~/.freqtrade @@ -224,7 +180,7 @@ mv config.json ~/.freqtrade mv tradesv3.sqlite ~/.freqtrade ``` -**5.2. Run the docker image** +#### 5.2. Run the docker image ```bash docker run -d \ @@ -235,9 +191,8 @@ docker run -d \ freqtrade --db-url sqlite:///tradesv3.sqlite ``` -!!! Note - db-url defaults to `sqlite:///tradesv3.sqlite` but it defaults to `sqlite://` if `dry_run=True` is being used. - To override this behaviour use a custom db-url value: i.e.: `--db-url sqlite:///tradesv3.dryrun.sqlite` +*Note*: db-url defaults to `sqlite:///tradesv3.sqlite` but it defaults to `sqlite://` if `dry_run=True` is being used. +To override this behaviour use a custom db-url value: i.e.: `--db-url sqlite:///tradesv3.dryrun.sqlite` ### 6. Monitor your Docker instance @@ -253,15 +208,14 @@ docker start freqtrade For more information on how to operate Docker, please refer to the [official Docker documentation](https://docs.docker.com/). -!!! Note - You do not need to rebuild the image for configuration changes, it will suffice to edit `config.json` and restart the container. +*Note*: You do not need to rebuild the image for configuration changes, it will suffice to edit `config.json` and restart the container. ### 7. Backtest with docker The following assumes that the above steps (1-4) have been completed successfully. Also, backtest-data should be available at `~/.freqtrade/user_data/`. -```bash +``` bash docker run -d \ --name freqtrade \ -v /etc/localtime:/etc/localtime:ro \ @@ -273,15 +227,14 @@ docker run -d \ Head over to the [Backtesting Documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md) for more details. -!!! Note - Additional parameters can be appended after the image name (`freqtrade` in the above example). +*Note*: Additional parameters can be appended after the image name (`freqtrade` in the above example). ------ ## Custom Installation We've included/collected install instructions for Ubuntu 16.04, MacOS, and Windows. These are guidelines and your success may vary with other distros. -OS Specific steps are listed first, the [Common](#common) section below is necessary for all systems. +OS Specific steps are listed first, the [common](#common) section below is necessary for all systems. ### Requirements @@ -333,7 +286,7 @@ python3 -m pip install -e . brew install python3 git wget ``` -### Common +### common #### 1. Install TA-Lib @@ -351,13 +304,11 @@ cd .. rm -rf ./ta-lib* ``` -!!! Note - An already downloaded version of ta-lib is included in the repository, as the sourceforge.net source seems to have problems frequently. +*Note*: An already downloaded version of ta-lib is included in the repository, as the sourceforge.net source seems to have problems frequently. #### 2. Setup your Python virtual environment (virtualenv) -!!! Note - This step is optional but strongly recommended to keep your system organized +*Note*: This step is optional but strongly recommended to keep your system organized ```bash python3 -m venv .env diff --git a/docs/installation.md b/docs/installation.md index 71ae5a6b5..d5c3e6293 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,25 +1,69 @@ # Installation - This page explains how to prepare your environment for running the bot. -To understand how to set up the bot please read the [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md) page. +## Prerequisite +Before running your bot in production you will need to setup few +external API. In production mode, the bot required valid Bittrex API +credentials and a Telegram bot (optional but recommended). -## Table of Contents +- [Setup your exchange account](#setup-your-exchange-account) +- [Backtesting commands](#setup-your-telegram-bot) -* [Table of Contents](#table-of-contents) -* [Easy Installation - Linux Script](#easy-installation---linux-script) -* [Automatic Installation - Docker](#automatic-installation---docker) -* [Custom Linux MacOS Installation](#custom-installation) - - [Requirements](#requirements) - - [Linux - Ubuntu 16.04](#linux---ubuntu-1604) - - [MacOS](#macos) - - [Setup Config and virtual env](#setup-config-and-virtual-env) -* [Windows](#windows) +### Setup your exchange account +*To be completed, please feel free to complete this section.* - +### Setup your Telegram bot +The only things you need is a working Telegram bot and its API token. +Below we explain how to create your Telegram Bot, and how to get your +Telegram user id. ------- +### 1. Create your Telegram bot +**1.1. Start a chat with https://telegram.me/BotFather** + +**1.2. Send the message `/newbot`. ** *BotFather response:* +``` +Alright, a new bot. How are we going to call it? Please choose a name for your bot. +``` + +**1.3. Choose the public name of your bot (e.x. `Freqtrade bot`)** +*BotFather response:* +``` +Good. Now let's choose a username for your bot. It must end in `bot`. Like this, for example: TetrisBot or tetris_bot. +``` +**1.4. Choose the name id of your bot (e.x "`My_own_freqtrade_bot`")** + +**1.5. Father bot will return you the token (API key)**
+Copy it and keep it you will use it for the config parameter `token`. +*BotFather response:* +```hl_lines="4" +Done! Congratulations on your new bot. You will find it at t.me/My_own_freqtrade_bot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it. Just make sure the bot is fully operational before you do this. + +Use this token to access the HTTP API: +521095879:AAEcEZEL7ADJ56FtG_qD0bQJSKETbXCBCi0 + +For a description of the Bot API, see this page: https://core.telegram.org/bots/api +``` +**1.6. Don't forget to start the conversation with your bot, by clicking /START button** + +### 2. Get your user id +**2.1. Talk to https://telegram.me/userinfobot** + +**2.2. Get your "Id", you will use it for the config parameter +`chat_id`.** +
+## Quick start +Freqtrade provides a Linux/MacOS script to install all dependencies and help you to configure the bot. + +```bash +git clone git@github.com:freqtrade/freqtrade.git +cd freqtrade +git checkout develop +./setup.sh --install +``` +!!! Note + Windows installation is explained [here](/#windows). +
## Easy Installation - Linux Script If you are on Debian, Ubuntu or MacOS a freqtrade provides a script to Install, Update, Configure, and Reset your bot. @@ -33,7 +77,7 @@ usage: -c,--config Easy config generator (Will override your existing file). ``` -### --install +** --install ** This script will install everything you need to run the bot: @@ -43,15 +87,15 @@ This script will install everything you need to run the bot: This script is a combination of `install script` `--reset`, `--config` -### --update +** --update ** Update parameter will pull the last version of your current branch and update your virtualenv. -### --reset +** --reset ** Reset parameter will hard reset your branch (only if you are on `master` or `develop`) and recreate your virtualenv. -### --config +** --config ** Config parameter is a `config.json` configurator. This script will ask you questions to setup your bot and create your `config.json`. @@ -69,33 +113,33 @@ Once you have Docker installed, simply create the config file (e.g. `config.json ### 1. Prepare the Bot -#### 1.1. Clone the git repository +**1.1. Clone the git repository** ```bash git clone https://github.com/freqtrade/freqtrade.git ``` -#### 1.2. (Optional) Checkout the develop branch +**1.2. (Optional) Checkout the develop branch** ```bash git checkout develop ``` -#### 1.3. Go into the new directory +**1.3. Go into the new directory** ```bash cd freqtrade ``` -#### 1.4. Copy `config.json.example` to `config.json` +**1.4. Copy `config.json.example` to `config.json`** ```bash cp -n config.json.example config.json ``` -> To edit the config please refer to the [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md) page. +> To edit the config please refer to the [Bot Configuration](/configuration.md) page. -#### 1.5. Create your database file *(optional - the bot will create it if it is missing)* +**1.5. Create your database file *(optional - the bot will create it if it is missing)** Production @@ -115,7 +159,7 @@ Either use the prebuilt image from docker hub - or build the image yourself if y Branches / tags available can be checked out on [Dockerhub](https://hub.docker.com/r/freqtradeorg/freqtrade/tags/). -#### 2.1. Download the docker image +**2.1. Download the docker image** Pull the image from docker hub and (optionally) change the name of the image @@ -127,7 +171,7 @@ docker tag freqtradeorg/freqtrade:develop freqtrade To update the image, simply run the above commands again and restart your running container. -#### 2.2. Build the Docker image +**2.2. Build the Docker image** ```bash cd freqtrade @@ -164,7 +208,7 @@ There is known issue in OSX Docker versions after 17.09.1, whereby /etc/localtim docker run --rm -e TZ=`ls -la /etc/localtime | cut -d/ -f8-9` -v `pwd`/config.json:/freqtrade/config.json -it freqtrade ``` -More information on this docker issue and work-around can be read [here](https://github.com/docker/for-mac/issues/2396) +More information on this docker issue and work-around can be read [here](https://github.com/docker/for-mac/issues/2396). In this example, the database will be created inside the docker instance and will be lost when you will refresh your image. @@ -172,7 +216,7 @@ In this example, the database will be created inside the docker instance and wil To run a restartable instance in the background (feel free to place your configuration and database files wherever it feels comfortable on your filesystem). -#### 5.1. Move your config file and database +**5.1. Move your config file and database** ```bash mkdir ~/.freqtrade @@ -180,7 +224,7 @@ mv config.json ~/.freqtrade mv tradesv3.sqlite ~/.freqtrade ``` -#### 5.2. Run the docker image +**5.2. Run the docker image** ```bash docker run -d \ @@ -191,8 +235,9 @@ docker run -d \ freqtrade --db-url sqlite:///tradesv3.sqlite ``` -*Note*: db-url defaults to `sqlite:///tradesv3.sqlite` but it defaults to `sqlite://` if `dry_run=True` is being used. -To override this behaviour use a custom db-url value: i.e.: `--db-url sqlite:///tradesv3.dryrun.sqlite` +!!! Note + db-url defaults to `sqlite:///tradesv3.sqlite` but it defaults to `sqlite://` if `dry_run=True` is being used. + To override this behaviour use a custom db-url value: i.e.: `--db-url sqlite:///tradesv3.dryrun.sqlite` ### 6. Monitor your Docker instance @@ -208,14 +253,15 @@ docker start freqtrade For more information on how to operate Docker, please refer to the [official Docker documentation](https://docs.docker.com/). -*Note*: You do not need to rebuild the image for configuration changes, it will suffice to edit `config.json` and restart the container. +!!! Note + You do not need to rebuild the image for configuration changes, it will suffice to edit `config.json` and restart the container. ### 7. Backtest with docker The following assumes that the above steps (1-4) have been completed successfully. Also, backtest-data should be available at `~/.freqtrade/user_data/`. -``` bash +```bash docker run -d \ --name freqtrade \ -v /etc/localtime:/etc/localtime:ro \ @@ -227,14 +273,15 @@ docker run -d \ Head over to the [Backtesting Documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md) for more details. -*Note*: Additional parameters can be appended after the image name (`freqtrade` in the above example). +!!! Note + Additional parameters can be appended after the image name (`freqtrade` in the above example). ------ ## Custom Installation We've included/collected install instructions for Ubuntu 16.04, MacOS, and Windows. These are guidelines and your success may vary with other distros. -OS Specific steps are listed first, the [common](#common) section below is necessary for all systems. +OS Specific steps are listed first, the [Common](#common) section below is necessary for all systems. ### Requirements @@ -286,7 +333,7 @@ python3 -m pip install -e . brew install python3 git wget ``` -### common +### Common #### 1. Install TA-Lib @@ -304,11 +351,13 @@ cd .. rm -rf ./ta-lib* ``` -*Note*: An already downloaded version of ta-lib is included in the repository, as the sourceforge.net source seems to have problems frequently. +!!! Note + An already downloaded version of ta-lib is included in the repository, as the sourceforge.net source seems to have problems frequently. #### 2. Setup your Python virtual environment (virtualenv) -*Note*: This step is optional but strongly recommended to keep your system organized +!!! Note + This step is optional but strongly recommended to keep your system organized ```bash python3 -m venv .env @@ -426,4 +475,4 @@ The easiest way is to download install Microsoft Visual Studio Community [here]( --- Now you have an environment ready, the next step is -[Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md)... +[Bot Configuration](/configuration). diff --git a/mkdocs.yml b/mkdocs.yml index c553d1fc7..07672884a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,7 +1,7 @@ site_name: Freqtrade nav: - About: index.md - - Installation: pre-requisite.md + - Installation: installation.md - Configuration: configuration.md - Start the bot: bot-usage.md - Optimization: bot-optimization.md From 7bf1a92dc36088de6767bc704633cab63236045d Mon Sep 17 00:00:00 2001 From: misagh Date: Mon, 31 Dec 2018 11:12:56 +0100 Subject: [PATCH 051/358] =?UTF-8?q?=E2=80=9Cinofficial=E2=80=9D=20=3D>=20u?= =?UTF-8?q?nofficial?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index d5c3e6293..6ee3b17dd 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -445,7 +445,7 @@ copy paste `config.json` to ``\path\freqtrade-develop\freqtrade` Install ta-lib according to the [ta-lib documentation](https://github.com/mrjbq7/ta-lib#windows). -As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), there is also a repository of inofficial precompiled windows Wheels [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib), which needs to be downloaded and installed using `pip install TA_Lib‑0.4.17‑cp36‑cp36m‑win32.whl` (make sure to use the version matching your python version) +As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), there is also a repository of unofficial precompiled windows Wheels [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib), which needs to be downloaded and installed using `pip install TA_Lib‑0.4.17‑cp36‑cp36m‑win32.whl` (make sure to use the version matching your python version) ```cmd >cd \path\freqtrade-develop From a86b34e41c3e9ff4bb442f41608a3c4c1e3d927f Mon Sep 17 00:00:00 2001 From: misagh Date: Mon, 31 Dec 2018 11:52:09 +0100 Subject: [PATCH 052/358] old files removed --- docs/index-2.md | 37 ---- docs/installation-2.md | 429 ----------------------------------------- 2 files changed, 466 deletions(-) delete mode 100644 docs/index-2.md delete mode 100644 docs/installation-2.md diff --git a/docs/index-2.md b/docs/index-2.md deleted file mode 100644 index c0679f245..000000000 --- a/docs/index-2.md +++ /dev/null @@ -1,37 +0,0 @@ -# Introduction -Freqtrade is a cryptocurrency trading bot written in Python. - -[Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) - if you do not find the answer to your questions. - -## Table of Contents - -- [Pre-requisite](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md) - - [Setup your Bittrex account](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md#setup-your-bittrex-account) - - [Setup your Telegram bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md#setup-your-telegram-bot) -- [Bot Installation](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md) - - [Install with Docker (all platforms)](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#docker) - - [Install on Linux Ubuntu](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#21-linux---ubuntu-1604) - - [Install on MacOS](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#23-macos-installation) - - [Install on Windows](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#windows) -- [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md) -- [Bot usage (Start your bot)](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md) - - [Bot commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#bot-commands) - - [Backtesting commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#backtesting-commands) - - [Hyperopt commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#hyperopt-commands) - - [Edge commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#edge-commands) -- [Bot Optimization](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md) - - [Change your strategy](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md#change-your-strategy) - - [Add more Indicator](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md#add-more-indicator) - - [Test your strategy with Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md) - - [Edge positioning](https://github.com/freqtrade/freqtrade/blob/develop/docs/edge.md) - - [Find optimal parameters with Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md) -- [Control the bot with telegram](https://github.com/freqtrade/freqtrade/blob/develop/docs/telegram-usage.md) -- [Receive notifications via webhook](https://github.com/freqtrade/freqtrade/blob/develop/docs/webhook-config.md) -- [Contribute to the project](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md) - - [How to contribute](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md) - - [Run tests & Check PEP8 compliance](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md) -- [FAQ](https://github.com/freqtrade/freqtrade/blob/develop/docs/faq.md) - - [SQL cheatsheet](https://github.com/freqtrade/freqtrade/blob/develop/docs/sql_cheatsheet.md) -- [Sandbox Testing](https://github.com/freqtrade/freqtrade/blob/develop/docs/sandbox-testing.md) -- [Developer Docs](https://github.com/freqtrade/freqtrade/blob/develop/docs/developer.md) diff --git a/docs/installation-2.md b/docs/installation-2.md deleted file mode 100644 index 71ae5a6b5..000000000 --- a/docs/installation-2.md +++ /dev/null @@ -1,429 +0,0 @@ -# Installation - -This page explains how to prepare your environment for running the bot. - -To understand how to set up the bot please read the [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md) page. - -## Table of Contents - -* [Table of Contents](#table-of-contents) -* [Easy Installation - Linux Script](#easy-installation---linux-script) -* [Automatic Installation - Docker](#automatic-installation---docker) -* [Custom Linux MacOS Installation](#custom-installation) - - [Requirements](#requirements) - - [Linux - Ubuntu 16.04](#linux---ubuntu-1604) - - [MacOS](#macos) - - [Setup Config and virtual env](#setup-config-and-virtual-env) -* [Windows](#windows) - - - ------- - -## Easy Installation - Linux Script - -If you are on Debian, Ubuntu or MacOS a freqtrade provides a script to Install, Update, Configure, and Reset your bot. - -```bash -$ ./setup.sh -usage: - -i,--install Install freqtrade from scratch - -u,--update Command git pull to update. - -r,--reset Hard reset your develop/master branch. - -c,--config Easy config generator (Will override your existing file). -``` - -### --install - -This script will install everything you need to run the bot: - -* Mandatory software as: `Python3`, `ta-lib`, `wget` -* Setup your virtualenv -* Configure your `config.json` file - -This script is a combination of `install script` `--reset`, `--config` - -### --update - -Update parameter will pull the last version of your current branch and update your virtualenv. - -### --reset - -Reset parameter will hard reset your branch (only if you are on `master` or `develop`) and recreate your virtualenv. - -### --config - -Config parameter is a `config.json` configurator. This script will ask you questions to setup your bot and create your `config.json`. - ------- - -## Automatic Installation - Docker - -Start by downloading Docker for your platform: - -* [Mac](https://www.docker.com/products/docker#/mac) -* [Windows](https://www.docker.com/products/docker#/windows) -* [Linux](https://www.docker.com/products/docker#/linux) - -Once you have Docker installed, simply create the config file (e.g. `config.json`) and then create a Docker image for `freqtrade` using the Dockerfile in this repo. - -### 1. Prepare the Bot - -#### 1.1. Clone the git repository - -```bash -git clone https://github.com/freqtrade/freqtrade.git -``` - -#### 1.2. (Optional) Checkout the develop branch - -```bash -git checkout develop -``` - -#### 1.3. Go into the new directory - -```bash -cd freqtrade -``` - -#### 1.4. Copy `config.json.example` to `config.json` - -```bash -cp -n config.json.example config.json -``` - -> To edit the config please refer to the [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md) page. - -#### 1.5. Create your database file *(optional - the bot will create it if it is missing)* - -Production - -```bash -touch tradesv3.sqlite -```` - -Dry-Run - -```bash -touch tradesv3.dryrun.sqlite -``` - -### 2. Download or build the docker image - -Either use the prebuilt image from docker hub - or build the image yourself if you would like more control on which version is used. - -Branches / tags available can be checked out on [Dockerhub](https://hub.docker.com/r/freqtradeorg/freqtrade/tags/). - -#### 2.1. Download the docker image - -Pull the image from docker hub and (optionally) change the name of the image - -```bash -docker pull freqtradeorg/freqtrade:develop -# Optionally tag the repository so the run-commands remain shorter -docker tag freqtradeorg/freqtrade:develop freqtrade -``` - -To update the image, simply run the above commands again and restart your running container. - -#### 2.2. Build the Docker image - -```bash -cd freqtrade -docker build -t freqtrade . -``` - -If you are developing using Docker, use `Dockerfile.develop` to build a dev Docker image, which will also set up develop dependencies: - -```bash -docker build -f ./Dockerfile.develop -t freqtrade-dev . -``` - -For security reasons, your configuration file will not be included in the image, you will need to bind mount it. It is also advised to bind mount an SQLite database file (see the "5. Run a restartable docker image" section) to keep it between updates. - -### 3. Verify the Docker image - -After the build process you can verify that the image was created with: - -```bash -docker images -``` - -### 4. Run the Docker image - -You can run a one-off container that is immediately deleted upon exiting with the following command (`config.json` must be in the current working directory): - -```bash -docker run --rm -v /etc/localtime:/etc/localtime:ro -v `pwd`/config.json:/freqtrade/config.json -it freqtrade -``` - -There is known issue in OSX Docker versions after 17.09.1, whereby /etc/localtime cannot be shared causing Docker to not start. A work-around for this is to start with the following cmd. - -```bash -docker run --rm -e TZ=`ls -la /etc/localtime | cut -d/ -f8-9` -v `pwd`/config.json:/freqtrade/config.json -it freqtrade -``` - -More information on this docker issue and work-around can be read [here](https://github.com/docker/for-mac/issues/2396) - -In this example, the database will be created inside the docker instance and will be lost when you will refresh your image. - -### 5. Run a restartable docker image - -To run a restartable instance in the background (feel free to place your configuration and database files wherever it feels comfortable on your filesystem). - -#### 5.1. Move your config file and database - -```bash -mkdir ~/.freqtrade -mv config.json ~/.freqtrade -mv tradesv3.sqlite ~/.freqtrade -``` - -#### 5.2. Run the docker image - -```bash -docker run -d \ - --name freqtrade \ - -v /etc/localtime:/etc/localtime:ro \ - -v ~/.freqtrade/config.json:/freqtrade/config.json \ - -v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \ - freqtrade --db-url sqlite:///tradesv3.sqlite -``` - -*Note*: db-url defaults to `sqlite:///tradesv3.sqlite` but it defaults to `sqlite://` if `dry_run=True` is being used. -To override this behaviour use a custom db-url value: i.e.: `--db-url sqlite:///tradesv3.dryrun.sqlite` - -### 6. Monitor your Docker instance - -You can then use the following commands to monitor and manage your container: - -```bash -docker logs freqtrade -docker logs -f freqtrade -docker restart freqtrade -docker stop freqtrade -docker start freqtrade -``` - -For more information on how to operate Docker, please refer to the [official Docker documentation](https://docs.docker.com/). - -*Note*: You do not need to rebuild the image for configuration changes, it will suffice to edit `config.json` and restart the container. - -### 7. Backtest with docker - -The following assumes that the above steps (1-4) have been completed successfully. -Also, backtest-data should be available at `~/.freqtrade/user_data/`. - -``` bash -docker run -d \ - --name freqtrade \ - -v /etc/localtime:/etc/localtime:ro \ - -v ~/.freqtrade/config.json:/freqtrade/config.json \ - -v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \ - -v ~/.freqtrade/user_data/:/freqtrade/user_data/ \ - freqtrade --strategy AwsomelyProfitableStrategy backtesting -``` - -Head over to the [Backtesting Documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md) for more details. - -*Note*: Additional parameters can be appended after the image name (`freqtrade` in the above example). - ------- - -## Custom Installation - -We've included/collected install instructions for Ubuntu 16.04, MacOS, and Windows. These are guidelines and your success may vary with other distros. -OS Specific steps are listed first, the [common](#common) section below is necessary for all systems. - -### Requirements - -Click each one for install guide: - -* [Python >= 3.6.x](http://docs.python-guide.org/en/latest/starting/installation/) -* [pip](https://pip.pypa.io/en/stable/installing/) -* [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [virtualenv](https://virtualenv.pypa.io/en/stable/installation/) (Recommended) -* [TA-Lib](https://mrjbq7.github.io/ta-lib/install.html) - -### Linux - Ubuntu 16.04 - -#### Install Python 3.6, Git, and wget - -```bash -sudo add-apt-repository ppa:jonathonf/python-3.6 -sudo apt-get update -sudo apt-get install python3.6 python3.6-venv python3.6-dev build-essential autoconf libtool pkg-config make wget git -``` - -#### Raspberry Pi / Raspbian - -Before installing FreqTrade on a Raspberry Pi running the official Raspbian Image, make sure you have at least Python 3.6 installed. The default image only provides Python 3.5. Probably the easiest way to get a recent version of python is [miniconda](https://repo.continuum.io/miniconda/). - -The following assumes that miniconda3 is installed and available in your environment. Last miniconda3 installation file use python 3.4, we will update to python 3.6 on this installation. -It's recommended to use (mini)conda for this as installation/compilation of `numpy`, `scipy` and `pandas` takes a long time. -If you have installed it from (mini)conda, you can remove `numpy`, `scipy`, and `pandas` from `requirements.txt` before you install it with `pip`. - -Additional package to install on your Raspbian, `libffi-dev` required by cryptography (from python-telegram-bot). - -``` bash -conda config --add channels rpi -conda install python=3.6 -conda create -n freqtrade python=3.6 -conda activate freqtrade -conda install scipy pandas numpy - -sudo apt install libffi-dev -python3 -m pip install -r requirements.txt -python3 -m pip install -e . -``` - -### MacOS - -#### Install Python 3.6, git and wget - -```bash -brew install python3 git wget -``` - -### common - -#### 1. Install TA-Lib - -Official webpage: https://mrjbq7.github.io/ta-lib/install.html - -```bash -wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz -tar xvzf ta-lib-0.4.0-src.tar.gz -cd ta-lib -sed -i.bak "s|0.00000001|0.000000000000000001 |g" src/ta_func/ta_utility.h -./configure --prefix=/usr/local -make -sudo make install -cd .. -rm -rf ./ta-lib* -``` - -*Note*: An already downloaded version of ta-lib is included in the repository, as the sourceforge.net source seems to have problems frequently. - -#### 2. Setup your Python virtual environment (virtualenv) - -*Note*: This step is optional but strongly recommended to keep your system organized - -```bash -python3 -m venv .env -source .env/bin/activate -``` - -#### 3. Install FreqTrade - -Clone the git repository: - -```bash -git clone https://github.com/freqtrade/freqtrade.git - -``` - -Optionally checkout the stable/master branch: - -```bash -git checkout master -``` - -#### 4. Initialize the configuration - -```bash -cd freqtrade -cp config.json.example config.json -``` - -> *To edit the config please refer to [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md).* - -#### 5. Install python dependencies - -``` bash -pip3 install --upgrade pip -pip3 install -r requirements.txt -pip3 install -e . -``` - -#### 6. Run the Bot - -If this is the first time you run the bot, ensure you are running it in Dry-run `"dry_run": true,` otherwise it will start to buy and sell coins. - -```bash -python3.6 ./freqtrade/main.py -c config.json -``` - -*Note*: If you run the bot on a server, you should consider using [Docker](#automatic-installation---docker) a terminal multiplexer like `screen` or [`tmux`](https://en.wikipedia.org/wiki/Tmux) to avoid that the bot is stopped on logout. - -#### 7. [Optional] Configure `freqtrade` as a `systemd` service - -From the freqtrade repo... copy `freqtrade.service` to your systemd user directory (usually `~/.config/systemd/user`) and update `WorkingDirectory` and `ExecStart` to match your setup. - -After that you can start the daemon with: - -```bash -systemctl --user start freqtrade -``` - -For this to be persistent (run when user is logged out) you'll need to enable `linger` for your freqtrade user. - -```bash -sudo loginctl enable-linger "$USER" -``` - ------- - -## Windows - -We recommend that Windows users use [Docker](#docker) as this will work much easier and smoother (also more secure). - -If that is not possible, try using the Windows Linux subsystem (WSL) - for which the Ubuntu instructions should work. -If that is not available on your system, feel free to try the instructions below, which led to success for some. - -### Install freqtrade manually - -#### Clone the git repository - -```bash -git clone https://github.com/freqtrade/freqtrade.git -``` - -copy paste `config.json` to ``\path\freqtrade-develop\freqtrade` - -#### Install ta-lib - -Install ta-lib according to the [ta-lib documentation](https://github.com/mrjbq7/ta-lib#windows). - -As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), there is also a repository of inofficial precompiled windows Wheels [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib), which needs to be downloaded and installed using `pip install TA_Lib‑0.4.17‑cp36‑cp36m‑win32.whl` (make sure to use the version matching your python version) - -```cmd ->cd \path\freqtrade-develop ->python -m venv .env ->cd .env\Scripts ->activate.bat ->cd \path\freqtrade-develop -REM optionally install ta-lib from wheel -REM >pip install TA_Lib‑0.4.17‑cp36‑cp36m‑win32.whl ->pip install -r requirements.txt ->pip install -e . ->python freqtrade\main.py -``` - -> Thanks [Owdr](https://github.com/Owdr) for the commands. Source: [Issue #222](https://github.com/freqtrade/freqtrade/issues/222) - -#### Error during installation under Windows - -``` bash -error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools -``` - -Unfortunately, many packages requiring compilation don't provide a pre-build wheel. It is therefore mandatory to have a C/C++ compiler installed and available for your python environment to use. - -The easiest way is to download install Microsoft Visual Studio Community [here](https://visualstudio.microsoft.com/downloads/) and make sure to install "Common Tools for Visual C++" to enable building c code on Windows. Unfortunately, this is a heavy download / dependency (~4Gb) so you might want to consider WSL or docker first. - ---- - -Now you have an environment ready, the next step is -[Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md)... From a07a004bb6b6537dbb5adc6cfc48eeb47d9c84a4 Mon Sep 17 00:00:00 2001 From: misagh Date: Mon, 31 Dec 2018 13:19:00 +0100 Subject: [PATCH 053/358] test relative link --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index cb2d63408..92ea4fe43 100644 --- a/docs/index.md +++ b/docs/index.md @@ -64,4 +64,4 @@ For any questions not covered by the documentation or for further information ab Click [here](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) to join Slack channel. ## Ready to try? -Begin by reading our installation guide [here](/installation). \ No newline at end of file +Begin by reading our installation guide [here](installation). \ No newline at end of file From dd91b5c7314ba5c78e699b9944fb76d68679580a Mon Sep 17 00:00:00 2001 From: misagh Date: Mon, 31 Dec 2018 13:26:25 +0100 Subject: [PATCH 054/358] links corrected --- docs/backtesting.md | 2 +- docs/bot-optimization.md | 2 +- docs/configuration.md | 2 +- docs/hyperopt.md | 2 +- docs/installation.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 3111a1a39..851cbb327 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -262,4 +262,4 @@ Detailed output for all strategies one after the other will be available, so mak Great, your strategy is profitable. What if the bot can give your the optimal parameters to use for your strategy? -Your next step is to learn [how to find optimal parameters with Hyperopt](/hyperopt) +Your next step is to learn [how to find optimal parameters with Hyperopt](hyperopt) diff --git a/docs/bot-optimization.md b/docs/bot-optimization.md index cf40cb884..184f74cb6 100644 --- a/docs/bot-optimization.md +++ b/docs/bot-optimization.md @@ -247,4 +247,4 @@ We also got a *strategy-sharing* channel in our [Slack community](https://join.s ## Next step Now you have a perfect strategy you probably want to backtest it. -Your next step is to learn [How to use the Backtesting](/backtesting). +Your next step is to learn [How to use the Backtesting](backtesting). diff --git a/docs/configuration.md b/docs/configuration.md index 530cedc9c..e95fbd4c8 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -344,4 +344,4 @@ Please ensure that 'NameOfStrategy' is identical to the strategy name! ## Next step -Now you have configured your config.json, the next step is to [start your bot](/bot-usage). +Now you have configured your config.json, the next step is to [start your bot](bot-usage). diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 54f5d2a6d..20dc618dc 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -242,4 +242,4 @@ Dry/live runs will **NOT** use position stacking - therefore it does make sense ## Next Step Now you have a perfect bot and want to control it from Telegram. Your -next step is to learn the [Telegram usage](/telegram-usage). +next step is to learn the [Telegram usage](telegram-usage). diff --git a/docs/installation.md b/docs/installation.md index 6ee3b17dd..cd60d3833 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -475,4 +475,4 @@ The easiest way is to download install Microsoft Visual Studio Community [here]( --- Now you have an environment ready, the next step is -[Bot Configuration](/configuration). +[Bot Configuration](configuration). From 361b294e432fb834577ade5e8bab858836c2b037 Mon Sep 17 00:00:00 2001 From: misagh Date: Mon, 31 Dec 2018 13:27:59 +0100 Subject: [PATCH 055/358] =?UTF-8?q?block=20=E2=80=9C=3D=3D=E2=80=9C=20remo?= =?UTF-8?q?ved?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/configuration.md | 146 +++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index e95fbd4c8..92a813b44 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -11,67 +11,67 @@ The table below will list all configuration parameters. | Command | Default | Mandatory | Description | |----------|---------|-----------|-------------| -| ==`max_open_trades`== | 3 | Yes | Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades) -| ==`stake_currency`== | BTC | Yes | Crypto-currency used for trading. -| ==`stake_amount`== | 0.05 | Yes | Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to 'unlimited' to allow the bot to use all avaliable balance. -| ==`ticker_interval`== | [1m, 5m, 30m, 1h, 1d] | No | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes -| ==`fiat_display_currency`== | USD | Yes | Fiat currency used to show your profits. More information below. -| ==`dry_run`== | true | Yes | Define if the bot must be in Dry-run or production mode. -| ==`process_only_new_candles`== | false | No | If set to true indicators are processed only once a new candle arrives. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. Can be set either in Configuration or in the strategy. -| ==`minimal_roi`== | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. If set, this parameter will override `minimal_roi` from your strategy file. -| ==`stoploss`== | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. If set, this parameter will override `stoploss` from your strategy file. -| ==`trailing_stop`== | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file). -| ==`trailing_stop_positve`== | 0 | No | Changes stop-loss once profit has been reached. -| ==`trailing_stop_positve_offset`== | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. -| ==`unfilledtimeout.buy`== | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled. -| ==`unfilledtimeout.sell`== | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled. -| ==`bid_strategy.ask_last_balance`== | 0.0 | Yes | Set the bidding price. More information below. -| ==`bid_strategy.use_order_book`== | false | No | Allows buying of pair using the rates in Order Book Bids. -| ==`bid_strategy.order_book_top`== | 0 | No | Bot will use the top N rate in Order Book Bids. Ie. a value of 2 will allow the bot to pick the 2nd bid rate in Order Book Bids. -| ==`bid_strategy. check_depth_of_market.enabled`== | false | No | Does not buy if the % difference of buy orders and sell orders is met in Order Book. -| ==`bid_strategy. check_depth_of_market.bids_to_ask_delta`== | 0 | No | The % difference of buy orders and sell orders found in Order Book. A value lesser than 1 means sell orders is greater, while value greater than 1 means buy orders is higher. -| ==`ask_strategy.use_order_book`== | false | No | Allows selling of open traded pair using the rates in Order Book Asks. -| ==`ask_strategy.order_book_min`== | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. -| ==`ask_strategy.order_book_max`== | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. -| ==`order_types`== | None | No | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). -| ==`order_time_in_force`== | None | No | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). -| ==`exchange.name`== | bittrex | Yes | Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). -| ==`exchange.key`== | key | No | API key to use for the exchange. Only required when you are in production mode. -| ==`exchange.secret`== | secret | No | API secret to use for the exchange. Only required when you are in production mode. -| ==`exchange.pair_whitelist`== | [] | No | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param. -| ==`exchange.pair_blacklist`== | [] | No | List of currency the bot must avoid. Useful when using `--dynamic-whitelist` param. -| ==`exchange.ccxt_rate_limit`== | True | No | DEPRECATED!! Have CCXT handle Exchange rate limits. Depending on the exchange, having this to false can lead to temporary bans from the exchange. -| ==`exchange.ccxt_config`== | None | No | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) -| ==`exchange.ccxt_async_config`== | None | No | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) -| ==`edge`== | false | No | Please refer to [edge configuration document](edge.md) for detailed explanation. -| ==`experimental.use_sell_signal`== | false | No | Use your sell strategy in addition of the `minimal_roi`. -| ==`experimental.sell_profit_only`== | false | No | waits until you have made a positive profit before taking a sell decision. -| ==`experimental.ignore_roi_if_buy_signal`== | false | No | Does not sell if the buy-signal is still active. Takes preference over `minimal_roi` and `use_sell_signal` -| ==`pairlist.method`== | StaticPairList | No | Use Static whitelist. [More information below](#dynamic-pairlists). -| ==`pairlist.config`== | None | No | Additional configuration for dynamic pairlists. [More information below](#dynamic-pairlists). -| ==`telegram.enabled`== | true | Yes | Enable or not the usage of Telegram. -| ==`telegram.token`== | token | No | Your Telegram bot token. Only required if `telegram.enabled` is `true`. -| ==`telegram.chat_id`== | chat_id | No | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. -| ==`webhook.enabled`== | false | No | Enable usage of Webhook notifications -| ==`webhook.url`== | false | No | URL for the webhook. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details. -| ==`webhook.webhookbuy`== | false | No | Payload to send on buy. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. -| ==`webhook.webhooksell`== | false | No | Payload to send on sell. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. -| ==`webhook.webhookstatus`== | false | No | Payload to send on status calls. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. -| ==`db_url`== | `sqlite:///tradesv3.sqlite`| No | Declares database URL to use. NOTE: This defaults to `sqlite://` if `dry_run` is `True`. -| ==`initial_state`== | running | No | Defines the initial application state. More information below. -| ==`forcebuy_enable`== | false | No | Enables the RPC Commands to force a buy. More information below. -| ==`strategy`== | DefaultStrategy | No | Defines Strategy class to use. -| ==`strategy_path`== | null | No | Adds an additional strategy lookup path (must be a folder). -| ==`internals.process_throttle_secs`== | 5 | Yes | Set the process throttle. Value in second. +| `max_open_trades` | 3 | Yes | Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades) +| `stake_currency` | BTC | Yes | Crypto-currency used for trading. +| `stake_amount` | 0.05 | Yes | Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to 'unlimited' to allow the bot to use all avaliable balance. +| `ticker_interval` | [1m, 5m, 30m, 1h, 1d] | No | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes +| `fiat_display_currency` | USD | Yes | Fiat currency used to show your profits. More information below. +| `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode. +| `process_only_new_candles` | false | No | If set to true indicators are processed only once a new candle arrives. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. Can be set either in Configuration or in the strategy. +| `minimal_roi` | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. If set, this parameter will override `minimal_roi` from your strategy file. +| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. If set, this parameter will override `stoploss` from your strategy file. +| `trailing_stop` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file). +| `trailing_stop_positve` | 0 | No | Changes stop-loss once profit has been reached. +| `trailing_stop_positve_offset` | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. +| `unfilledtimeout.buy` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled. +| `unfilledtimeout.sell` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled. +| `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. More information below. +| `bid_strategy.use_order_book` | false | No | Allows buying of pair using the rates in Order Book Bids. +| `bid_strategy.order_book_top` | 0 | No | Bot will use the top N rate in Order Book Bids. Ie. a value of 2 will allow the bot to pick the 2nd bid rate in Order Book Bids. +| `bid_strategy. check_depth_of_market.enabled` | false | No | Does not buy if the % difference of buy orders and sell orders is met in Order Book. +| `bid_strategy. check_depth_of_market.bids_to_ask_delta` | 0 | No | The % difference of buy orders and sell orders found in Order Book. A value lesser than 1 means sell orders is greater, while value greater than 1 means buy orders is higher. +| `ask_strategy.use_order_book` | false | No | Allows selling of open traded pair using the rates in Order Book Asks. +| `ask_strategy.order_book_min` | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. +| `ask_strategy.order_book_max` | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. +| `order_types` | None | No | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). +| `order_time_in_force` | None | No | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). +| `exchange.name` | bittrex | Yes | Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). +| `exchange.key` | key | No | API key to use for the exchange. Only required when you are in production mode. +| `exchange.secret` | secret | No | API secret to use for the exchange. Only required when you are in production mode. +| `exchange.pair_whitelist` | [] | No | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param. +| `exchange.pair_blacklist` | [] | No | List of currency the bot must avoid. Useful when using `--dynamic-whitelist` param. +| `exchange.ccxt_rate_limit` | True | No | DEPRECATED!! Have CCXT handle Exchange rate limits. Depending on the exchange, having this to false can lead to temporary bans from the exchange. +| `exchange.ccxt_config` | None | No | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) +| `exchange.ccxt_async_config` | None | No | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) +| `edge` | false | No | Please refer to [edge configuration document](edge.md) for detailed explanation. +| `experimental.use_sell_signal` | false | No | Use your sell strategy in addition of the `minimal_roi`. +| `experimental.sell_profit_only` | false | No | waits until you have made a positive profit before taking a sell decision. +| `experimental.ignore_roi_if_buy_signal` | false | No | Does not sell if the buy-signal is still active. Takes preference over `minimal_roi` and `use_sell_signal` +| `pairlist.method` | StaticPairList | No | Use Static whitelist. [More information below](#dynamic-pairlists). +| `pairlist.config` | None | No | Additional configuration for dynamic pairlists. [More information below](#dynamic-pairlists). +| `telegram.enabled` | true | Yes | Enable or not the usage of Telegram. +| `telegram.token` | token | No | Your Telegram bot token. Only required if `telegram.enabled` is `true`. +| `telegram.chat_id` | chat_id | No | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. +| `webhook.enabled` | false | No | Enable usage of Webhook notifications +| `webhook.url` | false | No | URL for the webhook. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details. +| `webhook.webhookbuy` | false | No | Payload to send on buy. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. +| `webhook.webhooksell` | false | No | Payload to send on sell. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. +| `webhook.webhookstatus` | false | No | Payload to send on status calls. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. +| `db_url` | `sqlite:///tradesv3.sqlite`| No | Declares database URL to use. NOTE: This defaults to `sqlite://` if `dry_run` is `True`. +| `initial_state` | running | No | Defines the initial application state. More information below. +| `forcebuy_enable` | false | No | Enables the RPC Commands to force a buy. More information below. +| `strategy` | DefaultStrategy | No | Defines Strategy class to use. +| `strategy_path` | null | No | Adds an additional strategy lookup path (must be a folder). +| `internals.process_throttle_secs` | 5 | Yes | Set the process throttle. Value in second. The definition of each config parameters is in [misc.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/misc.py#L205). ### Understand stake_amount -==`stake_amount`== is an amount of crypto-currency your bot will use for each trade. +`stake_amount` is an amount of crypto-currency your bot will use for each trade. The minimal value is 0.0005. If there is not enough crypto-currency in the account an exception is generated. -To allow the bot to trade all the avaliable ==`stake_currency`== in your account set
+To allow the bot to trade all the avaliable `stake_currency` in your account set
```json "stake_amount" : "unlimited", ``` @@ -82,7 +82,7 @@ currency_balanse / (max_open_trades - current_open_trades) ### Understand minimal_roi -==`minimal_roi`== is a JSON object where the key is a duration +`minimal_roi` is a JSON object where the key is a duration in minutes and the value is the minimum ROI in percent. See the example below: @@ -95,19 +95,19 @@ See the example below: }, ``` -Most of the strategy files already include the optimal ==`minimal_roi`== +Most of the strategy files already include the optimal `minimal_roi` value. This parameter is optional. If you use it, it will take over the -==`minimal_roi`== value from the strategy file. +`minimal_roi` value from the strategy file. ### Understand stoploss -==`stoploss`== is loss in percentage that should trigger a sale. +`stoploss` is loss in percentage that should trigger a sale. For example value `-0.10` will cause immediate sell if the profit dips below -10% for a given trade. This parameter is optional. -Most of the strategy files already include the optimal ==`stoploss`== +Most of the strategy files already include the optimal `stoploss` value. This parameter is optional. If you use it, it will take over the -==`stoploss`== value from the strategy file. +`stoploss` value from the strategy file. ### Understand trailing stoploss @@ -115,13 +115,13 @@ Go to the [trailing stoploss Documentation](stoploss.md) for details on trailing ### Understand initial_state -==`initial_state`== is an optional field that defines the initial application state. +`initial_state` is an optional field that defines the initial application state. Possible values are `running` or `stopped`. (default=`running`) If the value is `stopped` the bot has to be started with `/start` first. ### Understand forcebuy_enable -==`forcebuy_enable`== enables the usage of forcebuy commands via Telegram. +`forcebuy_enable` enables the usage of forcebuy commands via Telegram. This is disabled for security reasons by default, and will show a warning message on startup if enabled. You send `/forcebuy ETH/BTC` to the bot, who buys the pair and holds it until a regular sell-signal appears (ROI, stoploss, /forcesell). @@ -130,21 +130,21 @@ See [the telegram documentation](telegram-usage.md) for details on usage. ### Understand process_throttle_secs -==`process_throttle_secs`== is an optional field that defines in seconds how long the bot should wait +`process_throttle_secs` is an optional field that defines in seconds how long the bot should wait before asking the strategy if we should buy or a sell an asset. After each wait period, the strategy is asked again for every opened trade wether or not we should sell, and for all the remaining pairs (either the dynamic list of pairs or the static list of pairs) if we should buy. ### Understand ask_last_balance -==`ask_last_balance`== sets the bidding price. Value `0.0` will use `ask` price, `1.0` will +`ask_last_balance` sets the bidding price. Value `0.0` will use `ask` price, `1.0` will use the `last` price and values between those interpolate between ask and last price. Using `ask` price will guarantee quick success in bid, but bot will also end up paying more then would probably have been necessary. ### Understand order_types -==`order_types`== contains a dict mapping order-types to market-types as well as stoploss on or off exchange type. This allows to buy using limit orders, sell using limit-orders, and create stoploss orders using market. It also allows to set the stoploss "on exchange" which means stoploss order would be placed immediately once the buy order is fulfilled. +`order_types` contains a dict mapping order-types to market-types as well as stoploss on or off exchange type. This allows to buy using limit orders, sell using limit-orders, and create stoploss orders using market. It also allows to set the stoploss "on exchange" which means stoploss order would be placed immediately once the buy order is fulfilled. This can be set in the configuration or in the strategy. Configuration overwrites strategy configurations. If this is configured, all 4 values (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`) need to be present, otherwise the bot warn about it and will fail to start. @@ -164,7 +164,7 @@ The below is the default which is used if this is not configured in either Strat The following message will be shown if your exchange does not support market orders: `"Exchange does not support market orders."` ### Understand order_time_in_force -==`order_time_in_force`== defines the policy by which the order is executed on the exchange. Three commonly used time in force are:
+`order_time_in_force` defines the policy by which the order is executed on the exchange. Three commonly used time in force are:
**GTC (Goog Till Canceled):** This is most of the time the default time in force. It means the order will remain on exchange till it is canceled by user. It can be fully or partially fulfilled. If partially fulfilled, the remaining will stay on the exchange till cancelled.
**FOK (Full Or Kill):** @@ -172,7 +172,7 @@ It means if the order is not executed immediately AND fully then it is canceled **IOC (Immediate Or Canceled):** It is the same as FOK (above) except it can be partially fulfilled. The remaining part is automatically cancelled by the exchange.
-==`order_time_in_force`== contains a dict buy and sell time in force policy. This can be set in the configuration or in the strategy. Configuration overwrites strategy configurations.
+`order_time_in_force` contains a dict buy and sell time in force policy. This can be set in the configuration or in the strategy. Configuration overwrites strategy configurations.
possible values are: `gtc` (default), `fok` or `ioc`.
``` python "order_time_in_force": { @@ -200,7 +200,7 @@ Feel free to test other exchanges and submit your PR to improve the bot. ### What values for fiat_display_currency? -==`fiat_display_currency`== set the base currency to use for the conversion from coin to fiat in Telegram. +`fiat_display_currency` set the base currency to use for the conversion from coin to fiat in Telegram. The valid values are:
```json "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK", "EUR", "GBP", "HKD", "HUF", "IDR", "ILS", "INR", "JPY", "KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN", "RUB", "SEK", "SGD", "THB", "TRY", "TWD", "ZAR", "USD" @@ -243,17 +243,17 @@ production mode. ### Dynamic Pairlists Dynamic pairlists select pairs for you based on the logic configured. -The bot runs against all pairs (with that stake) on the exchange, and a number of assets (==`number_assets`==) is selected based on the selected criteria. +The bot runs against all pairs (with that stake) on the exchange, and a number of assets (`number_assets`) is selected based on the selected criteria. -By default, a Static Pairlist is used (configured as ==`"pair_whitelist"`== under the ==`"exchange"`== section of this configuration). +By default, a Static Pairlist is used (configured as `"pair_whitelist"` under the `"exchange"` section of this configuration). **Available Pairlist methods:** * `"StaticPairList"` - * uses configuration from ==`exchange.pair_whitelist`== and ==`exchange.pair_blacklist`== + * uses configuration from `exchange.pair_whitelist` and `exchange.pair_blacklist` * `"VolumePairList"` * Formerly available as `--dynamic-whitelist []` - * Selects ==`number_assets`== top pairs based on ==`sort_key`==, which can be one of `askVolume`, `bidVolume` and `quoteVolume`, defaults to `quoteVolume`. + * Selects `number_assets` top pairs based on `sort_key`, which can be one of `askVolume`, `bidVolume` and `quoteVolume`, defaults to `quoteVolume`. ```json "pairlist": { @@ -297,7 +297,7 @@ you run it in production mode. ### Using proxy with FreqTrade -To use a proxy with freqtrade, add the kwarg ==`"aiohttp_trust_env"=true`== to the ==`"ccxt_async_kwargs"`== dict in the exchange section of the configuration. +To use a proxy with freqtrade, add the kwarg `"aiohttp_trust_env"=true` to the `"ccxt_async_kwargs"` dict in the exchange section of the configuration. An example for this can be found in `config_full.json.example` From 5be21fd9d853ed09355eb614bbc05be966342066 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Mon, 31 Dec 2018 13:34:07 +0100 Subject: [PATCH 056/358] Update ccxt from 1.18.86 to 1.18.87 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4c6b0799f..2498ab8e2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.86 +ccxt==1.18.87 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.12.1 From 42cc3e525e193273970cca40ab26b8b7bd2da5cf Mon Sep 17 00:00:00 2001 From: misagh Date: Mon, 31 Dec 2018 13:36:27 +0100 Subject: [PATCH 057/358] link second try --- docs/backtesting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 851cbb327..0a60d2db3 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -262,4 +262,4 @@ Detailed output for all strategies one after the other will be available, so mak Great, your strategy is profitable. What if the bot can give your the optimal parameters to use for your strategy? -Your next step is to learn [how to find optimal parameters with Hyperopt](hyperopt) +Your next step is to learn [how to find optimal parameters with Hyperopt](hyperopt.md) From 366980fd6291051f62c1c89d3085b1cb48c48357 Mon Sep 17 00:00:00 2001 From: misagh Date: Mon, 31 Dec 2018 13:39:18 +0100 Subject: [PATCH 058/358] broken link corrected --- docs/bot-optimization.md | 2 +- docs/configuration.md | 2 +- docs/hyperopt.md | 2 +- docs/installation.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/bot-optimization.md b/docs/bot-optimization.md index 184f74cb6..4ff0c6f24 100644 --- a/docs/bot-optimization.md +++ b/docs/bot-optimization.md @@ -247,4 +247,4 @@ We also got a *strategy-sharing* channel in our [Slack community](https://join.s ## Next step Now you have a perfect strategy you probably want to backtest it. -Your next step is to learn [How to use the Backtesting](backtesting). +Your next step is to learn [How to use the Backtesting](backtesting.md). diff --git a/docs/configuration.md b/docs/configuration.md index 92a813b44..eb58e4925 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -344,4 +344,4 @@ Please ensure that 'NameOfStrategy' is identical to the strategy name! ## Next step -Now you have configured your config.json, the next step is to [start your bot](bot-usage). +Now you have configured your config.json, the next step is to [start your bot](bot-usage.md). diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 20dc618dc..1e96f4b5c 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -242,4 +242,4 @@ Dry/live runs will **NOT** use position stacking - therefore it does make sense ## Next Step Now you have a perfect bot and want to control it from Telegram. Your -next step is to learn the [Telegram usage](telegram-usage). +next step is to learn the [Telegram usage](telegram-usage.md). diff --git a/docs/installation.md b/docs/installation.md index cd60d3833..10a64c110 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -475,4 +475,4 @@ The easiest way is to download install Microsoft Visual Studio Community [here]( --- Now you have an environment ready, the next step is -[Bot Configuration](configuration). +[Bot Configuration](configuration.md). From 827a8309d75117f6287bd3f1c22a28e098073743 Mon Sep 17 00:00:00 2001 From: misagh Date: Mon, 31 Dec 2018 14:00:36 +0100 Subject: [PATCH 059/358] more links corrected --- docs/faq.md | 51 ++++++++++++------------- docs/hyperopt.md | 2 +- docs/installation.md | 8 ++-- docs/telegram-usage.md | 87 +++++++++++++++++++++--------------------- 4 files changed, 73 insertions(+), 75 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 31a302067..4bbf28fe6 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -2,43 +2,43 @@ #### I have waited 5 minutes, why hasn't the bot made any trades yet?! -Depending on the buy strategy, the amount of whitelisted coins, the -situation of the market etc, it can take up to hours to find good entry +Depending on the buy strategy, the amount of whitelisted coins, the +situation of the market etc, it can take up to hours to find good entry position for a trade. Be patient! #### I have made 12 trades already, why is my total profit negative?! -I understand your disappointment but unfortunately 12 trades is just -not enough to say anything. If you run backtesting, you can see that our -current algorithm does leave you on the plus side, but that is after -thousands of trades and even there, you will be left with losses on -specific coins that you have traded tens if not hundreds of times. We -of course constantly aim to improve the bot but it will _always_ be a -gamble, which should leave you with modest wins on monthly basis but +I understand your disappointment but unfortunately 12 trades is just +not enough to say anything. If you run backtesting, you can see that our +current algorithm does leave you on the plus side, but that is after +thousands of trades and even there, you will be left with losses on +specific coins that you have traded tens if not hundreds of times. We +of course constantly aim to improve the bot but it will _always_ be a +gamble, which should leave you with modest wins on monthly basis but you can't say much from few trades. -#### I’d like to change the stake amount. Can I just stop the bot with +#### I’d like to change the stake amount. Can I just stop the bot with /stop and then change the config.json and run it again? -Not quite. Trades are persisted to a database but the configuration is -currently only read when the bot is killed and restarted. `/stop` more +Not quite. Trades are persisted to a database but the configuration is +currently only read when the bot is killed and restarted. `/stop` more like pauses. You can stop your bot, adjust settings and start it again. #### I want to improve the bot with a new strategy -That's great. We have a nice backtesting and hyperoptimizing setup. See -the tutorial [here|Testing-new-strategies-with-Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#hyperopt-commands). +That's great. We have a nice backtesting and hyperoptimizing setup. See +the tutorial [here|Testing-new-strategies-with-Hyperopt](bot-usage.md#hyperopt-commands). -#### Is there a setting to only SELL the coins being held and not +#### Is there a setting to only SELL the coins being held and not perform anymore BUYS? -You can use the `/forcesell all` command from Telegram. +You can use the `/forcesell all` command from Telegram. ### How many epoch do I need to get a good Hyperopt result? -Per default Hyperopts without `-e` or `--epochs` parameter will only -run 100 epochs, means 100 evals of your triggers, guards, .... Too few -to find a great result (unless if you are very lucky), so you probably -have to run it for 10.000 or more. But it will take an eternity to +Per default Hyperopts without `-e` or `--epochs` parameter will only +run 100 epochs, means 100 evals of your triggers, guards, .... Too few +to find a great result (unless if you are very lucky), so you probably +have to run it for 10.000 or more. But it will take an eternity to compute. We recommend you to run it at least 10.000 epochs: @@ -52,7 +52,7 @@ for i in {1..100}; do python3 ./freqtrade/main.py hyperopt -e 100; done ``` #### Why it is so long to run hyperopt? -Finding a great Hyperopt results takes time. +Finding a great Hyperopt results takes time. If you wonder why it takes a while to find great hyperopt results @@ -60,12 +60,11 @@ This answer was written during the under the release 0.15.1, when we had : - 8 triggers - 9 guards: let's say we evaluate even 10 values from each -- 1 stoploss calculation: let's say we want 10 values from that too to +- 1 stoploss calculation: let's say we want 10 values from that too to be evaluated The following calculation is still very rough and not very precise -but it will give the idea. With only these triggers and guards there is -already 8*10^9*10 evaluations. A roughly total of 80 billion evals. -Did you run 100 000 evals? Congrats, you've done roughly 1 / 100 000 th +but it will give the idea. With only these triggers and guards there is +already 8*10^9*10 evaluations. A roughly total of 80 billion evals. +Did you run 100 000 evals? Congrats, you've done roughly 1 / 100 000 th of the search space. - diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 1e96f4b5c..58dc91e3a 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -12,7 +12,7 @@ and still take a long time. ## Prepare Hyperopting Before we start digging in Hyperopt, we recommend you to take a look at -an example hyperopt file located into [user_data/hyperopts/](https://github.com/gcarq/freqtrade/blob/develop/user_data/hyperopts/test_hyperopt.py) +an example hyperopt file located into [user_data/hyperopts/](https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/test_hyperopt.py) ### 1. Install a Custom Hyperopt File This is very simple. Put your hyperopt file into the folder diff --git a/docs/installation.md b/docs/installation.md index 10a64c110..e5f514eb1 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -62,7 +62,7 @@ git checkout develop ./setup.sh --install ``` !!! Note - Windows installation is explained [here](/#windows). + Windows installation is explained [here](#windows).
## Easy Installation - Linux Script @@ -137,7 +137,7 @@ cd freqtrade cp -n config.json.example config.json ``` -> To edit the config please refer to the [Bot Configuration](/configuration.md) page. +> To edit the config please refer to the [Bot Configuration](configuration.md) page. **1.5. Create your database file *(optional - the bot will create it if it is missing)** @@ -271,7 +271,7 @@ docker run -d \ freqtrade --strategy AwsomelyProfitableStrategy backtesting ``` -Head over to the [Backtesting Documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md) for more details. +Head over to the [Backtesting Documentation](backtesting.md) for more details. !!! Note Additional parameters can be appended after the image name (`freqtrade` in the above example). @@ -386,7 +386,7 @@ cd freqtrade cp config.json.example config.json ``` -> *To edit the config please refer to [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md).* +> *To edit the config please refer to [Bot Configuration](configuration.md).* #### 5. Install python dependencies diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index 28213fb5d..fa765f697 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -3,13 +3,13 @@ This page explains how to command your bot with Telegram. ## Pre-requisite -To control your bot with Telegram, you need first to -[set up a Telegram bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md) +To control your bot with Telegram, you need first to +[set up a Telegram bot](installation.md) and add your Telegram API keys into your config file. ## Telegram commands -Per default, the Telegram bot shows predefined commands. Some commands -are only available by sending them to the bot. The table below list the +Per default, the Telegram bot shows predefined commands. Some commands +are only available by sending them to the bot. The table below list the official commands. You can ask at any moment for help with `/help`. | Command | Default | Description | @@ -40,30 +40,30 @@ Below, example of Telegram message you will receive for each command. ### /stop -> `Stopping trader ...` +> `Stopping trader ...` > **Status:** `stopped` ## /status For each open trade, the bot will send you the following message. -> **Trade ID:** `123` +> **Trade ID:** `123` > **Current Pair:** CVC/BTC -> **Open Since:** `1 days ago` -> **Amount:** `26.64180098` -> **Open Rate:** `0.00007489` -> **Close Rate:** `None` -> **Current Rate:** `0.00007489` -> **Close Profit:** `None` -> **Current Profit:** `12.95%` +> **Open Since:** `1 days ago` +> **Amount:** `26.64180098` +> **Open Rate:** `0.00007489` +> **Close Rate:** `None` +> **Current Rate:** `0.00007489` +> **Close Profit:** `None` +> **Current Profit:** `12.95%` > **Open Order:** `None` ## /status table Return the status of all open trades in a table format. ``` - ID Pair Since Profit ----- -------- ------- -------- + ID Pair Since Profit +---- -------- ------- -------- 67 SC/BTC 1 d 13.33% 123 CVC/BTC 1 h 12.95% ``` @@ -73,32 +73,32 @@ Return the status of all open trades in a table format. Return the number of trades used and available. ``` current max ---------- ----- - 2 10 +--------- ----- + 2 10 ``` ## /profit Return a summary of your profit/loss and performance. -> **ROI:** Close trades -> ∙ `0.00485701 BTC (258.45%)` -> ∙ `62.968 USD` -> **ROI:** All trades -> ∙ `0.00255280 BTC (143.43%)` -> ∙ `33.095 EUR` -> -> **Total Trade Count:** `138` -> **First Trade opened:** `3 days ago` -> **Latest Trade opened:** `2 minutes ago` -> **Avg. Duration:** `2:33:45` +> **ROI:** Close trades +> ∙ `0.00485701 BTC (258.45%)` +> ∙ `62.968 USD` +> **ROI:** All trades +> ∙ `0.00255280 BTC (143.43%)` +> ∙ `33.095 EUR` +> +> **Total Trade Count:** `138` +> **First Trade opened:** `3 days ago` +> **Latest Trade opened:** `2 minutes ago` +> **Avg. Duration:** `2:33:45` > **Best Performing:** `PAY/BTC: 50.23%` ## /forcesell > **BITTREX:** Selling BTC/LTC with limit `0.01650000 (profit: ~-4.07%, -0.00008168)` -## /forcebuy +## /forcebuy > **BITTREX**: Buying ETH/BTC with limit `0.03400000` (`1.000000 ETH`, `225.290 USD`) @@ -107,7 +107,7 @@ Note that for this to work, `forcebuy_enable` needs to be set to true. ## /performance Return the performance of each crypto-currency the bot has sold. -> Performance: +> Performance: > 1. `RCN/BTC 57.77%` > 2. `PAY/BTC 56.91%` > 3. `VIB/BTC 47.07%` @@ -119,31 +119,30 @@ Return the performance of each crypto-currency the bot has sold. Return the balance of all crypto-currency your have on the exchange. -> **Currency:** BTC -> **Available:** 3.05890234 -> **Balance:** 3.05890234 -> **Pending:** 0.0 +> **Currency:** BTC +> **Available:** 3.05890234 +> **Balance:** 3.05890234 +> **Pending:** 0.0 -> **Currency:** CVC -> **Available:** 86.64180098 -> **Balance:** 86.64180098 +> **Currency:** CVC +> **Available:** 86.64180098 +> **Balance:** 86.64180098 > **Pending:** 0.0 ## /daily -Per default `/daily` will return the 7 last days. +Per default `/daily` will return the 7 last days. The example below if for `/daily 3`: > **Daily Profit over the last 3 days:** ``` -Day Profit BTC Profit USD ----------- -------------- ------------ -2018-01-03 0.00224175 BTC 29,142 USD -2018-01-02 0.00033131 BTC 4,307 USD +Day Profit BTC Profit USD +---------- -------------- ------------ +2018-01-03 0.00224175 BTC 29,142 USD +2018-01-02 0.00033131 BTC 4,307 USD 2018-01-01 0.00269130 BTC 34.986 USD ``` ## /version -> **Version:** `0.14.3` - +> **Version:** `0.14.3` From 7b1f4aec7689b329d08d1625b3284510f310d73a Mon Sep 17 00:00:00 2001 From: misagh Date: Mon, 31 Dec 2018 14:01:55 +0100 Subject: [PATCH 060/358] typo --- docs/telegram-usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index fa765f697..e01c8f9bc 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -2,7 +2,7 @@ This page explains how to command your bot with Telegram. -## Pre-requisite +## Prerequisite To control your bot with Telegram, you need first to [set up a Telegram bot](installation.md) and add your Telegram API keys into your config file. From 8b9cc45f41cbcb31b4893e4042a84e72212e1903 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 31 Dec 2018 15:09:50 +0100 Subject: [PATCH 061/358] move test for data completeness should be done before analyzing strategy --- freqtrade/optimize/backtesting.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index cc05c5de8..86b5bdd19 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -19,6 +19,7 @@ from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration from freqtrade.exchange import Exchange from freqtrade.data import history +from freqtrade.data.converter import ohlcv_fill_up_missing_data from freqtrade.misc import file_dump_json from freqtrade.persistence import Trade from freqtrade.resolvers import StrategyResolver @@ -393,12 +394,9 @@ class Backtesting(object): logger.info("Running backtesting for Strategy %s", strat.get_strategy_name()) self._set_strategy(strat) - # need to reprocess data every time to populate signals - preprocessed = self.strategy.tickerdata_to_dataframe(data) - - min_date, max_date = optimize.get_timeframe(preprocessed) + min_date, max_date = optimize.get_timeframe(data) # Validate dataframe for missing values - optimize.validate_backtest_data(preprocessed, min_date, max_date, + optimize.validate_backtest_data(data, min_date, max_date, constants.TICKER_INTERVAL_MINUTES[self.ticker_interval]) logger.info( 'Measuring data from %s up to %s (%s days)..', @@ -406,6 +404,8 @@ class Backtesting(object): max_date.isoformat(), (max_date - min_date).days ) + # need to reprocess data every time to populate signals + preprocessed = self.strategy.tickerdata_to_dataframe(data) # Execute backtest and print results all_results[self.strategy.get_strategy_name()] = self.backtest( From ef4555735a533f753bdf2b7feb813fa178024390 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 31 Dec 2018 19:13:34 +0100 Subject: [PATCH 062/358] Fill up missing as part of loading data --- freqtrade/data/converter.py | 11 +++++++++-- freqtrade/data/history.py | 9 ++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py index c1462e351..a75b1e997 100644 --- a/freqtrade/data/converter.py +++ b/freqtrade/data/converter.py @@ -10,10 +10,13 @@ from freqtrade.constants import TICKER_INTERVAL_MINUTES logger = logging.getLogger(__name__) -def parse_ticker_dataframe(ticker: list) -> DataFrame: +def parse_ticker_dataframe(ticker: list, ticker_interval: str, + fill_missing: bool = False) -> DataFrame: """ Converts a ticker-list (format ccxt.fetch_ohlcv) to a Dataframe :param ticker: ticker list, as returned by exchange.async_get_candle_history + :param ticker_interval: ticker_interval (e.g. 5m). Used to fill up eventual missing data + :param fill_missing: boolean :return: DataFrame """ logger.debug("Parsing tickerlist to dataframe") @@ -35,7 +38,11 @@ def parse_ticker_dataframe(ticker: list) -> DataFrame: }) frame.drop(frame.tail(1).index, inplace=True) # eliminate partial candle logger.debug('Dropping last candle') - return frame + + if fill_missing: + return ohlcv_fill_up_missing_data(frame, ticker_interval) + else: + return frame def ohlcv_fill_up_missing_data(dataframe: DataFrame, ticker_interval: str) -> DataFrame: diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index ae56aa6c7..475ce581d 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -82,6 +82,7 @@ def load_pair_history(pair: str, timerange: TimeRange = TimeRange(None, None, 0, 0), refresh_pairs: bool = False, exchange: Optional[Exchange] = None, + fill_up_missing: bool = True ) -> DataFrame: """ Loads cached ticker history for the given pair. @@ -110,7 +111,7 @@ def load_pair_history(pair: str, logger.warning('Missing data at end for pair %s, data ends at %s', pair, arrow.get(pairdata[-1][0] // 1000).strftime('%Y-%m-%d %H:%M:%S')) - return parse_ticker_dataframe(pairdata) + return parse_ticker_dataframe(pairdata, ticker_interval, fill_up_missing) else: logger.warning('No data for pair: "%s", Interval: %s. ' 'Use --refresh-pairs-cached to download the data', @@ -123,7 +124,8 @@ def load_data(datadir: Optional[Path], pairs: List[str], refresh_pairs: bool = False, exchange: Optional[Exchange] = None, - timerange: TimeRange = TimeRange(None, None, 0, 0)) -> Dict[str, DataFrame]: + timerange: TimeRange = TimeRange(None, None, 0, 0), + fill_up_missing: bool = True) -> Dict[str, DataFrame]: """ Loads ticker history data for a list of pairs the given parameters :return: dict(:) @@ -134,7 +136,8 @@ def load_data(datadir: Optional[Path], hist = load_pair_history(pair=pair, ticker_interval=ticker_interval, datadir=datadir, timerange=timerange, refresh_pairs=refresh_pairs, - exchange=exchange) + exchange=exchange, + fill_up_missing=fill_up_missing) if hist is not None: result[pair] = hist return result From fae875f58833300eea3ed454559752c117b761b4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 31 Dec 2018 19:15:05 +0100 Subject: [PATCH 063/358] Implement missing_data_fillup to tests and operations --- freqtrade/exchange/__init__.py | 2 +- freqtrade/optimize/backtesting.py | 3 +-- freqtrade/tests/conftest.py | 4 ++-- freqtrade/tests/data/test_converter.py | 12 ++++-------- freqtrade/tests/edge/test_edge.py | 4 ++-- freqtrade/tests/optimize/test_backtesting.py | 12 ++++++------ freqtrade/tests/optimize/test_hyperopt.py | 4 ++-- freqtrade/tests/optimize/test_optimize.py | 3 ++- freqtrade/tests/strategy/test_default_strategy.py | 2 +- freqtrade/tests/strategy/test_interface.py | 4 ++-- freqtrade/tests/test_misc.py | 4 ++-- 11 files changed, 25 insertions(+), 29 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 98522b98a..c74b32ad2 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -558,7 +558,7 @@ class Exchange(object): if ticks: self._pairs_last_refresh_time[pair] = ticks[-1][0] // 1000 # keeping parsed dataframe in cache - self._klines[pair] = parse_ticker_dataframe(ticks) + self._klines[pair] = parse_ticker_dataframe(ticks, tick_interval, fill_missing=True) return tickers @retrier_async diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 86b5bdd19..5ebb5e3a6 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -19,7 +19,6 @@ from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration from freqtrade.exchange import Exchange from freqtrade.data import history -from freqtrade.data.converter import ohlcv_fill_up_missing_data from freqtrade.misc import file_dump_json from freqtrade.persistence import Trade from freqtrade.resolvers import StrategyResolver @@ -395,7 +394,7 @@ class Backtesting(object): self._set_strategy(strat) min_date, max_date = optimize.get_timeframe(data) - # Validate dataframe for missing values + # Validate dataframe for missing values (mainly at start and end, as fillup is called) optimize.validate_backtest_data(data, min_date, max_date, constants.TICKER_INTERVAL_MINUTES[self.ticker_interval]) logger.info( diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 0a2633c7c..ba2e1e84e 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -542,7 +542,7 @@ def ticker_history_list(): @pytest.fixture def ticker_history(ticker_history_list): - return parse_ticker_dataframe(ticker_history_list) + return parse_ticker_dataframe(ticker_history_list, "5m", True) @pytest.fixture @@ -724,7 +724,7 @@ def tickers(): @pytest.fixture def result(): with open('freqtrade/tests/testdata/UNITTEST_BTC-1m.json') as data_file: - return parse_ticker_dataframe(json.load(data_file)) + return parse_ticker_dataframe(json.load(data_file), '1m', True) # FIX: # Create an fixture/function diff --git a/freqtrade/tests/data/test_converter.py b/freqtrade/tests/data/test_converter.py index aa4a161a7..8d732c70f 100644 --- a/freqtrade/tests/data/test_converter.py +++ b/freqtrade/tests/data/test_converter.py @@ -7,21 +7,16 @@ from freqtrade.optimize import validate_backtest_data, get_timeframe from freqtrade.tests.conftest import log_has -def test_dataframe_correct_length(result): - dataframe = parse_ticker_dataframe(result) - assert len(result.index) - 1 == len(dataframe.index) # last partial candle removed - - def test_dataframe_correct_columns(result): assert result.columns.tolist() == ['date', 'open', 'high', 'low', 'close', 'volume'] -def test_parse_ticker_dataframe(ticker_history, caplog): +def test_parse_ticker_dataframe(ticker_history_list, caplog): columns = ['date', 'open', 'high', 'low', 'close', 'volume'] caplog.set_level(logging.DEBUG) # Test file with BV data - dataframe = parse_ticker_dataframe(ticker_history) + dataframe = parse_ticker_dataframe(ticker_history_list, '5m', fill_missing=True) assert dataframe.columns.tolist() == columns assert log_has('Parsing tickerlist to dataframe', caplog.record_tuples) @@ -30,7 +25,8 @@ def test_ohlcv_fill_up_missing_data(caplog): data = load_pair_history(datadir=None, ticker_interval='1m', refresh_pairs=False, - pair='UNITTEST/BTC') + pair='UNITTEST/BTC', + fill_up_missing=False) caplog.set_level(logging.DEBUG) data2 = ohlcv_fill_up_missing_data(data, '1m') assert len(data2) > len(data) diff --git a/freqtrade/tests/edge/test_edge.py b/freqtrade/tests/edge/test_edge.py index 4fbe9c494..c1c1b49cd 100644 --- a/freqtrade/tests/edge/test_edge.py +++ b/freqtrade/tests/edge/test_edge.py @@ -281,8 +281,8 @@ def mocked_load_data(datadir, pairs=[], ticker_interval='0m', refresh_pairs=Fals 123.45 ] for x in range(0, 500)] - pairdata = {'NEO/BTC': parse_ticker_dataframe(ETHBTC), - 'LTC/BTC': parse_ticker_dataframe(LTCBTC)} + pairdata = {'NEO/BTC': parse_ticker_dataframe(ETHBTC, '1h', fill_missing=True), + 'LTC/BTC': parse_ticker_dataframe(LTCBTC, '1h', fill_missing=True)} return pairdata diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 8f7c80523..db044d8cd 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -75,7 +75,7 @@ def load_data_test(what): pair[x][5] # Keep old volume ] for x in range(0, datalen) ] - return {'UNITTEST/BTC': parse_ticker_dataframe(data)} + return {'UNITTEST/BTC': parse_ticker_dataframe(data, '1m', fill_missing=True)} def simple_backtest(config, contour, num_results, mocker) -> None: @@ -104,7 +104,7 @@ def simple_backtest(config, contour, num_results, mocker) -> None: def mocked_load_data(datadir, pairs=[], ticker_interval='0m', refresh_pairs=False, timerange=None, exchange=None): tickerdata = history.load_tickerdata_file(datadir, 'UNITTEST/BTC', '1m', timerange=timerange) - pairdata = {'UNITTEST/BTC': parse_ticker_dataframe(tickerdata)} + pairdata = {'UNITTEST/BTC': parse_ticker_dataframe(tickerdata, '1m', fill_missing=True)} return pairdata @@ -322,15 +322,15 @@ def test_backtesting_init(mocker, default_conf) -> None: assert backtesting.fee == 0.5 -def test_tickerdata_to_dataframe(default_conf, mocker) -> None: +def test_tickerdata_to_dataframe_bt(default_conf, mocker) -> None: patch_exchange(mocker) timerange = TimeRange(None, 'line', 0, -100) tick = history.load_tickerdata_file(None, 'UNITTEST/BTC', '1m', timerange=timerange) - tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick)} + tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick, '1m', fill_missing=True)} backtesting = Backtesting(default_conf) data = backtesting.strategy.tickerdata_to_dataframe(tickerlist) - assert len(data['UNITTEST/BTC']) == 99 + assert len(data['UNITTEST/BTC']) == 102 # Load strategy to compare the result between Backtesting function and strategy are the same strategy = DefaultStrategy(default_conf) @@ -593,7 +593,7 @@ def test_processed(default_conf, mocker) -> None: def test_backtest_pricecontours(default_conf, fee, mocker) -> None: mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - tests = [['raise', 18], ['lower', 0], ['sine', 19]] + tests = [['raise', 19], ['lower', 0], ['sine', 18]] # We need to enable sell-signal - otherwise it sells on ROI!! default_conf['experimental'] = {"use_sell_signal": True} diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index 5a40441bb..53d991c09 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -243,7 +243,7 @@ def test_has_space(hyperopt): def test_populate_indicators(hyperopt) -> None: tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m') - tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick)} + tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick, '1m', fill_missing=True)} dataframes = hyperopt.strategy.tickerdata_to_dataframe(tickerlist) dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'], {'pair': 'UNITTEST/BTC'}) @@ -256,7 +256,7 @@ def test_populate_indicators(hyperopt) -> None: def test_buy_strategy_generator(hyperopt) -> None: tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m') - tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick)} + tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick, '1m', fill_missing=True)} dataframes = hyperopt.strategy.tickerdata_to_dataframe(tickerlist) dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'], {'pair': 'UNITTEST/BTC'}) diff --git a/freqtrade/tests/optimize/test_optimize.py b/freqtrade/tests/optimize/test_optimize.py index 02d0e9c36..99cd24c26 100644 --- a/freqtrade/tests/optimize/test_optimize.py +++ b/freqtrade/tests/optimize/test_optimize.py @@ -30,7 +30,8 @@ def test_validate_backtest_data_warn(default_conf, mocker, caplog) -> None: history.load_data( datadir=None, ticker_interval='1m', - pairs=['UNITTEST/BTC'] + pairs=['UNITTEST/BTC'], + fill_up_missing=False ) ) min_date, max_date = optimize.get_timeframe(data) diff --git a/freqtrade/tests/strategy/test_default_strategy.py b/freqtrade/tests/strategy/test_default_strategy.py index 45ed54c4d..be514f2d1 100644 --- a/freqtrade/tests/strategy/test_default_strategy.py +++ b/freqtrade/tests/strategy/test_default_strategy.py @@ -10,7 +10,7 @@ from freqtrade.strategy.default_strategy import DefaultStrategy @pytest.fixture def result(): with open('freqtrade/tests/testdata/ETH_BTC-1m.json') as data_file: - return parse_ticker_dataframe(json.load(data_file)) + return parse_ticker_dataframe(json.load(data_file), '1m', fill_missing=True) def test_default_strategy_structure(): diff --git a/freqtrade/tests/strategy/test_interface.py b/freqtrade/tests/strategy/test_interface.py index 22ba9a2b6..32e6338bd 100644 --- a/freqtrade/tests/strategy/test_interface.py +++ b/freqtrade/tests/strategy/test_interface.py @@ -111,9 +111,9 @@ def test_tickerdata_to_dataframe(default_conf) -> None: timerange = TimeRange(None, 'line', 0, -100) tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m', timerange=timerange) - tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick)} + tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick, '1m', True)} data = strategy.tickerdata_to_dataframe(tickerlist) - assert len(data['UNITTEST/BTC']) == 99 # partial candle was removed + assert len(data['UNITTEST/BTC']) == 102 # partial candle was removed def test_min_roi_reached(default_conf, fee) -> None: diff --git a/freqtrade/tests/test_misc.py b/freqtrade/tests/test_misc.py index 33a59effd..c7ff1f077 100644 --- a/freqtrade/tests/test_misc.py +++ b/freqtrade/tests/test_misc.py @@ -17,7 +17,7 @@ def test_shorten_date() -> None: def test_datesarray_to_datetimearray(ticker_history_list): - dataframes = parse_ticker_dataframe(ticker_history_list) + dataframes = parse_ticker_dataframe(ticker_history_list, "5m", fill_missing=True) dates = datesarray_to_datetimearray(dataframes['date']) assert isinstance(dates[0], datetime.datetime) @@ -34,7 +34,7 @@ def test_datesarray_to_datetimearray(ticker_history_list): def test_common_datearray(default_conf) -> None: strategy = DefaultStrategy(default_conf) tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m') - tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick)} + tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick, "1m", fill_missing=True)} dataframes = strategy.tickerdata_to_dataframe(tickerlist) dates = common_datearray(dataframes) From dd1d3430b98601c5aeae8abb65e77aaa713d81bf Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 31 Dec 2018 19:40:14 +0100 Subject: [PATCH 064/358] Add explicit test for ohlcv fillup --- freqtrade/tests/data/test_converter.py | 57 ++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/freqtrade/tests/data/test_converter.py b/freqtrade/tests/data/test_converter.py index 8d732c70f..46d564003 100644 --- a/freqtrade/tests/data/test_converter.py +++ b/freqtrade/tests/data/test_converter.py @@ -40,3 +40,60 @@ def test_ohlcv_fill_up_missing_data(caplog): min_date, max_date = get_timeframe({'UNITTEST/BTC': data}) assert validate_backtest_data({'UNITTEST/BTC': data}, min_date, max_date, 1) assert not validate_backtest_data({'UNITTEST/BTC': data2}, min_date, max_date, 1) + + +def test_ohlcv_fill_up_missing_data2(caplog): + ticker_interval = '5m' + ticks = [[ + 1511686200000, # 8:50:00 + 8.794e-05, # open + 8.948e-05, # high + 8.794e-05, # low + 8.88e-05, # close + 2255, # volume (in quote currency) + ], + [ + 1511686500000, # 8:55:00 + 8.88e-05, + 8.942e-05, + 8.88e-05, + 8.893e-05, + 9911, + ], + [ + 1511687100000, # 9:05:00 + 8.891e-05, + 8.893e-05, + 8.875e-05, + 8.877e-05, + 2251 + ], + [ + 1511687400000, # 9:10:00 + 8.877e-05, + 8.883e-05, + 8.895e-05, + 8.817e-05, + 123551 + ] + ] + + # Generate test-data without filling missing + data = parse_ticker_dataframe(ticks, ticker_interval, fill_missing=False) + assert len(data) == 3 + caplog.set_level(logging.DEBUG) + data2 = ohlcv_fill_up_missing_data(data, ticker_interval) + assert len(data2) == 4 + # 3rd candle has been filled + row = data2.loc[2, :] + assert row['volume'] == 0 + # close shoult match close of previous candle + assert row['close'] == data.loc[1, 'close'] + assert row['open'] == row['close'] + assert row['high'] == row['close'] + assert row['low'] == row['close'] + # Column names should not change + assert (data.columns == data2.columns).all() + + assert log_has(f"Missing data fillup: before: {len(data)} - after: {len(data2)}", + caplog.record_tuples) From 672d115eca9077157088834c873c402abc8b06ed Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 31 Dec 2018 19:42:14 +0100 Subject: [PATCH 065/358] Change default value and add docstring --- freqtrade/data/converter.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py index a75b1e997..62e9a68f6 100644 --- a/freqtrade/data/converter.py +++ b/freqtrade/data/converter.py @@ -11,12 +11,13 @@ logger = logging.getLogger(__name__) def parse_ticker_dataframe(ticker: list, ticker_interval: str, - fill_missing: bool = False) -> DataFrame: + fill_missing: bool = True) -> DataFrame: """ Converts a ticker-list (format ccxt.fetch_ohlcv) to a Dataframe :param ticker: ticker list, as returned by exchange.async_get_candle_history :param ticker_interval: ticker_interval (e.g. 5m). Used to fill up eventual missing data - :param fill_missing: boolean + :param fill_missing: fill up missing candles with 0 candles + (see ohlcv_fill_up_missing_data for details) :return: DataFrame """ logger.debug("Parsing tickerlist to dataframe") From a909322f602224613bbfe515a0bcd70dd7282f3e Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Tue, 1 Jan 2019 13:34:06 +0100 Subject: [PATCH 066/358] Update ccxt from 1.18.87 to 1.18.89 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2498ab8e2..995f83eb3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.87 +ccxt==1.18.89 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.12.1 From c337a931c2ae37aa8f4148108325341cf06a2711 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 1 Jan 2019 13:42:30 +0100 Subject: [PATCH 067/358] Fix bug on --refresh-pairs-cached --- freqtrade/data/history.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index ae56aa6c7..49f2858aa 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -88,20 +88,21 @@ def load_pair_history(pair: str, :return: DataFrame with ohlcv data """ - pairdata = load_tickerdata_file(datadir, pair, ticker_interval, timerange=timerange) # If the user force the refresh of pairs if refresh_pairs: if not exchange: raise OperationalException("Exchange needs to be initialized when " "calling load_data with refresh_pairs=True") - logger.info('Download data for all pairs and store them in %s', datadir) + logger.info('Download data for pair and store them in %s', datadir) download_pair_history(datadir=datadir, exchange=exchange, pair=pair, tick_interval=ticker_interval, timerange=timerange) + pairdata = load_tickerdata_file(datadir, pair, ticker_interval, timerange=timerange) + if pairdata: if timerange.starttype == 'date' and pairdata[0][0] > timerange.startts * 1000: logger.warning('Missing data at start for pair %s, data starts at %s', From a54d8f0e1637e2476c3ae07be3e4e81fe59ff1d8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 1 Jan 2019 14:07:40 +0100 Subject: [PATCH 068/358] Create datadir when not exists --- freqtrade/configuration.py | 21 +++++++++++--------- freqtrade/tests/optimize/test_backtesting.py | 3 ++- freqtrade/tests/optimize/test_edge_cli.py | 3 ++- freqtrade/tests/test_configuration.py | 11 ++++++++++ 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/freqtrade/configuration.py b/freqtrade/configuration.py index 5c7da3413..ddcf99c1d 100644 --- a/freqtrade/configuration.py +++ b/freqtrade/configuration.py @@ -152,13 +152,16 @@ class Configuration(object): return config - def _create_default_datadir(self, config: Dict[str, Any]) -> str: - exchange_name = config.get('exchange', {}).get('name').lower() - default_path = os.path.join('user_data', 'data', exchange_name) - if not os.path.isdir(default_path): - os.makedirs(default_path) - logger.info(f'Created data directory: {default_path}') - return default_path + def _create_datadir(self, config: Dict[str, Any], datadir: Optional[str] = None) -> str: + if not datadir: + # set datadir + exchange_name = config.get('exchange', {}).get('name').lower() + datadir = os.path.join('user_data', 'data', exchange_name) + + if not os.path.isdir(datadir): + os.makedirs(datadir) + logger.info(f'Created data directory: {datadir}') + return datadir def _load_backtesting_config(self, config: Dict[str, Any]) -> Dict[str, Any]: """ @@ -198,9 +201,9 @@ class Configuration(object): # If --datadir is used we add it to the configuration if 'datadir' in self.args and self.args.datadir: - config.update({'datadir': self.args.datadir}) + config.update({'datadir': self._create_datadir(config, self.args.datadir)}) else: - config.update({'datadir': self._create_default_datadir(config)}) + config.update({'datadir': self._create_datadir(config, None)}) logger.info('Using data folder: %s ...', config.get('datadir')) # If -r/--refresh-pairs-cached is used we add it to the configuration diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 8f7c80523..e09781018 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -202,10 +202,11 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> assert 'export' not in config -def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> None: +def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> None: mocker.patch('freqtrade.configuration.open', mocker.mock_open( read_data=json.dumps(default_conf) )) + mocker.patch('freqtrade.configuration.Configuration._create_datadir', lambda s, c, x: x) args = [ '--config', 'config.json', diff --git a/freqtrade/tests/optimize/test_edge_cli.py b/freqtrade/tests/optimize/test_edge_cli.py index 0d0f64e0c..8ffab7f11 100644 --- a/freqtrade/tests/optimize/test_edge_cli.py +++ b/freqtrade/tests/optimize/test_edge_cli.py @@ -46,10 +46,11 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> assert 'stoploss_range' not in config -def test_setup_configuration_with_arguments(mocker, edge_conf, caplog) -> None: +def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> None: mocker.patch('freqtrade.configuration.open', mocker.mock_open( read_data=json.dumps(edge_conf) )) + mocker.patch('freqtrade.configuration.Configuration._create_datadir', lambda s, c, x: x) args = [ '--config', 'config.json', diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index be381770b..ac322c6fc 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -247,6 +247,7 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non mocker.patch('freqtrade.configuration.open', mocker.mock_open( read_data=json.dumps(default_conf) )) + mocker.patch('freqtrade.configuration.Configuration._create_datadir', lambda s, c, x: x) arglist = [ '--config', 'config.json', @@ -487,3 +488,13 @@ def test_load_config_warn_forcebuy(default_conf, mocker, caplog) -> None: def test_validate_default_conf(default_conf) -> None: validate(default_conf, constants.CONF_SCHEMA, Draft4Validator) + + +def test__create_datadir(mocker, default_conf, caplog) -> None: + mocker.patch('os.path.isdir', MagicMock(return_value=False)) + md = MagicMock() + mocker.patch('os.makedirs', md) + cfg = Configuration(Namespace()) + cfg._create_datadir(default_conf, '/foo/bar') + assert md.call_args[0][0] == "/foo/bar" + assert log_has('Created data directory: /foo/bar', caplog.record_tuples) From da6f1a3945259302cab765b53759be2f77e29367 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 1 Jan 2019 16:32:45 +0100 Subject: [PATCH 069/358] Sequence of minroi dict must be irrelevant --- freqtrade/tests/strategy/test_interface.py | 38 ++++++++++++---------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/freqtrade/tests/strategy/test_interface.py b/freqtrade/tests/strategy/test_interface.py index 22ba9a2b6..e7a44fe7e 100644 --- a/freqtrade/tests/strategy/test_interface.py +++ b/freqtrade/tests/strategy/test_interface.py @@ -117,26 +117,30 @@ def test_tickerdata_to_dataframe(default_conf) -> None: def test_min_roi_reached(default_conf, fee) -> None: - strategy = DefaultStrategy(default_conf) - strategy.minimal_roi = {0: 0.1, 20: 0.05, 55: 0.01} - trade = Trade( - pair='ETH/BTC', - stake_amount=0.001, - open_date=arrow.utcnow().shift(hours=-1).datetime, - fee_open=fee.return_value, - fee_close=fee.return_value, - exchange='bittrex', - open_rate=1, - ) - assert not strategy.min_roi_reached(trade, 0.01, arrow.utcnow().shift(minutes=-55).datetime) - assert strategy.min_roi_reached(trade, 0.12, arrow.utcnow().shift(minutes=-55).datetime) + min_roi_list = [{20: 0.05, 55: 0.01, 0: 0.1}, + {0: 0.1, 20: 0.05, 55: 0.01}] + for roi in min_roi_list: + strategy = DefaultStrategy(default_conf) + strategy.minimal_roi = roi + trade = Trade( + pair='ETH/BTC', + stake_amount=0.001, + open_date=arrow.utcnow().shift(hours=-1).datetime, + fee_open=fee.return_value, + fee_close=fee.return_value, + exchange='bittrex', + open_rate=1, + ) - assert not strategy.min_roi_reached(trade, 0.04, arrow.utcnow().shift(minutes=-39).datetime) - assert strategy.min_roi_reached(trade, 0.06, arrow.utcnow().shift(minutes=-39).datetime) + assert not strategy.min_roi_reached(trade, 0.02, arrow.utcnow().shift(minutes=-55).datetime) + assert strategy.min_roi_reached(trade, 0.12, arrow.utcnow().shift(minutes=-55).datetime) - assert not strategy.min_roi_reached(trade, -0.01, arrow.utcnow().shift(minutes=-1).datetime) - assert strategy.min_roi_reached(trade, 0.02, arrow.utcnow().shift(minutes=-1).datetime) + assert not strategy.min_roi_reached(trade, 0.04, arrow.utcnow().shift(minutes=-39).datetime) + assert strategy.min_roi_reached(trade, 0.06, arrow.utcnow().shift(minutes=-39).datetime) + + assert not strategy.min_roi_reached(trade, -0.01, arrow.utcnow().shift(minutes=-1).datetime) + assert strategy.min_roi_reached(trade, 0.02, arrow.utcnow().shift(minutes=-1).datetime) def test_analyze_ticker_default(ticker_history, mocker, caplog) -> None: From b55994cb71d07bb8d0cc952050dfeca0dd6fbe3f Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 1 Jan 2019 16:33:02 +0100 Subject: [PATCH 070/358] Clarify documentation --- docs/bot-optimization.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/bot-optimization.md b/docs/bot-optimization.md index 4ff0c6f24..1cfae1bc4 100644 --- a/docs/bot-optimization.md +++ b/docs/bot-optimization.md @@ -179,10 +179,9 @@ minimal_roi = { The above configuration would therefore mean: - Sell whenever 4% profit was reached -- Sell after 20 minutes when 2% profit was reached -- Sell after 20 minutes when 2% profit was reached -- Sell after 30 minutes when 1% profit was reached -- Sell after 40 minutes when the trade is non-loosing (no profit) +- Sell when 2% profit was reached (in effect after 20 minutes) +- Sell when 1% profit was reached (in effect after 30 minutes) +- Sell when trade is non-loosing (in effect after 40 minutes) The calculation does include fees. From 1d518885a95a79f86278393b01c44bd6c3d521fc Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 1 Jan 2019 16:45:52 +0100 Subject: [PATCH 071/358] fix roi-reached when list is unsorted --- freqtrade/strategy/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index a5c10b58c..2d4de2358 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -321,7 +321,7 @@ class IStrategy(ABC): time_diff = (current_time.timestamp() - trade.open_date.timestamp()) / 60 for duration, threshold in self.minimal_roi.items(): if time_diff <= duration: - return False + continue if current_profit > threshold: return True From 2bc76771bfb3dfbc4b51085ecb4e8071706e609e Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 1 Jan 2019 16:50:10 +0100 Subject: [PATCH 072/358] Align backtest to interface.py interface.py roi calculation skips on <= duration the correct selection is therefore trade_duration > x. --- freqtrade/optimize/backtesting.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index cc05c5de8..48d47d9c1 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -219,8 +219,9 @@ class Backtesting(object): # Set close_rate to stoploss closerate = trade.stop_loss elif sell.sell_type == (SellType.ROI): - # get entry in min_roi >= to trade duration - roi_entry = max(list(filter(lambda x: trade_dur >= x, + # get next entry in min_roi > to trade duration + # Interface.py skips on trade_duration <= duration + roi_entry = max(list(filter(lambda x: trade_dur > x, self.strategy.minimal_roi.keys()))) roi = self.strategy.minimal_roi[roi_entry] From 1b84aa82ebc18dafeb6877382411c4b736717512 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 1 Jan 2019 16:54:44 +0100 Subject: [PATCH 073/358] dont use 55 for regular check as that's a key in the dict --- freqtrade/tests/strategy/test_interface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/tests/strategy/test_interface.py b/freqtrade/tests/strategy/test_interface.py index e7a44fe7e..89d50d036 100644 --- a/freqtrade/tests/strategy/test_interface.py +++ b/freqtrade/tests/strategy/test_interface.py @@ -133,8 +133,8 @@ def test_min_roi_reached(default_conf, fee) -> None: open_rate=1, ) - assert not strategy.min_roi_reached(trade, 0.02, arrow.utcnow().shift(minutes=-55).datetime) - assert strategy.min_roi_reached(trade, 0.12, arrow.utcnow().shift(minutes=-55).datetime) + assert not strategy.min_roi_reached(trade, 0.02, arrow.utcnow().shift(minutes=-56).datetime) + assert strategy.min_roi_reached(trade, 0.12, arrow.utcnow().shift(minutes=-56).datetime) assert not strategy.min_roi_reached(trade, 0.04, arrow.utcnow().shift(minutes=-39).datetime) assert strategy.min_roi_reached(trade, 0.06, arrow.utcnow().shift(minutes=-39).datetime) From 848af3755e246afab8986683e6bec23e1ad0c53c Mon Sep 17 00:00:00 2001 From: Misagh Date: Wed, 2 Jan 2019 13:33:33 +0100 Subject: [PATCH 074/358] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0d0724d3a..dc34ebfb1 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Build Status](https://travis-ci.org/freqtrade/freqtrade.svg?branch=develop)](https://travis-ci.org/freqtrade/freqtrade) [![Coverage Status](https://coveralls.io/repos/github/freqtrade/freqtrade/badge.svg?branch=develop&service=github)](https://coveralls.io/github/freqtrade/freqtrade?branch=develop) +[![Documentation](https://readthedocs.org/projects/freqtrade/badge/)](https://www.freqtrade.io) [![Maintainability](https://api.codeclimate.com/v1/badges/5737e6d668200b7518ff/maintainability)](https://codeclimate.com/github/freqtrade/freqtrade/maintainability) Simple High frequency trading bot for crypto currencies designed to support multi exchanges and be controlled via Telegram. From 6cc6ce359b58de0f8006e18183d3d169c237bf7a Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 2 Jan 2019 13:34:06 +0100 Subject: [PATCH 075/358] Update ccxt from 1.18.89 to 1.18.94 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 995f83eb3..20f5ef08d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.89 +ccxt==1.18.94 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.12.1 From 516217b6cbff7054fd31dad104c2c6a5278c8b7b Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 2 Jan 2019 13:34:08 +0100 Subject: [PATCH 076/358] Stop loss should also be shown when trailing is active --- freqtrade/strategy/interface.py | 3 ++- freqtrade/tests/test_freqtradebot.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 2d4de2358..4b7061f18 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -278,7 +278,8 @@ class IStrategy(ABC): # evaluate if the stoploss was hit if self.stoploss is not None and trade.stop_loss >= current_rate: selltype = SellType.STOP_LOSS - if trailing_stop: + # Trailing stop (and rate did move) + if trailing_stop and trade.open_rate != trade.max_rate: selltype = SellType.TRAILING_STOP_LOSS logger.debug( f"HIT STOP: current price at {current_rate:.6f}, " diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 0b6a14112..2b14c586b 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -2066,6 +2066,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, markets, caplog, trade = Trade.query.first() trade.update(limit_buy_order) + trade.max_rate = trade.open_rate * 1.003 caplog.set_level(logging.DEBUG) # Sell as trailing-stop is reached assert freqtrade.handle_trade(trade) is True From 207daf084e79c084e2dcd2c479473eee4f49eada Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 2 Jan 2019 14:18:34 +0100 Subject: [PATCH 077/358] change description --- README.md | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index dc34ebfb1..8cbed64aa 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# freqtrade +# Freqtrade [![Build Status](https://travis-ci.org/freqtrade/freqtrade.svg?branch=develop)](https://travis-ci.org/freqtrade/freqtrade) [![Coverage Status](https://coveralls.io/repos/github/freqtrade/freqtrade/badge.svg?branch=develop&service=github)](https://coveralls.io/github/freqtrade/freqtrade?branch=develop) @@ -11,15 +11,15 @@ Simple High frequency trading bot for crypto currencies designed to support mult ## Disclaimer -This software is for educational purposes only. Do not risk money which -you are afraid to lose. USE THE SOFTWARE AT YOUR OWN RISK. THE AUTHORS -AND ALL AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR TRADING RESULTS. +This software is for educational purposes only. Do not risk money which +you are afraid to lose. USE THE SOFTWARE AT YOUR OWN RISK. THE AUTHORS +AND ALL AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR TRADING RESULTS. Always start by running a trading bot in Dry-run and do not engage money before you understand how it works and what profit/loss you should expect. -We strongly recommend you to have coding and Python knowledge. Do not +We strongly recommend you to have coding and Python knowledge. Do not hesitate to read the source code and understand the mechanism of this bot. ## Exchange marketplaces supported @@ -68,7 +68,7 @@ hesitate to read the source code and understand the mechanism of this bot. - [Software requirements](#software-requirements) - [Wanna help?](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md) - [Dev - getting started](https://github.com/freqtrade/freqtrade/blob/develop/docs/developer.md) (WIP) - + ## Quick start @@ -177,29 +177,29 @@ information about the bot, we encourage you to join our slack channel. ### [Bugs / Issues](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue) -If you discover a bug in the bot, please -[search our issue tracker](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue) -first. If it hasn't been reported, please -[create a new issue](https://github.com/freqtrade/freqtrade/issues/new) and -ensure you follow the template guide so that our team can assist you as +If you discover a bug in the bot, please +[search our issue tracker](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue) +first. If it hasn't been reported, please +[create a new issue](https://github.com/freqtrade/freqtrade/issues/new) and +ensure you follow the template guide so that our team can assist you as quickly as possible. ### [Feature Requests](https://github.com/freqtrade/freqtrade/labels/enhancement) Have you a great idea to improve the bot you want to share? Please, first search if this feature was not [already discussed](https://github.com/freqtrade/freqtrade/labels/enhancement). -If it hasn't been requested, please -[create a new request](https://github.com/freqtrade/freqtrade/issues/new) -and ensure you follow the template guide so that it does not get lost +If it hasn't been requested, please +[create a new request](https://github.com/freqtrade/freqtrade/issues/new) +and ensure you follow the template guide so that it does not get lost in the bug reports. ### [Pull Requests](https://github.com/freqtrade/freqtrade/pulls) Feel like our bot is missing a feature? We welcome your pull requests! -Please read our +Please read our [Contributing document](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md) -to understand the requirements before sending your pull-requests. +to understand the requirements before sending your pull-requests. Coding is not a neccessity to contribute - maybe start with improving our documentation? Issues labeled [good first issue](https://github.com/freqtrade/freqtrade/labels/good%20first%20issue) can be good first contributions, and will help get you familiar with the codebase. @@ -222,10 +222,9 @@ To run this bot we recommend you a cloud instance with a minimum of: ### Software requirements -- [Python 3.6.x](http://docs.python-guide.org/en/latest/starting/installation/) +- [Python 3.6.x](http://docs.python-guide.org/en/latest/starting/installation/) - [pip](https://pip.pypa.io/en/stable/installing/) - [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) - [TA-Lib](https://mrjbq7.github.io/ta-lib/install.html) - [virtualenv](https://virtualenv.pypa.io/en/stable/installation/) (Recommended) - [Docker](https://www.docker.com/products/docker) (Recommended) - From 68a9d1b2b848d461305eb3c0c689cdb951199034 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 2 Jan 2019 14:21:02 +0100 Subject: [PATCH 078/358] description --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8cbed64aa..708813f33 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Documentation](https://readthedocs.org/projects/freqtrade/badge/)](https://www.freqtrade.io) [![Maintainability](https://api.codeclimate.com/v1/badges/5737e6d668200b7518ff/maintainability)](https://codeclimate.com/github/freqtrade/freqtrade/maintainability) -Simple High frequency trading bot for crypto currencies designed to support multi exchanges and be controlled via Telegram. +Freqtrade is a free and open source crypto trading bot written in Python. It is designed to support multiple exchanges and be controlled via Telegram. ![freqtrade](https://raw.githubusercontent.com/freqtrade/freqtrade/develop/docs/assets/freqtrade-screenshot.png) From 05ce7787d69940be2850b812a473fe7220c5f676 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 2 Jan 2019 14:41:27 +0100 Subject: [PATCH 079/358] readme enriched --- README.md | 107 +++--------------------------------------------------- 1 file changed, 5 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index 708813f33..50114a41c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Documentation](https://readthedocs.org/projects/freqtrade/badge/)](https://www.freqtrade.io) [![Maintainability](https://api.codeclimate.com/v1/badges/5737e6d668200b7518ff/maintainability)](https://codeclimate.com/github/freqtrade/freqtrade/maintainability) -Freqtrade is a free and open source crypto trading bot written in Python. It is designed to support multiple exchanges and be controlled via Telegram. +Freqtrade is a free and open source crypto trading bot written in Python. It is designed to support all major exchanges and be controlled via Telegram. It contains backtesting, plotting and money management tools as well as strategy optimization by machine learning. ![freqtrade](https://raw.githubusercontent.com/freqtrade/freqtrade/develop/docs/assets/freqtrade-screenshot.png) @@ -22,53 +22,9 @@ expect. We strongly recommend you to have coding and Python knowledge. Do not hesitate to read the source code and understand the mechanism of this bot. -## Exchange marketplaces supported - -- [X] [Bittrex](https://bittrex.com/) -- [X] [Binance](https://www.binance.com/) ([*Note for binance users](#a-note-on-binance)) -- [ ] [113 others to tests](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_ - -## Features - -- [x] **Based on Python 3.6+**: For botting on any operating system - Windows, macOS and Linux -- [x] **Persistence**: Persistence is achieved through sqlite -- [x] **Dry-run**: Run the bot without playing money. -- [x] **Backtesting**: Run a simulation of your buy/sell strategy. -- [x] **Strategy Optimization by machine learning**: Use machine learning to optimize your buy/sell strategy parameters with real exchange data. -- [x] **Edge position sizing** Calculate your win rate, risk reward ratio, the best stoploss and adjust your position size before taking a position for each specific market. [Learn more](https://github.com/freqtrade/freqtrade/blob/develop/docs/edge.md) -- [x] **Whitelist crypto-currencies**: Select which crypto-currency you want to trade or use dynamic whitelists. -- [x] **Blacklist crypto-currencies**: Select which crypto-currency you want to avoid. -- [x] **Manageable via Telegram**: Manage the bot with Telegram -- [x] **Display profit/loss in fiat**: Display your profit/loss in 33 fiat. -- [x] **Daily summary of profit/loss**: Provide a daily summary of your profit/loss. -- [x] **Performance status report**: Provide a performance status of your current trades. - - -## Table of Contents - -- [Quick start](#quick-start) -- [Documentations](https://github.com/freqtrade/freqtrade/blob/develop/docs/index.md) - - [Installation](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md) - - [Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md) - - [Strategy Optimization](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md) - - [Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md) - - [Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md) - - [Sandbox Testing](https://github.com/freqtrade/freqtrade/blob/develop/docs/sandbox-testing.md) - - [Edge](https://github.com/freqtrade/freqtrade/blob/develop/docs/edge.md) -- [Basic Usage](#basic-usage) - - [Bot commands](#bot-commands) - - [Telegram RPC commands](#telegram-rpc-commands) -- [Support](#support) - - [Help](#help--slack) - - [Bugs](#bugs--issues) - - [Feature Requests](#feature-requests) - - [Pull Requests](#pull-requests) -- [Requirements](#requirements) - - [Min hardware required](#min-hardware-required) - - [Software requirements](#software-requirements) -- [Wanna help?](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md) - - [Dev - getting started](https://github.com/freqtrade/freqtrade/blob/develop/docs/developer.md) (WIP) +## Documentation +Please find the complete documentation on our [website](https://www.freqtrade.io). ## Quick start @@ -81,26 +37,11 @@ git checkout develop ./setup.sh --install ``` -_Windows installation is explained in [Installation doc](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md)_ +For any other type of installation please refer to [Installation doc](https://www.freqtrade.io/en/latest/installation/). -## Documentation - -We invite you to read the bot documentation to ensure you understand how the bot is working. - -- [Index](https://github.com/freqtrade/freqtrade/blob/develop/docs/index.md) -- [Installation](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md) -- [Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md) -- [Bot usage](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md) - - [How to run the bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#bot-commands) - - [How to use Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#backtesting-commands) - - [How to use Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#hyperopt-commands) -- [Strategy Optimization](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md) -- [Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md) -- [Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md) ## Basic Usage -### Bot commands ```bash usage: main.py [-h] [-v] [--version] [-c PATH] [-d PATH] [-s NAME] @@ -135,23 +76,6 @@ optional arguments: only if dry_run is enabled. ``` -### Telegram RPC commands - -Telegram is not mandatory. However, this is a great way to control your bot. More details on our [documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/index.md) - -- `/start`: Starts the trader -- `/stop`: Stops the trader -- `/status [table]`: Lists all open trades -- `/count`: Displays number of open trades -- `/profit`: Lists cumulative profit from all finished trades -- `/forcesell |all`: Instantly sells the given trade (Ignoring `minimum_roi`). -- `/performance`: Show performance of each finished trade grouped by pair -- `/balance`: Show account balance per currency -- `/daily `: Shows profit or loss per day, over the last n days -- `/help`: Show help message -- `/version`: Show version - - ## Development branches The project is currently setup in two main branches: @@ -206,25 +130,4 @@ Issues labeled [good first issue](https://github.com/freqtrade/freqtrade/labels/ **Note** before starting any major new feature work, *please open an issue describing what you are planning to do* or talk to us on [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE). This will ensure that interested parties can give valuable feedback on the feature, and let others know that you are working on it. -**Important:** Always create your PR against the `develop` branch, not `master`. - -## Requirements - -### Uptodate clock - -The clock must be accurate, syncronized to a NTP server very frequently to avoid problems with communication to the exchanges. - -### Min hardware required - -To run this bot we recommend you a cloud instance with a minimum of: - -- Minimal (advised) system requirements: 2GB RAM, 1GB disk space, 2vCPU - -### Software requirements - -- [Python 3.6.x](http://docs.python-guide.org/en/latest/starting/installation/) -- [pip](https://pip.pypa.io/en/stable/installing/) -- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -- [TA-Lib](https://mrjbq7.github.io/ta-lib/install.html) -- [virtualenv](https://virtualenv.pypa.io/en/stable/installation/) (Recommended) -- [Docker](https://www.docker.com/products/docker) (Recommended) +**Important:** Always create your PR against the `develop` branch, not `master`. \ No newline at end of file From 3329ffd071baf5b7c711082b3ffd2735d14dcecc Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 2 Jan 2019 14:44:17 +0100 Subject: [PATCH 080/358] improve comment --- freqtrade/strategy/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 4b7061f18..694430fd6 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -278,7 +278,7 @@ class IStrategy(ABC): # evaluate if the stoploss was hit if self.stoploss is not None and trade.stop_loss >= current_rate: selltype = SellType.STOP_LOSS - # Trailing stop (and rate did move) + # If Trailing stop (and max-rate did move above open rate) if trailing_stop and trade.open_rate != trade.max_rate: selltype = SellType.TRAILING_STOP_LOSS logger.debug( From 2c31fd662c80bd9ecac0541c7e91a0e505367d66 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 2 Jan 2019 19:26:58 +0100 Subject: [PATCH 081/358] cd build_helpers added before extracting Tar --- setup.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.sh b/setup.sh index b8e99e679..58aabd507 100755 --- a/setup.sh +++ b/setup.sh @@ -45,6 +45,7 @@ function updateenv () { # Install tab lib function install_talib () { + cd build_helpers tar zxvf ta-lib-0.4.0-src.tar.gz cd ta-lib sed -i.bak "s|0.00000001|0.000000000000000001 |g" src/ta_func/ta_utility.h @@ -52,6 +53,7 @@ function install_talib () { make sudo make install cd .. && rm -rf ./ta-lib/ + cd .. } # Install bot MacOS From de278a77d7527c9cc6fd58f52c0f69312234788e Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 3 Jan 2019 13:33:08 +0100 Subject: [PATCH 082/358] Update ccxt from 1.18.94 to 1.18.95 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 20f5ef08d..b80afba92 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.94 +ccxt==1.18.95 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.12.1 From afffa2f313bcc3a15dcb7c770ff7b7429a5ff15b Mon Sep 17 00:00:00 2001 From: misagh Date: Thu, 3 Jan 2019 14:38:38 +0100 Subject: [PATCH 083/358] =?UTF-8?q?changed=20to=20=E2=80=9CFree,=20open=20?= =?UTF-8?q?source=20=E2=80=A6=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 72 ++++++++++++++++++++++++++++++++++++++--------- docs/bot-usage.md | 2 +- freqtrade/main.py | 4 +-- setup.py | 2 +- 4 files changed, 62 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 50114a41c..e89edf76b 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,33 @@ expect. We strongly recommend you to have coding and Python knowledge. Do not hesitate to read the source code and understand the mechanism of this bot. +## Exchange marketplaces supported + +- [X] [Bittrex](https://bittrex.com/) +- [X] [Binance](https://www.binance.com/) ([*Note for binance users](#a-note-on-binance)) +- [ ] [113 others to tests](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_ + ## Documentation +We invite you to read the bot documentation to ensure you understand how the bot is working. + Please find the complete documentation on our [website](https://www.freqtrade.io). +## Features + +- [x] **Based on Python 3.6+**: For botting on any operating system - Windows, macOS and Linux. +- [x] **Persistence**: Persistence is achieved through sqlite. +- [x] **Dry-run**: Run the bot without playing money. +- [x] **Backtesting**: Run a simulation of your buy/sell strategy. +- [x] **Strategy Optimization by machine learning**: Use machine learning to optimize your buy/sell strategy parameters with real exchange data. +- [x] **Edge position sizing** Calculate your win rate, risk reward ratio, the best stoploss and adjust your position size before taking a position for each specific market. [Learn more](https://www.freqtrade.io/en/latest/edge/). +- [x] **Whitelist crypto-currencies**: Select which crypto-currency you want to trade or use dynamic whitelists. +- [x] **Blacklist crypto-currencies**: Select which crypto-currency you want to avoid. +- [x] **Manageable via Telegram**: Manage the bot with Telegram. +- [x] **Display profit/loss in fiat**: Display your profit/loss in 33 fiat. +- [x] **Daily summary of profit/loss**: Provide a daily summary of your profit/loss. +- [x] **Performance status report**: Provide a performance status of your current trades. + ## Quick start Freqtrade provides a Linux/macOS script to install all dependencies and help you to configure the bot. @@ -42,40 +65,61 @@ For any other type of installation please refer to [Installation doc](https://ww ## Basic Usage +### Bot commands -```bash +``` usage: main.py [-h] [-v] [--version] [-c PATH] [-d PATH] [-s NAME] - [--strategy-path PATH] [--dynamic-whitelist [INT]] - [--dry-run-db] - {backtesting,hyperopt} ... + [--strategy-path PATH] [--customhyperopt NAME] + [--dynamic-whitelist [INT]] [--db-url PATH] + {backtesting,edge,hyperopt} ... -Simple High Frequency Trading Bot for crypto currencies +Free, open source crypto trading bot positional arguments: - {backtesting,hyperopt} + {backtesting,edge,hyperopt} backtesting backtesting module + edge edge module hyperopt hyperopt module optional arguments: -h, --help show this help message and exit - -v, --verbose be verbose - --version show program's version number and exit + -v, --verbose verbose mode (-vv for more, -vvv to get all messages) + --version show program\'s version number and exit -c PATH, --config PATH specify configuration file (default: config.json) -d PATH, --datadir PATH - path to backtest data (default: - freqtrade/tests/testdata + path to backtest data -s NAME, --strategy NAME specify strategy class name (default: DefaultStrategy) --strategy-path PATH specify additional strategy lookup path + --customhyperopt NAME + specify hyperopt class name (default: + DefaultHyperOpts) --dynamic-whitelist [INT] dynamically generate and update whitelist based on 24h - BaseVolume (Default 20 currencies) - --dry-run-db Force dry run to use a local DB - "tradesv3.dry_run.sqlite" instead of memory DB. Work - only if dry_run is enabled. + BaseVolume (default: 20) DEPRECATED. + --db-url PATH Override trades database URL, this is useful if + dry_run is enabled or in custom deployments (default: + None) ``` +### Telegram RPC commands + +Telegram is not mandatory. However, this is a great way to control your bot. More details on our [documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/index.md) + +- `/start`: Starts the trader +- `/stop`: Stops the trader +- `/status [table]`: Lists all open trades +- `/count`: Displays number of open trades +- `/profit`: Lists cumulative profit from all finished trades +- `/forcesell |all`: Instantly sells the given trade (Ignoring `minimum_roi`). +- `/performance`: Show performance of each finished trade grouped by pair +- `/balance`: Show account balance per currency +- `/daily `: Shows profit or loss per day, over the last n days +- `/help`: Show help message +- `/version`: Show version + + ## Development branches The project is currently setup in two main branches: diff --git a/docs/bot-usage.md b/docs/bot-usage.md index bc5e7f0b4..96b16b6b6 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -11,7 +11,7 @@ usage: main.py [-h] [-v] [--version] [-c PATH] [-d PATH] [-s NAME] [--dynamic-whitelist [INT]] [--db-url PATH] {backtesting,edge,hyperopt} ... -Simple High Frequency Trading Bot for crypto currencies +Free, open source crypto trading bot positional arguments: {backtesting,edge,hyperopt} diff --git a/freqtrade/main.py b/freqtrade/main.py index 3ed478ec3..f27145b45 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -25,7 +25,7 @@ def main(sysargv: List[str]) -> None: """ arguments = Arguments( sysargv, - 'Simple High Frequency Trading Bot for crypto currencies' + 'Free, open source crypto trading bot' ) args = arguments.get_parsed_arg() @@ -45,7 +45,7 @@ def main(sysargv: List[str]) -> None: freqtrade = FreqtradeBot(config) state = None - while 1: + while True: state = freqtrade.worker(old_state=state) if state == State.RELOAD_CONF: freqtrade = reconfigure(freqtrade, args) diff --git a/setup.py b/setup.py index 11f957e67..35fdb2938 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ from freqtrade import __version__ setup(name='freqtrade', version=__version__, - description='Simple High Frequency Trading Bot for crypto currencies', + description='Crypto Trading Bot', url='https://github.com/freqtrade/freqtrade', author='gcarq and contributors', author_email='michael.egger@tsn.at', From f1bb4233c9464abd07035ff580f86766b99d5242 Mon Sep 17 00:00:00 2001 From: misagh Date: Thu, 3 Jan 2019 14:40:27 +0100 Subject: [PATCH 084/358] telegram documentation link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e89edf76b..d2b7c7374 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ optional arguments: ### Telegram RPC commands -Telegram is not mandatory. However, this is a great way to control your bot. More details on our [documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/index.md) +Telegram is not mandatory. However, this is a great way to control your bot. More details on our [documentation](https://www.freqtrade.io/en/latest/telegram-usage/) - `/start`: Starts the trader - `/stop`: Stops the trader From a5ec564fc3f1dba2b099c1905fd9609111b47e70 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 4 Jan 2019 13:33:09 +0100 Subject: [PATCH 085/358] Update ccxt from 1.18.95 to 1.18.97 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b80afba92..d2eeba08c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.95 +ccxt==1.18.97 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.12.1 From 67cbd5d77f33a364c95ab5d8f1cf223fbca2543d Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 4 Jan 2019 22:39:44 +0100 Subject: [PATCH 086/358] putting back requirements --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d2b7c7374..49b10e417 100644 --- a/README.md +++ b/README.md @@ -174,4 +174,25 @@ Issues labeled [good first issue](https://github.com/freqtrade/freqtrade/labels/ **Note** before starting any major new feature work, *please open an issue describing what you are planning to do* or talk to us on [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE). This will ensure that interested parties can give valuable feedback on the feature, and let others know that you are working on it. -**Important:** Always create your PR against the `develop` branch, not `master`. \ No newline at end of file +**Important:** Always create your PR against the `develop` branch, not `master`. + +## Requirements + +### Uptodate clock + +The clock must be accurate, syncronized to a NTP server very frequently to avoid problems with communication to the exchanges. + +### Min hardware required + +To run this bot we recommend you a cloud instance with a minimum of: + +- Minimal (advised) system requirements: 2GB RAM, 1GB disk space, 2vCPU + +### Software requirements + +- [Python 3.6.x](http://docs.python-guide.org/en/latest/starting/installation/) +- [pip](https://pip.pypa.io/en/stable/installing/) +- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +- [TA-Lib](https://mrjbq7.github.io/ta-lib/install.html) +- [virtualenv](https://virtualenv.pypa.io/en/stable/installation/) (Recommended) +- [Docker](https://www.docker.com/products/docker) (Recommended) \ No newline at end of file From 4599c80e79aeafbdf4ec0508a7dee2f3951af23e Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 07:10:25 +0100 Subject: [PATCH 087/358] Add trailing-stop to strategy --- freqtrade/resolvers/strategy_resolver.py | 27 ++++++++++++++++ freqtrade/strategy/interface.py | 5 +++ freqtrade/tests/strategy/test_strategy.py | 39 +++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index c1d967383..ee00b419b 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -56,6 +56,33 @@ class StrategyResolver(IResolver): else: config['stoploss'] = self.strategy.stoploss + if 'trailing_stop' in config: + self.strategy.trailing_stop = config['trailing_stop'] + logger.info( + "Override strategy 'trailing_stop' with value in config file: %s.", + config['trailing_stop'] + ) + elif hasattr(self.strategy, "trailing_stop"): + config['trailing_stop'] = self.strategy.trailing_stop + + if 'trailing_stop_positive' in config: + self.strategy.trailing_stop_positive = config['trailing_stop_positive'] + logger.info( + "Override strategy 'trailing_stop_positive' with value in config file: %s.", + config['trailing_stop_positive'] + ) + elif hasattr(self.strategy, "trailing_stop_positive"): + config['trailing_stop_positive'] = self.strategy.trailing_stop_positive + + if 'trailing_stop_positive_offset' in config: + self.strategy.trailing_stop_positive_offset = config['trailing_stop_positive_offset'] + logger.info( + "Override strategy 'trailing_stop_positive_offset' with value in config file: %s.", + config['trailing_stop_positive_offset'] + ) + elif hasattr(self.strategy, "trailing_stop_positive_offset"): + config['trailing_stop_positive_offset'] = self.strategy.trailing_stop_positive_offset + if 'ticker_interval' in config: self.strategy.ticker_interval = config['ticker_interval'] logger.info( diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 694430fd6..a6569ec19 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -67,6 +67,11 @@ class IStrategy(ABC): # associated stoploss stoploss: float + # trailing stoploss + trailing_stop: bool = False + trailing_stop_positive: float + trailing_stop_positive_offset: float + # associated ticker interval ticker_interval: str diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index 08d95fc5d..676ffc95b 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -150,6 +150,45 @@ def test_strategy_override_stoploss(caplog): ) in caplog.record_tuples +def test_strategy_override_trailing_stop(caplog): + caplog.set_level(logging.INFO) + config = { + 'strategy': 'DefaultStrategy', + 'trailing_stop': True + } + resolver = StrategyResolver(config) + + assert resolver.strategy.trailing_stop + assert isinstance(resolver.strategy.trailing_stop, bool) + assert ('freqtrade.resolvers.strategy_resolver', + logging.INFO, + "Override strategy 'trailing_stop' with value in config file: True." + ) in caplog.record_tuples + + +def test_strategy_override_trailing_stop_positive(caplog): + caplog.set_level(logging.INFO) + config = { + 'strategy': 'DefaultStrategy', + 'trailing_stop_positive': -0.1, + 'trailing_stop_positive_offset': -0.2 + + } + resolver = StrategyResolver(config) + + assert resolver.strategy.trailing_stop_positive == -0.1 + assert ('freqtrade.resolvers.strategy_resolver', + logging.INFO, + "Override strategy 'trailing_stop_positive' with value in config file: -0.1." + ) in caplog.record_tuples + + assert resolver.strategy.trailing_stop_positive_offset == -0.2 + assert ('freqtrade.resolvers.strategy_resolver', + logging.INFO, + "Override strategy 'trailing_stop_positive' with value in config file: -0.1." + ) in caplog.record_tuples + + def test_strategy_override_ticker_interval(caplog): caplog.set_level(logging.INFO) From 5e234420325469d145f3c79e2d078eac5f1a1c08 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 07:20:38 +0100 Subject: [PATCH 088/358] Simplify StrategyResolver by code deduplication --- freqtrade/resolvers/strategy_resolver.py | 104 +++++----------------- freqtrade/tests/strategy/test_strategy.py | 3 +- 2 files changed, 24 insertions(+), 83 deletions(-) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index ee00b419b..3be73f982 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -38,87 +38,35 @@ class StrategyResolver(IResolver): self.strategy: IStrategy = self._load_strategy(strategy_name, config=config, extra_dir=config.get('strategy_path')) - # Set attributes # Check if we need to override configuration - if 'minimal_roi' in config: - self.strategy.minimal_roi = config['minimal_roi'] - logger.info("Override strategy 'minimal_roi' with value in config file: %s.", - config['minimal_roi']) - else: - config['minimal_roi'] = self.strategy.minimal_roi + self._override_attribute_helper(config, "minimal_roi") + self._override_attribute_helper(config, "ticker_interval") + self._override_attribute_helper(config, "stoploss") + self._override_attribute_helper(config, "trailing_stop") + self._override_attribute_helper(config, "trailing_stop_positive") + self._override_attribute_helper(config, "trailing_stop_positive_offset") + self._override_attribute_helper(config, "process_only_new_candles") + self._override_attribute_helper(config, "order_types") + self._override_attribute_helper(config, "order_time_in_force") - if 'stoploss' in config: - self.strategy.stoploss = config['stoploss'] - logger.info( - "Override strategy 'stoploss' with value in config file: %s.", config['stoploss'] - ) - else: - config['stoploss'] = self.strategy.stoploss + # Sort and apply type conversions + self.strategy.minimal_roi = OrderedDict(sorted( + {int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(), + key=lambda t: t[0])) + self.strategy.stoploss = float(self.strategy.stoploss) - if 'trailing_stop' in config: - self.strategy.trailing_stop = config['trailing_stop'] - logger.info( - "Override strategy 'trailing_stop' with value in config file: %s.", - config['trailing_stop'] - ) - elif hasattr(self.strategy, "trailing_stop"): - config['trailing_stop'] = self.strategy.trailing_stop + self._strategy_sanity_validations() - if 'trailing_stop_positive' in config: - self.strategy.trailing_stop_positive = config['trailing_stop_positive'] - logger.info( - "Override strategy 'trailing_stop_positive' with value in config file: %s.", - config['trailing_stop_positive'] - ) - elif hasattr(self.strategy, "trailing_stop_positive"): - config['trailing_stop_positive'] = self.strategy.trailing_stop_positive - - if 'trailing_stop_positive_offset' in config: - self.strategy.trailing_stop_positive_offset = config['trailing_stop_positive_offset'] - logger.info( - "Override strategy 'trailing_stop_positive_offset' with value in config file: %s.", - config['trailing_stop_positive_offset'] - ) - elif hasattr(self.strategy, "trailing_stop_positive_offset"): - config['trailing_stop_positive_offset'] = self.strategy.trailing_stop_positive_offset - - if 'ticker_interval' in config: - self.strategy.ticker_interval = config['ticker_interval'] - logger.info( - "Override strategy 'ticker_interval' with value in config file: %s.", - config['ticker_interval'] - ) - else: - config['ticker_interval'] = self.strategy.ticker_interval - - if 'process_only_new_candles' in config: - self.strategy.process_only_new_candles = config['process_only_new_candles'] - logger.info( - "Override process_only_new_candles 'process_only_new_candles' " - "with value in config file: %s.", config['process_only_new_candles'] - ) - else: - config['process_only_new_candles'] = self.strategy.process_only_new_candles - - if 'order_types' in config: - self.strategy.order_types = config['order_types'] - logger.info( - "Override strategy 'order_types' with value in config file: %s.", - config['order_types'] - ) - else: - config['order_types'] = self.strategy.order_types - - if 'order_time_in_force' in config: - self.strategy.order_time_in_force = config['order_time_in_force'] - logger.info( - "Override strategy 'order_time_in_force' with value in config file: %s.", - config['order_time_in_force'] - ) - else: - config['order_time_in_force'] = self.strategy.order_time_in_force + def _override_attribute_helper(self, config, attribute: str): + if attribute in config: + setattr(self.strategy, attribute, config[attribute]) + logger.info("Override strategy '%s' with value in config file: %s.", + attribute, config[attribute]) + elif hasattr(self.strategy, attribute): + config[attribute] = getattr(self.strategy, attribute) + def _strategy_sanity_validations(self): if not all(k in self.strategy.order_types for k in constants.REQUIRED_ORDERTYPES): raise ImportError(f"Impossible to load Strategy '{self.strategy.__class__.__name__}'. " f"Order-types mapping is incomplete.") @@ -127,12 +75,6 @@ class StrategyResolver(IResolver): raise ImportError(f"Impossible to load Strategy '{self.strategy.__class__.__name__}'. " f"Order-time-in-force mapping is incomplete.") - # Sort and apply type conversions - self.strategy.minimal_roi = OrderedDict(sorted( - {int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(), - key=lambda t: t[0])) - self.strategy.stoploss = float(self.strategy.stoploss) - def _load_strategy( self, strategy_name: str, config: dict, extra_dir: Optional[str] = None) -> IStrategy: """ diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index 676ffc95b..b2315f381 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -217,8 +217,7 @@ def test_strategy_override_process_only_new_candles(caplog): assert resolver.strategy.process_only_new_candles assert ('freqtrade.resolvers.strategy_resolver', logging.INFO, - "Override process_only_new_candles 'process_only_new_candles' " - "with value in config file: True." + "Override strategy 'process_only_new_candles' with value in config file: True." ) in caplog.record_tuples From a7dc6b18aaeb27f0460af9555c277e83ac5c4be0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 07:22:19 +0100 Subject: [PATCH 089/358] Overridable attributs as list --- freqtrade/resolvers/strategy_resolver.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 3be73f982..d3972d0ba 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -40,15 +40,19 @@ class StrategyResolver(IResolver): extra_dir=config.get('strategy_path')) # Set attributes # Check if we need to override configuration - self._override_attribute_helper(config, "minimal_roi") - self._override_attribute_helper(config, "ticker_interval") - self._override_attribute_helper(config, "stoploss") - self._override_attribute_helper(config, "trailing_stop") - self._override_attribute_helper(config, "trailing_stop_positive") - self._override_attribute_helper(config, "trailing_stop_positive_offset") - self._override_attribute_helper(config, "process_only_new_candles") - self._override_attribute_helper(config, "order_types") - self._override_attribute_helper(config, "order_time_in_force") + attributes = ["minimal_roi", + "ticker_interval", + "stoploss", + "trailing_stop", + "trailing_stop_positive", + "trailing_stop_positive_offset", + "process_only_new_candles", + "order_types", + "order_time_in_force" + ] + for attribute in attributes: + self._override_attribute_helper(config, attribute) + # Sort and apply type conversions self.strategy.minimal_roi = OrderedDict(sorted( From 00c5ac56d438162a704a2e32d708014445b825a6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 07:24:15 +0100 Subject: [PATCH 090/358] Print startup strategy summary --- freqtrade/resolvers/strategy_resolver.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index d3972d0ba..35c02599a 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -52,6 +52,9 @@ class StrategyResolver(IResolver): ] for attribute in attributes: self._override_attribute_helper(config, attribute) + if attribute in config: + logger.info("Strategy using %s: %s", attribute, config[attribute]) + # Sort and apply type conversions From cacb9ef3add0bec62dfcf0ce6ceb9cb9a803f2a0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 07:25:35 +0100 Subject: [PATCH 091/358] Loop twice --- freqtrade/resolvers/strategy_resolver.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 35c02599a..15451a7d9 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -52,6 +52,9 @@ class StrategyResolver(IResolver): ] for attribute in attributes: self._override_attribute_helper(config, attribute) + + # Loop this list again to have output combined + for attribute in attributes: if attribute in config: logger.info("Strategy using %s: %s", attribute, config[attribute]) From f32232ba9602fb85b38a902aa256f0f192a0eb55 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 07:31:03 +0100 Subject: [PATCH 092/358] Add documentation for stoploss in strategy --- docs/configuration.md | 8 ++++---- docs/stoploss.md | 3 +++ freqtrade/resolvers/strategy_resolver.py | 2 -- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index eb58e4925..44db019d0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -19,10 +19,10 @@ The table below will list all configuration parameters. | `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode. | `process_only_new_candles` | false | No | If set to true indicators are processed only once a new candle arrives. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. Can be set either in Configuration or in the strategy. | `minimal_roi` | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. If set, this parameter will override `minimal_roi` from your strategy file. -| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. If set, this parameter will override `stoploss` from your strategy file. -| `trailing_stop` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file). -| `trailing_stop_positve` | 0 | No | Changes stop-loss once profit has been reached. -| `trailing_stop_positve_offset` | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. +| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. More details in the [stoploss documentation](stoploss.md). +| `trailing_stop` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file). More details in the [stoploss documentation](stoploss.md). +| `trailing_stop_positve` | 0 | No | Changes stop-loss once profit has been reached. More details in the [stoploss documentation](stoploss.md). +| `trailing_stop_positve_offset` | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. More details in the [stoploss documentation](stoploss.md). | `unfilledtimeout.buy` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled. | `unfilledtimeout.sell` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled. | `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. More information below. diff --git a/docs/stoploss.md b/docs/stoploss.md index f34050a85..0278e7bbb 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -6,6 +6,9 @@ At this stage the bot contains the following stoploss support modes: 2. trailing stop loss, defined in the configuration 3. trailing stop loss, custom positive loss, defined in configuration +!!! Note + All stoploss properties can be configured in eihter Strategy or configuration. Configuration values override strategy values. + ## Static Stop Loss This is very simple, basically you define a stop loss of x in your strategy file or alternative in the configuration, which diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 15451a7d9..0d1c9f9a0 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -58,8 +58,6 @@ class StrategyResolver(IResolver): if attribute in config: logger.info("Strategy using %s: %s", attribute, config[attribute]) - - # Sort and apply type conversions self.strategy.minimal_roi = OrderedDict(sorted( {int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(), From f7b96d839d644474321f5cca34b79aa28fc8a419 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 09:03:14 +0100 Subject: [PATCH 093/358] Add trailing options to sample-strategy --- user_data/strategies/test_strategy.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/user_data/strategies/test_strategy.py b/user_data/strategies/test_strategy.py index f72677e3d..048b398c7 100644 --- a/user_data/strategies/test_strategy.py +++ b/user_data/strategies/test_strategy.py @@ -42,6 +42,11 @@ class TestStrategy(IStrategy): # This attribute will be overridden if the config file contains "stoploss" stoploss = -0.10 + # trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.01 + trailing_stop_positive_offset = None # Disabled / not configured + # Optimal ticker interval for the strategy ticker_interval = '5m' From 8505ffbe78f7de9faa9d84dbdf2cf4acec9bc693 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 5 Jan 2019 13:33:06 +0100 Subject: [PATCH 094/358] Update ccxt from 1.18.97 to 1.18.101 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d2eeba08c..551b85456 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.97 +ccxt==1.18.101 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.12.1 From 16512d9918c60455f4cb8e109f6340f64b2ee911 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 5 Jan 2019 13:33:07 +0100 Subject: [PATCH 095/358] Update plotly from 3.4.2 to 3.5.0 --- requirements-plot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-plot.txt b/requirements-plot.txt index fa6827211..6da32b0f7 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,5 +1,5 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==3.4.2 +plotly==3.5.0 From dbf8ec6a20ffd5df1d8d7cd0cc0ed9735ada52d9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 19:24:07 +0100 Subject: [PATCH 096/358] Update dockerfile to python 3.7.2 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 24cce0049..ded74bd18 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.7.0-slim-stretch +FROM python:3.7.2-slim-stretch RUN apt-get update \ && apt-get -y install curl build-essential \ From 94aa1aaff3e47d320197ac3bd24e521411e844fc Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 19:34:45 +0100 Subject: [PATCH 097/358] fix typo, improve doc sequence --- mkdocs.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 07672884a..36428d1c1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -4,18 +4,18 @@ nav: - Installation: installation.md - Configuration: configuration.md - Start the bot: bot-usage.md - - Optimization: bot-optimization.md + - Stoploss: stoploss.md + - Custom Strategy: bot-optimization.md + - Telegram: telegram-usage.md + - Web Hook: webhook-config.md - Backtesting: backtesting.md - Hyperopt: hyperopt.md - - Stoploss: stoploss.md - - Telegram: telegram-usage.md - - Plotting: plotting.md - Edge positioning: edge.md - - Web Hook: webhook-config.md + - Plotting: plotting.md - FAQ: faq.md - - Contributors guide: developer.md - - SQL Cheatsheeet: sql_cheatsheet.md + - SQL Cheatsheet: sql_cheatsheet.md - Sanbox testing: sandbox-testing.md + - Contributors guide: developer.md theme: name: material logo: 'images/logo.png' @@ -40,4 +40,4 @@ markdown_extensions: - pymdownx.superfences - pymdownx.tasklist: custom_checkbox: true - - pymdownx.tilde \ No newline at end of file + - pymdownx.tilde From 31da42a485396d3440ca97d9de242a3828adc115 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 19:50:46 +0100 Subject: [PATCH 098/358] Show error when no python is found --- setup.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup.sh b/setup.sh index 58aabd507..11a36380c 100755 --- a/setup.sh +++ b/setup.sh @@ -17,6 +17,11 @@ function check_installed_python() { return fi + if [ -z ${PYTHON} ]; then + echo "No usable python found. Please make sure to have python3.6 or python3.7 installed" + exit 1 + fi + } function updateenv () { From 9e5e485d0a090de77200c781d9618e280357433d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 19:51:28 +0100 Subject: [PATCH 099/358] put --upgrade flag to the same location in subsequent requests --- setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.sh b/setup.sh index 11a36380c..f2ebb7009 100755 --- a/setup.sh +++ b/setup.sh @@ -31,7 +31,7 @@ function updateenv () { source .env/bin/activate echo "pip3 install in-progress. Please wait..." pip3 install --quiet --upgrade pip - pip3 install --quiet -r requirements.txt --upgrade + pip3 install --quiet --upgrade -r requirements.txt pip3 install --quiet -r requirements.txt read -p "Do you want to install dependencies for dev [Y/N]? " From 337ebdeccb02fc75bdd4b0c92ddf02129c84afee Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 19:51:51 +0100 Subject: [PATCH 100/358] Avoid installing ta-lib multiple times --- setup.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup.sh b/setup.sh index f2ebb7009..3ab013250 100755 --- a/setup.sh +++ b/setup.sh @@ -50,6 +50,11 @@ function updateenv () { # Install tab lib function install_talib () { + if [ -f /usr/local/lib/libta_lib.a ]; then + echo "ta-lib already installed, skipping" + return + fi + cd build_helpers tar zxvf ta-lib-0.4.0-src.tar.gz cd ta-lib From 01e2dc17b5c506da86373cee27aa98ac7e3bbd2d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 20:00:10 +0100 Subject: [PATCH 101/358] Remove whitespace in fucntion definition --- setup.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/setup.sh b/setup.sh index 3ab013250..9e969be2b 100755 --- a/setup.sh +++ b/setup.sh @@ -24,7 +24,7 @@ function check_installed_python() { } -function updateenv () { +function updateenv() { echo "-------------------------" echo "Update your virtual env" echo "-------------------------" @@ -49,7 +49,7 @@ function updateenv () { } # Install tab lib -function install_talib () { +function install_talib() { if [ -f /usr/local/lib/libta_lib.a ]; then echo "ta-lib already installed, skipping" return @@ -67,7 +67,7 @@ function install_talib () { } # Install bot MacOS -function install_macos () { +function install_macos() { if [ ! -x "$(command -v brew)" ] then echo "-------------------------" @@ -81,7 +81,7 @@ function install_macos () { } # Install bot Debian_ubuntu -function install_debian () { +function install_debian() { sudo add-apt-repository ppa:jonathonf/python-3.6 sudo apt-get update sudo apt-get install python3.6 python3.6-venv python3.6-dev build-essential autoconf libtool pkg-config make wget git @@ -89,13 +89,13 @@ function install_debian () { } # Upgrade the bot -function update () { +function update() { git pull updateenv } # Reset Develop or Master branch -function reset () { +function reset() { echo "----------------------------" echo "Reset branch and virtual env" echo "----------------------------" @@ -139,7 +139,7 @@ function test_and_fix_python_on_mac() { fi } -function config_generator () { +function config_generator() { echo "Starting to generate config.json" echo @@ -185,7 +185,7 @@ function config_generator () { } -function config () { +function config() { echo "-------------------------" echo "Config file generator" @@ -211,7 +211,7 @@ function config () { echo } -function install () { +function install() { echo "-------------------------" echo "Install mandatory dependencies" echo "-------------------------" @@ -239,7 +239,7 @@ function install () { echo "You can now use the bot by executing 'source .env/bin/activate; python freqtrade/main.py'." } -function plot () { +function plot() { echo " ----------------------------------------- Install dependencies for Plotting scripts @@ -248,7 +248,7 @@ Install dependencies for Plotting scripts pip install plotly --upgrade } -function help () { +function help() { echo "usage:" echo " -i,--install Install freqtrade from scratch" echo " -u,--update Command git pull to update." From f088f43b406613ad1fd9aff7c8db1909129a7c40 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 20:04:54 +0100 Subject: [PATCH 102/358] Install numpy before py_find_1st --- setup.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.sh b/setup.sh index 9e969be2b..55bd746e1 100755 --- a/setup.sh +++ b/setup.sh @@ -30,9 +30,9 @@ function updateenv() { echo "-------------------------" source .env/bin/activate echo "pip3 install in-progress. Please wait..." - pip3 install --quiet --upgrade pip + # Install numpy first to have py_find_1st install clean + pip3 install --quiet --upgrade pip numpy pip3 install --quiet --upgrade -r requirements.txt - pip3 install --quiet -r requirements.txt read -p "Do you want to install dependencies for dev [Y/N]? " if [[ $REPLY =~ ^[Yy]$ ]] From 506237e3b4973e84b7022657b441c6ba0e08d2ca Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 20:06:15 +0100 Subject: [PATCH 103/358] Don't use --quiet on pip install this hides errors from users and complicates debugging in case of problmes --- setup.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/setup.sh b/setup.sh index 55bd746e1..2005f4f82 100755 --- a/setup.sh +++ b/setup.sh @@ -31,14 +31,13 @@ function updateenv() { source .env/bin/activate echo "pip3 install in-progress. Please wait..." # Install numpy first to have py_find_1st install clean - pip3 install --quiet --upgrade pip numpy - pip3 install --quiet --upgrade -r requirements.txt + pip3 install --upgrade pip numpy + pip3 install --upgrade -r requirements.txt read -p "Do you want to install dependencies for dev [Y/N]? " if [[ $REPLY =~ ^[Yy]$ ]] then - pip3 install --quiet -r requirements-dev.txt --upgrade - pip3 install --quiet -r requirements-dev.txt + pip3 install --upgrade -r requirements-dev.txt else echo "Dev dependencies ignored." fi From a07353d3c72b03145b9753ca981e632081489317 Mon Sep 17 00:00:00 2001 From: Misagh Date: Sun, 6 Jan 2019 12:02:57 +0100 Subject: [PATCH 104/358] fixes few wordings --- setup.sh | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/setup.sh b/setup.sh index 2005f4f82..66d449037 100755 --- a/setup.sh +++ b/setup.sh @@ -26,7 +26,7 @@ function check_installed_python() { function updateenv() { echo "-------------------------" - echo "Update your virtual env" + echo "Updating your virtual env" echo "-------------------------" source .env/bin/activate echo "pip3 install in-progress. Please wait..." @@ -34,7 +34,7 @@ function updateenv() { pip3 install --upgrade pip numpy pip3 install --upgrade -r requirements.txt - read -p "Do you want to install dependencies for dev [Y/N]? " + read -p "Do you want to install dependencies for dev [y/N]? " if [[ $REPLY =~ ^[Yy]$ ]] then pip3 install --upgrade -r requirements-dev.txt @@ -70,7 +70,7 @@ function install_macos() { if [ ! -x "$(command -v brew)" ] then echo "-------------------------" - echo "Install Brew" + echo "Installing Brew" echo "-------------------------" /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" fi @@ -96,7 +96,7 @@ function update() { # Reset Develop or Master branch function reset() { echo "----------------------------" - echo "Reset branch and virtual env" + echo "Reseting branch and virtual env" echo "----------------------------" if [ "1" == $(git branch -vv |grep -cE "\* develop|\* master") ] then @@ -142,7 +142,7 @@ function config_generator() { echo "Starting to generate config.json" echo - echo "General configuration" + echo "Generating General configuration" echo "-------------------------" default_max_trades=3 read -p "Max open trades: (Default: $default_max_trades) " max_trades @@ -161,13 +161,13 @@ function config_generator() { fiat_currency=${fiat_currency:-$default_fiat_currency} echo - echo "Exchange config generator" + echo "Generating exchange config " echo "------------------------" read -p "Exchange API key: " api_key read -p "Exchange API Secret: " api_secret echo - echo "Telegram config generator" + echo "Generating Telegram config" echo "-------------------------" read -p "Telegram Token: " token read -p "Telegram Chat_id: " chat_id @@ -187,11 +187,11 @@ function config_generator() { function config() { echo "-------------------------" - echo "Config file generator" + echo "Generating config file" echo "-------------------------" if [ -f config.json ] then - read -p "A config file already exist, do you want to override it [Y/N]? " + read -p "A config file already exist, do you want to override it [y/N]? " if [[ $REPLY =~ ^[Yy]$ ]] then config_generator @@ -212,7 +212,7 @@ function config() { function install() { echo "-------------------------" - echo "Install mandatory dependencies" + echo "Installing mandatory dependencies" echo "-------------------------" if [ "$(uname -s)" == "Darwin" ] @@ -233,7 +233,7 @@ function install() { reset config echo "-------------------------" - echo "Run the bot" + echo "Run the bot !" echo "-------------------------" echo "You can now use the bot by executing 'source .env/bin/activate; python freqtrade/main.py'." } @@ -241,7 +241,7 @@ function install() { function plot() { echo " ----------------------------------------- -Install dependencies for Plotting scripts +Installing dependencies for Plotting scripts ----------------------------------------- " pip install plotly --upgrade From 68ba1e1f3741d248ba21ff05705000d47ca8f9b1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 6 Jan 2019 10:16:30 +0100 Subject: [PATCH 105/358] Add sell signal hyperopt --- freqtrade/arguments.py | 2 +- freqtrade/optimize/default_hyperopt.py | 61 ++++++++++++++++++++++++ freqtrade/optimize/hyperopt.py | 8 +++- freqtrade/optimize/hyperopt_interface.py | 14 ++++++ user_data/hyperopts/sample_hyperopt.py | 61 ++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 2 deletions(-) diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index 19cccfe8b..b86d3502a 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -272,7 +272,7 @@ class Arguments(object): '-s', '--spaces', help='Specify which parameters to hyperopt. Space separate list. \ Default: %(default)s', - choices=['all', 'buy', 'roi', 'stoploss'], + choices=['all', 'buy', 'sell', 'roi', 'stoploss'], default='all', nargs='+', dest='spaces', diff --git a/freqtrade/optimize/default_hyperopt.py b/freqtrade/optimize/default_hyperopt.py index 6139f8140..1c2d92950 100644 --- a/freqtrade/optimize/default_hyperopt.py +++ b/freqtrade/optimize/default_hyperopt.py @@ -93,6 +93,67 @@ class DefaultHyperOpts(IHyperOpt): Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger') ] + @staticmethod + def sell_strategy_generator(params: Dict[str, Any]) -> Callable: + """ + Define the sell strategy parameters to be used by hyperopt + """ + def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Sell strategy Hyperopt will build and use + """ + # print(params) + conditions = [] + # GUARDS AND TRENDS + if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']: + conditions.append(dataframe['mfi'] > params['sell-mfi-value']) + if 'sell-fastd-enabled' in params and params['sell-fastd-enabled']: + conditions.append(dataframe['fastd'] > params['sell-fastd-value']) + if 'sell-adx-enabled' in params and params['sell-adx-enabled']: + conditions.append(dataframe['adx'] > params['sell-adx-value']) + if 'sell-rsi-enabled' in params and params['sell-rsi-enabled']: + conditions.append(dataframe['rsi'] > params['sell-rsi-value']) + + # TRIGGERS + if 'sell-trigger' in params: + if params['sell-trigger'] == 'sell-bb_lower': + conditions.append(dataframe['close'] < dataframe['bb_lowerband']) + if params['sell-trigger'] == 'sell-macd_cross_signal': + conditions.append(qtpylib.crossed_above( + dataframe['macd'], dataframe['macdsignal'] + )) + if params['sell-trigger'] == 'sell-sar_reversal': + conditions.append(qtpylib.crossed_above( + dataframe['close'], dataframe['sar'] + )) + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'sell'] = 1 + + return dataframe + + return populate_sell_trend + + @staticmethod + def sell_indicator_space() -> List[Dimension]: + """ + Define your Hyperopt space for searching sell strategy parameters + """ + return [ + Integer(75, 100, name='sell-mfi-value'), + Integer(50, 100, name='sell-fastd-value'), + Integer(50, 100, name='sell-adx-value'), + Integer(60, 100, name='sell-rsi-value'), + Categorical([True, False], name='sell-mfi-enabled'), + Categorical([True, False], name='sell-fastd-enabled'), + Categorical([True, False], name='sell-adx-enabled'), + Categorical([True, False], name='sell-rsi-enabled'), + Categorical(['sell-bb_lower', + 'sell-macd_cross_signal', + 'sell-sar_reversal'], name='sell-trigger') + ] + @staticmethod def generate_roi_table(params: Dict) -> Dict[int, float]: """ diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 2d08fec81..674f7d3d1 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -151,6 +151,9 @@ class Hyperopt(Backtesting): spaces: List[Dimension] = [] if self.has_space('buy'): spaces += self.custom_hyperopt.indicator_space() + if self.has_space('sell'): + spaces += self.custom_hyperopt.sell_indicator_space() + self.config['experimental']['use_sell_signal'] = True if self.has_space('roi'): spaces += self.custom_hyperopt.roi_space() if self.has_space('stoploss'): @@ -165,6 +168,9 @@ class Hyperopt(Backtesting): if self.has_space('buy'): self.advise_buy = self.custom_hyperopt.buy_strategy_generator(params) + if self.has_space('sell'): + self.advise_sell = self.custom_hyperopt.sell_strategy_generator(params) + if self.has_space('stoploss'): self.strategy.stoploss = params['stoploss'] @@ -247,7 +253,7 @@ class Hyperopt(Backtesting): timerange=timerange ) - if self.has_space('buy'): + if self.has_space('buy') or self.has_space('sell'): self.strategy.advise_indicators = \ self.custom_hyperopt.populate_indicators # type: ignore dump(self.strategy.tickerdata_to_dataframe(data), TICKERDATA_PICKLE) diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index d42206658..622de3015 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -37,6 +37,13 @@ class IHyperOpt(ABC): Create a buy strategy generator """ + @staticmethod + @abstractmethod + def sell_strategy_generator(params: Dict[str, Any]) -> Callable: + """ + Create a sell strategy generator + """ + @staticmethod @abstractmethod def indicator_space() -> List[Dimension]: @@ -44,6 +51,13 @@ class IHyperOpt(ABC): Create an indicator space """ + @staticmethod + @abstractmethod + def sell_indicator_space() -> List[Dimension]: + """ + Create a sell indicator space + """ + @staticmethod @abstractmethod def generate_roi_table(params: Dict) -> Dict[int, float]: diff --git a/user_data/hyperopts/sample_hyperopt.py b/user_data/hyperopts/sample_hyperopt.py index f11236a82..ee4e4dca7 100644 --- a/user_data/hyperopts/sample_hyperopt.py +++ b/user_data/hyperopts/sample_hyperopt.py @@ -102,6 +102,67 @@ class SampleHyperOpts(IHyperOpt): Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger') ] + @staticmethod + def sell_strategy_generator(params: Dict[str, Any]) -> Callable: + """ + Define the sell strategy parameters to be used by hyperopt + """ + def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Sell strategy Hyperopt will build and use + """ + # print(params) + conditions = [] + # GUARDS AND TRENDS + if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']: + conditions.append(dataframe['mfi'] > params['sell-mfi-value']) + if 'sell-fastd-enabled' in params and params['sell-fastd-enabled']: + conditions.append(dataframe['fastd'] > params['sell-fastd-value']) + if 'sell-adx-enabled' in params and params['sell-adx-enabled']: + conditions.append(dataframe['adx'] > params['sell-adx-value']) + if 'sell-rsi-enabled' in params and params['sell-rsi-enabled']: + conditions.append(dataframe['rsi'] > params['sell-rsi-value']) + + # TRIGGERS + if 'sell-trigger' in params: + if params['sell-trigger'] == 'sell-bb_lower': + conditions.append(dataframe['close'] < dataframe['bb_lowerband']) + if params['sell-trigger'] == 'sell-macd_cross_signal': + conditions.append(qtpylib.crossed_above( + dataframe['macd'], dataframe['macdsignal'] + )) + if params['sell-trigger'] == 'sell-sar_reversal': + conditions.append(qtpylib.crossed_above( + dataframe['close'], dataframe['sar'] + )) + + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'sell'] = 1 + + return dataframe + + return populate_sell_trend + + @staticmethod + def sell_indicator_space() -> List[Dimension]: + """ + Define your Hyperopt space for searching sell strategy parameters + """ + return [ + Integer(75, 100, name='sell-mfi-value'), + Integer(50, 100, name='sell-fastd-value'), + Integer(50, 100, name='sell-adx-value'), + Integer(60, 100, name='sell-rsi-value'), + Categorical([True, False], name='sell-mfi-enabled'), + Categorical([True, False], name='sell-fastd-enabled'), + Categorical([True, False], name='sell-adx-enabled'), + Categorical([True, False], name='sell-rsi-enabled'), + Categorical(['sell-bb_lower', + 'sell-macd_cross_signal', + 'sell-sar_reversal'], name='sell-trigger') + ] + @staticmethod def generate_roi_table(params: Dict) -> Dict[int, float]: """ From 5e08769366a298517a56249806dc3c61af7ab930 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 6 Jan 2019 10:27:23 +0100 Subject: [PATCH 106/358] Update hyperopt documentation with sell-stuff --- docs/hyperopt.md | 47 +++++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 58dc91e3a..06b90cff9 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -11,30 +11,29 @@ and still take a long time. ## Prepare Hyperopting -Before we start digging in Hyperopt, we recommend you to take a look at +Before we start digging into Hyperopt, we recommend you to take a look at an example hyperopt file located into [user_data/hyperopts/](https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/test_hyperopt.py) -### 1. Install a Custom Hyperopt File -This is very simple. Put your hyperopt file into the folder -`user_data/hyperopts`. +Configuring hyperopt is similar to writing your own strategy, and many tasks will be similar and a lot of code can be copied across from the strategy. -Let assume you want a hyperopt file `awesome_hyperopt.py`:
+### 1. Install a Custom Hyperopt File + +Put your hyperopt file into the folder`user_data/hyperopts`. + +Let assume you want a hyperopt file `awesome_hyperopt.py`: Copy the file `user_data/hyperopts/sample_hyperopt.py` into `user_data/hyperopts/awesome_hyperopt.py` - ### 2. Configure your Guards and Triggers -There are two places you need to change in your hyperopt file to add a -new buy hyperopt for testing: -- Inside [populate_buy_trend()](https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/test_hyperopt.py#L230-L251). -- Inside [indicator_space()](https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/test_hyperopt.py#L207-L223). + +There are two places you need to change in your hyperopt file to add a new buy hyperopt for testing: + +- Inside `indicator_space()` - the parameters hyperopt shall be optimizing. +- Inside `populate_buy_trend()` - applying the parameters. There you have two different types of indicators: 1. `guards` and 2. `triggers`. -1. Guards are conditions like "never buy if ADX < 10", or never buy if -current price is over EMA10. -2. Triggers are ones that actually trigger buy in specific moment, like -"buy when EMA5 crosses over EMA10" or "buy when close price touches lower -bollinger band". +1. Guards are conditions like "never buy if ADX < 10", or never buy if current price is over EMA10. +2. Triggers are ones that actually trigger buy in specific moment, like "buy when EMA5 crosses over EMA10" or "buy when close price touches lower bollinger band". Hyperoptimization will, for each eval round, pick one trigger and possibly multiple guards. The constructed strategy will be something like @@ -45,6 +44,17 @@ If you have updated the buy strategy, ie. changed the contents of `populate_buy_trend()` method you have to update the `guards` and `triggers` hyperopts must use. +#### Sell optimization + +Similar to the buy-signal above, sell-signals can also be optimized. +Place the corresponding settings into the following methods + +- Inside `sell_indicator_space()` - the parameters hyperopt shall be optimizing. +- Inside `populate_sell_trend()` - applying the parameters. + +The configuration and rules are the same than for buy signals. +To avoid naming collisions in the search-space, please prefix all sell-spaces with sell-. + ## Solving a Mystery Let's say you are curious: should you use MACD crossings or lower Bollinger @@ -125,11 +135,10 @@ Because hyperopt tries a lot of combinations to find the best parameters it will We strongly recommend to use `screen` or `tmux` to prevent any connection loss. ```bash -python3 ./freqtrade/main.py -s --hyperopt -c config.json hyperopt -e 5000 +python3 ./freqtrade/main.py --hyperopt -c config.json hyperopt -e 5000 ``` -Use `` and `` as the names of the custom strategy -(only required for generating sells) and the custom hyperopt used. +Use `` as the name of the custom hyperopt used. The `-e` flag will set how many evaluations hyperopt will do. We recommend running at least several thousand evaluations. @@ -162,6 +171,7 @@ Legal values are: - `all`: optimize everything - `buy`: just search for a new buy strategy +- `sell`: just search for a new sell strategy - `roi`: just optimize the minimal profit table for your strategy - `stoploss`: search for the best stoploss value - space-separated list of any of the above values for example `--spaces roi stoploss` @@ -237,6 +247,7 @@ Once the optimized strategy has been implemented into your strategy, you should To archive the same results (number of trades, ...) than during hyperopt, please use the command line flag `--disable-max-market-positions`. This setting is the default for hyperopt for speed reasons. You can overwrite this in the configuration by setting `"position_stacking"=false` or by changing the relevant line in your hyperopt file [here](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/optimize/hyperopt.py#L283). +!!! Note: Dry/live runs will **NOT** use position stacking - therefore it does make sense to also validate the strategy without this as it's closer to reality. ## Next Step From 798ae460d8f8c91435f4e0716c50365cdf11b479 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 6 Jan 2019 10:30:58 +0100 Subject: [PATCH 107/358] Add check if trigger is in parameters --- docs/hyperopt.md | 13 +++++++------ freqtrade/optimize/default_hyperopt.py | 21 +++++++++++---------- user_data/hyperopts/sample_hyperopt.py | 21 +++++++++++---------- 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 06b90cff9..c4dd76009 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -98,12 +98,13 @@ So let's write the buy strategy using these values: conditions.append(dataframe['rsi'] < params['rsi-value']) # TRIGGERS - if params['trigger'] == 'bb_lower': - conditions.append(dataframe['close'] < dataframe['bb_lowerband']) - if params['trigger'] == 'macd_cross_signal': - conditions.append(qtpylib.crossed_above( - dataframe['macd'], dataframe['macdsignal'] - )) + if 'trigger' in params: + if params['trigger'] == 'bb_lower': + conditions.append(dataframe['close'] < dataframe['bb_lowerband']) + if params['trigger'] == 'macd_cross_signal': + conditions.append(qtpylib.crossed_above( + dataframe['macd'], dataframe['macdsignal'] + )) dataframe.loc[ reduce(lambda x, y: x & y, conditions), diff --git a/freqtrade/optimize/default_hyperopt.py b/freqtrade/optimize/default_hyperopt.py index 1c2d92950..315a1e8a5 100644 --- a/freqtrade/optimize/default_hyperopt.py +++ b/freqtrade/optimize/default_hyperopt.py @@ -57,16 +57,17 @@ class DefaultHyperOpts(IHyperOpt): conditions.append(dataframe['rsi'] < params['rsi-value']) # TRIGGERS - if params['trigger'] == 'bb_lower': - conditions.append(dataframe['close'] < dataframe['bb_lowerband']) - if params['trigger'] == 'macd_cross_signal': - conditions.append(qtpylib.crossed_above( - dataframe['macd'], dataframe['macdsignal'] - )) - if params['trigger'] == 'sar_reversal': - conditions.append(qtpylib.crossed_above( - dataframe['close'], dataframe['sar'] - )) + if 'trigger' in params: + if params['trigger'] == 'bb_lower': + conditions.append(dataframe['close'] < dataframe['bb_lowerband']) + if params['trigger'] == 'macd_cross_signal': + conditions.append(qtpylib.crossed_above( + dataframe['macd'], dataframe['macdsignal'] + )) + if params['trigger'] == 'sar_reversal': + conditions.append(qtpylib.crossed_above( + dataframe['close'], dataframe['sar'] + )) dataframe.loc[ reduce(lambda x, y: x & y, conditions), diff --git a/user_data/hyperopts/sample_hyperopt.py b/user_data/hyperopts/sample_hyperopt.py index ee4e4dca7..1700af932 100644 --- a/user_data/hyperopts/sample_hyperopt.py +++ b/user_data/hyperopts/sample_hyperopt.py @@ -66,16 +66,17 @@ class SampleHyperOpts(IHyperOpt): conditions.append(dataframe['rsi'] < params['rsi-value']) # TRIGGERS - if params['trigger'] == 'bb_lower': - conditions.append(dataframe['close'] < dataframe['bb_lowerband']) - if params['trigger'] == 'macd_cross_signal': - conditions.append(qtpylib.crossed_above( - dataframe['macd'], dataframe['macdsignal'] - )) - if params['trigger'] == 'sar_reversal': - conditions.append(qtpylib.crossed_above( - dataframe['close'], dataframe['sar'] - )) + if 'trigger' in params: + if params['trigger'] == 'bb_lower': + conditions.append(dataframe['close'] < dataframe['bb_lowerband']) + if params['trigger'] == 'macd_cross_signal': + conditions.append(qtpylib.crossed_above( + dataframe['macd'], dataframe['macdsignal'] + )) + if params['trigger'] == 'sar_reversal': + conditions.append(qtpylib.crossed_above( + dataframe['close'], dataframe['sar'] + )) dataframe.loc[ reduce(lambda x, y: x & y, conditions), From 2147bd88470dba08b047f76ef6c50f0281a11000 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 6 Jan 2019 10:35:18 +0100 Subject: [PATCH 108/358] Fix problem when no experimental dict is available --- freqtrade/optimize/hyperopt.py | 3 +++ freqtrade/tests/optimize/test_hyperopt.py | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 674f7d3d1..ba9c186da 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -153,6 +153,9 @@ class Hyperopt(Backtesting): spaces += self.custom_hyperopt.indicator_space() if self.has_space('sell'): spaces += self.custom_hyperopt.sell_indicator_space() + # Make sure experimental is enabled + if 'experimental' not in self.config: + self.config['experimental'] = {} self.config['experimental']['use_sell_signal'] = True if self.has_space('roi'): spaces += self.custom_hyperopt.roi_space() diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index 53d991c09..5c6d3981a 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -312,6 +312,15 @@ def test_generate_optimizer(mocker, default_conf) -> None: 'mfi-enabled': False, 'rsi-enabled': False, 'trigger': 'macd_cross_signal', + 'sell-adx-value': 0, + 'sell-fastd-value': 75, + 'sell-mfi-value': 0, + 'sell-rsi-value': 0, + 'sell-adx-enabled': False, + 'sell-fastd-enabled': True, + 'sell-mfi-enabled': False, + 'sell-rsi-enabled': False, + 'sell-trigger': 'macd_cross_signal', 'roi_t1': 60.0, 'roi_t2': 30.0, 'roi_t3': 20.0, From 3f82dd05aa85def6b9e3559b763a767531b2cf95 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sun, 6 Jan 2019 13:33:06 +0100 Subject: [PATCH 109/358] Update ccxt from 1.18.101 to 1.18.102 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 551b85456..b07c450f3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.101 +ccxt==1.18.102 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.12.1 From 13800701ce4612d7198e43da0ffbc8c26e57941b Mon Sep 17 00:00:00 2001 From: Gianluca Puglia Date: Sun, 6 Jan 2019 13:47:36 +0100 Subject: [PATCH 110/358] Fix custom db_url ignored if provided by conf.json --- docs/configuration.md | 2 +- freqtrade/configuration.py | 3 -- freqtrade/tests/test_configuration.py | 40 +++++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 44db019d0..a7deaa60c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -223,7 +223,7 @@ creating trades. ```json "dry_run": true, -"db_url": "sqlite///tradesv3.dryrun.sqlite", +"db_url": "sqlite:///tradesv3.dryrun.sqlite", ``` 3. Remove your Exchange API key (change them by fake api credentials) diff --git a/freqtrade/configuration.py b/freqtrade/configuration.py index ddcf99c1d..9fd93629f 100644 --- a/freqtrade/configuration.py +++ b/freqtrade/configuration.py @@ -124,9 +124,6 @@ class Configuration(object): if self.args.db_url and self.args.db_url != constants.DEFAULT_DB_PROD_URL: config.update({'db_url': self.args.db_url}) logger.info('Parameter --db-url detected ...') - else: - # Set default here - config.update({'db_url': constants.DEFAULT_DB_PROD_URL}) if config.get('dry_run', False): logger.info('Dry run is enabled') diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index ac322c6fc..f5c887089 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -73,7 +73,6 @@ def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) -> args = Arguments([], '').get_parsed_arg() configuration = Configuration(args) validated_conf = configuration.load_config() - print(validated_conf) assert validated_conf['max_open_trades'] > 999999999 assert validated_conf['max_open_trades'] == float('inf') @@ -125,6 +124,43 @@ def test_load_config_with_params(default_conf, mocker) -> None: assert validated_conf.get('strategy_path') == '/some/path' assert validated_conf.get('db_url') == 'sqlite:///someurl' + # Test conf provided db_url prod + conf = default_conf.copy() + conf["dry_run"] = False + conf["db_url"] = "sqlite:///path/to/db.sqlite" + mocker.patch('freqtrade.configuration.open', mocker.mock_open( + read_data=json.dumps(conf) + )) + + arglist = [ + '--strategy', 'TestStrategy', + '--strategy-path', '/some/path' + ] + args = Arguments(arglist, '').get_parsed_arg() + + configuration = Configuration(args) + validated_conf = configuration.load_config() + assert validated_conf.get('db_url') == "sqlite:///path/to/db.sqlite" + + # Test conf provided db_url dry_run + conf = default_conf.copy() + conf["dry_run"] = True + conf["db_url"] = "sqlite:///path/to/db.sqlite" + mocker.patch('freqtrade.configuration.open', mocker.mock_open( + read_data=json.dumps(conf) + )) + + arglist = [ + '--strategy', 'TestStrategy', + '--strategy-path', '/some/path' + ] + args = Arguments(arglist, '').get_parsed_arg() + + configuration = Configuration(args) + validated_conf = configuration.load_config() + assert validated_conf.get('db_url') == "sqlite:///path/to/db.sqlite" + + # Test args provided db_url prod conf = default_conf.copy() conf["dry_run"] = False del conf["db_url"] @@ -142,7 +178,7 @@ def test_load_config_with_params(default_conf, mocker) -> None: validated_conf = configuration.load_config() assert validated_conf.get('db_url') == DEFAULT_DB_PROD_URL - # Test dry=run with ProdURL + # Test args provided db_url dry_run conf = default_conf.copy() conf["dry_run"] = True conf["db_url"] = DEFAULT_DB_PROD_URL From 87cbff5d0e99f896e92ce2e74203446b3c6fb7cf Mon Sep 17 00:00:00 2001 From: Gianluca Puglia Date: Sun, 6 Jan 2019 13:48:27 +0100 Subject: [PATCH 111/358] Fix warning for max_open_trades when edge is enabled --- freqtrade/edge/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/edge/__init__.py b/freqtrade/edge/__init__.py index 8ccfc90de..76af703aa 100644 --- a/freqtrade/edge/__init__.py +++ b/freqtrade/edge/__init__.py @@ -59,7 +59,7 @@ class Edge(): # checking max_open_trades. it should be -1 as with Edge # the number of trades is determined by position size - if self.config['max_open_trades'] != -1: + if self.config['max_open_trades'] != float('inf'): logger.critical('max_open_trades should be -1 in config !') if self.config['stake_amount'] != constants.UNLIMITED_STAKE_AMOUNT: From a0df7b9d7cfa6e5e8d5c841724e5b1a0fcc661e6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 6 Jan 2019 14:12:55 +0100 Subject: [PATCH 112/358] Use sell/buy trends from hyperopt file if available --- freqtrade/optimize/hyperopt.py | 4 ++++ freqtrade/resolvers/hyperopt_resolver.py | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index ba9c186da..b8b8aaf56 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -170,9 +170,13 @@ class Hyperopt(Backtesting): if self.has_space('buy'): self.advise_buy = self.custom_hyperopt.buy_strategy_generator(params) + elif hasattr(self.custom_hyperopt, 'populate_buy_trend'): + self.advise_buy = self.custom_hyperopt.populate_buy_trend if self.has_space('sell'): self.advise_sell = self.custom_hyperopt.sell_strategy_generator(params) + elif hasattr(self.custom_hyperopt, 'populate_sell_trend'): + self.advise_sell = self.custom_hyperopt.populate_sell_trend if self.has_space('stoploss'): self.strategy.stoploss = params['stoploss'] diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index eb91c0e89..6bf7fa17d 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -32,6 +32,13 @@ class HyperOptResolver(IResolver): hyperopt_name = config.get('hyperopt') or DEFAULT_HYPEROPT self.hyperopt = self._load_hyperopt(hyperopt_name, extra_dir=config.get('hyperopt_path')) + if not hasattr(self.hyperopt, 'populate_buy_trend'): + logger.warning("Custom Hyperopt does not provide populate_buy_trend. " + "Using populate_buy_trend from DefaultStrategy.") + if not hasattr(self.hyperopt, 'populate_sell_trend'): + logger.warning("Custom Hyperopt does not provide populate_sell_trend. " + "Using populate_sell_trend from DefaultStrategy.") + def _load_hyperopt( self, hyperopt_name: str, extra_dir: Optional[str] = None) -> IHyperOpt: """ From 167088827adbd03f7799afebe62d6f80f3c7207c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 6 Jan 2019 14:13:15 +0100 Subject: [PATCH 113/358] include default buy/sell trends for the hyperopt strategy --- freqtrade/optimize/default_hyperopt.py | 46 ++++++++++++++++++++++---- user_data/hyperopts/sample_hyperopt.py | 46 ++++++++++++++++++++++---- 2 files changed, 80 insertions(+), 12 deletions(-) diff --git a/freqtrade/optimize/default_hyperopt.py b/freqtrade/optimize/default_hyperopt.py index 315a1e8a5..721848d2e 100644 --- a/freqtrade/optimize/default_hyperopt.py +++ b/freqtrade/optimize/default_hyperopt.py @@ -33,6 +33,7 @@ class DefaultHyperOpts(IHyperOpt): # Bollinger bands bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) dataframe['bb_lowerband'] = bollinger['lower'] + dataframe['bb_upperband'] = bollinger['upper'] dataframe['sar'] = ta.SAR(dataframe) return dataframe @@ -111,21 +112,21 @@ class DefaultHyperOpts(IHyperOpt): if 'sell-fastd-enabled' in params and params['sell-fastd-enabled']: conditions.append(dataframe['fastd'] > params['sell-fastd-value']) if 'sell-adx-enabled' in params and params['sell-adx-enabled']: - conditions.append(dataframe['adx'] > params['sell-adx-value']) + conditions.append(dataframe['adx'] < params['sell-adx-value']) if 'sell-rsi-enabled' in params and params['sell-rsi-enabled']: conditions.append(dataframe['rsi'] > params['sell-rsi-value']) # TRIGGERS if 'sell-trigger' in params: - if params['sell-trigger'] == 'sell-bb_lower': - conditions.append(dataframe['close'] < dataframe['bb_lowerband']) + if params['sell-trigger'] == 'sell-bb_upper': + conditions.append(dataframe['close'] > dataframe['bb_upperband']) if params['sell-trigger'] == 'sell-macd_cross_signal': conditions.append(qtpylib.crossed_above( - dataframe['macd'], dataframe['macdsignal'] + dataframe['macdsignal'], dataframe['macd'] )) if params['sell-trigger'] == 'sell-sar_reversal': conditions.append(qtpylib.crossed_above( - dataframe['close'], dataframe['sar'] + dataframe['sar'], dataframe['close'] )) dataframe.loc[ @@ -150,7 +151,7 @@ class DefaultHyperOpts(IHyperOpt): Categorical([True, False], name='sell-fastd-enabled'), Categorical([True, False], name='sell-adx-enabled'), Categorical([True, False], name='sell-rsi-enabled'), - Categorical(['sell-bb_lower', + Categorical(['sell-bb_upper', 'sell-macd_cross_signal', 'sell-sar_reversal'], name='sell-trigger') ] @@ -190,3 +191,36 @@ class DefaultHyperOpts(IHyperOpt): Real(0.01, 0.07, name='roi_p2'), Real(0.01, 0.20, name='roi_p3'), ] + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators. Should be a copy of from strategy + must align to populate_indicators in this file + Only used when --spaces does not include buy + """ + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) & + (dataframe['mfi'] < 16) & + (dataframe['adx'] > 25) & + (dataframe['rsi'] < 21) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators. Should be a copy of from strategy + must align to populate_indicators in this file + Only used when --spaces does not include sell + """ + dataframe.loc[ + ( + (qtpylib.crossed_above( + dataframe['macdsignal'], dataframe['macd'] + )) & + (dataframe['fastd'] > 54) + ), + 'sell'] = 1 + return dataframe diff --git a/user_data/hyperopts/sample_hyperopt.py b/user_data/hyperopts/sample_hyperopt.py index 1700af932..54f65a7e6 100644 --- a/user_data/hyperopts/sample_hyperopt.py +++ b/user_data/hyperopts/sample_hyperopt.py @@ -42,6 +42,7 @@ class SampleHyperOpts(IHyperOpt): # Bollinger bands bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) dataframe['bb_lowerband'] = bollinger['lower'] + dataframe['bb_upperband'] = bollinger['upper'] dataframe['sar'] = ta.SAR(dataframe) return dataframe @@ -120,21 +121,21 @@ class SampleHyperOpts(IHyperOpt): if 'sell-fastd-enabled' in params and params['sell-fastd-enabled']: conditions.append(dataframe['fastd'] > params['sell-fastd-value']) if 'sell-adx-enabled' in params and params['sell-adx-enabled']: - conditions.append(dataframe['adx'] > params['sell-adx-value']) + conditions.append(dataframe['adx'] < params['sell-adx-value']) if 'sell-rsi-enabled' in params and params['sell-rsi-enabled']: conditions.append(dataframe['rsi'] > params['sell-rsi-value']) # TRIGGERS if 'sell-trigger' in params: - if params['sell-trigger'] == 'sell-bb_lower': - conditions.append(dataframe['close'] < dataframe['bb_lowerband']) + if params['sell-trigger'] == 'sell-bb_upper': + conditions.append(dataframe['close'] > dataframe['bb_upperband']) if params['sell-trigger'] == 'sell-macd_cross_signal': conditions.append(qtpylib.crossed_above( - dataframe['macd'], dataframe['macdsignal'] + dataframe['macdsignal'], dataframe['macd'] )) if params['sell-trigger'] == 'sell-sar_reversal': conditions.append(qtpylib.crossed_above( - dataframe['close'], dataframe['sar'] + dataframe['sar'], dataframe['close'] )) dataframe.loc[ @@ -159,7 +160,7 @@ class SampleHyperOpts(IHyperOpt): Categorical([True, False], name='sell-fastd-enabled'), Categorical([True, False], name='sell-adx-enabled'), Categorical([True, False], name='sell-rsi-enabled'), - Categorical(['sell-bb_lower', + Categorical(['sell-bb_upper', 'sell-macd_cross_signal', 'sell-sar_reversal'], name='sell-trigger') ] @@ -199,3 +200,36 @@ class SampleHyperOpts(IHyperOpt): Real(0.01, 0.07, name='roi_p2'), Real(0.01, 0.20, name='roi_p3'), ] + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators. Should be a copy of from strategy + must align to populate_indicators in this file + Only used when --spaces does not include buy + """ + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) & + (dataframe['mfi'] < 16) & + (dataframe['adx'] > 25) & + (dataframe['rsi'] < 21) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Based on TA indicators. Should be a copy of from strategy + must align to populate_indicators in this file + Only used when --spaces does not include sell + """ + dataframe.loc[ + ( + (qtpylib.crossed_above( + dataframe['macdsignal'], dataframe['macd'] + )) & + (dataframe['fastd'] > 54) + ), + 'sell'] = 1 + return dataframe From 821e299afb77181dcaf9c66c95beccbbaf3529e6 Mon Sep 17 00:00:00 2001 From: misagh Date: Sun, 6 Jan 2019 14:45:29 +0100 Subject: [PATCH 114/358] adjusting trailing stoploss on exchange --- freqtrade/freqtradebot.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4febe9dd0..6d3f7db07 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -648,7 +648,17 @@ class FreqtradeBot(object): trade.update(order) result = True else: - result = False + # check if trailing stoploss is enabled and stoploss value has changed + # in which case we cancel stoploss order and put another one with new + # value immediately + if self.config.get('trailing_stop', False) and trade.stop_loss > order['price']: + if self.exchange.cancel_order(order['id'], trade.pair): + stoploss_order_id = self.exchange.stoploss_limit( + pair=trade.pair, amount=trade.amount, + stop_price=stop_price, rate=limit_price + )['id'] + trade.stoploss_order_id = str(stoploss_order_id) + return result def check_sell(self, trade: Trade, sell_rate: float, buy: bool, sell: bool) -> bool: From dd2af86a4187afc389f26c2602d0190b948ecef8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 6 Jan 2019 14:47:38 +0100 Subject: [PATCH 115/358] pprint results --- freqtrade/optimize/hyperopt.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index b8b8aaf56..7d6a991d2 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -5,17 +5,18 @@ This module contains the hyperopt logic """ import logging -from argparse import Namespace +import multiprocessing import os import sys -from pathlib import Path +from argparse import Namespace from math import exp -import multiprocessing from operator import itemgetter +from pathlib import Path +from pprint import pprint from typing import Any, Dict, List -from pandas import DataFrame from joblib import Parallel, delayed, dump, load, wrap_non_picklable_objects +from pandas import DataFrame from skopt import Optimizer from skopt.space import Dimension @@ -26,7 +27,6 @@ from freqtrade.optimize import get_timeframe from freqtrade.optimize.backtesting import Backtesting from freqtrade.resolvers import HyperOptResolver - logger = logging.getLogger(__name__) MAX_LOSS = 100000 # just a big enough number to be bad result in loss optimization @@ -104,11 +104,11 @@ class Hyperopt(Backtesting): logger.info( 'Best result:\n%s\nwith values:\n%s', best_result['result'], - best_result['params'] + pprint(best_result['params'], indent=4) ) if 'roi_t1' in best_result['params']: logger.info('ROI table:\n%s', - self.custom_hyperopt.generate_roi_table(best_result['params'])) + pprint(self.custom_hyperopt.generate_roi_table(best_result['params']), indent=4)) def log_results(self, results) -> None: """ From 5dd1f9b38a8f9a2ee5feecfe25e7828f3305a921 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 6 Jan 2019 14:47:53 +0100 Subject: [PATCH 116/358] improve hyperopt docs --- docs/hyperopt.md | 58 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index c4dd76009..d17464d32 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -16,6 +16,22 @@ an example hyperopt file located into [user_data/hyperopts/](https://github.com/ Configuring hyperopt is similar to writing your own strategy, and many tasks will be similar and a lot of code can be copied across from the strategy. +### Checklist on all tasks / possibilities in hyperopt + +Depending on the space you want to optimize, only some of the below are required. + +* fill `populate_indicators` - probably a copy from your strategy +* fill `buy_strategy_generator` - for buy signal optimization +* fill `indicator_space` - for buy signal optimzation +* fill `sell_strategy_generator` - for sell signal optimization +* fill `sell_indicator_space` - for sell signal optimzation +* fill `roi_space` - for ROI optimization +* fill `generate_roi_table` - for ROI optimization (if you need more than 3 entries) +* fill `stoploss_space` - stoploss optimization +* Optional but reccomended + * copy `populate_buy_trend` from your strategy - otherwise default-strategy will be used + * copy `populate_sell_trend` from your strategy - otherwise default-strategy will be used + ### 1. Install a Custom Hyperopt File Put your hyperopt file into the folder`user_data/hyperopts`. @@ -49,11 +65,11 @@ If you have updated the buy strategy, ie. changed the contents of Similar to the buy-signal above, sell-signals can also be optimized. Place the corresponding settings into the following methods -- Inside `sell_indicator_space()` - the parameters hyperopt shall be optimizing. -- Inside `populate_sell_trend()` - applying the parameters. +* Inside `sell_indicator_space()` - the parameters hyperopt shall be optimizing. +* Inside `populate_sell_trend()` - applying the parameters. The configuration and rules are the same than for buy signals. -To avoid naming collisions in the search-space, please prefix all sell-spaces with sell-. +To avoid naming collisions in the search-space, please prefix all sell-spaces with `sell-`. ## Solving a Mystery @@ -65,7 +81,7 @@ mystery. We will start by defining a search space: -``` +```python def indicator_space() -> List[Dimension]: """ Define your Hyperopt space for searching strategy parameters @@ -88,7 +104,7 @@ one we call `trigger` and use it to decide which buy trigger we want to use. So let's write the buy strategy using these values: -``` +``` python def populate_buy_trend(dataframe: DataFrame) -> DataFrame: conditions = [] # GUARDS AND TRENDS @@ -136,7 +152,7 @@ Because hyperopt tries a lot of combinations to find the best parameters it will We strongly recommend to use `screen` or `tmux` to prevent any connection loss. ```bash -python3 ./freqtrade/main.py --hyperopt -c config.json hyperopt -e 5000 +python3 ./freqtrade/main.py --hyperopt -c config.json hyperopt -e 5000 --spaces all ``` Use `` as the name of the custom hyperopt used. @@ -144,6 +160,11 @@ Use `` as the name of the custom hyperopt used. The `-e` flag will set how many evaluations hyperopt will do. We recommend running at least several thousand evaluations. +The `--spaces all` flag determines that all possible parameters should be optimized. Possibilities are listed below. + +!!! Warning +When switching parameters or changing configuration options, the file `user_data/hyperopt_results.pickle` should be removed. It's used to be able to continue interrupted calculations, but does not detect changes to settings or the hyperopt file. + ### Execute Hyperopt with Different Ticker-Data Source If you would like to hyperopt parameters using an alternate ticker data that @@ -186,7 +207,11 @@ Given the following result from hyperopt: Best result: 135 trades. Avg profit 0.57%. Total profit 0.03871918 BTC (0.7722Σ%). Avg duration 180.4 mins. with values: -{'adx-value': 44, 'rsi-value': 29, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'bb_lower'} +{ 'adx-value': 44, + 'rsi-value': 29, + 'adx-enabled': False, + 'rsi-enabled': True, + 'trigger': 'bb_lower'} ``` You should understand this result like: @@ -226,9 +251,24 @@ If you are optimizing ROI, you're result will look as follows and include a ROI Best result: 135 trades. Avg profit 0.57%. Total profit 0.03871918 BTC (0.7722Σ%). Avg duration 180.4 mins. with values: -{'adx-value': 44, 'rsi-value': 29, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'bb_lower', 'roi_t1': 40, 'roi_t2': 57, 'roi_t3': 21, 'roi_p1': 0.03634636907306948, 'roi_p2': 0.055237357937802885, 'roi_p3': 0.015163796015548354, 'stoploss': -0.37996664668703606} +{ 'adx-value': 44, + 'rsi-value': 29, + 'adx-enabled': false, + 'rsi-enabled': True, + 'trigger': 'bb_lower', + 'roi_t1': 40, + 'roi_t2': 57, + 'roi_t3': 21, + 'roi_p1': 0.03634636907306948, + 'roi_p2': 0.055237357937802885, + 'roi_p3': 0.015163796015548354, + 'stoploss': -0.37996664668703606 +} ROI table: -{0: 0.10674752302642071, 21: 0.09158372701087236, 78: 0.03634636907306948, 118: 0} +{ 0: 0.10674752302642071, + 21: 0.09158372701087236, + 78: 0.03634636907306948, + 118: 0} ``` This would translate to the following ROI table: From 40b1d8f0678ce320f44ed25cbb645233be8e6336 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 6 Jan 2019 14:57:14 +0100 Subject: [PATCH 117/358] Fix CI problems --- freqtrade/optimize/hyperopt.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 7d6a991d2..6930bed04 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -102,13 +102,13 @@ class Hyperopt(Backtesting): results = sorted(self.trials, key=itemgetter('loss')) best_result = results[0] logger.info( - 'Best result:\n%s\nwith values:\n%s', - best_result['result'], - pprint(best_result['params'], indent=4) + 'Best result:\n%s\nwith values:\n', + best_result['result'] ) + pprint(best_result['params'], indent=4) if 'roi_t1' in best_result['params']: - logger.info('ROI table:\n%s', - pprint(self.custom_hyperopt.generate_roi_table(best_result['params']), indent=4)) + logger.info('ROI table:') + pprint(self.custom_hyperopt.generate_roi_table(best_result['params']), indent=4) def log_results(self, results) -> None: """ @@ -171,12 +171,12 @@ class Hyperopt(Backtesting): if self.has_space('buy'): self.advise_buy = self.custom_hyperopt.buy_strategy_generator(params) elif hasattr(self.custom_hyperopt, 'populate_buy_trend'): - self.advise_buy = self.custom_hyperopt.populate_buy_trend + self.advise_buy = self.custom_hyperopt.populate_buy_trend # type: ignore if self.has_space('sell'): self.advise_sell = self.custom_hyperopt.sell_strategy_generator(params) elif hasattr(self.custom_hyperopt, 'populate_sell_trend'): - self.advise_sell = self.custom_hyperopt.populate_sell_trend + self.advise_sell = self.custom_hyperopt.populate_sell_trend # type: ignore if self.has_space('stoploss'): self.strategy.stoploss = params['stoploss'] From 440a7ec9c2d287abf58168c5affcf88e6eda1569 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 6 Jan 2019 19:31:25 +0100 Subject: [PATCH 118/358] fix pytest --- freqtrade/tests/optimize/test_hyperopt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index 5c6d3981a..d816988a8 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -201,7 +201,7 @@ def test_start_calls_optimizer(mocker, default_conf, caplog) -> None: hyperopt.start() parallel.assert_called_once() - assert 'Best result:\nfoo result\nwith values:\n{}' in caplog.text + assert 'Best result:\nfoo result\nwith values:\n\n' in caplog.text assert dumper.called From f620449bec59b4cb1df8eb5f50f458ab0a922a75 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 6 Jan 2019 19:38:32 +0100 Subject: [PATCH 119/358] Add test for hyperoptresolver --- freqtrade/tests/optimize/test_hyperopt.py | 25 ++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index d816988a8..20baee99e 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -9,7 +9,8 @@ import pytest from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.data.history import load_tickerdata_file from freqtrade.optimize.hyperopt import Hyperopt, start -from freqtrade.resolvers import StrategyResolver +from freqtrade.optimize.default_hyperopt import DefaultHyperOpts +from freqtrade.resolvers import StrategyResolver, HyperOptResolver from freqtrade.tests.conftest import log_has, patch_exchange from freqtrade.tests.optimize.test_backtesting import get_args @@ -38,6 +39,28 @@ def create_trials(mocker, hyperopt) -> None: return [{'loss': 1, 'result': 'foo', 'params': {}}] +def test_hyperoptresolver(mocker, default_conf, caplog) -> None: + + mocker.patch( + 'freqtrade.configuration.Configuration._load_config_file', + lambda *args, **kwargs: default_conf + ) + hyperopts = DefaultHyperOpts + delattr(hyperopts, 'populate_buy_trend') + delattr(hyperopts, 'populate_sell_trend') + mocker.patch( + 'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver._load_hyperopt', + MagicMock(return_value=hyperopts) + ) + x = HyperOptResolver(default_conf, ).hyperopt + assert not hasattr(x, 'populate_buy_trend') + assert not hasattr(x, 'populate_sell_trend') + assert log_has("Custom Hyperopt does not provide populate_sell_trend. " + "Using populate_sell_trend from DefaultStrategy.", caplog.record_tuples) + assert log_has("Custom Hyperopt does not provide populate_buy_trend. " + "Using populate_buy_trend from DefaultStrategy.", caplog.record_tuples) + + def test_start(mocker, default_conf, caplog) -> None: start_mock = MagicMock() mocker.patch( From 16472535ebabe4bf0ba3f9de77f87ae4cd33a38f Mon Sep 17 00:00:00 2001 From: misagh Date: Tue, 8 Jan 2019 12:39:10 +0100 Subject: [PATCH 120/358] adding stoploss_last_update to persistence --- freqtrade/persistence.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index a14d22b98..c466f2fc1 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -83,7 +83,7 @@ def check_migrate(engine) -> None: logger.debug(f'trying {table_back_name}') # Check for latest column - if not has_column(cols, 'stoploss_order_id'): + if not has_column(cols, 'stoploss_last_update'): logger.info(f'Running database migration - backup available as {table_back_name}') fee_open = get_column_def(cols, 'fee_open', 'fee') @@ -93,6 +93,7 @@ def check_migrate(engine) -> None: stop_loss = get_column_def(cols, 'stop_loss', '0.0') initial_stop_loss = get_column_def(cols, 'initial_stop_loss', '0.0') stoploss_order_id = get_column_def(cols, 'stoploss_order_id', 'null') + stoploss_last_update = get_column_def(cols, 'stoploss_last_update', 'null') max_rate = get_column_def(cols, 'max_rate', '0.0') sell_reason = get_column_def(cols, 'sell_reason', 'null') strategy = get_column_def(cols, 'strategy', 'null') @@ -111,7 +112,8 @@ def check_migrate(engine) -> None: (id, exchange, pair, is_open, fee_open, fee_close, open_rate, open_rate_requested, close_rate, close_rate_requested, close_profit, stake_amount, amount, open_date, close_date, open_order_id, - stop_loss, initial_stop_loss, stoploss_order_id, max_rate, sell_reason, strategy, + stop_loss, initial_stop_loss, stoploss_order_id, stoploss_last_update, + max_rate, sell_reason, strategy, ticker_interval ) select id, lower(exchange), @@ -127,9 +129,9 @@ def check_migrate(engine) -> None: {close_rate_requested} close_rate_requested, close_profit, stake_amount, amount, open_date, close_date, open_order_id, {stop_loss} stop_loss, {initial_stop_loss} initial_stop_loss, - {stoploss_order_id} stoploss_order_id, {max_rate} max_rate, - {sell_reason} sell_reason, {strategy} strategy, - {ticker_interval} ticker_interval + {stoploss_order_id} stoploss_order_id, {stoploss_last_update} stoploss_last_update, + {max_rate} max_rate, {sell_reason} sell_reason, + {strategy} strategy, {ticker_interval} ticker_interval from {table_back_name} """) @@ -185,6 +187,8 @@ class Trade(_DECL_BASE): initial_stop_loss = Column(Float, nullable=True, default=0.0) # stoploss order id which is on exchange stoploss_order_id = Column(String, nullable=True, index=True) + # last update time of the stoploss order on exchange + stoploss_last_update = Column(DateTime, nullable=True) # absolute value of the highest reached price max_rate = Column(Float, nullable=True, default=0.0) sell_reason = Column(String, nullable=True) From 4fbb9d4462d353414a99087cbe54562466fd0c7e Mon Sep 17 00:00:00 2001 From: misagh Date: Tue, 8 Jan 2019 12:39:53 +0100 Subject: [PATCH 121/358] adding stoploss_on_exchange_interval to order_types dict. default to 1 minute (60) --- freqtrade/strategy/interface.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 694430fd6..acf627c8a 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -75,7 +75,8 @@ class IStrategy(ABC): 'buy': 'limit', 'sell': 'limit', 'stoploss': 'limit', - 'stoploss_on_exchange': False + 'stoploss_on_exchange': False, + 'stoploss_on_exchange_interval': 60, } # Optional time in force From b98526d32c47d71b1dc1aa5c6f6e700012cbfee6 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Tue, 8 Jan 2019 13:33:06 +0100 Subject: [PATCH 122/358] Update ccxt from 1.18.102 to 1.18.114 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b07c450f3..87ecc53f6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.102 +ccxt==1.18.114 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.12.1 From c3107272d34ee7893e8611948733e7c2961d3e7f Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Tue, 8 Jan 2019 13:33:08 +0100 Subject: [PATCH 123/358] Update arrow from 0.12.1 to 0.13.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 87ecc53f6..b0d8e2563 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ ccxt==1.18.114 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 -arrow==0.12.1 +arrow==0.13.0 cachetools==3.0.0 requests==2.21.0 urllib3==1.24.1 From ec22512fd95a95473a661f260b960dfe26809b9c Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Tue, 8 Jan 2019 13:33:10 +0100 Subject: [PATCH 124/358] Update pytest from 4.0.2 to 4.1.0 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 4a7416ca1..b2248c9c6 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,7 +2,7 @@ -r requirements.txt flake8==3.6.0 -pytest==4.0.2 +pytest==4.1.0 pytest-mock==1.10.0 pytest-asyncio==0.9.0 pytest-cov==2.6.0 From 4069e2fdfb600d45901054f0ba4cb54e2a062d26 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Tue, 8 Jan 2019 13:33:11 +0100 Subject: [PATCH 125/358] Update pytest-asyncio from 0.9.0 to 0.10.0 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index b2248c9c6..87be5d195 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,5 +4,5 @@ flake8==3.6.0 pytest==4.1.0 pytest-mock==1.10.0 -pytest-asyncio==0.9.0 +pytest-asyncio==0.10.0 pytest-cov==2.6.0 From f4e0e044622c0a5d77069fbb854a8bec5fbcd7be Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Tue, 8 Jan 2019 13:33:13 +0100 Subject: [PATCH 126/358] Update pytest-cov from 2.6.0 to 2.6.1 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 87be5d195..2eeae1fbc 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -5,4 +5,4 @@ flake8==3.6.0 pytest==4.1.0 pytest-mock==1.10.0 pytest-asyncio==0.10.0 -pytest-cov==2.6.0 +pytest-cov==2.6.1 From aed855284cb3707d2fc994914441779be3115636 Mon Sep 17 00:00:00 2001 From: misagh Date: Tue, 8 Jan 2019 13:44:51 +0100 Subject: [PATCH 127/358] comparing with stopPrice instead of price --- freqtrade/freqtradebot.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 6d3f7db07..02b55954e 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -613,7 +613,7 @@ class FreqtradeBot(object): def handle_stoploss_on_exchange(self, trade: Trade) -> bool: """ Check if trade is fulfilled in which case the stoploss - on exchange should be added immediately if stoploss on exchnage + on exchange should be added immediately if stoploss on exchange is enabled. """ @@ -630,13 +630,14 @@ class FreqtradeBot(object): stop_price = trade.open_rate * (1 + stoploss) # limit price should be less than stop price. - # 0.98 is arbitrary here. - limit_price = stop_price * 0.98 + # 0.99 is arbitrary here. + limit_price = stop_price * 0.99 stoploss_order_id = self.exchange.stoploss_limit( pair=trade.pair, amount=trade.amount, stop_price=stop_price, rate=limit_price )['id'] trade.stoploss_order_id = str(stoploss_order_id) + trade.stoploss_last_update = datetime.now() # Or the trade open and there is already a stoploss on exchange. # so we check if it is hit ... @@ -647,17 +648,22 @@ class FreqtradeBot(object): trade.sell_reason = SellType.STOPLOSS_ON_EXCHANGE.value trade.update(order) result = True - else: + elif self.config.get('trailing_stop', False): # check if trailing stoploss is enabled and stoploss value has changed # in which case we cancel stoploss order and put another one with new # value immediately - if self.config.get('trailing_stop', False) and trade.stop_loss > order['price']: - if self.exchange.cancel_order(order['id'], trade.pair): - stoploss_order_id = self.exchange.stoploss_limit( - pair=trade.pair, amount=trade.amount, - stop_price=stop_price, rate=limit_price - )['id'] - trade.stoploss_order_id = str(stoploss_order_id) + if trade.stop_loss > order['info']['stopPrice']: + # we check also if the update is neccesary + update_beat = self.strategy.order_types['stoploss_on_exchange_interval'] + if (datetime.now() - trade.stoploss_last_update).total_seconds > update_beat: + # cancelling the current stoploss on exchange first + if self.exchange.cancel_order(order['id'], trade.pair): + # creating the new one + stoploss_order_id = self.exchange.stoploss_limit( + pair=trade.pair, amount=trade.amount, + stop_price=stop_price, rate=limit_price + )['id'] + trade.stoploss_order_id = str(stoploss_order_id) return result From 9e133eb32e206c2d9c01da340f099c7cef191004 Mon Sep 17 00:00:00 2001 From: misagh Date: Tue, 8 Jan 2019 16:31:02 +0100 Subject: [PATCH 128/358] adding guard not to cancel the previous stop loss on exchange if market is falling quickly --- freqtrade/freqtradebot.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 02b55954e..513b27755 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -649,9 +649,19 @@ class FreqtradeBot(object): trade.update(order) result = True elif self.config.get('trailing_stop', False): - # check if trailing stoploss is enabled and stoploss value has changed + # if trailing stoploss is enabled we check if stoploss value has changed # in which case we cancel stoploss order and put another one with new # value immediately + + # This is a guard: there is a situation where market is going doing down fast + # the stoploss on exchange checked previously is not hit but + # it is too late and too risky to cancel the previous stoploss + if trade.stop_loss < self.exchange.get_ticker(trade.pair)['bid']: + logger.info('stoploss on exchange update: too risky to update stoploss as ' + 'current best bid price (%s) is higher than stoploss value (%s)', + self.exchange.get_ticker(trade.pair)['bid'], trade.stop_loss) + return result + if trade.stop_loss > order['info']['stopPrice']: # we check also if the update is neccesary update_beat = self.strategy.order_types['stoploss_on_exchange_interval'] From 1a27258469cf9fd756203dacca721886ca5297b9 Mon Sep 17 00:00:00 2001 From: misagh Date: Tue, 8 Jan 2019 16:34:23 +0100 Subject: [PATCH 129/358] condition fixed --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 513b27755..a081d364a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -656,7 +656,7 @@ class FreqtradeBot(object): # This is a guard: there is a situation where market is going doing down fast # the stoploss on exchange checked previously is not hit but # it is too late and too risky to cancel the previous stoploss - if trade.stop_loss < self.exchange.get_ticker(trade.pair)['bid']: + if trade.stop_loss <= self.exchange.get_ticker(trade.pair)['bid']: logger.info('stoploss on exchange update: too risky to update stoploss as ' 'current best bid price (%s) is higher than stoploss value (%s)', self.exchange.get_ticker(trade.pair)['bid'], trade.stop_loss) From b5adfcf51a49da8ac65979f4af32e5f94a16a715 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 8 Jan 2019 17:07:32 +0100 Subject: [PATCH 130/358] Fix documentation typo --- docs/hyperopt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index d17464d32..0c18110bd 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -28,7 +28,7 @@ Depending on the space you want to optimize, only some of the below are required * fill `roi_space` - for ROI optimization * fill `generate_roi_table` - for ROI optimization (if you need more than 3 entries) * fill `stoploss_space` - stoploss optimization -* Optional but reccomended +* Optional but recommended * copy `populate_buy_trend` from your strategy - otherwise default-strategy will be used * copy `populate_sell_trend` from your strategy - otherwise default-strategy will be used From b3f67bb8c6289c43a38865ff3abf36f5eef80c4c Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 8 Jan 2019 20:09:32 +0100 Subject: [PATCH 131/358] Fix git pull in docker-image --- build_helpers/publish_docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_helpers/publish_docker.sh b/build_helpers/publish_docker.sh index 9d82fc2d5..7a8127c44 100755 --- a/build_helpers/publish_docker.sh +++ b/build_helpers/publish_docker.sh @@ -13,7 +13,7 @@ if [ "${TRAVIS_EVENT_TYPE}" = "cron" ]; then else echo "event ${TRAVIS_EVENT_TYPE}: building with cache" # Pull last build to avoid rebuilding the whole image - docker pull ${REPO}:${TAG} + docker pull ${IMAGE_NAME}:${TAG} docker build --cache-from ${IMAGE_NAME}:${TAG} -t freqtrade:${TAG} . fi From 64372ea6fb1b02f6ec2e709b2b21652c64081967 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 8 Jan 2019 20:12:39 +0100 Subject: [PATCH 132/358] Fix ta-lib installation build helpers --- build_helpers/install_ta-lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_helpers/install_ta-lib.sh b/build_helpers/install_ta-lib.sh index 4d4f37c17..95119a51f 100755 --- a/build_helpers/install_ta-lib.sh +++ b/build_helpers/install_ta-lib.sh @@ -1,5 +1,5 @@ if [ ! -f "ta-lib/CHANGELOG.TXT" ]; then - tar zxvf ta-lib-0.4.0-src.tar.gz + tar zxvf build_helpers/ta-lib-0.4.0-src.tar.gz cd ta-lib \ && sed -i.bak "s|0.00000001|0.000000000000000001 |g" src/ta_func/ta_utility.h \ && ./configure \ From df97652f6e07ddb24b3cda697a469acf0797224d Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 8 Jan 2019 20:35:08 +0100 Subject: [PATCH 133/358] Try fix ta-lib install --- .travis.yml | 2 +- build_helpers/install_ta-lib.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4398a1386..f448c72d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ addons: - libdw-dev - binutils-dev install: -- ./build_helpers/install_ta-lib.sh +- cd build_helpers && ./install_ta-lib.sh - export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH - pip install --upgrade flake8 coveralls pytest-random-order pytest-asyncio mypy - pip install -r requirements-dev.txt diff --git a/build_helpers/install_ta-lib.sh b/build_helpers/install_ta-lib.sh index 95119a51f..4d4f37c17 100755 --- a/build_helpers/install_ta-lib.sh +++ b/build_helpers/install_ta-lib.sh @@ -1,5 +1,5 @@ if [ ! -f "ta-lib/CHANGELOG.TXT" ]; then - tar zxvf build_helpers/ta-lib-0.4.0-src.tar.gz + tar zxvf ta-lib-0.4.0-src.tar.gz cd ta-lib \ && sed -i.bak "s|0.00000001|0.000000000000000001 |g" src/ta_func/ta_utility.h \ && ./configure \ From 69eed95a54f6dc2aaa9595f2e963ed6e6423d578 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 8 Jan 2019 20:39:50 +0100 Subject: [PATCH 134/358] cd out of the build-helpers dir --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f448c72d8..503295e4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ addons: - libdw-dev - binutils-dev install: -- cd build_helpers && ./install_ta-lib.sh +- cd build_helpers && ./install_ta-lib.sh; cd .. - export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH - pip install --upgrade flake8 coveralls pytest-random-order pytest-asyncio mypy - pip install -r requirements-dev.txt From da436c920f6499f43085c6a8658353b633616c17 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 8 Jan 2019 20:58:14 +0100 Subject: [PATCH 135/358] switch travis to xenial --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 503295e4a..c389189b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ sudo: true os: - linux -dist: trusty +dist: xenial language: python python: - 3.6 From 356a17cdaaf8753875527b5d8d609f3619ddd89c Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 8 Jan 2019 21:07:08 +0100 Subject: [PATCH 136/358] Build python 3.7 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c389189b9..de1c6a139 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ dist: xenial language: python python: - 3.6 +- 3.7 services: - docker env: From bb3d78757d763535673823268d24eabf957f2f43 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 8 Jan 2019 21:11:36 +0100 Subject: [PATCH 137/358] Test python 3.7 --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index de1c6a139..495fe3726 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,9 @@ jobs: - pytest --cov=freqtrade --cov-config=.coveragerc freqtrade/tests/ - coveralls name: pytest + python: + - 3.6 + - 3.7 - script: - cp config.json.example config.json - python freqtrade/main.py --datadir freqtrade/tests/testdata backtesting From 7a13565efb54618b0564d0c4dac4b00657efec86 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 8 Jan 2019 21:13:56 +0100 Subject: [PATCH 138/358] travis - python - test --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 495fe3726..4617aa19e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ dist: xenial language: python python: - 3.6 -- 3.7 services: - docker env: From f9a99f4ad33dc7b77a31d89812bfe4f3a42dae61 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 8 Jan 2019 21:27:50 +0100 Subject: [PATCH 139/358] Change caching --- .travis.yml | 4 ++-- build_helpers/install_ta-lib.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4617aa19e..7e2e9ac34 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,6 @@ jobs: - coveralls name: pytest python: - - 3.6 - 3.7 - script: - cp config.json.example config.json @@ -56,6 +55,7 @@ notifications: slack: secure: bKLXmOrx8e2aPZl7W8DA5BdPAXWGpI5UzST33oc1G/thegXcDVmHBTJrBs4sZak6bgAclQQrdZIsRd2eFYzHLalJEaw6pk7hoAw8SvLnZO0ZurWboz7qg2+aZZXfK4eKl/VUe4sM9M4e/qxjkK+yWG7Marg69c4v1ypF7ezUi1fPYILYw8u0paaiX0N5UX8XNlXy+PBlga2MxDjUY70MuajSZhPsY2pDUvYnMY1D/7XN3cFW0g+3O8zXjF0IF4q1Z/1ASQe+eYjKwPQacE+O8KDD+ZJYoTOFBAPllrtpO1jnOPFjNGf3JIbVMZw4bFjIL0mSQaiSUaUErbU3sFZ5Or79rF93XZ81V7uEZ55vD8KMfR2CB1cQJcZcj0v50BxLo0InkFqa0Y8Nra3sbpV4fV5Oe8pDmomPJrNFJnX6ULQhQ1gTCe0M5beKgVms5SITEpt4/Y0CmLUr6iHDT0CUiyMIRWAXdIgbGh1jfaWOMksybeRevlgDsIsNBjXmYI1Sw2ZZR2Eo2u4R6zyfyjOMLwYJ3vgq9IrACv2w5nmf0+oguMWHf6iWi2hiOqhlAN1W74+3HsYQcqnuM3LGOmuCnPprV1oGBqkPXjIFGpy21gNx4vHfO1noLUyJnMnlu2L7SSuN1CdLsnjJ1hVjpJjPfqB4nn8g12x87TqM1bOm+3Q= cache: + pip: True directories: - - $HOME/.cache/pip - ta-lib + - /usr/local/lib/ diff --git a/build_helpers/install_ta-lib.sh b/build_helpers/install_ta-lib.sh index 4d4f37c17..4f46a5784 100755 --- a/build_helpers/install_ta-lib.sh +++ b/build_helpers/install_ta-lib.sh @@ -1,4 +1,4 @@ -if [ ! -f "ta-lib/CHANGELOG.TXT" ]; then +if [ ! -f "/usr/local/lib/libta_lib.a" ]; then tar zxvf ta-lib-0.4.0-src.tar.gz cd ta-lib \ && sed -i.bak "s|0.00000001|0.000000000000000001 |g" src/ta_func/ta_utility.h \ From dd7d655a63abd90430f39f6859e626add15fbe89 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 8 Jan 2019 21:30:11 +0100 Subject: [PATCH 140/358] remove unwriteable cache-dir --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7e2e9ac34..c995ec700 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,4 +58,3 @@ cache: pip: True directories: - ta-lib - - /usr/local/lib/ From f4979e0e8a917ed7a41d33cd8222ba58e2909aae Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 9 Jan 2019 06:27:58 +0100 Subject: [PATCH 141/358] Cache /usr/loca/lib --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c995ec700..4c453a873 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,8 +29,6 @@ jobs: - pytest --cov=freqtrade --cov-config=.coveragerc freqtrade/tests/ - coveralls name: pytest - python: - - 3.7 - script: - cp config.json.example config.json - python freqtrade/main.py --datadir freqtrade/tests/testdata backtesting @@ -58,3 +56,4 @@ cache: pip: True directories: - ta-lib + - /usr/local/lib From e76ed31b0863b8521a9f9eb9c78a2099ff353913 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 9 Jan 2019 08:15:36 +0100 Subject: [PATCH 142/358] fix ta-lib cache --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4c453a873..c39817aa1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,5 +55,4 @@ notifications: cache: pip: True directories: - - ta-lib - - /usr/local/lib + - build_helpers/ta-lib From 2e530a3e0382e86ff41043f395cc0df51aca5709 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 9 Jan 2019 08:16:33 +0100 Subject: [PATCH 143/358] Update install_ta-lib.sh --- build_helpers/install_ta-lib.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/build_helpers/install_ta-lib.sh b/build_helpers/install_ta-lib.sh index 4f46a5784..9fe341bba 100755 --- a/build_helpers/install_ta-lib.sh +++ b/build_helpers/install_ta-lib.sh @@ -7,7 +7,5 @@ if [ ! -f "/usr/local/lib/libta_lib.a" ]; then && which sudo && sudo make install || make install \ && cd .. else - echo "TA-lib already installed, skipping download and build." - cd ta-lib && sudo make install && cd .. - + echo "TA-lib already installed, skipping installation" fi From a4947554492949e2d9dc728593312b73edb4d309 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 9 Jan 2019 08:17:03 +0100 Subject: [PATCH 144/358] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c39817aa1..57265fd40 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,4 +55,4 @@ notifications: cache: pip: True directories: - - build_helpers/ta-lib + - /usr/local/lib From c1007f95b32cdd622c124e663081a797941f69a3 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 9 Jan 2019 13:32:07 +0100 Subject: [PATCH 145/358] Update ccxt from 1.18.114 to 1.18.117 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b0d8e2563..25c441d73 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.114 +ccxt==1.18.117 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.13.0 From e025ad391871b2bfcd7b6584641cd0a66cc07953 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 9 Jan 2019 16:23:13 +0100 Subject: [PATCH 146/358] temp test commit --- freqtrade/freqtradebot.py | 4 +- freqtrade/tests/test_freqtradebot.py | 66 ++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index a081d364a..5169d162c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -656,12 +656,14 @@ class FreqtradeBot(object): # This is a guard: there is a situation where market is going doing down fast # the stoploss on exchange checked previously is not hit but # it is too late and too risky to cancel the previous stoploss - if trade.stop_loss <= self.exchange.get_ticker(trade.pair)['bid']: + if trade.stop_loss > self.exchange.get_ticker(trade.pair)['bid']: logger.info('stoploss on exchange update: too risky to update stoploss as ' 'current best bid price (%s) is higher than stoploss value (%s)', self.exchange.get_ticker(trade.pair)['bid'], trade.stop_loss) return result + print(trade.stop_loss) + print(order['info']['stopPrice']) if trade.stop_loss > order['info']['stopPrice']: # we check also if the update is neccesary update_beat = self.strategy.order_types['stoploss_on_exchange_interval'] diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 2b14c586b..5aa8c55a9 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1014,6 +1014,72 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, assert trade.is_open is False +def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, + markets, limit_buy_order, limit_sell_order) -> None: + # When trailing stoploss is set + stoploss_limit = MagicMock(return_value={'id': 13434334}) + patch_RPCManager(mocker) + patch_exchange(mocker) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_ticker=MagicMock(return_value={ + 'bid': 0.00001172, + 'ask': 0.00001173, + 'last': 0.00001172 + }), + buy=MagicMock(return_value={'id': limit_buy_order['id']}), + sell=MagicMock(return_value={'id': limit_sell_order['id']}), + get_fee=fee, + get_markets=markets, + stoploss_limit=stoploss_limit + ) + default_conf['trailing_stop'] = True + freqtrade = FreqtradeBot(default_conf) + freqtrade.strategy.stoploss = -0.05 + patch_get_signal(freqtrade) + + freqtrade.create_trade() + trade = Trade.query.first() + trade.is_open = True + trade.open_order_id = None + trade.stoploss_order_id = 100 + + stoploss_order_hanging = MagicMock(return_value={ + 'id': 100, + 'status': 'open', + 'type': 'stop_loss_limit', + 'price': 3, + 'average': 2, + 'info' : { + 'stopPrice': 1.113399999 + } + }) + + mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hanging) + + # stoploss initially at 5% + assert freqtrade.handle_trade(trade) is False + + # price jumped 2x + trade.max_rate = trade.open_rate * 2 + assert freqtrade.handle_trade(trade) is False + assert freqtrade.handle_stoploss_on_exchange(trade) is False + + assert trade.stop_loss == (trade.open_rate * 2) * 0.95 + + stoploss_order_hit = MagicMock(return_value={ + 'status': 'open', + 'type': 'stop_loss_limit', + 'price': 3, + 'average': 2 + }) + mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hit) + assert freqtrade.handle_stoploss_on_exchange(trade) is False + assert log_has('STOP_LOSS_LIMIT is hit for {}.'.format(trade), caplog.record_tuples) + assert trade.stoploss_order_id is None + assert trade.is_open is False + + def test_process_maybe_execute_buy(mocker, default_conf) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) From 3f8092192ebc664102463894d56fd18dbe825c58 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 10 Jan 2019 13:32:07 +0100 Subject: [PATCH 147/358] Update ccxt from 1.18.117 to 1.18.119 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 25c441d73..b847fb1a9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.117 +ccxt==1.18.119 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.13.0 From 4920ee34554512cfc56ba32bd0145929881dbfe4 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 10 Jan 2019 13:32:09 +0100 Subject: [PATCH 148/358] Update wrapt from 1.10.11 to 1.11.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b847fb1a9..30feb1f6c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ arrow==0.13.0 cachetools==3.0.0 requests==2.21.0 urllib3==1.24.1 -wrapt==1.10.11 +wrapt==1.11.0 pandas==0.23.4 scikit-learn==0.20.2 joblib==0.13.0 From 3867f73c8ca0f9d83aa5656e05cdc1e390dc08cd Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 11 Jan 2019 13:32:07 +0100 Subject: [PATCH 149/358] Update ccxt from 1.18.119 to 1.18.120 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 30feb1f6c..380185348 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.119 +ccxt==1.18.120 SQLAlchemy==1.2.15 python-telegram-bot==11.1.0 arrow==0.13.0 From e95351fd0407ade72f475943178d8023b70ed5da Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 12 Jan 2019 13:32:06 +0100 Subject: [PATCH 150/358] Update sqlalchemy from 1.2.15 to 1.2.16 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 380185348..d7c4e2bcf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ ccxt==1.18.120 -SQLAlchemy==1.2.15 +SQLAlchemy==1.2.16 python-telegram-bot==11.1.0 arrow==0.13.0 cachetools==3.0.0 From 690fbeb9074b9779dcfe360311732c3efbbe16f9 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 12 Jan 2019 13:32:08 +0100 Subject: [PATCH 151/358] Update joblib from 0.13.0 to 0.13.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d7c4e2bcf..505bebc7a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ urllib3==1.24.1 wrapt==1.11.0 pandas==0.23.4 scikit-learn==0.20.2 -joblib==0.13.0 +joblib==0.13.1 scipy==1.2.0 jsonschema==2.6.0 numpy==1.15.4 From 9e0902e72fbe8b7ee4071ddc5fd0602151668fc3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Jan 2019 13:38:49 +0100 Subject: [PATCH 152/358] Add test for case for odd ROI dict - #1478 --- freqtrade/tests/strategy/test_interface.py | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/freqtrade/tests/strategy/test_interface.py b/freqtrade/tests/strategy/test_interface.py index 08634073d..f2e8c577f 100644 --- a/freqtrade/tests/strategy/test_interface.py +++ b/freqtrade/tests/strategy/test_interface.py @@ -118,6 +118,7 @@ def test_tickerdata_to_dataframe(default_conf) -> None: def test_min_roi_reached(default_conf, fee) -> None: + # Use list to confirm sequence does not matter min_roi_list = [{20: 0.05, 55: 0.01, 0: 0.1}, {0: 0.1, 20: 0.05, 55: 0.01}] for roi in min_roi_list: @@ -143,6 +144,47 @@ def test_min_roi_reached(default_conf, fee) -> None: assert strategy.min_roi_reached(trade, 0.02, arrow.utcnow().shift(minutes=-1).datetime) +def test_min_roi_reached2(default_conf, fee) -> None: + + # test with ROI raising after last interval + min_roi_list = [{20: 0.07, + 30: 0.05, + 55: 0.30, + 0: 0.1 + }, + {0: 0.1, + 20: 0.07, + 30: 0.05, + 55: 0.30 + }, + ] + for roi in min_roi_list: + strategy = DefaultStrategy(default_conf) + strategy.minimal_roi = roi + trade = Trade( + pair='ETH/BTC', + stake_amount=0.001, + open_date=arrow.utcnow().shift(hours=-1).datetime, + fee_open=fee.return_value, + fee_close=fee.return_value, + exchange='bittrex', + open_rate=1, + ) + + assert not strategy.min_roi_reached(trade, 0.02, arrow.utcnow().shift(minutes=-56).datetime) + assert strategy.min_roi_reached(trade, 0.12, arrow.utcnow().shift(minutes=-56).datetime) + + assert not strategy.min_roi_reached(trade, 0.04, arrow.utcnow().shift(minutes=-39).datetime) + assert strategy.min_roi_reached(trade, 0.071, arrow.utcnow().shift(minutes=-39).datetime) + + assert not strategy.min_roi_reached(trade, 0.04, arrow.utcnow().shift(minutes=-26).datetime) + assert strategy.min_roi_reached(trade, 0.06, arrow.utcnow().shift(minutes=-26).datetime) + + # Should not trigger with 20% profit since after 55 minutes only 30% is active. + assert not strategy.min_roi_reached(trade, 0.20, arrow.utcnow().shift(minutes=-2).datetime) + assert strategy.min_roi_reached(trade, 0.31, arrow.utcnow().shift(minutes=-2).datetime) + + def test_analyze_ticker_default(ticker_history, mocker, caplog) -> None: caplog.set_level(logging.DEBUG) ind_mock = MagicMock(side_effect=lambda x, meta: x) From e9d61eb35dce1b697de008fa570320d86409c90c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Jan 2019 13:44:50 +0100 Subject: [PATCH 153/358] Fix ROI calculation problem Prior to that all ROI entries with a key > trade_duration where active. This causes a problem if the ROI is not linearly declining --- freqtrade/strategy/interface.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index a6569ec19..47ec25198 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -319,17 +319,18 @@ class IStrategy(ABC): def min_roi_reached(self, trade: Trade, current_profit: float, current_time: datetime) -> bool: """ Based an earlier trade and current price and ROI configuration, decides whether bot should - sell + sell. Requires current_profit to be in percent!! :return True if bot should sell at current rate """ # Check if time matches and current rate is above threshold - time_diff = (current_time.timestamp() - trade.open_date.timestamp()) / 60 - for duration, threshold in self.minimal_roi.items(): - if time_diff <= duration: - continue - if current_profit > threshold: - return True + trade_dur = (current_time.timestamp() - trade.open_date.timestamp()) / 60 + + # Get highest entry in ROI dict where key >= trade-duration + roi_entry = max(list(filter(lambda x: trade_dur >= x, self.minimal_roi.keys()))) + threshold = self.minimal_roi[roi_entry] + if current_profit > threshold: + return True return False From cd2bccd44198e29ade6932be23b6617dca4a5899 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Jan 2019 13:45:43 +0100 Subject: [PATCH 154/358] Have backtest use the same logic to get the ROI entry --- freqtrade/optimize/backtesting.py | 2 +- freqtrade/tests/optimize/test_backtesting.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index e96a91856..80dc9a443 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -221,7 +221,7 @@ class Backtesting(object): elif sell.sell_type == (SellType.ROI): # get next entry in min_roi > to trade duration # Interface.py skips on trade_duration <= duration - roi_entry = max(list(filter(lambda x: trade_dur > x, + roi_entry = max(list(filter(lambda x: trade_dur >= x, self.strategy.minimal_roi.keys()))) roi = self.strategy.minimal_roi[roi_entry] diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 6e0ab24a4..5ab44baad 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -530,10 +530,10 @@ def test_backtest(default_conf, fee, mocker) -> None: 'open_time': [Arrow(2018, 1, 29, 18, 40, 0).datetime, Arrow(2018, 1, 30, 3, 30, 0).datetime], 'close_time': [Arrow(2018, 1, 29, 22, 35, 0).datetime, - Arrow(2018, 1, 30, 4, 15, 0).datetime], + Arrow(2018, 1, 30, 4, 10, 0).datetime], 'open_index': [78, 184], - 'close_index': [125, 193], - 'trade_duration': [235, 45], + 'close_index': [125, 192], + 'trade_duration': [235, 40], 'open_at_end': [False, False], 'open_rate': [0.104445, 0.10302485], 'close_rate': [0.104969, 0.103541], From 04786f09f44d53253532e6395dbdb25bcb7d53d9 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sun, 13 Jan 2019 13:32:06 +0100 Subject: [PATCH 155/358] Update pytest from 4.1.0 to 4.1.1 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 2eeae1fbc..50d181296 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,7 +2,7 @@ -r requirements.txt flake8==3.6.0 -pytest==4.1.0 +pytest==4.1.1 pytest-mock==1.10.0 pytest-asyncio==0.10.0 pytest-cov==2.6.1 From a3897c990d16b7482f2f3b6579a8c7902b4f32f7 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Mon, 14 Jan 2019 13:32:07 +0100 Subject: [PATCH 156/358] Update ccxt from 1.18.120 to 1.18.126 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 505bebc7a..0ec1f1f5f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.120 +ccxt==1.18.126 SQLAlchemy==1.2.16 python-telegram-bot==11.1.0 arrow==0.13.0 From da04182287a648637b5133a77617187a67590d0b Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Mon, 14 Jan 2019 13:32:08 +0100 Subject: [PATCH 157/358] Update numpy from 1.15.4 to 1.16.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0ec1f1f5f..9baeacf08 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ scikit-learn==0.20.2 joblib==0.13.1 scipy==1.2.0 jsonschema==2.6.0 -numpy==1.15.4 +numpy==1.16.0 TA-Lib==0.4.17 tabulate==0.8.2 coinmarketcap==5.0.3 From cfe00c2f0c118c93e1870567eb75c195bfa91ddd Mon Sep 17 00:00:00 2001 From: misagh Date: Tue, 15 Jan 2019 11:04:32 +0100 Subject: [PATCH 158/358] initial test added for TSL on exchange --- freqtrade/freqtradebot.py | 6 ++--- freqtrade/persistence.py | 2 ++ freqtrade/tests/test_freqtradebot.py | 39 ++++++++++++++++++---------- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 5169d162c..477141782 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -662,18 +662,16 @@ class FreqtradeBot(object): self.exchange.get_ticker(trade.pair)['bid'], trade.stop_loss) return result - print(trade.stop_loss) - print(order['info']['stopPrice']) if trade.stop_loss > order['info']['stopPrice']: # we check also if the update is neccesary update_beat = self.strategy.order_types['stoploss_on_exchange_interval'] - if (datetime.now() - trade.stoploss_last_update).total_seconds > update_beat: + if (datetime.now() - trade.stoploss_last_update).total_seconds() > update_beat: # cancelling the current stoploss on exchange first if self.exchange.cancel_order(order['id'], trade.pair): # creating the new one stoploss_order_id = self.exchange.stoploss_limit( pair=trade.pair, amount=trade.amount, - stop_price=stop_price, rate=limit_price + stop_price=trade.stop_loss, rate=trade.stop_loss * 0.99 )['id'] trade.stoploss_order_id = str(stoploss_order_id) diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index c466f2fc1..f9b34fc64 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -222,11 +222,13 @@ class Trade(_DECL_BASE): logger.debug("assigning new stop loss") self.stop_loss = new_loss self.initial_stop_loss = new_loss + self.stoploss_last_update = datetime.utcnow() # evaluate if the stop loss needs to be updated else: if new_loss > self.stop_loss: # stop losses only walk up, never down! self.stop_loss = new_loss + self.stoploss_last_update = datetime.utcnow() logger.debug("adjusted stop loss") else: logger.debug("keeping current stop loss") diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 5aa8c55a9..6d6e5fe91 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1033,9 +1033,21 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, get_markets=markets, stoploss_limit=stoploss_limit ) + + # enabling TSL default_conf['trailing_stop'] = True + + # disabling ROI + default_conf['minimal_roi']['0'] = 999999999 + freqtrade = FreqtradeBot(default_conf) + + # setting stoploss freqtrade.strategy.stoploss = -0.05 + + # setting stoploss_on_exchange_interval + freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0 + patch_get_signal(freqtrade) freqtrade.create_trade() @@ -1051,7 +1063,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, 'price': 3, 'average': 2, 'info' : { - 'stopPrice': 1.113399999 + 'stopPrice': 0.000011134 } }) @@ -1061,23 +1073,22 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, assert freqtrade.handle_trade(trade) is False # price jumped 2x - trade.max_rate = trade.open_rate * 2 + mocker.patch('freqtrade.exchange.Exchange.get_ticker', MagicMock(return_value={ + 'bid': 0.00002344, + 'ask': 0.00002346, + 'last': 0.00002344 + })) + assert freqtrade.handle_trade(trade) is False - assert freqtrade.handle_stoploss_on_exchange(trade) is False + assert trade.stop_loss == 0.00002344 * 0.95 - assert trade.stop_loss == (trade.open_rate * 2) * 0.95 + mocker.patch('freqtrade.exchange.Exchange.cancel_order', MagicMock(return_value={ + "orderId": 100, + "status": "CANCELED", + })) - stoploss_order_hit = MagicMock(return_value={ - 'status': 'open', - 'type': 'stop_loss_limit', - 'price': 3, - 'average': 2 - }) - mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hit) assert freqtrade.handle_stoploss_on_exchange(trade) is False - assert log_has('STOP_LOSS_LIMIT is hit for {}.'.format(trade), caplog.record_tuples) - assert trade.stoploss_order_id is None - assert trade.is_open is False + assert freqtrade.exchange.cancel_order.call_count == 1 def test_process_maybe_execute_buy(mocker, default_conf) -> None: From f0cfab79404048366a225af2316365eb5fbdf433 Mon Sep 17 00:00:00 2001 From: misagh Date: Tue, 15 Jan 2019 11:10:28 +0100 Subject: [PATCH 159/358] flaking 8 --- freqtrade/tests/test_freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 6d6e5fe91..40f5f3327 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1062,7 +1062,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, 'type': 'stop_loss_limit', 'price': 3, 'average': 2, - 'info' : { + 'info': { 'stopPrice': 0.000011134 } }) From 494b905d1e0ddb1ae5e497763f605604aefcb4d5 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Tue, 15 Jan 2019 13:32:08 +0100 Subject: [PATCH 160/358] Update ccxt from 1.18.126 to 1.18.131 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9baeacf08..92885045d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.126 +ccxt==1.18.131 SQLAlchemy==1.2.16 python-telegram-bot==11.1.0 arrow==0.13.0 From 399d2d89a3642601355309426cf6ce9cf272cc6e Mon Sep 17 00:00:00 2001 From: gautier pialat Date: Tue, 15 Jan 2019 15:02:01 +0100 Subject: [PATCH 161/358] Win rate formula was wrong (but not the definition) --- docs/edge.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/edge.md b/docs/edge.md index 61abf354b..b208cb318 100644 --- a/docs/edge.md +++ b/docs/edge.md @@ -24,7 +24,7 @@ The answer comes to two factors: Means over X trades what is the percentage of winning trades to total number of trades (note that we don't consider how much you gained but only If you won or not). -`W = (Number of winning trades) / (Number of losing trades)` +`W = (Number of winning trades) / (Total number of trades)` ### Risk Reward Ratio Risk Reward Ratio is a formula used to measure the expected gains of a given investment against the risk of loss. It is basically what you potentially win divided by what you potentially lose: @@ -209,4 +209,4 @@ The full timerange specification: * Use tickframes till 2018/01/31: --timerange=-20180131 * Use tickframes since 2018/01/31: --timerange=20180131- * Use tickframes since 2018/01/31 till 2018/03/01 : --timerange=20180131-20180301 -* Use tickframes between POSIX timestamps 1527595200 1527618600: --timerange=1527595200-1527618600 \ No newline at end of file +* Use tickframes between POSIX timestamps 1527595200 1527618600: --timerange=1527595200-1527618600 From 12e81080159343025717803a2ec3a5c1d8d2fc0f Mon Sep 17 00:00:00 2001 From: misagh Date: Tue, 15 Jan 2019 15:36:41 +0100 Subject: [PATCH 162/358] checking params of cancel order and stop loss order --- freqtrade/freqtradebot.py | 8 +++++--- freqtrade/tests/test_freqtradebot.py | 15 ++++++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 477141782..23e1e422b 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -657,9 +657,11 @@ class FreqtradeBot(object): # the stoploss on exchange checked previously is not hit but # it is too late and too risky to cancel the previous stoploss if trade.stop_loss > self.exchange.get_ticker(trade.pair)['bid']: - logger.info('stoploss on exchange update: too risky to update stoploss as ' - 'current best bid price (%s) is higher than stoploss value (%s)', - self.exchange.get_ticker(trade.pair)['bid'], trade.stop_loss) + logger.warning( + 'stoploss on exchange update: too risky to update stoploss as ' + 'current best bid price (%s) is higher than stoploss value (%s).', + self.exchange.get_ticker(trade.pair)['bid'], trade.stop_loss + ) return result if trade.stop_loss > order['info']['stopPrice']: diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 40f5f3327..d0a708865 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1082,13 +1082,18 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, assert freqtrade.handle_trade(trade) is False assert trade.stop_loss == 0.00002344 * 0.95 - mocker.patch('freqtrade.exchange.Exchange.cancel_order', MagicMock(return_value={ - "orderId": 100, - "status": "CANCELED", - })) + cancel_order_mock = MagicMock() + stoploss_order_mock = MagicMock() + mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order_mock) + mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_order_mock) assert freqtrade.handle_stoploss_on_exchange(trade) is False - assert freqtrade.exchange.cancel_order.call_count == 1 + + cancel_order_mock.assert_called_once_with(100, 'ETH/BTC') + stoploss_order_mock.assert_called_once_with(amount=85.25149190110828, + pair='ETH/BTC', + rate=0.00002344 * 0.95 * 0.99, + stop_price=0.00002344 * 0.95) def test_process_maybe_execute_buy(mocker, default_conf) -> None: From 1cd5abde3789a79c7acb2182ff06f716e19f94a7 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 11:22:25 +0100 Subject: [PATCH 163/358] removing unnecessary guard --- freqtrade/freqtradebot.py | 12 ------------ freqtrade/tests/test_freqtradebot.py | 1 + 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 23e1e422b..2a1415e09 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -652,18 +652,6 @@ class FreqtradeBot(object): # if trailing stoploss is enabled we check if stoploss value has changed # in which case we cancel stoploss order and put another one with new # value immediately - - # This is a guard: there is a situation where market is going doing down fast - # the stoploss on exchange checked previously is not hit but - # it is too late and too risky to cancel the previous stoploss - if trade.stop_loss > self.exchange.get_ticker(trade.pair)['bid']: - logger.warning( - 'stoploss on exchange update: too risky to update stoploss as ' - 'current best bid price (%s) is higher than stoploss value (%s).', - self.exchange.get_ticker(trade.pair)['bid'], trade.stop_loss - ) - return result - if trade.stop_loss > order['info']['stopPrice']: # we check also if the update is neccesary update_beat = self.strategy.order_types['stoploss_on_exchange_interval'] diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index d0a708865..b86d76553 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1071,6 +1071,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, # stoploss initially at 5% assert freqtrade.handle_trade(trade) is False + assert freqtrade.handle_stoploss_on_exchange(trade) is False # price jumped 2x mocker.patch('freqtrade.exchange.Exchange.get_ticker', MagicMock(return_value={ From 29439c05d66fd069b17e8d6b8f47dd1ca053ffdc Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 11:51:54 +0100 Subject: [PATCH 164/358] adding update beat test --- freqtrade/freqtradebot.py | 2 +- freqtrade/tests/test_freqtradebot.py | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 2a1415e09..6334aef04 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -655,7 +655,7 @@ class FreqtradeBot(object): if trade.stop_loss > order['info']['stopPrice']: # we check also if the update is neccesary update_beat = self.strategy.order_types['stoploss_on_exchange_interval'] - if (datetime.now() - trade.stoploss_last_update).total_seconds() > update_beat: + if (datetime.utcnow() - trade.stoploss_last_update).total_seconds() > update_beat: # cancelling the current stoploss on exchange first if self.exchange.cancel_order(order['id'], trade.pair): # creating the new one diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index b86d76553..dcdae16fe 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1045,8 +1045,8 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, # setting stoploss freqtrade.strategy.stoploss = -0.05 - # setting stoploss_on_exchange_interval - freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0 + # setting stoploss_on_exchange_interval to 60 seconds + freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60 patch_get_signal(freqtrade) @@ -1080,14 +1080,24 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, 'last': 0.00002344 })) - assert freqtrade.handle_trade(trade) is False - assert trade.stop_loss == 0.00002344 * 0.95 cancel_order_mock = MagicMock() stoploss_order_mock = MagicMock() mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order_mock) mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_order_mock) + # stoploss should not be updated as the interval is 60 seconds + assert freqtrade.handle_trade(trade) is False + assert freqtrade.handle_stoploss_on_exchange(trade) is False + cancel_order_mock.assert_not_called() + stoploss_order_mock.assert_not_called() + + assert freqtrade.handle_trade(trade) is False + assert trade.stop_loss == 0.00002344 * 0.95 + + # setting stoploss_on_exchange_interval to 0 seconds + freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0 + assert freqtrade.handle_stoploss_on_exchange(trade) is False cancel_order_mock.assert_called_once_with(100, 'ETH/BTC') From bfb71215834fcc817bba9e5478afc15c25cc915b Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 12:16:32 +0100 Subject: [PATCH 165/358] refactoring handle_stoploss_on_exchange --- freqtrade/freqtradebot.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 6334aef04..25951aba5 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -652,21 +652,24 @@ class FreqtradeBot(object): # if trailing stoploss is enabled we check if stoploss value has changed # in which case we cancel stoploss order and put another one with new # value immediately - if trade.stop_loss > order['info']['stopPrice']: - # we check also if the update is neccesary - update_beat = self.strategy.order_types['stoploss_on_exchange_interval'] - if (datetime.utcnow() - trade.stoploss_last_update).total_seconds() > update_beat: - # cancelling the current stoploss on exchange first - if self.exchange.cancel_order(order['id'], trade.pair): - # creating the new one - stoploss_order_id = self.exchange.stoploss_limit( - pair=trade.pair, amount=trade.amount, - stop_price=trade.stop_loss, rate=trade.stop_loss * 0.99 - )['id'] - trade.stoploss_order_id = str(stoploss_order_id) + handle_trailing_stoploss_on_exchange(trade, order) return result + def handle_trailing_stoploss_on_exchange(self, trade: Trade, order): + if trade.stop_loss > order['info']['stopPrice']: + # we check if the update is neccesary + update_beat = self.strategy.order_types['stoploss_on_exchange_interval'] + if (datetime.utcnow() - trade.stoploss_last_update).total_seconds() > update_beat: + # cancelling the current stoploss on exchange first + if self.exchange.cancel_order(order['id'], trade.pair): + # creating the new one + stoploss_order_id = self.exchange.stoploss_limit( + pair=trade.pair, amount=trade.amount, + stop_price=trade.stop_loss, rate=trade.stop_loss * 0.99 + )['id'] + trade.stoploss_order_id = str(stoploss_order_id) + def check_sell(self, trade: Trade, sell_rate: float, buy: bool, sell: bool) -> bool: if self.edge: stoploss = self.edge.stoploss(trade.pair) From 6d588b3b0bf0f464a5cd86bb435c4887773823f1 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 14:28:52 +0100 Subject: [PATCH 166/358] trailing stop loss on exchange extracted to a separate function --- freqtrade/freqtradebot.py | 2 +- freqtrade/tests/test_freqtradebot.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 25951aba5..544e4a997 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -652,7 +652,7 @@ class FreqtradeBot(object): # if trailing stoploss is enabled we check if stoploss value has changed # in which case we cancel stoploss order and put another one with new # value immediately - handle_trailing_stoploss_on_exchange(trade, order) + self.handle_trailing_stoploss_on_exchange(trade, order) return result diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index dcdae16fe..fe2dd0ec2 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1080,7 +1080,6 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, 'last': 0.00002344 })) - cancel_order_mock = MagicMock() stoploss_order_mock = MagicMock() mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order_mock) From cffc9ce890deae1db17d79ca4ad0cef5ce31f8e1 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 14:49:47 +0100 Subject: [PATCH 167/358] last step: stop loss on exchange added to trailing --- freqtrade/strategy/interface.py | 16 ++++++++-------- freqtrade/tests/test_freqtradebot.py | 3 +++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 7afb0c59f..f3ff9ba7a 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -234,12 +234,9 @@ class IStrategy(ABC): current_rate = low or rate current_profit = trade.calc_profit_percent(current_rate) - if self.order_types.get('stoploss_on_exchange'): - stoplossflag = SellCheckTuple(sell_flag=False, sell_type=SellType.NONE) - else: - stoplossflag = self.stop_loss_reached(current_rate=current_rate, trade=trade, - current_time=date, current_profit=current_profit, - force_stoploss=force_stoploss) + stoplossflag = self.stop_loss_reached(current_rate=current_rate, trade=trade, + current_time=date, current_profit=current_profit, + force_stoploss=force_stoploss) if stoplossflag.sell_flag: return stoplossflag @@ -281,8 +278,11 @@ class IStrategy(ABC): trade.adjust_stop_loss(trade.open_rate, force_stoploss if force_stoploss else self.stoploss, initial=True) - # evaluate if the stoploss was hit - if self.stoploss is not None and trade.stop_loss >= current_rate: + # evaluate if the stoploss was hit if stoploss is not on exchange + if self.stoploss is not None and \ + trade.stop_loss >= current_rate and \ + not self.order_types.get('stoploss_on_exchange'): + selltype = SellType.STOP_LOSS # If Trailing stop (and max-rate did move above open rate) if trailing_stop and trade.open_rate != trade.max_rate: diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index fe2dd0ec2..569477fb1 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1042,6 +1042,9 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, freqtrade = FreqtradeBot(default_conf) + # enabling stoploss on exchange + freqtrade.strategy.order_types['stoploss_on_exchange'] = True + # setting stoploss freqtrade.strategy.stoploss = -0.05 From aa03a864f70b92320080abd444225d0a4fc734ba Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 15:00:35 +0100 Subject: [PATCH 168/358] comments added for TSL on exchange function --- freqtrade/freqtradebot.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 544e4a997..682a850b8 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -657,6 +657,14 @@ class FreqtradeBot(object): return result def handle_trailing_stoploss_on_exchange(self, trade: Trade, order): + """ + Check to see if stoploss on exchange should be updated + in case of trailing stoploss on exchange + :param Trade: Corresponding Trade + :param order: Current on exchange stoploss order + :return: None + """ + if trade.stop_loss > order['info']['stopPrice']: # we check if the update is neccesary update_beat = self.strategy.order_types['stoploss_on_exchange_interval'] From baa5cc5b9e4ccff978a6ea100b70d63d409d6738 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 15:10:31 +0100 Subject: [PATCH 169/358] logs enriched --- freqtrade/exchange/__init__.py | 5 ++++- freqtrade/freqtradebot.py | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index c74b32ad2..588504894 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -402,8 +402,11 @@ class Exchange(object): return self._dry_run_open_orders[order_id] try: - return self._api.create_order(pair, 'stop_loss_limit', 'sell', + order = self._api.create_order(pair, 'stop_loss_limit', 'sell', amount, rate, {'stopPrice': stop_price}) + logger.info('stoploss limit order added for %s. ' + 'stop price: %s. limit: %s' % (pair,stop_price,rate)) + return order except ccxt.InsufficientFunds as e: raise DependencyException( diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 682a850b8..dc5f3d8ad 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -670,6 +670,8 @@ class FreqtradeBot(object): update_beat = self.strategy.order_types['stoploss_on_exchange_interval'] if (datetime.utcnow() - trade.stoploss_last_update).total_seconds() > update_beat: # cancelling the current stoploss on exchange first + logger.info('Trailing stoploss: cancelling current stoploss on exchange ' + 'in order to add another one ...') if self.exchange.cancel_order(order['id'], trade.pair): # creating the new one stoploss_order_id = self.exchange.stoploss_limit( From 50bc20134f67fdc56c18352cd85849929f815c92 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 15:17:28 +0100 Subject: [PATCH 170/358] adding whitespace --- freqtrade/exchange/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 588504894..094d2cc29 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -403,7 +403,7 @@ class Exchange(object): try: order = self._api.create_order(pair, 'stop_loss_limit', 'sell', - amount, rate, {'stopPrice': stop_price}) + amount, rate, {'stopPrice': stop_price}) logger.info('stoploss limit order added for %s. ' 'stop price: %s. limit: %s' % (pair,stop_price,rate)) return order From 611b48dbb99e86a53b948a5e468767d046e1459d Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 16:15:36 +0100 Subject: [PATCH 171/358] fix return value from info hash: value is in string --- freqtrade/freqtradebot.py | 2 +- freqtrade/tests/test_freqtradebot.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index dc5f3d8ad..9b5ab4bba 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -665,7 +665,7 @@ class FreqtradeBot(object): :return: None """ - if trade.stop_loss > order['info']['stopPrice']: + if trade.stop_loss > float(order['info']['stopPrice']): # we check if the update is neccesary update_beat = self.strategy.order_types['stoploss_on_exchange_interval'] if (datetime.utcnow() - trade.stoploss_last_update).total_seconds() > update_beat: diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 569477fb1..6c25b1a7f 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1066,7 +1066,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, 'price': 3, 'average': 2, 'info': { - 'stopPrice': 0.000011134 + 'stopPrice': '0.000011134' } }) From 5e2e96acd2aa3e3cf01ae2a6291ebea917122734 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 18:38:20 +0100 Subject: [PATCH 172/358] compatibility with edge added --- freqtrade/exchange/__init__.py | 2 +- freqtrade/strategy/interface.py | 5 +- freqtrade/tests/test_freqtradebot.py | 110 +++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 4 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 094d2cc29..90a33a226 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -405,7 +405,7 @@ class Exchange(object): order = self._api.create_order(pair, 'stop_loss_limit', 'sell', amount, rate, {'stopPrice': stop_price}) logger.info('stoploss limit order added for %s. ' - 'stop price: %s. limit: %s' % (pair,stop_price,rate)) + 'stop price: %s. limit: %s' % (pair, stop_price, rate)) return order except ccxt.InsufficientFunds as e: diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 6dbdf72e8..88029f4d4 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -274,7 +274,6 @@ class IStrategy(ABC): """ trailing_stop = self.config.get('trailing_stop', False) - trade.adjust_stop_loss(trade.open_rate, force_stoploss if force_stoploss else self.stoploss, initial=True) @@ -282,7 +281,6 @@ class IStrategy(ABC): if self.stoploss is not None and \ trade.stop_loss >= current_rate and \ not self.order_types.get('stoploss_on_exchange'): - selltype = SellType.STOP_LOSS # If Trailing stop (and max-rate did move above open rate) if trailing_stop and trade.open_rate != trade.max_rate: @@ -302,7 +300,8 @@ class IStrategy(ABC): # check if we have a special stop loss for positive condition # and if profit is positive - stop_loss_value = self.stoploss + stop_loss_value = force_stoploss if force_stoploss else self.stoploss + sl_offset = self.config.get('trailing_stop_positive_offset', 0.0) if 'trailing_stop_positive' in self.config and current_profit > sl_offset: diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 6c25b1a7f..1149a69e9 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1109,6 +1109,116 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, stop_price=0.00002344 * 0.95) +def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, + markets, limit_buy_order, limit_sell_order) -> None: + + # When trailing stoploss is set + stoploss_limit = MagicMock(return_value={'id': 13434334}) + patch_RPCManager(mocker) + patch_exchange(mocker) + patch_edge(mocker) + + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_ticker=MagicMock(return_value={ + 'bid': 0.00001172, + 'ask': 0.00001173, + 'last': 0.00001172 + }), + buy=MagicMock(return_value={'id': limit_buy_order['id']}), + sell=MagicMock(return_value={'id': limit_sell_order['id']}), + get_fee=fee, + get_markets=markets, + stoploss_limit=stoploss_limit + ) + + # enabling TSL + edge_conf['trailing_stop'] = True + edge_conf['trailing_stop_positive'] = 0.01 + edge_conf['trailing_stop_positive_offset'] = 0.011 + + # disabling ROI + edge_conf['minimal_roi']['0'] = 999999999 + + freqtrade = FreqtradeBot(edge_conf) + + # enabling stoploss on exchange + freqtrade.strategy.order_types['stoploss_on_exchange'] = True + + # setting stoploss + freqtrade.strategy.stoploss = -0.02 + + # setting stoploss_on_exchange_interval to 0 second + freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0 + + patch_get_signal(freqtrade) + + freqtrade.active_pair_whitelist = freqtrade.edge.adjust(freqtrade.active_pair_whitelist) + + freqtrade.create_trade() + trade = Trade.query.first() + trade.is_open = True + trade.open_order_id = None + trade.stoploss_order_id = 100 + + stoploss_order_hanging = MagicMock(return_value={ + 'id': 100, + 'status': 'open', + 'type': 'stop_loss_limit', + 'price': 3, + 'average': 2, + 'info': { + 'stopPrice': '0.000009384' + } + }) + + mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hanging) + + # stoploss initially at 20% as edge dictated it. + assert freqtrade.handle_trade(trade) is False + assert freqtrade.handle_stoploss_on_exchange(trade) is False + assert trade.stop_loss == 0.000009384 + + cancel_order_mock = MagicMock() + stoploss_order_mock = MagicMock() + mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order_mock) + mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_order_mock) + + # price goes down 5% + mocker.patch('freqtrade.exchange.Exchange.get_ticker', MagicMock(return_value={ + 'bid': 0.00001172 * 0.95, + 'ask': 0.00001173 * 0.95, + 'last': 0.00001172 * 0.95 + })) + + assert freqtrade.handle_trade(trade) is False + assert freqtrade.handle_stoploss_on_exchange(trade) is False + + # stoploss should remain the same + assert trade.stop_loss == 0.000009384 + + # stoploss on exchange should not be canceled + cancel_order_mock.assert_not_called() + + # price jumped 2x + mocker.patch('freqtrade.exchange.Exchange.get_ticker', MagicMock(return_value={ + 'bid': 0.00002344, + 'ask': 0.00002346, + 'last': 0.00002344 + })) + + assert freqtrade.handle_trade(trade) is False + assert freqtrade.handle_stoploss_on_exchange(trade) is False + + # stoploss should be set to 1% as trailing is on + assert trade.stop_loss == 0.00002344 * 0.99 + cancel_order_mock.assert_called_once_with(100, 'NEO/BTC') + stoploss_order_mock.assert_called_once_with(amount=2131074.168797954, + pair='NEO/BTC', + rate=0.00002344 * 0.99 * 0.99, + stop_price=0.00002344 * 0.99) + + def test_process_maybe_execute_buy(mocker, default_conf) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) From 91c714c7d17907afc9404075eb78ef87d634d984 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 18:58:12 +0100 Subject: [PATCH 173/358] stoploss_on_exchange_interval added to full config --- config_full.json.example | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config_full.json.example b/config_full.json.example index 0427f8700..234722f82 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -37,7 +37,8 @@ "buy": "limit", "sell": "limit", "stoploss": "market", - "stoploss_on_exchange": "false" + "stoploss_on_exchange": "false", + "stoploss_on_exchange_interval": 60 }, "order_time_in_force": { "buy": "gtc", From da51ef40f804db5cbd7e711f345e4459d3d87379 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 19:04:43 +0100 Subject: [PATCH 174/358] SL interval added to CONF_SCHEMA --- freqtrade/constants.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index b2393c2b7..9e5561ba3 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -112,7 +112,8 @@ CONF_SCHEMA = { 'buy': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, 'sell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, 'stoploss': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, - 'stoploss_on_exchange': {'type': 'boolean'} + 'stoploss_on_exchange': {'type': 'boolean'}, + 'stoploss_on_exchange_interval': {'type': 'boolean'} }, 'required': ['buy', 'sell', 'stoploss', 'stoploss_on_exchange'] }, @@ -137,7 +138,7 @@ CONF_SCHEMA = { 'pairlist': { 'type': 'object', 'properties': { - 'method': {'type': 'string', 'enum': AVAILABLE_PAIRLISTS}, + 'method': {'type': 'string', 'enum': AVAILABLE_PAIRLISTS}, 'config': {'type': 'object'} }, 'required': ['method'] From 08d98773f39503f01506214fd2d39485abefcbf0 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 19:10:13 +0100 Subject: [PATCH 175/358] added SL interval to configuration document --- docs/configuration.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index a7deaa60c..2f72da794 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -144,10 +144,10 @@ end up paying more then would probably have been necessary. ### Understand order_types -`order_types` contains a dict mapping order-types to market-types as well as stoploss on or off exchange type. This allows to buy using limit orders, sell using limit-orders, and create stoploss orders using market. It also allows to set the stoploss "on exchange" which means stoploss order would be placed immediately once the buy order is fulfilled. +`order_types` contains a dict mapping order-types to market-types as well as stoploss on or off exchange type and stoploss on exchange update interval in seconds. This allows to buy using limit orders, sell using limit-orders, and create stoploss orders using market. It also allows to set the stoploss "on exchange" which means stoploss order would be placed immediately once the buy order is fulfilled. In case stoploss on exchange is set then the bot will use `stoploss_on_exchange_interval` to check it periodically and update it if necessary (e.x. in case of trailing stoploss). This can be set in the configuration or in the strategy. Configuration overwrites strategy configurations. -If this is configured, all 4 values (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`) need to be present, otherwise the bot warn about it and will fail to start. +If this is configured, all 4 values (`"buy"`, `"sell"`, `"stoploss"` and `"stoploss_on_exchange"`) need to be present, otherwise the bot warn about it and will fail to start. The below is the default which is used if this is not configured in either Strategy or configuration. ```python @@ -155,7 +155,8 @@ The below is the default which is used if this is not configured in either Strat "buy": "limit", "sell": "limit", "stoploss": "market", - "stoploss_on_exchange": False + "stoploss_on_exchange": False, + "stoploss_on_exchange_interval": 60 }, ``` @@ -163,6 +164,9 @@ The below is the default which is used if this is not configured in either Strat Not all exchanges support "market" orders. The following message will be shown if your exchange does not support market orders: `"Exchange does not support market orders."` +!!! Note + stoploss on exchange interval is not mandatory. Do not change it's value if you are unsure of what you are doing. + ### Understand order_time_in_force `order_time_in_force` defines the policy by which the order is executed on the exchange. Three commonly used time in force are:
**GTC (Goog Till Canceled):** From 2533112254f1fe183f993630a3b1491eb4bd72ce Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 19:12:52 +0100 Subject: [PATCH 176/358] added referral to stop loss documentation --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 2f72da794..bad8d8b4e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -165,7 +165,7 @@ The below is the default which is used if this is not configured in either Strat The following message will be shown if your exchange does not support market orders: `"Exchange does not support market orders."` !!! Note - stoploss on exchange interval is not mandatory. Do not change it's value if you are unsure of what you are doing. + stoploss on exchange interval is not mandatory. Do not change it's value if you are unsure of what you are doing. For more information about how stoploss works please read [the stoploss documentation](stoploss.md). ### Understand order_time_in_force `order_time_in_force` defines the policy by which the order is executed on the exchange. Three commonly used time in force are:
From e682eceae4f5d7b89de1d98c5834132dd629cac6 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 19:22:06 +0100 Subject: [PATCH 177/358] stop loss documentation added --- docs/stoploss.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/stoploss.md b/docs/stoploss.md index 0278e7bbb..6ee7dd6e8 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -2,12 +2,17 @@ At this stage the bot contains the following stoploss support modes: -1. static stop loss, defined in either the strategy or configuration -2. trailing stop loss, defined in the configuration -3. trailing stop loss, custom positive loss, defined in configuration +1. static stop loss, defined in either the strategy or configuration. +2. trailing stop loss, defined in the configuration. +3. trailing stop loss, custom positive loss, defined in configuration. !!! Note - All stoploss properties can be configured in eihter Strategy or configuration. Configuration values override strategy values. + All stoploss properties can be configured in either Strategy or configuration. Configuration values override strategy values. + +Those stoploss modes can be `on exchange` or `off exchange`. If the stoploss is `on exchange` it means a stoploss limit order is placed on the exchange immediately after buy order happens successfuly. This will protect you against sudden crashes in market as the order will be in the queue immediately and if market goes down then the order has more chance of being fulfilled. + +In case of stoploss on exchange there is another parameter called `stoploss_on_exchange_interval`. That means the interval in seconds the bot will check the stoploss and update it if necessary. As an example in case of trailing stoploss if the order is on the exchange and the market is going up then the bot automatically cancels the previous stoploss order and put a new one with a stop value higher than previous one. It is clear that the bot cannot do it every 5 seconds otherwise it gets banned. So this parameter will tell the bot how often it should update the stoploss order. The default value is 60 (1 minute). + ## Static Stop Loss From 9d6c54791b5a1d8d2ee2b824c19368c322d03a84 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 19:23:55 +0100 Subject: [PATCH 178/358] added note for only binance --- docs/stoploss.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/stoploss.md b/docs/stoploss.md index 6ee7dd6e8..70ce6b0f6 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -13,6 +13,9 @@ Those stoploss modes can be `on exchange` or `off exchange`. If the stoploss is In case of stoploss on exchange there is another parameter called `stoploss_on_exchange_interval`. That means the interval in seconds the bot will check the stoploss and update it if necessary. As an example in case of trailing stoploss if the order is on the exchange and the market is going up then the bot automatically cancels the previous stoploss order and put a new one with a stop value higher than previous one. It is clear that the bot cannot do it every 5 seconds otherwise it gets banned. So this parameter will tell the bot how often it should update the stoploss order. The default value is 60 (1 minute). +!!! Note + Stoploss on exchange is only supported for Binance as of now. + ## Static Stop Loss From 75cedfafb88d9db97a88e17add8ee958d04c0efc Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 16 Jan 2019 20:03:34 +0100 Subject: [PATCH 179/358] unreachable code removed --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4febe9dd0..41938f291 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -601,7 +601,7 @@ class FreqtradeBot(object): if self.check_sell(trade, sell_rate, buy, sell): return True - break + else: logger.debug('checking sell') if self.check_sell(trade, sell_rate, buy, sell): From 97a8341436cd9c4f16e6139e3ec3de524630ff70 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 17 Jan 2019 13:32:07 +0100 Subject: [PATCH 180/358] Update ccxt from 1.18.131 to 1.18.133 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 92885045d..57fda05eb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.131 +ccxt==1.18.133 SQLAlchemy==1.2.16 python-telegram-bot==11.1.0 arrow==0.13.0 From 648def69cab0e80c7b3220410ebd59d457a87107 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 17 Jan 2019 19:05:28 +0100 Subject: [PATCH 181/358] Remove unversioned install of most dev packages --- .travis.yml | 2 +- requirements-dev.txt | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 57265fd40..3dfcf6111 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ addons: install: - cd build_helpers && ./install_ta-lib.sh; cd .. - export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH -- pip install --upgrade flake8 coveralls pytest-random-order pytest-asyncio mypy +- pip install --upgrade pytest-random-order - pip install -r requirements-dev.txt - pip install -e . jobs: diff --git a/requirements-dev.txt b/requirements-dev.txt index 50d181296..870a0e1b9 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,3 +6,5 @@ pytest==4.1.1 pytest-mock==1.10.0 pytest-asyncio==0.10.0 pytest-cov==2.6.1 +coveralls==1.5.1 +mypy==0.650 From a2c01916e19955c1f4ace5c83dde60041b6afc58 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 17 Jan 2019 20:28:21 +0100 Subject: [PATCH 182/358] Add type-ignores to floatfmt tabulate supports this: https://bitbucket.org/astanin/python-tabulate/src/30554300d76f673377956f7980ba4fd2f892c671/tabulate.py?at=master&fileviewer=file-view-default#tabulate.py-1291:1294 --- freqtrade/optimize/backtesting.py | 8 ++++++-- freqtrade/optimize/edge_cli.py | 4 +++- freqtrade/rpc/telegram.py | 16 ++++++++-------- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 80dc9a443..38bbe13d4 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -134,7 +134,9 @@ class Backtesting(object): len(results[results.profit_abs > 0]), len(results[results.profit_abs < 0]) ]) - return tabulate(tabular_data, headers=headers, floatfmt=floatfmt, tablefmt="pipe") + # Ignore type as floatfmt does allow tuples but mypy does not know that + return tabulate(tabular_data, headers=headers, # type: ignore + floatfmt=floatfmt, tablefmt="pipe") def _generate_text_table_sell_reason(self, data: Dict[str, Dict], results: DataFrame) -> str: """ @@ -168,7 +170,9 @@ class Backtesting(object): len(results[results.profit_abs > 0]), len(results[results.profit_abs < 0]) ]) - return tabulate(tabular_data, headers=headers, floatfmt=floatfmt, tablefmt="pipe") + # Ignore type as floatfmt does allow tuples but mypy does not know that + return tabulate(tabular_data, headers=headers, # type: ignore + floatfmt=floatfmt, tablefmt="pipe") def _store_backtest_result(self, recordfilename: str, results: DataFrame, strategyname: Optional[str] = None) -> None: diff --git a/freqtrade/optimize/edge_cli.py b/freqtrade/optimize/edge_cli.py index a98f0c877..fdae47b99 100644 --- a/freqtrade/optimize/edge_cli.py +++ b/freqtrade/optimize/edge_cli.py @@ -67,7 +67,9 @@ class EdgeCli(object): round(result[1].avg_trade_duration) ]) - return tabulate(tabular_data, headers=headers, floatfmt=floatfmt, tablefmt="pipe") + # Ignore type as floatfmt does allow tuples but mypy does not know that + return tabulate(tabular_data, headers=headers, # type: ignore + floatfmt=floatfmt, tablefmt="pipe") def start(self) -> None: self.edge.calculate() diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index be2498d78..3ce7c9167 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -246,14 +246,14 @@ class Telegram(RPC): stake_cur, fiat_disp_cur ) - stats = tabulate(stats, - headers=[ - 'Day', - f'Profit {stake_cur}', - f'Profit {fiat_disp_cur}' - ], - tablefmt='simple') - message = f'Daily Profit over the last {timescale} days:\n
{stats}
' + stats_tab = tabulate(stats, + headers=[ + 'Day', + f'Profit {stake_cur}', + f'Profit {fiat_disp_cur}' + ], + tablefmt='simple') + message = f'Daily Profit over the last {timescale} days:\n
{stats_tab}
' self._send_msg(message, bot=bot, parse_mode=ParseMode.HTML) except RPCException as e: self._send_msg(str(e), bot=bot) From cc6466388e9aa61c420fbf8db15cc6dd4c0a6aad Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 17 Jan 2019 20:31:00 +0100 Subject: [PATCH 183/358] update mypy --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 870a0e1b9..f68fe8814 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,4 +7,4 @@ pytest-mock==1.10.0 pytest-asyncio==0.10.0 pytest-cov==2.6.1 coveralls==1.5.1 -mypy==0.650 +mypy==0.660 From e41e45413faf518bc0cf0fb86b43640cb3e09a46 Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 18 Jan 2019 11:58:23 +0100 Subject: [PATCH 184/358] adding tailing_stop to docs --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index bad8d8b4e..c3843cc2a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -144,7 +144,7 @@ end up paying more then would probably have been necessary. ### Understand order_types -`order_types` contains a dict mapping order-types to market-types as well as stoploss on or off exchange type and stoploss on exchange update interval in seconds. This allows to buy using limit orders, sell using limit-orders, and create stoploss orders using market. It also allows to set the stoploss "on exchange" which means stoploss order would be placed immediately once the buy order is fulfilled. In case stoploss on exchange is set then the bot will use `stoploss_on_exchange_interval` to check it periodically and update it if necessary (e.x. in case of trailing stoploss). +`order_types` contains a dict mapping order-types to market-types as well as stoploss on or off exchange type and stoploss on exchange update interval in seconds. This allows to buy using limit orders, sell using limit-orders, and create stoploss orders using market. It also allows to set the stoploss "on exchange" which means stoploss order would be placed immediately once the buy order is fulfilled. In case stoploss on exchange and `trailing_stop` are both set, then the bot will use `stoploss_on_exchange_interval` to check it periodically and update it if necessary (e.x. in case of trailing stoploss). This can be set in the configuration or in the strategy. Configuration overwrites strategy configurations. If this is configured, all 4 values (`"buy"`, `"sell"`, `"stoploss"` and `"stoploss_on_exchange"`) need to be present, otherwise the bot warn about it and will fail to start. From 1c4ee35ecab03f4a4b1295f8b71bdd89788a4a20 Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 18 Jan 2019 12:00:02 +0100 Subject: [PATCH 185/358] =?UTF-8?q?using=20italic=20for=20=E2=80=9Coff=20e?= =?UTF-8?q?xchange=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/stoploss.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/stoploss.md b/docs/stoploss.md index 70ce6b0f6..78a486ab5 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -9,7 +9,7 @@ At this stage the bot contains the following stoploss support modes: !!! Note All stoploss properties can be configured in either Strategy or configuration. Configuration values override strategy values. -Those stoploss modes can be `on exchange` or `off exchange`. If the stoploss is `on exchange` it means a stoploss limit order is placed on the exchange immediately after buy order happens successfuly. This will protect you against sudden crashes in market as the order will be in the queue immediately and if market goes down then the order has more chance of being fulfilled. +Those stoploss modes can be *on exchange* or *off exchange*. If the stoploss is *on exchange* it means a stoploss limit order is placed on the exchange immediately after buy order happens successfuly. This will protect you against sudden crashes in market as the order will be in the queue immediately and if market goes down then the order has more chance of being fulfilled. In case of stoploss on exchange there is another parameter called `stoploss_on_exchange_interval`. That means the interval in seconds the bot will check the stoploss and update it if necessary. As an example in case of trailing stoploss if the order is on the exchange and the market is going up then the bot automatically cancels the previous stoploss order and put a new one with a stop value higher than previous one. It is clear that the bot cannot do it every 5 seconds otherwise it gets banned. So this parameter will tell the bot how often it should update the stoploss order. The default value is 60 (1 minute). From 89eddfd349d4ac7b04c519d61aed45f6e047273d Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 18 Jan 2019 12:00:53 +0100 Subject: [PATCH 186/358] =?UTF-8?q?refactoring=20english=20=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/stoploss.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/stoploss.md b/docs/stoploss.md index 78a486ab5..0726aebbc 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -11,7 +11,7 @@ At this stage the bot contains the following stoploss support modes: Those stoploss modes can be *on exchange* or *off exchange*. If the stoploss is *on exchange* it means a stoploss limit order is placed on the exchange immediately after buy order happens successfuly. This will protect you against sudden crashes in market as the order will be in the queue immediately and if market goes down then the order has more chance of being fulfilled. -In case of stoploss on exchange there is another parameter called `stoploss_on_exchange_interval`. That means the interval in seconds the bot will check the stoploss and update it if necessary. As an example in case of trailing stoploss if the order is on the exchange and the market is going up then the bot automatically cancels the previous stoploss order and put a new one with a stop value higher than previous one. It is clear that the bot cannot do it every 5 seconds otherwise it gets banned. So this parameter will tell the bot how often it should update the stoploss order. The default value is 60 (1 minute). +In case of stoploss on exchange there is another parameter called `stoploss_on_exchange_interval`. This configures the interval in seconds at which the bot will check the stoploss and update it if necessary. As an example in case of trailing stoploss if the order is on the exchange and the market is going up then the bot automatically cancels the previous stoploss order and put a new one with a stop value higher than previous one. It is clear that the bot cannot do it every 5 seconds otherwise it gets banned. So this parameter will tell the bot how often it should update the stoploss order. The default value is 60 (1 minute). !!! Note Stoploss on exchange is only supported for Binance as of now. From 70780bb01e491945a02f3ba73975a513a95fbad4 Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 18 Jan 2019 12:02:29 +0100 Subject: [PATCH 187/358] using dict.get to fetch interval --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 9b5ab4bba..56b518472 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -667,7 +667,7 @@ class FreqtradeBot(object): if trade.stop_loss > float(order['info']['stopPrice']): # we check if the update is neccesary - update_beat = self.strategy.order_types['stoploss_on_exchange_interval'] + update_beat = self.strategy.order_types.get('stoploss_on_exchange_interval', 60) if (datetime.utcnow() - trade.stoploss_last_update).total_seconds() > update_beat: # cancelling the current stoploss on exchange first logger.info('Trailing stoploss: cancelling current stoploss on exchange ' From a2618208ef78427fe334c90b9a875c67c6664ec4 Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 18 Jan 2019 12:07:51 +0100 Subject: [PATCH 188/358] wrapping in parantheses instead of line breaks --- freqtrade/strategy/interface.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 88029f4d4..08a5cf1cd 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -278,9 +278,9 @@ class IStrategy(ABC): else self.stoploss, initial=True) # evaluate if the stoploss was hit if stoploss is not on exchange - if self.stoploss is not None and \ - trade.stop_loss >= current_rate and \ - not self.order_types.get('stoploss_on_exchange'): + if ((self.stoploss is not None) and + (trade.stop_loss >= current_rate) and + (not self.order_types.get('stoploss_on_exchange'))): selltype = SellType.STOP_LOSS # If Trailing stop (and max-rate did move above open rate) if trailing_stop and trade.open_rate != trade.max_rate: From 87329a393c444937dda6e0487f5b888e2cba62fc Mon Sep 17 00:00:00 2001 From: misagh Date: Fri, 18 Jan 2019 12:14:00 +0100 Subject: [PATCH 189/358] adding stop loss last update to test persistence --- freqtrade/tests/test_persistence.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/tests/test_persistence.py b/freqtrade/tests/test_persistence.py index e64a08262..be6efc2ff 100644 --- a/freqtrade/tests/test_persistence.py +++ b/freqtrade/tests/test_persistence.py @@ -516,6 +516,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog): assert trade.strategy is None assert trade.ticker_interval is None assert trade.stoploss_order_id is None + assert trade.stoploss_last_update is None assert log_has("trying trades_bak1", caplog.record_tuples) assert log_has("trying trades_bak2", caplog.record_tuples) assert log_has("Running database migration - backup available as trades_bak2", From 2b65e3f35cc2fba3f1ebb87b171ad0a794f2902a Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 19 Jan 2019 13:32:06 +0100 Subject: [PATCH 190/358] Update ccxt from 1.18.133 to 1.18.134 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 57fda05eb..9781093e9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.133 +ccxt==1.18.134 SQLAlchemy==1.2.16 python-telegram-bot==11.1.0 arrow==0.13.0 From b421e437ab9290ae374090e4245fbb76212d14a3 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 19 Jan 2019 13:32:07 +0100 Subject: [PATCH 191/358] Update wrapt from 1.11.0 to 1.11.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9781093e9..098aa4e19 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ arrow==0.13.0 cachetools==3.0.0 requests==2.21.0 urllib3==1.24.1 -wrapt==1.11.0 +wrapt==1.11.1 pandas==0.23.4 scikit-learn==0.20.2 joblib==0.13.1 From 797ac71376474ac10b9c4dfbfd8f9c61f1e16347 Mon Sep 17 00:00:00 2001 From: Axel CHERUBIN Date: Sat, 19 Jan 2019 13:26:30 -0400 Subject: [PATCH 192/358] fix stoploss_on_exchange_interval type from boolean to number --- freqtrade/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 9e5561ba3..8fbcdfed7 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -113,7 +113,7 @@ CONF_SCHEMA = { 'sell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, 'stoploss': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, 'stoploss_on_exchange': {'type': 'boolean'}, - 'stoploss_on_exchange_interval': {'type': 'boolean'} + 'stoploss_on_exchange_interval': {'type': 'number'} }, 'required': ['buy', 'sell', 'stoploss', 'stoploss_on_exchange'] }, From 30e3b52b1e7f02b8a159acefa99627b6ad1104c7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 19 Jan 2019 20:02:37 +0100 Subject: [PATCH 193/358] catch errors found in #1491 --- freqtrade/exchange/__init__.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 90a33a226..b8d32306e 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -556,12 +556,17 @@ class Exchange(object): tickers = await asyncio.gather(*input_coroutines, return_exceptions=True) # handle caching - for pair, ticks in tickers: + for res in tickers: + if isinstance(res, Exception): + logger.warning("Async code raised an exception: %s", res.__class__.__name__) + continue + pair = res[0] + ticks = res[1] # keeping last candle time as last refreshed time of the pair if ticks: self._pairs_last_refresh_time[pair] = ticks[-1][0] // 1000 - # keeping parsed dataframe in cache - self._klines[pair] = parse_ticker_dataframe(ticks, tick_interval, fill_missing=True) + # keeping parsed dataframe in cache + self._klines[pair] = parse_ticker_dataframe(ticks, tick_interval, fill_missing=True) return tickers @retrier_async @@ -578,9 +583,12 @@ class Exchange(object): # Ex: Bittrex returns a list of tickers ASC (oldest first, newest last) # when GDAX returns a list of tickers DESC (newest first, oldest last) # Only sort if necessary to save computing time - if data and data[0][0] > data[-1][0]: - data = sorted(data, key=lambda x: x[0]) - + try: + if data and data[0][0] > data[-1][0]: + data = sorted(data, key=lambda x: x[0]) + except IndexError: + logger.exception("Error loading %s. Result was %s.", pair, data) + return pair, None logger.debug("done fetching %s ...", pair) return pair, data From 4e760e1a5e52e213be77931ed4f9586f5de7cdb1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 19 Jan 2019 20:03:04 +0100 Subject: [PATCH 194/358] Test for errors found in 1491 fixes #1491 --- freqtrade/tests/exchange/test_exchange.py | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index 29154bc39..26808e78a 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -923,6 +923,30 @@ async def test_async_get_candles_history(default_conf, mocker): assert exchange._async_get_candle_history.call_count == 2 +@pytest.mark.asyncio +async def test_async_get_candles_history_inv_result(default_conf, mocker, caplog): + + async def mock_get_candle_hist(pair, *args, **kwargs): + if pair == 'ETH/BTC': + return [[]] + else: + raise TypeError() + + exchange = get_patched_exchange(mocker, default_conf) + + # Monkey-patch async function with empty result + exchange._api_async.fetch_ohlcv = MagicMock(side_effect=mock_get_candle_hist) + + pairs = ['ETH/BTC', 'XRP/BTC'] + res = await exchange.async_get_candles_history(pairs, "5m") + assert type(res) is list + assert len(res) == 2 + assert type(res[0]) is tuple + assert type(res[1]) is TypeError + assert log_has("Error loading ETH/BTC. Result was [[]].", caplog.record_tuples) + assert log_has("Async code raised an exception: TypeError", caplog.record_tuples) + + def test_get_order_book(default_conf, mocker, order_book_l2): default_conf['exchange']['name'] = 'binance' api_mock = MagicMock() From b48430f9221c33407dc6c35d890ba916a0876a8a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 19 Jan 2019 20:21:33 +0100 Subject: [PATCH 195/358] Return list not None --- freqtrade/exchange/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index b8d32306e..e0e4d7723 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -588,7 +588,7 @@ class Exchange(object): data = sorted(data, key=lambda x: x[0]) except IndexError: logger.exception("Error loading %s. Result was %s.", pair, data) - return pair, None + return pair, [] logger.debug("done fetching %s ...", pair) return pair, data From a7336300837fc15cc7ebef8c82cf8dc0b962f1b0 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sun, 20 Jan 2019 13:32:07 +0100 Subject: [PATCH 196/358] Update ccxt from 1.18.134 to 1.18.137 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 098aa4e19..063f961bc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.134 +ccxt==1.18.137 SQLAlchemy==1.2.16 python-telegram-bot==11.1.0 arrow==0.13.0 From 6fb50e35c933ef4c7b673a3745894569062b02cd Mon Sep 17 00:00:00 2001 From: macd2 Date: Sun, 20 Jan 2019 14:21:42 +0100 Subject: [PATCH 197/358] JSON standard does not allow single quoted strings --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index c3843cc2a..95bf3b1ff 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -13,7 +13,7 @@ The table below will list all configuration parameters. |----------|---------|-----------|-------------| | `max_open_trades` | 3 | Yes | Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades) | `stake_currency` | BTC | Yes | Crypto-currency used for trading. -| `stake_amount` | 0.05 | Yes | Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to 'unlimited' to allow the bot to use all avaliable balance. +| `stake_amount` | 0.05 | Yes | Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to `"unlimited"` to allow the bot to use all avaliable balance. | `ticker_interval` | [1m, 5m, 30m, 1h, 1d] | No | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes | `fiat_display_currency` | USD | Yes | Fiat currency used to show your profits. More information below. | `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode. From e4a399039be75f73de892e8197f2ff9d8b6f445e Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Mon, 21 Jan 2019 13:32:07 +0100 Subject: [PATCH 198/358] Update ccxt from 1.18.137 to 1.18.141 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 063f961bc..8e6720195 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.137 +ccxt==1.18.141 SQLAlchemy==1.2.16 python-telegram-bot==11.1.0 arrow==0.13.0 From 1be3d57b60a4f5feef41e7178b2f0a04d2f82307 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 21 Jan 2019 19:53:14 +0100 Subject: [PATCH 199/358] Improve developer docs --- CONTRIBUTING.md | 20 ++++---------------- docs/developer.md | 14 +++++++++++++- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c9a967834..3c511f44d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,6 +14,10 @@ Few pointers for contributions: If you are unsure, discuss the feature on our [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) or in a [issue](https://github.com/freqtrade/freqtrade/issues) before a PR. +## Getting started + +Best start by reading the [documentation](https://www.freqtrade.io/) to get a feel for what is possible with the bot, or head straight to the [Developer-documentation](https://www.freqtrade.io/en/latest/developer/) (WIP) which should help you getting started. + ## Before sending the PR: ### 1. Run unit tests @@ -41,12 +45,6 @@ pytest freqtrade/tests/test_.py::test_ ### 2. Test if your code is PEP8 compliant -#### Install packages - -```bash -pip3.6 install flake8 coveralls -``` - #### Run Flake8 ```bash @@ -60,22 +58,12 @@ Guide for installing them is [here](http://flake8.pycqa.org/en/latest/user/using ### 3. Test if all type-hints are correct -#### Install packages - -``` bash -pip3.6 install mypy -``` - #### Run mypy ``` bash mypy freqtrade ``` -## Getting started - -Best start by reading the [documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/index.md) to get a feel for what is possible with the bot, or head straight to the [Developer-documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/developer.md) (WIP) which should help you getting started. - ## (Core)-Committer Guide ### Process: Pull Requests diff --git a/docs/developer.md b/docs/developer.md index 64f3b9a52..6fbcdc812 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -4,8 +4,20 @@ This page is intended for developers of FreqTrade, people who want to contribute All contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome. We [track issues](https://github.com/freqtrade/freqtrade/issues) on [GitHub](https://github.com) and also have a dev channel in [slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) where you can ask questions. +## Documentation -## Module +Documentation is available at [https://freqtrade.io](https://www.freqtrade.io/) and needs to be provided with every new feature PR. + +Special fields for the documentation (like Note boxes, ...) can be found [here](https://squidfunk.github.io/mkdocs-material/extensions/admonition/). + +## Developer setup + +To configure a development environment, use best use the `setup.sh` script and answer "y" when asked "Do you want to install dependencies for dev [y/N]? ". +Alternatively (if your system is not supported by the setup.sh script), follow the manual installation process and run `pip3 install -r requirements-dev.txt`. + +This will install all required tools for development, including `pytest`, `flake8`, `mypy`, and `coveralls`. + +## Modules ### Dynamic Pairlist From a6d74a146363e60d918b291f819d5de65ea57ccd Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 30 Nov 2018 20:42:16 +0100 Subject: [PATCH 200/358] Draft of dataprovider --- freqtrade/dataprovider.py | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 freqtrade/dataprovider.py diff --git a/freqtrade/dataprovider.py b/freqtrade/dataprovider.py new file mode 100644 index 000000000..0220048c6 --- /dev/null +++ b/freqtrade/dataprovider.py @@ -0,0 +1,45 @@ +""" +Dataprovider +Responsible to provide data to the bot +including Klines, tickers, historic data +Common Interface for bot and strategy to access data. +""" +import logging + +from freqtrade.exchange import Exchange + +logger = logging.getLogger(__name__) + + +class DataProvider(object): + + def __init__(self, exchange: Exchange) -> None: + pass + + def refresh() -> None: + """ + Refresh data, called with each cycle + """ + pass + + def kline(pair: str): + """ + get ohlcv data for the given pair + """ + pass + + def historic_kline(pair: str): + """ + get historic ohlcv data stored for backtesting + """ + pass + + def ticker(pair: str): + pass + + def orderbook(pair: str, max: int): + pass + + def balance(pair): + # TODO: maybe use wallet directly?? + pass From b119a767de5de4d66a2912c3b68a88d6cd19325d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 2 Dec 2018 09:16:35 +0100 Subject: [PATCH 201/358] Some more restructuring --- freqtrade/dataprovider.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/freqtrade/dataprovider.py b/freqtrade/dataprovider.py index 0220048c6..4b924bd9a 100644 --- a/freqtrade/dataprovider.py +++ b/freqtrade/dataprovider.py @@ -6,6 +6,8 @@ Common Interface for bot and strategy to access data. """ import logging +from pandas import DataFrame + from freqtrade.exchange import Exchange logger = logging.getLogger(__name__) @@ -13,8 +15,9 @@ logger = logging.getLogger(__name__) class DataProvider(object): - def __init__(self, exchange: Exchange) -> None: - pass + def __init__(self, config: dict, exchange: Exchange) -> None: + self._config = config + self._exchange = exchange def refresh() -> None: """ @@ -22,24 +25,31 @@ class DataProvider(object): """ pass - def kline(pair: str): + def ohlcv(self, pair: str) -> DataFrame: """ - get ohlcv data for the given pair + get ohlcv data for the given pair as DataFrame """ - pass + # TODO: Should not be stored in exchange but in this class + return self._exchange.klines.get(pair) - def historic_kline(pair: str): + def historic_ohlcv(self, pair: str) -> DataFrame: """ get historic ohlcv data stored for backtesting """ pass - def ticker(pair: str): + def ticker(self, pair: str): + """ + Return last ticker data + """ pass - def orderbook(pair: str, max: int): + def orderbook(self, pair: str, max: int): + """ + return latest orderbook data + """ pass - def balance(pair): + def balance(self, pair): # TODO: maybe use wallet directly?? pass From 7206287b001d561a2e32ee412bdd294501d9dd58 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 2 Dec 2018 15:57:49 +0100 Subject: [PATCH 202/358] Use Dataprovider --- freqtrade/dataprovider.py | 7 ++++--- freqtrade/freqtradebot.py | 9 ++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/freqtrade/dataprovider.py b/freqtrade/dataprovider.py index 4b924bd9a..035e9852a 100644 --- a/freqtrade/dataprovider.py +++ b/freqtrade/dataprovider.py @@ -5,6 +5,7 @@ including Klines, tickers, historic data Common Interface for bot and strategy to access data. """ import logging +from typing import List, Dict from pandas import DataFrame @@ -19,13 +20,13 @@ class DataProvider(object): self._config = config self._exchange = exchange - def refresh() -> None: + def refresh(self, pairlist: List[str]) -> None: """ Refresh data, called with each cycle """ - pass + self._exchange.refresh_tickers(pairlist, self._config['ticker_interval']) - def ohlcv(self, pair: str) -> DataFrame: + def ohlcv(self, pair: str) -> List[str]: """ get ohlcv data for the given pair as DataFrame """ diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 2e09cf116..9bcc3f86b 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -16,6 +16,7 @@ from freqtrade import (DependencyException, OperationalException, TemporaryError, __version__, constants, persistence) from freqtrade.data.converter import order_book_to_dataframe from freqtrade.edge import Edge +from freqtrade.dataprovider import DataProvider from freqtrade.exchange import Exchange from freqtrade.persistence import Trade from freqtrade.rpc import RPCManager, RPCMessageType @@ -57,6 +58,8 @@ class FreqtradeBot(object): self.persistence = None self.exchange = Exchange(self.config) self.wallets = Wallets(self.exchange) + + self.dataprovider = DataProvider(self.config, self.exchange) pairlistname = self.config.get('pairlist', {}).get('method', 'StaticPairList') self.pairlists = PairListResolver(pairlistname, self, self.config).pairlist @@ -167,7 +170,7 @@ class FreqtradeBot(object): if trade.pair not in self.active_pair_whitelist]) # Refreshing candles - self.exchange.refresh_tickers(self.active_pair_whitelist, self.strategy.ticker_interval) + self.dataprovider.refresh(self.active_pair_whitelist) # First process current opened trades for trade in trades: @@ -317,7 +320,7 @@ class FreqtradeBot(object): # running get_signal on historical data fetched for _pair in whitelist: - (buy, sell) = self.strategy.get_signal(_pair, interval, self.exchange.klines(_pair)) + (buy, sell) = self.strategy.get_signal(_pair, interval, self.dataprovider.ohlcv(_pair)) if buy and not sell: stake_amount = self._get_trade_stake_amount(_pair) if not stake_amount: @@ -579,7 +582,7 @@ class FreqtradeBot(object): experimental = self.config.get('experimental', {}) if experimental.get('use_sell_signal') or experimental.get('ignore_roi_if_buy_signal'): (buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.ticker_interval, - self.exchange.klines(trade.pair)) + self.dataprovider.ohlcv(trade.pair)) config_ask_strategy = self.config.get('ask_strategy', {}) if config_ask_strategy.get('use_order_book', False): From 05570732c6d3561d2aaac54b2e8967a17cd2eda2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 2 Dec 2018 21:57:30 +0100 Subject: [PATCH 203/358] add get_runmode --- freqtrade/dataprovider.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/freqtrade/dataprovider.py b/freqtrade/dataprovider.py index 035e9852a..ddbd35fb7 100644 --- a/freqtrade/dataprovider.py +++ b/freqtrade/dataprovider.py @@ -31,6 +31,7 @@ class DataProvider(object): get ohlcv data for the given pair as DataFrame """ # TODO: Should not be stored in exchange but in this class + # TODO: should return dataframe, not list return self._exchange.klines.get(pair) def historic_ohlcv(self, pair: str) -> DataFrame: @@ -54,3 +55,11 @@ class DataProvider(object): def balance(self, pair): # TODO: maybe use wallet directly?? pass + + @property + def runmode(self) -> str: + """ + Get runmode of the bot + can be "live", "dry-run", "backtest", "edgecli", "hyperopt". + """ + return self._config.get['runmode'] From 4ab7edd3d6f412abbc0f46dd3581cfa3ecdec4ba Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 17 Dec 2018 06:43:01 +0100 Subject: [PATCH 204/358] small adaptations --- freqtrade/{ => data}/dataprovider.py | 2 +- freqtrade/freqtradebot.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename freqtrade/{ => data}/dataprovider.py (97%) diff --git a/freqtrade/dataprovider.py b/freqtrade/data/dataprovider.py similarity index 97% rename from freqtrade/dataprovider.py rename to freqtrade/data/dataprovider.py index ddbd35fb7..32969fe3b 100644 --- a/freqtrade/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -32,7 +32,7 @@ class DataProvider(object): """ # TODO: Should not be stored in exchange but in this class # TODO: should return dataframe, not list - return self._exchange.klines.get(pair) + return self._exchange.klines(pair) def historic_ohlcv(self, pair: str) -> DataFrame: """ diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 9bcc3f86b..4d0369849 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -15,8 +15,8 @@ from requests.exceptions import RequestException from freqtrade import (DependencyException, OperationalException, TemporaryError, __version__, constants, persistence) from freqtrade.data.converter import order_book_to_dataframe +from freqtrade.data.dataprovider import DataProvider from freqtrade.edge import Edge -from freqtrade.dataprovider import DataProvider from freqtrade.exchange import Exchange from freqtrade.persistence import Trade from freqtrade.rpc import RPCManager, RPCMessageType From f1a5a8e20ee021650e365d144de055d4ec4bde01 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 17 Dec 2018 06:52:13 +0100 Subject: [PATCH 205/358] provide history --- freqtrade/data/dataprovider.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 32969fe3b..5970e826f 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -5,11 +5,13 @@ including Klines, tickers, historic data Common Interface for bot and strategy to access data. """ import logging +from pathlib import Path from typing import List, Dict from pandas import DataFrame from freqtrade.exchange import Exchange +from freqtrade.data.history import load_pair_history logger = logging.getLogger(__name__) @@ -31,14 +33,18 @@ class DataProvider(object): get ohlcv data for the given pair as DataFrame """ # TODO: Should not be stored in exchange but in this class - # TODO: should return dataframe, not list return self._exchange.klines(pair) - def historic_ohlcv(self, pair: str) -> DataFrame: + def historic_ohlcv(self, pair: str, ticker_interval: str) -> DataFrame: """ get historic ohlcv data stored for backtesting """ - pass + return load_pair_history(pair=pair, + ticker_interval=ticker_interval, + refresh_pairs=False, + datadir=Path(self.config['datadir']) if self.config.get( + 'datadir') else None + ) def ticker(self, pair: str): """ @@ -62,4 +68,5 @@ class DataProvider(object): Get runmode of the bot can be "live", "dry-run", "backtest", "edgecli", "hyperopt". """ + # TODO: this needs to be set somewhere ... return self._config.get['runmode'] From e38c06afe98c8b6405d6eab4802e46d5dd56e4b6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 25 Dec 2018 13:20:25 +0100 Subject: [PATCH 206/358] Small fixes --- freqtrade/data/dataprovider.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 5970e826f..142c12f2a 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -42,7 +42,7 @@ class DataProvider(object): return load_pair_history(pair=pair, ticker_interval=ticker_interval, refresh_pairs=False, - datadir=Path(self.config['datadir']) if self.config.get( + datadir=Path(self._config['datadir']) if self._config.get( 'datadir') else None ) @@ -69,4 +69,4 @@ class DataProvider(object): can be "live", "dry-run", "backtest", "edgecli", "hyperopt". """ # TODO: this needs to be set somewhere ... - return self._config.get['runmode'] + return str(self._config.get('runmode')) From 84cc4887cedd45b182c622c3e0a7bf3c39666381 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 25 Dec 2018 13:37:15 +0100 Subject: [PATCH 207/358] Add copy parameter --- freqtrade/data/dataprovider.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 142c12f2a..4d819a7d8 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -28,12 +28,15 @@ class DataProvider(object): """ self._exchange.refresh_tickers(pairlist, self._config['ticker_interval']) - def ohlcv(self, pair: str) -> List[str]: + def ohlcv(self, pair: str, copy: bool = True) -> List[str]: """ get ohlcv data for the given pair as DataFrame + :param pair: pair to get the data for + :param copy: copy dataframe before returning. + Use false only for RO operations (where the dataframe is not modified) """ # TODO: Should not be stored in exchange but in this class - return self._exchange.klines(pair) + return self._exchange.klines(pair, copy) def historic_ohlcv(self, pair: str, ticker_interval: str) -> DataFrame: """ From a7db4d74cb0a7b38159cab1d6586506e29cbd510 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 25 Dec 2018 13:56:30 +0100 Subject: [PATCH 208/358] Add some simple dataprovider tests --- freqtrade/tests/data/test_dataprovider.py | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 freqtrade/tests/data/test_dataprovider.py diff --git a/freqtrade/tests/data/test_dataprovider.py b/freqtrade/tests/data/test_dataprovider.py new file mode 100644 index 000000000..3eaa45abb --- /dev/null +++ b/freqtrade/tests/data/test_dataprovider.py @@ -0,0 +1,36 @@ +from unittest.mock import Mock, MagicMock, PropertyMock + +from pandas import DataFrame + +from freqtrade.data.dataprovider import DataProvider +from freqtrade.exchange import Exchange +from freqtrade.tests.conftest import get_patched_exchange, log_has + + +def test_ohlcv(mocker, default_conf, ticker_history): + + exchange = get_patched_exchange(mocker, default_conf) + exchange._klines['XRP/BTC'] = ticker_history + exchange._klines['UNITEST/BTC'] = ticker_history + dp = DataProvider(default_conf, exchange) + assert ticker_history.equals(dp.ohlcv('UNITEST/BTC')) + assert isinstance(dp.ohlcv('UNITEST/BTC'), DataFrame) + assert dp.ohlcv('UNITEST/BTC') is not ticker_history + assert dp.ohlcv('UNITEST/BTC', copy=False) is ticker_history + assert dp.ohlcv('NONESENSE/AAA') is None + + +def test_historic_ohlcv(mocker, default_conf, ticker_history): + + historymock = MagicMock(return_value=ticker_history) + mocker.patch('freqtrade.data.dataprovider.load_pair_history', historymock) + + # exchange = get_patched_exchange(mocker, default_conf) + dp = DataProvider(default_conf, None) + data = dp.historic_ohlcv('UNITTEST/BTC', "5m") + assert isinstance(data, DataFrame) + assert historymock.call_count == 1 + assert historymock.call_args_list[0][1]['datadir'] is None + assert historymock.call_args_list[0][1]['refresh_pairs'] == False + assert historymock.call_args_list[0][1]['ticker_interval'] == '5m' + From fed3ebfb468d4ce28346eb9860ba8449e202b122 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 25 Dec 2018 14:05:40 +0100 Subject: [PATCH 209/358] Change enum from 0 to 1 according to the documentation see [here](https://docs.python.org/3/library/enum.html#functional-api) --- freqtrade/state.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/state.py b/freqtrade/state.py index 42bfb6e41..4845b72f0 100644 --- a/freqtrade/state.py +++ b/freqtrade/state.py @@ -3,13 +3,13 @@ """ Bot state constant """ -import enum +from enum import Enum -class State(enum.Enum): +class State(Enum): """ Bot application states """ - RUNNING = 0 - STOPPED = 1 - RELOAD_CONF = 2 + RUNNING = 1 + STOPPED = 2 + RELOAD_CONF = 3 From 1340b71633b455fee16b88892b179cc54abdbe92 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 25 Dec 2018 14:23:59 +0100 Subject: [PATCH 210/358] Add RunMode setting to determine bot state --- freqtrade/configuration.py | 11 ++++++++++- freqtrade/data/dataprovider.py | 10 +++++----- freqtrade/main.py | 4 ++-- freqtrade/optimize/backtesting.py | 3 ++- freqtrade/optimize/edge_cli.py | 5 +++-- freqtrade/optimize/hyperopt.py | 3 ++- freqtrade/state.py | 13 +++++++++++++ freqtrade/tests/data/test_dataprovider.py | 8 +++----- scripts/plot_profit.py | 3 ++- 9 files changed, 42 insertions(+), 18 deletions(-) diff --git a/freqtrade/configuration.py b/freqtrade/configuration.py index 9fd93629f..d972f50b8 100644 --- a/freqtrade/configuration.py +++ b/freqtrade/configuration.py @@ -12,6 +12,7 @@ from jsonschema import Draft4Validator, validate from jsonschema.exceptions import ValidationError, best_match from freqtrade import OperationalException, constants +from freqtrade.state import RunMode logger = logging.getLogger(__name__) @@ -34,9 +35,10 @@ class Configuration(object): Reuse this class for the bot, backtesting, hyperopt and every script that required configuration """ - def __init__(self, args: Namespace) -> None: + def __init__(self, args: Namespace, runmode: RunMode = None) -> None: self.args = args self.config: Optional[Dict[str, Any]] = None + self.runmode = runmode def load_config(self) -> Dict[str, Any]: """ @@ -68,6 +70,13 @@ class Configuration(object): # Load Hyperopt config = self._load_hyperopt_config(config) + # Set runmode + if not self.runmode: + # Handle real mode, infer dry/live from config + self.runmode = RunMode.DRY_RUN if config.get('dry_run', True) else RunMode.LIVE + + config.update({'runmode': self.runmode}) + return config def _load_config_file(self, path: str) -> Dict[str, Any]: diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 4d819a7d8..5df2f2fd9 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -6,12 +6,13 @@ Common Interface for bot and strategy to access data. """ import logging from pathlib import Path -from typing import List, Dict +from typing import List from pandas import DataFrame -from freqtrade.exchange import Exchange from freqtrade.data.history import load_pair_history +from freqtrade.exchange import Exchange +from freqtrade.state import RunMode logger = logging.getLogger(__name__) @@ -66,10 +67,9 @@ class DataProvider(object): pass @property - def runmode(self) -> str: + def runmode(self) -> RunMode: """ Get runmode of the bot can be "live", "dry-run", "backtest", "edgecli", "hyperopt". """ - # TODO: this needs to be set somewhere ... - return str(self._config.get('runmode')) + return self._config.get('runmode') diff --git a/freqtrade/main.py b/freqtrade/main.py index f27145b45..75b15915b 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -39,7 +39,7 @@ def main(sysargv: List[str]) -> None: return_code = 1 try: # Load and validate configuration - config = Configuration(args).get_config() + config = Configuration(args, None).get_config() # Init the bot freqtrade = FreqtradeBot(config) @@ -76,7 +76,7 @@ def reconfigure(freqtrade: FreqtradeBot, args: Namespace) -> FreqtradeBot: freqtrade.cleanup() # Create new instance - freqtrade = FreqtradeBot(Configuration(args).get_config()) + freqtrade = FreqtradeBot(Configuration(args, None).get_config()) freqtrade.rpc.send_msg({ 'type': RPCMessageType.STATUS_NOTIFICATION, 'status': 'config reloaded' diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 38bbe13d4..88ad4cc60 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -22,6 +22,7 @@ from freqtrade.data import history from freqtrade.misc import file_dump_json from freqtrade.persistence import Trade from freqtrade.resolvers import StrategyResolver +from freqtrade.state import RunMode from freqtrade.strategy.interface import SellType, IStrategy logger = logging.getLogger(__name__) @@ -452,7 +453,7 @@ def setup_configuration(args: Namespace) -> Dict[str, Any]: :param args: Cli args from Arguments() :return: Configuration """ - configuration = Configuration(args) + configuration = Configuration(args, RunMode.BACKTEST) config = configuration.get_config() # Ensure we do not use Exchange credentials diff --git a/freqtrade/optimize/edge_cli.py b/freqtrade/optimize/edge_cli.py index fdae47b99..9b628cf2e 100644 --- a/freqtrade/optimize/edge_cli.py +++ b/freqtrade/optimize/edge_cli.py @@ -9,10 +9,11 @@ from typing import Dict, Any from tabulate import tabulate from freqtrade.edge import Edge -from freqtrade.configuration import Configuration from freqtrade.arguments import Arguments +from freqtrade.configuration import Configuration from freqtrade.exchange import Exchange from freqtrade.resolvers import StrategyResolver +from freqtrade.state import RunMode logger = logging.getLogger(__name__) @@ -83,7 +84,7 @@ def setup_configuration(args: Namespace) -> Dict[str, Any]: :param args: Cli args from Arguments() :return: Configuration """ - configuration = Configuration(args) + configuration = Configuration(args, RunMode.EDGECLI) config = configuration.get_config() # Ensure we do not use Exchange credentials diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 6930bed04..f6d39f11c 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -25,6 +25,7 @@ from freqtrade.configuration import Configuration from freqtrade.data.history import load_data from freqtrade.optimize import get_timeframe from freqtrade.optimize.backtesting import Backtesting +from freqtrade.state import RunMode from freqtrade.resolvers import HyperOptResolver logger = logging.getLogger(__name__) @@ -306,7 +307,7 @@ def start(args: Namespace) -> None: # Initialize configuration # Monkey patch the configuration with hyperopt_conf.py - configuration = Configuration(args) + configuration = Configuration(args, RunMode.HYPEROPT) logger.info('Starting freqtrade in Hyperopt mode') config = configuration.load_config() diff --git a/freqtrade/state.py b/freqtrade/state.py index 4845b72f0..b69c26cb5 100644 --- a/freqtrade/state.py +++ b/freqtrade/state.py @@ -13,3 +13,16 @@ class State(Enum): RUNNING = 1 STOPPED = 2 RELOAD_CONF = 3 + + +class RunMode(Enum): + """ + Bot running mode (backtest, hyperopt, ...) + can be "live", "dry-run", "backtest", "edgecli", "hyperopt". + """ + LIVE = "live" + DRY_RUN = "dry_run" + BACKTEST = "backtest" + EDGECLI = "edgecli" + HYPEROPT = "hyperopt" + OTHER = "other" # Used for plotting scripts and test diff --git a/freqtrade/tests/data/test_dataprovider.py b/freqtrade/tests/data/test_dataprovider.py index 3eaa45abb..154ef07a5 100644 --- a/freqtrade/tests/data/test_dataprovider.py +++ b/freqtrade/tests/data/test_dataprovider.py @@ -1,10 +1,9 @@ -from unittest.mock import Mock, MagicMock, PropertyMock +from unittest.mock import MagicMock from pandas import DataFrame from freqtrade.data.dataprovider import DataProvider -from freqtrade.exchange import Exchange -from freqtrade.tests.conftest import get_patched_exchange, log_has +from freqtrade.tests.conftest import get_patched_exchange def test_ohlcv(mocker, default_conf, ticker_history): @@ -31,6 +30,5 @@ def test_historic_ohlcv(mocker, default_conf, ticker_history): assert isinstance(data, DataFrame) assert historymock.call_count == 1 assert historymock.call_args_list[0][1]['datadir'] is None - assert historymock.call_args_list[0][1]['refresh_pairs'] == False + assert historymock.call_args_list[0][1]['refresh_pairs'] is False assert historymock.call_args_list[0][1]['ticker_interval'] == '5m' - diff --git a/scripts/plot_profit.py b/scripts/plot_profit.py index a1561bc89..72ac4031a 100755 --- a/scripts/plot_profit.py +++ b/scripts/plot_profit.py @@ -29,6 +29,7 @@ from freqtrade.configuration import Configuration from freqtrade import constants from freqtrade.data import history from freqtrade.resolvers import StrategyResolver +from freqtrade.state import RunMode import freqtrade.misc as misc @@ -82,7 +83,7 @@ def plot_profit(args: Namespace) -> None: # to match the tickerdata against the profits-results timerange = Arguments.parse_timerange(args.timerange) - config = Configuration(args).get_config() + config = Configuration(args, RunMode.OTHER).get_config() # Init strategy try: From f034235af4dac69e48429cb31328e89197815f97 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 25 Dec 2018 14:35:48 +0100 Subject: [PATCH 211/358] Tests for RunMode --- freqtrade/data/dataprovider.py | 2 +- freqtrade/tests/optimize/test_backtesting.py | 5 +++++ freqtrade/tests/optimize/test_edge_cli.py | 4 ++++ freqtrade/tests/test_configuration.py | 12 ++++++++++-- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 5df2f2fd9..dbca1e035 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -72,4 +72,4 @@ class DataProvider(object): Get runmode of the bot can be "live", "dry-run", "backtest", "edgecli", "hyperopt". """ - return self._config.get('runmode') + return RunMode(self._config.get('runmode', RunMode.OTHER)) diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 5ab44baad..beef1b16e 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -18,6 +18,7 @@ from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.optimize import get_timeframe from freqtrade.optimize.backtesting import (Backtesting, setup_configuration, start) +from freqtrade.state import RunMode from freqtrade.strategy.default_strategy import DefaultStrategy from freqtrade.strategy.interface import SellType from freqtrade.tests.conftest import log_has, patch_exchange @@ -200,6 +201,8 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> assert 'timerange' not in config assert 'export' not in config + assert 'runmode' in config + assert config['runmode'] == RunMode.BACKTEST def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> None: @@ -230,6 +233,8 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> assert 'exchange' in config assert 'pair_whitelist' in config['exchange'] assert 'datadir' in config + assert config['runmode'] == RunMode.BACKTEST + assert log_has( 'Using data folder: {} ...'.format(config['datadir']), caplog.record_tuples diff --git a/freqtrade/tests/optimize/test_edge_cli.py b/freqtrade/tests/optimize/test_edge_cli.py index 8ffab7f11..a58620139 100644 --- a/freqtrade/tests/optimize/test_edge_cli.py +++ b/freqtrade/tests/optimize/test_edge_cli.py @@ -7,6 +7,7 @@ from typing import List from freqtrade.edge import PairInfo from freqtrade.arguments import Arguments from freqtrade.optimize.edge_cli import (EdgeCli, setup_configuration, start) +from freqtrade.state import RunMode from freqtrade.tests.conftest import log_has, patch_exchange @@ -26,6 +27,8 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> ] config = setup_configuration(get_args(args)) + assert config['runmode'] == RunMode.EDGECLI + assert 'max_open_trades' in config assert 'stake_currency' in config assert 'stake_amount' in config @@ -70,6 +73,7 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N assert 'exchange' in config assert 'pair_whitelist' in config['exchange'] assert 'datadir' in config + assert config['runmode'] == RunMode.EDGECLI assert log_has( 'Using data folder: {} ...'.format(config['datadir']), caplog.record_tuples diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index f5c887089..67445238b 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -13,6 +13,7 @@ from freqtrade import OperationalException from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration, set_loggers from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL +from freqtrade.state import RunMode from freqtrade.tests.conftest import log_has @@ -77,6 +78,8 @@ def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) -> assert validated_conf['max_open_trades'] > 999999999 assert validated_conf['max_open_trades'] == float('inf') assert log_has('Validating configuration ...', caplog.record_tuples) + assert "runmode" in validated_conf + assert validated_conf['runmode'] == RunMode.DRY_RUN def test_load_config_file_exception(mocker) -> None: @@ -177,6 +180,8 @@ def test_load_config_with_params(default_conf, mocker) -> None: configuration = Configuration(args) validated_conf = configuration.load_config() assert validated_conf.get('db_url') == DEFAULT_DB_PROD_URL + assert "runmode" in validated_conf + assert validated_conf['runmode'] == RunMode.LIVE # Test args provided db_url dry_run conf = default_conf.copy() @@ -365,8 +370,9 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non args = Arguments(arglist, '').get_parsed_arg() - configuration = Configuration(args) + configuration = Configuration(args, RunMode.BACKTEST) config = configuration.get_config() + assert config['runmode'] == RunMode.BACKTEST assert 'max_open_trades' in config assert 'stake_currency' in config assert 'stake_amount' in config @@ -411,7 +417,7 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None: ] args = Arguments(arglist, '').get_parsed_arg() - configuration = Configuration(args) + configuration = Configuration(args, RunMode.HYPEROPT) config = configuration.get_config() assert 'epochs' in config @@ -422,6 +428,8 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None: assert 'spaces' in config assert config['spaces'] == ['all'] assert log_has('Parameter -s/--spaces detected: [\'all\']', caplog.record_tuples) + assert "runmode" in config + assert config['runmode'] == RunMode.HYPEROPT def test_check_exchange(default_conf, caplog) -> None: From d3a37db79ada8a9802488df419883995b34d7d60 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 26 Dec 2018 14:23:21 +0100 Subject: [PATCH 212/358] Provide available pairs --- freqtrade/data/dataprovider.py | 8 ++++++++ freqtrade/tests/data/test_dataprovider.py | 20 +++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index dbca1e035..31489d43c 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -29,6 +29,14 @@ class DataProvider(object): """ self._exchange.refresh_tickers(pairlist, self._config['ticker_interval']) + @property + def available_pairs(self) -> List[str]: + """ + Return a list of pairs for which data is currently cached. + Should be whitelist + open trades. + """ + return list(self._exchange._klines.keys()) + def ohlcv(self, pair: str, copy: bool = True) -> List[str]: """ get ohlcv data for the given pair as DataFrame diff --git a/freqtrade/tests/data/test_dataprovider.py b/freqtrade/tests/data/test_dataprovider.py index 154ef07a5..86fcf3699 100644 --- a/freqtrade/tests/data/test_dataprovider.py +++ b/freqtrade/tests/data/test_dataprovider.py @@ -10,12 +10,12 @@ def test_ohlcv(mocker, default_conf, ticker_history): exchange = get_patched_exchange(mocker, default_conf) exchange._klines['XRP/BTC'] = ticker_history - exchange._klines['UNITEST/BTC'] = ticker_history + exchange._klines['UNITTEST/BTC'] = ticker_history dp = DataProvider(default_conf, exchange) - assert ticker_history.equals(dp.ohlcv('UNITEST/BTC')) - assert isinstance(dp.ohlcv('UNITEST/BTC'), DataFrame) - assert dp.ohlcv('UNITEST/BTC') is not ticker_history - assert dp.ohlcv('UNITEST/BTC', copy=False) is ticker_history + assert ticker_history.equals(dp.ohlcv('UNITTEST/BTC')) + assert isinstance(dp.ohlcv('UNITTEST/BTC'), DataFrame) + assert dp.ohlcv('UNITTEST/BTC') is not ticker_history + assert dp.ohlcv('UNITTEST/BTC', copy=False) is ticker_history assert dp.ohlcv('NONESENSE/AAA') is None @@ -32,3 +32,13 @@ def test_historic_ohlcv(mocker, default_conf, ticker_history): assert historymock.call_args_list[0][1]['datadir'] is None assert historymock.call_args_list[0][1]['refresh_pairs'] is False assert historymock.call_args_list[0][1]['ticker_interval'] == '5m' + + +def test_available_pairs(mocker, default_conf, ticker_history): + exchange = get_patched_exchange(mocker, default_conf) + exchange._klines['XRP/BTC'] = ticker_history + exchange._klines['UNITTEST/BTC'] = ticker_history + dp = DataProvider(default_conf, exchange) + + assert len(dp.available_pairs) == 2 + assert dp.available_pairs == ['XRP/BTC', 'UNITTEST/BTC'] From 58f1abf28712cb3f3c8aa3d4ff05b26f0c5dc5dc Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 26 Dec 2018 14:32:17 +0100 Subject: [PATCH 213/358] Add dp / wallets to strategy interface --- freqtrade/freqtradebot.py | 7 ++++++- freqtrade/strategy/interface.py | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4d0369849..a096a37da 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -58,8 +58,13 @@ class FreqtradeBot(object): self.persistence = None self.exchange = Exchange(self.config) self.wallets = Wallets(self.exchange) - self.dataprovider = DataProvider(self.config, self.exchange) + + # Attach Dataprovider to Strategy baseclass + IStrategy.dp = self.dataprovider + # Attach Wallets to Strategy baseclass + IStrategy.wallets = self.wallets + pairlistname = self.config.get('pairlist', {}).get('method', 'StaticPairList') self.pairlists = PairListResolver(pairlistname, self, self.config).pairlist diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 08a5cf1cd..7210f5c78 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -13,7 +13,9 @@ import arrow from pandas import DataFrame from freqtrade import constants +from freqtrade.data.dataprovider import DataProvider from freqtrade.persistence import Trade +from freqtrade.wallets import Wallets logger = logging.getLogger(__name__) @@ -96,6 +98,12 @@ class IStrategy(ABC): # Dict to determine if analysis is necessary _last_candle_seen_per_pair: Dict[str, datetime] = {} + # Class level variables (intentional) containing + # the dataprovider (dp) (access to other candles, historic data, ...) + # and wallets - access to the current balance. + dp: DataProvider + wallets: Wallets + def __init__(self, config: dict) -> None: self.config = config self._last_candle_seen_per_pair = {} From 5ecdecd1ebd852e1a6fb99b34973650878724f02 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 26 Dec 2018 14:37:25 +0100 Subject: [PATCH 214/358] remove unused local variable persistance --- freqtrade/freqtradebot.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index a096a37da..071cbaf2e 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -37,9 +37,9 @@ class FreqtradeBot(object): def __init__(self, config: Dict[str, Any])-> None: """ - Init all variables and object the bot need to work - :param config: configuration dict, you can use the Configuration.get_config() - method to get the config dict. + Init all variables and objects the bot needs to work + :param config: configuration dict, you can use Configuration.get_config() + to get the config dict. """ logger.info( @@ -55,7 +55,6 @@ class FreqtradeBot(object): self.strategy: IStrategy = StrategyResolver(self.config).strategy self.rpc: RPCManager = RPCManager(self) - self.persistence = None self.exchange = Exchange(self.config) self.wallets = Wallets(self.exchange) self.dataprovider = DataProvider(self.config, self.exchange) From 8f3ea3608adbcc45616e625d399bdcb600e06e7c Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 26 Dec 2018 14:58:16 +0100 Subject: [PATCH 215/358] some cleanup --- freqtrade/data/dataprovider.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 31489d43c..758e401e8 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -62,16 +62,14 @@ class DataProvider(object): """ Return last ticker data """ + # TODO: Implement me pass def orderbook(self, pair: str, max: int): """ return latest orderbook data """ - pass - - def balance(self, pair): - # TODO: maybe use wallet directly?? + # TODO: Implement me pass @property From 35c8d1dcbe85441cb89f4be2fafcbefa5b144824 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 29 Dec 2018 08:47:14 +0100 Subject: [PATCH 216/358] Update comment --- freqtrade/data/dataprovider.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 758e401e8..c9c3e452a 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -40,6 +40,7 @@ class DataProvider(object): def ohlcv(self, pair: str, copy: bool = True) -> List[str]: """ get ohlcv data for the given pair as DataFrame + Please check `available_pairs` to verify which pairs are currently cached. :param pair: pair to get the data for :param copy: copy dataframe before returning. Use false only for RO operations (where the dataframe is not modified) From 9edb88051d8291ab33eb199a6c449a9095e3b62d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 29 Dec 2018 08:47:25 +0100 Subject: [PATCH 217/358] Add dataprovider documentation --- docs/bot-optimization.md | 51 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/docs/bot-optimization.md b/docs/bot-optimization.md index 1cfae1bc4..6d03f55ac 100644 --- a/docs/bot-optimization.md +++ b/docs/bot-optimization.md @@ -222,6 +222,57 @@ Please note that the same buy/sell signals may work with one interval, but not t The metadata-dict (available for `populate_buy_trend`, `populate_sell_trend`, `populate_indicators`) contains additional information. Currently this is `pair`, which can be accessed using `metadata['pair']` - and will return a pair in the format `XRP/BTC`. +### Additional data (DataProvider) + +The strategy provides access to the `DataProvider`. This allows you to get additional data to use in your strategy. + +**NOTE**: The DataProvier is currently not available during backtesting / hyperopt. + +Please always check if the `DataProvider` is available to avoid failures during backtesting. + +``` python +if self.dp: + if dp.runmode == 'live': + if 'ETH/BTC' in self.dp.available_pairs: + data_eth = self.dp.ohlcv(pair='ETH/BTC', + ticker_interval=ticker_interval) + else: + # Get historic ohlcv data (cached on disk). + history_eth = self.dp.historic_ohlcv(pair='ETH/BTC', + ticker_interval='1h') +``` + +All methods return `None` in case of failure (do not raise an exception). + +#### Possible options for DataProvider + +- `available_pairs` - Property containing cached pairs +- `ohlcv(pair, ticker_interval)` - Currently cached ticker data for all pairs in the whitelist +- `historic_ohlcv(pair, ticker_interval)` - Data stored on disk +- `runmode` - Property containing the current runmode. + +### Additional data - Wallets + +The strategy provides access to the `Wallets` object. This contains the current balances on the exchange. + +**NOTE**: Wallets is not available during backtesting / hyperopt. + +Please always check if `Wallets` is available to avoid failures during backtesting. + +``` python +if self.wallets: + free_eth = self.wallets.get_free('ETH') + used_eth = self.wallets.get_used('ETH') + total_eth = self.wallets.get_total('ETH') +``` + +#### Possible options for Wallets + +- `get_free(asset)` - currently available balance to trade +- `get_used(asset)` - currently tied up balance (open orders) +- `get_total(asset)` - total available balance - sum of the 2 above + + ### Where is the default strategy? The default buy strategy is located in the file From 2b029b2a8627be34d02ec6025fd43fed473d8b4c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 29 Dec 2018 09:13:20 +0100 Subject: [PATCH 218/358] Only return ohlcv if available (Live and dry modes) --- freqtrade/data/dataprovider.py | 7 +++++-- freqtrade/tests/data/test_dataprovider.py | 14 +++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index c9c3e452a..e75f6ce33 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -46,7 +46,10 @@ class DataProvider(object): Use false only for RO operations (where the dataframe is not modified) """ # TODO: Should not be stored in exchange but in this class - return self._exchange.klines(pair, copy) + if self.runmode in (RunMode.DRY_RUN, RunMode.LIVE): + return self._exchange.klines(pair, copy) + else: + return None def historic_ohlcv(self, pair: str, ticker_interval: str) -> DataFrame: """ @@ -77,6 +80,6 @@ class DataProvider(object): def runmode(self) -> RunMode: """ Get runmode of the bot - can be "live", "dry-run", "backtest", "edgecli", "hyperopt". + can be "live", "dry-run", "backtest", "edgecli", "hyperopt" or "other". """ return RunMode(self._config.get('runmode', RunMode.OTHER)) diff --git a/freqtrade/tests/data/test_dataprovider.py b/freqtrade/tests/data/test_dataprovider.py index 86fcf3699..9988d0fef 100644 --- a/freqtrade/tests/data/test_dataprovider.py +++ b/freqtrade/tests/data/test_dataprovider.py @@ -3,21 +3,33 @@ from unittest.mock import MagicMock from pandas import DataFrame from freqtrade.data.dataprovider import DataProvider +from freqtrade.state import RunMode from freqtrade.tests.conftest import get_patched_exchange def test_ohlcv(mocker, default_conf, ticker_history): - + default_conf['runmode'] = RunMode.DRY_RUN exchange = get_patched_exchange(mocker, default_conf) exchange._klines['XRP/BTC'] = ticker_history exchange._klines['UNITTEST/BTC'] = ticker_history dp = DataProvider(default_conf, exchange) + assert dp.runmode == RunMode.DRY_RUN assert ticker_history.equals(dp.ohlcv('UNITTEST/BTC')) assert isinstance(dp.ohlcv('UNITTEST/BTC'), DataFrame) assert dp.ohlcv('UNITTEST/BTC') is not ticker_history assert dp.ohlcv('UNITTEST/BTC', copy=False) is ticker_history assert dp.ohlcv('NONESENSE/AAA') is None + default_conf['runmode'] = RunMode.LIVE + dp = DataProvider(default_conf, exchange) + assert dp.runmode == RunMode.LIVE + assert isinstance(dp.ohlcv('UNITTEST/BTC'), DataFrame) + + default_conf['runmode'] = RunMode.BACKTEST + dp = DataProvider(default_conf, exchange) + assert dp.runmode == RunMode.BACKTEST + assert dp.ohlcv('UNITTEST/BTC') is None + def test_historic_ohlcv(mocker, default_conf, ticker_history): From 646e98da55bd0e20c2a6dfa42e5a5272d319bead Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 29 Dec 2018 13:00:50 +0100 Subject: [PATCH 219/358] Always return dataframe --- docs/bot-optimization.md | 2 +- freqtrade/data/dataprovider.py | 4 ++-- freqtrade/exchange/__init__.py | 2 +- freqtrade/tests/data/test_dataprovider.py | 5 +++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/bot-optimization.md b/docs/bot-optimization.md index 6d03f55ac..7fee559b8 100644 --- a/docs/bot-optimization.md +++ b/docs/bot-optimization.md @@ -247,7 +247,7 @@ All methods return `None` in case of failure (do not raise an exception). #### Possible options for DataProvider - `available_pairs` - Property containing cached pairs -- `ohlcv(pair, ticker_interval)` - Currently cached ticker data for all pairs in the whitelist +- `ohlcv(pair, ticker_interval)` - Currently cached ticker data for all pairs in the whitelist, returns DataFrame or empty DataFrame - `historic_ohlcv(pair, ticker_interval)` - Data stored on disk - `runmode` - Property containing the current runmode. diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index e75f6ce33..4f854e647 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -37,7 +37,7 @@ class DataProvider(object): """ return list(self._exchange._klines.keys()) - def ohlcv(self, pair: str, copy: bool = True) -> List[str]: + def ohlcv(self, pair: str, copy: bool = True) -> DataFrame: """ get ohlcv data for the given pair as DataFrame Please check `available_pairs` to verify which pairs are currently cached. @@ -49,7 +49,7 @@ class DataProvider(object): if self.runmode in (RunMode.DRY_RUN, RunMode.LIVE): return self._exchange.klines(pair, copy) else: - return None + return DataFrame() def historic_ohlcv(self, pair: str, ticker_interval: str) -> DataFrame: """ diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index e0e4d7723..03b28939e 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -162,7 +162,7 @@ class Exchange(object): if pair in self._klines: return self._klines[pair].copy() if copy else self._klines[pair] else: - return None + return DataFrame() def set_sandbox(self, api, exchange_config: dict, name: str): if exchange_config.get('sandbox'): diff --git a/freqtrade/tests/data/test_dataprovider.py b/freqtrade/tests/data/test_dataprovider.py index 9988d0fef..9f17da391 100644 --- a/freqtrade/tests/data/test_dataprovider.py +++ b/freqtrade/tests/data/test_dataprovider.py @@ -18,7 +18,8 @@ def test_ohlcv(mocker, default_conf, ticker_history): assert isinstance(dp.ohlcv('UNITTEST/BTC'), DataFrame) assert dp.ohlcv('UNITTEST/BTC') is not ticker_history assert dp.ohlcv('UNITTEST/BTC', copy=False) is ticker_history - assert dp.ohlcv('NONESENSE/AAA') is None + assert not dp.ohlcv('UNITTEST/BTC').empty + assert dp.ohlcv('NONESENSE/AAA').empty default_conf['runmode'] = RunMode.LIVE dp = DataProvider(default_conf, exchange) @@ -28,7 +29,7 @@ def test_ohlcv(mocker, default_conf, ticker_history): default_conf['runmode'] = RunMode.BACKTEST dp = DataProvider(default_conf, exchange) assert dp.runmode == RunMode.BACKTEST - assert dp.ohlcv('UNITTEST/BTC') is None + assert dp.ohlcv('UNITTEST/BTC').empty def test_historic_ohlcv(mocker, default_conf, ticker_history): From 06ec1060797f86d84dd6afdca8da42c5b5b50705 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 29 Dec 2018 13:07:22 +0100 Subject: [PATCH 220/358] simplify refresh_tickers --- freqtrade/exchange/__init__.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 03b28939e..84f20a7fe 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -530,30 +530,26 @@ class Exchange(object): logger.info("downloaded %s with length %s.", pair, len(data)) return data - def refresh_tickers(self, pair_list: List[str], ticker_interval: str) -> None: + def refresh_tickers(self, pair_list: List[str], ticker_interval: str) -> List[Tuple[str, List]]: """ - Refresh tickers asyncronously and set `_klines` of this object with the result + Refresh ohlcv asyncronously and set `_klines` with the result """ logger.debug("Refreshing klines for %d pairs", len(pair_list)) - asyncio.get_event_loop().run_until_complete( - self.async_get_candles_history(pair_list, ticker_interval)) - async def async_get_candles_history(self, pairs: List[str], - tick_interval: str) -> List[Tuple[str, List]]: - """Download ohlcv history for pair-list asyncronously """ # Calculating ticker interval in second - interval_in_sec = constants.TICKER_INTERVAL_MINUTES[tick_interval] * 60 + interval_in_sec = constants.TICKER_INTERVAL_MINUTES[ticker_interval] * 60 input_coroutines = [] # Gather corotines to run - for pair in pairs: + for pair in pair_list: if not (self._pairs_last_refresh_time.get(pair, 0) + interval_in_sec >= arrow.utcnow().timestamp and pair in self._klines): - input_coroutines.append(self._async_get_candle_history(pair, tick_interval)) + input_coroutines.append(self._async_get_candle_history(pair, ticker_interval)) else: logger.debug("Using cached klines data for %s ...", pair) - tickers = await asyncio.gather(*input_coroutines, return_exceptions=True) + tickers = asyncio.get_event_loop().run_until_complete( + asyncio.gather(*input_coroutines, return_exceptions=True)) # handle caching for res in tickers: From a206777fe50c2ad25c71cdf14c0705bc12d30f0d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 29 Dec 2018 13:13:07 +0100 Subject: [PATCH 221/358] Rename refresh_tickers to refresh_latest_ohlcv --- freqtrade/data/dataprovider.py | 2 +- freqtrade/exchange/__init__.py | 5 +++-- freqtrade/freqtradebot.py | 4 ++-- freqtrade/optimize/backtesting.py | 2 +- freqtrade/tests/exchange/test_exchange.py | 6 +++--- freqtrade/tests/optimize/test_backtesting.py | 4 ++-- freqtrade/tests/test_freqtradebot.py | 2 +- scripts/plot_dataframe.py | 2 +- 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 4f854e647..628ca1dd4 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -27,7 +27,7 @@ class DataProvider(object): """ Refresh data, called with each cycle """ - self._exchange.refresh_tickers(pairlist, self._config['ticker_interval']) + self._exchange.refresh_latest_ohlcv(pairlist, self._config['ticker_interval']) @property def available_pairs(self) -> List[str]: diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 84f20a7fe..03acc2e61 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -530,9 +530,10 @@ class Exchange(object): logger.info("downloaded %s with length %s.", pair, len(data)) return data - def refresh_tickers(self, pair_list: List[str], ticker_interval: str) -> List[Tuple[str, List]]: + def refresh_latest_ohlcv(self, pair_list: List[str], + ticker_interval: str) -> List[Tuple[str, List]]: """ - Refresh ohlcv asyncronously and set `_klines` with the result + Refresh in-memory ohlcv asyncronously and set `_klines` with the result """ logger.debug("Refreshing klines for %d pairs", len(pair_list)) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 071cbaf2e..41cd35da7 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -158,9 +158,9 @@ class FreqtradeBot(object): self.active_pair_whitelist = self.pairlists.whitelist # Calculating Edge positiong - # Should be called before refresh_tickers + # Should be called before refresh_latest_ohlcv # Otherwise it will override cached klines in exchange - # with delta value (klines only from last refresh_pairs) + # with delta value (klines only from last refresh_latest_ohlcv) if self.edge: self.edge.calculate() self.active_pair_whitelist = self.edge.adjust(self.active_pair_whitelist) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 88ad4cc60..192f8cff0 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -368,7 +368,7 @@ class Backtesting(object): if self.config.get('live'): logger.info('Downloading data for all pairs in whitelist ...') - self.exchange.refresh_tickers(pairs, self.ticker_interval) + self.exchange.refresh_latest_ohlcv(pairs, self.ticker_interval) data = self.exchange._klines else: logger.info('Using local backtesting data (using whitelist in given config) ...') diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index 26808e78a..e0be9a4bb 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -778,7 +778,7 @@ def test_get_history(default_conf, mocker, caplog): assert len(ret) == 2 -def test_refresh_tickers(mocker, default_conf, caplog) -> None: +def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None: tick = [ [ (arrow.utcnow().timestamp - 1) * 1000, # unix timestamp ms @@ -805,7 +805,7 @@ def test_refresh_tickers(mocker, default_conf, caplog) -> None: pairs = ['IOTA/ETH', 'XRP/ETH'] # empty dicts assert not exchange._klines - exchange.refresh_tickers(['IOTA/ETH', 'XRP/ETH'], '5m') + exchange.refresh_latest_ohlcv(['IOTA/ETH', 'XRP/ETH'], '5m') assert log_has(f'Refreshing klines for {len(pairs)} pairs', caplog.record_tuples) assert exchange._klines @@ -822,7 +822,7 @@ def test_refresh_tickers(mocker, default_conf, caplog) -> None: assert exchange.klines(pair, copy=False) is exchange.klines(pair, copy=False) # test caching - exchange.refresh_tickers(['IOTA/ETH', 'XRP/ETH'], '5m') + exchange.refresh_latest_ohlcv(['IOTA/ETH', 'XRP/ETH'], '5m') assert exchange._api_async.fetch_ohlcv.call_count == 2 assert log_has(f"Using cached klines data for {pairs[0]} ...", caplog.record_tuples) diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index beef1b16e..b0e5da0f8 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -448,7 +448,7 @@ def test_backtesting_start(default_conf, mocker, caplog) -> None: mocker.patch('freqtrade.data.history.load_data', mocked_load_data) mocker.patch('freqtrade.optimize.get_timeframe', get_timeframe) - mocker.patch('freqtrade.exchange.Exchange.refresh_tickers', MagicMock()) + mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', MagicMock()) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.optimize.backtesting.Backtesting', @@ -483,7 +483,7 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None: mocker.patch('freqtrade.data.history.load_data', MagicMock(return_value={})) mocker.patch('freqtrade.optimize.get_timeframe', get_timeframe) - mocker.patch('freqtrade.exchange.Exchange.refresh_tickers', MagicMock()) + mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', MagicMock()) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.optimize.backtesting.Backtesting', diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 1149a69e9..ca6190ae4 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -43,7 +43,7 @@ def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False)) -> None: :return: None """ freqtrade.strategy.get_signal = lambda e, s, t: value - freqtrade.exchange.refresh_tickers = lambda p, i: None + freqtrade.exchange.refresh_latest_ohlcv = lambda p, i: None def patch_RPCManager(mocker) -> MagicMock: diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index ae9cd7f1d..8f3e8327a 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -138,7 +138,7 @@ def plot_analyzed_dataframe(args: Namespace) -> None: tickers = {} if args.live: logger.info('Downloading pair.') - exchange.refresh_tickers([pair], tick_interval) + exchange.refresh_latest_ohlcv([pair], tick_interval) tickers[pair] = exchange.klines(pair) else: tickers = history.load_data( From b981cfcaa0b800cac89b8417690a42e96a90e13d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 29 Dec 2018 14:15:13 +0100 Subject: [PATCH 222/358] remove comment which proves untrue now --- freqtrade/freqtradebot.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 41cd35da7..6b27130bc 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -158,9 +158,6 @@ class FreqtradeBot(object): self.active_pair_whitelist = self.pairlists.whitelist # Calculating Edge positiong - # Should be called before refresh_latest_ohlcv - # Otherwise it will override cached klines in exchange - # with delta value (klines only from last refresh_latest_ohlcv) if self.edge: self.edge.calculate() self.active_pair_whitelist = self.edge.adjust(self.active_pair_whitelist) From e503d811bd1e33eff318cadd6866bff97cab0993 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 29 Dec 2018 14:18:49 +0100 Subject: [PATCH 223/358] Change logmessages to match functions called --- freqtrade/exchange/__init__.py | 6 +++--- freqtrade/tests/exchange/test_exchange.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 03acc2e61..243badfcb 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -525,7 +525,7 @@ class Exchange(object): for p, ticker in tickers: if p == pair: data.extend(ticker) - # Sort data again after extending the result - above calls return in "async order" order + # Sort data again after extending the result - above calls return in "async order" data = sorted(data, key=lambda x: x[0]) logger.info("downloaded %s with length %s.", pair, len(data)) return data @@ -535,7 +535,7 @@ class Exchange(object): """ Refresh in-memory ohlcv asyncronously and set `_klines` with the result """ - logger.debug("Refreshing klines for %d pairs", len(pair_list)) + logger.debug("Refreshing ohlcv data for %d pairs", len(pair_list)) # Calculating ticker interval in second interval_in_sec = constants.TICKER_INTERVAL_MINUTES[ticker_interval] * 60 @@ -547,7 +547,7 @@ class Exchange(object): arrow.utcnow().timestamp and pair in self._klines): input_coroutines.append(self._async_get_candle_history(pair, ticker_interval)) else: - logger.debug("Using cached klines data for %s ...", pair) + logger.debug("Using cached ohlcv data for %s ...", pair) tickers = asyncio.get_event_loop().run_until_complete( asyncio.gather(*input_coroutines, return_exceptions=True)) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index e0be9a4bb..ee53f5cdd 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -807,7 +807,7 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None: assert not exchange._klines exchange.refresh_latest_ohlcv(['IOTA/ETH', 'XRP/ETH'], '5m') - assert log_has(f'Refreshing klines for {len(pairs)} pairs', caplog.record_tuples) + assert log_has(f'Refreshing ohlcv data for {len(pairs)} pairs', caplog.record_tuples) assert exchange._klines assert exchange._api_async.fetch_ohlcv.call_count == 2 for pair in pairs: @@ -825,7 +825,7 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None: exchange.refresh_latest_ohlcv(['IOTA/ETH', 'XRP/ETH'], '5m') assert exchange._api_async.fetch_ohlcv.call_count == 2 - assert log_has(f"Using cached klines data for {pairs[0]} ...", caplog.record_tuples) + assert log_has(f"Using cached ohlcv data for {pairs[0]} ...", caplog.record_tuples) @pytest.mark.asyncio @@ -854,7 +854,7 @@ async def test__async_get_candle_history(default_conf, mocker, caplog): assert res[0] == pair assert res[1] == tick assert exchange._api_async.fetch_ohlcv.call_count == 1 - assert not log_has(f"Using cached klines data for {pair} ...", caplog.record_tuples) + assert not log_has(f"Using cached ohlcv data for {pair} ...", caplog.record_tuples) # exchange = Exchange(default_conf) await async_ccxt_exception(mocker, default_conf, MagicMock(), From d6df3e55c05ea5f8a7e9950f86d5d4160d83a042 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 29 Dec 2018 14:32:24 +0100 Subject: [PATCH 224/358] Return ticker_interval from async routine used to identify calls in refresh_latest_ohlcv --- freqtrade/exchange/__init__.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 243badfcb..7ce4a38c1 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -522,7 +522,7 @@ class Exchange(object): # Combine tickers data: List = [] - for p, ticker in tickers: + for p, ticker_interval, ticker in tickers: if p == pair: data.extend(ticker) # Sort data again after extending the result - above calls return in "async order" @@ -558,7 +558,8 @@ class Exchange(object): logger.warning("Async code raised an exception: %s", res.__class__.__name__) continue pair = res[0] - ticks = res[1] + tick_interval[1] + ticks = res[2] # keeping last candle time as last refreshed time of the pair if ticks: self._pairs_last_refresh_time[pair] = ticks[-1][0] // 1000 @@ -568,7 +569,11 @@ class Exchange(object): @retrier_async async def _async_get_candle_history(self, pair: str, tick_interval: str, - since_ms: Optional[int] = None) -> Tuple[str, List]: + since_ms: Optional[int] = None) -> Tuple[str, str, List]: + """ + Asyncronously gets candle histories using fetch_ohlcv + returns tuple: (pair, tick_interval, ohlcv_list) + """ try: # fetch ohlcv asynchronously logger.debug("fetching %s since %s ...", pair, since_ms) @@ -587,7 +592,7 @@ class Exchange(object): logger.exception("Error loading %s. Result was %s.", pair, data) return pair, [] logger.debug("done fetching %s ...", pair) - return pair, data + return pair, tick_interval, data except ccxt.NotSupported as e: raise OperationalException( From 5f61da30ed8049b2503de6bab402e8af8d607cd9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 29 Dec 2018 19:30:47 +0100 Subject: [PATCH 225/358] Adjust tests to 3tuple return value from async method --- freqtrade/exchange/__init__.py | 1 + freqtrade/tests/exchange/test_exchange.py | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 7ce4a38c1..9a426c805 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -518,6 +518,7 @@ class Exchange(object): input_coroutines = [self._async_get_candle_history( pair, tick_interval, since) for since in range(since_ms, arrow.utcnow().timestamp * 1000, one_call)] + tickers = await asyncio.gather(*input_coroutines, return_exceptions=True) # Combine tickers diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index ee53f5cdd..eaf48fa56 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -765,7 +765,7 @@ def test_get_history(default_conf, mocker, caplog): pair = 'ETH/BTC' async def mock_candle_hist(pair, tick_interval, since_ms): - return pair, tick + return pair, tick_interval, tick exchange._async_get_candle_history = Mock(wraps=mock_candle_hist) # one_call calculation * 1.8 should do 2 calls @@ -850,9 +850,10 @@ async def test__async_get_candle_history(default_conf, mocker, caplog): pair = 'ETH/BTC' res = await exchange._async_get_candle_history(pair, "5m") assert type(res) is tuple - assert len(res) == 2 + assert len(res) == 3 assert res[0] == pair - assert res[1] == tick + assert res[1] == "5m" + assert res[2] == tick assert exchange._api_async.fetch_ohlcv.call_count == 1 assert not log_has(f"Using cached ohlcv data for {pair} ...", caplog.record_tuples) @@ -883,9 +884,10 @@ async def test__async_get_candle_history_empty(default_conf, mocker, caplog): pair = 'ETH/BTC' res = await exchange._async_get_candle_history(pair, "5m") assert type(res) is tuple - assert len(res) == 2 + assert len(res) == 3 assert res[0] == pair - assert res[1] == tick + assert res[1] == "5m" + assert res[2] == tick assert exchange._api_async.fetch_ohlcv.call_count == 1 @@ -1010,7 +1012,7 @@ async def test___async_get_candle_history_sort(default_conf, mocker): # Test the ticker history sort res = await exchange._async_get_candle_history('ETH/BTC', default_conf['ticker_interval']) assert res[0] == 'ETH/BTC' - ticks = res[1] + ticks = res[2] assert sort_mock.call_count == 1 assert ticks[0][0] == 1527830400000 @@ -1047,7 +1049,8 @@ async def test___async_get_candle_history_sort(default_conf, mocker): # Test the ticker history sort res = await exchange._async_get_candle_history('ETH/BTC', default_conf['ticker_interval']) assert res[0] == 'ETH/BTC' - ticks = res[1] + assert res[1] == default_conf['ticker_interval'] + ticks = res[2] # Sorted not called again - data is already in order assert sort_mock.call_count == 0 assert ticks[0][0] == 1527827700000 From 0aa0b1d4feeb7ad3abe366d760852db879bbcd3c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 30 Dec 2018 07:15:21 +0100 Subject: [PATCH 226/358] Store tickers by pair / ticker_interval --- freqtrade/data/dataprovider.py | 18 ++++++++++++----- freqtrade/exchange/__init__.py | 32 ++++++++++++++++--------------- freqtrade/freqtradebot.py | 14 ++++++++++---- freqtrade/optimize/backtesting.py | 5 +++-- 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 628ca1dd4..9b9c73e55 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -6,7 +6,7 @@ Common Interface for bot and strategy to access data. """ import logging from pathlib import Path -from typing import List +from typing import List, Tuple from pandas import DataFrame @@ -23,11 +23,11 @@ class DataProvider(object): self._config = config self._exchange = exchange - def refresh(self, pairlist: List[str]) -> None: + def refresh(self, pairlist: List[Tuple[str, str]]) -> None: """ Refresh data, called with each cycle """ - self._exchange.refresh_latest_ohlcv(pairlist, self._config['ticker_interval']) + self._exchange.refresh_latest_ohlcv(pairlist) @property def available_pairs(self) -> List[str]: @@ -37,23 +37,31 @@ class DataProvider(object): """ return list(self._exchange._klines.keys()) - def ohlcv(self, pair: str, copy: bool = True) -> DataFrame: + def ohlcv(self, pair: str, tick_interval: str = None, copy: bool = True) -> DataFrame: """ get ohlcv data for the given pair as DataFrame Please check `available_pairs` to verify which pairs are currently cached. :param pair: pair to get the data for + :param tick_interval: ticker_interval to get pair for :param copy: copy dataframe before returning. Use false only for RO operations (where the dataframe is not modified) """ # TODO: Should not be stored in exchange but in this class if self.runmode in (RunMode.DRY_RUN, RunMode.LIVE): - return self._exchange.klines(pair, copy) + if tick_interval: + pairtick = (pair, tick_interval) + else: + pairtick = (pair, self._config['ticker_interval']) + + return self._exchange.klines(pairtick, copy=copy) else: return DataFrame() def historic_ohlcv(self, pair: str, ticker_interval: str) -> DataFrame: """ get historic ohlcv data stored for backtesting + :param pair: pair to get the data for + :param tick_interval: ticker_interval to get pair for """ return load_pair_history(pair=pair, ticker_interval=ticker_interval, diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 9a426c805..21eab39d4 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -83,7 +83,7 @@ class Exchange(object): self._pairs_last_refresh_time: Dict[str, int] = {} # Holds candles - self._klines: Dict[str, DataFrame] = {} + self._klines: Dict[Tuple[str, str], DataFrame] = {} # Holds all open sell orders for dry_run self._dry_run_open_orders: Dict[str, Any] = {} @@ -158,9 +158,10 @@ class Exchange(object): """exchange ccxt id""" return self._api.id - def klines(self, pair: str, copy=True) -> DataFrame: - if pair in self._klines: - return self._klines[pair].copy() if copy else self._klines[pair] + def klines(self, pair_interval: Tuple[str, str], copy=True) -> DataFrame: + # create key tuple + if pair_interval in self._klines: + return self._klines[pair_interval].copy() if copy else self._klines[pair_interval] else: return DataFrame() @@ -531,24 +532,24 @@ class Exchange(object): logger.info("downloaded %s with length %s.", pair, len(data)) return data - def refresh_latest_ohlcv(self, pair_list: List[str], - ticker_interval: str) -> List[Tuple[str, List]]: + def refresh_latest_ohlcv(self, pair_list: List[Tuple[str, str]]) -> List[Tuple[str, List]]: """ - Refresh in-memory ohlcv asyncronously and set `_klines` with the result + Refresh in-memory ohlcv asyncronously and set `_klines` with the result """ logger.debug("Refreshing ohlcv data for %d pairs", len(pair_list)) - # Calculating ticker interval in second - interval_in_sec = constants.TICKER_INTERVAL_MINUTES[ticker_interval] * 60 input_coroutines = [] # Gather corotines to run - for pair in pair_list: + for pair, ticker_interval in pair_list: + # Calculating ticker interval in second + interval_in_sec = constants.TICKER_INTERVAL_MINUTES[ticker_interval] * 60 + if not (self._pairs_last_refresh_time.get(pair, 0) + interval_in_sec >= - arrow.utcnow().timestamp and pair in self._klines): + arrow.utcnow().timestamp and (pair, ticker_interval) in self._klines): input_coroutines.append(self._async_get_candle_history(pair, ticker_interval)) else: - logger.debug("Using cached ohlcv data for %s ...", pair) + logger.debug("Using cached ohlcv data for %s, %s ...", pair, ticker_interval) tickers = asyncio.get_event_loop().run_until_complete( asyncio.gather(*input_coroutines, return_exceptions=True)) @@ -559,13 +560,14 @@ class Exchange(object): logger.warning("Async code raised an exception: %s", res.__class__.__name__) continue pair = res[0] - tick_interval[1] + tick_interval = res[1] ticks = res[2] # keeping last candle time as last refreshed time of the pair if ticks: self._pairs_last_refresh_time[pair] = ticks[-1][0] // 1000 - # keeping parsed dataframe in cache - self._klines[pair] = parse_ticker_dataframe(ticks, tick_interval, fill_missing=True) + # keeping parsed dataframe in cache + self._klines[(pair, tick_interval)] = parse_ticker_dataframe( + ticks, tick_interval, fill_missing=True) return tickers @retrier_async diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 6b27130bc..cc75fd89b 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -170,8 +170,11 @@ class FreqtradeBot(object): self.active_pair_whitelist.extend([trade.pair for trade in trades if trade.pair not in self.active_pair_whitelist]) + # Create pair-whitelist tuple with (pair, ticker_interval) + pair_whitelist = [(pair, self.config['ticker_interval']) + for pair in self.active_pair_whitelist] # Refreshing candles - self.dataprovider.refresh(self.active_pair_whitelist) + self.dataprovider.refresh(pair_whitelist) # First process current opened trades for trade in trades: @@ -321,7 +324,9 @@ class FreqtradeBot(object): # running get_signal on historical data fetched for _pair in whitelist: - (buy, sell) = self.strategy.get_signal(_pair, interval, self.dataprovider.ohlcv(_pair)) + (buy, sell) = self.strategy.get_signal( + _pair, interval, self.dataprovider.ohlcv(_pair, self.strategy.ticker_interval)) + if buy and not sell: stake_amount = self._get_trade_stake_amount(_pair) if not stake_amount: @@ -582,8 +587,9 @@ class FreqtradeBot(object): (buy, sell) = (False, False) experimental = self.config.get('experimental', {}) if experimental.get('use_sell_signal') or experimental.get('ignore_roi_if_buy_signal'): - (buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.ticker_interval, - self.dataprovider.ohlcv(trade.pair)) + (buy, sell) = self.strategy.get_signal( + trade.pair, self.strategy.ticker_interval, + self.dataprovider.ohlcv(trade.pair, self.strategy.ticker_interval)) config_ask_strategy = self.config.get('ask_strategy', {}) if config_ask_strategy.get('use_order_book', False): diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 192f8cff0..a3e74704b 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -368,8 +368,9 @@ class Backtesting(object): if self.config.get('live'): logger.info('Downloading data for all pairs in whitelist ...') - self.exchange.refresh_latest_ohlcv(pairs, self.ticker_interval) - data = self.exchange._klines + self.exchange.refresh_latest_ohlcv([(pair, self.ticker_interval) for pair in pairs]) + data = {key[0]: value for key, value in self.exchange._klines.items()} + else: logger.info('Using local backtesting data (using whitelist in given config) ...') From a9abc25785fe865c6e9f6a7febabcdc1b49f25fb Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 30 Dec 2018 07:15:49 +0100 Subject: [PATCH 227/358] Improve data-provider tests --- docs/bot-optimization.md | 4 +-- freqtrade/tests/data/test_dataprovider.py | 30 ++++++++++++++--------- freqtrade/tests/exchange/test_exchange.py | 9 ++++--- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/docs/bot-optimization.md b/docs/bot-optimization.md index 7fee559b8..979adcd08 100644 --- a/docs/bot-optimization.md +++ b/docs/bot-optimization.md @@ -235,11 +235,11 @@ if self.dp: if dp.runmode == 'live': if 'ETH/BTC' in self.dp.available_pairs: data_eth = self.dp.ohlcv(pair='ETH/BTC', - ticker_interval=ticker_interval) + ticker_interval=ticker_interval) else: # Get historic ohlcv data (cached on disk). history_eth = self.dp.historic_ohlcv(pair='ETH/BTC', - ticker_interval='1h') + ticker_interval='1h') ``` All methods return `None` in case of failure (do not raise an exception). diff --git a/freqtrade/tests/data/test_dataprovider.py b/freqtrade/tests/data/test_dataprovider.py index 9f17da391..89b709572 100644 --- a/freqtrade/tests/data/test_dataprovider.py +++ b/freqtrade/tests/data/test_dataprovider.py @@ -9,27 +9,31 @@ from freqtrade.tests.conftest import get_patched_exchange def test_ohlcv(mocker, default_conf, ticker_history): default_conf['runmode'] = RunMode.DRY_RUN + tick_interval = default_conf['ticker_interval'] exchange = get_patched_exchange(mocker, default_conf) - exchange._klines['XRP/BTC'] = ticker_history - exchange._klines['UNITTEST/BTC'] = ticker_history + exchange._klines[('XRP/BTC', tick_interval)] = ticker_history + exchange._klines[('UNITTEST/BTC', tick_interval)] = ticker_history dp = DataProvider(default_conf, exchange) assert dp.runmode == RunMode.DRY_RUN - assert ticker_history.equals(dp.ohlcv('UNITTEST/BTC')) - assert isinstance(dp.ohlcv('UNITTEST/BTC'), DataFrame) - assert dp.ohlcv('UNITTEST/BTC') is not ticker_history - assert dp.ohlcv('UNITTEST/BTC', copy=False) is ticker_history - assert not dp.ohlcv('UNITTEST/BTC').empty - assert dp.ohlcv('NONESENSE/AAA').empty + assert ticker_history.equals(dp.ohlcv('UNITTEST/BTC', tick_interval)) + assert isinstance(dp.ohlcv('UNITTEST/BTC', tick_interval), DataFrame) + assert dp.ohlcv('UNITTEST/BTC', tick_interval) is not ticker_history + assert dp.ohlcv('UNITTEST/BTC', tick_interval, copy=False) is ticker_history + assert not dp.ohlcv('UNITTEST/BTC', tick_interval).empty + assert dp.ohlcv('NONESENSE/AAA', tick_interval).empty + + # Test with and without parameter + assert dp.ohlcv('UNITTEST/BTC', tick_interval).equals(dp.ohlcv('UNITTEST/BTC')) default_conf['runmode'] = RunMode.LIVE dp = DataProvider(default_conf, exchange) assert dp.runmode == RunMode.LIVE - assert isinstance(dp.ohlcv('UNITTEST/BTC'), DataFrame) + assert isinstance(dp.ohlcv('UNITTEST/BTC', tick_interval), DataFrame) default_conf['runmode'] = RunMode.BACKTEST dp = DataProvider(default_conf, exchange) assert dp.runmode == RunMode.BACKTEST - assert dp.ohlcv('UNITTEST/BTC').empty + assert dp.ohlcv('UNITTEST/BTC', tick_interval).empty def test_historic_ohlcv(mocker, default_conf, ticker_history): @@ -49,8 +53,10 @@ def test_historic_ohlcv(mocker, default_conf, ticker_history): def test_available_pairs(mocker, default_conf, ticker_history): exchange = get_patched_exchange(mocker, default_conf) - exchange._klines['XRP/BTC'] = ticker_history - exchange._klines['UNITTEST/BTC'] = ticker_history + + tick_interval = default_conf['ticker_interval'] + exchange._klines[('XRP/BTC', tick_interval)] = ticker_history + exchange._klines[('UNITTEST/BTC', tick_interval)] = ticker_history dp = DataProvider(default_conf, exchange) assert len(dp.available_pairs) == 2 diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index eaf48fa56..8fc3f1a32 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -802,10 +802,10 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None: exchange = get_patched_exchange(mocker, default_conf) exchange._api_async.fetch_ohlcv = get_mock_coro(tick) - pairs = ['IOTA/ETH', 'XRP/ETH'] + pairs = [('IOTA/ETH', '5m'), ('XRP/ETH', '5m')] # empty dicts assert not exchange._klines - exchange.refresh_latest_ohlcv(['IOTA/ETH', 'XRP/ETH'], '5m') + exchange.refresh_latest_ohlcv([('IOTA/ETH', '5m'), ('XRP/ETH', '5m')]) assert log_has(f'Refreshing ohlcv data for {len(pairs)} pairs', caplog.record_tuples) assert exchange._klines @@ -822,10 +822,11 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None: assert exchange.klines(pair, copy=False) is exchange.klines(pair, copy=False) # test caching - exchange.refresh_latest_ohlcv(['IOTA/ETH', 'XRP/ETH'], '5m') + exchange.refresh_latest_ohlcv([('IOTA/ETH', '5m'), ('XRP/ETH', '5m')]) assert exchange._api_async.fetch_ohlcv.call_count == 2 - assert log_has(f"Using cached ohlcv data for {pairs[0]} ...", caplog.record_tuples) + assert log_has(f"Using cached ohlcv data for {pairs[0][0]}, {pairs[0][1]} ...", + caplog.record_tuples) @pytest.mark.asyncio From f0af4601f9820f29b8230101d7a6fb456c7017e4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 30 Dec 2018 07:20:03 +0100 Subject: [PATCH 228/358] Adopt plot_dataframe to work with --live --- freqtrade/data/dataprovider.py | 2 +- freqtrade/exchange/__init__.py | 2 +- freqtrade/tests/test_freqtradebot.py | 2 +- scripts/plot_dataframe.py | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 9b9c73e55..c6f9c9c88 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -32,7 +32,7 @@ class DataProvider(object): @property def available_pairs(self) -> List[str]: """ - Return a list of pairs for which data is currently cached. + Return a list of tuples containing pair, tick_interval for which data is currently cached. Should be whitelist + open trades. """ return list(self._exchange._klines.keys()) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 21eab39d4..1fff1c1b5 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -579,7 +579,7 @@ class Exchange(object): """ try: # fetch ohlcv asynchronously - logger.debug("fetching %s since %s ...", pair, since_ms) + logger.debug("fetching %s, %s since %s ...", pair, tick_interval, since_ms) data = await self._api_async.fetch_ohlcv(pair, timeframe=tick_interval, since=since_ms) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index ca6190ae4..ef2815bed 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -43,7 +43,7 @@ def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False)) -> None: :return: None """ freqtrade.strategy.get_signal = lambda e, s, t: value - freqtrade.exchange.refresh_latest_ohlcv = lambda p, i: None + freqtrade.exchange.refresh_latest_ohlcv = lambda p: None def patch_RPCManager(mocker) -> MagicMock: diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index 8f3e8327a..8c0793174 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -138,8 +138,8 @@ def plot_analyzed_dataframe(args: Namespace) -> None: tickers = {} if args.live: logger.info('Downloading pair.') - exchange.refresh_latest_ohlcv([pair], tick_interval) - tickers[pair] = exchange.klines(pair) + exchange.refresh_latest_ohlcv([(pair, tick_interval)]) + tickers[pair] = exchange.klines((pair, tick_interval)) else: tickers = history.load_data( datadir=Path(_CONF.get("datadir")), From 6525a838d1dea85530d03d89e09d4451ead0aed7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 30 Dec 2018 10:25:47 +0100 Subject: [PATCH 229/358] Adjust documentation to tuple use --- docs/bot-optimization.md | 26 ++++++++++++++++++-------- freqtrade/data/dataprovider.py | 1 - 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/bot-optimization.md b/docs/bot-optimization.md index 979adcd08..51e2f0f63 100644 --- a/docs/bot-optimization.md +++ b/docs/bot-optimization.md @@ -228,12 +228,23 @@ The strategy provides access to the `DataProvider`. This allows you to get addit **NOTE**: The DataProvier is currently not available during backtesting / hyperopt. +All methods return `None` in case of failure (do not raise an exception). + Please always check if the `DataProvider` is available to avoid failures during backtesting. +#### Possible options for DataProvider + +- `available_pairs` - Property with tuples listing cached pairs with their intervals. (pair, interval) +- `ohlcv(pair, ticker_interval)` - Currently cached ticker data for all pairs in the whitelist, returns DataFrame or empty DataFrame +- `historic_ohlcv(pair, ticker_interval)` - Data stored on disk +- `runmode` - Property containing the current runmode. + +#### ohlcv / historic_ohlcv + ``` python if self.dp: if dp.runmode == 'live': - if 'ETH/BTC' in self.dp.available_pairs: + if ('ETH/BTC', ticker_interval) in self.dp.available_pairs: data_eth = self.dp.ohlcv(pair='ETH/BTC', ticker_interval=ticker_interval) else: @@ -242,14 +253,13 @@ if self.dp: ticker_interval='1h') ``` -All methods return `None` in case of failure (do not raise an exception). +#### Available Pairs -#### Possible options for DataProvider - -- `available_pairs` - Property containing cached pairs -- `ohlcv(pair, ticker_interval)` - Currently cached ticker data for all pairs in the whitelist, returns DataFrame or empty DataFrame -- `historic_ohlcv(pair, ticker_interval)` - Data stored on disk -- `runmode` - Property containing the current runmode. +``` python +if self.dp: + for pair, ticker in self.dp.available_pairs: + print(f"available {pair}, {ticker}") +``` ### Additional data - Wallets diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index c6f9c9c88..fdb3714fc 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -46,7 +46,6 @@ class DataProvider(object): :param copy: copy dataframe before returning. Use false only for RO operations (where the dataframe is not modified) """ - # TODO: Should not be stored in exchange but in this class if self.runmode in (RunMode.DRY_RUN, RunMode.LIVE): if tick_interval: pairtick = (pair, tick_interval) From d7df5d57153c41d5c3a292b36bb312da6242afcb Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jan 2019 06:44:08 +0100 Subject: [PATCH 230/358] Keep last_pair_refresh as tuple asw ell --- freqtrade/exchange/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 1fff1c1b5..44642476c 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -80,7 +80,7 @@ class Exchange(object): self._cached_ticker: Dict[str, Any] = {} # Holds last candle refreshed time of each pair - self._pairs_last_refresh_time: Dict[str, int] = {} + self._pairs_last_refresh_time: Dict[Tuple[str, str], int] = {} # Holds candles self._klines: Dict[Tuple[str, str], DataFrame] = {} @@ -545,7 +545,7 @@ class Exchange(object): # Calculating ticker interval in second interval_in_sec = constants.TICKER_INTERVAL_MINUTES[ticker_interval] * 60 - if not (self._pairs_last_refresh_time.get(pair, 0) + interval_in_sec >= + if not (self._pairs_last_refresh_time.get((pair, ticker_interval), 0) + interval_in_sec >= arrow.utcnow().timestamp and (pair, ticker_interval) in self._klines): input_coroutines.append(self._async_get_candle_history(pair, ticker_interval)) else: @@ -564,7 +564,7 @@ class Exchange(object): ticks = res[2] # keeping last candle time as last refreshed time of the pair if ticks: - self._pairs_last_refresh_time[pair] = ticks[-1][0] // 1000 + self._pairs_last_refresh_time[(pair, tick_interval)] = ticks[-1][0] // 1000 # keeping parsed dataframe in cache self._klines[(pair, tick_interval)] = parse_ticker_dataframe( ticks, tick_interval, fill_missing=True) From 1e749a0f9b9b28f0862d57db0b825905e9c1a713 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 21 Jan 2019 19:25:39 +0100 Subject: [PATCH 231/358] Rename variable to be clearer --- freqtrade/freqtradebot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index cc75fd89b..74d2cc43d 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -171,10 +171,10 @@ class FreqtradeBot(object): if trade.pair not in self.active_pair_whitelist]) # Create pair-whitelist tuple with (pair, ticker_interval) - pair_whitelist = [(pair, self.config['ticker_interval']) - for pair in self.active_pair_whitelist] + pair_whitelist_tuple = [(pair, self.config['ticker_interval']) + for pair in self.active_pair_whitelist] # Refreshing candles - self.dataprovider.refresh(pair_whitelist) + self.dataprovider.refresh(pair_whitelist_tuple) # First process current opened trades for trade in trades: From a2bc1da669a3f7fbfb0d401ecd5b7a2739cc710a Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 21 Jan 2019 19:28:53 +0100 Subject: [PATCH 232/358] Remove private var from class instance it's overwritten in __init__ anyway --- freqtrade/strategy/interface.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 7210f5c78..ecb32b2b5 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -95,9 +95,6 @@ class IStrategy(ABC): # run "populate_indicators" only for new candle process_only_new_candles: bool = False - # Dict to determine if analysis is necessary - _last_candle_seen_per_pair: Dict[str, datetime] = {} - # Class level variables (intentional) containing # the dataprovider (dp) (access to other candles, historic data, ...) # and wallets - access to the current balance. @@ -106,7 +103,8 @@ class IStrategy(ABC): def __init__(self, config: dict) -> None: self.config = config - self._last_candle_seen_per_pair = {} + # Dict to determine if analysis is necessary + self._last_candle_seen_per_pair: Dict[str, datetime] = {} @abstractmethod def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: From e7800aa88a1a559f0973531628fd898b5b882f43 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 21 Jan 2019 19:30:59 +0100 Subject: [PATCH 233/358] Import only what's necessary --- freqtrade/resolvers/strategy_resolver.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 0d1c9f9a0..0f71501ae 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -3,11 +3,11 @@ """ This module load custom strategies """ -import inspect import logging import tempfile from base64 import urlsafe_b64decode from collections import OrderedDict +from inspect import getfullargspec from pathlib import Path from typing import Dict, Optional @@ -126,11 +126,9 @@ class StrategyResolver(IResolver): if strategy: logger.info('Using resolved strategy %s from \'%s\'', strategy_name, _path) strategy._populate_fun_len = len( - inspect.getfullargspec(strategy.populate_indicators).args) - strategy._buy_fun_len = len( - inspect.getfullargspec(strategy.populate_buy_trend).args) - strategy._sell_fun_len = len( - inspect.getfullargspec(strategy.populate_sell_trend).args) + getfullargspec(strategy.populate_indicators).args) + strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args) + strategy._sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args) return import_strategy(strategy, config=config) except FileNotFoundError: From 27b2021726cedb53f24b7beeee9d599fa630f9f1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 21 Jan 2019 19:56:15 +0100 Subject: [PATCH 234/358] Only run once per pair --- freqtrade/exchange/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 44642476c..62730e034 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -541,7 +541,7 @@ class Exchange(object): input_coroutines = [] # Gather corotines to run - for pair, ticker_interval in pair_list: + for pair, ticker_interval in set(pair_list): # Calculating ticker interval in second interval_in_sec = constants.TICKER_INTERVAL_MINUTES[ticker_interval] * 60 From 7b138ef3b496b33e4c27790eec42b394f589172e Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 21 Jan 2019 20:03:19 +0100 Subject: [PATCH 235/358] Add warning about strategy/backtesting --- docs/bot-optimization.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/bot-optimization.md b/docs/bot-optimization.md index 51e2f0f63..cefcacee6 100644 --- a/docs/bot-optimization.md +++ b/docs/bot-optimization.md @@ -47,6 +47,12 @@ python3 ./freqtrade/main.py --strategy AwesomeStrategy **For the following section we will use the [user_data/strategies/test_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/test_strategy.py) file as reference.** +!!! Note: Strategies and Backtesting + To avoid problems and unexpected differences between Backtesting and dry/live modes, please be aware + that during backtesting the full time-interval is passed to the `populate_*()` methods at once. + It is therefore best to use vectorized operations (across the whole dataframe, not loops) and + avoid index referencing (`df.iloc[-1]`), but instead use `df.shift()` to get to the previous candle. + ### Customize Indicators Buy and sell strategies need indicators. You can add more indicators by extending the list contained in the method `populate_indicators()` from your strategy file. From d6cdfc58afcd5345ebdef573e1683ea29e3146f4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 21 Jan 2019 20:19:34 +0100 Subject: [PATCH 236/358] Fix mypy hickup after changing list to tuples --- freqtrade/data/dataprovider.py | 2 +- freqtrade/tests/data/test_dataprovider.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index fdb3714fc..ce1b480f9 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -30,7 +30,7 @@ class DataProvider(object): self._exchange.refresh_latest_ohlcv(pairlist) @property - def available_pairs(self) -> List[str]: + def available_pairs(self) -> List[Tuple[str, str]]: """ Return a list of tuples containing pair, tick_interval for which data is currently cached. Should be whitelist + open trades. diff --git a/freqtrade/tests/data/test_dataprovider.py b/freqtrade/tests/data/test_dataprovider.py index 89b709572..834ff94b5 100644 --- a/freqtrade/tests/data/test_dataprovider.py +++ b/freqtrade/tests/data/test_dataprovider.py @@ -60,4 +60,4 @@ def test_available_pairs(mocker, default_conf, ticker_history): dp = DataProvider(default_conf, exchange) assert len(dp.available_pairs) == 2 - assert dp.available_pairs == ['XRP/BTC', 'UNITTEST/BTC'] + assert dp.available_pairs == [('XRP/BTC', tick_interval), ('UNITTEST/BTC', tick_interval)] From 6e2de75bcbfb755097477821818db2c49f0e91b7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 21 Jan 2019 20:22:27 +0100 Subject: [PATCH 237/358] Add additional_pairs to strategy --- freqtrade/strategy/interface.py | 13 +++++++++++++ user_data/strategies/test_strategy.py | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index ecb32b2b5..c7ec8dda6 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -133,6 +133,19 @@ class IStrategy(ABC): :return: DataFrame with sell column """ + def additional_pairs(self) -> List[Tuple[str, str]]: + """ + Define additional pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + def get_strategy_name(self) -> str: """ Returns strategy class name diff --git a/user_data/strategies/test_strategy.py b/user_data/strategies/test_strategy.py index 048b398c7..314787f7a 100644 --- a/user_data/strategies/test_strategy.py +++ b/user_data/strategies/test_strategy.py @@ -67,6 +67,19 @@ class TestStrategy(IStrategy): 'sell': 'gtc' } + def additional_pairs(self): + """ + Define additional pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Adds several different TA indicators to the given DataFrame From fc92491a4780873dded3ee3a5ae36a540d95afd2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 21 Jan 2019 20:22:49 +0100 Subject: [PATCH 238/358] Add documentation for additional_pairs --- docs/bot-optimization.md | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/docs/bot-optimization.md b/docs/bot-optimization.md index cefcacee6..9200d6b1b 100644 --- a/docs/bot-optimization.md +++ b/docs/bot-optimization.md @@ -232,7 +232,8 @@ Currently this is `pair`, which can be accessed using `metadata['pair']` - and w The strategy provides access to the `DataProvider`. This allows you to get additional data to use in your strategy. -**NOTE**: The DataProvier is currently not available during backtesting / hyperopt. +!!!Note: + The DataProvier is currently not available during backtesting / hyperopt, but this is planned for the future. All methods return `None` in case of failure (do not raise an exception). @@ -259,6 +260,10 @@ if self.dp: ticker_interval='1h') ``` +!!! Warning: Warning about backtesting + Be carefull when using dataprovider in backtesting. `historic_ohlcv()` provides the full time-range in one go, + so please be aware of it and make sure to not "look into the future" to avoid surprises when running in dry/live mode). + #### Available Pairs ``` python @@ -267,11 +272,35 @@ if self.dp: print(f"available {pair}, {ticker}") ``` +#### Get data for non-tradeable pairs + +Data for additional pairs (reference pairs) can be beneficial for some strategies. +Ohlcv data for these pairs will be downloaded as part of the regular whitelist refresh process and is available via `DataProvider` just as other pairs (see above). +These parts will **not** be traded unless they are also specified in the pair whitelist, or have been selected by Dynamic Whitelisting. + +The pairs need to be specified as tuples in the format `("pair", "interval")`, with pair as the first and time interval as the second argument. + +Sample: + +``` python +def additional_pairs(self): + return [("ETH/USDT", "5m"), + ("BTC/TUSD", "15m"), + ] +``` + +!!! Warning: + As these pairs will be refreshed as part of the regular whitelist refresh, it's best to keep this list short. + All intervals and all pairs can be specified as long as they are available (and active) on the used exchange. + It is however better to use resampling to longer time-intervals when possible + to avoid hammering the exchange with too many requests and risk beeing blocked. + ### Additional data - Wallets The strategy provides access to the `Wallets` object. This contains the current balances on the exchange. -**NOTE**: Wallets is not available during backtesting / hyperopt. +!!!NOTE: + Wallets is not available during backtesting / hyperopt. Please always check if `Wallets` is available to avoid failures during backtesting. From e66808bb02cb3e697033252c8261ebb168d99ab0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 22 Jan 2019 06:55:40 +0100 Subject: [PATCH 239/358] Add additional pairs to refresh call --- freqtrade/data/dataprovider.py | 9 +++++-- freqtrade/freqtradebot.py | 3 ++- freqtrade/tests/data/test_dataprovider.py | 30 +++++++++++++++++++++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index ce1b480f9..f69b1287d 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -23,11 +23,16 @@ class DataProvider(object): self._config = config self._exchange = exchange - def refresh(self, pairlist: List[Tuple[str, str]]) -> None: + def refresh(self, + pairlist: List[Tuple[str, str]], + helping_pairs: List[Tuple[str, str]] = None) -> None: """ Refresh data, called with each cycle """ - self._exchange.refresh_latest_ohlcv(pairlist) + if helping_pairs: + self._exchange.refresh_latest_ohlcv(pairlist + helping_pairs) + else: + self._exchange.refresh_latest_ohlcv(pairlist) @property def available_pairs(self) -> List[Tuple[str, str]]: diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 74d2cc43d..22ccab8c0 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -174,7 +174,8 @@ class FreqtradeBot(object): pair_whitelist_tuple = [(pair, self.config['ticker_interval']) for pair in self.active_pair_whitelist] # Refreshing candles - self.dataprovider.refresh(pair_whitelist_tuple) + self.dataprovider.refresh(pair_whitelist_tuple, + self.strategy.additional_pairs()) # First process current opened trades for trade in trades: diff --git a/freqtrade/tests/data/test_dataprovider.py b/freqtrade/tests/data/test_dataprovider.py index 834ff94b5..0fed86a4e 100644 --- a/freqtrade/tests/data/test_dataprovider.py +++ b/freqtrade/tests/data/test_dataprovider.py @@ -61,3 +61,33 @@ def test_available_pairs(mocker, default_conf, ticker_history): assert len(dp.available_pairs) == 2 assert dp.available_pairs == [('XRP/BTC', tick_interval), ('UNITTEST/BTC', tick_interval)] + + +def test_refresh(mocker, default_conf, ticker_history): + refresh_mock = MagicMock() + mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', refresh_mock) + + exchange = get_patched_exchange(mocker, default_conf, id='binance') + tick_interval = default_conf['ticker_interval'] + pairs = [('XRP/BTC', tick_interval), + ('UNITTEST/BTC', tick_interval), + ] + + pairs_non_trad = [('ETH/USDT', tick_interval), + ('BTC/TUSD', "1h"), + ] + + dp = DataProvider(default_conf, exchange) + dp.refresh(pairs) + + assert refresh_mock.call_count == 1 + assert len(refresh_mock.call_args[0]) == 1 + assert len(refresh_mock.call_args[0][0]) == len(pairs) + assert refresh_mock.call_args[0][0] == pairs + + refresh_mock.reset_mock() + dp.refresh(pairs, pairs_non_trad) + assert refresh_mock.call_count == 1 + assert len(refresh_mock.call_args[0]) == 1 + assert len(refresh_mock.call_args[0][0]) == len(pairs) + len(pairs_non_trad) + assert refresh_mock.call_args[0][0] == pairs + pairs_non_trad From 1e7431a7b81df9087e0fbbfd2b6f2a0cf1646105 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 22 Jan 2019 06:56:54 +0100 Subject: [PATCH 240/358] Blackify --- freqtrade/tests/data/test_dataprovider.py | 65 +++++++++++------------ 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/freqtrade/tests/data/test_dataprovider.py b/freqtrade/tests/data/test_dataprovider.py index 0fed86a4e..b17bba273 100644 --- a/freqtrade/tests/data/test_dataprovider.py +++ b/freqtrade/tests/data/test_dataprovider.py @@ -8,74 +8,73 @@ from freqtrade.tests.conftest import get_patched_exchange def test_ohlcv(mocker, default_conf, ticker_history): - default_conf['runmode'] = RunMode.DRY_RUN - tick_interval = default_conf['ticker_interval'] + default_conf["runmode"] = RunMode.DRY_RUN + tick_interval = default_conf["ticker_interval"] exchange = get_patched_exchange(mocker, default_conf) - exchange._klines[('XRP/BTC', tick_interval)] = ticker_history - exchange._klines[('UNITTEST/BTC', tick_interval)] = ticker_history + exchange._klines[("XRP/BTC", tick_interval)] = ticker_history + exchange._klines[("UNITTEST/BTC", tick_interval)] = ticker_history dp = DataProvider(default_conf, exchange) assert dp.runmode == RunMode.DRY_RUN - assert ticker_history.equals(dp.ohlcv('UNITTEST/BTC', tick_interval)) - assert isinstance(dp.ohlcv('UNITTEST/BTC', tick_interval), DataFrame) - assert dp.ohlcv('UNITTEST/BTC', tick_interval) is not ticker_history - assert dp.ohlcv('UNITTEST/BTC', tick_interval, copy=False) is ticker_history - assert not dp.ohlcv('UNITTEST/BTC', tick_interval).empty - assert dp.ohlcv('NONESENSE/AAA', tick_interval).empty + assert ticker_history.equals(dp.ohlcv("UNITTEST/BTC", tick_interval)) + assert isinstance(dp.ohlcv("UNITTEST/BTC", tick_interval), DataFrame) + assert dp.ohlcv("UNITTEST/BTC", tick_interval) is not ticker_history + assert dp.ohlcv("UNITTEST/BTC", tick_interval, copy=False) is ticker_history + assert not dp.ohlcv("UNITTEST/BTC", tick_interval).empty + assert dp.ohlcv("NONESENSE/AAA", tick_interval).empty # Test with and without parameter - assert dp.ohlcv('UNITTEST/BTC', tick_interval).equals(dp.ohlcv('UNITTEST/BTC')) + assert dp.ohlcv("UNITTEST/BTC", tick_interval).equals(dp.ohlcv("UNITTEST/BTC")) - default_conf['runmode'] = RunMode.LIVE + default_conf["runmode"] = RunMode.LIVE dp = DataProvider(default_conf, exchange) assert dp.runmode == RunMode.LIVE - assert isinstance(dp.ohlcv('UNITTEST/BTC', tick_interval), DataFrame) + assert isinstance(dp.ohlcv("UNITTEST/BTC", tick_interval), DataFrame) - default_conf['runmode'] = RunMode.BACKTEST + default_conf["runmode"] = RunMode.BACKTEST dp = DataProvider(default_conf, exchange) assert dp.runmode == RunMode.BACKTEST - assert dp.ohlcv('UNITTEST/BTC', tick_interval).empty + assert dp.ohlcv("UNITTEST/BTC", tick_interval).empty def test_historic_ohlcv(mocker, default_conf, ticker_history): historymock = MagicMock(return_value=ticker_history) - mocker.patch('freqtrade.data.dataprovider.load_pair_history', historymock) + mocker.patch("freqtrade.data.dataprovider.load_pair_history", historymock) # exchange = get_patched_exchange(mocker, default_conf) dp = DataProvider(default_conf, None) - data = dp.historic_ohlcv('UNITTEST/BTC', "5m") + data = dp.historic_ohlcv("UNITTEST/BTC", "5m") assert isinstance(data, DataFrame) assert historymock.call_count == 1 - assert historymock.call_args_list[0][1]['datadir'] is None - assert historymock.call_args_list[0][1]['refresh_pairs'] is False - assert historymock.call_args_list[0][1]['ticker_interval'] == '5m' + assert historymock.call_args_list[0][1]["datadir"] is None + assert historymock.call_args_list[0][1]["refresh_pairs"] is False + assert historymock.call_args_list[0][1]["ticker_interval"] == "5m" def test_available_pairs(mocker, default_conf, ticker_history): exchange = get_patched_exchange(mocker, default_conf) - tick_interval = default_conf['ticker_interval'] - exchange._klines[('XRP/BTC', tick_interval)] = ticker_history - exchange._klines[('UNITTEST/BTC', tick_interval)] = ticker_history + tick_interval = default_conf["ticker_interval"] + exchange._klines[("XRP/BTC", tick_interval)] = ticker_history + exchange._klines[("UNITTEST/BTC", tick_interval)] = ticker_history dp = DataProvider(default_conf, exchange) assert len(dp.available_pairs) == 2 - assert dp.available_pairs == [('XRP/BTC', tick_interval), ('UNITTEST/BTC', tick_interval)] + assert dp.available_pairs == [ + ("XRP/BTC", tick_interval), + ("UNITTEST/BTC", tick_interval), + ] def test_refresh(mocker, default_conf, ticker_history): refresh_mock = MagicMock() - mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv', refresh_mock) + mocker.patch("freqtrade.exchange.Exchange.refresh_latest_ohlcv", refresh_mock) - exchange = get_patched_exchange(mocker, default_conf, id='binance') - tick_interval = default_conf['ticker_interval'] - pairs = [('XRP/BTC', tick_interval), - ('UNITTEST/BTC', tick_interval), - ] + exchange = get_patched_exchange(mocker, default_conf, id="binance") + tick_interval = default_conf["ticker_interval"] + pairs = [("XRP/BTC", tick_interval), ("UNITTEST/BTC", tick_interval)] - pairs_non_trad = [('ETH/USDT', tick_interval), - ('BTC/TUSD', "1h"), - ] + pairs_non_trad = [("ETH/USDT", tick_interval), ("BTC/TUSD", "1h")] dp = DataProvider(default_conf, exchange) dp.refresh(pairs) From 3221f883d34b112acf5991d31d353c1c1fa47e4d Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 22 Jan 2019 06:58:51 +0100 Subject: [PATCH 241/358] Wrap line correctly --- freqtrade/exchange/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 62730e034..abbdae610 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -545,8 +545,9 @@ class Exchange(object): # Calculating ticker interval in second interval_in_sec = constants.TICKER_INTERVAL_MINUTES[ticker_interval] * 60 - if not (self._pairs_last_refresh_time.get((pair, ticker_interval), 0) + interval_in_sec >= - arrow.utcnow().timestamp and (pair, ticker_interval) in self._klines): + if not ((self._pairs_last_refresh_time.get((pair, ticker_interval), 0) + + interval_in_sec) >= arrow.utcnow().timestamp + and (pair, ticker_interval) in self._klines): input_coroutines.append(self._async_get_candle_history(pair, ticker_interval)) else: logger.debug("Using cached ohlcv data for %s, %s ...", pair, ticker_interval) From c77607b9977cdb70014e0aa525a12304aa25b3fe Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 22 Jan 2019 07:38:15 +0100 Subject: [PATCH 242/358] Fix tests after rebase --- freqtrade/exchange/__init__.py | 2 +- freqtrade/tests/exchange/test_exchange.py | 46 ++++------------------- 2 files changed, 8 insertions(+), 40 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index abbdae610..e1aa0984a 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -594,7 +594,7 @@ class Exchange(object): data = sorted(data, key=lambda x: x[0]) except IndexError: logger.exception("Error loading %s. Result was %s.", pair, data) - return pair, [] + return pair, tick_interval, [] logger.debug("done fetching %s ...", pair) return pair, tick_interval, data diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index 8fc3f1a32..a91b0ac56 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -805,7 +805,7 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None: pairs = [('IOTA/ETH', '5m'), ('XRP/ETH', '5m')] # empty dicts assert not exchange._klines - exchange.refresh_latest_ohlcv([('IOTA/ETH', '5m'), ('XRP/ETH', '5m')]) + exchange.refresh_latest_ohlcv(pairs) assert log_has(f'Refreshing ohlcv data for {len(pairs)} pairs', caplog.record_tuples) assert exchange._klines @@ -892,42 +892,7 @@ async def test__async_get_candle_history_empty(default_conf, mocker, caplog): assert exchange._api_async.fetch_ohlcv.call_count == 1 -@pytest.mark.asyncio -async def test_async_get_candles_history(default_conf, mocker): - tick = [ - [ - 1511686200000, # unix timestamp ms - 1, # open - 2, # high - 3, # low - 4, # close - 5, # volume (in quote currency) - ] - ] - - async def mock_get_candle_hist(pair, tick_interval, since_ms=None): - return (pair, tick) - - exchange = get_patched_exchange(mocker, default_conf) - # Monkey-patch async function - exchange._api_async.fetch_ohlcv = get_mock_coro(tick) - - exchange._async_get_candle_history = Mock(wraps=mock_get_candle_hist) - - pairs = ['ETH/BTC', 'XRP/BTC'] - res = await exchange.async_get_candles_history(pairs, "5m") - assert type(res) is list - assert len(res) == 2 - assert type(res[0]) is tuple - assert res[0][0] == pairs[0] - assert res[0][1] == tick - assert res[1][0] == pairs[1] - assert res[1][1] == tick - assert exchange._async_get_candle_history.call_count == 2 - - -@pytest.mark.asyncio -async def test_async_get_candles_history_inv_result(default_conf, mocker, caplog): +def test_refresh_latest_ohlcv_inv_result(default_conf, mocker, caplog): async def mock_get_candle_hist(pair, *args, **kwargs): if pair == 'ETH/BTC': @@ -940,8 +905,11 @@ async def test_async_get_candles_history_inv_result(default_conf, mocker, caplog # Monkey-patch async function with empty result exchange._api_async.fetch_ohlcv = MagicMock(side_effect=mock_get_candle_hist) - pairs = ['ETH/BTC', 'XRP/BTC'] - res = await exchange.async_get_candles_history(pairs, "5m") + pairs = [("ETH/BTC", "5m"), ("XRP/BTC", "5m")] + res = exchange.refresh_latest_ohlcv(pairs) + assert exchange._klines + assert exchange._api_async.fetch_ohlcv.call_count == 2 + assert type(res) is list assert len(res) == 2 assert type(res[0]) is tuple From 7c71b9513cf8cbb9e75e277b01cd5455f3c464ed Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Tue, 22 Jan 2019 13:32:11 +0100 Subject: [PATCH 243/358] Update ccxt from 1.18.141 to 1.18.144 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8e6720195..25e337306 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.141 +ccxt==1.18.144 SQLAlchemy==1.2.16 python-telegram-bot==11.1.0 arrow==0.13.0 From 89ddfe08f4ce241cc1f8a16be4f521dd80d74bd8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 22 Jan 2019 19:17:08 +0100 Subject: [PATCH 244/358] Add additional-pairs (sample) to defaultstrategy --- freqtrade/strategy/default_strategy.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/freqtrade/strategy/default_strategy.py b/freqtrade/strategy/default_strategy.py index 085a383db..36064f640 100644 --- a/freqtrade/strategy/default_strategy.py +++ b/freqtrade/strategy/default_strategy.py @@ -42,6 +42,19 @@ class DefaultStrategy(IStrategy): 'sell': 'gtc', } + def additional_pairs(self): + """ + Define additional pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Adds several different TA indicators to the given DataFrame From a06593e6e9495ca06c9dd19283575278e0b56c41 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 22 Jan 2019 19:17:21 +0100 Subject: [PATCH 245/358] Fix test --- freqtrade/tests/exchange/test_exchange.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index a91b0ac56..b384035b0 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -912,8 +912,9 @@ def test_refresh_latest_ohlcv_inv_result(default_conf, mocker, caplog): assert type(res) is list assert len(res) == 2 - assert type(res[0]) is tuple - assert type(res[1]) is TypeError + # Test that each is in list at least once as order is not guaranteed + assert type(res[0]) is tuple or type(res[1]) is tuple + assert type(res[0]) is TypeError or type(res[1]) is TypeError assert log_has("Error loading ETH/BTC. Result was [[]].", caplog.record_tuples) assert log_has("Async code raised an exception: TypeError", caplog.record_tuples) From 86a0863e30f3ff1e625c031aadf28dd91862f736 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 22 Jan 2019 19:25:58 +0100 Subject: [PATCH 246/358] Clarify logmessage Done fetching --- freqtrade/exchange/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index e1aa0984a..e4d83cf6d 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -595,7 +595,7 @@ class Exchange(object): except IndexError: logger.exception("Error loading %s. Result was %s.", pair, data) return pair, tick_interval, [] - logger.debug("done fetching %s ...", pair) + logger.debug("done fetching %s, %s ...", pair, tick_interval) return pair, tick_interval, data except ccxt.NotSupported as e: From c412cd9e5704d220035826524469db8aaf87e69d Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 22 Jan 2019 19:44:56 +0100 Subject: [PATCH 247/358] Add information about dataframe fix #1192 --- docs/bot-optimization.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/bot-optimization.md b/docs/bot-optimization.md index 1cfae1bc4..1c622737f 100644 --- a/docs/bot-optimization.md +++ b/docs/bot-optimization.md @@ -216,12 +216,40 @@ This is the set of candles the bot should download and use for the analysis. Common values are `"1m"`, `"5m"`, `"15m"`, `"1h"`, however all values supported by your exchange should work. Please note that the same buy/sell signals may work with one interval, but not the other. +This setting is accessible within the strategy by using `self.ticker_interval`. ### Metadata dict The metadata-dict (available for `populate_buy_trend`, `populate_sell_trend`, `populate_indicators`) contains additional information. Currently this is `pair`, which can be accessed using `metadata['pair']` - and will return a pair in the format `XRP/BTC`. +The Metadata-dict should not be modified and does not persist information across multiple calls. +Instead, have a look at the section [Storing information](#Storing-information) + +### Storing information + +Storing information can be accomplished by crating a new dictionary within the strategy class. + +The name of the variable can be choosen at will, but should be prefixed with `cust_` to avoid naming collisions with predefined strategy variables. + +```python +class Awesomestrategy(IStrategy): + # Create custom dictionary + cust_info = {} + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Check if the entry already exists + if "crosstime" in self.cust_info[metadata["pair"]: + self.cust_info[metadata["pair"]["crosstime"] += 1 + else: + self.cust_info[metadata["pair"]["crosstime"] = 1 +``` + +!!! Warning: + The data is not persisted after a bot-restart (or config-reload). Also, the amount of data should be kept smallish (no DataFrames and such), otherwise the bot will start to consume a lot of memory and eventually run out of memory and crash. + +!!! Note: + If the data is pair-specific, make sure to use pair as one of the keys in the dictionary. + ### Where is the default strategy? The default buy strategy is located in the file From 13e2f71d3008d9c6fa66bf9c53ddc53ab6cffd9e Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 22 Jan 2019 20:01:12 +0100 Subject: [PATCH 248/358] Add flake8 plugins and implement small improvements --- freqtrade/freqtradebot.py | 2 +- freqtrade/optimize/backtesting.py | 2 +- requirements-dev.txt | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 2e09cf116..7d14c734c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -34,7 +34,7 @@ class FreqtradeBot(object): This is from here the bot start its logic. """ - def __init__(self, config: Dict[str, Any])-> None: + def __init__(self, config: Dict[str, Any]) -> None: """ Init all variables and object the bot need to work :param config: configuration dict, you can use the Configuration.get_config() diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 38bbe13d4..37ba2ad83 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -13,7 +13,7 @@ from typing import Any, Dict, List, NamedTuple, Optional from pandas import DataFrame from tabulate import tabulate -import freqtrade.optimize as optimize +from freqtrade import optimize from freqtrade import DependencyException, constants from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration diff --git a/requirements-dev.txt b/requirements-dev.txt index f68fe8814..4f8504c8a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,6 +2,8 @@ -r requirements.txt flake8==3.6.0 +flake8-type-annotations==0.1.0 +flake8-tidy-imports==1.1.0 pytest==4.1.1 pytest-mock==1.10.0 pytest-asyncio==0.10.0 From 896c9d34fdc5b4e13f158217ac6432184ace4c76 Mon Sep 17 00:00:00 2001 From: Gianluca Puglia Date: Tue, 22 Jan 2019 22:41:53 +0100 Subject: [PATCH 249/358] 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 5ea332e9be97fb30b2fe898124a1cd9625fc6f26 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Jan 2019 14:13:41 +0100 Subject: [PATCH 250/358] fix bug with trailing_stop_offset if it's disabled --- freqtrade/strategy/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 08a5cf1cd..512736558 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -302,7 +302,7 @@ class IStrategy(ABC): # and if profit is positive stop_loss_value = force_stoploss if force_stoploss else self.stoploss - sl_offset = self.config.get('trailing_stop_positive_offset', 0.0) + sl_offset = self.config.get('trailing_stop_positive_offset') or 0.0 if 'trailing_stop_positive' in self.config and current_profit > sl_offset: From ad8b1bbb79044cc130f34757f84055732b4c58d2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Jan 2019 19:28:05 +0100 Subject: [PATCH 251/358] Change default for positive_offset in sample --- user_data/strategies/test_strategy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_data/strategies/test_strategy.py b/user_data/strategies/test_strategy.py index 048b398c7..6ca45a5dc 100644 --- a/user_data/strategies/test_strategy.py +++ b/user_data/strategies/test_strategy.py @@ -45,7 +45,7 @@ class TestStrategy(IStrategy): # trailing stoploss trailing_stop = False trailing_stop_positive = 0.01 - trailing_stop_positive_offset = None # Disabled / not configured + trailing_stop_positive_offset = 0.0 # Disabled / not configured # Optimal ticker interval for the strategy ticker_interval = '5m' From 97f6a4581965173d206b226c056e1e9c03043625 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Jan 2019 19:28:20 +0100 Subject: [PATCH 252/358] Allow more settings to come from strategy --- freqtrade/resolvers/strategy_resolver.py | 53 +++++++++++++++++------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 0d1c9f9a0..7a6292c8d 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -38,24 +38,38 @@ class StrategyResolver(IResolver): self.strategy: IStrategy = self._load_strategy(strategy_name, config=config, extra_dir=config.get('strategy_path')) + + # make sure experimental dict is available + if 'experimental' not in config: + config['experimental'] = {} + # Set attributes # Check if we need to override configuration - attributes = ["minimal_roi", - "ticker_interval", - "stoploss", - "trailing_stop", - "trailing_stop_positive", - "trailing_stop_positive_offset", - "process_only_new_candles", - "order_types", - "order_time_in_force" + # (Attribute name, default, experimental) + attributes = [("minimal_roi", None, False), + ("ticker_interval", None, False), + ("stoploss", None, False), + ("trailing_stop", None, False), + ("trailing_stop_positive", None, False), + ("trailing_stop_positive_offset", 0.0, False), + ("process_only_new_candles", None, False), + ("order_types", None, False), + ("order_time_in_force", None, False), + ("use_sell_signal", False, True), + ("sell_profit_only", None, True), + ("ignore_roi_if_buy_signal", None, True), ] - for attribute in attributes: - self._override_attribute_helper(config, attribute) + for attribute, default, experimental in attributes: + if experimental: + self._override_attribute_helper(config['experimental'], attribute, default) + else: + self._override_attribute_helper(config, attribute, default) # Loop this list again to have output combined - for attribute in attributes: - if attribute in config: + for attribute, _, exp in attributes: + if exp and attribute in config['experimental']: + logger.info("Strategy using %s: %s", attribute, config['experimental'][attribute]) + elif attribute in config: logger.info("Strategy using %s: %s", attribute, config[attribute]) # Sort and apply type conversions @@ -66,13 +80,24 @@ class StrategyResolver(IResolver): self._strategy_sanity_validations() - def _override_attribute_helper(self, config, attribute: str): + def _override_attribute_helper(self, config, attribute: str, default): + """ + Override attributes in the strategy. + Prevalence: + - Configuration + - Strategy + - default (if not None) + """ if attribute in config: setattr(self.strategy, attribute, config[attribute]) logger.info("Override strategy '%s' with value in config file: %s.", attribute, config[attribute]) elif hasattr(self.strategy, attribute): config[attribute] = getattr(self.strategy, attribute) + # Explicitly check for None here as other "falsy" values are possible + elif default is not None: + setattr(self.strategy, attribute, default) + config[attribute] = default def _strategy_sanity_validations(self): if not all(k in self.strategy.order_types for k in constants.REQUIRED_ORDERTYPES): From 05d65b81da79bf50f4a373fa94926d4f150f0fb1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 23 Jan 2019 21:05:07 +0100 Subject: [PATCH 253/358] Fix typo --- docs/configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 95bf3b1ff..7bdc97860 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -21,8 +21,8 @@ The table below will list all configuration parameters. | `minimal_roi` | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. If set, this parameter will override `minimal_roi` from your strategy file. | `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. More details in the [stoploss documentation](stoploss.md). | `trailing_stop` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file). More details in the [stoploss documentation](stoploss.md). -| `trailing_stop_positve` | 0 | No | Changes stop-loss once profit has been reached. More details in the [stoploss documentation](stoploss.md). -| `trailing_stop_positve_offset` | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. More details in the [stoploss documentation](stoploss.md). +| `trailing_stop_positive` | 0 | No | Changes stop-loss once profit has been reached. More details in the [stoploss documentation](stoploss.md). +| `trailing_stop_positive_offset` | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. More details in the [stoploss documentation](stoploss.md). | `unfilledtimeout.buy` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled. | `unfilledtimeout.sell` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled. | `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. More information below. From 8750f1be3f0adf620a896099f96b4c1af8b8ad32 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 24 Jan 2019 06:43:28 +0100 Subject: [PATCH 254/358] Add strategy-override options --- docs/configuration.md | 47 ++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 7bdc97860..f2595b4b6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -17,12 +17,12 @@ The table below will list all configuration parameters. | `ticker_interval` | [1m, 5m, 30m, 1h, 1d] | No | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes | `fiat_display_currency` | USD | Yes | Fiat currency used to show your profits. More information below. | `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode. -| `process_only_new_candles` | false | No | If set to true indicators are processed only once a new candle arrives. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. Can be set either in Configuration or in the strategy. -| `minimal_roi` | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. If set, this parameter will override `minimal_roi` from your strategy file. -| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. More details in the [stoploss documentation](stoploss.md). -| `trailing_stop` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file). More details in the [stoploss documentation](stoploss.md). -| `trailing_stop_positive` | 0 | No | Changes stop-loss once profit has been reached. More details in the [stoploss documentation](stoploss.md). -| `trailing_stop_positive_offset` | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. More details in the [stoploss documentation](stoploss.md). +| `process_only_new_candles` | false | No | If set to true indicators are processed only once a new candle arrives. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. [Strategy Override](#parameters-in-strategy). +| `minimal_roi` | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. [Strategy Override](#parameters-in-strategy). +| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-strategy). +| `trailing_stop` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file). More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-strategy). +| `trailing_stop_positive` | 0 | No | Changes stop-loss once profit has been reached. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-strategy). +| `trailing_stop_positive_offset` | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-strategy). | `unfilledtimeout.buy` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled. | `unfilledtimeout.sell` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled. | `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. More information below. @@ -33,8 +33,8 @@ The table below will list all configuration parameters. | `ask_strategy.use_order_book` | false | No | Allows selling of open traded pair using the rates in Order Book Asks. | `ask_strategy.order_book_min` | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. | `ask_strategy.order_book_max` | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. -| `order_types` | None | No | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). -| `order_time_in_force` | None | No | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). +| `order_types` | None | No | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). [Strategy Override](#parameters-in-strategy). +| `order_time_in_force` | None | No | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-strategy). | `exchange.name` | bittrex | Yes | Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). | `exchange.key` | key | No | API key to use for the exchange. Only required when you are in production mode. | `exchange.secret` | secret | No | API secret to use for the exchange. Only required when you are in production mode. @@ -44,9 +44,9 @@ The table below will list all configuration parameters. | `exchange.ccxt_config` | None | No | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) | `exchange.ccxt_async_config` | None | No | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) | `edge` | false | No | Please refer to [edge configuration document](edge.md) for detailed explanation. -| `experimental.use_sell_signal` | false | No | Use your sell strategy in addition of the `minimal_roi`. -| `experimental.sell_profit_only` | false | No | waits until you have made a positive profit before taking a sell decision. -| `experimental.ignore_roi_if_buy_signal` | false | No | Does not sell if the buy-signal is still active. Takes preference over `minimal_roi` and `use_sell_signal` +| `experimental.use_sell_signal` | false | No | Use your sell strategy in addition of the `minimal_roi`. [Strategy Override](#parameters-in-strategy). +| `experimental.sell_profit_only` | false | No | waits until you have made a positive profit before taking a sell decision. [Strategy Override](#parameters-in-strategy). +| `experimental.ignore_roi_if_buy_signal` | false | No | Does not sell if the buy-signal is still active. Takes preference over `minimal_roi` and `use_sell_signal`. [Strategy Override](#parameters-in-strategy). | `pairlist.method` | StaticPairList | No | Use Static whitelist. [More information below](#dynamic-pairlists). | `pairlist.config` | None | No | Additional configuration for dynamic pairlists. [More information below](#dynamic-pairlists). | `telegram.enabled` | true | Yes | Enable or not the usage of Telegram. @@ -64,18 +64,37 @@ The table below will list all configuration parameters. | `strategy_path` | null | No | Adds an additional strategy lookup path (must be a folder). | `internals.process_throttle_secs` | 5 | Yes | Set the process throttle. Value in second. -The definition of each config parameters is in [misc.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/misc.py#L205). +### Parameters in strategy + +The following parameters can be set in either configuration or strategy. +Values in the configuration are always overwriting values set in the strategy. + +* `minimal_roi` +* `ticker_interval` +* `stoploss` +* `trailing_stop` +* `trailing_stop_positive` +* `trailing_stop_positive_offset` +* `process_only_new_candles` +* `order_types` +* `order_time_in_force` +* `use_sell_signal` (experimental) +* `sell_profit_only` (experimental) +* `ignore_roi_if_buy_signal` (experimental) ### Understand stake_amount `stake_amount` is an amount of crypto-currency your bot will use for each trade. The minimal value is 0.0005. If there is not enough crypto-currency in the account an exception is generated. -To allow the bot to trade all the avaliable `stake_currency` in your account set
+To allow the bot to trade all the avaliable `stake_currency` in your account set + ```json "stake_amount" : "unlimited", ``` -In this case a trade amount is calclulated as:
+ +In this case a trade amount is calclulated as: + ```python currency_balanse / (max_open_trades - current_open_trades) ``` From ac199b626a0e10accf0bd51a7fbfbbb7129c85a5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 24 Jan 2019 07:03:25 +0100 Subject: [PATCH 255/358] Drop mandatory column --- docs/configuration.md | 110 +++++++++++++++++++++--------------------- 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index f2595b4b6..d8af209d4 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -9,60 +9,62 @@ for your bot configuration. The table below will list all configuration parameters. -| Command | Default | Mandatory | Description | -|----------|---------|-----------|-------------| -| `max_open_trades` | 3 | Yes | Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades) -| `stake_currency` | BTC | Yes | Crypto-currency used for trading. -| `stake_amount` | 0.05 | Yes | Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to `"unlimited"` to allow the bot to use all avaliable balance. -| `ticker_interval` | [1m, 5m, 30m, 1h, 1d] | No | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes -| `fiat_display_currency` | USD | Yes | Fiat currency used to show your profits. More information below. -| `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode. -| `process_only_new_candles` | false | No | If set to true indicators are processed only once a new candle arrives. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. [Strategy Override](#parameters-in-strategy). -| `minimal_roi` | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. [Strategy Override](#parameters-in-strategy). -| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-strategy). -| `trailing_stop` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file). More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-strategy). -| `trailing_stop_positive` | 0 | No | Changes stop-loss once profit has been reached. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-strategy). -| `trailing_stop_positive_offset` | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-strategy). -| `unfilledtimeout.buy` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled. -| `unfilledtimeout.sell` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled. -| `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. More information below. -| `bid_strategy.use_order_book` | false | No | Allows buying of pair using the rates in Order Book Bids. -| `bid_strategy.order_book_top` | 0 | No | Bot will use the top N rate in Order Book Bids. Ie. a value of 2 will allow the bot to pick the 2nd bid rate in Order Book Bids. -| `bid_strategy. check_depth_of_market.enabled` | false | No | Does not buy if the % difference of buy orders and sell orders is met in Order Book. -| `bid_strategy. check_depth_of_market.bids_to_ask_delta` | 0 | No | The % difference of buy orders and sell orders found in Order Book. A value lesser than 1 means sell orders is greater, while value greater than 1 means buy orders is higher. -| `ask_strategy.use_order_book` | false | No | Allows selling of open traded pair using the rates in Order Book Asks. -| `ask_strategy.order_book_min` | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. -| `ask_strategy.order_book_max` | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. -| `order_types` | None | No | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). [Strategy Override](#parameters-in-strategy). -| `order_time_in_force` | None | No | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-strategy). -| `exchange.name` | bittrex | Yes | Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). -| `exchange.key` | key | No | API key to use for the exchange. Only required when you are in production mode. -| `exchange.secret` | secret | No | API secret to use for the exchange. Only required when you are in production mode. -| `exchange.pair_whitelist` | [] | No | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param. -| `exchange.pair_blacklist` | [] | No | List of currency the bot must avoid. Useful when using `--dynamic-whitelist` param. -| `exchange.ccxt_rate_limit` | True | No | DEPRECATED!! Have CCXT handle Exchange rate limits. Depending on the exchange, having this to false can lead to temporary bans from the exchange. -| `exchange.ccxt_config` | None | No | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) -| `exchange.ccxt_async_config` | None | No | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) -| `edge` | false | No | Please refer to [edge configuration document](edge.md) for detailed explanation. -| `experimental.use_sell_signal` | false | No | Use your sell strategy in addition of the `minimal_roi`. [Strategy Override](#parameters-in-strategy). -| `experimental.sell_profit_only` | false | No | waits until you have made a positive profit before taking a sell decision. [Strategy Override](#parameters-in-strategy). -| `experimental.ignore_roi_if_buy_signal` | false | No | Does not sell if the buy-signal is still active. Takes preference over `minimal_roi` and `use_sell_signal`. [Strategy Override](#parameters-in-strategy). -| `pairlist.method` | StaticPairList | No | Use Static whitelist. [More information below](#dynamic-pairlists). -| `pairlist.config` | None | No | Additional configuration for dynamic pairlists. [More information below](#dynamic-pairlists). -| `telegram.enabled` | true | Yes | Enable or not the usage of Telegram. -| `telegram.token` | token | No | Your Telegram bot token. Only required if `telegram.enabled` is `true`. -| `telegram.chat_id` | chat_id | No | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. -| `webhook.enabled` | false | No | Enable usage of Webhook notifications -| `webhook.url` | false | No | URL for the webhook. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details. -| `webhook.webhookbuy` | false | No | Payload to send on buy. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. -| `webhook.webhooksell` | false | No | Payload to send on sell. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. -| `webhook.webhookstatus` | false | No | Payload to send on status calls. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. -| `db_url` | `sqlite:///tradesv3.sqlite`| No | Declares database URL to use. NOTE: This defaults to `sqlite://` if `dry_run` is `True`. -| `initial_state` | running | No | Defines the initial application state. More information below. -| `forcebuy_enable` | false | No | Enables the RPC Commands to force a buy. More information below. -| `strategy` | DefaultStrategy | No | Defines Strategy class to use. -| `strategy_path` | null | No | Adds an additional strategy lookup path (must be a folder). -| `internals.process_throttle_secs` | 5 | Yes | Set the process throttle. Value in second. +Mandatory Parameters are marked as **Required**. + +| Command | Default | Description | +|----------|---------|-------------| +| `max_open_trades` | 3 | **Required.** Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades) +| `stake_currency` | BTC | **Required.** Crypto-currency used for trading. +| `stake_amount` | 0.05 | **Required.** Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to `"unlimited"` to allow the bot to use all avaliable balance. +| `ticker_interval` | [1m, 5m, 30m, 1h, 1d] | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes. [Strategy Override](#parameters-in-strategy). +| `fiat_display_currency` | USD | **Required.** Fiat currency used to show your profits. More information below. +| `dry_run` | true | **Required.** Define if the bot must be in Dry-run or production mode. +| `process_only_new_candles` | false | If set to true indicators are processed only once a new candle arrives. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. [Strategy Override](#parameters-in-strategy). +| `minimal_roi` | See below | Set the threshold in percent the bot will use to sell a trade. More information below. [Strategy Override](#parameters-in-strategy). +| `stoploss` | -0.10 | Value of the stoploss in percent used by the bot. More information below. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-strategy). +| `trailing_stop` | false | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file). More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-strategy). +| `trailing_stop_positive` | 0 | Changes stop-loss once profit has been reached. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-strategy). +| `trailing_stop_positive_offset` | 0 | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-strategy). +| `unfilledtimeout.buy` | 10 | **Required.** How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled. +| `unfilledtimeout.sell` | 10 | **Required.** How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled. +| `bid_strategy.ask_last_balance` | 0.0 | **Required.** Set the bidding price. More information [below](#understand-ask_last_balance). +| `bid_strategy.use_order_book` | false | Allows buying of pair using the rates in Order Book Bids. +| `bid_strategy.order_book_top` | 0 | Bot will use the top N rate in Order Book Bids. Ie. a value of 2 will allow the bot to pick the 2nd bid rate in Order Book Bids. +| `bid_strategy. check_depth_of_market.enabled` | false | Does not buy if the % difference of buy orders and sell orders is met in Order Book. +| `bid_strategy. check_depth_of_market.bids_to_ask_delta` | 0 | The % difference of buy orders and sell orders found in Order Book. A value lesser than 1 means sell orders is greater, while value greater than 1 means buy orders is higher. +| `ask_strategy.use_order_book` | false | Allows selling of open traded pair using the rates in Order Book Asks. +| `ask_strategy.order_book_min` | 0 | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. +| `ask_strategy.order_book_max` | 0 | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. +| `order_types` | None | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). [Strategy Override](#parameters-in-strategy). +| `order_time_in_force` | None | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-strategy). +| `exchange.name` | bittrex | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). +| `exchange.key` | key | API key to use for the exchange. Only required when you are in production mode. +| `exchange.secret` | secret | API secret to use for the exchange. Only required when you are in production mode. +| `exchange.pair_whitelist` | [] | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param. +| `exchange.pair_blacklist` | [] | List of currency the bot must avoid. Useful when using `--dynamic-whitelist` param. +| `exchange.ccxt_rate_limit` | True | DEPRECATED!! Have CCXT handle Exchange rate limits. Depending on the exchange, having this to false can lead to temporary bans from the exchange. +| `exchange.ccxt_config` | None | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) +| `exchange.ccxt_async_config` | None | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) +| `edge` | false | Please refer to [edge configuration document](edge.md) for detailed explanation. +| `experimental.use_sell_signal` | false | Use your sell strategy in addition of the `minimal_roi`. [Strategy Override](#parameters-in-strategy). +| `experimental.sell_profit_only` | false | Waits until you have made a positive profit before taking a sell decision. [Strategy Override](#parameters-in-strategy). +| `experimental.ignore_roi_if_buy_signal` | false | Does not sell if the buy-signal is still active. Takes preference over `minimal_roi` and `use_sell_signal`. [Strategy Override](#parameters-in-strategy). +| `pairlist.method` | StaticPairList | Use Static whitelist. [More information below](#dynamic-pairlists). +| `pairlist.config` | None | Additional configuration for dynamic pairlists. [More information below](#dynamic-pairlists). +| `telegram.enabled` | true | **Required.** Enable or not the usage of Telegram. +| `telegram.token` | token | Your Telegram bot token. Only required if `telegram.enabled` is `true`. +| `telegram.chat_id` | chat_id | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. +| `webhook.enabled` | false | Enable usage of Webhook notifications +| `webhook.url` | false | URL for the webhook. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details. +| `webhook.webhookbuy` | false | Payload to send on buy. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. +| `webhook.webhooksell` | false | Payload to send on sell. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. +| `webhook.webhookstatus` | false | Payload to send on status calls. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details. +| `db_url` | `sqlite:///tradesv3.sqlite`| Declares database URL to use. NOTE: This defaults to `sqlite://` if `dry_run` is `True`. +| `initial_state` | running | Defines the initial application state. More information below. +| `forcebuy_enable` | false | Enables the RPC Commands to force a buy. More information below. +| `strategy` | DefaultStrategy | Defines Strategy class to use. +| `strategy_path` | null | Adds an additional strategy lookup path (must be a folder). +| `internals.process_throttle_secs` | 5 | **Required.** Set the process throttle. Value in second. ### Parameters in strategy From 74b03d05291408003032eb4e4b0b926e0dc28c76 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 24 Jan 2019 07:03:41 +0100 Subject: [PATCH 256/358] Add tests and default values for all experimental features --- freqtrade/resolvers/strategy_resolver.py | 4 +- freqtrade/tests/strategy/test_strategy.py | 56 +++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 7a6292c8d..ab506131f 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -56,8 +56,8 @@ class StrategyResolver(IResolver): ("order_types", None, False), ("order_time_in_force", None, False), ("use_sell_signal", False, True), - ("sell_profit_only", None, True), - ("ignore_roi_if_buy_signal", None, True), + ("sell_profit_only", False, True), + ("ignore_roi_if_buy_signal", False, True), ] for attribute, default, experimental in attributes: if experimental: diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index b2315f381..e9240de99 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -294,6 +294,62 @@ def test_strategy_override_order_tif(caplog): StrategyResolver(config) +def test_strategy_override_use_sell_signal(caplog): + caplog.set_level(logging.INFO) + config = { + 'strategy': 'DefaultStrategy', + } + resolver = StrategyResolver(config) + assert not resolver.strategy.use_sell_signal + assert isinstance(resolver.strategy.use_sell_signal, bool) + # must be inserted to configuration + assert 'use_sell_signal' in config['experimental'] + assert not config['experimental']['use_sell_signal'] + + config = { + 'strategy': 'DefaultStrategy', + 'experimental': { + 'use_sell_signal': True, + }, + } + resolver = StrategyResolver(config) + + assert resolver.strategy.use_sell_signal + assert isinstance(resolver.strategy.use_sell_signal, bool) + assert ('freqtrade.resolvers.strategy_resolver', + logging.INFO, + "Override strategy 'use_sell_signal' with value in config file: True." + ) in caplog.record_tuples + + +def test_strategy_override_use_sell_profit_only(caplog): + caplog.set_level(logging.INFO) + config = { + 'strategy': 'DefaultStrategy', + } + resolver = StrategyResolver(config) + assert not resolver.strategy.sell_profit_only + assert isinstance(resolver.strategy.sell_profit_only, bool) + # must be inserted to configuration + assert 'sell_profit_only' in config['experimental'] + assert not config['experimental']['sell_profit_only'] + + config = { + 'strategy': 'DefaultStrategy', + 'experimental': { + 'sell_profit_only': True, + }, + } + resolver = StrategyResolver(config) + + assert resolver.strategy.sell_profit_only + assert isinstance(resolver.strategy.sell_profit_only, bool) + assert ('freqtrade.resolvers.strategy_resolver', + logging.INFO, + "Override strategy 'sell_profit_only' with value in config file: True." + ) in caplog.record_tuples + + def test_deprecate_populate_indicators(result): default_location = path.join(path.dirname(path.realpath(__file__))) resolver = StrategyResolver({'strategy': 'TestStrategyLegacy', From 9960fe07bc3bc9a289a05e6716510c6d3dae7bdb Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 24 Jan 2019 07:08:21 +0100 Subject: [PATCH 257/358] Add experimental settings to sample strategy --- user_data/strategies/test_strategy.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/user_data/strategies/test_strategy.py b/user_data/strategies/test_strategy.py index 6ca45a5dc..0241475e9 100644 --- a/user_data/strategies/test_strategy.py +++ b/user_data/strategies/test_strategy.py @@ -53,6 +53,11 @@ class TestStrategy(IStrategy): # run "populate_indicators" only for new candle ta_on_candle = False + # Experimental settings (configuration will overide these if set) + use_sell_signal = False + use_profit_only = False + ignore_roi_if_buy_signal = False + # Optional order type mapping order_types = { 'buy': 'limit', From dcceb40fab456ddb742344126c3e7972c6753ff4 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 24 Jan 2019 13:33:07 +0100 Subject: [PATCH 258/358] 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 259/358] 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 260/358] 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 261/358] 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 56a3d781286e944acc375ea6858223a3ac1c7d14 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 25 Jan 2019 19:14:29 +0100 Subject: [PATCH 262/358] Fix typo --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index d8af209d4..f09c11720 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -15,7 +15,7 @@ Mandatory Parameters are marked as **Required**. |----------|---------|-------------| | `max_open_trades` | 3 | **Required.** Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades) | `stake_currency` | BTC | **Required.** Crypto-currency used for trading. -| `stake_amount` | 0.05 | **Required.** Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to `"unlimited"` to allow the bot to use all avaliable balance. +| `stake_amount` | 0.05 | **Required.** Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to `"unlimited"` to allow the bot to use all available balance. | `ticker_interval` | [1m, 5m, 30m, 1h, 1d] | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes. [Strategy Override](#parameters-in-strategy). | `fiat_display_currency` | USD | **Required.** Fiat currency used to show your profits. More information below. | `dry_run` | true | **Required.** Define if the bot must be in Dry-run or production mode. From 3c316fe3e4710cc1943b0334276d797d98ea32a5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 25 Jan 2019 19:14:38 +0100 Subject: [PATCH 263/358] Fix alignment --- freqtrade/resolvers/strategy_resolver.py | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index ab506131f..8fe45959d 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -45,19 +45,19 @@ class StrategyResolver(IResolver): # Set attributes # Check if we need to override configuration - # (Attribute name, default, experimental) - attributes = [("minimal_roi", None, False), - ("ticker_interval", None, False), - ("stoploss", None, False), - ("trailing_stop", None, False), - ("trailing_stop_positive", None, False), - ("trailing_stop_positive_offset", 0.0, False), - ("process_only_new_candles", None, False), - ("order_types", None, False), - ("order_time_in_force", None, False), - ("use_sell_signal", False, True), - ("sell_profit_only", False, True), - ("ignore_roi_if_buy_signal", False, True), + # (Attribute name, default, experimental) + attributes = [("minimal_roi", None, False), + ("ticker_interval", None, False), + ("stoploss", None, False), + ("trailing_stop", None, False), + ("trailing_stop_positive", None, False), + ("trailing_stop_positive_offset", 0.0, False), + ("process_only_new_candles", None, False), + ("order_types", None, False), + ("order_time_in_force", None, False), + ("use_sell_signal", False, True), + ("sell_profit_only", False, True), + ("ignore_roi_if_buy_signal", False, True), ] for attribute, default, experimental in attributes: if experimental: From 5e7ba85dbe4bcbeeeb40d1f98302478cd80f11a0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 25 Jan 2019 19:17:44 +0100 Subject: [PATCH 264/358] Fix typo --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index f09c11720..2caae81f1 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -89,7 +89,7 @@ Values in the configuration are always overwriting values set in the strategy. `stake_amount` is an amount of crypto-currency your bot will use for each trade. The minimal value is 0.0005. If there is not enough crypto-currency in the account an exception is generated. -To allow the bot to trade all the avaliable `stake_currency` in your account set +To allow the bot to trade all the available `stake_currency` in your account set ```json "stake_amount" : "unlimited", From eec727639358dd8c2ca6c95475dfa38005cd4d9c Mon Sep 17 00:00:00 2001 From: AxelCh Date: Wed, 23 Jan 2019 14:11:05 -0400 Subject: [PATCH 265/358] 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 266/358] 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 267/358] 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 268/358] 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 269/358] 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 270/358] 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 From d222dd6717eead5fb28f2d3459b5725ae0a48648 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 26 Jan 2019 13:33:18 +0100 Subject: [PATCH 271/358] Update sqlalchemy from 1.2.16 to 1.2.17 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index eb1054f62..ef213ef3e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ ccxt==1.18.152 -SQLAlchemy==1.2.16 +SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 cachetools==3.0.0 From b112f2f3152da8cae8bf5fe4d954a1269b8c28cf Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 26 Jan 2019 13:33:20 +0100 Subject: [PATCH 272/358] Update pandas from 0.23.4 to 0.24.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ef213ef3e..30a561e01 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ cachetools==3.0.0 requests==2.21.0 urllib3==1.24.1 wrapt==1.11.1 -pandas==0.23.4 +pandas==0.24.0 scikit-learn==0.20.2 joblib==0.13.1 scipy==1.2.0 From bfd86093521478f788e208e4c0ce48c3ca54867f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 26 Jan 2019 19:16:33 +0100 Subject: [PATCH 273/358] Fix comment --- freqtrade/data/dataprovider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index f69b1287d..375b8bf5b 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -63,7 +63,7 @@ class DataProvider(object): def historic_ohlcv(self, pair: str, ticker_interval: str) -> DataFrame: """ - get historic ohlcv data stored for backtesting + get stored historic ohlcv data :param pair: pair to get the data for :param tick_interval: ticker_interval to get pair for """ From ba07348b82ffc6c9be80fa2a62742bdc383c509d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 26 Jan 2019 19:22:45 +0100 Subject: [PATCH 274/358] Rename additional_pairs to informative_pairs --- docs/bot-optimization.md | 4 ++-- freqtrade/freqtradebot.py | 2 +- freqtrade/strategy/default_strategy.py | 4 ++-- freqtrade/strategy/interface.py | 4 ++-- user_data/strategies/test_strategy.py | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/bot-optimization.md b/docs/bot-optimization.md index 5ac4be04c..8592f6cca 100644 --- a/docs/bot-optimization.md +++ b/docs/bot-optimization.md @@ -302,7 +302,7 @@ if self.dp: #### Get data for non-tradeable pairs -Data for additional pairs (reference pairs) can be beneficial for some strategies. +Data for additional, informative pairs (reference pairs) can be beneficial for some strategies. Ohlcv data for these pairs will be downloaded as part of the regular whitelist refresh process and is available via `DataProvider` just as other pairs (see above). These parts will **not** be traded unless they are also specified in the pair whitelist, or have been selected by Dynamic Whitelisting. @@ -311,7 +311,7 @@ The pairs need to be specified as tuples in the format `("pair", "interval")`, w Sample: ``` python -def additional_pairs(self): +def informative_pairs(self): return [("ETH/USDT", "5m"), ("BTC/TUSD", "15m"), ] diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 56ba404d6..656c700ac 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -175,7 +175,7 @@ class FreqtradeBot(object): for pair in self.active_pair_whitelist] # Refreshing candles self.dataprovider.refresh(pair_whitelist_tuple, - self.strategy.additional_pairs()) + self.strategy.informative_pairs()) # First process current opened trades for trade in trades: diff --git a/freqtrade/strategy/default_strategy.py b/freqtrade/strategy/default_strategy.py index 36064f640..5c7d50a65 100644 --- a/freqtrade/strategy/default_strategy.py +++ b/freqtrade/strategy/default_strategy.py @@ -42,9 +42,9 @@ class DefaultStrategy(IStrategy): 'sell': 'gtc', } - def additional_pairs(self): + def informative_pairs(self): """ - Define additional pair/interval combinations to be cached from the exchange. + Define additional, informative pair/interval combinations to be cached from the exchange. These pair/interval combinations are non-tradeable, unless they are part of the whitelist as well. For more information, please consult the documentation diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index c7ec8dda6..733651df4 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -133,9 +133,9 @@ class IStrategy(ABC): :return: DataFrame with sell column """ - def additional_pairs(self) -> List[Tuple[str, str]]: + def informative_pairs(self) -> List[Tuple[str, str]]: """ - Define additional pair/interval combinations to be cached from the exchange. + Define additional, informative pair/interval combinations to be cached from the exchange. These pair/interval combinations are non-tradeable, unless they are part of the whitelist as well. For more information, please consult the documentation diff --git a/user_data/strategies/test_strategy.py b/user_data/strategies/test_strategy.py index 314787f7a..e1f7d9c11 100644 --- a/user_data/strategies/test_strategy.py +++ b/user_data/strategies/test_strategy.py @@ -67,9 +67,9 @@ class TestStrategy(IStrategy): 'sell': 'gtc' } - def additional_pairs(self): + def informative_pairs(self): """ - Define additional pair/interval combinations to be cached from the exchange. + Define additional, informative pair/interval combinations to be cached from the exchange. These pair/interval combinations are non-tradeable, unless they are part of the whitelist as well. For more information, please consult the documentation From 3446dd179244097d8be4b25abeedcccb361364d0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 26 Jan 2019 20:05:49 +0100 Subject: [PATCH 275/358] Add test informative_pairs_added --- freqtrade/tests/test_freqtradebot.py | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index ef2815bed..9200c5fa6 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -807,6 +807,37 @@ def test_process_trade_no_whitelist_pair( assert result is True +def test_process_informative_pairs_added(default_conf, ticker, markets, mocker) -> None: + patch_RPCManager(mocker) + patch_exchange(mocker) + + def _refresh_whitelist(list): + return ['ETH/BTC', 'LTC/BTC', 'XRP/BTC', 'NEO/BTC'] + + refresh_mock = MagicMock() + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_ticker=ticker, + get_markets=markets, + buy=MagicMock(side_effect=TemporaryError), + refresh_latest_ohlcv=refresh_mock, + ) + inf_pairs = MagicMock(return_value=[("BTC/ETH", '1m'), ("ETH/USDT", "1h")]) + mocker.patch('time.sleep', return_value=None) + + freqtrade = FreqtradeBot(default_conf) + freqtrade.pairlists._validate_whitelist = _refresh_whitelist + freqtrade.strategy.informative_pairs = inf_pairs + # patch_get_signal(freqtrade) + + freqtrade._process() + assert inf_pairs.call_count == 1 + assert refresh_mock.call_count == 1 + assert ("BTC/ETH", "1m") in refresh_mock.call_args[0][0] + assert ("ETH/USDT", "1h") in refresh_mock.call_args[0][0] + assert ("ETH/BTC", default_conf["ticker_interval"]) in refresh_mock.call_args[0][0] + + def test_balance_fully_ask_side(mocker, default_conf) -> None: default_conf['bid_strategy']['ask_last_balance'] = 0.0 freqtrade = get_patched_freqtradebot(mocker, default_conf) From 1d08ada939d7d1d6862ce8d3c06f7d97603e7993 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Jan 2019 10:40:52 +0100 Subject: [PATCH 276/358] Fix backtest-test with timestamp-conversion --- freqtrade/tests/optimize/test_backtesting.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index fbcbe4c55..35ed9c49e 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -529,10 +529,11 @@ def test_backtest(default_conf, fee, mocker) -> None: {'pair': [pair, pair], 'profit_percent': [0.0, 0.0], 'profit_abs': [0.0, 0.0], - 'open_time': [Arrow(2018, 1, 29, 18, 40, 0).datetime, - Arrow(2018, 1, 30, 3, 30, 0).datetime], - 'close_time': [Arrow(2018, 1, 29, 22, 35, 0).datetime, - Arrow(2018, 1, 30, 4, 10, 0).datetime], + 'open_time': pd.to_datetime([Arrow(2018, 1, 29, 18, 40, 0).datetime, + Arrow(2018, 1, 30, 3, 30, 0).datetime], utc=True + ), + 'close_time': pd.to_datetime([Arrow(2018, 1, 29, 22, 35, 0).datetime, + Arrow(2018, 1, 30, 4, 10, 0).datetime], utc=True), 'open_index': [78, 184], 'close_index': [125, 192], 'trade_duration': [235, 40], From e0ad095bc7541200b4005ae714d38e041e6c072c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Jan 2019 10:47:02 +0100 Subject: [PATCH 277/358] Simplify conversation to python dates (pandas offers this "for free" --- freqtrade/misc.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 23d8732a0..d03187d77 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -38,12 +38,7 @@ def datesarray_to_datetimearray(dates: np.ndarray) -> np.ndarray: An numpy-array of datetimes :return: numpy-array of datetime """ - times = [] - dates = dates.astype(datetime) - for index in range(0, dates.size): - date = dates[index].to_pydatetime() - times.append(date) - return np.array(times) + return dates.dt.to_pydatetime() def common_datearray(dfs: Dict[str, DataFrame]) -> np.ndarray: From 38f73dafb39e999d01431169fc7115c83ac6f447 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Jan 2019 10:47:24 +0100 Subject: [PATCH 278/358] Fix indexing error --- freqtrade/tests/test_misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/tests/test_misc.py b/freqtrade/tests/test_misc.py index c7ff1f077..2da6b8718 100644 --- a/freqtrade/tests/test_misc.py +++ b/freqtrade/tests/test_misc.py @@ -41,7 +41,7 @@ def test_common_datearray(default_conf) -> None: assert dates.size == dataframes['UNITTEST/BTC']['date'].size assert dates[0] == dataframes['UNITTEST/BTC']['date'][0] - assert dates[-1] == dataframes['UNITTEST/BTC']['date'][-1] + assert dates[-1] == dataframes['UNITTEST/BTC']['date'].iloc[-1] def test_file_dump_json(mocker) -> None: From f095492804701cc031f0f8ec187ef25cb031175a Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sun, 27 Jan 2019 13:33:07 +0100 Subject: [PATCH 279/358] Update ccxt from 1.18.152 to 1.18.155 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 30a561e01..1ecb4bfdb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.152 +ccxt==1.18.155 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From 1a1123a55594c8fef0f9db3a48cdf4ba5731515e Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Mon, 28 Jan 2019 13:33:07 +0100 Subject: [PATCH 280/358] Update ccxt from 1.18.155 to 1.18.160 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1ecb4bfdb..bf09bdddc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.155 +ccxt==1.18.160 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From cf283344de0ac88ebdcbe276564e8913b0a1a55b Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Tue, 29 Jan 2019 13:33:08 +0100 Subject: [PATCH 281/358] Update ccxt from 1.18.160 to 1.18.171 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bf09bdddc..24c21abe9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.160 +ccxt==1.18.171 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From 84b6b8fe9770030866ec36ea8da1266373e07d4a Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 30 Jan 2019 11:23:23 +0100 Subject: [PATCH 282/358] edge messages enriched --- freqtrade/edge/__init__.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/freqtrade/edge/__init__.py b/freqtrade/edge/__init__.py index 76af703aa..baef811de 100644 --- a/freqtrade/edge/__init__.py +++ b/freqtrade/edge/__init__.py @@ -190,9 +190,16 @@ class Edge(): if self._final_pairs != final: self._final_pairs = final if self._final_pairs: - logger.info('Edge validated only %s', self._final_pairs) + logger.info( + 'Minimum expectancy and minimum winrate are met only for %s,' + ' so other pairs are filtered out.', + self._final_pairs + ) else: - logger.info('Edge removed all pairs as no pair with minimum expectancy was found !') + logger.info( + 'Edge removed all pairs as no pair with minimum expectancy ' + 'and minimum winrate was found !' + ) return self._final_pairs From 9f87a27465b2fc7ad7cef193eef4352292fe168a Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 30 Jan 2019 13:33:06 +0100 Subject: [PATCH 283/358] Update ccxt from 1.18.171 to 1.18.179 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 24c21abe9..bce61d65a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.171 +ccxt==1.18.179 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From 6ad1089f45e2299cc8f449d7297fe8e980277c67 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 30 Jan 2019 13:33:08 +0100 Subject: [PATCH 284/358] Update cachetools from 3.0.0 to 3.1.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bce61d65a..61d5a5295 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ ccxt==1.18.179 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 -cachetools==3.0.0 +cachetools==3.1.0 requests==2.21.0 urllib3==1.24.1 wrapt==1.11.1 From 0c959c22ecbbb840e63a6d76c065729e24de2e2d Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 30 Jan 2019 13:33:10 +0100 Subject: [PATCH 285/358] Update flake8 from 3.6.0 to 3.7.1 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 4f8504c8a..d30fd724d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,7 @@ # Include all requirements to run the bot. -r requirements.txt -flake8==3.6.0 +flake8==3.7.1 flake8-type-annotations==0.1.0 flake8-tidy-imports==1.1.0 pytest==4.1.1 From 576d9b8f5cc3fc800cf5266002d38393e4c42b84 Mon Sep 17 00:00:00 2001 From: Samuel Husso Date: Thu, 31 Jan 2019 07:50:13 +0200 Subject: [PATCH 286/358] requirements: move numpy installation earlier as later packages require it --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 61d5a5295..fc2a5e55d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,12 +6,12 @@ cachetools==3.1.0 requests==2.21.0 urllib3==1.24.1 wrapt==1.11.1 +numpy==1.16.0 pandas==0.24.0 scikit-learn==0.20.2 joblib==0.13.1 scipy==1.2.0 jsonschema==2.6.0 -numpy==1.16.0 TA-Lib==0.4.17 tabulate==0.8.3 coinmarketcap==5.0.3 From e3ae8d3f69b77d89159ec83cefc39137989212d6 Mon Sep 17 00:00:00 2001 From: Samuel Husso Date: Thu, 31 Jan 2019 07:51:03 +0200 Subject: [PATCH 287/358] flake8 3.7.1 fixes --- freqtrade/data/history.py | 6 +++--- freqtrade/exchange/__init__.py | 4 ++-- freqtrade/freqtradebot.py | 2 +- freqtrade/tests/optimize/test_backtesting.py | 4 ++-- freqtrade/tests/rpc/test_fiat_convert.py | 2 +- freqtrade/tests/strategy/test_strategy.py | 2 +- freqtrade/vendor/qtpylib/indicators.py | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index 5492f15ad..7d89f7ad6 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -230,6 +230,6 @@ def download_pair_history(datadir: Optional[Path], misc.file_dump_json(filename, data) return True except BaseException: - logger.info('Failed to download the pair: "%s", Interval: %s', - pair, tick_interval) - return False + logger.info('Failed to download the pair: "%s", Interval: %s', + pair, tick_interval) + return False diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index e4d83cf6d..47886989e 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -237,7 +237,7 @@ class Exchange(object): f'Exchange {self.name} does not support market orders.') if order_types.get('stoploss_on_exchange'): - if self.name is not 'Binance': + if self.name != 'Binance': raise OperationalException( 'On exchange stoploss is not supported for %s.' % self.name ) @@ -247,7 +247,7 @@ class Exchange(object): Checks if order time in force configured in strategy/config are supported """ if any(v != 'gtc' for k, v in order_time_in_force.items()): - if self.name is not 'Binance': + if self.name != 'Binance': raise OperationalException( f'Time in force policies are not supporetd for {self.name} yet.') diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 656c700ac..e81e05e9e 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -820,7 +820,7 @@ class FreqtradeBot(object): # we consider the sell price stop price if self.config.get('dry_run', False) and sell_type == 'stoploss' \ and self.strategy.order_types['stoploss_on_exchange']: - limit = trade.stop_loss + limit = trade.stop_loss # First cancelling stoploss on exchange ... if self.strategy.order_types.get('stoploss_on_exchange') and trade.stoploss_order_id: diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 11d011ccd..e69b1374e 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -663,8 +663,8 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker): def test_backtest_multi_pair(default_conf, fee, mocker): def evaluate_result_multi(results, freq, max_open_trades): - # Find overlapping trades by expanding each trade once per period - # and then counting overlaps + # Find overlapping trades by expanding each trade once per period + # and then counting overlaps dates = [pd.Series(pd.date_range(row[1].open_time, row[1].close_time, freq=freq)) for row in results[['open_time', 'close_time']].iterrows()] deltas = [len(x) for x in dates] diff --git a/freqtrade/tests/rpc/test_fiat_convert.py b/freqtrade/tests/rpc/test_fiat_convert.py index 7d857d2f1..fbc942432 100644 --- a/freqtrade/tests/rpc/test_fiat_convert.py +++ b/freqtrade/tests/rpc/test_fiat_convert.py @@ -117,7 +117,7 @@ def test_fiat_convert_get_price(mocker): assert fiat_convert._pairs[0].crypto_symbol == 'BTC' assert fiat_convert._pairs[0].fiat_symbol == 'USD' assert fiat_convert._pairs[0].price == 28000.0 - assert fiat_convert._pairs[0]._expiration is not 0 + assert fiat_convert._pairs[0]._expiration != 0 assert len(fiat_convert._pairs) == 1 # Verify the cached is used diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index e9240de99..602ea5dbe 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -364,7 +364,7 @@ def test_deprecate_populate_indicators(result): in str(w[-1].message) with warnings.catch_warnings(record=True) as w: - # Cause all warnings to always be triggered. + # Cause all warnings to always be triggered. warnings.simplefilter("always") resolver.strategy.advise_buy(indicators, 'ETH/BTC') assert len(w) == 1 diff --git a/freqtrade/vendor/qtpylib/indicators.py b/freqtrade/vendor/qtpylib/indicators.py index e68932998..3866d36c1 100644 --- a/freqtrade/vendor/qtpylib/indicators.py +++ b/freqtrade/vendor/qtpylib/indicators.py @@ -236,7 +236,7 @@ def crossed(series1, series2, direction=None): if direction is None: return above or below - return above if direction is "above" else below + return above if direction == "above" else below def crossed_above(series1, series2): From 68a4e0426ee0ada546995dbb5cdadd897faa9337 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 31 Jan 2019 13:34:07 +0100 Subject: [PATCH 288/358] Update ccxt from 1.18.179 to 1.18.190 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index fc2a5e55d..30ab6ea1d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.179 +ccxt==1.18.190 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From 8d9114aa79ce5b1348bb905bb3f7c7ca9ea8cb06 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 31 Jan 2019 13:34:09 +0100 Subject: [PATCH 289/358] Update flake8 from 3.7.1 to 3.7.3 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index d30fd724d..96d4a7af1 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,7 @@ # Include all requirements to run the bot. -r requirements.txt -flake8==3.7.1 +flake8==3.7.3 flake8-type-annotations==0.1.0 flake8-tidy-imports==1.1.0 pytest==4.1.1 From d07cc5929e09958ae0536dcf5333086fd27b0829 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 31 Jan 2019 13:34:11 +0100 Subject: [PATCH 290/358] Update pytest from 4.1.1 to 4.2.0 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 96d4a7af1..88a39d034 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,7 +4,7 @@ flake8==3.7.3 flake8-type-annotations==0.1.0 flake8-tidy-imports==1.1.0 -pytest==4.1.1 +pytest==4.2.0 pytest-mock==1.10.0 pytest-asyncio==0.10.0 pytest-cov==2.6.1 From ba4e5cae54c126abe2afaa33ae6d3e1227eac1c3 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 1 Feb 2019 13:34:07 +0100 Subject: [PATCH 291/358] Update ccxt from 1.18.190 to 1.18.193 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 30ab6ea1d..cc707cdc8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.190 +ccxt==1.18.193 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From 2ce3bd956d936f1a1dc9cc7f8059b84f1743d754 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 1 Feb 2019 13:34:09 +0100 Subject: [PATCH 292/358] Update numpy from 1.16.0 to 1.16.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index cc707cdc8..27baf868b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ cachetools==3.1.0 requests==2.21.0 urllib3==1.24.1 wrapt==1.11.1 -numpy==1.16.0 +numpy==1.16.1 pandas==0.24.0 scikit-learn==0.20.2 joblib==0.13.1 From 24f779eda70fc66b2d85f8c583ce8ca6e9b1452c Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 1 Feb 2019 13:34:10 +0100 Subject: [PATCH 293/358] Update flake8 from 3.7.3 to 3.7.4 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 88a39d034..29c8aa523 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,7 @@ # Include all requirements to run the bot. -r requirements.txt -flake8==3.7.3 +flake8==3.7.4 flake8-type-annotations==0.1.0 flake8-tidy-imports==1.1.0 pytest==4.2.0 From 576d893d95f92fee963692ab90a41c068315f815 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 2 Feb 2019 13:34:06 +0100 Subject: [PATCH 294/358] Update ccxt from 1.18.193 to 1.18.195 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 27baf868b..857d9d86d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.193 +ccxt==1.18.195 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From 8d5474d4d5d540c20fbe9bc576e7039e33fad418 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 2 Feb 2019 13:34:08 +0100 Subject: [PATCH 295/358] Update plotly from 3.5.0 to 3.6.0 --- requirements-plot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-plot.txt b/requirements-plot.txt index 6da32b0f7..d3f003fba 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,5 +1,5 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==3.5.0 +plotly==3.6.0 From f2dd32e3195111a7f5356b984e545715099d72da Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sun, 3 Feb 2019 13:34:07 +0100 Subject: [PATCH 296/358] Update flake8-tidy-imports from 1.1.0 to 2.0.0 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 29c8aa523..1631eb02c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,7 +3,7 @@ flake8==3.7.4 flake8-type-annotations==0.1.0 -flake8-tidy-imports==1.1.0 +flake8-tidy-imports==2.0.0 pytest==4.2.0 pytest-mock==1.10.0 pytest-asyncio==0.10.0 From e8ed8a2ea73c42ffd872896ca57d4c1937230787 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 3 Feb 2019 13:39:05 +0100 Subject: [PATCH 297/358] Handle orders cancelled on exchange fix #1527 --- freqtrade/freqtradebot.py | 48 ++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index e81e05e9e..3c0979861 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -738,8 +738,15 @@ class FreqtradeBot(object): self.wallets.update() continue - # Check if trade is still actually open - if order['status'] == 'open': + # Handle cancelled on exchange + if order['status'] == 'canceled': + if order['side'] == 'buy': + self.handle_buy_order_full_cancel(trade, "canceled on Exchange") + elif order['side'] == 'sell': + self.handle_timedout_limit_sell(trade, order) + self.wallets.update() + # Check if order is still actually open + elif order['status'] == 'open': if order['side'] == 'buy' and ordertime < buy_timeoutthreashold: self.handle_timedout_limit_buy(trade, order) self.wallets.update() @@ -747,24 +754,24 @@ class FreqtradeBot(object): self.handle_timedout_limit_sell(trade, order) self.wallets.update() - # FIX: 20180110, why is cancel.order unconditionally here, whereas - # it is conditionally called in the - # handle_timedout_limit_sell()? + def handle_buy_order_full_cancel(self, trade: Trade, reason: str) -> None: + + Trade.session.delete(trade) + Trade.session.flush() + logger.info('Buy order %s for %s.', reason, trade) + self.rpc.send_msg({ + 'type': RPCMessageType.STATUS_NOTIFICATION, + 'status': f'Unfilled buy order for {trade.pair} {reason}' + }) + def handle_timedout_limit_buy(self, trade: Trade, order: Dict) -> bool: """Buy timeout - cancel order :return: True if order was fully cancelled """ - pair_s = trade.pair.replace('_', '/') self.exchange.cancel_order(trade.open_order_id, trade.pair) if order['remaining'] == order['amount']: # if trade is not partially completed, just delete the trade - Trade.session.delete(trade) - Trade.session.flush() - logger.info('Buy order timeout for %s.', trade) - self.rpc.send_msg({ - 'type': RPCMessageType.STATUS_NOTIFICATION, - 'status': f'Unfilled buy order for {pair_s} cancelled due to timeout' - }) + self.handle_buy_order_full_cancel(trade, "cancelled due to timeout") return True # if trade is partially complete, edit the stake details for the trade @@ -775,11 +782,10 @@ class FreqtradeBot(object): logger.info('Partial buy order timeout for %s.', trade) self.rpc.send_msg({ 'type': RPCMessageType.STATUS_NOTIFICATION, - 'status': f'Remaining buy order for {pair_s} cancelled due to timeout' + 'status': f'Remaining buy order for {trade.pair} cancelled due to timeout' }) return False - # FIX: 20180110, should cancel_order() be cond. or unconditionally called? def handle_timedout_limit_sell(self, trade: Trade, order: Dict) -> bool: """ Sell timeout - cancel order and update trade @@ -788,7 +794,13 @@ class FreqtradeBot(object): pair_s = trade.pair.replace('_', '/') if order['remaining'] == order['amount']: # if trade is not partially completed, just cancel the trade - self.exchange.cancel_order(trade.open_order_id, trade.pair) + if order["status"] != "canceled": + reason = "due to timeout" + self.exchange.cancel_order(trade.open_order_id, trade.pair) + logger.info('Sell order timeout for %s.', trade) + else: + reason = "on exchange" + logger.info('Sell order Cancelled on exchange for %s.', trade) trade.close_rate = None trade.close_profit = None trade.close_date = None @@ -796,9 +808,9 @@ class FreqtradeBot(object): trade.open_order_id = None self.rpc.send_msg({ 'type': RPCMessageType.STATUS_NOTIFICATION, - 'status': f'Unfilled sell order for {pair_s} cancelled due to timeout' + 'status': f'Unfilled sell order for {pair_s} cancelled {reason}' }) - logger.info('Sell order timeout for %s.', trade) + return True # TODO: figure out how to handle partially complete sell orders From 91ed02134e56000eb46b5d347daa3f06c13e460b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 3 Feb 2019 13:39:19 +0100 Subject: [PATCH 298/358] Add tests for cancelled on exchnage --- freqtrade/tests/test_freqtradebot.py | 81 +++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 9200c5fa6..8513ee96d 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1554,6 +1554,47 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, fe assert nb_trades == 0 +def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old, + fee, mocker, caplog) -> None: + """ Handle Buy order cancelled on exchange""" + rpc_mock = patch_RPCManager(mocker) + cancel_order_mock = MagicMock() + patch_exchange(mocker) + limit_buy_order_old.update({"status": "canceled"}) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_ticker=ticker, + get_order=MagicMock(return_value=limit_buy_order_old), + cancel_order=cancel_order_mock, + get_fee=fee + ) + freqtrade = FreqtradeBot(default_conf) + + trade_buy = Trade( + pair='ETH/BTC', + open_rate=0.00001099, + exchange='bittrex', + open_order_id='123456789', + amount=90.99181073, + fee_open=0.0, + fee_close=0.0, + stake_amount=1, + open_date=arrow.utcnow().shift(minutes=-601).datetime, + is_open=True + ) + + Trade.session.add(trade_buy) + + # check it does cancel buy orders over the time limit + freqtrade.check_handle_timedout() + assert cancel_order_mock.call_count == 0 + assert rpc_mock.call_count == 1 + trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all() + nb_trades = len(trades) + assert nb_trades == 0 + # assert log_has("Buy order canceled on Exchange for Trade.*", caplog.record_tuples) + + def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old, fee, mocker) -> None: rpc_mock = patch_RPCManager(mocker) @@ -1628,6 +1669,43 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old, assert trade_sell.is_open is True +def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old, mocker) -> None: + """ Handle sell order cancelled on exchange""" + rpc_mock = patch_RPCManager(mocker) + cancel_order_mock = MagicMock() + limit_sell_order_old.update({"status": "canceled"}) + patch_exchange(mocker) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_ticker=ticker, + get_order=MagicMock(return_value=limit_sell_order_old), + cancel_order=cancel_order_mock + ) + freqtrade = FreqtradeBot(default_conf) + + trade_sell = Trade( + pair='ETH/BTC', + open_rate=0.00001099, + exchange='bittrex', + open_order_id='123456789', + amount=90.99181073, + fee_open=0.0, + fee_close=0.0, + stake_amount=1, + open_date=arrow.utcnow().shift(hours=-5).datetime, + close_date=arrow.utcnow().shift(minutes=-601).datetime, + is_open=False + ) + + Trade.session.add(trade_sell) + + # check it does cancel sell orders over the time limit + freqtrade.check_handle_timedout() + assert cancel_order_mock.call_count == 0 + assert rpc_mock.call_count == 1 + assert trade_sell.is_open is True + + def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial, mocker) -> None: rpc_mock = patch_RPCManager(mocker) @@ -1744,7 +1822,8 @@ def test_handle_timedout_limit_sell(mocker, default_conf) -> None: trade = MagicMock() order = {'remaining': 1, - 'amount': 1} + 'amount': 1, + 'status': "open"} assert freqtrade.handle_timedout_limit_sell(trade, order) assert cancel_order_mock.call_count == 1 order['amount'] = 2 From 02c3552954dc3400bc3972eec0146f01dadee424 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 3 Feb 2019 13:49:55 +0100 Subject: [PATCH 299/358] Adjust comment --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 3c0979861..733aefe4a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -800,7 +800,7 @@ class FreqtradeBot(object): logger.info('Sell order timeout for %s.', trade) else: reason = "on exchange" - logger.info('Sell order Cancelled on exchange for %s.', trade) + logger.info('Sell order canceled on exchange for %s.', trade) trade.close_rate = None trade.close_profit = None trade.close_date = None From 40fea4593f0ea2f9e462d20f10a0c71728efb8b1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 3 Feb 2019 13:50:03 +0100 Subject: [PATCH 300/358] Add log_has_re --- freqtrade/tests/conftest.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index ba2e1e84e..809dc12e0 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -1,6 +1,7 @@ # pragma pylint: disable=missing-docstring import json import logging +import re from datetime import datetime from functools import reduce from typing import Dict, Optional @@ -27,6 +28,12 @@ def log_has(line, logs): False) +def log_has_re(line, logs): + return reduce(lambda a, b: a or b, + filter(lambda x: re.match(line, x[2]), logs), + False) + + def patch_exchange(mocker, api_mock=None, id='bittrex') -> None: mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) From 1d940041e31bc2f18cd7a43b3d87482f267f730d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 3 Feb 2019 13:50:15 +0100 Subject: [PATCH 301/358] Add log test for cancelled order --- freqtrade/tests/test_freqtradebot.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 8513ee96d..2edf063b5 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -18,7 +18,7 @@ from freqtrade.persistence import Trade from freqtrade.rpc import RPCMessageType from freqtrade.state import State from freqtrade.strategy.interface import SellType, SellCheckTuple -from freqtrade.tests.conftest import log_has, patch_exchange, patch_edge, patch_wallet +from freqtrade.tests.conftest import log_has, log_has_re, patch_exchange, patch_edge, patch_wallet # Functions for recurrent object patching @@ -1592,7 +1592,7 @@ def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old, trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all() nb_trades = len(trades) assert nb_trades == 0 - # assert log_has("Buy order canceled on Exchange for Trade.*", caplog.record_tuples) + assert log_has_re("Buy order canceled on Exchange for Trade.*", caplog.record_tuples) def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old, @@ -1669,7 +1669,8 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old, assert trade_sell.is_open is True -def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old, mocker) -> None: +def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old, + mocker, caplog) -> None: """ Handle sell order cancelled on exchange""" rpc_mock = patch_RPCManager(mocker) cancel_order_mock = MagicMock() @@ -1704,6 +1705,7 @@ def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old, assert cancel_order_mock.call_count == 0 assert rpc_mock.call_count == 1 assert trade_sell.is_open is True + assert log_has_re("Sell order canceled on exchange for Trade.*", caplog.record_tuples) def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial, From 0a71ebce68c1372efd92349e52aa8ed8a6142c91 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 4 Feb 2019 06:13:22 +0100 Subject: [PATCH 302/358] Remove unneeded pair conversation, add docstring --- freqtrade/freqtradebot.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 733aefe4a..a13e92926 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -755,7 +755,7 @@ class FreqtradeBot(object): self.wallets.update() def handle_buy_order_full_cancel(self, trade: Trade, reason: str) -> None: - + """Close trade in database and send message""" Trade.session.delete(trade) Trade.session.flush() logger.info('Buy order %s for %s.', reason, trade) @@ -791,7 +791,6 @@ class FreqtradeBot(object): Sell timeout - cancel order and update trade :return: True if order was fully cancelled """ - pair_s = trade.pair.replace('_', '/') if order['remaining'] == order['amount']: # if trade is not partially completed, just cancel the trade if order["status"] != "canceled": @@ -808,7 +807,7 @@ class FreqtradeBot(object): trade.open_order_id = None self.rpc.send_msg({ 'type': RPCMessageType.STATUS_NOTIFICATION, - 'status': f'Unfilled sell order for {pair_s} cancelled {reason}' + 'status': f'Unfilled sell order for {trade.pair} cancelled {reason}' }) return True From 218b501119be3b94c866c313c344d17d9082b3e8 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Mon, 4 Feb 2019 13:34:10 +0100 Subject: [PATCH 303/358] Update ccxt from 1.18.195 to 1.18.197 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 857d9d86d..62b97e1cd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.195 +ccxt==1.18.197 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From 64891df122bdb36d95ef807cfb882d719c5d7355 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Mon, 4 Feb 2019 13:34:12 +0100 Subject: [PATCH 304/358] Update pandas from 0.24.0 to 0.24.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 62b97e1cd..530271e9c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ requests==2.21.0 urllib3==1.24.1 wrapt==1.11.1 numpy==1.16.1 -pandas==0.24.0 +pandas==0.24.1 scikit-learn==0.20.2 joblib==0.13.1 scipy==1.2.0 From fcc7cb98924bc8f5d845d9016653e3a692365e01 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Tue, 5 Feb 2019 13:34:06 +0100 Subject: [PATCH 305/358] Update flake8 from 3.7.4 to 3.7.5 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 1631eb02c..f6e3cd328 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,7 @@ # Include all requirements to run the bot. -r requirements.txt -flake8==3.7.4 +flake8==3.7.5 flake8-type-annotations==0.1.0 flake8-tidy-imports==2.0.0 pytest==4.2.0 From 6fd932bf7dd0661a9a47add9048bf180ad6f9e53 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Tue, 5 Feb 2019 13:34:07 +0100 Subject: [PATCH 306/358] Update pytest-mock from 1.10.0 to 1.10.1 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index f6e3cd328..5f945f0a3 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -5,7 +5,7 @@ flake8==3.7.5 flake8-type-annotations==0.1.0 flake8-tidy-imports==2.0.0 pytest==4.2.0 -pytest-mock==1.10.0 +pytest-mock==1.10.1 pytest-asyncio==0.10.0 pytest-cov==2.6.1 coveralls==1.5.1 From 395a7b25be4b614d901e5b69d900dbe4446c74e3 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 6 Feb 2019 13:34:09 +0100 Subject: [PATCH 307/358] Update ccxt from 1.18.197 to 1.18.203 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 530271e9c..11f15012f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.197 +ccxt==1.18.203 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From 39626bb520b8940f38981bcbaaa66b82d4dfea20 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 7 Feb 2019 13:34:09 +0100 Subject: [PATCH 308/358] Update ccxt from 1.18.203 to 1.18.208 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 11f15012f..2ba7c1d0a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.203 +ccxt==1.18.208 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From 92eb951966ba87795988a79fee0c4eb8d31d8903 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Fri, 8 Feb 2019 13:34:26 +0100 Subject: [PATCH 309/358] Update ccxt from 1.18.208 to 1.18.210 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 2ba7c1d0a..012b4a3f5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.208 +ccxt==1.18.210 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From e9aba03981e6b3ba93dc2ff61c41e05d86fb2468 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 9 Feb 2019 13:33:06 +0100 Subject: [PATCH 310/358] Update ccxt from 1.18.210 to 1.18.221 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 012b4a3f5..439687560 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.210 +ccxt==1.18.221 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From e0142526e3111c82f9014ff5dfda09500adca906 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 9 Feb 2019 13:33:08 +0100 Subject: [PATCH 311/358] Update scipy from 1.2.0 to 1.2.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 439687560..208109318 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ numpy==1.16.1 pandas==0.24.1 scikit-learn==0.20.2 joblib==0.13.1 -scipy==1.2.0 +scipy==1.2.1 jsonschema==2.6.0 TA-Lib==0.4.17 tabulate==0.8.3 From f0c9064b77e79b920998dbecd96d87e85c966b93 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 9 Feb 2019 13:33:09 +0100 Subject: [PATCH 312/358] Update mypy from 0.660 to 0.670 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 5f945f0a3..768509903 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -9,4 +9,4 @@ pytest-mock==1.10.1 pytest-asyncio==0.10.0 pytest-cov==2.6.1 coveralls==1.5.1 -mypy==0.660 +mypy==0.670 From 3bb9e17b0d0a429926b5e1a31d5cd3f9845a5345 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 9 Feb 2019 13:33:11 +0100 Subject: [PATCH 313/358] Update plotly from 3.6.0 to 3.6.1 --- requirements-plot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-plot.txt b/requirements-plot.txt index d3f003fba..c01ea6a60 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,5 +1,5 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==3.6.0 +plotly==3.6.1 From fe2f98c80281530f3707007c6142ca51c3697e0b Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sun, 10 Feb 2019 13:33:06 +0100 Subject: [PATCH 314/358] Update ccxt from 1.18.221 to 1.18.223 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 208109318..323da61ad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.221 +ccxt==1.18.223 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From 585f525879c9355becf17ecf1b8cd4fe58003018 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sun, 10 Feb 2019 21:02:53 +0300 Subject: [PATCH 315/358] Cosmetic: fix logging --- freqtrade/strategy/interface.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index d38b4e0ba..bf05e1902 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -166,19 +166,19 @@ class IStrategy(ABC): if (not self.process_only_new_candles or self._last_candle_seen_per_pair.get(pair, None) != dataframe.iloc[-1]['date']): # Defs that only make change on new candle data. - logging.debug("TA Analysis Launched") + logger.debug("TA Analysis Launched") dataframe = self.advise_indicators(dataframe, metadata) dataframe = self.advise_buy(dataframe, metadata) dataframe = self.advise_sell(dataframe, metadata) self._last_candle_seen_per_pair[pair] = dataframe.iloc[-1]['date'] else: - logging.debug("Skippinig TA Analysis for already analyzed candle") + logger.debug("Skippinig TA Analysis for already analyzed candle") dataframe['buy'] = 0 dataframe['sell'] = 0 # Other Defs in strategy that want to be called every loop here # twitter_sell = self.watch_twitter_feed(dataframe, metadata) - logging.debug("Loop Analysis Launched") + logger.debug("Loop Analysis Launched") return dataframe From b91981f0aac4c4478bd3cdc2c111819889e595b9 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sun, 10 Feb 2019 21:31:13 +0300 Subject: [PATCH 316/358] Cosmetic: fix logging Output stake_amount as it is defined in the config (it may by int) instead of float. In order to avoid unnecessary questions where and why it was converted to float and changed in the last digit while it should be integer for the exchange... Other small cosmetic improvements to logging in freqtradebot.py --- freqtrade/freqtradebot.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index a13e92926..15c6578ab 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -267,9 +267,8 @@ class FreqtradeBot(object): # Check if stake_amount is fulfilled if avaliable_amount < stake_amount: raise DependencyException( - 'Available balance(%f %s) is lower than stake amount(%f %s)' % ( - avaliable_amount, self.config['stake_currency'], - stake_amount, self.config['stake_currency']) + f'Available balance({avaliable_amount} {self.config['stake_currency']}) is lower than ' + f'stake amount({stake_amount} {self.config['stake_currency']})' ) return stake_amount @@ -333,10 +332,7 @@ class FreqtradeBot(object): if not stake_amount: return False - logger.info( - 'Buy signal found: about create a new trade with stake_amount: %f ...', - stake_amount - ) + logger.info(f'Buy signal found: about create a new trade with stake_amount: {stake_amount} ...') bidstrat_check_depth_of_market = self.config.get('bid_strategy', {}).\ get('check_depth_of_market', {}) @@ -388,8 +384,8 @@ class FreqtradeBot(object): min_stake_amount = self._get_min_pair_stake_amount(pair_s, buy_limit_requested) if min_stake_amount is not None and min_stake_amount > stake_amount: logger.warning( - f'Can\'t open a new trade for {pair_s}: stake amount' - f' is too small ({stake_amount} < {min_stake_amount})' + f'Can\'t open a new trade for {pair_s}: stake amount ' + f'is too small ({stake_amount} < {min_stake_amount})' ) return False @@ -507,7 +503,7 @@ class FreqtradeBot(object): trade.fee_open = 0 except OperationalException as exception: - logger.warning("could not update trade amount: %s", exception) + logger.warning("Could not update trade amount: %s", exception) trade.update(order) @@ -566,12 +562,12 @@ class FreqtradeBot(object): fee_abs += exectrade['fee']['cost'] if amount != order_amount: - logger.warning(f"amount {amount} does not match amount {trade.amount}") + logger.warning(f"Amount {amount} does not match amount {trade.amount}") raise OperationalException("Half bought? Amounts don't match") real_amount = amount - fee_abs if fee_abs != 0: - logger.info(f"""Applying fee on amount for {trade} \ -(from {order_amount} to {real_amount}) from Trades""") + logger.info(f"Applying fee on amount for {trade} " + f"(from {order_amount} to {real_amount}) from Trades") return real_amount def handle_trade(self, trade: Trade) -> bool: @@ -580,7 +576,7 @@ class FreqtradeBot(object): :return: True if trade has been sold, False otherwise """ if not trade.is_open: - raise ValueError(f'attempt to handle closed trade: {trade}') + raise ValueError(f'Attempt to handle closed trade: {trade}') logger.debug('Handling %s ...', trade) sell_rate = self.exchange.get_ticker(trade.pair)['bid'] From 14d6cdf9b2197518a123c09c38a07a4b76e9cd27 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sun, 10 Feb 2019 21:52:33 +0300 Subject: [PATCH 317/358] OHLCV should be float for TA-LIB indicators in the strategy Some exchanges (BitMEX) return integer values for Volume field. And sometimes even for OHLC -- same, on BitMEX, since price decrease is 0.5. TA-LIB functions assume floats and fail with exception. Of course, this can be fixed (converted) in ccxt for particular exchange, but TA-LIB will still fail for exchanges for that such a conversion is not implemented in ccxt code. So let's make perform this conversion here in order to be sure our strategy will not crash on a new exchange. --- freqtrade/data/converter.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py index 62e9a68f6..6abb87925 100644 --- a/freqtrade/data/converter.py +++ b/freqtrade/data/converter.py @@ -29,6 +29,10 @@ def parse_ticker_dataframe(ticker: list, ticker_interval: str, utc=True, infer_datetime_format=True) + # Some exchanges return int values for volume and even for ohlc. + # Convert them since TA-LIB indicators used in the strategy assume floats and fail with exception... + frame = frame.astype(dtype={'open': 'float', 'high': 'float', 'low': 'float', 'close': 'float', 'volume': 'float'}) + # group by index and aggregate results to eliminate duplicate ticks frame = frame.groupby(by='date', as_index=False, sort=True).agg({ 'open': 'first', From 4dffb17dd6547e5fa97ebd4920fe5cf57dedc99f Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sun, 10 Feb 2019 22:01:46 +0300 Subject: [PATCH 318/358] fix flake --- freqtrade/data/converter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py index 6abb87925..231aaefca 100644 --- a/freqtrade/data/converter.py +++ b/freqtrade/data/converter.py @@ -31,7 +31,8 @@ def parse_ticker_dataframe(ticker: list, ticker_interval: str, # Some exchanges return int values for volume and even for ohlc. # Convert them since TA-LIB indicators used in the strategy assume floats and fail with exception... - frame = frame.astype(dtype={'open': 'float', 'high': 'float', 'low': 'float', 'close': 'float', 'volume': 'float'}) + frame = frame.astype(dtype={'open': 'float', 'high': 'float', 'low': 'float', 'close': 'float', + 'volume': 'float'}) # group by index and aggregate results to eliminate duplicate ticks frame = frame.groupby(by='date', as_index=False, sort=True).agg({ From 08d35f3e150e1e7096059d490c1b77ba4d91059a Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sun, 10 Feb 2019 22:09:11 +0300 Subject: [PATCH 319/358] fix checks Should not make cosmetic changes right in the github editor without local smoketests... --- freqtrade/freqtradebot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 15c6578ab..4535c22a7 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -267,8 +267,8 @@ class FreqtradeBot(object): # Check if stake_amount is fulfilled if avaliable_amount < stake_amount: raise DependencyException( - f'Available balance({avaliable_amount} {self.config['stake_currency']}) is lower than ' - f'stake amount({stake_amount} {self.config['stake_currency']})' + f"Available balance({avaliable_amount} {self.config['stake_currency']}) is lower than " + f"stake amount({stake_amount} {self.config['stake_currency']})" ) return stake_amount From 7ed15c64bae75e7e13076a995267ce9efe0284b9 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sun, 10 Feb 2019 22:13:40 +0300 Subject: [PATCH 320/358] what else? --- freqtrade/data/converter.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py index 231aaefca..2b8406ff1 100644 --- a/freqtrade/data/converter.py +++ b/freqtrade/data/converter.py @@ -30,8 +30,9 @@ def parse_ticker_dataframe(ticker: list, ticker_interval: str, infer_datetime_format=True) # Some exchanges return int values for volume and even for ohlc. - # Convert them since TA-LIB indicators used in the strategy assume floats and fail with exception... - frame = frame.astype(dtype={'open': 'float', 'high': 'float', 'low': 'float', 'close': 'float', + # Convert them since TA-LIB indicators used in the strategy assume floats + # and fail with exception... + frame = frame.astype(dtype={'open': 'float', 'high': 'float', 'low': 'float', 'close': 'float', 'volume': 'float'}) # group by index and aggregate results to eliminate duplicate ticks From d6c0c107ac7156b1063078649453616c881bc52e Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sun, 10 Feb 2019 22:23:00 +0300 Subject: [PATCH 321/358] fixed flake hmm, even in the comments? --- freqtrade/data/converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py index 2b8406ff1..c32338bbe 100644 --- a/freqtrade/data/converter.py +++ b/freqtrade/data/converter.py @@ -30,7 +30,7 @@ def parse_ticker_dataframe(ticker: list, ticker_interval: str, infer_datetime_format=True) # Some exchanges return int values for volume and even for ohlc. - # Convert them since TA-LIB indicators used in the strategy assume floats + # Convert them since TA-LIB indicators used in the strategy assume floats # and fail with exception... frame = frame.astype(dtype={'open': 'float', 'high': 'float', 'low': 'float', 'close': 'float', 'volume': 'float'}) From 5e741a0f734a126787ecf50f59a381e91a158bbd Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sun, 10 Feb 2019 22:28:40 +0300 Subject: [PATCH 322/358] fixed flake --- freqtrade/freqtradebot.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4535c22a7..5413c57f9 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -267,8 +267,8 @@ class FreqtradeBot(object): # Check if stake_amount is fulfilled if avaliable_amount < stake_amount: raise DependencyException( - f"Available balance({avaliable_amount} {self.config['stake_currency']}) is lower than " - f"stake amount({stake_amount} {self.config['stake_currency']})" + f"Available balance({avaliable_amount} {self.config['stake_currency']}) is " + f"lower than stake amount({stake_amount} {self.config['stake_currency']})" ) return stake_amount @@ -332,7 +332,8 @@ class FreqtradeBot(object): if not stake_amount: return False - logger.info(f'Buy signal found: about create a new trade with stake_amount: {stake_amount} ...') + logger.info(f"Buy signal found: about create a new trade with stake_amount: " + f"{stake_amount} ...") bidstrat_check_depth_of_market = self.config.get('bid_strategy', {}).\ get('check_depth_of_market', {}) From 69f69d965c828fbf511d466898d64e3a5655c745 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sun, 10 Feb 2019 23:37:24 +0300 Subject: [PATCH 323/358] test asserts aligned accordingly to new log messages --- freqtrade/tests/test_freqtradebot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 2edf063b5..1a0e1e217 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -691,7 +691,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order, assert trade.amount == 90.99181073703367 assert log_has( - 'Buy signal found: about create a new trade with stake_amount: 0.001000 ...', + 'Buy signal found: about create a new trade with stake_amount: 0.001 ...', caplog.record_tuples ) @@ -1318,7 +1318,7 @@ def test_process_maybe_execute_sell_exception(mocker, default_conf, side_effect=OperationalException() ) freqtrade.process_maybe_execute_sell(trade) - assert log_has('could not update trade amount: ', caplog.record_tuples) + assert log_has('Could not update trade amount: ', caplog.record_tuples) # Test raise of DependencyException exception mocker.patch( From 160c467e01b49eae81c162ed1e464bf84ed7c758 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Mon, 11 Feb 2019 13:31:07 +0100 Subject: [PATCH 324/358] Update ccxt from 1.18.223 to 1.18.225 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 323da61ad..736540984 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.223 +ccxt==1.18.225 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From 0b5f4dc38ea10513e286d337503fc1960e51fcb4 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Mon, 11 Feb 2019 13:31:09 +0100 Subject: [PATCH 325/358] Update python-rapidjson from 0.6.3 to 0.7.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 736540984..164e4dd7c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,4 +23,4 @@ scikit-optimize==0.5.2 py_find_1st==1.1.3 #Load ticker files 30% faster -python-rapidjson==0.6.3 +python-rapidjson==0.7.0 From 607190cd385ba183061c922f82bb1c39e0478117 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Tue, 12 Feb 2019 13:31:08 +0100 Subject: [PATCH 326/358] Update ccxt from 1.18.225 to 1.18.230 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 164e4dd7c..0621c5b04 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.225 +ccxt==1.18.230 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From e8ef36fb6ed8d984711531313487692eba9669c3 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Wed, 13 Feb 2019 02:55:55 +0300 Subject: [PATCH 327/358] execute_buy: do not use ticker if use_order_book:true is set in config This PR corresponds to: https://github.com/freqtrade/freqtrade/issues/1377#issue-386200394 in understanfing that pair Ticker is mostly statistics, but on the other side, create_trade/execute_buy. It resolves problem with some exchanges (BitMex) where ticker structure returned by ccxt does not contain bid and ask values. 1. On exchanges like Bitmex, set use_order_book: true for buys. FT won't request ticker and will use data from order book only. 2. On exchanges where order book is not available, set use_order_book: false, ticker data (including ask/last balance logic) will be used. 3. On other exchanges, either approach may be used in the config. Performance: current implementation fetches ticker every time even if order book data will be later used. With this change it's eliminated. Comparison of order book rate and ticker rate is removed (in order to split fetching order book and ticker completely in execute_buy), so some tests that touch this code may require adjustments. --- freqtrade/freqtradebot.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 5413c57f9..4671b6053 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -204,19 +204,11 @@ class FreqtradeBot(object): self.state = State.STOPPED return state_changed - def get_target_bid(self, pair: str, ticker: Dict[str, float]) -> float: + def get_target_bid(self, pair: str) -> float: """ Calculates bid target between current ask price and last price - :param ticker: Ticker to use for getting Ask and Last Price :return: float: Price """ - if ticker['ask'] < ticker['last']: - ticker_rate = ticker['ask'] - else: - balance = self.config['bid_strategy']['ask_last_balance'] - ticker_rate = ticker['ask'] + balance * (ticker['last'] - ticker['ask']) - - used_rate = ticker_rate config_bid_strategy = self.config.get('bid_strategy', {}) if 'use_order_book' in config_bid_strategy and\ config_bid_strategy.get('use_order_book', False): @@ -226,15 +218,16 @@ class FreqtradeBot(object): logger.debug('order_book %s', order_book) # top 1 = index 0 order_book_rate = order_book['bids'][order_book_top - 1][0] - # if ticker has lower rate, then use ticker ( usefull if down trending ) logger.info('...top %s order book buy rate %0.8f', order_book_top, order_book_rate) - if ticker_rate < order_book_rate: - logger.info('...using ticker rate instead %0.8f', ticker_rate) - used_rate = ticker_rate - else: - used_rate = order_book_rate + used_rate = order_book_rate else: logger.info('Using Last Ask / Last Price') + ticker = self.exchange.get_ticker(pair) + if ticker['ask'] < ticker['last']: + ticker_rate = ticker['ask'] + else: + balance = self.config['bid_strategy']['ask_last_balance'] + ticker_rate = ticker['ask'] + balance * (ticker['last'] - ticker['ask']) used_rate = ticker_rate return used_rate @@ -380,7 +373,7 @@ class FreqtradeBot(object): buy_limit_requested = price else: # Calculate amount - buy_limit_requested = self.get_target_bid(pair, self.exchange.get_ticker(pair)) + buy_limit_requested = self.get_target_bid(pair) min_stake_amount = self._get_min_pair_stake_amount(pair_s, buy_limit_requested) if min_stake_amount is not None and min_stake_amount > stake_amount: From f551fb5ff7c135b039047cbae970e002e6c709f3 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Wed, 13 Feb 2019 03:14:24 +0300 Subject: [PATCH 328/358] adjusted for passing tests Don't like this test_ticker parameter, but it's needed for tests to pass prepared ticker. Any ideas? --- freqtrade/freqtradebot.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4671b6053..0183b5dc6 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -204,9 +204,10 @@ class FreqtradeBot(object): self.state = State.STOPPED return state_changed - def get_target_bid(self, pair: str) -> float: + def get_target_bid(self, pair: str, test_ticker: Dict[str, float]) -> float: """ Calculates bid target between current ask price and last price + :param test_ticker: Ticker to use for getting Ask and Last Price; left for tests :return: float: Price """ config_bid_strategy = self.config.get('bid_strategy', {}) @@ -222,7 +223,10 @@ class FreqtradeBot(object): used_rate = order_book_rate else: logger.info('Using Last Ask / Last Price') - ticker = self.exchange.get_ticker(pair) + if test_ticker is not None: + ticker = test_ticker + else: + ticker = self.exchange.get_ticker(pair) if ticker['ask'] < ticker['last']: ticker_rate = ticker['ask'] else: From 91629807f7f64c00a9480d27c35f5087677473b0 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Wed, 13 Feb 2019 03:17:54 +0300 Subject: [PATCH 329/358] shame on me --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 0183b5dc6..1118deb9d 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -204,7 +204,7 @@ class FreqtradeBot(object): self.state = State.STOPPED return state_changed - def get_target_bid(self, pair: str, test_ticker: Dict[str, float]) -> float: + def get_target_bid(self, pair: str, test_ticker: Dict[str, float] = None) -> float: """ Calculates bid target between current ask price and last price :param test_ticker: Ticker to use for getting Ask and Last Price; left for tests From e8daadfb7e5d943bde8fa2208883211e2558e5b3 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Wed, 13 Feb 2019 03:54:57 +0300 Subject: [PATCH 330/358] same approach for the sell side (at handle_trade) --- freqtrade/freqtradebot.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 1118deb9d..e5c1bf8ba 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -577,7 +577,6 @@ class FreqtradeBot(object): raise ValueError(f'Attempt to handle closed trade: {trade}') logger.debug('Handling %s ...', trade) - sell_rate = self.exchange.get_ticker(trade.pair)['bid'] (buy, sell) = (False, False) experimental = self.config.get('experimental', {}) @@ -597,18 +596,15 @@ class FreqtradeBot(object): for i in range(order_book_min, order_book_max + 1): order_book_rate = order_book['asks'][i - 1][0] - - # if orderbook has higher rate (high profit), - # use orderbook, otherwise just use bids rate logger.info(' order book asks top %s: %0.8f', i, order_book_rate) - if sell_rate < order_book_rate: - sell_rate = order_book_rate + sell_rate = order_book_rate if self.check_sell(trade, sell_rate, buy, sell): return True else: logger.debug('checking sell') + sell_rate = self.exchange.get_ticker(trade.pair)['bid'] if self.check_sell(trade, sell_rate, buy, sell): return True From 69a24c12722a6f76e7ca401e7ec851da8ee3c6f7 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Wed, 13 Feb 2019 12:23:22 +0300 Subject: [PATCH 331/358] no need for test_ticker parameter just for making current tests happy, tests should be reimplemented --- freqtrade/freqtradebot.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index e5c1bf8ba..dc54fce78 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -204,10 +204,9 @@ class FreqtradeBot(object): self.state = State.STOPPED return state_changed - def get_target_bid(self, pair: str, test_ticker: Dict[str, float] = None) -> float: + def get_target_bid(self, pair: str) -> float: """ Calculates bid target between current ask price and last price - :param test_ticker: Ticker to use for getting Ask and Last Price; left for tests :return: float: Price """ config_bid_strategy = self.config.get('bid_strategy', {}) @@ -223,10 +222,7 @@ class FreqtradeBot(object): used_rate = order_book_rate else: logger.info('Using Last Ask / Last Price') - if test_ticker is not None: - ticker = test_ticker - else: - ticker = self.exchange.get_ticker(pair) + ticker = self.exchange.get_ticker(pair) if ticker['ask'] < ticker['last']: ticker_rate = ticker['ask'] else: From 8fc8c985d8676e6aa330d437fc483a573240e4cf Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Wed, 13 Feb 2019 12:38:44 +0300 Subject: [PATCH 332/358] logs: cosmetic changes "30 seconds" was errorneously hardcoded in the text of the log message, while actually it's RETRY_TIMEOUT which may differ --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 5413c57f9..9c211608f 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -191,7 +191,7 @@ class FreqtradeBot(object): Trade.session.flush() except TemporaryError as error: - logger.warning('%s, retrying in 30 seconds...', error) + logger.warning(f"Error: {error}, retrying in {constants.RETRY_TIMEOUT} seconds...") time.sleep(constants.RETRY_TIMEOUT) except OperationalException: tb = traceback.format_exc() From 8e7e6700030214338e4b826a16100f43583f7ab3 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Wed, 13 Feb 2019 12:42:39 +0300 Subject: [PATCH 333/358] Orthography in the log message --- freqtrade/strategy/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index bf05e1902..1d6147357 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -172,7 +172,7 @@ class IStrategy(ABC): dataframe = self.advise_sell(dataframe, metadata) self._last_candle_seen_per_pair[pair] = dataframe.iloc[-1]['date'] else: - logger.debug("Skippinig TA Analysis for already analyzed candle") + logger.debug("Skipping TA Analysis for already analyzed candle") dataframe['buy'] = 0 dataframe['sell'] = 0 From b87e15774b58f0206be1bca4ed6a902af73af61c Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Wed, 13 Feb 2019 13:02:57 +0300 Subject: [PATCH 334/358] test adjusted --- freqtrade/tests/strategy/test_interface.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/tests/strategy/test_interface.py b/freqtrade/tests/strategy/test_interface.py index f2e8c577f..d6ef0c8e7 100644 --- a/freqtrade/tests/strategy/test_interface.py +++ b/freqtrade/tests/strategy/test_interface.py @@ -204,7 +204,7 @@ def test_analyze_ticker_default(ticker_history, mocker, caplog) -> None: assert buy_mock.call_count == 1 assert log_has('TA Analysis Launched', caplog.record_tuples) - assert not log_has('Skippinig TA Analysis for already analyzed candle', + assert not log_has('Skipping TA Analysis for already analyzed candle', caplog.record_tuples) caplog.clear() @@ -214,7 +214,7 @@ def test_analyze_ticker_default(ticker_history, mocker, caplog) -> None: assert buy_mock.call_count == 2 assert buy_mock.call_count == 2 assert log_has('TA Analysis Launched', caplog.record_tuples) - assert not log_has('Skippinig TA Analysis for already analyzed candle', + assert not log_has('Skipping TA Analysis for already analyzed candle', caplog.record_tuples) @@ -242,7 +242,7 @@ def test_analyze_ticker_skip_analyze(ticker_history, mocker, caplog) -> None: assert buy_mock.call_count == 1 assert buy_mock.call_count == 1 assert log_has('TA Analysis Launched', caplog.record_tuples) - assert not log_has('Skippinig TA Analysis for already analyzed candle', + assert not log_has('Skipping TA Analysis for already analyzed candle', caplog.record_tuples) caplog.clear() @@ -257,5 +257,5 @@ def test_analyze_ticker_skip_analyze(ticker_history, mocker, caplog) -> None: assert ret['buy'].sum() == 0 assert ret['sell'].sum() == 0 assert not log_has('TA Analysis Launched', caplog.record_tuples) - assert log_has('Skippinig TA Analysis for already analyzed candle', + assert log_has('Skipping TA Analysis for already analyzed candle', caplog.record_tuples) From aaa85677085f99d2c9301a250f388d295674fafc Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 13 Feb 2019 13:32:06 +0100 Subject: [PATCH 335/358] Update ccxt from 1.18.230 to 1.18.234 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0621c5b04..1dd8290c2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.230 +ccxt==1.18.234 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.0 From c17eb89e8429b6a395c5db51addec088169686a0 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 13 Feb 2019 13:32:08 +0100 Subject: [PATCH 336/358] Update arrow from 0.13.0 to 0.13.1 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1dd8290c2..9d3bc6d0d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ ccxt==1.18.234 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 -arrow==0.13.0 +arrow==0.13.1 cachetools==3.1.0 requests==2.21.0 urllib3==1.24.1 From aee7b2c29d094b03fccd55249639768664938e2f Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 13 Feb 2019 13:32:10 +0100 Subject: [PATCH 337/358] Update pytest from 4.2.0 to 4.2.1 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 768509903..1bbf5cbe1 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,7 +4,7 @@ flake8==3.7.5 flake8-type-annotations==0.1.0 flake8-tidy-imports==2.0.0 -pytest==4.2.0 +pytest==4.2.1 pytest-mock==1.10.1 pytest-asyncio==0.10.0 pytest-cov==2.6.1 From f852be1a9be8c39666dc0195ced391b7098c256c Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 14 Feb 2019 07:27:13 +0100 Subject: [PATCH 338/358] Fix tests for get_ticker fix --- freqtrade/tests/test_freqtradebot.py | 50 +++++++++++----------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 1a0e1e217..9083255ba 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -841,22 +841,27 @@ def test_process_informative_pairs_added(default_conf, ticker, markets, mocker) def test_balance_fully_ask_side(mocker, default_conf) -> None: default_conf['bid_strategy']['ask_last_balance'] = 0.0 freqtrade = get_patched_freqtradebot(mocker, default_conf) + mocker.patch('freqtrade.exchange.Exchange.get_ticker', + MagicMock(return_value={'ask': 20, 'last': 10})) - assert freqtrade.get_target_bid('ETH/BTC', {'ask': 20, 'last': 10}) == 20 + assert freqtrade.get_target_bid('ETH/BTC') == 20 def test_balance_fully_last_side(mocker, default_conf) -> None: default_conf['bid_strategy']['ask_last_balance'] = 1.0 freqtrade = get_patched_freqtradebot(mocker, default_conf) + mocker.patch('freqtrade.exchange.Exchange.get_ticker', + MagicMock(return_value={'ask': 20, 'last': 10})) - assert freqtrade.get_target_bid('ETH/BTC', {'ask': 20, 'last': 10}) == 10 + assert freqtrade.get_target_bid('ETH/BTC') == 10 def test_balance_bigger_last_ask(mocker, default_conf) -> None: default_conf['bid_strategy']['ask_last_balance'] = 1.0 freqtrade = get_patched_freqtradebot(mocker, default_conf) - - assert freqtrade.get_target_bid('ETH/BTC', {'ask': 5, 'last': 10}) == 5 + mocker.patch('freqtrade.exchange.Exchange.get_ticker', + MagicMock(return_value={'ask': 5, 'last': 10})) + assert freqtrade.get_target_bid('ETH/BTC') == 5 def test_execute_buy(mocker, default_conf, fee, markets, limit_buy_order) -> None: @@ -2813,10 +2818,13 @@ def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2, markets) instead of the ask rate """ patch_exchange(mocker) + ticker_mock = MagicMock(return_value={'ask': 0.045, 'last': 0.046}) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_markets=markets, - get_order_book=order_book_l2 + get_order_book=order_book_l2, + get_ticker=ticker_mock, + ) default_conf['exchange']['name'] = 'binance' default_conf['bid_strategy']['use_order_book'] = True @@ -2825,7 +2833,8 @@ def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2, markets) default_conf['telegram']['enabled'] = False freqtrade = FreqtradeBot(default_conf) - assert freqtrade.get_target_bid('ETH/BTC', {'ask': 0.045, 'last': 0.046}) == 0.043935 + assert freqtrade.get_target_bid('ETH/BTC') == 0.043935 + assert ticker_mock.call_count == 0 def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2, markets) -> None: @@ -2834,10 +2843,13 @@ def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2, markets) instead of the order book rate (even if enabled) """ patch_exchange(mocker) + ticker_mock = MagicMock(return_value={'ask': 0.042, 'last': 0.046}) mocker.patch.multiple( 'freqtrade.exchange.Exchange', get_markets=markets, - get_order_book=order_book_l2 + get_order_book=order_book_l2, + get_ticker=ticker_mock, + ) default_conf['exchange']['name'] = 'binance' default_conf['bid_strategy']['use_order_book'] = True @@ -2846,29 +2858,7 @@ def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2, markets) default_conf['telegram']['enabled'] = False freqtrade = FreqtradeBot(default_conf) - assert freqtrade.get_target_bid('ETH/BTC', {'ask': 0.042, 'last': 0.046}) == 0.042 - - -def test_order_book_bid_strategy3(default_conf, mocker, order_book_l2, markets) -> None: - """ - test if function get_target_bid will return ask rate instead - of the order book rate - """ - patch_exchange(mocker) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - get_markets=markets, - get_order_book=order_book_l2 - ) - default_conf['exchange']['name'] = 'binance' - default_conf['bid_strategy']['use_order_book'] = True - default_conf['bid_strategy']['order_book_top'] = 1 - default_conf['bid_strategy']['ask_last_balance'] = 0 - default_conf['telegram']['enabled'] = False - - freqtrade = FreqtradeBot(default_conf) - - assert freqtrade.get_target_bid('ETH/BTC', {'ask': 0.03, 'last': 0.029}) == 0.03 + assert freqtrade.get_target_bid('ETH/BTC', ) == 0.042 def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2, markets) -> None: From 98bd7136244a1ef1a06ef15eec4e4117519ea33e Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 14 Feb 2019 19:15:16 +0100 Subject: [PATCH 339/358] iUpdate orderbook_bid_test --- freqtrade/tests/test_freqtradebot.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 9083255ba..a0ac6ee99 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -2858,7 +2858,9 @@ def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2, markets) default_conf['telegram']['enabled'] = False freqtrade = FreqtradeBot(default_conf) - assert freqtrade.get_target_bid('ETH/BTC', ) == 0.042 + # ordrebook shall be used even if tickers would be lower. + assert freqtrade.get_target_bid('ETH/BTC', ) != 0.042 + assert ticker_mock.call_count == 0 def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2, markets) -> None: From c2bc316e2fb4940e3944dca767ae9586993c8d7c Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 16 Feb 2019 13:32:04 +0100 Subject: [PATCH 340/358] Update ccxt from 1.18.234 to 1.18.243 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9d3bc6d0d..034d44055 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.234 +ccxt==1.18.243 SQLAlchemy==1.2.17 python-telegram-bot==11.1.0 arrow==0.13.1 From 13a16178d27d29ee5bc14ec86b16331fa25e2c3c Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 16 Feb 2019 13:32:05 +0100 Subject: [PATCH 341/358] Update sqlalchemy from 1.2.17 to 1.2.18 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 034d44055..4c7e3d486 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ ccxt==1.18.243 -SQLAlchemy==1.2.17 +SQLAlchemy==1.2.18 python-telegram-bot==11.1.0 arrow==0.13.1 cachetools==3.1.0 From 7c651632f1568efad20b80603949483d7cc4cc23 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 16 Feb 2019 13:32:06 +0100 Subject: [PATCH 342/358] Update joblib from 0.13.1 to 0.13.2 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4c7e3d486..c690b237d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ wrapt==1.11.1 numpy==1.16.1 pandas==0.24.1 scikit-learn==0.20.2 -joblib==0.13.1 +joblib==0.13.2 scipy==1.2.1 jsonschema==2.6.0 TA-Lib==0.4.17 From ea6d4a9d362e0ac733562e50185bd90bee6e4454 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sat, 16 Feb 2019 19:50:55 +0300 Subject: [PATCH 343/358] fetch amount_reserve_percent from config --- freqtrade/freqtradebot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4247a6612..3dea34df1 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -290,7 +290,8 @@ class FreqtradeBot(object): if not min_stake_amounts: return None - amount_reserve_percent = 1 - 0.05 # reserve 5% + stoploss + # reserve some percent defined in config (5% default) + stoploss + amount_reserve_percent = 1.0 - self.config.get('amount_reserve_percent', 0.05) if self.strategy.stoploss is not None: amount_reserve_percent += self.strategy.stoploss # it should not be more than 50% From 66cc600076fa2347a3eec3ca290a6e7b2a4a42f0 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sat, 16 Feb 2019 19:53:35 +0300 Subject: [PATCH 344/358] add amount_reserve_percent into the full config file --- config_full.json.example | 1 + 1 file changed, 1 insertion(+) diff --git a/config_full.json.example b/config_full.json.example index 234722f82..23a36dd4c 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -3,6 +3,7 @@ "stake_currency": "BTC", "stake_amount": 0.05, "fiat_display_currency": "USD", + "amount_reserve_percent" : 0.05, "dry_run": false, "ticker_interval": "5m", "trailing_stop": false, From df8067d6c4d479424051b0d4b9240e1562049449 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sun, 17 Feb 2019 12:56:28 +0300 Subject: [PATCH 345/358] add description of the new configuration option amount_reserve_percent --- docs/configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/configuration.md b/docs/configuration.md index 2caae81f1..108e264c6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -16,6 +16,7 @@ Mandatory Parameters are marked as **Required**. | `max_open_trades` | 3 | **Required.** Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades) | `stake_currency` | BTC | **Required.** Crypto-currency used for trading. | `stake_amount` | 0.05 | **Required.** Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to `"unlimited"` to allow the bot to use all available balance. +| `amount_reserve_percent` | 0.05 | Reserve some amount in min pair stake amount. Default is 5%. The bot will reserve `amount_reserve_percent` + stop-loss value when calculating min pair stake amount in order to avoid possible trade refusals. | `ticker_interval` | [1m, 5m, 30m, 1h, 1d] | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes. [Strategy Override](#parameters-in-strategy). | `fiat_display_currency` | USD | **Required.** Fiat currency used to show your profits. More information below. | `dry_run` | true | **Required.** Define if the bot must be in Dry-run or production mode. From 1bbb04da60ff1d616806b0f190dfb74cc5b27781 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sun, 17 Feb 2019 13:30:07 +0100 Subject: [PATCH 346/358] Update ccxt from 1.18.243 to 1.18.245 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c690b237d..be655167d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.243 +ccxt==1.18.245 SQLAlchemy==1.2.18 python-telegram-bot==11.1.0 arrow==0.13.1 From f1ededf0ebd14dd2d2de440fbc4f6e3232f7a57f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 17 Feb 2019 15:40:08 +0100 Subject: [PATCH 347/358] Fix typo in test-strategy, add volume > 0 check to buy/sell check --- user_data/strategies/test_strategy.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/user_data/strategies/test_strategy.py b/user_data/strategies/test_strategy.py index c4ea59e45..128f39486 100644 --- a/user_data/strategies/test_strategy.py +++ b/user_data/strategies/test_strategy.py @@ -55,7 +55,7 @@ class TestStrategy(IStrategy): # Experimental settings (configuration will overide these if set) use_sell_signal = False - use_profit_only = False + sell_profit_only = False ignore_roi_if_buy_signal = False # Optional order type mapping @@ -266,7 +266,8 @@ class TestStrategy(IStrategy): ( (dataframe['adx'] > 30) & (dataframe['tema'] <= dataframe['bb_middleband']) & - (dataframe['tema'] > dataframe['tema'].shift(1)) + (dataframe['tema'] > dataframe['tema'].shift(1)) & + (dataframe['volume'] > 1) # Make sure Volume is not 0 ), 'buy'] = 1 @@ -283,7 +284,8 @@ class TestStrategy(IStrategy): ( (dataframe['adx'] > 70) & (dataframe['tema'] > dataframe['bb_middleband']) & - (dataframe['tema'] < dataframe['tema'].shift(1)) + (dataframe['tema'] < dataframe['tema'].shift(1)) & + (dataframe['volume'] > 1) # Make sure Volume is not 0 ), 'sell'] = 1 return dataframe From 9fad83bd15a5ab6cd96a3d3f7bfb9f292fccce37 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 17 Feb 2019 15:55:47 +0100 Subject: [PATCH 348/358] Fix 1 to 0 --- user_data/strategies/test_strategy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_data/strategies/test_strategy.py b/user_data/strategies/test_strategy.py index 128f39486..3cb78842f 100644 --- a/user_data/strategies/test_strategy.py +++ b/user_data/strategies/test_strategy.py @@ -267,7 +267,7 @@ class TestStrategy(IStrategy): (dataframe['adx'] > 30) & (dataframe['tema'] <= dataframe['bb_middleband']) & (dataframe['tema'] > dataframe['tema'].shift(1)) & - (dataframe['volume'] > 1) # Make sure Volume is not 0 + (dataframe['volume'] > 0) # Make sure Volume is not 0 ), 'buy'] = 1 @@ -285,7 +285,7 @@ class TestStrategy(IStrategy): (dataframe['adx'] > 70) & (dataframe['tema'] > dataframe['bb_middleband']) & (dataframe['tema'] < dataframe['tema'].shift(1)) & - (dataframe['volume'] > 1) # Make sure Volume is not 0 + (dataframe['volume'] > 0) # Make sure Volume is not 0 ), 'sell'] = 1 return dataframe From f63fdf411d7178b05c7d35caad9376f60fb78f49 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Mon, 18 Feb 2019 13:30:10 +0100 Subject: [PATCH 349/358] Update ccxt from 1.18.245 to 1.18.247 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index be655167d..c9cedcb0c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.245 +ccxt==1.18.247 SQLAlchemy==1.2.18 python-telegram-bot==11.1.0 arrow==0.13.1 From dffb27326e8fd3dcfab49df3ae3f25de30e15500 Mon Sep 17 00:00:00 2001 From: gautier pialat Date: Mon, 18 Feb 2019 17:16:27 +0100 Subject: [PATCH 350/358] prevent convert LF->CRLF line ending on window During docker built on windows if in global git config core.autocrlf = true then when have this message : Step 6/12 : RUN cd /tmp && /tmp/install_ta-lib.sh && rm -r /tmp/*ta-lib* ---> Running in c0a626821132 /tmp/install_ta-lib.sh: 4: /tmp/install_ta-lib.sh: Syntax error: "&&" unexpected this behave is because file is in the wrong line ending format Has script files run on linux they need to use LF lines ending so we need to ensure they are not converted to CRLF lines ending if global git config is at core.autocrlf = true. --- docs/installation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/installation.md b/docs/installation.md index e5f514eb1..08592880f 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -115,10 +115,16 @@ Once you have Docker installed, simply create the config file (e.g. `config.json **1.1. Clone the git repository** +Linux ```bash git clone https://github.com/freqtrade/freqtrade.git ``` +Windows (docker or WSL) +```bash +git clone --config core.autocrlf=input https://github.com/freqtrade/freqtrade.git +``` + **1.2. (Optional) Checkout the develop branch** ```bash From f9d68d919c47693bce824464bdf475d4db861d44 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Tue, 19 Feb 2019 11:45:19 +0300 Subject: [PATCH 351/358] move default amount_reserve_percent value into constants --- freqtrade/constants.py | 1 + freqtrade/freqtradebot.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 8fbcdfed7..6c71ddf7b 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -13,6 +13,7 @@ DEFAULT_HYPEROPT = 'DefaultHyperOpts' DEFAULT_DB_PROD_URL = 'sqlite:///tradesv3.sqlite' DEFAULT_DB_DRYRUN_URL = 'sqlite://' UNLIMITED_STAKE_AMOUNT = 'unlimited' +DEFAULT_AMOUNT_RESERVE_PERCENT = 0.05 REQUIRED_ORDERTIF = ['buy', 'sell'] REQUIRED_ORDERTYPES = ['buy', 'sell', 'stoploss', 'stoploss_on_exchange'] ORDERTYPE_POSSIBILITIES = ['limit', 'market'] diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 3dea34df1..f67be724c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -291,7 +291,8 @@ class FreqtradeBot(object): return None # reserve some percent defined in config (5% default) + stoploss - amount_reserve_percent = 1.0 - self.config.get('amount_reserve_percent', 0.05) + amount_reserve_percent = 1.0 - self.config.get('amount_reserve_percent', + constants.DEFAULT_AMOUNT_RESERVE_PERCENT) if self.strategy.stoploss is not None: amount_reserve_percent += self.strategy.stoploss # it should not be more than 50% From 58864adc4a51b49bd2818955c6ece65e00b16d5c Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 19 Feb 2019 19:54:53 +0100 Subject: [PATCH 352/358] Move coveralls to after_success --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3dfcf6111..84f3c78d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,6 @@ jobs: - stage: tests script: - pytest --cov=freqtrade --cov-config=.coveragerc freqtrade/tests/ - - coveralls name: pytest - script: - cp config.json.example config.json @@ -48,6 +47,8 @@ jobs: - build_helpers/publish_docker.sh name: "Build and test and push docker image" +after_success: + - coveralls notifications: slack: From bd6644a91a5a049bc0a31cebb70eeb5c8d940801 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 20 Feb 2019 13:31:05 +0100 Subject: [PATCH 353/358] Update ccxt from 1.18.247 to 1.18.270 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c9cedcb0c..b4dd302e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ccxt==1.18.247 +ccxt==1.18.270 SQLAlchemy==1.2.18 python-telegram-bot==11.1.0 arrow==0.13.1 From 1cd54829cc22c54d8344b2585016a476da21cf22 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 20 Feb 2019 13:31:06 +0100 Subject: [PATCH 354/358] Update flake8 from 3.7.5 to 3.7.6 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 1bbf5cbe1..9fbc99d16 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,7 @@ # Include all requirements to run the bot. -r requirements.txt -flake8==3.7.5 +flake8==3.7.6 flake8-type-annotations==0.1.0 flake8-tidy-imports==2.0.0 pytest==4.2.1 From 3ec3438acfe1a5e1e9e63dcec098b0cb3e18efb0 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 20 Feb 2019 13:31:07 +0100 Subject: [PATCH 355/358] Update pytest from 4.2.1 to 4.3.0 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 9fbc99d16..9aa755827 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,7 +4,7 @@ flake8==3.7.6 flake8-type-annotations==0.1.0 flake8-tidy-imports==2.0.0 -pytest==4.2.1 +pytest==4.3.0 pytest-mock==1.10.1 pytest-asyncio==0.10.0 pytest-cov==2.6.1 From 7dbb7a52edaebd651ec43127c466f449f4a7ab55 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Wed, 20 Feb 2019 13:31:08 +0100 Subject: [PATCH 356/358] Update coveralls from 1.5.1 to 1.6.0 --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 9aa755827..34d59d802 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,5 +8,5 @@ pytest==4.3.0 pytest-mock==1.10.1 pytest-asyncio==0.10.0 pytest-cov==2.6.1 -coveralls==1.5.1 +coveralls==1.6.0 mypy==0.670 From ba23f58ff30aba7c1f5b5919bf24b45493b1fe5b Mon Sep 17 00:00:00 2001 From: gautier pialat Date: Wed, 20 Feb 2019 18:09:26 +0100 Subject: [PATCH 357/358] change the way to clone git rep for Window with docker --- docs/installation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index 08592880f..80223f954 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -115,12 +115,12 @@ Once you have Docker installed, simply create the config file (e.g. `config.json **1.1. Clone the git repository** -Linux +Linux/Mac/Windows with WSL ```bash git clone https://github.com/freqtrade/freqtrade.git ``` -Windows (docker or WSL) +Windows with docker ```bash git clone --config core.autocrlf=input https://github.com/freqtrade/freqtrade.git ``` From af02e34b5720349858a98230855690089371565a Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 20 Feb 2019 21:04:52 +0100 Subject: [PATCH 358/358] Version bump to 0.18.1 --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index ca148f518..0aa01211d 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ FreqTrade bot """ -__version__ = '0.18.1-dev' +__version__ = '0.18.1' class DependencyException(BaseException):