From 054b63700180913d88535e79c5e6d3bde244ae81 Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Fri, 25 Mar 2022 06:56:05 -0600 Subject: [PATCH 1/3] Add amount_to_contracts and order_contracts_to_amount to stoploss --- freqtrade/exchange/exchange.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index fe47ca4d1..6ce6e3d54 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1103,11 +1103,12 @@ class Exchange: if self.trading_mode == TradingMode.FUTURES: params['reduceOnly'] = True - amount = self.amount_to_precision(pair, amount) + amount = self.amount_to_precision(pair, self._amount_to_contracts(pair, amount)) self._lev_prep(pair, leverage, side) order = self._api.create_order(symbol=pair, type=ordertype, side=side, amount=amount, price=rate, params=params) + order = self._order_contracts_to_amount(order) logger.info(f"stoploss {user_order_type} order added for {pair}. " f"stop price: {stop_price}. limit: {rate}") self._log_exchange_response('create_stoploss_order', order) From d3ea14de68c62d54798c668e7aee77ee182e5f5b Mon Sep 17 00:00:00 2001 From: Sam Germain Date: Fri, 25 Mar 2022 07:21:31 -0600 Subject: [PATCH 2/3] test_stoploss_contract_size --- tests/exchange/test_exchange.py | 45 ++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 9bb9db58f..23ff5a5c9 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -3966,7 +3966,7 @@ def test__fetch_and_calculate_funding_fees( exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange) mocker.patch('freqtrade.exchange.Exchange.timeframes', PropertyMock( - return_value=['1h', '4h', '8h'])) + return_value=['1h', '4h', '8h'])) funding_fees = exchange._fetch_and_calculate_funding_fees( pair='ADA/USDT', amount=amount, is_short=True, open_date=d1, close_date=d2) assert pytest.approx(funding_fees) == expected_fees @@ -4787,3 +4787,46 @@ def test_get_liquidation_price( buffer_amount = liquidation_buffer * abs(open_rate - expected_liq) expected_liq = expected_liq - buffer_amount if is_short else expected_liq + buffer_amount isclose(expected_liq, liq) + + +@pytest.mark.parametrize('contract_size,order_amount', [ + (10, 10), + (0.01, 10000), +]) +def test_stoploss_contract_size(mocker, default_conf, contract_size, order_amount): + api_mock = MagicMock() + order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) + + api_mock.create_order = MagicMock(return_value={ + 'id': order_id, + 'info': { + 'foo': 'bar' + }, + 'amount': order_amount, + 'cost': order_amount, + 'filled': order_amount, + 'remaining': order_amount, + 'symbol': 'ETH/BTC', + }) + default_conf['dry_run'] = False + mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y) + mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) + + exchange = get_patched_exchange(mocker, default_conf, api_mock) + exchange._get_contract_size = MagicMock(return_value=contract_size) + + api_mock.create_order.reset_mock() + order = exchange.stoploss( + pair='ETH/BTC', + amount=100, + stop_price=220, + order_types={}, + side='buy', + leverage=1.0 + ) + + assert api_mock.create_order.call_args_list[0][1]['amount'] == order_amount + assert order['amount'] == 100 + assert order['cost'] == 100 + assert order['filled'] == 100 + assert order['remaining'] == 100 From 46f4227329a7493a12886b38f7f64391e6565ef6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 25 Mar 2022 18:09:18 +0100 Subject: [PATCH 3/3] Check if symbol is not None --- freqtrade/exchange/exchange.py | 4 ++-- tests/exchange/test_exchange.py | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 6ce6e3d54..10e7056dc 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -402,7 +402,7 @@ class Exchange: return trades def _order_contracts_to_amount(self, order: Dict) -> Dict: - if 'symbol' in order: + if 'symbol' in order and order['symbol'] is not None: contract_size = self._get_contract_size(order['symbol']) if contract_size != 1: for prop in ['amount', 'cost', 'filled', 'remaining']: @@ -1108,10 +1108,10 @@ class Exchange: self._lev_prep(pair, leverage, side) order = self._api.create_order(symbol=pair, type=ordertype, side=side, amount=amount, price=rate, params=params) + self._log_exchange_response('create_stoploss_order', order) order = self._order_contracts_to_amount(order) logger.info(f"stoploss {user_order_type} order added for {pair}. " f"stop price: {stop_price}. limit: {rate}") - self._log_exchange_response('create_stoploss_order', order) return order except ccxt.InsufficientFunds as e: raise InsufficientFundsError( diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 23ff5a5c9..711b46e3e 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -4111,10 +4111,36 @@ def test__order_contracts_to_amount( 'trades': None, 'info': {}, }, + { + # Realistic stoploss order on gateio. + 'id': '123456380', + 'clientOrderId': '12345638203', + 'timestamp': None, + 'datetime': None, + 'lastTradeTimestamp': None, + 'status': None, + 'symbol': None, + 'type': None, + 'timeInForce': None, + 'postOnly': None, + 'side': None, + 'price': None, + 'stopPrice': None, + 'average': None, + 'amount': None, + 'cost': None, + 'filled': None, + 'remaining': None, + 'fee': None, + 'fees': [], + 'trades': None, + 'info': {}, + }, ] order1 = exchange._order_contracts_to_amount(orders[0]) order2 = exchange._order_contracts_to_amount(orders[1]) + exchange._order_contracts_to_amount(orders[2]) assert order1['amount'] == 30.0 * contract_size assert order2['amount'] == 40.0 * contract_size