From 176beefa88e27ccda79bada82a0143ef7e39c3de Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 11 Aug 2019 14:14:51 +0200 Subject: [PATCH 1/4] Disable stoploss on exchange for dry-runs --- freqtrade/resolvers/strategy_resolver.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index aa73327ff..4c521e2f4 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -81,7 +81,7 @@ class StrategyResolver(IResolver): key=lambda t: t[0])) self.strategy.stoploss = float(self.strategy.stoploss) - self._strategy_sanity_validations() + self._strategy_sanity_validations(config) def _override_attribute_helper(self, config, attribute: str, default): """ @@ -102,7 +102,7 @@ class StrategyResolver(IResolver): setattr(self.strategy, attribute, default) config[attribute] = default - def _strategy_sanity_validations(self): + def _strategy_sanity_validations(self, config): 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.") @@ -111,6 +111,12 @@ class StrategyResolver(IResolver): raise ImportError(f"Impossible to load Strategy '{self.strategy.__class__.__name__}'. " f"Order-time-in-force mapping is incomplete.") + # Stoploss on exchange does not make sense, therefore we need to disable that. + if config.get('dry_run'): + logger.info("Disabling stoploss_on_exchange during dry-run.") + self.strategy.order_types['stoploss_on_exchange'] = False + config['order_types']['stoploss_on_exchange'] = False + def _load_strategy( self, strategy_name: str, config: dict, extra_dir: Optional[str] = None) -> IStrategy: """ From e02e64fc0756d719a65ac914cc859f9410fd8e48 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 11 Aug 2019 14:15:04 +0200 Subject: [PATCH 2/4] Add test to make sure dry-run disables stoploss on exchange --- freqtrade/tests/strategy/test_strategy.py | 39 +++++++++++++++++++---- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index df8c0f126..aa386fa6c 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -15,7 +15,7 @@ from freqtrade.resolvers import StrategyResolver from freqtrade.strategy import import_strategy from freqtrade.strategy.default_strategy import DefaultStrategy from freqtrade.strategy.interface import IStrategy -from freqtrade.tests.conftest import log_has_re +from freqtrade.tests.conftest import log_has_re, log_has def test_import_strategy(caplog): @@ -257,12 +257,9 @@ def test_strategy_override_order_types(caplog): for method in ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']: assert resolver.strategy.order_types[method] == order_types[method] - assert ('freqtrade.resolvers.strategy_resolver', - logging.INFO, - "Override strategy 'order_types' with value in config file:" - " {'buy': 'market', 'sell': 'limit', 'stoploss': 'limit'," - " 'stoploss_on_exchange': True}." - ) in caplog.record_tuples + assert log_has("Override strategy 'order_types' with value in config file:" + " {'buy': 'market', 'sell': 'limit', 'stoploss': 'limit'," + " 'stoploss_on_exchange': True}.", caplog.record_tuples) config = { 'strategy': 'DefaultStrategy', @@ -275,6 +272,34 @@ def test_strategy_override_order_types(caplog): StrategyResolver(config) +def test_strategy_override_order_types_dryrun(caplog): + caplog.set_level(logging.INFO) + + order_types = { + 'buy': 'market', + 'sell': 'limit', + 'stoploss': 'limit', + 'stoploss_on_exchange': True, + } + + config = { + 'strategy': 'DefaultStrategy', + 'order_types': order_types, + 'dry_run': True, + } + resolver = StrategyResolver(config) + + assert resolver.strategy.order_types + for method in ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']: + assert resolver.strategy.order_types[method] == order_types[method] + + assert log_has("Disabling stoploss_on_exchange during dry-run.", caplog.record_tuples) + + assert log_has("Override strategy 'order_types' with value in config file:" + " {'buy': 'market', 'sell': 'limit', 'stoploss': 'limit'," + " 'stoploss_on_exchange': False}.", caplog.record_tuples) + + def test_strategy_override_order_tif(caplog): caplog.set_level(logging.INFO) From 4b4fcc703471890c3bb584f9a47b04402750c9f2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 11 Aug 2019 19:43:57 +0200 Subject: [PATCH 3/4] Change stoploss_on_exchange in freqtradebot --- freqtrade/freqtradebot.py | 8 ++++++- freqtrade/resolvers/strategy_resolver.py | 10 ++------ freqtrade/tests/strategy/test_strategy.py | 28 ----------------------- 3 files changed, 9 insertions(+), 37 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index d52165e0a..603b0631f 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -20,7 +20,7 @@ from freqtrade.exchange import timeframe_to_minutes from freqtrade.persistence import Trade from freqtrade.rpc import RPCManager, RPCMessageType from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListResolver -from freqtrade.state import State +from freqtrade.state import State, RunMode from freqtrade.strategy.interface import SellType, IStrategy from freqtrade.wallets import Wallets @@ -75,6 +75,12 @@ class FreqtradeBot(object): persistence.init(self.config.get('db_url', None), clean_open_orders=self.config.get('dry_run', False)) + # Stoploss on exchange does not make sense, therefore we need to disable that. + if (self.dataprovider.runmode == RunMode.DRY_RUN and + self.strategy.order_types.get('stoploss_on_exchange', False)): + logger.info("Disabling stoploss_on_exchange during dry-run.") + self.strategy.order_types['stoploss_on_exchange'] = False + config['order_types']['stoploss_on_exchange'] = False # Set initial bot state from config initial_state = self.config.get('initial_state') self.state = State[initial_state.upper()] if initial_state else State.STOPPED diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 4c521e2f4..aa73327ff 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -81,7 +81,7 @@ class StrategyResolver(IResolver): key=lambda t: t[0])) self.strategy.stoploss = float(self.strategy.stoploss) - self._strategy_sanity_validations(config) + self._strategy_sanity_validations() def _override_attribute_helper(self, config, attribute: str, default): """ @@ -102,7 +102,7 @@ class StrategyResolver(IResolver): setattr(self.strategy, attribute, default) config[attribute] = default - def _strategy_sanity_validations(self, config): + 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.") @@ -111,12 +111,6 @@ class StrategyResolver(IResolver): raise ImportError(f"Impossible to load Strategy '{self.strategy.__class__.__name__}'. " f"Order-time-in-force mapping is incomplete.") - # Stoploss on exchange does not make sense, therefore we need to disable that. - if config.get('dry_run'): - logger.info("Disabling stoploss_on_exchange during dry-run.") - self.strategy.order_types['stoploss_on_exchange'] = False - config['order_types']['stoploss_on_exchange'] = False - 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 aa386fa6c..93c0a9e8a 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -272,34 +272,6 @@ def test_strategy_override_order_types(caplog): StrategyResolver(config) -def test_strategy_override_order_types_dryrun(caplog): - caplog.set_level(logging.INFO) - - order_types = { - 'buy': 'market', - 'sell': 'limit', - 'stoploss': 'limit', - 'stoploss_on_exchange': True, - } - - config = { - 'strategy': 'DefaultStrategy', - 'order_types': order_types, - 'dry_run': True, - } - resolver = StrategyResolver(config) - - assert resolver.strategy.order_types - for method in ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']: - assert resolver.strategy.order_types[method] == order_types[method] - - assert log_has("Disabling stoploss_on_exchange during dry-run.", caplog.record_tuples) - - assert log_has("Override strategy 'order_types' with value in config file:" - " {'buy': 'market', 'sell': 'limit', 'stoploss': 'limit'," - " 'stoploss_on_exchange': False}.", caplog.record_tuples) - - def test_strategy_override_order_tif(caplog): caplog.set_level(logging.INFO) From a225672c8776f848597546fe945bb124f3fc4782 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 11 Aug 2019 19:44:15 +0200 Subject: [PATCH 4/4] Add tests for dry-run stoposs_on_exchange --- freqtrade/tests/test_freqtradebot.py | 74 +++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 1a4c5159c..7ed918318 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -16,7 +16,7 @@ from freqtrade.data.dataprovider import DataProvider from freqtrade.freqtradebot import FreqtradeBot from freqtrade.persistence import Trade from freqtrade.rpc import RPCMessageType -from freqtrade.state import State +from freqtrade.state import State, RunMode from freqtrade.strategy.interface import SellCheckTuple, SellType from freqtrade.tests.conftest import (get_patched_freqtradebot, get_patched_worker, log_has, log_has_re, @@ -130,7 +130,77 @@ def test_throttle_with_assets(mocker, default_conf) -> None: assert result == -1 -def test_get_trade_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None: +def test_order_dict_dry_run(default_conf, mocker, caplog) -> None: + patch_RPCManager(mocker) + patch_exchange(mocker) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2) + ) + conf = default_conf.copy() + conf['runmode'] = RunMode.DRY_RUN + conf['order_types'] = { + 'buy': 'market', + 'sell': 'limit', + 'stoploss': 'limit', + 'stoploss_on_exchange': True, + } + + freqtrade = FreqtradeBot(conf) + assert log_has("Disabling stoploss_on_exchange during dry-run.", caplog.record_tuples) + assert not freqtrade.strategy.order_types['stoploss_on_exchange'] + + caplog.clear() + # is left untouched + conf = default_conf.copy() + conf['runmode'] = RunMode.DRY_RUN + conf['order_types'] = { + 'buy': 'market', + 'sell': 'limit', + 'stoploss': 'limit', + 'stoploss_on_exchange': False, + } + freqtrade = FreqtradeBot(conf) + assert not freqtrade.strategy.order_types['stoploss_on_exchange'] + assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog.record_tuples) + + +def test_order_dict_live(default_conf, mocker, caplog) -> None: + patch_RPCManager(mocker) + patch_exchange(mocker) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2) + ) + conf = default_conf.copy() + conf['runmode'] = RunMode.LIVE + conf['order_types'] = { + 'buy': 'market', + 'sell': 'limit', + 'stoploss': 'limit', + 'stoploss_on_exchange': True, + } + + freqtrade = FreqtradeBot(conf) + assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog.record_tuples) + assert freqtrade.strategy.order_types['stoploss_on_exchange'] + + caplog.clear() + # is left untouched + conf = default_conf.copy() + conf['runmode'] = RunMode.LIVE + conf['order_types'] = { + 'buy': 'market', + 'sell': 'limit', + 'stoploss': 'limit', + 'stoploss_on_exchange': False, + } + freqtrade = FreqtradeBot(conf) + assert not freqtrade.strategy.order_types['stoploss_on_exchange'] + assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog.record_tuples) + + +def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple(