From 9b233764153adcf939e72fc45d8a0c54bd865093 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 5 Oct 2019 13:29:59 +0300 Subject: [PATCH 1/8] Move experimental settings to ask_strategy --- config.json.example | 10 ++++------ config_binance.json.example | 10 ++++------ config_full.json.example | 10 ++++------ config_kraken.json.example | 6 +++++- docs/configuration.md | 12 ++++++------ freqtrade/constants.py | 8 ++++++-- freqtrade/freqtradebot.py | 8 +++++--- freqtrade/optimize/hyperopt.py | 8 ++++---- freqtrade/resolvers/strategy_resolver.py | 20 ++++++++++---------- freqtrade/strategy/interface.py | 8 ++++---- tests/config_test_comments.json | 5 ----- tests/optimize/test_backtest_detail.py | 2 +- tests/optimize/test_backtesting.py | 17 +++++++++++------ tests/strategy/test_strategy.py | 20 ++++++++++---------- tests/test_freqtradebot.py | 19 ++++++++----------- 15 files changed, 82 insertions(+), 81 deletions(-) diff --git a/config.json.example b/config.json.example index 94084434a..419019030 100644 --- a/config.json.example +++ b/config.json.example @@ -22,7 +22,10 @@ "ask_strategy":{ "use_order_book": false, "order_book_min": 1, - "order_book_max": 9 + "order_book_max": 9, + "use_sell_signal": true, + "sell_profit_only": false, + "ignore_roi_if_buy_signal": false }, "exchange": { "name": "bittrex", @@ -49,11 +52,6 @@ "DOGE/BTC" ] }, - "experimental": { - "use_sell_signal": false, - "sell_profit_only": false, - "ignore_roi_if_buy_signal": false - }, "edge": { "enabled": false, "process_throttle_secs": 3600, diff --git a/config_binance.json.example b/config_binance.json.example index 1d492fc3c..58817a78e 100644 --- a/config_binance.json.example +++ b/config_binance.json.example @@ -22,7 +22,10 @@ "ask_strategy":{ "use_order_book": false, "order_book_min": 1, - "order_book_max": 9 + "order_book_max": 9, + "use_sell_signal": true, + "sell_profit_only": false, + "ignore_roi_if_buy_signal": false }, "exchange": { "name": "binance", @@ -51,11 +54,6 @@ "BNB/BTC" ] }, - "experimental": { - "use_sell_signal": false, - "sell_profit_only": false, - "ignore_roi_if_buy_signal": false - }, "edge": { "enabled": false, "process_throttle_secs": 3600, diff --git a/config_full.json.example b/config_full.json.example index 957967042..c6b229ea3 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -33,7 +33,10 @@ "ask_strategy":{ "use_order_book": false, "order_book_min": 1, - "order_book_max": 9 + "order_book_max": 9, + "use_sell_signal": true, + "sell_profit_only": false, + "ignore_roi_if_buy_signal": false }, "order_types": { "buy": "limit", @@ -100,11 +103,6 @@ "max_trade_duration_minute": 1440, "remove_pumps": false }, - "experimental": { - "use_sell_signal": false, - "sell_profit_only": false, - "ignore_roi_if_buy_signal": false - }, "telegram": { "enabled": true, "token": "your_telegram_token", diff --git a/config_kraken.json.example b/config_kraken.json.example index ea3677b2d..51aa9a8e9 100644 --- a/config_kraken.json.example +++ b/config_kraken.json.example @@ -22,7 +22,11 @@ "ask_strategy":{ "use_order_book": false, "order_book_min": 1, - "order_book_max": 9 + "order_book_max": 9, + "use_sell_signal": true, + "sell_profit_only": false, + "ignore_roi_if_buy_signal": false + }, "exchange": { "name": "kraken", diff --git a/docs/configuration.md b/docs/configuration.md index 0d902766a..a31650f78 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -65,6 +65,9 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `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. +| `ask_strategy.use_sell_signal` | true | Use your strategy sell signals in addition to the `minimal_roi`. [Strategy Override](#parameters-in-the-strategy). +| `ask_strategy.sell_profit_only` | false | Wait until you have made a positive profit before taking a sell decision. [Strategy Override](#parameters-in-the-strategy). +| `ask_strategy.ignore_roi_if_buy_signal` | false | Do not sell if the buy-signal is still active. This setting takes preference over `minimal_roi` and `use_sell_signal`. [Strategy Override](#parameters-in-the-strategy). | `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-the-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-the-strategy). | `exchange.name` | | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). @@ -78,9 +81,6 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `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) | `exchange.markets_refresh_interval` | 60 | The interval in minutes in which markets are reloaded. | `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-the-strategy). -| `experimental.sell_profit_only` | false | Waits until you have made a positive profit before taking a sell decision. [Strategy Override](#parameters-in-the-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-the-strategy). | `experimental.block_bad_exchanges` | true | Block exchanges known to not work with freqtrade. Leave on default unless you want to test if that exchange works now. | `pairlist.method` | StaticPairList | Use static or dynamic volume-based pairlist. [More information below](#dynamic-pairlists). | `pairlist.config` | None | Additional configuration for dynamic pairlists. [More information below](#dynamic-pairlists). @@ -116,9 +116,9 @@ Values set in the configuration file always overwrite values set in the strategy * `process_only_new_candles` * `order_types` * `order_time_in_force` -* `use_sell_signal` (experimental) -* `sell_profit_only` (experimental) -* `ignore_roi_if_buy_signal` (experimental) +* `use_sell_signal` (ask_strategy) +* `sell_profit_only` (ask_strategy) +* `ignore_roi_if_buy_signal` (ask_strategy) ### Understand stake_amount diff --git a/freqtrade/constants.py b/freqtrade/constants.py index abf43b24d..697bc280f 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -114,7 +114,10 @@ CONF_SCHEMA = { 'properties': { 'use_order_book': {'type': 'boolean'}, 'order_book_min': {'type': 'number', 'minimum': 1}, - 'order_book_max': {'type': 'number', 'minimum': 1, 'maximum': 50} + 'order_book_max': {'type': 'number', 'minimum': 1, 'maximum': 50}, + 'use_sell_signal': {'type': 'boolean'}, + 'sell_profit_only': {'type': 'boolean'}, + 'ignore_roi_if_buy_signal': {'type': 'boolean'} } }, 'order_types': { @@ -144,7 +147,8 @@ CONF_SCHEMA = { 'properties': { 'use_sell_signal': {'type': 'boolean'}, 'sell_profit_only': {'type': 'boolean'}, - 'ignore_roi_if_buy_signal_true': {'type': 'boolean'} + 'ignore_roi_if_buy_signal': {'type': 'boolean'}, + 'block_bad_exchanges': {'type': 'boolean'} } }, 'pairlist': { diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 3f7eab27a..9a2f2cdc4 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -580,13 +580,15 @@ class FreqtradeBot: logger.debug('Handling %s ...', trade) (buy, sell) = (False, False) - experimental = self.config.get('experimental', {}) - if experimental.get('use_sell_signal') or experimental.get('ignore_roi_if_buy_signal'): + + config_ask_strategy = self.config.get('ask_strategy', {}) + + if (config_ask_strategy.get('use_sell_signal', True) or + config_ask_strategy.get('ignore_roi_if_buy_signal')): (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): logger.info('Using order book for selling...') # logger.debug('Order book %s',orderBook) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index a70ff8142..07258a048 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -98,10 +98,10 @@ class Hyperopt: self.position_stacking = self.config.get('position_stacking', False) if self.has_space('sell'): - # Make sure experimental is enabled - if 'experimental' not in self.config: - self.config['experimental'] = {} - self.config['experimental']['use_sell_signal'] = True + # Make sure use_sell_signal is enabled + if 'ask_strategy' not in self.config: + self.config['ask_strategy'] = {} + self.config['ask_strategy']['use_sell_signal'] = True @staticmethod def get_lock_filename(config) -> str: diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index ca7e1165b..43197e5bc 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -38,13 +38,13 @@ class StrategyResolver(IResolver): config=config, extra_dir=config.get('strategy_path')) - # make sure experimental dict is available - if 'experimental' not in config: - config['experimental'] = {} + # make sure ask_strategy dict is available + if 'ask_strategy' not in config: + config['ask_strategy'] = {} # Set attributes # Check if we need to override configuration - # (Attribute name, default, experimental) + # (Attribute name, default, ask_strategy) attributes = [("minimal_roi", {"0": 10.0}, False), ("ticker_interval", None, False), ("stoploss", None, False), @@ -57,20 +57,20 @@ class StrategyResolver(IResolver): ("order_time_in_force", None, False), ("stake_currency", None, False), ("stake_amount", None, False), - ("use_sell_signal", False, True), + ("use_sell_signal", True, True), ("sell_profit_only", False, True), ("ignore_roi_if_buy_signal", False, True), ] - for attribute, default, experimental in attributes: - if experimental: - self._override_attribute_helper(config['experimental'], attribute, default) + for attribute, default, ask_strategy in attributes: + if ask_strategy: + self._override_attribute_helper(config['ask_strategy'], attribute, default) else: self._override_attribute_helper(config, attribute, default) # Loop this list again to have output combined for attribute, _, exp in attributes: - if exp and attribute in config['experimental']: - logger.info("Strategy using %s: %s", attribute, config['experimental'][attribute]) + if exp and attribute in config['ask_strategy']: + logger.info("Strategy using %s: %s", attribute, config['ask_strategy'][attribute]) elif attribute in config: logger.info("Strategy using %s: %s", attribute, config[attribute]) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 17246ecf7..b35ebabbb 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -309,9 +309,9 @@ class IStrategy(ABC): # Set current rate to high for backtesting sell current_rate = high or rate current_profit = trade.calc_profit_percent(current_rate) - experimental = self.config.get('experimental', {}) + config_ask_strategy = self.config.get('ask_strategy', {}) - if buy and experimental.get('ignore_roi_if_buy_signal', False): + if buy and config_ask_strategy.get('ignore_roi_if_buy_signal', False): # This one is noisy, commented out # logger.debug(f"{trade.pair} - Buy signal still active. sell_flag=False") return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE) @@ -322,7 +322,7 @@ class IStrategy(ABC): f"sell_type=SellType.ROI") return SellCheckTuple(sell_flag=True, sell_type=SellType.ROI) - if experimental.get('sell_profit_only', False): + if config_ask_strategy.get('sell_profit_only', False): # This one is noisy, commented out # logger.debug(f"{trade.pair} - Checking if trade is profitable...") if trade.calc_profit(rate=rate) <= 0: @@ -330,7 +330,7 @@ class IStrategy(ABC): # logger.debug(f"{trade.pair} - Trade is not profitable. sell_flag=False") return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE) - if sell and not buy and experimental.get('use_sell_signal', False): + if sell and not buy and config_ask_strategy.get('use_sell_signal', True): logger.debug(f"{trade.pair} - Sell signal received. sell_flag=True, " f"sell_type=SellType.SELL_SIGNAL") return SellCheckTuple(sell_flag=True, sell_type=SellType.SELL_SIGNAL) diff --git a/tests/config_test_comments.json b/tests/config_test_comments.json index 85becc3e8..8af39d6ba 100644 --- a/tests/config_test_comments.json +++ b/tests/config_test_comments.json @@ -103,11 +103,6 @@ "max_trade_duration_minute": 1440, "remove_pumps": false }, - "experimental": { - "use_sell_signal": false, - "sell_profit_only": false, - "ignore_roi_if_buy_signal": false - }, "telegram": { // We can now comment out some settings // "enabled": true, diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index d8a4190e2..a85dc99c3 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -285,7 +285,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None: if data.trailing_stop_positive: default_conf["trailing_stop_positive"] = data.trailing_stop_positive default_conf["trailing_stop_positive_offset"] = data.trailing_stop_positive_offset - default_conf["experimental"] = {"use_sell_signal": data.use_sell_signal} + default_conf["ask_strategy"] = {"use_sell_signal": data.use_sell_signal} mocker.patch("freqtrade.exchange.Exchange.get_fee", MagicMock(return_value=0.0)) patch_exchange(mocker) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index fa40809d8..589896c39 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -507,6 +507,7 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog, testdatadir) -> def test_backtest(default_conf, fee, mocker, testdatadir) -> None: + default_conf['ask_strategy']['use_sell_signal'] = False mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) patch_exchange(mocker) backtesting = Backtesting(default_conf) @@ -561,6 +562,7 @@ def test_backtest(default_conf, fee, mocker, testdatadir) -> None: def test_backtest_1min_ticker_interval(default_conf, fee, mocker, testdatadir) -> None: + default_conf['ask_strategy']['use_sell_signal'] = False mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) patch_exchange(mocker) backtesting = Backtesting(default_conf) @@ -603,8 +605,9 @@ def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir) -> None: # TODO: Evaluate usefullness of this, the patterns and buy-signls are unrealistic mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) tests = [['raise', 19], ['lower', 0], ['sine', 35]] - # We need to enable sell-signal - otherwise it sells on ROI!! - default_conf['experimental'] = {"use_sell_signal": True} + # use_sell_signal is True by default now, it will sell on the sell signal instead of ROI, + # which is what we need here +# default_conf['ask_strategy'] = {"use_sell_signal": True} for [contour, numres] in tests: simple_backtest(default_conf, contour, numres, mocker, testdatadir) @@ -645,8 +648,9 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir): mocker.patch('freqtrade.optimize.backtesting.file_dump_json', MagicMock()) backtest_conf = _make_backtest_conf(mocker, conf=default_conf, pair='UNITTEST/BTC', datadir=testdatadir) - # We need to enable sell-signal - otherwise it sells on ROI!! - default_conf['experimental'] = {"use_sell_signal": True} + # use_sell_signal is True by default now, it will sell on the sell signal instead of ROI, + # which is what we need here +# default_conf['ask_strategy'] = {"use_sell_signal": True} default_conf['ticker_interval'] = '1m' backtesting = Backtesting(default_conf) backtesting.strategy.advise_buy = _trend_alternate # Override @@ -687,8 +691,9 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir) # Remove data for one pair from the beginning of the data data[pair] = data[pair][tres:].reset_index() - # We need to enable sell-signal - otherwise it sells on ROI!! - default_conf['experimental'] = {"use_sell_signal": True} + # use_sell_signal is True by default now, it will sell on the sell signal instead of ROI, + # which is what we need here +# default_conf['ask_strategy'] = {"use_sell_signal": True} default_conf['ticker_interval'] = '5m' backtesting = Backtesting(default_conf) diff --git a/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py index 6992d1aa5..88e29b40e 100644 --- a/tests/strategy/test_strategy.py +++ b/tests/strategy/test_strategy.py @@ -256,23 +256,23 @@ def test_strategy_override_use_sell_signal(caplog, default_conf): 'strategy': 'DefaultStrategy', }) resolver = StrategyResolver(default_conf) - assert not resolver.strategy.use_sell_signal + assert resolver.strategy.use_sell_signal assert isinstance(resolver.strategy.use_sell_signal, bool) # must be inserted to configuration - assert 'use_sell_signal' in default_conf['experimental'] - assert not default_conf['experimental']['use_sell_signal'] + assert 'use_sell_signal' in default_conf['ask_strategy'] + assert default_conf['ask_strategy']['use_sell_signal'] default_conf.update({ 'strategy': 'DefaultStrategy', - 'experimental': { - 'use_sell_signal': True, + 'ask_strategy': { + 'use_sell_signal': False, }, }) resolver = StrategyResolver(default_conf) - assert resolver.strategy.use_sell_signal + assert not resolver.strategy.use_sell_signal assert isinstance(resolver.strategy.use_sell_signal, bool) - assert log_has("Override strategy 'use_sell_signal' with value in config file: True.", caplog) + assert log_has("Override strategy 'use_sell_signal' with value in config file: False.", caplog) def test_strategy_override_use_sell_profit_only(caplog, default_conf): @@ -284,12 +284,12 @@ def test_strategy_override_use_sell_profit_only(caplog, default_conf): 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 default_conf['experimental'] - assert not default_conf['experimental']['sell_profit_only'] + assert 'sell_profit_only' in default_conf['ask_strategy'] + assert not default_conf['ask_strategy']['sell_profit_only'] default_conf.update({ 'strategy': 'DefaultStrategy', - 'experimental': { + 'ask_strategy': { 'sell_profit_only': True, }, }) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index ee28f2e58..a9c85b4c3 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1768,8 +1768,6 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, fee, markets, mocker) -> None: - default_conf.update({'experimental': {'use_sell_signal': True}}) - patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -1824,7 +1822,6 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, def test_handle_trade_roi(default_conf, ticker, limit_buy_order, fee, mocker, markets, caplog) -> None: caplog.set_level(logging.DEBUG) - default_conf.update({'experimental': {'use_sell_signal': True}}) patch_RPCManager(mocker) patch_exchange(mocker) @@ -1856,10 +1853,10 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order, caplog) -def test_handle_trade_experimental( +def test_handle_trade_use_sell_signal( default_conf, ticker, limit_buy_order, fee, mocker, markets, caplog) -> None: + # use_sell_signal is True buy default caplog.set_level(logging.DEBUG) - default_conf.update({'experimental': {'use_sell_signal': True}}) patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -2600,7 +2597,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, get_fee=fee, markets=PropertyMock(return_value=markets) ) - default_conf['experimental'] = { + default_conf['ask_strategy'] = { 'use_sell_signal': True, 'sell_profit_only': True, } @@ -2632,7 +2629,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, get_fee=fee, markets=PropertyMock(return_value=markets) ) - default_conf['experimental'] = { + default_conf['ask_strategy'] = { 'use_sell_signal': True, 'sell_profit_only': False, } @@ -2662,7 +2659,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, market get_fee=fee, markets=PropertyMock(return_value=markets) ) - default_conf['experimental'] = { + default_conf['ask_strategy'] = { 'use_sell_signal': True, 'sell_profit_only': True, } @@ -2692,7 +2689,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, marke get_fee=fee, markets=PropertyMock(return_value=markets) ) - default_conf['experimental'] = { + default_conf['ask_strategy'] = { 'use_sell_signal': True, 'sell_profit_only': False, } @@ -2761,7 +2758,7 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, markets, m get_fee=fee, markets=PropertyMock(return_value=markets) ) - default_conf['experimental'] = { + default_conf['ask_strategy'] = { 'ignore_roi_if_buy_signal': True } freqtrade = FreqtradeBot(default_conf) @@ -3029,7 +3026,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, get_fee=fee, markets=PropertyMock(return_value=markets) ) - default_conf['experimental'] = { + default_conf['ask_strategy'] = { 'ignore_roi_if_buy_signal': False } freqtrade = FreqtradeBot(default_conf) From 057ab1b7a6f6429bac88a090973fa82552765263 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 8 Oct 2019 00:50:47 +0300 Subject: [PATCH 2/8] Remove unnecessary comments --- tests/optimize/test_backtesting.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 589896c39..7e538c999 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -605,9 +605,6 @@ def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir) -> None: # TODO: Evaluate usefullness of this, the patterns and buy-signls are unrealistic mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) tests = [['raise', 19], ['lower', 0], ['sine', 35]] - # use_sell_signal is True by default now, it will sell on the sell signal instead of ROI, - # which is what we need here -# default_conf['ask_strategy'] = {"use_sell_signal": True} for [contour, numres] in tests: simple_backtest(default_conf, contour, numres, mocker, testdatadir) @@ -648,9 +645,6 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir): mocker.patch('freqtrade.optimize.backtesting.file_dump_json', MagicMock()) backtest_conf = _make_backtest_conf(mocker, conf=default_conf, pair='UNITTEST/BTC', datadir=testdatadir) - # use_sell_signal is True by default now, it will sell on the sell signal instead of ROI, - # which is what we need here -# default_conf['ask_strategy'] = {"use_sell_signal": True} default_conf['ticker_interval'] = '1m' backtesting = Backtesting(default_conf) backtesting.strategy.advise_buy = _trend_alternate # Override @@ -691,9 +685,6 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir) # Remove data for one pair from the beginning of the data data[pair] = data[pair][tres:].reset_index() - # use_sell_signal is True by default now, it will sell on the sell signal instead of ROI, - # which is what we need here -# default_conf['ask_strategy'] = {"use_sell_signal": True} default_conf['ticker_interval'] = '5m' backtesting = Backtesting(default_conf) From e78e42339d9759dbe40bea3769643de05a6edaa1 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 8 Oct 2019 00:58:25 +0300 Subject: [PATCH 3/8] Improve docs wordings --- docs/configuration.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index a31650f78..0eff4da88 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -59,15 +59,15 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `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.order_book_top` | 0 | Bot will use the top N rate in Order Book Bids. I.e. 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. -| `ask_strategy.use_sell_signal` | true | Use your strategy sell signals in addition to the `minimal_roi`. [Strategy Override](#parameters-in-the-strategy). -| `ask_strategy.sell_profit_only` | false | Wait until you have made a positive profit before taking a sell decision. [Strategy Override](#parameters-in-the-strategy). -| `ask_strategy.ignore_roi_if_buy_signal` | false | Do not sell if the buy-signal is still active. This setting takes preference over `minimal_roi` and `use_sell_signal`. [Strategy Override](#parameters-in-the-strategy). +| `ask_strategy.use_sell_signal` | true | Use sell signals produced by the strategy in addition to the `minimal_roi`. [Strategy Override](#parameters-in-the-strategy). +| `ask_strategy.sell_profit_only` | false | Wait until the bot makes a positive profit before taking a sell decision. [Strategy Override](#parameters-in-the-strategy). +| `ask_strategy.ignore_roi_if_buy_signal` | false | Do not sell if the buy signal is still active. This setting takes preference over `minimal_roi` and `use_sell_signal`. [Strategy Override](#parameters-in-the-strategy). | `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-the-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-the-strategy). | `exchange.name` | | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename). From 4d062d41cb487d5d984eb23bf08dcfbf491e40b6 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 8 Oct 2019 01:07:22 +0300 Subject: [PATCH 4/8] Improve comments in the SampleStrategy; set use_sell_signal = True --- user_data/strategies/sample_strategy.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/user_data/strategies/sample_strategy.py b/user_data/strategies/sample_strategy.py index 0649c6f94..623addb1e 100644 --- a/user_data/strategies/sample_strategy.py +++ b/user_data/strategies/sample_strategy.py @@ -27,12 +27,12 @@ class SampleStrategy(IStrategy): - the prototype for the methods: minimal_roi, stoploss, populate_indicators, populate_buy_trend, populate_sell_trend, hyperopt_space, buy_strategy_generator """ - # Strategy intervace version - allow new iterations of the strategy interface. + # Strategy interface version - allow new iterations of the strategy interface. # Check the documentation or the Sample strategy to get the latest version. INTERFACE_VERSION = 2 # Minimal ROI designed for the strategy. - # This attribute will be overridden if the config file contains "minimal_roi" + # This attribute will be overridden if the config file contains "minimal_roi". minimal_roi = { "40": 0.0, "30": 0.01, @@ -40,27 +40,27 @@ class SampleStrategy(IStrategy): "0": 0.04 } - # Optimal stoploss designed for the strategy - # This attribute will be overridden if the config file contains "stoploss" + # Optimal stoploss designed for the strategy. + # This attribute will be overridden if the config file contains "stoploss". stoploss = -0.10 - # trailing stoploss + # Trailing stoploss trailing_stop = False # trailing_stop_positive = 0.01 # trailing_stop_positive_offset = 0.0 # Disabled / not configured - # Optimal ticker interval for the strategy + # Optimal ticker interval for the strategy. ticker_interval = '5m' - # run "populate_indicators" only for new candle + # Run "populate_indicators()" only for new candle. process_only_new_candles = False - # Experimental settings (configuration will overide these if set) - use_sell_signal = False + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True sell_profit_only = False ignore_roi_if_buy_signal = False - # Optional order type mapping + # Optional order type mapping. order_types = { 'buy': 'limit', 'sell': 'limit', @@ -68,7 +68,7 @@ class SampleStrategy(IStrategy): 'stoploss_on_exchange': False } - # Optional order time in force + # Optional order time in force. order_time_in_force = { 'buy': 'gtc', 'sell': 'gtc' From 434e0234c5a55cf641189e413d9470abcba739cf Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Wed, 9 Oct 2019 02:37:29 +0300 Subject: [PATCH 5/8] Add handling deprecated settings --- freqtrade/configuration/configuration.py | 11 +++- .../configuration/deprecated_settings.py | 59 +++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 freqtrade/configuration/deprecated_settings.py diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 764593d0f..84d58f754 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -9,8 +9,9 @@ from typing import Any, Callable, Dict, List, Optional from freqtrade import OperationalException, constants from freqtrade.configuration.check_exchange import check_exchange -from freqtrade.configuration.config_validation import ( - validate_config_consistency, validate_config_schema) +from freqtrade.configuration.config_validation import (validate_config_consistency, + validate_config_schema) +from freqtrade.configuration.deprecated_settings import process_temporary_deprecated_settings from freqtrade.configuration.directory_operations import (create_datadir, create_userdata_dir) from freqtrade.configuration.load_config import load_config_file @@ -75,6 +76,10 @@ class Configuration: # Normalize config if 'internals' not in config: config['internals'] = {} + # TODO: This can be deleted along with removal of deprecated + # experimental settings + if 'ask_strategy' not in config: + config['ask_strategy'] = {} # validate configuration before returning logger.info('Validating configuration ...') @@ -106,6 +111,8 @@ class Configuration: self._resolve_pairs_list(config) + process_temporary_deprecated_settings(config) + validate_config_consistency(config) return config diff --git a/freqtrade/configuration/deprecated_settings.py b/freqtrade/configuration/deprecated_settings.py new file mode 100644 index 000000000..927497c56 --- /dev/null +++ b/freqtrade/configuration/deprecated_settings.py @@ -0,0 +1,59 @@ +""" +Functions to handle deprecated warnings +""" + +import logging +from typing import Any, Dict + +from freqtrade import OperationalException + + +logger = logging.getLogger(__name__) + + +def check_conflicting_settings(config: Dict[str, Any], + section1: str, name1: str, + section2: str, name2: str): + section1_config = config.get(section1, {}) + section2_config = config.get(section2, {}) + if name1 in section1_config and name2 in section2_config: + raise OperationalException( + f"Conflicting settings `{section1}.{name1}` and `{section2}.{name2}` " + "(DEPRECATED) detected in the configuration file. " + "This deprecated setting will be removed in the next versions of Freqtrade. " + f"Please delete it from your configuration and use the `{section1}.{name1}` " + "setting instead." + ) + + +def process_deprecated_setting(config: Dict[str, Any], + section1: str, name1: str, + section2: str, name2: str): + section2_config = config.get(section2, {}) + + if name2 in section2_config: + logger.warning( + "DEPRECATED: " + f"The `{section2}.{name2}` setting is deprecated and " + "will be removed in the next versions of Freqtrade. " + f"Please use the `{section1}.{name1}` setting in your configuration instead." + ) + section1_config = config.get(section1, {}) + section1_config[name1] = section2_config[name2] + + +def process_temporary_deprecated_settings(config: Dict[str, Any]) -> None: + + check_conflicting_settings(config, 'ask_strategy', 'use_sell_signal', + 'experimental', 'use_sell_signal') + check_conflicting_settings(config, 'ask_strategy', 'sell_profit_only', + 'experimental', 'sell_profit_only') + check_conflicting_settings(config, 'ask_strategy', 'ignore_roi_if_buy_signal', + 'experimental', 'ignore_roi_if_buy_signal') + + process_deprecated_setting(config, 'ask_strategy', 'use_sell_signal', + 'experimental', 'use_sell_signal') + process_deprecated_setting(config, 'ask_strategy', 'sell_profit_only', + 'experimental', 'sell_profit_only') + process_deprecated_setting(config, 'ask_strategy', 'ignore_roi_if_buy_signal', + 'experimental', 'ignore_roi_if_buy_signal') From 2a9c06c40fa62456b15fbae78e4b5ace3d55ee7b Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Wed, 9 Oct 2019 02:44:04 +0300 Subject: [PATCH 6/8] Test added --- tests/test_configuration.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 0f2d6a50a..69e6d621c 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -14,6 +14,7 @@ from freqtrade.configuration import (Arguments, Configuration, validate_config_consistency) from freqtrade.configuration.check_exchange import check_exchange from freqtrade.configuration.config_validation import validate_config_schema +from freqtrade.configuration.deprecated_settings import (process_temporary_deprecated_settings) from freqtrade.configuration.directory_operations import (create_datadir, create_userdata_dir) from freqtrade.configuration.load_config import load_config_file @@ -897,3 +898,27 @@ def test_pairlist_resolving_fallback(mocker): assert config['pairs'] == ['ETH/BTC', 'XRP/BTC'] assert config['exchange']['name'] == 'binance' assert config['datadir'] == str(Path.cwd() / "user_data/data/binance") + + +@pytest.mark.parametrize("setting", [ + ("ask_strategy", "use_sell_signal", True, + "experimental", "use_sell_signal", False), + ("ask_strategy", "sell_profit_only", False, + "experimental", "sell_profit_only", True), + ("ask_strategy", "ignore_roi_if_buy_signal", False, + "experimental", "ignore_roi_if_buy_signal", True), + ]) +def test_deprecated_experimental(mocker, default_conf, setting, caplog): + patched_configuration_load_config_file(mocker, default_conf) + + default_conf[setting[3]] = {} + default_conf[setting[0]][setting[1]] = setting[2] + default_conf[setting[3]][setting[4]] = setting[5] + + with pytest.raises(OperationalException, match=r'DEPRECATED'): + process_temporary_deprecated_settings(default_conf) + + del default_conf[setting[0]][setting[1]] + process_temporary_deprecated_settings(default_conf) + assert log_has_re('DEPRECATED', caplog) + assert default_conf[setting[0]][setting[1]] == setting[5] From cdd1bc425b26e5a7f24952d3cf5415b131dd69b3 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Wed, 9 Oct 2019 03:12:30 +0300 Subject: [PATCH 7/8] Fix typo --- freqtrade/configuration/deprecated_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/configuration/deprecated_settings.py b/freqtrade/configuration/deprecated_settings.py index 927497c56..f00b23894 100644 --- a/freqtrade/configuration/deprecated_settings.py +++ b/freqtrade/configuration/deprecated_settings.py @@ -1,5 +1,5 @@ """ -Functions to handle deprecated warnings +Functions to handle deprecated settings """ import logging From 23b5c0e833b2702b3649ddeec6d11ebdf11b5700 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Wed, 9 Oct 2019 18:22:54 +0300 Subject: [PATCH 8/8] Improve tests for handling deprecated settings --- tests/test_configuration.py | 105 +++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 69e6d621c..c6864a643 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -14,7 +14,9 @@ from freqtrade.configuration import (Arguments, Configuration, validate_config_consistency) from freqtrade.configuration.check_exchange import check_exchange from freqtrade.configuration.config_validation import validate_config_schema -from freqtrade.configuration.deprecated_settings import (process_temporary_deprecated_settings) +from freqtrade.configuration.deprecated_settings import (check_conflicting_settings, + process_deprecated_setting, + process_temporary_deprecated_settings) from freqtrade.configuration.directory_operations import (create_datadir, create_userdata_dir) from freqtrade.configuration.load_config import load_config_file @@ -908,17 +910,116 @@ def test_pairlist_resolving_fallback(mocker): ("ask_strategy", "ignore_roi_if_buy_signal", False, "experimental", "ignore_roi_if_buy_signal", True), ]) -def test_deprecated_experimental(mocker, default_conf, setting, caplog): +def test_process_temporary_deprecated_settings(mocker, default_conf, setting, caplog): patched_configuration_load_config_file(mocker, default_conf) + # Create sections for new and deprecated settings + # (they may not exist in the config) + default_conf[setting[0]] = {} default_conf[setting[3]] = {} + # Assign new setting default_conf[setting[0]][setting[1]] = setting[2] + # Assign deprecated setting default_conf[setting[3]][setting[4]] = setting[5] + # New and deprecated settings are conflicting ones with pytest.raises(OperationalException, match=r'DEPRECATED'): process_temporary_deprecated_settings(default_conf) + caplog.clear() + + # Delete new setting del default_conf[setting[0]][setting[1]] + process_temporary_deprecated_settings(default_conf) assert log_has_re('DEPRECATED', caplog) + # The value of the new setting shall have been set to the + # value of the deprecated one assert default_conf[setting[0]][setting[1]] == setting[5] + + +def test_check_conflicting_settings(mocker, default_conf, caplog): + patched_configuration_load_config_file(mocker, default_conf) + + # Create sections for new and deprecated settings + # (they may not exist in the config) + default_conf['sectionA'] = {} + default_conf['sectionB'] = {} + # Assign new setting + default_conf['sectionA']['new_setting'] = 'valA' + # Assign deprecated setting + default_conf['sectionB']['deprecated_setting'] = 'valB' + + # New and deprecated settings are conflicting ones + with pytest.raises(OperationalException, match=r'DEPRECATED'): + check_conflicting_settings(default_conf, + 'sectionA', 'new_setting', + 'sectionB', 'deprecated_setting') + + caplog.clear() + + # Delete new setting (deprecated exists) + del default_conf['sectionA']['new_setting'] + check_conflicting_settings(default_conf, + 'sectionA', 'new_setting', + 'sectionB', 'deprecated_setting') + assert not log_has_re('DEPRECATED', caplog) + assert 'new_setting' not in default_conf['sectionA'] + + caplog.clear() + + # Assign new setting + default_conf['sectionA']['new_setting'] = 'valA' + # Delete deprecated setting + del default_conf['sectionB']['deprecated_setting'] + check_conflicting_settings(default_conf, + 'sectionA', 'new_setting', + 'sectionB', 'deprecated_setting') + assert not log_has_re('DEPRECATED', caplog) + assert default_conf['sectionA']['new_setting'] == 'valA' + + +def test_process_deprecated_setting(mocker, default_conf, caplog): + patched_configuration_load_config_file(mocker, default_conf) + + # Create sections for new and deprecated settings + # (they may not exist in the config) + default_conf['sectionA'] = {} + default_conf['sectionB'] = {} + # Assign new setting + default_conf['sectionA']['new_setting'] = 'valA' + # Assign deprecated setting + default_conf['sectionB']['deprecated_setting'] = 'valB' + + # Both new and deprecated settings exists + process_deprecated_setting(default_conf, + 'sectionA', 'new_setting', + 'sectionB', 'deprecated_setting') + assert log_has_re('DEPRECATED', caplog) + # The value of the new setting shall have been set to the + # value of the deprecated one + assert default_conf['sectionA']['new_setting'] == 'valB' + + caplog.clear() + + # Delete new setting (deprecated exists) + del default_conf['sectionA']['new_setting'] + process_deprecated_setting(default_conf, + 'sectionA', 'new_setting', + 'sectionB', 'deprecated_setting') + assert log_has_re('DEPRECATED', caplog) + # The value of the new setting shall have been set to the + # value of the deprecated one + assert default_conf['sectionA']['new_setting'] == 'valB' + + caplog.clear() + + # Assign new setting + default_conf['sectionA']['new_setting'] = 'valA' + # Delete deprecated setting + del default_conf['sectionB']['deprecated_setting'] + process_deprecated_setting(default_conf, + 'sectionA', 'new_setting', + 'sectionB', 'deprecated_setting') + assert not log_has_re('DEPRECATED', caplog) + assert default_conf['sectionA']['new_setting'] == 'valA'