Use get_stoploss_order and cancel_stoploss_order
This allows exchanges to use stoploss which don't have the same endpoints
This commit is contained in:
parent
d90d6ed5d0
commit
f83c1c5abf
@ -79,7 +79,7 @@ class Exchange:
|
|||||||
|
|
||||||
if config['dry_run']:
|
if config['dry_run']:
|
||||||
logger.info('Instance is running with dry_run enabled')
|
logger.info('Instance is running with dry_run enabled')
|
||||||
|
logger.info(f"Using CCXT {ccxt.__version__}")
|
||||||
exchange_config = config['exchange']
|
exchange_config = config['exchange']
|
||||||
|
|
||||||
# Deep merge ft_has with default ft_has options
|
# Deep merge ft_has with default ft_has options
|
||||||
@ -952,6 +952,9 @@ class Exchange:
|
|||||||
except ccxt.BaseError as e:
|
except ccxt.BaseError as e:
|
||||||
raise OperationalException(e) from e
|
raise OperationalException(e) from e
|
||||||
|
|
||||||
|
# Assign method to get_stoploss_order to allow easy overriding in other classes
|
||||||
|
cancel_stoploss_order = cancel_order
|
||||||
|
|
||||||
def is_cancel_order_result_suitable(self, corder) -> bool:
|
def is_cancel_order_result_suitable(self, corder) -> bool:
|
||||||
if not isinstance(corder, dict):
|
if not isinstance(corder, dict):
|
||||||
return False
|
return False
|
||||||
@ -1004,6 +1007,9 @@ class Exchange:
|
|||||||
except ccxt.BaseError as e:
|
except ccxt.BaseError as e:
|
||||||
raise OperationalException(e) from e
|
raise OperationalException(e) from e
|
||||||
|
|
||||||
|
# Assign method to get_stoploss_order to allow easy overriding in other classes
|
||||||
|
get_stoploss_order = get_order
|
||||||
|
|
||||||
@retrier
|
@retrier
|
||||||
def fetch_l2_order_book(self, pair: str, limit: int = 100) -> dict:
|
def fetch_l2_order_book(self, pair: str, limit: int = 100) -> dict:
|
||||||
"""
|
"""
|
||||||
|
@ -7,6 +7,7 @@ import ccxt
|
|||||||
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
|
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
|
||||||
OperationalException, TemporaryError)
|
OperationalException, TemporaryError)
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
|
from freqtrade.exchange.common import retrier
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -66,3 +67,46 @@ class Ftx(Exchange):
|
|||||||
f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e
|
f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e
|
||||||
except ccxt.BaseError as e:
|
except ccxt.BaseError as e:
|
||||||
raise OperationalException(e) from e
|
raise OperationalException(e) from e
|
||||||
|
|
||||||
|
@retrier
|
||||||
|
def get_stoploss_order(self, order_id: str, pair: str) -> Dict:
|
||||||
|
if self._config['dry_run']:
|
||||||
|
try:
|
||||||
|
order = self._dry_run_open_orders[order_id]
|
||||||
|
return order
|
||||||
|
except KeyError as e:
|
||||||
|
# Gracefully handle errors with dry-run orders.
|
||||||
|
raise InvalidOrderException(
|
||||||
|
f'Tried to get an invalid dry-run-order (id: {order_id}). Message: {e}') from e
|
||||||
|
try:
|
||||||
|
orders = self._api.fetch_orders('BNB/USD', None, params={'type': 'stop'})
|
||||||
|
|
||||||
|
order = [order for order in orders if order['id'] == order_id]
|
||||||
|
if len(order) == 1:
|
||||||
|
return order[0]
|
||||||
|
else:
|
||||||
|
raise InvalidOrderException(f"Could not get Stoploss Order for id {order_id}")
|
||||||
|
|
||||||
|
except ccxt.InvalidOrder as e:
|
||||||
|
raise InvalidOrderException(
|
||||||
|
f'Tried to get an invalid order (id: {order_id}). Message: {e}') from e
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not get order due to {e.__class__.__name__}. Message: {e}') from e
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e) from e
|
||||||
|
|
||||||
|
@retrier
|
||||||
|
def cancel_stoploss_order(self, order_id: str, pair: str) -> None:
|
||||||
|
if self._config['dry_run']:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
return self._api.cancel_order(order_id, pair, params={'type': 'stop'})
|
||||||
|
except ccxt.InvalidOrder as e:
|
||||||
|
raise InvalidOrderException(
|
||||||
|
f'Could not cancel order. Message: {e}') from e
|
||||||
|
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
|
||||||
|
raise TemporaryError(
|
||||||
|
f'Could not cancel order due to {e.__class__.__name__}. Message: {e}') from e
|
||||||
|
except ccxt.BaseError as e:
|
||||||
|
raise OperationalException(e) from e
|
||||||
|
@ -774,13 +774,13 @@ class FreqtradeBot:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# First we check if there is already a stoploss on exchange
|
# First we check if there is already a stoploss on exchange
|
||||||
stoploss_order = self.exchange.get_order(trade.stoploss_order_id, trade.pair) \
|
stoploss_order = self.exchange.get_stoploss_order(trade.stoploss_order_id, trade.pair) \
|
||||||
if trade.stoploss_order_id else None
|
if trade.stoploss_order_id else None
|
||||||
except InvalidOrderException as exception:
|
except InvalidOrderException as exception:
|
||||||
logger.warning('Unable to fetch stoploss order: %s', exception)
|
logger.warning('Unable to fetch stoploss order: %s', exception)
|
||||||
|
|
||||||
# We check if stoploss order is fulfilled
|
# We check if stoploss order is fulfilled
|
||||||
if stoploss_order and stoploss_order['status'] == 'closed':
|
if stoploss_order and stoploss_order['status'] in ('closed', 'triggered'):
|
||||||
trade.sell_reason = SellType.STOPLOSS_ON_EXCHANGE.value
|
trade.sell_reason = SellType.STOPLOSS_ON_EXCHANGE.value
|
||||||
self.update_trade_state(trade, stoploss_order, sl_order=True)
|
self.update_trade_state(trade, stoploss_order, sl_order=True)
|
||||||
# Lock pair for one candle to prevent immediate rebuys
|
# Lock pair for one candle to prevent immediate rebuys
|
||||||
@ -807,7 +807,7 @@ class FreqtradeBot:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# If stoploss order is canceled for some reason we add it
|
# If stoploss order is canceled for some reason we add it
|
||||||
if stoploss_order and stoploss_order['status'] == 'canceled':
|
if stoploss_order and stoploss_order['status'] in ('canceled', 'cancelled'):
|
||||||
if self.create_stoploss_order(trade=trade, stop_price=trade.stop_loss,
|
if self.create_stoploss_order(trade=trade, stop_price=trade.stop_loss,
|
||||||
rate=trade.stop_loss):
|
rate=trade.stop_loss):
|
||||||
return False
|
return False
|
||||||
@ -840,7 +840,7 @@ class FreqtradeBot:
|
|||||||
logger.info('Trailing stoploss: cancelling current stoploss on exchange (id:{%s}) '
|
logger.info('Trailing stoploss: cancelling current stoploss on exchange (id:{%s}) '
|
||||||
'in order to add another one ...', order['id'])
|
'in order to add another one ...', order['id'])
|
||||||
try:
|
try:
|
||||||
self.exchange.cancel_order(order['id'], trade.pair)
|
self.exchange.cancel_stoploss_order(order['id'], trade.pair)
|
||||||
except InvalidOrderException:
|
except InvalidOrderException:
|
||||||
logger.exception(f"Could not cancel stoploss order {order['id']} "
|
logger.exception(f"Could not cancel stoploss order {order['id']} "
|
||||||
f"for pair {trade.pair}")
|
f"for pair {trade.pair}")
|
||||||
@ -1068,7 +1068,7 @@ class FreqtradeBot:
|
|||||||
# First cancelling stoploss on exchange ...
|
# First cancelling stoploss on exchange ...
|
||||||
if self.strategy.order_types.get('stoploss_on_exchange') and trade.stoploss_order_id:
|
if self.strategy.order_types.get('stoploss_on_exchange') and trade.stoploss_order_id:
|
||||||
try:
|
try:
|
||||||
self.exchange.cancel_order(trade.stoploss_order_id, trade.pair)
|
self.exchange.cancel_stoploss_order(trade.stoploss_order_id, trade.pair)
|
||||||
except InvalidOrderException:
|
except InvalidOrderException:
|
||||||
logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}")
|
logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user