Merge pull request #7093 from freqtrade/fix/gate_futures_stoposs

gateio futures - several fixes
This commit is contained in:
Matthias 2022-07-16 15:18:32 +02:00 committed by GitHub
commit 004bf31142
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 49 additions and 8 deletions

View File

@ -79,6 +79,7 @@ class Exchange:
"ccxt_futures_name": "swap", "ccxt_futures_name": "swap",
"fee_cost_in_contracts": False, # Fee cost needs contract conversion "fee_cost_in_contracts": False, # Fee cost needs contract conversion
"needs_trading_fees": False, # use fetch_trading_fees to cache fees "needs_trading_fees": False, # use fetch_trading_fees to cache fees
"order_props_in_contracts": ['amount', 'cost', 'filled', 'remaining'],
} }
_ft_has: Dict = {} _ft_has: Dict = {}
_ft_has_futures: Dict = {} _ft_has_futures: Dict = {}
@ -425,7 +426,7 @@ class Exchange:
if 'symbol' in order and order['symbol'] is not None: if 'symbol' in order and order['symbol'] is not None:
contract_size = self._get_contract_size(order['symbol']) contract_size = self._get_contract_size(order['symbol'])
if contract_size != 1: 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: if prop in order and order[prop] is not None:
order[prop] = order[prop] * contract_size order[prop] = order[prop] * contract_size
return order return order
@ -823,7 +824,7 @@ class Exchange:
'price': rate, 'price': rate,
'average': rate, 'average': rate,
'amount': _amount, 'amount': _amount,
'cost': _amount * rate / leverage, 'cost': _amount * rate,
'type': ordertype, 'type': ordertype,
'side': side, 'side': side,
'filled': 0, 'filled': 0,
@ -1648,7 +1649,7 @@ class Exchange:
fee_curr = fee.get('currency') fee_curr = fee.get('currency')
if fee_curr is None: if fee_curr is None:
return None return None
fee_cost = fee['cost'] fee_cost = float(fee['cost'])
if self._ft_has['fee_cost_in_contracts']: if self._ft_has['fee_cost_in_contracts']:
# Convert cost via "contracts" conversion # Convert cost via "contracts" conversion
fee_cost = self._contracts_to_amount(symbol, fee['cost']) fee_cost = self._contracts_to_amount(symbol, fee['cost'])
@ -1656,7 +1657,7 @@ class Exchange:
# Calculate fee based on order details # Calculate fee based on order details
if fee_curr == self.get_pair_base_currency(symbol): if fee_curr == self.get_pair_base_currency(symbol):
# Base currency - divide by amount # 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): elif fee_curr == self.get_pair_quote_currency(symbol):
# Quote currency - divide by cost # Quote currency - divide by cost
return round(fee_cost / cost, 8) if cost else None return round(fee_cost / cost, 8) if cost else None
@ -1687,7 +1688,7 @@ class Exchange:
:param amount: Amount of the order :param amount: Amount of the order
:return: Tuple with cost, currency, rate of the given fee dict :return: Tuple with cost, currency, rate of the given fee dict
""" """
return (fee['cost'], return (float(fee['cost']),
fee['currency'], fee['currency'],
self.calculate_fee_rate( self.calculate_fee_rate(
fee, fee,

View File

@ -1,12 +1,13 @@
""" Gate.io exchange subclass """ """ Gate.io exchange subclass """
import logging import logging
from datetime import datetime 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.constants import BuySell
from freqtrade.enums import MarginMode, TradingMode from freqtrade.enums import MarginMode, TradingMode
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.misc import safe_value_fallback2
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -34,6 +35,7 @@ class Gateio(Exchange):
_ft_has_futures: Dict = { _ft_has_futures: Dict = {
"needs_trading_fees": True, "needs_trading_fees": True,
"fee_cost_in_contracts": False, # Set explicitly to false for clarity "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]] = [ _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [
@ -96,12 +98,29 @@ class Gateio(Exchange):
} }
return trades 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: 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, order_id=order_id,
pair=pair, pair=pair,
params={'stop': True} 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: def cancel_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict:
return self.cancel_order( return self.cancel_order(

View File

@ -332,6 +332,8 @@ class FreqtradeBot(LoggingMixin):
if not trade.is_open and not trade.fee_updated(trade.exit_side): if not trade.is_open and not trade.fee_updated(trade.exit_side):
# Get sell fee # Get sell fee
order = trade.select_order(trade.exit_side, False) order = trade.select_order(trade.exit_side, False)
if not order:
order = trade.select_order('stoploss', False)
if order: if order:
logger.info( logger.info(
f"Updating {trade.exit_side}-fee on trade {trade}" f"Updating {trade.exit_side}-fee on trade {trade}"

View File

@ -1135,7 +1135,7 @@ def test_create_dry_run_order(default_conf, mocker, side, exchange_name, leverag
assert order["symbol"] == "ETH/BTC" assert order["symbol"] == "ETH/BTC"
assert order["amount"] == 1 assert order["amount"] == 1
assert order["leverage"] == leverage assert order["leverage"] == leverage
assert order["cost"] == 1 * 200 / leverage assert order["cost"] == 1 * 200
@pytest.mark.parametrize("side,startprice,endprice", [ @pytest.mark.parametrize("side,startprice,endprice", [

View File

@ -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]['pair'] == 'ETH/BTC'
assert fetch_order_mock.call_args_list[0][1]['params'] == {'stop': True} 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): def test_cancel_stoploss_order_gateio(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf, id='gateio') exchange = get_patched_exchange(mocker, default_conf, id='gateio')