From 33229c91cb160f836eecf28d4949e519034b6339 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 26 Mar 2022 13:53:36 +0100 Subject: [PATCH 01/10] Add fetch_trading_fees endpoint --- freqtrade/exchange/exchange.py | 21 ++++++++++++ tests/exchange/test_exchange.py | 58 +++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index e27a56aff..1d4fc8337 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1299,6 +1299,27 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e + @retrier + def fetch_trading_fees(self) -> Dict[str, Any]: + """ + Fetch user account trading fees + Can be cached, should not update often. + """ + if (self._config['dry_run'] or self.trading_mode != TradingMode.FUTURES + or not self.exchange_has('fetchTradingFees')): + return {} + try: + trading_fees: Dict[str, Any] = self._api.fetch_trading_fees() + self._log_exchange_response('fetch_trading_fees', trading_fees) + return trading_fees + except ccxt.DDoSProtection as e: + raise DDosProtection(e) from e + except (ccxt.NetworkError, ccxt.ExchangeError) as e: + raise TemporaryError( + f'Could not fetch trading fees due to {e.__class__.__name__}. Message: {e}') from e + except ccxt.BaseError as e: + raise OperationalException(e) from e + @retrier def fetch_bids_asks(self, symbols: List[str] = None, cached: bool = False) -> Dict: """ diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 815ebcec2..d19baf39a 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1624,6 +1624,64 @@ def test_fetch_positions(default_conf, mocker, exchange_name): "fetch_positions", "fetch_positions") +def test_fetch_trading_fees(default_conf, mocker): + api_mock = MagicMock() + tick = { + '1INCH/USDT:USDT': { + 'info': {'user_id': '6266643', + 'taker_fee': '0.0018', + 'maker_fee': '0.0018', + 'gt_discount': False, + 'gt_taker_fee': '0', + 'gt_maker_fee': '0', + 'loan_fee': '0.18', + 'point_type': '1', + 'futures_taker_fee': '0.0005', + 'futures_maker_fee': '0'}, + 'symbol': '1INCH/USDT:USDT', + 'maker': 0.0, + 'taker': 0.0005}, + 'ETH/USDT:USDT': { + 'info': {'user_id': '6266643', + 'taker_fee': '0.0018', + 'maker_fee': '0.0018', + 'gt_discount': False, + 'gt_taker_fee': '0', + 'gt_maker_fee': '0', + 'loan_fee': '0.18', + 'point_type': '1', + 'futures_taker_fee': '0.0005', + 'futures_maker_fee': '0'}, + 'symbol': 'ETH/USDT:USDT', + 'maker': 0.0, + 'taker': 0.0005} + } + exchange_name = 'gateio' + default_conf['dry_run'] = False + default_conf['trading_mode'] = TradingMode.FUTURES + default_conf['margin_mode'] = MarginMode.ISOLATED + api_mock.fetch_trading_fees = MagicMock(return_value=tick) + mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) + exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) + # retrieve original ticker + tradingfees = exchange.fetch_trading_fees() + + assert '1INCH/USDT:USDT' in tradingfees + assert 'ETH/USDT:USDT' in tradingfees + assert api_mock.fetch_trading_fees.call_count == 1 + + api_mock.fetch_trading_fees.reset_mock() + + ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, + "fetch_trading_fees", "fetch_trading_fees") + + api_mock.fetch_trading_fees = MagicMock(return_value={}) + exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) + exchange.fetch_trading_fees() + mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) + assert exchange.fetch_trading_fees() == {} + + def test_fetch_bids_asks(default_conf, mocker): api_mock = MagicMock() tick = {'ETH/BTC': { From 9a8c24ddf3f199d05a15e0e011b8d85b4ea25e7b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 26 Mar 2022 14:57:42 +0100 Subject: [PATCH 02/10] Update gateio to patch fees --- freqtrade/exchange/exchange.py | 4 ++++ freqtrade/exchange/gateio.py | 24 ++++++++++++++++++++++++ freqtrade/freqtradebot.py | 1 + tests/exchange/test_exchange.py | 6 ++---- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 1d4fc8337..3a39f9300 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -75,6 +75,7 @@ class Exchange: "mark_ohlcv_price": "mark", "mark_ohlcv_timeframe": "8h", "ccxt_futures_name": "swap", + "needs_trading_fees": False, # use fetch_trading_fees to cache fees } _ft_has: Dict = {} _ft_has_futures: Dict = {} @@ -451,6 +452,9 @@ class Exchange: self._markets = self._api.load_markets() self._load_async_markets() self._last_markets_refresh = arrow.utcnow().int_timestamp + if self._ft_has['needs_trading_fees']: + self.trading_fees = self.fetch_trading_fees() + except ccxt.BaseError: logger.exception('Unable to initialize markets.') diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index 50ff0c872..726b5c7ed 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -27,6 +27,10 @@ class Gateio(Exchange): "stoploss_on_exchange": True, } + _ft_has_futures: Dict = { + "needs_trading_fees": True + } + _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ # TradingMode.SPOT always supported and not required in this list # (TradingMode.MARGIN, MarginMode.CROSS), @@ -42,6 +46,26 @@ class Gateio(Exchange): raise OperationalException( f'Exchange {self.name} does not support market orders.') + def fetch_order(self, order_id: str, pair: str, params={}) -> Dict: + order = super().fetch_order(order_id, pair, params) + + if self.trading_mode == TradingMode.FUTURES and order.get('fee') is None: + # Futures usually don't contain fees in the response. + # As such, futures orders on gateio will not contain a fee, which causes + # a repeated "update fee" cycle and wrong calculations. + # Therefore we patch the response with fees if it's not available. + # An alternative also contianing fees would be + # privateFuturesGetSettleAccountBook({"settle": "usdt"}) + + pair_fees = self.trading_fees.get(pair, {}) + if pair_fees and pair_fees['taker'] is not None: + order['fee'] = { + 'currency': self.get_pair_quote_currency(pair), + 'cost': abs(order['cost']) * pair_fees['taker'], + 'rate': pair_fees['taker'], + } + return order + def fetch_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict: return self.fetch_order( order_id=order_id, diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 089a5804a..ebc129777 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1564,6 +1564,7 @@ class FreqtradeBot(LoggingMixin): if not order_obj: raise DependencyException( f"Order_obj not found for {order_id}. This should not have happened.") + self.handle_order_fee(trade, order_obj, order) trade.update_trade(order_obj) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index d19baf39a..24ca47e8b 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1663,11 +1663,9 @@ def test_fetch_trading_fees(default_conf, mocker): api_mock.fetch_trading_fees = MagicMock(return_value=tick) mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - # retrieve original ticker - tradingfees = exchange.fetch_trading_fees() - assert '1INCH/USDT:USDT' in tradingfees - assert 'ETH/USDT:USDT' in tradingfees + assert '1INCH/USDT:USDT' in exchange.trading_fees + assert 'ETH/USDT:USDT' in exchange.trading_fees assert api_mock.fetch_trading_fees.call_count == 1 api_mock.fetch_trading_fees.reset_mock() From 9b8a2435f885b29a64dde0410c5db414869e60da Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 26 Mar 2022 15:12:05 +0100 Subject: [PATCH 03/10] Add tests for fetch_order gateio patch --- tests/exchange/test_exchange.py | 4 ++-- tests/exchange/test_gateio.py | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 24ca47e8b..4f5aac199 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1628,7 +1628,7 @@ def test_fetch_trading_fees(default_conf, mocker): api_mock = MagicMock() tick = { '1INCH/USDT:USDT': { - 'info': {'user_id': '6266643', + 'info': {'user_id': '', 'taker_fee': '0.0018', 'maker_fee': '0.0018', 'gt_discount': False, @@ -1642,7 +1642,7 @@ def test_fetch_trading_fees(default_conf, mocker): 'maker': 0.0, 'taker': 0.0005}, 'ETH/USDT:USDT': { - 'info': {'user_id': '6266643', + 'info': {'user_id': '', 'taker_fee': '0.0018', 'maker_fee': '0.0018', 'gt_discount': False, diff --git a/tests/exchange/test_gateio.py b/tests/exchange/test_gateio.py index 5a46e30a6..73a3b1623 100644 --- a/tests/exchange/test_gateio.py +++ b/tests/exchange/test_gateio.py @@ -2,6 +2,7 @@ from unittest.mock import MagicMock import pytest +from freqtrade.enums import MarginMode, TradingMode from freqtrade.exceptions import OperationalException from freqtrade.exchange import Gateio from freqtrade.resolvers.exchange_resolver import ExchangeResolver @@ -70,3 +71,40 @@ def test_stoploss_adjust_gateio(mocker, default_conf, sl1, sl2, sl3, side): } assert exchange.stoploss_adjust(sl1, order, side) assert not exchange.stoploss_adjust(sl2, order, side) + + +def test_fetch_order_gateio(mocker, default_conf): + tick = {'ETH/USDT:USDT': { + 'info': {'user_id': '', + 'taker_fee': '0.0018', + 'maker_fee': '0.0018', + 'gt_discount': False, + 'gt_taker_fee': '0', + 'gt_maker_fee': '0', + 'loan_fee': '0.18', + 'point_type': '1', + 'futures_taker_fee': '0.0005', + 'futures_maker_fee': '0'}, + 'symbol': 'ETH/USDT:USDT', + 'maker': 0.0, + 'taker': 0.0005} + } + default_conf['dry_run'] = False + default_conf['trading_mode'] = TradingMode.FUTURES + default_conf['margin_mode'] = MarginMode.ISOLATED + + api_mock = MagicMock() + api_mock.fetch_order = MagicMock(return_value={ + 'fee': None, + 'price': 3108.65, + 'cost': 0.310865, + 'amount': 1, # 1 contract + }) + exchange = get_patched_exchange(mocker, default_conf, api_mock=api_mock, id='gateio') + exchange.trading_fees = tick + order = exchange.fetch_order('22255', 'ETH/USDT:USDT') + + assert order['fee'] + assert order['fee']['rate'] == 0.0005 + assert order['fee']['currency'] == 'USDT' + assert order['fee']['cost'] == 0.0001554325 From f5578aba48f174190697ac63908b3d3993c3a10c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 26 Mar 2022 15:15:16 +0100 Subject: [PATCH 04/10] Update trading_fee naming --- freqtrade/exchange/exchange.py | 3 ++- freqtrade/exchange/gateio.py | 2 +- tests/exchange/test_exchange.py | 4 ++-- tests/exchange/test_gateio.py | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 3a39f9300..67692cd27 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -93,6 +93,7 @@ class Exchange: self._api: ccxt.Exchange = None self._api_async: ccxt_async.Exchange = None self._markets: Dict = {} + self._trading_fees: Dict[str, Any] = {} self._leverage_tiers: Dict[str, List[Dict]] = {} self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) @@ -453,7 +454,7 @@ class Exchange: self._load_async_markets() self._last_markets_refresh = arrow.utcnow().int_timestamp if self._ft_has['needs_trading_fees']: - self.trading_fees = self.fetch_trading_fees() + self._trading_fees = self.fetch_trading_fees() except ccxt.BaseError: logger.exception('Unable to initialize markets.') diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index 726b5c7ed..23174bcd6 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -57,7 +57,7 @@ class Gateio(Exchange): # An alternative also contianing fees would be # privateFuturesGetSettleAccountBook({"settle": "usdt"}) - pair_fees = self.trading_fees.get(pair, {}) + pair_fees = self._trading_fees.get(pair, {}) if pair_fees and pair_fees['taker'] is not None: order['fee'] = { 'currency': self.get_pair_quote_currency(pair), diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 4f5aac199..5d16c3501 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1664,8 +1664,8 @@ def test_fetch_trading_fees(default_conf, mocker): mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - assert '1INCH/USDT:USDT' in exchange.trading_fees - assert 'ETH/USDT:USDT' in exchange.trading_fees + assert '1INCH/USDT:USDT' in exchange._trading_fees + assert 'ETH/USDT:USDT' in exchange._trading_fees assert api_mock.fetch_trading_fees.call_count == 1 api_mock.fetch_trading_fees.reset_mock() diff --git a/tests/exchange/test_gateio.py b/tests/exchange/test_gateio.py index 73a3b1623..9102d6704 100644 --- a/tests/exchange/test_gateio.py +++ b/tests/exchange/test_gateio.py @@ -101,7 +101,7 @@ def test_fetch_order_gateio(mocker, default_conf): 'amount': 1, # 1 contract }) exchange = get_patched_exchange(mocker, default_conf, api_mock=api_mock, id='gateio') - exchange.trading_fees = tick + exchange._trading_fees = tick order = exchange.fetch_order('22255', 'ETH/USDT:USDT') assert order['fee'] From f509959bd45b6dbe1ef569584cf7f7592b8b0743 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 26 Mar 2022 16:19:29 +0100 Subject: [PATCH 05/10] Update --- freqtrade/exchange/gateio.py | 29 +++++++++++++++++------------ tests/exchange/test_gateio.py | 28 ++++++++++++++++++---------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index 23174bcd6..656a1e30f 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -1,6 +1,7 @@ """ Gate.io exchange subclass """ import logging -from typing import Dict, List, Tuple +from datetime import datetime +from typing import Dict, List, Optional, Tuple from freqtrade.enums import MarginMode, TradingMode from freqtrade.exceptions import OperationalException @@ -46,25 +47,29 @@ class Gateio(Exchange): raise OperationalException( f'Exchange {self.name} does not support market orders.') - def fetch_order(self, order_id: str, pair: str, params={}) -> Dict: - order = super().fetch_order(order_id, pair, params) + def get_trades_for_order(self, order_id: str, pair: str, since: datetime, + params: Optional[Dict] = None) -> List: + trades = super().get_trades_for_order(order_id, pair, since, params) - if self.trading_mode == TradingMode.FUTURES and order.get('fee') is None: + if self.trading_mode == TradingMode.FUTURES: # Futures usually don't contain fees in the response. # As such, futures orders on gateio will not contain a fee, which causes # a repeated "update fee" cycle and wrong calculations. # Therefore we patch the response with fees if it's not available. # An alternative also contianing fees would be # privateFuturesGetSettleAccountBook({"settle": "usdt"}) - pair_fees = self._trading_fees.get(pair, {}) - if pair_fees and pair_fees['taker'] is not None: - order['fee'] = { - 'currency': self.get_pair_quote_currency(pair), - 'cost': abs(order['cost']) * pair_fees['taker'], - 'rate': pair_fees['taker'], - } - return order + if pair_fees: + for idx, trade in enumerate(trades): + if trade.get('fee', {}).get('cost') is None: + takerOrMaker = trade.get('takerOrMaker', 'taker') + if pair_fees.get(takerOrMaker) is not None: + trades[idx]['fee'] = { + 'currency': self.get_pair_quote_currency(pair), + 'cost': abs(trade['cost']) * pair_fees[takerOrMaker], + 'rate': pair_fees[takerOrMaker], + } + return trades def fetch_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict: return self.fetch_order( diff --git a/tests/exchange/test_gateio.py b/tests/exchange/test_gateio.py index 9102d6704..c718c3838 100644 --- a/tests/exchange/test_gateio.py +++ b/tests/exchange/test_gateio.py @@ -1,3 +1,4 @@ +from datetime import datetime, timezone from unittest.mock import MagicMock import pytest @@ -72,8 +73,13 @@ def test_stoploss_adjust_gateio(mocker, default_conf, sl1, sl2, sl3, side): assert exchange.stoploss_adjust(sl1, order, side) assert not exchange.stoploss_adjust(sl2, order, side) +@pytest.mark.parametrize('takerormaker,rate,cost', [ + ('taker', 0.0005, 0.0001554325), + ('maker', 0.0, 0.0), -def test_fetch_order_gateio(mocker, default_conf): +]) +def test_fetch_my_trades_gateio(mocker, default_conf, takerormaker, rate, cost): + mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) tick = {'ETH/USDT:USDT': { 'info': {'user_id': '', 'taker_fee': '0.0018', @@ -94,17 +100,19 @@ def test_fetch_order_gateio(mocker, default_conf): default_conf['margin_mode'] = MarginMode.ISOLATED api_mock = MagicMock() - api_mock.fetch_order = MagicMock(return_value={ - 'fee': None, + api_mock.fetch_my_trades = MagicMock(return_value=[{ + 'fee': {'cost': None}, 'price': 3108.65, 'cost': 0.310865, + 'order': '22255', + 'takerOrMaker': takerormaker, 'amount': 1, # 1 contract - }) + }]) exchange = get_patched_exchange(mocker, default_conf, api_mock=api_mock, id='gateio') exchange._trading_fees = tick - order = exchange.fetch_order('22255', 'ETH/USDT:USDT') - - assert order['fee'] - assert order['fee']['rate'] == 0.0005 - assert order['fee']['currency'] == 'USDT' - assert order['fee']['cost'] == 0.0001554325 + trades = exchange.get_trades_for_order('22255', 'ETH/USDT:USDT', datetime.now(timezone.utc)) + trade = trades[0] + assert trade['fee'] + assert trade['fee']['rate'] == rate + assert trade['fee']['currency'] == 'USDT' + assert trade['fee']['cost'] == cost From d244391860f748d8195edde74615c47f3911939c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 26 Mar 2022 16:32:20 +0100 Subject: [PATCH 06/10] no need to "abs" cost will be fixed in ccxt --- freqtrade/exchange/gateio.py | 2 +- tests/exchange/test_gateio.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index 656a1e30f..609cf4901 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -66,7 +66,7 @@ class Gateio(Exchange): if pair_fees.get(takerOrMaker) is not None: trades[idx]['fee'] = { 'currency': self.get_pair_quote_currency(pair), - 'cost': abs(trade['cost']) * pair_fees[takerOrMaker], + 'cost': trade['cost'] * pair_fees[takerOrMaker], 'rate': pair_fees[takerOrMaker], } return trades diff --git a/tests/exchange/test_gateio.py b/tests/exchange/test_gateio.py index c718c3838..ad30a7d86 100644 --- a/tests/exchange/test_gateio.py +++ b/tests/exchange/test_gateio.py @@ -73,10 +73,10 @@ def test_stoploss_adjust_gateio(mocker, default_conf, sl1, sl2, sl3, side): assert exchange.stoploss_adjust(sl1, order, side) assert not exchange.stoploss_adjust(sl2, order, side) + @pytest.mark.parametrize('takerormaker,rate,cost', [ ('taker', 0.0005, 0.0001554325), ('maker', 0.0, 0.0), - ]) def test_fetch_my_trades_gateio(mocker, default_conf, takerormaker, rate, cost): mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) From 0f1de435da4edbc45f2bbed32939e8677dea77ad Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Mar 2022 08:28:44 +0200 Subject: [PATCH 07/10] Fix ccxt compat tests --- tests/exchange/test_ccxt_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/exchange/test_ccxt_compat.py b/tests/exchange/test_ccxt_compat.py index 5eb7e68d4..89b3bcc1f 100644 --- a/tests/exchange/test_ccxt_compat.py +++ b/tests/exchange/test_ccxt_compat.py @@ -134,7 +134,7 @@ def exchange_futures(request, exchange_conf, class_mocker): class_mocker.patch( 'freqtrade.exchange.binance.Binance.fill_leverage_tiers') - + class_mocker.patch('freqtrade.exchange.exchange.Exchange.fetch_trading_fees') exchange = ExchangeResolver.load_exchange(request.param, exchange_conf, validate=True) yield exchange, request.param From 84777e255e653a078c307f2e827ccf9c0a582213 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Mar 2022 14:58:08 +0200 Subject: [PATCH 08/10] Force-bump ccxt version to 1.77.29 --- requirements.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index c57efb3ec..1fb98ef21 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.22.3 pandas==1.4.1 pandas-ta==0.3.14b -ccxt==1.76.65 +ccxt==1.77.29 # Pin cryptography for now due to rust build errors with piwheels cryptography==36.0.2 aiohttp==3.8.1 diff --git a/setup.py b/setup.py index eb2921e73..640c8cc7b 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ setup( ], install_requires=[ # from requirements.txt - 'ccxt>=1.76.5', + 'ccxt>=1.77.29', 'SQLAlchemy', 'python-telegram-bot>=13.4', 'arrow>=0.17.0', From 30cff1bd2ca7481c60d9bafa761ad03fd6ea006f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Mar 2022 16:38:12 +0200 Subject: [PATCH 09/10] Update hdf5 to not raise naturalNaming warnings --- freqtrade/data/history/hdf5datahandler.py | 4 +++- tests/data/test_history.py | 4 ++-- tests/test_misc.py | 2 ++ ...-mark.h5 => UNITTEST_USDT_USDT-1h-mark.h5} | Bin 37751 -> 72922 bytes 4 files changed, 7 insertions(+), 3 deletions(-) rename tests/testdata/futures/{UNITTEST_USDT-1h-mark.h5 => UNITTEST_USDT_USDT-1h-mark.h5} (51%) diff --git a/freqtrade/data/history/hdf5datahandler.py b/freqtrade/data/history/hdf5datahandler.py index db96bdf21..23120a4ba 100644 --- a/freqtrade/data/history/hdf5datahandler.py +++ b/freqtrade/data/history/hdf5datahandler.py @@ -206,7 +206,9 @@ class HDF5DataHandler(IDataHandler): @classmethod def _pair_ohlcv_key(cls, pair: str, timeframe: str) -> str: - return f"{pair}/ohlcv/tf_{timeframe}" + # Escape futures pairs to avoid warnings + pair_esc = pair.replace(':', '_') + return f"{pair_esc}/ohlcv/tf_{timeframe}" @classmethod def _pair_trades_key(cls, pair: str) -> str: diff --git a/tests/data/test_history.py b/tests/data/test_history.py index 43a3aaefd..0585fa0d4 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -683,7 +683,7 @@ def test_datahandler_ohlcv_get_pairs(testdatadir): assert set(pairs) == {'XRP/USDT'} pairs = HDF5DataHandler.ohlcv_get_pairs(testdatadir, '1h', candle_type=CandleType.MARK) - assert set(pairs) == {'UNITTEST/USDT'} + assert set(pairs) == {'UNITTEST/USDT:USDT'} @pytest.mark.parametrize('filename,pair,timeframe,candletype', [ @@ -914,7 +914,7 @@ def test_hdf5datahandler_trades_purge(mocker, testdatadir): # Data goes from 2018-01-10 - 2018-01-30 ('UNITTEST/BTC', '5m', 'spot', '', '2018-01-15', '2018-01-19'), # Mark data goes from to 2021-11-15 2021-11-19 - ('UNITTEST/USDT', '1h', 'mark', '-mark', '2021-11-16', '2021-11-18'), + ('UNITTEST/USDT:USDT', '1h', 'mark', '-mark', '2021-11-16', '2021-11-18'), ]) def test_hdf5datahandler_ohlcv_load_and_resave( testdatadir, diff --git a/tests/test_misc.py b/tests/test_misc.py index d28050dfb..107932be4 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -73,6 +73,8 @@ def test_file_load_json(mocker, testdatadir) -> None: ("ETH/BTC", 'ETH_BTC'), ("ETH/USDT", 'ETH_USDT'), ("ETH/USDT:USDT", 'ETH_USDT_USDT'), # swap with USDT as settlement currency + ("ETH/USD:USD", 'ETH_USD_USD'), # swap with USD as settlement currency + ("AAVE/USD:USD", 'AAVE_USD_USD'), # swap with USDT as settlement currency ("ETH/USDT:USDT-210625", 'ETH_USDT_USDT-210625'), # expiring futures ("Fabric Token/ETH", 'Fabric_Token_ETH'), ("ETHH20", 'ETHH20'), diff --git a/tests/testdata/futures/UNITTEST_USDT-1h-mark.h5 b/tests/testdata/futures/UNITTEST_USDT_USDT-1h-mark.h5 similarity index 51% rename from tests/testdata/futures/UNITTEST_USDT-1h-mark.h5 rename to tests/testdata/futures/UNITTEST_USDT_USDT-1h-mark.h5 index ce17eb9e104af3472fbdb121cd4453a34bad6088..e6b128dc1f9ba8b2a1e89fc22f58aabce284c862 100644 GIT binary patch delta 2145 zcmcgs{Zmv`7(VCRyX@UvP(V~%cSS3O1wkc|WQIWTV+SM1b<5ci++hdWOwA^toG59= zvN1E(H_VR|9Fxk#>3StUMvYNHN5{-YVH+cZW9I9C#;{e6kCATsEmaH-xua1k&3G zW=jQ3<^*$eF<6BbQ}6C!s|xmwSXHnKj*eby?a~vo@`{dAc8TI798MagU{ykkd2^GN zw2X(k+-XGlERvRrCinJ&Cuyy9;~s4m%lo;GFYb-xjZ$i}+oa}kmpHPx1y)(RO>`|s z$mT;(GBJhUU0YZQ>n0|rx3!NgiFiuk%3Mx&?dlc}#ET9RF0-(s4Nvp%e1ctS>ZQWk0-w5D4+TpdFa-M2L&9%)yUp$AsySIlqg*JyU2Z|nF`P~+LzE59&u-n%+J z*s0!UW|p+7Uy3a_LP+LSI6ZHRcEXG2J4xe>fH=vVrlmlAKTyPO%D$@`7ffY;7l8#QWxhmniB}S5S4>r5BS_clfFlp_P4lkRAxsC}~y6Mgo zxL2)MGE^7@b9CP!srq?R6QZV%864CK73%fg;<40PZ6IT+jGD2`ps@mmHTfkLhHKOU z_Lo`-$$v1?v4cajf9BZWKNK;?1|O)%!W?tQ;DBhiu+lfN3LF0?&pHg6Jl!*HtO-MN z*(B}9W2Tk=O`B(?wLYYXnb!9QmBLJ0!-b_V*YXjS64|RNb??fTo+0)S^Nm}WfZ=_Y hR*xFToe{8f=IyDN+pQWFc22bE&P7j_^hAS;`xks92)6(L delta 56 zcmcb$ljZv|rU@F%<&zmUYOAtrK0aB1X>tOa#AF3-zsdVVxHey4o5uoTOs?Qo*!+P_ LfpPN=p8ahATpbkx From d1f61c4cf9dddad67c93befcb0f470b96532ae78 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 27 Mar 2022 17:00:45 +0200 Subject: [PATCH 10/10] Use proper fee for DCA entries --- freqtrade/freqtradebot.py | 9 ++------- tests/test_integration.py | 3 +-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index ebc129777..b85052ff9 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -511,7 +511,7 @@ class FreqtradeBot(LoggingMixin): return else: logger.debug("Max adjustment entries is set to unlimited.") - current_rate = self.exchange.get_rate(trade.pair, refresh=True, side="buy") + current_rate = self.exchange.get_rate(trade.pair, refresh=True, side=trade.enter_side) current_profit = trade.calc_profit_ratio(current_rate) min_stake_amount = self.exchange.get_min_pair_stake_amount(trade.pair, @@ -536,12 +536,7 @@ class FreqtradeBot(LoggingMixin): logger.error(f"Unable to decrease trade position / sell partially" f" for pair {trade.pair}, feature not implemented.") - def _check_depth_of_market( - self, - pair: str, - conf: Dict, - side: SignalDirection - ) -> bool: + def _check_depth_of_market(self, pair: str, conf: Dict, side: SignalDirection) -> bool: """ Checks depth of market before executing a buy """ diff --git a/tests/test_integration.py b/tests/test_integration.py index 606290495..d1fac3d71 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -311,8 +311,7 @@ def test_dca_short(default_conf_usdt, ticker_usdt, fee, mocker) -> None: # Reduce bid amount ticker_usdt_modif = ticker_usdt.return_value - ticker_usdt_modif['ask'] = ticker_usdt_modif['ask'] * 1.015 - ticker_usdt_modif['bid'] = ticker_usdt_modif['bid'] * 1.0125 + ticker_usdt_modif['ask'] = ticker_usdt_modif['ask'] * 1.004 mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value=ticker_usdt_modif) # additional buy order