From ba4890d303348f29af540bfb9dff05d6f9f52f65 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 17 Jun 2019 14:34:45 +0200 Subject: [PATCH 01/11] Fix tests on windows --- freqtrade/tests/conftest.py | 3 ++- freqtrade/tests/strategy/test_strategy.py | 8 ++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 034cb5f8b..7424d5ece 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -5,6 +5,7 @@ import re from copy import deepcopy from datetime import datetime from functools import reduce +from pathlib import Path from typing import List from unittest.mock import MagicMock, PropertyMock @@ -860,7 +861,7 @@ def tickers(): @pytest.fixture def result(): - with open('freqtrade/tests/testdata/UNITTEST_BTC-1m.json') as data_file: + with Path('freqtrade/tests/testdata/UNITTEST_BTC-1m.json').open('r') as data_file: return parse_ticker_dataframe(json.load(data_file), '1m', fill_missing=True) # FIX: diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index b96f9c79e..15d1c18ef 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -75,14 +75,10 @@ def test_load_strategy_byte64(result): def test_load_strategy_invalid_directory(result, caplog): resolver = StrategyResolver() - extra_dir = path.join('some', 'path') + extra_dir = Path.cwd() / 'some/path' resolver._load_strategy('TestStrategy', config={}, extra_dir=extra_dir) - assert ( - 'freqtrade.resolvers.strategy_resolver', - logging.WARNING, - 'Path "{}" does not exist'.format(extra_dir), - ) in caplog.record_tuples + assert log_has_re(r'Path .*' + r'some.*path.*' + r'.* does not exist', caplog.record_tuples) assert 'adx' in resolver.strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) From 911e71cd9b5c87c0c04930c8324966296ec06ad0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 20 Jun 2019 20:30:05 +0200 Subject: [PATCH 02/11] remove redundant test-functions --- freqtrade/tests/conftest.py | 12 ++++++++ freqtrade/tests/test_freqtradebot.py | 41 +++------------------------- 2 files changed, 16 insertions(+), 37 deletions(-) diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 034cb5f8b..c857d33e3 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -110,11 +110,23 @@ def patch_freqtradebot(mocker, config) -> None: def get_patched_freqtradebot(mocker, config) -> FreqtradeBot: + """ + This function patches _init_modules() to not call dependencies + :param mocker: a Mocker object to apply patches + :param config: Config to pass to the bot + :return: FreqtradeBot + """ patch_freqtradebot(mocker, config) return FreqtradeBot(config) def get_patched_worker(mocker, config) -> Worker: + """ + This function patches _init_modules() to not call dependencies + :param mocker: a Mocker object to apply patches + :param config: Config to pass to the bot + :return: Worker + """ patch_freqtradebot(mocker, config) return Worker(args=None, config=config) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 65225689b..31c4a791a 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -19,47 +19,14 @@ from freqtrade.persistence import Trade from freqtrade.rpc import RPCMessageType from freqtrade.state import State from freqtrade.strategy.interface import SellCheckTuple, SellType -from freqtrade.tests.conftest import (log_has, log_has_re, patch_edge, - patch_exchange, patch_get_signal, +from freqtrade.tests.conftest import (get_patched_freqtradebot, + get_patched_worker, log_has, log_has_re, + patch_edge, patch_exchange, + patch_freqtradebot, patch_get_signal, patch_wallet) from freqtrade.worker import Worker -# Functions for recurrent object patching -def patch_freqtradebot(mocker, config) -> None: - """ - This function patches _init_modules() to not call dependencies - :param mocker: a Mocker object to apply patches - :param config: Config to pass to the bot - :return: None - """ - mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) - mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock()) - patch_exchange(mocker) - - -def get_patched_freqtradebot(mocker, config) -> FreqtradeBot: - """ - This function patches _init_modules() to not call dependencies - :param mocker: a Mocker object to apply patches - :param config: Config to pass to the bot - :return: FreqtradeBot - """ - patch_freqtradebot(mocker, config) - return FreqtradeBot(config) - - -def get_patched_worker(mocker, config) -> Worker: - """ - This function patches _init_modules() to not call dependencies - :param mocker: a Mocker object to apply patches - :param config: Config to pass to the bot - :return: Worker - """ - patch_freqtradebot(mocker, config) - return Worker(args=None, config=config) - - def patch_RPCManager(mocker) -> MagicMock: """ This function mock RPC manager to avoid repeating this code in almost every tests From dd379c4192771dd7d56a80be162862b911906986 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 20 Jun 2019 20:32:46 +0200 Subject: [PATCH 03/11] Cancelling stoploss order should not kill the bot --- freqtrade/freqtradebot.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 305a97ba6..0c6ebdeff 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -843,7 +843,10 @@ class FreqtradeBot(object): # First cancelling stoploss on exchange ... if self.strategy.order_types.get('stoploss_on_exchange') and trade.stoploss_order_id: - self.exchange.cancel_order(trade.stoploss_order_id, trade.pair) + try: + self.exchange.cancel_order(trade.stoploss_order_id, trade.pair) + except InvalidOrderException: + logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}") # Execute sell and update trade record order_id = self.exchange.sell(pair=str(trade.pair), From a8dcfc05c58d7d74a20f79122db4b079f1629e9c Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 20 Jun 2019 20:36:39 +0200 Subject: [PATCH 04/11] Add test to verify InvalidOrder is handled correctly --- freqtrade/tests/test_freqtradebot.py | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 31c4a791a..5229eb2c6 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -2075,6 +2075,35 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe } == last_msg +def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, markets, caplog) -> None: + freqtrade = get_patched_freqtradebot(mocker, default_conf) + mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException()) + sellmock = MagicMock() + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + _load_markets=MagicMock(return_value={}), + get_ticker=ticker, + get_fee=fee, + markets=PropertyMock(return_value=markets), + sell=sellmock + ) + + freqtrade.strategy.order_types['stoploss_on_exchange'] = True + patch_get_signal(freqtrade) + freqtrade.create_trade() + + trade = Trade.query.first() + Trade.session = MagicMock() + + freqtrade.config['dry_run'] = False + trade.stoploss_order_id = "abcd" + + freqtrade.execute_sell(trade=trade, limit=1234, + sell_reason=SellType.STOP_LOSS) + assert sellmock.call_count == 1 + assert log_has('Could not cancel stoploss order abcd', caplog.record_tuples) + + def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticker_sell_up, markets, mocker) -> None: From 63640518da73fa8722dc70d5b7f73c3db5e69305 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 20 Jun 2019 20:56:58 +0200 Subject: [PATCH 05/11] Gracefully handle errosr when cancelling stoploss orders fixes #1933 --- freqtrade/freqtradebot.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 0c6ebdeff..c601465d9 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -691,13 +691,22 @@ class FreqtradeBot(object): # cancelling the current stoploss on exchange first logger.info('Trailing stoploss: cancelling current stoploss on exchange (id:{%s})' 'in order to add another one ...', order['id']) - if self.exchange.cancel_order(order['id'], trade.pair): + try: + self.exchange.cancel_order(order['id'], trade.pair) + except InvalidOrderException: + logger.exception(f"Could not cancel stoploss order {order['id']} " + f"for pair {trade.pair}") + + try: # 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) + except DependencyException: + logger.exception(f"Could create trailing stoploss order " + f"for pair {trade.pair}.") def check_sell(self, trade: Trade, sell_rate: float, buy: bool, sell: bool) -> bool: if self.edge: From 89ba649ddb7e053fae0350e98689d43f9ade004e Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 20 Jun 2019 20:57:15 +0200 Subject: [PATCH 06/11] Test handling errors while trailing stop loss --- freqtrade/tests/test_freqtradebot.py | 77 ++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 5229eb2c6..87b344853 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -22,8 +22,7 @@ from freqtrade.strategy.interface import SellCheckTuple, SellType from freqtrade.tests.conftest import (get_patched_freqtradebot, get_patched_worker, log_has, log_has_re, patch_edge, patch_exchange, - patch_freqtradebot, patch_get_signal, - patch_wallet) + patch_get_signal, patch_wallet) from freqtrade.worker import Worker @@ -1143,6 +1142,77 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, stop_price=0.00002344 * 0.95) +def test_handle_stoploss_on_exchange_trailing_error(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_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, + markets=PropertyMock(return_value=markets), + stoploss_limit=stoploss_limit + ) + + # enabling TSL + default_conf['trailing_stop'] = True + + freqtrade = get_patched_freqtradebot(mocker, default_conf) + # enabling stoploss on exchange + freqtrade.strategy.order_types['stoploss_on_exchange'] = True + + # setting stoploss + freqtrade.strategy.stoploss = -0.05 + + # setting stoploss_on_exchange_interval to 60 seconds + freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60 + patch_get_signal(freqtrade) + freqtrade.create_trade() + trade = Trade.query.first() + trade.is_open = True + trade.open_order_id = None + trade.stoploss_order_id = "abcd" + trade.stop_loss = 0.2 + trade.stoploss_last_update = arrow.utcnow().shift(minutes=-601).datetime.replace(tzinfo=None) + + stoploss_order_hanging = { + 'id': "abcd", + 'status': 'open', + 'type': 'stop_loss_limit', + 'price': 3, + 'average': 2, + 'info': { + 'stopPrice': '0.1' + } + } + mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException()) + mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_order_hanging) + freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging) + assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/BTC.*", + caplog.record_tuples) + + # Still try to create order + assert stoploss_limit.call_count == 1 + + # Fail creating stoploss order + caplog.clear() + cancel_mock = mocker.patch("freqtrade.exchange.Exchange.cancel_order", MagicMock()) + mocker.patch("freqtrade.exchange.Exchange.stoploss_limit", side_effect=DependencyException()) + freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging) + assert cancel_mock.call_count == 1 + assert log_has_re(r"Could create trailing stoploss order for pair ETH/BTC\..*", + caplog.record_tuples) + + def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, markets, limit_buy_order, limit_sell_order) -> None: @@ -2075,7 +2145,8 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe } == last_msg -def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, markets, caplog) -> None: +def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, + markets, caplog) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException()) sellmock = MagicMock() From 026784efac660d2a891ef1e1ee99dfa17b861a16 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 22 Jun 2019 16:45:38 +0200 Subject: [PATCH 07/11] remove get_tickers_data from plot_dataframe --- scripts/plot_dataframe.py | 48 +++++++++------------------------------ 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index 4aacc99dd..23e70a987 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -31,7 +31,7 @@ from typing import Any, Dict, List import pandas as pd -from freqtrade.arguments import Arguments, TimeRange +from freqtrade.arguments import Arguments from freqtrade.data import history from freqtrade.data.btanalysis import load_trades, extract_trades_of_period from freqtrade.optimize import setup_configuration @@ -43,38 +43,6 @@ from freqtrade.state import RunMode logger = logging.getLogger(__name__) -def get_tickers_data(strategy, exchange, pairs: List[str], timerange: TimeRange, - datadir: Path, refresh_pairs: bool, live: bool): - """ - Get tickers data for each pairs on live or local, option defined in args - :return: dictionary of tickers. output format: {'pair': tickersdata} - """ - - ticker_interval = strategy.ticker_interval - - tickers = history.load_data( - datadir=datadir, - pairs=pairs, - ticker_interval=ticker_interval, - refresh_pairs=refresh_pairs, - timerange=timerange, - exchange=exchange, - live=live, - ) - - # 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 data 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 @@ -113,10 +81,16 @@ def analyse_and_plot_pairs(config: Dict[str, Any]): timerange = Arguments.parse_timerange(config["timerange"]) ticker_interval = strategy.ticker_interval - tickers = get_tickers_data(strategy, exchange, pairs, timerange, - datadir=Path(str(config.get("datadir"))), - refresh_pairs=config.get('refresh_pairs', False), - live=config.get("live", False)) + tickers = history.load_data( + datadir=Path(str(config.get("datadir"))), + pairs=pairs, + ticker_interval=config['ticker_interval'], + refresh_pairs=config.get('refresh_pairs', False), + timerange=timerange, + exchange=exchange, + live=config.get("live", False), + ) + pair_counter = 0 for pair, data in tickers.items(): pair_counter += 1 From 4cbcb5f36f365bab31d6a16e12f2ceeb885201d2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 22 Jun 2019 16:52:14 +0200 Subject: [PATCH 08/11] Move .title to ExchangeResolver (it does not make sense to do this over and over again) --- freqtrade/freqtradebot.py | 3 +-- freqtrade/optimize/backtesting.py | 3 +-- freqtrade/resolvers/exchange_resolver.py | 1 + freqtrade/tests/conftest.py | 2 +- freqtrade/tests/exchange/test_exchange.py | 4 ++-- scripts/plot_dataframe.py | 3 +-- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 305a97ba6..480bb680f 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -53,8 +53,7 @@ class FreqtradeBot(object): self.rpc: RPCManager = RPCManager(self) - exchange_name = self.config.get('exchange', {}).get('name').title() - self.exchange = ExchangeResolver(exchange_name, self.config).exchange + self.exchange = ExchangeResolver(self.config['exchange']['name'], self.config).exchange self.wallets = Wallets(self.config, self.exchange) self.dataprovider = DataProvider(self.config, self.exchange) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 6cc78ad2b..8bdf66f92 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -63,8 +63,7 @@ class Backtesting(object): self.config['dry_run'] = True self.strategylist: List[IStrategy] = [] - exchange_name = self.config.get('exchange', {}).get('name').title() - self.exchange = ExchangeResolver(exchange_name, self.config).exchange + self.exchange = ExchangeResolver(self.config['exchange']['name'], self.config).exchange self.fee = self.exchange.get_fee() if self.config.get('runmode') != RunMode.HYPEROPT: diff --git a/freqtrade/resolvers/exchange_resolver.py b/freqtrade/resolvers/exchange_resolver.py index 8d1845c71..25a86dd0e 100644 --- a/freqtrade/resolvers/exchange_resolver.py +++ b/freqtrade/resolvers/exchange_resolver.py @@ -22,6 +22,7 @@ class ExchangeResolver(IResolver): Load the custom class from config parameter :param config: configuration dictionary """ + exchange_name = exchange_name.title() try: self.exchange = self._load_exchange(exchange_name, kwargs={'config': config}) except ImportError: diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index e956d89c4..3523b44c4 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -60,7 +60,7 @@ def get_patched_exchange(mocker, config, api_mock=None, id='bittrex') -> Exchang patch_exchange(mocker, api_mock, id) config["exchange"]["name"] = id try: - exchange = ExchangeResolver(id.title(), config).exchange + exchange = ExchangeResolver(id, config).exchange except ImportError: exchange = Exchange(config) return exchange diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index f0dc96626..48a8538a9 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -124,14 +124,14 @@ def test_exchange_resolver(default_conf, mocker, caplog): caplog.record_tuples) caplog.clear() - exchange = ExchangeResolver('Kraken', default_conf).exchange + exchange = ExchangeResolver('kraken', default_conf).exchange assert isinstance(exchange, Exchange) assert isinstance(exchange, Kraken) assert not isinstance(exchange, Binance) assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.", caplog.record_tuples) - exchange = ExchangeResolver('Binance', default_conf).exchange + exchange = ExchangeResolver('binance', default_conf).exchange assert isinstance(exchange, Exchange) assert isinstance(exchange, Binance) assert not isinstance(exchange, Kraken) diff --git a/scripts/plot_dataframe.py b/scripts/plot_dataframe.py index 23e70a987..3792233de 100755 --- a/scripts/plot_dataframe.py +++ b/scripts/plot_dataframe.py @@ -68,8 +68,7 @@ def analyse_and_plot_pairs(config: Dict[str, Any]): -Generate plot files :return: None """ - exchange_name = config.get('exchange', {}).get('name').title() - exchange = ExchangeResolver(exchange_name, config).exchange + exchange = ExchangeResolver(config.get('exchange', {}).get('name'), config).exchange strategy = StrategyResolver(config).strategy if "pairs" in config: From 451d4a400e3b92236cb40eee10463691b7eff77e Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 22 Jun 2019 23:51:29 +0300 Subject: [PATCH 09/11] fix help strings shown to the user --- freqtrade/arguments.py | 39 ++++++++++++++++++++------------------- freqtrade/constants.py | 2 ++ 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py index 3075dd0fe..aa1a3e6da 100644 --- a/freqtrade/arguments.py +++ b/freqtrade/arguments.py @@ -87,9 +87,9 @@ class Arguments(object): ) parser.add_argument( '-c', '--config', - help="Specify configuration file (default: %(default)s). " - "Multiple --config options may be used. " - "Can be set to '-' to read config from stdin.", + help=f'Specify configuration file (default: {constants.DEFAULT_CONFIG}). ' + f'Multiple --config options may be used. ' + f'Can be set to `-` to read config from stdin.', dest='config', action='append', metavar='PATH', @@ -122,9 +122,9 @@ class Arguments(object): ) parser.add_argument( '--dynamic-whitelist', - help='Dynamically generate and update whitelist' - ' based on 24h BaseVolume (default: %(const)s).' - ' DEPRECATED.', + help='Dynamically generate and update whitelist ' + 'based on 24h BaseVolume (default: %(const)s). ' + 'DEPRECATED.', dest='dynamic_whitelist', const=constants.DYNAMIC_WHITELIST, type=int, @@ -133,8 +133,8 @@ class Arguments(object): ) parser.add_argument( '--db-url', - help='Override trades database URL, this is useful if dry_run is enabled' - ' or in custom deployments (default: %(default)s).', + help=f'Override trades database URL, this is useful if dry_run is enabled ' + f'or in custom deployments (default: {constants.DEFAULT_DB_DRYRUN_URL}.', dest='db_url', metavar='PATH', ) @@ -228,10 +228,10 @@ class Arguments(object): ) parser.add_argument( '--export-filename', - help='Save backtest results to this filename \ - requires --export to be set as well\ - Example --export-filename=user_data/backtest_data/backtest_today.json\ - (default: %(default)s)', + help='Save backtest results to this filename ' + 'requires --export to be set as well. ' + 'Example --export-filename=user_data/backtest_data/backtest_today.json ' + '(default: %(default)s)', default=os.path.join('user_data', 'backtest_data', 'backtest-result.json'), dest='exportfilename', metavar='PATH', @@ -246,8 +246,8 @@ class Arguments(object): parser.add_argument( '--stoplosses', help='Defines a range of stoploss against which edge will assess the strategy ' - 'the format is "min,max,step" (without any space).' - 'example: --stoplosses=-0.01,-0.1,-0.001', + 'the format is "min,max,step" (without any space). ' + 'Example: --stoplosses=-0.01,-0.1,-0.001', dest='stoploss_range', ) @@ -289,8 +289,8 @@ class Arguments(object): ) parser.add_argument( '-s', '--spaces', - help='Specify which parameters to hyperopt. Space separate list. \ - Default: %(default)s.', + help='Specify which parameters to hyperopt. Space separate list. ' + 'Default: %(default)s.', choices=['all', 'buy', 'sell', 'roi', 'stoploss'], default='all', nargs='+', @@ -467,13 +467,14 @@ class Arguments(object): ) parser.add_argument( '--exchange', - help='Exchange name (default: %(default)s). Only valid if no config is provided.', + help=f'Exchange name (default: {constants.DEFAULT_EXCHANGE}). ' + f'Only valid if no config is provided.', dest='exchange', ) parser.add_argument( '-t', '--timeframes', - help='Specify which tickers to download. Space separated list. \ - Default: %(default)s.', + help=f'Specify which tickers to download. Space separated list. ' + f'Default: {constants.DEFAULT_DOWNLOAD_TICKER_INTERVALS}.', choices=['1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', '1d', '3d', '1w'], nargs='+', diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 4772952fc..7a487fcc7 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -4,6 +4,7 @@ bot constants """ DEFAULT_CONFIG = 'config.json' +DEFAULT_EXCHANGE = 'bittrex' DYNAMIC_WHITELIST = 20 # pairs PROCESS_THROTTLE_SECS = 5 # sec DEFAULT_TICKER_INTERVAL = 5 # min @@ -21,6 +22,7 @@ ORDERTYPE_POSSIBILITIES = ['limit', 'market'] ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc'] AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList'] DRY_RUN_WALLET = 999.9 +DEFAULT_DOWNLOAD_TICKER_INTERVALS = '1m 5m' TICKER_INTERVALS = [ '1m', '3m', '5m', '15m', '30m', From 5b84cb39ac0ec5589cef99ccc8cea4b66edbaa54 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Sun, 23 Jun 2019 22:51:33 +0300 Subject: [PATCH 10/11] typo fixed --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index d215dc8d6..f0c536ade 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -6,7 +6,7 @@ This page explains how to prepare your environment for running the bot. Before running your bot in production you will need to setup few external API. In production mode, the bot will require valid Exchange API -credentials. We also reccomend a [Telegram bot](telegram-usage.md#setup-your-telegram-bot) (optional but recommended). +credentials. We also recommend a [Telegram bot](telegram-usage.md#setup-your-telegram-bot) (optional but recommended). - [Setup your exchange account](#setup-your-exchange-account) From 116d8e853e40783a6f23a5ba576fc5f433d5a0ad Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sun, 23 Jun 2019 23:10:37 +0300 Subject: [PATCH 11/11] typos in docstrings fixed --- freqtrade/data/btanalysis.py | 4 ++-- freqtrade/data/history.py | 2 +- freqtrade/strategy/interface.py | 4 ++-- scripts/rest_client.py | 32 ++++++++++++++++---------------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index 4aecf8ecf..f78ca3fa8 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -23,7 +23,7 @@ def load_backtest_data(filename) -> pd.DataFrame: """ Load backtest data file. :param filename: pathlib.Path object, or string pointing to the file. - :return a dataframe with the analysis results + :return: a dataframe with the analysis results """ if isinstance(filename, str): filename = Path(filename) @@ -78,7 +78,7 @@ def load_trades(db_url: str = None, exportfilename: str = None) -> pd.DataFrame: Load trades, either from a DB (using dburl) or via a backtest export file. :param db_url: Sqlite url (default format sqlite:///tradesv3.dry-run.sqlite) :param exportfilename: Path to a file exported from backtesting - :returns: Dataframe containing Trades + :return: Dataframe containing Trades """ timeZone = pytz.UTC diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index 67f942119..e9694b90f 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -63,7 +63,7 @@ def load_tickerdata_file( timerange: Optional[TimeRange] = None) -> Optional[list]: """ Load a pair from file, either .json.gz or .json - :return tickerlist or None if unsuccesful + :return: tickerlist or None if unsuccesful """ filename = pair_data_filename(datadir, pair, ticker_interval) pairdata = misc.file_load_json(filename) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 68e0a7b37..949a88b91 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -158,7 +158,7 @@ class IStrategy(ABC): """ Parses the given ticker history and returns a populated DataFrame add several TA indicators and buy signal to it - :return DataFrame with ticker data and indicator data + :return: DataFrame with ticker data and indicator data """ pair = str(metadata.get('pair')) @@ -351,7 +351,7 @@ class IStrategy(ABC): """ Based an earlier trade and current price and ROI configuration, decides whether bot should sell. Requires current_profit to be in percent!! - :return True if bot should sell at current rate + :return: True if bot should sell at current rate """ # Check if time matches and current rate is above threshold diff --git a/scripts/rest_client.py b/scripts/rest_client.py index 2261fba0b..a46b3ebfb 100755 --- a/scripts/rest_client.py +++ b/scripts/rest_client.py @@ -65,14 +65,14 @@ class FtRestClient(): def start(self): """ Start the bot if it's in stopped state. - :returns: json object + :return: json object """ return self._post("start") def stop(self): """ Stop the bot. Use start to restart - :returns: json object + :return: json object """ return self._post("stop") @@ -80,77 +80,77 @@ class FtRestClient(): """ Stop buying (but handle sells gracefully). use reload_conf to reset - :returns: json object + :return: json object """ return self._post("stopbuy") def reload_conf(self): """ Reload configuration - :returns: json object + :return: json object """ return self._post("reload_conf") def balance(self): """ Get the account balance - :returns: json object + :return: json object """ return self._get("balance") def count(self): """ Returns the amount of open trades - :returns: json object + :return: json object """ return self._get("count") def daily(self, days=None): """ Returns the amount of open trades - :returns: json object + :return: json object """ return self._get("daily", params={"timescale": days} if days else None) def edge(self): """ Returns information about edge - :returns: json object + :return: json object """ return self._get("edge") def profit(self): """ Returns the profit summary - :returns: json object + :return: json object """ return self._get("profit") def performance(self): """ Returns the performance of the different coins - :returns: json object + :return: json object """ return self._get("performance") def status(self): """ Get the status of open trades - :returns: json object + :return: json object """ return self._get("status") def version(self): """ Returns the version of the bot - :returns: json object containing the version + :return: json object containing the version """ return self._get("version") def whitelist(self): """ Show the current whitelist - :returns: json object + :return: json object """ return self._get("whitelist") @@ -158,7 +158,7 @@ class FtRestClient(): """ Show the current blacklist :param add: List of coins to add (example: "BNB/BTC") - :returns: json object + :return: json object """ if not args: return self._get("blacklist") @@ -170,7 +170,7 @@ class FtRestClient(): Buy an asset :param pair: Pair to buy (ETH/BTC) :param price: Optional - price to buy - :returns: json object of the trade + :return: json object of the trade """ data = {"pair": pair, "price": price @@ -181,7 +181,7 @@ class FtRestClient(): """ Force-sell a trade :param tradeid: Id of the trade (can be received via status command) - :returns: json object + :return: json object """ return self._post("forcesell", data={"tradeid": tradeid})