Merge pull request #6513 from samgermain/gateio-stoploss

Gateio stoploss on exchange
This commit is contained in:
Matthias 2022-03-15 06:27:54 +01:00 committed by GitHub
commit ebd61ebdef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 72 additions and 7 deletions

View File

@ -210,6 +210,9 @@ OKX requires a passphrase for each api key, you will therefore need to add this
## Gate.io ## Gate.io
!!! Tip "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). 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. 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.

View File

@ -24,7 +24,7 @@ These modes can be configured with these values:
``` ```
!!! Note !!! 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.
<ins>Do not set too low/tight stoploss value if using stop loss on exchange!</ins> <ins>Do not set too low/tight stoploss value if using stop loss on exchange!</ins>
If set to low/tight then you have greater risk of missing fill on the order and stoploss will not work. If set to low/tight then you have greater risk of missing fill on the order and stoploss will not work.

View File

@ -376,7 +376,7 @@ class Exchange:
raise OperationalException( raise OperationalException(
'Could not load markets, therefore cannot start. ' 'Could not load markets, therefore cannot start. '
'Please investigate the above error for more details.' 'Please investigate the above error for more details.'
) )
quote_currencies = self.get_quote_currencies() quote_currencies = self.get_quote_currencies()
if stake_currency not in quote_currencies: if stake_currency not in quote_currencies:
raise OperationalException( raise OperationalException(
@ -882,11 +882,11 @@ class Exchange:
raise OperationalException(e) from e raise OperationalException(e) from e
@retrier(retries=API_FETCH_ORDER_RETRY_COUNT) @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']: if self._config['dry_run']:
return self.fetch_dry_run_order(order_id) return self.fetch_dry_run_order(order_id)
try: 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) self._log_exchange_response('fetch_order', order)
return order return order
except ccxt.OrderNotFound as e: except ccxt.OrderNotFound as e:
@ -929,7 +929,7 @@ class Exchange:
and order.get('filled') == 0.0) and order.get('filled') == 0.0)
@retrier @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']: if self._config['dry_run']:
try: try:
order = self.fetch_dry_run_order(order_id) order = self.fetch_dry_run_order(order_id)
@ -940,7 +940,7 @@ class Exchange:
return {} return {}
try: 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) self._log_exchange_response('cancel_order', order)
return order return order
except ccxt.InvalidOrder as e: except ccxt.InvalidOrder as e:

View File

@ -22,6 +22,8 @@ class Gateio(Exchange):
_ft_has: Dict = { _ft_has: Dict = {
"ohlcv_candle_limit": 1000, "ohlcv_candle_limit": 1000,
"ohlcv_volume_currency": "quote", "ohlcv_volume_currency": "quote",
"stoploss_order_types": {"limit": "limit"},
"stoploss_on_exchange": True,
} }
_headers = {'X-Gate-Channel-Id': 'freqtrade'} _headers = {'X-Gate-Channel-Id': 'freqtrade'}
@ -31,4 +33,25 @@ class Gateio(Exchange):
if any(v == 'market' for k, v in order_types.items()): if any(v == 'market' for k, v in order_types.items()):
raise OperationalException( 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}
)
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'])

View File

@ -1,8 +1,11 @@
from unittest.mock import MagicMock
import pytest import pytest
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.exchange import Gateio from freqtrade.exchange import Gateio
from freqtrade.resolvers.exchange_resolver import ExchangeResolver from freqtrade.resolvers.exchange_resolver import ExchangeResolver
from tests.conftest import get_patched_exchange
def test_validate_order_types_gateio(default_conf, mocker): def test_validate_order_types_gateio(default_conf, mocker):
@ -26,3 +29,39 @@ def test_validate_order_types_gateio(default_conf, mocker):
with pytest.raises(OperationalException, with pytest.raises(OperationalException,
match=r'Exchange .* does not support market orders.'): match=r'Exchange .* does not support market orders.'):
ExchangeResolver.load_exchange('gateio', default_conf, True) 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}
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)