From 61182f849bd252b5b38a91c48fe29b25e7f01ebb Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 9 Mar 2022 00:45:10 -0600 Subject: [PATCH 01/28] exchange.fetch_order and exchange.cancel_order added params argument --- freqtrade/exchange/exchange.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index da89a7c8a..81d20fd21 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -376,7 +376,7 @@ class Exchange: raise OperationalException( 'Could not load markets, therefore cannot start. ' 'Please investigate the above error for more details.' - ) + ) quote_currencies = self.get_quote_currencies() if stake_currency not in quote_currencies: raise OperationalException( @@ -882,11 +882,11 @@ class Exchange: raise OperationalException(e) from e @retrier(retries=API_FETCH_ORDER_RETRY_COUNT) - def fetch_order(self, order_id: str, pair: str) -> Dict: + def fetch_order(self, order_id: str, pair: str, params={}) -> Dict: if self._config['dry_run']: return self.fetch_dry_run_order(order_id) try: - order = self._api.fetch_order(order_id, pair) + order = self._api.fetch_order(order_id, pair, params=params) self._log_exchange_response('fetch_order', order) return order except ccxt.OrderNotFound as e: @@ -929,7 +929,7 @@ class Exchange: and order.get('filled') == 0.0) @retrier - def cancel_order(self, order_id: str, pair: str) -> Dict: + def cancel_order(self, order_id: str, pair: str, params={}) -> Dict: if self._config['dry_run']: try: order = self.fetch_dry_run_order(order_id) @@ -940,7 +940,7 @@ class Exchange: return {} try: - order = self._api.cancel_order(order_id, pair) + order = self._api.cancel_order(order_id, pair, params=params) self._log_exchange_response('cancel_order', order) return order except ccxt.InvalidOrder as e: From e3ced55f5c2979c30d3ad849ef6e20a000bd12da Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 9 Mar 2022 00:45:50 -0600 Subject: [PATCH 02/28] gateio.fetch_order and gateio.cancel_order --- freqtrade/exchange/gateio.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index 7e1f21921..7580281cf 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -31,4 +31,18 @@ class Gateio(Exchange): if any(v == 'market' for k, v in order_types.items()): raise OperationalException( - f'Exchange {self.name} does not support market orders.') + f'Exchange {self.name} does not support market orders.') + + def fetch_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict: + return self.fetch_order( + order_id=order_id, + pair=pair, + params={'stop': True} + ) + + def cancel_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict: + return self.cancel_order( + order_id=order_id, + pair=pair, + params={'stop': True} + ) From ae4742afcb78d58d30ec88125cdb526204780a62 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 9 Mar 2022 00:59:28 -0600 Subject: [PATCH 03/28] test_fetch_stoploss_order_gateio and test_cancel_stoploss_order_gateio --- tests/exchange/test_gateio.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/exchange/test_gateio.py b/tests/exchange/test_gateio.py index 6f7862909..a3ffcad65 100644 --- a/tests/exchange/test_gateio.py +++ b/tests/exchange/test_gateio.py @@ -1,8 +1,11 @@ +from unittest.mock import MagicMock + import pytest from freqtrade.exceptions import OperationalException from freqtrade.exchange import Gateio from freqtrade.resolvers.exchange_resolver import ExchangeResolver +from tests.conftest import get_patched_exchange def test_validate_order_types_gateio(default_conf, mocker): @@ -26,3 +29,29 @@ def test_validate_order_types_gateio(default_conf, mocker): with pytest.raises(OperationalException, match=r'Exchange .* does not support market orders.'): ExchangeResolver.load_exchange('gateio', default_conf, True) + + +def test_fetch_stoploss_order_gateio(default_conf, mocker): + exchange = get_patched_exchange(mocker, default_conf, id='gateio') + + fetch_order_mock = MagicMock() + exchange.fetch_order = fetch_order_mock + + exchange.fetch_stoploss_order('1234', 'ETH/BTC') + assert fetch_order_mock.call_count == 1 + assert fetch_order_mock.call_args_list[0][1]['order_id'] == '1234' + assert fetch_order_mock.call_args_list[0][1]['pair'] == 'ETH/BTC' + assert fetch_order_mock.call_args_list[0][1]['params'] == {'stop': True} + + +def test_cancel_stoploss_order_gateio(default_conf, mocker): + exchange = get_patched_exchange(mocker, default_conf, id='gateio') + + cancel_order_mock = MagicMock() + exchange.cancel_order = cancel_order_mock + + exchange.cancel_stoploss_order('1234', 'ETH/BTC') + assert cancel_order_mock.call_count == 1 + assert cancel_order_mock.call_args_list[0][1]['order_id'] == '1234' + assert cancel_order_mock.call_args_list[0][1]['pair'] == 'ETH/BTC' + assert cancel_order_mock.call_args_list[0][1]['params'] == {'stop': True} From d47274066ecdf09e5364773efd32853e2a763d7c Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 9 Mar 2022 01:05:21 -0600 Subject: [PATCH 04/28] Added stoploss_on_exchange flag to gateio --- freqtrade/exchange/gateio.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index 7580281cf..9f857c344 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -22,6 +22,7 @@ class Gateio(Exchange): _ft_has: Dict = { "ohlcv_candle_limit": 1000, "ohlcv_volume_currency": "quote", + "stoploss_on_exchange": True, } _headers = {'X-Gate-Channel-Id': 'freqtrade'} From 6bb93bdc25d3a00c97aca3eb2ab6a8355f1ad268 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 9 Mar 2022 15:47:16 -0600 Subject: [PATCH 05/28] moved binance.stoploss_adjust to exchange class --- freqtrade/exchange/binance.py | 7 ------- freqtrade/exchange/exchange.py | 2 +- tests/exchange/test_exchange.py | 12 ++++++++++++ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 37ead6dd8..ecdd002b2 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -23,13 +23,6 @@ class Binance(Exchange): "l2_limit_range": [5, 10, 20, 50, 100, 500, 1000], } - def stoploss_adjust(self, stop_loss: float, order: Dict) -> bool: - """ - Verify stop_loss against stoploss-order value (limit or price) - Returns True if adjustment is necessary. - """ - return order['type'] == 'stop_loss_limit' and stop_loss > float(order['info']['stopPrice']) - async def _async_get_historic_ohlcv(self, pair: str, timeframe: str, since_ms: int, is_new_pair: bool = False, raise_: bool = False diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 81d20fd21..8db261f63 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -795,7 +795,7 @@ class Exchange: Verify stop_loss against stoploss-order value (limit or price) Returns True if adjustment is necessary. """ - raise OperationalException(f"stoploss is not implemented for {self.name}.") + return stop_loss > float(order['stopPrice']) def _get_stop_params(self, ordertype: str, stop_price: float) -> Dict: params = self._params.copy() diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 527e8050b..1b33e836b 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -3058,3 +3058,15 @@ def test_calculate_fee_rate(mocker, default_conf, order, expected, unknown_fee_r ]) def test_calculate_backoff(retrycount, max_retries, expected): assert calculate_backoff(retrycount, max_retries) == expected + + +@pytest.mark.parametrize('exchange_name', ['binance', 'gateio']) +def test_stoploss_adjust(mocker, default_conf, exchange_name): + exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) + order = { + 'type': 'stop_loss_limit', + 'price': 1500, + 'stopPrice': 1500, + } + assert exchange.stoploss_adjust(1501, order) + assert not exchange.stoploss_adjust(1499, order) From 7db28b1b1697b937af0106668250670a1c22ca94 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 9 Mar 2022 15:54:17 -0600 Subject: [PATCH 06/28] gateio stoploss docs --- docs/exchanges.md | 3 +++ docs/stoploss.md | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/exchanges.md b/docs/exchanges.md index 8adf19081..df11a5971 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -210,6 +210,9 @@ OKX requires a passphrase for each api key, you will therefore need to add this ## Gate.io +!!! Tip "Stoploss on Exchange" + Binance supports `stoploss_on_exchange` and uses `stop-loss-limit` orders. It provides great advantages, so we recommend to benefit from it by enabling stoploss on exchange.. + Gate.io allows the use of `POINT` to pay for fees. As this is not a tradable currency (no regular market available), automatic fee calculations will fail (and default to a fee of 0). The configuration parameter `exchange.unknown_fee_rate` can be used to specify the exchange rate between Point and the stake currency. Obviously, changing the stake-currency will also require changes to this value. diff --git a/docs/stoploss.md b/docs/stoploss.md index d0e106d8f..62081b540 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -24,7 +24,7 @@ These modes can be configured with these values: ``` !!! Note - Stoploss on exchange is only supported for Binance (stop-loss-limit), Huobi (stop-limit), Kraken (stop-loss-market, stop-loss-limit), FTX (stop limit and stop-market) and kucoin (stop-limit and stop-market) as of now. + Stoploss on exchange is only supported for Binance (stop-loss-limit), Huobi (stop-limit), Kraken (stop-loss-market, stop-loss-limit), FTX (stop limit and stop-market) Gateio (stop-limit), and Kucoin (stop-limit and stop-market) as of now. Do not set too low/tight stoploss value if using stop loss on exchange! If set to low/tight then you have greater risk of missing fill on the order and stoploss will not work. From 6f4d6079023fc75987f7c08087ea4d9e16020387 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Wed, 9 Mar 2022 19:31:51 -0600 Subject: [PATCH 07/28] stoploss_adjust fixed breaking tests --- tests/exchange/test_binance.py | 14 -------------- tests/exchange/test_exchange.py | 3 --- tests/test_freqtradebot.py | 4 +--- 3 files changed, 1 insertion(+), 20 deletions(-) diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index d88ae9b1d..bf9340217 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -94,20 +94,6 @@ def test_stoploss_order_dry_run_binance(default_conf, mocker): assert order['amount'] == 1 -def test_stoploss_adjust_binance(mocker, default_conf): - exchange = get_patched_exchange(mocker, default_conf, id='binance') - order = { - 'type': 'stop_loss_limit', - 'price': 1500, - 'info': {'stopPrice': 1500}, - } - assert exchange.stoploss_adjust(1501, order) - assert not exchange.stoploss_adjust(1499, order) - # Test with invalid order case - order['type'] = 'stop_loss' - assert not exchange.stoploss_adjust(1501, order) - - @pytest.mark.asyncio async def test__async_get_historic_ohlcv_binance(default_conf, mocker, caplog): ohlcv = [ diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 1b33e836b..fa9020ba4 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -2698,9 +2698,6 @@ def test_stoploss_order_unsupported_exchange(default_conf, mocker): with pytest.raises(OperationalException, match=r"stoploss is not implemented .*"): exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}) - with pytest.raises(OperationalException, match=r"stoploss is not implemented .*"): - exchange.stoploss_adjust(1, {}) - def test_merge_ft_has_dict(default_conf, mocker): mocker.patch.multiple('freqtrade.exchange.Exchange', diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index e5f8d3694..492e55715 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1563,9 +1563,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, 'type': 'stop_loss_limit', 'price': 3, 'average': 2, - 'info': { - 'stopPrice': '2.178' - } + 'stopPrice': '2.178' }) mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', stoploss_order_hanging) From f343036e6695be138d07be3ca8e59c507379b8c5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 12 Mar 2022 19:23:20 +0100 Subject: [PATCH 08/28] Add stoploss-ordertypes mapping for gateio --- freqtrade/exchange/gateio.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index 9f857c344..ca57f85b3 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -22,6 +22,7 @@ class Gateio(Exchange): _ft_has: Dict = { "ohlcv_candle_limit": 1000, "ohlcv_volume_currency": "quote", + "stoploss_order_types": {"limit": "limit"}, "stoploss_on_exchange": True, } From 2ba79a32a02abb5a08b3fa3c18a296e6b3cb43ae Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 12 Mar 2022 19:42:40 -0600 Subject: [PATCH 09/28] Update docs/exchanges.md Co-authored-by: Matthias --- docs/exchanges.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/exchanges.md b/docs/exchanges.md index df11a5971..c2368170d 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -211,7 +211,7 @@ OKX requires a passphrase for each api key, you will therefore need to add this ## Gate.io !!! Tip "Stoploss on Exchange" - Binance supports `stoploss_on_exchange` and uses `stop-loss-limit` orders. It provides great advantages, so we recommend to benefit from it by enabling stoploss on exchange.. + Gate.io supports `stoploss_on_exchange` and uses `stop-loss-limit` orders. It provides great advantages, so we recommend to benefit from it by enabling stoploss on exchange.. Gate.io allows the use of `POINT` to pay for fees. As this is not a tradable currency (no regular market available), automatic fee calculations will fail (and default to a fee of 0). The configuration parameter `exchange.unknown_fee_rate` can be used to specify the exchange rate between Point and the stake currency. Obviously, changing the stake-currency will also require changes to this value. From 7e7e5963724e56ed052cb607df2cf4c145867eb0 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 12 Mar 2022 20:07:50 -0600 Subject: [PATCH 10/28] Revert "moved binance.stoploss_adjust to exchange class" This reverts commit 6bb93bdc25d3a00c97aca3eb2ab6a8355f1ad268. --- freqtrade/exchange/binance.py | 7 +++++++ freqtrade/exchange/exchange.py | 2 +- tests/exchange/test_exchange.py | 12 ------------ 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index ecdd002b2..37ead6dd8 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -23,6 +23,13 @@ class Binance(Exchange): "l2_limit_range": [5, 10, 20, 50, 100, 500, 1000], } + def stoploss_adjust(self, stop_loss: float, order: Dict) -> bool: + """ + Verify stop_loss against stoploss-order value (limit or price) + Returns True if adjustment is necessary. + """ + return order['type'] == 'stop_loss_limit' and stop_loss > float(order['info']['stopPrice']) + async def _async_get_historic_ohlcv(self, pair: str, timeframe: str, since_ms: int, is_new_pair: bool = False, raise_: bool = False diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 211531c34..a502ad034 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -795,7 +795,7 @@ class Exchange: Verify stop_loss against stoploss-order value (limit or price) Returns True if adjustment is necessary. """ - return stop_loss > float(order['stopPrice']) + raise OperationalException(f"stoploss is not implemented for {self.name}.") def _get_stop_params(self, ordertype: str, stop_price: float) -> Dict: params = self._params.copy() diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index db4d66fa5..b4b19a6d4 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -3062,15 +3062,3 @@ def test_calculate_fee_rate(mocker, default_conf, order, expected, unknown_fee_r ]) def test_calculate_backoff(retrycount, max_retries, expected): assert calculate_backoff(retrycount, max_retries) == expected - - -@pytest.mark.parametrize('exchange_name', ['binance', 'gateio']) -def test_stoploss_adjust(mocker, default_conf, exchange_name): - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - order = { - 'type': 'stop_loss_limit', - 'price': 1500, - 'stopPrice': 1500, - } - assert exchange.stoploss_adjust(1501, order) - assert not exchange.stoploss_adjust(1499, order) From 91549d3254a481bc187865b4d48d4406cc68013b Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 12 Mar 2022 20:07:56 -0600 Subject: [PATCH 11/28] Revert "stoploss_adjust fixed breaking tests" This reverts commit 6f4d6079023fc75987f7c08087ea4d9e16020387. --- tests/exchange/test_binance.py | 14 ++++++++++++++ tests/exchange/test_exchange.py | 3 +++ tests/test_freqtradebot.py | 4 +++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index bf9340217..d88ae9b1d 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -94,6 +94,20 @@ def test_stoploss_order_dry_run_binance(default_conf, mocker): assert order['amount'] == 1 +def test_stoploss_adjust_binance(mocker, default_conf): + exchange = get_patched_exchange(mocker, default_conf, id='binance') + order = { + 'type': 'stop_loss_limit', + 'price': 1500, + 'info': {'stopPrice': 1500}, + } + assert exchange.stoploss_adjust(1501, order) + assert not exchange.stoploss_adjust(1499, order) + # Test with invalid order case + order['type'] = 'stop_loss' + assert not exchange.stoploss_adjust(1501, order) + + @pytest.mark.asyncio async def test__async_get_historic_ohlcv_binance(default_conf, mocker, caplog): ohlcv = [ diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index b4b19a6d4..ff8383997 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -2705,6 +2705,9 @@ def test_stoploss_order_unsupported_exchange(default_conf, mocker): with pytest.raises(OperationalException, match=r"stoploss is not implemented .*"): exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}) + with pytest.raises(OperationalException, match=r"stoploss is not implemented .*"): + exchange.stoploss_adjust(1, {}) + def test_merge_ft_has_dict(default_conf, mocker): mocker.patch.multiple('freqtrade.exchange.Exchange', diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index caa9a824f..1aeb56cdd 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1588,7 +1588,9 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, 'type': 'stop_loss_limit', 'price': 3, 'average': 2, - 'stopPrice': '2.178' + 'info': { + 'stopPrice': '2.178' + } }) mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', stoploss_order_hanging) From 843606c9cbd4563beba7fbf405f03ddc012508c1 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Sat, 12 Mar 2022 20:14:23 -0600 Subject: [PATCH 12/28] gateio stoploss adjust --- freqtrade/exchange/gateio.py | 7 +++++++ tests/exchange/test_gateio.py | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index ca57f85b3..bfe996e86 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -48,3 +48,10 @@ class Gateio(Exchange): pair=pair, params={'stop': True} ) + + def stoploss_adjust(self, stop_loss: float, order: Dict) -> bool: + """ + Verify stop_loss against stoploss-order value (limit or price) + Returns True if adjustment is necessary. + """ + return stop_loss > float(order['stopPrice']) diff --git a/tests/exchange/test_gateio.py b/tests/exchange/test_gateio.py index a3ffcad65..ce356be8c 100644 --- a/tests/exchange/test_gateio.py +++ b/tests/exchange/test_gateio.py @@ -55,3 +55,13 @@ def test_cancel_stoploss_order_gateio(default_conf, mocker): assert cancel_order_mock.call_args_list[0][1]['order_id'] == '1234' assert cancel_order_mock.call_args_list[0][1]['pair'] == 'ETH/BTC' assert cancel_order_mock.call_args_list[0][1]['params'] == {'stop': True} + + +def test_stoploss_adjust_gateio(mocker, default_conf): + exchange = get_patched_exchange(mocker, default_conf, id='gateio') + order = { + 'price': 1500, + 'stopPrice': 1500, + } + assert exchange.stoploss_adjust(1501, order) + assert not exchange.stoploss_adjust(1499, order) From 32c06f4a05c4c804f98be8a923f8fda7f53f13b8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 16:45:11 +0100 Subject: [PATCH 13/28] Improve test --- tests/test_periodiccache.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_periodiccache.py b/tests/test_periodiccache.py index f874f9041..b2bd8ba2b 100644 --- a/tests/test_periodiccache.py +++ b/tests/test_periodiccache.py @@ -26,7 +26,9 @@ def test_ttl_cache(): assert 'a' in cache1h t.move_to("2021-09-01 05:59:59 +00:00") + assert 'a' not in cache assert 'a' in cache1h t.move_to("2021-09-01 06:00:00 +00:00") + assert 'a' not in cache assert 'a' not in cache1h From c63b5fbbbf22807526da4eb34411287eeb415be9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Mar 2022 17:53:52 +0100 Subject: [PATCH 14/28] Use last to get rates for /balance endpoints --- freqtrade/rpc/rpc.py | 2 +- tests/rpc/test_rpc.py | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 7a602978e..3d4fffbc9 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -582,7 +582,7 @@ class RPC: else: try: pair = self._freqtrade.exchange.get_valid_pair_combination(coin, stake_currency) - rate = tickers.get(pair, {}).get('bid', None) + rate = tickers.get(pair, {}).get('last', None) if rate: if pair.startswith(stake_currency) and not pair.endswith(stake_currency): rate = 1.0 / rate diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 6bfee8e86..d738760be 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -605,8 +605,8 @@ def test_rpc_balance_handle(default_conf, mocker, tickers): rpc._fiat_converter = CryptoToFiatConverter() result = rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency']) - assert prec_satoshi(result['total'], 12.309096315) - assert prec_satoshi(result['value'], 184636.44472997) + assert prec_satoshi(result['total'], 12.30909624) + assert prec_satoshi(result['value'], 184636.443606915) assert tickers.call_count == 1 assert tickers.call_args_list[0][1]['cached'] is True assert 'USD' == result['symbol'] @@ -624,17 +624,16 @@ def test_rpc_balance_handle(default_conf, mocker, tickers): 'est_stake': 0.30794, 'used': 4.0, 'stake': 'BTC', - }, {'free': 5.0, 'balance': 10.0, 'currency': 'USDT', - 'est_stake': 0.0011563153318162476, + 'est_stake': 0.0011562404610161968, 'used': 5.0, 'stake': 'BTC', } ] - assert result['total'] == 12.309096315331816 + assert result['total'] == 12.309096240461017 def test_rpc_start(mocker, default_conf) -> None: From be5b0acfbda471a966b967cea1a6c6c213641cbf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 03:01:00 +0000 Subject: [PATCH 15/28] Bump pytest from 7.0.1 to 7.1.0 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.0.1 to 7.1.0. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.0.1...7.1.0) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 5c94ac7c8..1d393538b 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,7 +7,7 @@ coveralls==3.3.1 flake8==4.0.1 flake8-tidy-imports==4.6.0 mypy==0.931 -pytest==7.0.1 +pytest==7.1.0 pytest-asyncio==0.18.2 pytest-cov==3.0.0 pytest-mock==3.7.0 From 7764ad1541fcc81e40ab3b94ad33ca4a0612ea6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 03:01:03 +0000 Subject: [PATCH 16/28] Bump types-cachetools from 4.2.10 to 5.0.0 Bumps [types-cachetools](https://github.com/python/typeshed) from 4.2.10 to 5.0.0. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-cachetools dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 5c94ac7c8..0f9d4d105 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -20,7 +20,7 @@ time-machine==2.6.0 nbconvert==6.4.2 # mypy types -types-cachetools==4.2.10 +types-cachetools==5.0.0 types-filelock==3.2.5 types-requests==2.27.11 types-tabulate==0.8.5 From 3a0ad2f26e2b96a2210f4da44ce08e767c4054cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 03:01:07 +0000 Subject: [PATCH 17/28] Bump uvicorn from 0.17.5 to 0.17.6 Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.17.5 to 0.17.6. - [Release notes](https://github.com/encode/uvicorn/releases) - [Changelog](https://github.com/encode/uvicorn/blob/master/CHANGELOG.md) - [Commits](https://github.com/encode/uvicorn/compare/0.17.5...0.17.6) --- updated-dependencies: - dependency-name: uvicorn dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f8b6049f1..095735180 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,7 +32,7 @@ sdnotify==0.3.2 # API Server fastapi==0.75.0 -uvicorn==0.17.5 +uvicorn==0.17.6 pyjwt==2.3.0 aiofiles==0.8.0 psutil==5.9.0 From 3fc1c94aba6bdb7045153d793aab9b1427c73c8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 03:01:14 +0000 Subject: [PATCH 18/28] Bump ccxt from 1.75.12 to 1.76.5 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.75.12 to 1.76.5. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg) - [Commits](https://github.com/ccxt/ccxt/compare/1.75.12...1.76.5) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f8b6049f1..245fa63d8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.22.2 pandas==1.4.1 pandas-ta==0.3.14b -ccxt==1.75.12 +ccxt==1.76.5 # Pin cryptography for now due to rust build errors with piwheels cryptography==36.0.1 aiohttp==3.8.1 From 4cbdc9a74ffe6cc39002b03739335f4a4d58cbb1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 03:01:17 +0000 Subject: [PATCH 19/28] Bump types-requests from 2.27.11 to 2.27.12 Bumps [types-requests](https://github.com/python/typeshed) from 2.27.11 to 2.27.12. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-requests dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 5c94ac7c8..beae48283 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -22,7 +22,7 @@ nbconvert==6.4.2 # mypy types types-cachetools==4.2.10 types-filelock==3.2.5 -types-requests==2.27.11 +types-requests==2.27.12 types-tabulate==0.8.5 # Extensions to datetime library From a7133f1974bf3f873eef7eb441ea1c924a85b10b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 03:01:20 +0000 Subject: [PATCH 20/28] Bump nbconvert from 6.4.2 to 6.4.4 Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 6.4.2 to 6.4.4. - [Release notes](https://github.com/jupyter/nbconvert/releases) - [Commits](https://github.com/jupyter/nbconvert/compare/6.4.2...6.4.4) --- updated-dependencies: - dependency-name: nbconvert dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 5c94ac7c8..26c873b1f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -17,7 +17,7 @@ isort==5.10.1 time-machine==2.6.0 # Convert jupyter notebooks to markdown documents -nbconvert==6.4.2 +nbconvert==6.4.4 # mypy types types-cachetools==4.2.10 From 3fbe4a9944369ad9b1e829838709ac53bf5a6914 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 03:01:30 +0000 Subject: [PATCH 21/28] Bump numpy from 1.22.2 to 1.22.3 Bumps [numpy](https://github.com/numpy/numpy) from 1.22.2 to 1.22.3. - [Release notes](https://github.com/numpy/numpy/releases) - [Changelog](https://github.com/numpy/numpy/blob/main/doc/HOWTO_RELEASE.rst.txt) - [Commits](https://github.com/numpy/numpy/compare/v1.22.2...v1.22.3) --- updated-dependencies: - dependency-name: numpy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f8b6049f1..b9c6d3d06 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -numpy==1.22.2 +numpy==1.22.3 pandas==1.4.1 pandas-ta==0.3.14b From 404d700a74b919cfefb467bae9b1910d5a0f056a Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 14 Mar 2022 06:23:48 +0100 Subject: [PATCH 22/28] Raise min-requirement for ccxt --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ec41228c1..a89e717a1 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ setup( ], install_requires=[ # from requirements.txt - 'ccxt>=1.74.17', + 'ccxt>=1.76.5', 'SQLAlchemy', 'python-telegram-bot>=13.4', 'arrow>=0.17.0', From 5462ff0ebc9f6ad09af17db0bc46d6be3bfd23f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 05:25:16 +0000 Subject: [PATCH 23/28] Bump mypy from 0.931 to 0.940 Bumps [mypy](https://github.com/python/mypy) from 0.931 to 0.940. - [Release notes](https://github.com/python/mypy/releases) - [Commits](https://github.com/python/mypy/compare/v0.931...v0.940) --- updated-dependencies: - dependency-name: mypy dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 0c3622f72..c2f3eae8a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,7 +6,7 @@ coveralls==3.3.1 flake8==4.0.1 flake8-tidy-imports==4.6.0 -mypy==0.931 +mypy==0.940 pytest==7.1.0 pytest-asyncio==0.18.2 pytest-cov==3.0.0 From 18030a30e7d4e081a893eb37ef9501e0a20648d1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 14 Mar 2022 19:21:58 +0100 Subject: [PATCH 24/28] Add exchange parameter to test-pairlist command This will allow for quick tests of the same pairlist config against multiple exchanges. --- docs/utils.md | 15 ++++++++++----- freqtrade/commands/arguments.py | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/utils.md b/docs/utils.md index c6e795e60..a28a0f456 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -517,20 +517,25 @@ Requires a configuration with specified `pairlists` attribute. Can be used to generate static pairlists to be used during backtesting / hyperopt. ``` -usage: freqtrade test-pairlist [-h] [-c PATH] +usage: freqtrade test-pairlist [-h] [-v] [-c PATH] [--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]] - [-1] [--print-json] + [-1] [--print-json] [--exchange EXCHANGE] optional arguments: -h, --help show this help message and exit + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). -c PATH, --config PATH - Specify configuration file (default: `config.json`). - Multiple --config options may be used. Can be set to - `-` to read config from stdin. + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. --quote QUOTE_CURRENCY [QUOTE_CURRENCY ...] Specify quote currency(-ies). Space-separated list. -1, --one-column Print output in one column. --print-json Print list of pairs or market symbols in JSON format. + --exchange EXCHANGE Exchange name (default: `bittrex`). Only valid if no + config is provided. + ``` ### Examples diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 201ec09bf..28f7d7148 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -51,7 +51,7 @@ ARGS_LIST_PAIRS = ["exchange", "print_list", "list_pairs_print_json", "print_one "print_csv", "base_currencies", "quote_currencies", "list_pairs_all"] ARGS_TEST_PAIRLIST = ["verbosity", "config", "quote_currencies", "print_one_column", - "list_pairs_print_json"] + "list_pairs_print_json", "exchange"] ARGS_CREATE_USERDIR = ["user_data_dir", "reset"] From 6024fa482e1b09bae8e85b3afc9fc58e483c1512 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 17 Mar 2022 07:41:08 +0100 Subject: [PATCH 25/28] Use brackets to break IF lines --- freqtrade/edge/edge_positioning.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/freqtrade/edge/edge_positioning.py b/freqtrade/edge/edge_positioning.py index 1950f0d08..08f43598d 100644 --- a/freqtrade/edge/edge_positioning.py +++ b/freqtrade/edge/edge_positioning.py @@ -219,9 +219,11 @@ class Edge: """ final = [] for pair, info in self._cached_pairs.items(): - if info.expectancy > float(self.edge_config.get('minimum_expectancy', 0.2)) and \ - info.winrate > float(self.edge_config.get('minimum_winrate', 0.60)) and \ - pair in pairs: + if ( + info.expectancy > float(self.edge_config.get('minimum_expectancy', 0.2)) + and info.winrate > float(self.edge_config.get('minimum_winrate', 0.60)) + and pair in pairs + ): final.append(pair) if self._final_pairs != final: @@ -246,8 +248,8 @@ class Edge: """ final = [] for pair, info in self._cached_pairs.items(): - if info.expectancy > float(self.edge_config.get('minimum_expectancy', 0.2)) and \ - info.winrate > float(self.edge_config.get('minimum_winrate', 0.60)): + if (info.expectancy > float(self.edge_config.get('minimum_expectancy', 0.2)) and + info.winrate > float(self.edge_config.get('minimum_winrate', 0.60))): final.append({ 'Pair': pair, 'Winrate': info.winrate, From 96bf82dbc64b824617f75ca8556f2f2d0e4d9446 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 17 Mar 2022 17:06:10 +0100 Subject: [PATCH 26/28] Remove gateio broker program --- freqtrade/exchange/gateio.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index bfe996e86..d0fd787b7 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -26,8 +26,6 @@ class Gateio(Exchange): "stoploss_on_exchange": True, } - _headers = {'X-Gate-Channel-Id': 'freqtrade'} - def validate_ordertypes(self, order_types: Dict) -> None: super().validate_ordertypes(order_types) From f55db8e262f2f64758682a9a5b96abbab6051d0b Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 17 Mar 2022 20:21:10 +0100 Subject: [PATCH 27/28] Spreadfilter should fail to start if fetchTickers is not supported --- freqtrade/plugins/pairlist/SpreadFilter.py | 7 +++++++ tests/plugins/test_pairlist.py | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/freqtrade/plugins/pairlist/SpreadFilter.py b/freqtrade/plugins/pairlist/SpreadFilter.py index ad0c0f0be..d1f88d2a5 100644 --- a/freqtrade/plugins/pairlist/SpreadFilter.py +++ b/freqtrade/plugins/pairlist/SpreadFilter.py @@ -4,6 +4,7 @@ Spread pair list filter import logging from typing import Any, Dict +from freqtrade.exceptions import OperationalException from freqtrade.plugins.pairlist.IPairList import IPairList @@ -20,6 +21,12 @@ class SpreadFilter(IPairList): self._max_spread_ratio = pairlistconfig.get('max_spread_ratio', 0.005) self._enabled = self._max_spread_ratio != 0 + if not self._exchange.exchange_has('fetchTickers'): + raise OperationalException( + 'Exchange does not support fetchTickers, therefore SpreadFilter cannot be used.' + 'Please edit your config and restart the bot.' + ) + @property def needstickers(self) -> bool: """ diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 52158a889..346bf1792 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -782,6 +782,18 @@ def test_gen_pair_whitelist_not_supported(mocker, default_conf, tickers) -> None get_patched_freqtradebot(mocker, default_conf) +def test_pair_whitelist_not_supported_Spread(mocker, default_conf, tickers) -> None: + default_conf['pairlists'] = [{'method': 'StaticPairList'}, {'method': 'SpreadFilter'}] + + mocker.patch.multiple('freqtrade.exchange.Exchange', + get_tickers=tickers, + exchange_has=MagicMock(return_value=False), + ) + + with pytest.raises(OperationalException, + match=r'Exchange does not support fetchTickers, .*'): + get_patched_freqtradebot(mocker, default_conf) + @pytest.mark.parametrize("pairlist", AVAILABLE_PAIRLISTS) def test_pairlist_class(mocker, whitelist_conf, markets, pairlist): whitelist_conf['pairlists'][0]['method'] = pairlist From 73fc344eb1d666cbf7f435abc8b5505e9f952798 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 18 Mar 2022 06:38:54 +0100 Subject: [PATCH 28/28] Improve wording in docs --- docs/bot-basics.md | 2 +- tests/plugins/test_pairlist.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/bot-basics.md b/docs/bot-basics.md index 8c6303063..4b5ba3a5b 100644 --- a/docs/bot-basics.md +++ b/docs/bot-basics.md @@ -24,7 +24,7 @@ By default, loop runs every few seconds (`internals.process_throttle_secs`) and * Fetch open trades from persistence. * Calculate current list of tradable pairs. -* Download ohlcv data for the pairlist including all [informative pairs](strategy-customization.md#get-data-for-non-tradeable-pairs) +* Download OHLCV data for the pairlist including all [informative pairs](strategy-customization.md#get-data-for-non-tradeable-pairs) This step is only executed once per Candle to avoid unnecessary network traffic. * Call `bot_loop_start()` strategy callback. * Analyze strategy per pair. diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 346bf1792..08ba892fe 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -794,6 +794,7 @@ def test_pair_whitelist_not_supported_Spread(mocker, default_conf, tickers) -> N match=r'Exchange does not support fetchTickers, .*'): get_patched_freqtradebot(mocker, default_conf) + @pytest.mark.parametrize("pairlist", AVAILABLE_PAIRLISTS) def test_pairlist_class(mocker, whitelist_conf, markets, pairlist): whitelist_conf['pairlists'][0]['method'] = pairlist