From f0c0f5618b1614978b9003be6165035c7e50ef34 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Aug 2019 16:11:04 +0200 Subject: [PATCH 01/13] Abstract creating stoploss-orders from stoploss-logic --- freqtrade/freqtradebot.py | 67 +++++++++++++--------------- freqtrade/tests/test_freqtradebot.py | 2 +- 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 8857b95da..1d78f0a21 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -617,6 +617,26 @@ class FreqtradeBot(object): logger.debug('Found no sell signal for %s.', trade) return False + def create_stoploss_order(self, trade: Trade, stop_price: float, rate: float) -> bool: + """ + Abstracts creating stoploss orders from the logic. + Handles errors and updates the trade database object. + :return: True if the order succeeded, and False in case of problems. + """ + # Limit price threshold: As limit price should always be below price + LIMIT_PRICE_PCT = 0.99 + + try: + stoploss_order = self.exchange.stoploss_limit(pair=trade.pair, amount=trade.amount, + stop_price=stop_price, + rate=rate * LIMIT_PRICE_PCT) + trade.stoploss_order_id = str(stoploss_order['id']) + return True + except DependencyException: + trade.stoploss_order_id = None + logger.exception('Unable to place a stoploss order on exchange.') + return False + def handle_stoploss_on_exchange(self, trade: Trade) -> bool: """ Check if trade is fulfilled in which case the stoploss @@ -638,9 +658,6 @@ class FreqtradeBot(object): # If trade open order id does not exist: buy order is fulfilled buy_order_fulfilled = not trade.open_order_id - # Limit price threshold: As limit price should always be below price - limit_price_pct = 0.99 - # If buy order is fulfilled but there is no stoploss, we add a stoploss on exchange if (buy_order_fulfilled and not stoploss_order): if self.edge: @@ -650,34 +667,18 @@ class FreqtradeBot(object): stop_price = trade.open_rate * (1 + stoploss) - # limit price should be less than stop price. - limit_price = stop_price * limit_price_pct - - try: - 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 self.create_stoploss_order(trade=trade, stop_price=stop_price, rate=stop_price): trade.stoploss_last_update = datetime.now() return False - except DependencyException as exception: - trade.stoploss_order_id = None - logger.warning('Unable to place a stoploss order on exchange: %s', exception) - # If stoploss order is canceled for some reason we add it if stoploss_order and stoploss_order['status'] == 'canceled': - try: - stoploss_order_id = self.exchange.stoploss_limit( - pair=trade.pair, amount=trade.amount, - stop_price=trade.stop_loss, rate=trade.stop_loss * limit_price_pct - )['id'] - trade.stoploss_order_id = str(stoploss_order_id) + if self.create_stoploss_order(trade=trade, stop_price=trade.stop_loss, + rate=trade.stop_loss): return False - except DependencyException as exception: + else: trade.stoploss_order_id = None - logger.warning('Stoploss order was cancelled, ' - 'but unable to recreate one: %s', exception) + logger.warning('Stoploss order was cancelled, but unable to recreate one.') # We check if stoploss order is fulfilled if stoploss_order and stoploss_order['status'] == 'closed': @@ -720,17 +721,13 @@ class FreqtradeBot(object): 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: - trade.stoploss_order_id = None - logger.exception(f"Could not create trailing stoploss order " - f"for pair {trade.pair}.") + # Create new stoploss order + if self.create_stoploss_order(trade=trade, stop_price=trade.stop_loss, + rate=trade.stop_loss): + return False + else: + logger.warning(f"Could not create trailing stoploss order " + f"for pair {trade.pair}.") def _check_and_execute_sell(self, trade: Trade, sell_rate: float, buy: bool, sell: bool) -> bool: diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 1119157c4..7b4023065 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1153,7 +1153,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, side_effect=DependencyException() ) freqtrade.handle_stoploss_on_exchange(trade) - assert log_has('Unable to place a stoploss order on exchange: ', caplog) + assert log_has('Unable to place a stoploss order on exchange.', caplog) assert trade.stoploss_order_id is None # Fifth case: get_order returns InvalidOrder From 7fc156648a88f974d278af8be947877fb92739c8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 31 Aug 2019 16:15:39 +0200 Subject: [PATCH 02/13] simplify stoploss_oe code --- freqtrade/freqtradebot.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 1d78f0a21..881e489be 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -655,15 +655,10 @@ class FreqtradeBot(object): except InvalidOrderException as exception: logger.warning('Unable to fetch stoploss order: %s', exception) - # If trade open order id does not exist: buy order is fulfilled - buy_order_fulfilled = not trade.open_order_id - # If buy order is fulfilled but there is no stoploss, we add a stoploss on exchange - if (buy_order_fulfilled and not stoploss_order): - if self.edge: - stoploss = self.edge.stoploss(pair=trade.pair) - else: - stoploss = self.strategy.stoploss + if (not trade.open_order_id and not stoploss_order): + + stoploss = self.edge.stoploss(pair=trade.pair) if self.edge else self.strategy.stoploss stop_price = trade.open_rate * (1 + stoploss) From ee808abfea45c594334e8c14540a2842df520073 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 09:07:09 +0200 Subject: [PATCH 03/13] Add emergency_sell as sell reason --- config_full.json.example | 1 + docs/configuration.md | 3 +++ freqtrade/constants.py | 1 + freqtrade/strategy/interface.py | 1 + 4 files changed, 6 insertions(+) diff --git a/config_full.json.example b/config_full.json.example index b6451859c..957967042 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -38,6 +38,7 @@ "order_types": { "buy": "limit", "sell": "limit", + "emergencysell": "market", "stoploss": "market", "stoploss_on_exchange": false, "stoploss_on_exchange_interval": 60 diff --git a/docs/configuration.md b/docs/configuration.md index fcd6c2bf6..dc4c365f6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -205,6 +205,7 @@ Values set in the configuration file overwrites values set in the strategy. If this is configured, all 4 values (`buy`, `sell`, `stoploss` and `stoploss_on_exchange`) need to be present, otherwise the bot will warn about it and fail to start. +`emergencysell` is an optional value, which defaults to `market` and is used when creating stoploss on exchange orders fails. The below is the default which is used if this is not configured in either strategy or configuration file. Syntax for Strategy: @@ -213,6 +214,7 @@ Syntax for Strategy: order_types = { "buy": "limit", "sell": "limit", + "emergencysell": "market", "stoploss": "market", "stoploss_on_exchange": False, "stoploss_on_exchange_interval": 60 @@ -225,6 +227,7 @@ Configuration: "order_types": { "buy": "limit", "sell": "limit", + "emergencysell": "market", "stoploss": "market", "stoploss_on_exchange": false, "stoploss_on_exchange_interval": 60 diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 05ee99c1b..d7ace131c 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -121,6 +121,7 @@ CONF_SCHEMA = { 'properties': { 'buy': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, 'sell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, + 'emergencysell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, 'stoploss': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}, 'stoploss_on_exchange': {'type': 'boolean'}, 'stoploss_on_exchange_interval': {'type': 'number'} diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 3f2478cc0..6c6593a53 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -39,6 +39,7 @@ class SellType(Enum): TRAILING_STOP_LOSS = "trailing_stop_loss" SELL_SIGNAL = "sell_signal" FORCE_SELL = "force_sell" + EMERGENCY_SELL = "emergency_sell" NONE = "" From 9f53e9f5dde06f2edcd9eb8fa9399daaa3e96f8b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 09:08:35 +0200 Subject: [PATCH 04/13] Raise InvalidOrder error when stoploss-creation fails --- freqtrade/exchange/binance.py | 7 +++++-- freqtrade/freqtradebot.py | 6 ++++++ freqtrade/tests/exchange/test_binance.py | 8 +++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 5834f26cd..c3021c50b 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -4,7 +4,8 @@ from typing import Dict import ccxt -from freqtrade import DependencyException, OperationalException, TemporaryError +from freqtrade import (DependencyException, InvalidOrderException, + OperationalException, TemporaryError) from freqtrade.exchange import Exchange logger = logging.getLogger(__name__) @@ -69,7 +70,9 @@ class Binance(Exchange): f'Tried to sell amount {amount} at rate {rate}.' f'Message: {e}') from e except ccxt.InvalidOrder as e: - raise DependencyException( + # Errors: + # `binance Order would trigger immediately.` + raise InvalidOrderException( f'Could not create {ordertype} sell order on market {pair}. ' f'Tried to sell amount {amount} at rate {rate}.' f'Message: {e}') from e diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 881e489be..ebc1fd4e8 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -632,6 +632,12 @@ class FreqtradeBot(object): rate=rate * LIMIT_PRICE_PCT) trade.stoploss_order_id = str(stoploss_order['id']) return True + except InvalidOrderException: + trade.stoploss_order_id = None + logger.exception('Unable to place a stoploss order on exchange.') + logger.warning('Selling the trade forcefully') + self.execute_sell(trade, trade.stop_loss, sell_reason=SellType.EMERGENCY_SELL) + except DependencyException: trade.stoploss_order_id = None logger.exception('Unable to place a stoploss order on exchange.') diff --git a/freqtrade/tests/exchange/test_binance.py b/freqtrade/tests/exchange/test_binance.py index 4afb7fcc4..6518c8523 100644 --- a/freqtrade/tests/exchange/test_binance.py +++ b/freqtrade/tests/exchange/test_binance.py @@ -4,7 +4,8 @@ from unittest.mock import MagicMock import ccxt import pytest -from freqtrade import DependencyException, OperationalException, TemporaryError +from freqtrade import (DependencyException, InvalidOrderException, + OperationalException, TemporaryError) from freqtrade.tests.conftest import get_patched_exchange @@ -49,8 +50,9 @@ def test_stoploss_limit_order(default_conf, mocker): exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) - with pytest.raises(DependencyException): - api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) + with pytest.raises(InvalidOrderException): + api_mock.create_order = MagicMock( + side_effect=ccxt.InvalidOrder("binance Order would trigger immediately.")) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') exchange.stoploss_limit(pair='ETH/BTC', amount=1, stop_price=220, rate=200) From 292df115e86b144db713566fea31b57f2d572f68 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 09:09:07 +0200 Subject: [PATCH 05/13] Support selling via emergencysell --- freqtrade/freqtradebot.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index ebc1fd4e8..cd0ea289a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -881,9 +881,14 @@ class FreqtradeBot(object): except InvalidOrderException: logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}") + ordertype = self.strategy.order_types[sell_type] + if sell_reason == SellType.EMERGENCY_SELL: + # Emergencysells (default to market!) + ordertype = self.strategy.order_types.get("emergencysell", "market") + # Execute sell and update trade record order = self.exchange.sell(pair=str(trade.pair), - ordertype=self.strategy.order_types[sell_type], + ordertype=ordertype, amount=trade.amount, rate=limit, time_in_force=self.strategy.order_time_in_force['sell'] ) From 7c0a49a6f9c04418f0b8f94fc8cf7cf67e6377ef Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 09:17:58 +0200 Subject: [PATCH 06/13] _notify_sell needs ordertype seperately --- freqtrade/freqtradebot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index cd0ea289a..6fe2c7f15 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -688,7 +688,7 @@ class FreqtradeBot(object): # Lock pair for one candle to prevent immediate rebuys self.strategy.lock_pair(trade.pair, timeframe_to_next_date(self.config['ticker_interval'])) - self._notify_sell(trade) + self._notify_sell(trade, "stoploss") return True # Finally we check if stoploss on exchange should be moved up because of trailing. @@ -904,9 +904,9 @@ class FreqtradeBot(object): # Lock pair for one candle to prevent immediate rebuys self.strategy.lock_pair(trade.pair, timeframe_to_next_date(self.config['ticker_interval'])) - self._notify_sell(trade) + self._notify_sell(trade, ordertype) - def _notify_sell(self, trade: Trade): + def _notify_sell(self, trade: Trade, order_type: str): """ Sends rpc notification when a sell occured. """ @@ -923,7 +923,7 @@ class FreqtradeBot(object): 'pair': trade.pair, 'gain': gain, 'limit': trade.close_rate_requested, - 'order_type': self.strategy.order_types['sell'], + 'order_type': order_type, 'amount': trade.amount, 'open_rate': trade.open_rate, 'current_rate': current_rate, From 6aab3fe25acab7998eeed07ff7ab58620c5e409a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 09:18:15 +0200 Subject: [PATCH 07/13] Add test for stoploss order handling behaviour --- freqtrade/tests/test_freqtradebot.py | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 7b4023065..e4e5104d0 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1201,6 +1201,50 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, assert trade.is_open is True +def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, + markets, limit_buy_order, limit_sell_order): + rpc_mock = patch_RPCManager(mocker) + patch_exchange(mocker) + sell_mock = MagicMock(return_value={'id': limit_sell_order['id']}) + 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=sell_mock, + get_fee=fee, + markets=PropertyMock(return_value=markets), + get_order=MagicMock(return_value={'status': 'canceled'}), + stoploss_limit=MagicMock(side_effect=InvalidOrderException()), + ) + freqtrade = FreqtradeBot(default_conf) + patch_get_signal(freqtrade) + freqtrade.strategy.order_types['stoploss_on_exchange'] = True + + freqtrade.create_trades() + trade = Trade.query.first() + caplog.clear() + freqtrade.create_stoploss_order(trade, 200, 199) + assert trade.stoploss_order_id is None + assert trade.sell_reason == SellType.EMERGENCY_SELL.value + assert log_has("Unable to place a stoploss order on exchange.", caplog) + assert log_has("Selling the trade forcefully", caplog) + + # Should call a market sell + assert sell_mock.call_count == 1 + assert sell_mock.call_args[1]['ordertype'] == 'market' + assert sell_mock.call_args[1]['pair'] == trade.pair + assert sell_mock.call_args[1]['amount'] == trade.amount + + # Rpc is sending first buy, then sell + assert rpc_mock.call_count == 2 + assert rpc_mock.call_args_list[1][0][0]['sell_reason'] == SellType.EMERGENCY_SELL.value + assert rpc_mock.call_args_list[1][0][0]['order_type'] == 'market' + + 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 From 9d7ebc65e734a7cbcf14e54fe8dc458f2137c97f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 09:21:45 +0200 Subject: [PATCH 08/13] Move return statement to correct intend --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 6fe2c7f15..ab3a1df28 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -641,7 +641,7 @@ class FreqtradeBot(object): except DependencyException: trade.stoploss_order_id = None logger.exception('Unable to place a stoploss order on exchange.') - return False + return False def handle_stoploss_on_exchange(self, trade: Trade) -> bool: """ From 514860ac3b5700f33b3e786893e916761a47541f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 10:17:02 +0200 Subject: [PATCH 09/13] Improve documentation --- docs/configuration.md | 27 +++++++++++++++------------ docs/strategy-customization.md | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index dc4c365f6..1f64da306 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -192,19 +192,20 @@ end up paying more then would probably have been necessary. ### Understand order_types -The `order_types` configuration parameter 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 +The `order_types` configuration parameter maps actions (`buy`, `sell`, `stoploss`) to order-types (`market`, `limit`, ...) as well as configures stoploss to be on the exchange 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 file or in the strategy. -Values set in the configuration file overwrites values set in the strategy. +both set, then the bot will use `stoploss_on_exchange_interval` to check the stoploss periodically +and update it if necessary (e.g. in case of trailing stoploss). +`order_types` can be set in the configuration file or in the strategy. +`order_types` set in the configuration file overwrites values set in the strategy as a whole, so you need to configure the whole `order_types` dictionary in one place. + +If this is configured, the following 4 values (`buy`, `sell`, `stoploss` and +`stoploss_on_exchange`) need to be present, otherwise the bot 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 will warn about it and fail to start. `emergencysell` is an optional value, which defaults to `market` and is used when creating stoploss on exchange orders fails. The below is the default which is used if this is not configured in either strategy or configuration file. @@ -242,11 +243,13 @@ Configuration: !!! Note Stoploss on exchange interval is not mandatory. Do not change its value if you are unsure of what you are doing. For more information about how stoploss works please - read [the stoploss documentation](stoploss.md). + refer to [the stoploss documentation](stoploss.md). !!! Note - In case of stoploss on exchange if the stoploss is cancelled manually then - the bot would recreate one. + If `stoploss_on_exchange` is enabled and the stoploss is cancelled manually on the exchange, then the bot wil create a new order. + +!!! Warning stoploss_on_exchange failures + If stoploss on exchange creation fails for some reason, then an "emergency sell" is initiated. By default, this will sell the asset using a market order. The order-type for the emergency-sell can be changed by setting `emergencysell` - however this is not advised. ### Understand order_time_in_force diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index c7da19659..e07585800 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -224,7 +224,7 @@ This would signify a stoploss of -10%. For the full documentation on stoploss features, look at the dedicated [stoploss page](stoploss.md). -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_types dictionary, so your stoploss is on the exchange and cannot be missed due to network-problems (or other problems). For more information on order_types please look [here](configuration.md#understand-order_types). From f91557f549f21e56136c358b782e759a1f4233cd Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 10:17:17 +0200 Subject: [PATCH 10/13] Add space to exception message --- freqtrade/exchange/binance.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index c3021c50b..14f409659 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -67,14 +67,14 @@ class Binance(Exchange): except ccxt.InsufficientFunds as e: raise DependencyException( f'Insufficient funds to create {ordertype} sell order on market {pair}.' - f'Tried to sell amount {amount} at rate {rate}.' + f'Tried to sell amount {amount} at rate {rate}. ' f'Message: {e}') from e except ccxt.InvalidOrder as e: # Errors: # `binance Order would trigger immediately.` raise InvalidOrderException( f'Could not create {ordertype} sell order on market {pair}. ' - f'Tried to sell amount {amount} at rate {rate}.' + f'Tried to sell amount {amount} at rate {rate}. ' f'Message: {e}') from e except (ccxt.NetworkError, ccxt.ExchangeError) as e: raise TemporaryError( From 771519e311170ca82de985c871a0f8f7db6870d1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 10:17:36 +0200 Subject: [PATCH 11/13] Don't show stacktrace in case of invalidorder Error This is handled gracefully by emergency-selling --- freqtrade/freqtradebot.py | 4 ++-- freqtrade/tests/test_freqtradebot.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index ab3a1df28..7c403a29b 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -632,9 +632,9 @@ class FreqtradeBot(object): rate=rate * LIMIT_PRICE_PCT) trade.stoploss_order_id = str(stoploss_order['id']) return True - except InvalidOrderException: + except InvalidOrderException as e: trade.stoploss_order_id = None - logger.exception('Unable to place a stoploss order on exchange.') + logger.error(f'Unable to place a stoploss order on exchange. {e}') logger.warning('Selling the trade forcefully') self.execute_sell(trade, trade.stop_loss, sell_reason=SellType.EMERGENCY_SELL) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index e4e5104d0..af69600b8 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1230,7 +1230,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, freqtrade.create_stoploss_order(trade, 200, 199) assert trade.stoploss_order_id is None assert trade.sell_reason == SellType.EMERGENCY_SELL.value - assert log_has("Unable to place a stoploss order on exchange.", caplog) + assert log_has("Unable to place a stoploss order on exchange. ", caplog) assert log_has("Selling the trade forcefully", caplog) # Should call a market sell From 20c9c93b3e197fe9fcfdce1e09599d163e1e4ba1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 1 Sep 2019 10:25:05 +0200 Subject: [PATCH 12/13] Improve docstring --- freqtrade/freqtradebot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7c403a29b..53f052f76 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -621,6 +621,7 @@ class FreqtradeBot(object): """ Abstracts creating stoploss orders from the logic. Handles errors and updates the trade database object. + Force-sells the pair (using EmergencySell reason) in case of Problems creating the order. :return: True if the order succeeded, and False in case of problems. """ # Limit price threshold: As limit price should always be below price From 88f823f899dd3d4f5ed1ff05c3f647d3390af004 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Sep 2019 06:56:25 +0200 Subject: [PATCH 13/13] Improvements to documentation --- docs/configuration.md | 13 ++++++------- docs/strategy-customization.md | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 1f64da306..a1c0d1a75 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -192,14 +192,13 @@ end up paying more then would probably have been necessary. ### Understand order_types -The `order_types` configuration parameter maps actions (`buy`, `sell`, `stoploss`) to order-types (`market`, `limit`, ...) as well as configures stoploss to be on the exchange and stoploss on exchange update interval in seconds. +The `order_types` configuration parameter maps actions (`buy`, `sell`, `stoploss`) to order-types (`market`, `limit`, ...) as well as configures stoploss to be on the exchange and defines 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 +limit-orders, and create stoplosses using using market orders. 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 the stoploss periodically -and update it if necessary (e.g. in case of trailing stoploss). +the buy order is fulfilled. +If `stoploss_on_exchange` and `trailing_stop` are both set, then the bot will use `stoploss_on_exchange_interval` to check and update the stoploss on exchange periodically. `order_types` can be set in the configuration file or in the strategy. `order_types` set in the configuration file overwrites values set in the strategy as a whole, so you need to configure the whole `order_types` dictionary in one place. @@ -246,10 +245,10 @@ Configuration: refer to [the stoploss documentation](stoploss.md). !!! Note - If `stoploss_on_exchange` is enabled and the stoploss is cancelled manually on the exchange, then the bot wil create a new order. + If `stoploss_on_exchange` is enabled and the stoploss is cancelled manually on the exchange, then the bot will create a new order. !!! Warning stoploss_on_exchange failures - If stoploss on exchange creation fails for some reason, then an "emergency sell" is initiated. By default, this will sell the asset using a market order. The order-type for the emergency-sell can be changed by setting `emergencysell` - however this is not advised. + If stoploss on exchange creation fails for some reason, then an "emergency sell" is initiated. By default, this will sell the asset using a market order. The order-type for the emergency-sell can be changed by setting the `emergencysell` value in the `order_types` dictionary - however this is not advised. ### Understand order_time_in_force diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index e07585800..85cab987a 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -224,7 +224,7 @@ This would signify a stoploss of -10%. For the full documentation on stoploss features, look at the dedicated [stoploss page](stoploss.md). -If your exchange supports it, it's recommended to also set `"stoploss_on_exchange"` in the order_types dictionary, so your stoploss is on the exchange and cannot be missed due to network-problems (or other problems). +If your exchange supports it, it's recommended to also set `"stoploss_on_exchange"` in the order_types dictionary, so your stoploss is on the exchange and cannot be missed due to network problems, high load or other reasons. For more information on order_types please look [here](configuration.md#understand-order_types).