From 7c4dd4c48c7257f433669522ff1bd14be9d8133a Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 15 Jul 2022 09:54:53 +0200 Subject: [PATCH 1/5] Support fee cost as string closes #7056 --- freqtrade/exchange/exchange.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index cd13964c4..f6b85436d 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1646,7 +1646,7 @@ class Exchange: fee_curr = fee.get('currency') if fee_curr is None: return None - fee_cost = fee['cost'] + fee_cost = float(fee['cost']) if self._ft_has['fee_cost_in_contracts']: # Convert cost via "contracts" conversion fee_cost = self._contracts_to_amount(symbol, fee['cost']) @@ -1654,7 +1654,7 @@ class Exchange: # Calculate fee based on order details if fee_curr == self.get_pair_base_currency(symbol): # Base currency - divide by amount - return round(fee['cost'] / amount, 8) + return round(fee_cost / amount, 8) elif fee_curr == self.get_pair_quote_currency(symbol): # Quote currency - divide by cost return round(fee_cost / cost, 8) if cost else None @@ -1685,7 +1685,7 @@ class Exchange: :param amount: Amount of the order :return: Tuple with cost, currency, rate of the given fee dict """ - return (fee['cost'], + return (float(fee['cost']), fee['currency'], self.calculate_fee_rate( fee, From 7b8a5585dd6b2cb7cbfc550c30d2353af3869616 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 15 Jul 2022 14:35:49 +0200 Subject: [PATCH 2/5] Fetch 2ndary stoploss order once the order triggered. --- freqtrade/exchange/gateio.py | 14 +++++++++++++- tests/exchange/test_gateio.py | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index b9de212de..f46508022 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -97,11 +97,23 @@ class Gateio(Exchange): return trades def fetch_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: - return self.fetch_order( + order = self.fetch_order( order_id=order_id, pair=pair, params={'stop': True} ) + if self.trading_mode == TradingMode.FUTURES: + if order['status'] == 'closed': + # Places a real order - which we need to fetch explicitly. + new_orderid = order.get('info', {}).get('trade_id') + if new_orderid: + order1 = self.fetch_order(order_id=new_orderid, pair=pair, params=params) + order1['id_stop'] = order1['id'] + order1['id'] = order_id + order1['stopPrice'] = order.get('stopPrice') + + return order1 + return order def cancel_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: return self.cancel_order( diff --git a/tests/exchange/test_gateio.py b/tests/exchange/test_gateio.py index cbd4776fb..dabdbba65 100644 --- a/tests/exchange/test_gateio.py +++ b/tests/exchange/test_gateio.py @@ -53,6 +53,25 @@ def test_fetch_stoploss_order_gateio(default_conf, mocker): assert fetch_order_mock.call_args_list[0][1]['pair'] == 'ETH/BTC' assert fetch_order_mock.call_args_list[0][1]['params'] == {'stop': True} + default_conf['trading_mode'] = 'futures' + default_conf['margin_mode'] = 'isolated' + + exchange = get_patched_exchange(mocker, default_conf, id='gateio') + + exchange.fetch_order = MagicMock(return_value={ + 'status': 'closed', + 'id': '1234', + 'stopPrice': 5.62, + 'info': { + 'trade_id': '222555' + } + }) + + exchange.fetch_stoploss_order('1234', 'ETH/BTC') + assert exchange.fetch_order.call_count == 2 + assert exchange.fetch_order.call_args_list[0][1]['order_id'] == '1234' + assert exchange.fetch_order.call_args_list[1][1]['order_id'] == '222555' + def test_cancel_stoploss_order_gateio(default_conf, mocker): exchange = get_patched_exchange(mocker, default_conf, id='gateio') From 415780a4fe1c37785ebe26c398762f467ed5b570 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 16 Jul 2022 11:58:38 +0200 Subject: [PATCH 3/5] gateio order cost is not in contracts closes #7081 --- freqtrade/exchange/exchange.py | 3 ++- freqtrade/exchange/gateio.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index f6b85436d..7ac45bcc9 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -79,6 +79,7 @@ class Exchange: "ccxt_futures_name": "swap", "fee_cost_in_contracts": False, # Fee cost needs contract conversion "needs_trading_fees": False, # use fetch_trading_fees to cache fees + "order_props_in_contracts": ['amount', 'cost', 'filled', 'remaining'], } _ft_has: Dict = {} _ft_has_futures: Dict = {} @@ -423,7 +424,7 @@ class Exchange: 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']: + for prop in self._ft_has.get('order_props_in_contracts', []): if prop in order and order[prop] is not None: order[prop] = order[prop] * contract_size return order diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index f46508022..c049ce4cc 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -34,6 +34,7 @@ class Gateio(Exchange): _ft_has_futures: Dict = { "needs_trading_fees": True, "fee_cost_in_contracts": False, # Set explicitly to false for clarity + "order_props_in_contracts": ['amount', 'filled', 'remaining'], } _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ From ed64e4299bb6b728513f3063a56e6fceda777e90 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 16 Jul 2022 12:52:35 +0200 Subject: [PATCH 4/5] Stoploss orders should also be eligible to update closed fees --- freqtrade/exchange/gateio.py | 8 +++++++- freqtrade/freqtradebot.py | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index c049ce4cc..6df3425d2 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -1,12 +1,13 @@ """ Gate.io exchange subclass """ import logging from datetime import datetime -from typing import Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple from freqtrade.constants import BuySell from freqtrade.enums import MarginMode, TradingMode from freqtrade.exceptions import OperationalException from freqtrade.exchange import Exchange +from freqtrade.misc import safe_value_fallback2 logger = logging.getLogger(__name__) @@ -97,6 +98,11 @@ class Gateio(Exchange): } return trades + def get_order_id_conditional(self, order: Dict[str, Any]) -> str: + if self.trading_mode == TradingMode.FUTURES: + return safe_value_fallback2(order, order, 'id_stop', 'id') + return order['id'] + def fetch_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: order = self.fetch_order( order_id=order_id, diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 469bfda7e..2007f9b4e 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -332,6 +332,8 @@ class FreqtradeBot(LoggingMixin): if not trade.is_open and not trade.fee_updated(trade.exit_side): # Get sell fee order = trade.select_order(trade.exit_side, False) + if not order: + order = trade.select_order('stoploss', False) if order: logger.info( f"Updating {trade.exit_side}-fee on trade {trade}" From d03dfb393497fe63b042ebd22d932a0bcabcbfec Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 16 Jul 2022 13:12:50 +0200 Subject: [PATCH 5/5] Oder cost is real cost (including leverage) --- freqtrade/exchange/exchange.py | 2 +- tests/exchange/test_exchange.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 7ac45bcc9..43fbbe28b 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -822,7 +822,7 @@ class Exchange: 'price': rate, 'average': rate, 'amount': _amount, - 'cost': _amount * rate / leverage, + 'cost': _amount * rate, 'type': ordertype, 'side': side, 'filled': 0, diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index acd48b3fd..bf4f44440 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1135,7 +1135,7 @@ def test_create_dry_run_order(default_conf, mocker, side, exchange_name, leverag assert order["symbol"] == "ETH/BTC" assert order["amount"] == 1 assert order["leverage"] == leverage - assert order["cost"] == 1 * 200 / leverage + assert order["cost"] == 1 * 200 @pytest.mark.parametrize("side,startprice,endprice", [