Added side to execute_trade_exit
This commit is contained in:
parent
d582ccd2e6
commit
83bd674ba7
@ -756,7 +756,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
logger.error(f'Unable to place a stoploss order on exchange. {e}')
|
logger.error(f'Unable to place a stoploss order on exchange. {e}')
|
||||||
logger.warning('Exiting the trade forcefully')
|
logger.warning('Exiting the trade forcefully')
|
||||||
self.execute_trade_exit(trade, trade.stop_loss, sell_reason=SellCheckTuple(
|
self.execute_trade_exit(trade, trade.stop_loss, sell_reason=SellCheckTuple(
|
||||||
sell_type=SellType.EMERGENCY_SELL))
|
sell_type=SellType.EMERGENCY_SELL), side=trade.exit_side)
|
||||||
|
|
||||||
except ExchangeError:
|
except ExchangeError:
|
||||||
trade.stoploss_order_id = None
|
trade.stoploss_order_id = None
|
||||||
@ -876,7 +876,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
|
|
||||||
if should_exit.sell_flag:
|
if should_exit.sell_flag:
|
||||||
logger.info(f'Exit for {trade.pair} detected. Reason: {should_exit.sell_type}')
|
logger.info(f'Exit for {trade.pair} detected. Reason: {should_exit.sell_type}')
|
||||||
self.execute_trade_exit(trade, exit_rate, should_exit)
|
self.execute_trade_exit(trade, exit_rate, should_exit, side=trade.exit_side)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -1081,21 +1081,28 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
raise DependencyException(
|
raise DependencyException(
|
||||||
f"Not enough amount to exit trade. Trade-amount: {amount}, Wallet: {wallet_amount}")
|
f"Not enough amount to exit trade. Trade-amount: {amount}, Wallet: {wallet_amount}")
|
||||||
|
|
||||||
def execute_trade_exit(self, trade: Trade, limit: float, sell_reason: SellCheckTuple) -> bool:
|
def execute_trade_exit(
|
||||||
|
self,
|
||||||
|
trade: Trade,
|
||||||
|
limit: float,
|
||||||
|
sell_reason: SellCheckTuple, # TODO-lev update to exit_reason
|
||||||
|
side: str
|
||||||
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Executes a trade exit for the given trade and limit
|
Executes a trade exit for the given trade and limit
|
||||||
:param trade: Trade instance
|
:param trade: Trade instance
|
||||||
:param limit: limit rate for the sell order
|
:param limit: limit rate for the sell order
|
||||||
:param sell_reason: Reason the sell was triggered
|
:param sell_reason: Reason the sell was triggered
|
||||||
|
:param side: "buy" or "sell"
|
||||||
:return: True if it succeeds (supported) False (not supported)
|
:return: True if it succeeds (supported) False (not supported)
|
||||||
"""
|
"""
|
||||||
sell_type = 'sell' # TODO-lev: Update to exit
|
exit_type = 'sell' # TODO-lev: Update to exit
|
||||||
if sell_reason.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS):
|
if sell_reason.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS):
|
||||||
sell_type = 'stoploss'
|
exit_type = 'stoploss'
|
||||||
|
|
||||||
# if stoploss is on exchange and we are on dry_run mode,
|
# if stoploss is on exchange and we are on dry_run mode,
|
||||||
# we consider the sell price stop price
|
# we consider the sell price stop price
|
||||||
if self.config['dry_run'] and sell_type == 'stoploss' \
|
if self.config['dry_run'] and exit_type == 'stoploss' \
|
||||||
and self.strategy.order_types['stoploss_on_exchange']:
|
and self.strategy.order_types['stoploss_on_exchange']:
|
||||||
limit = trade.stop_loss
|
limit = trade.stop_loss
|
||||||
|
|
||||||
@ -1119,7 +1126,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
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}")
|
||||||
|
|
||||||
order_type = self.strategy.order_types[sell_type]
|
order_type = self.strategy.order_types[exit_type]
|
||||||
if sell_reason.sell_type == SellType.EMERGENCY_SELL:
|
if sell_reason.sell_type == SellType.EMERGENCY_SELL:
|
||||||
# Emergency sells (default to market!)
|
# Emergency sells (default to market!)
|
||||||
order_type = self.strategy.order_types.get("emergencysell", "market")
|
order_type = self.strategy.order_types.get("emergencysell", "market")
|
||||||
@ -1143,10 +1150,10 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
order = self.exchange.create_order(
|
order = self.exchange.create_order(
|
||||||
pair=trade.pair,
|
pair=trade.pair,
|
||||||
ordertype=order_type,
|
ordertype=order_type,
|
||||||
side="sell",
|
|
||||||
amount=amount,
|
amount=amount,
|
||||||
rate=limit,
|
rate=limit,
|
||||||
time_in_force=time_in_force
|
time_in_force=time_in_force,
|
||||||
|
side=trade.exit_side
|
||||||
)
|
)
|
||||||
except InsufficientFundsError as e:
|
except InsufficientFundsError as e:
|
||||||
logger.warning(f"Unable to place order {e}.")
|
logger.warning(f"Unable to place order {e}.")
|
||||||
@ -1154,7 +1161,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
self.handle_insufficient_funds(trade)
|
self.handle_insufficient_funds(trade)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
order_obj = Order.parse_from_ccxt_object(order, trade.pair, 'sell')
|
order_obj = Order.parse_from_ccxt_object(order, trade.pair, trade.exit_side)
|
||||||
trade.orders.append(order_obj)
|
trade.orders.append(order_obj)
|
||||||
|
|
||||||
trade.open_order_id = order['id']
|
trade.open_order_id = order['id']
|
||||||
|
@ -561,7 +561,7 @@ class RPC:
|
|||||||
current_rate = self._freqtrade.exchange.get_rate(
|
current_rate = self._freqtrade.exchange.get_rate(
|
||||||
trade.pair, refresh=False, side="sell")
|
trade.pair, refresh=False, side="sell")
|
||||||
sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL)
|
sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL)
|
||||||
self._freqtrade.execute_trade_exit(trade, current_rate, sell_reason)
|
self._freqtrade.execute_trade_exit(trade, current_rate, sell_reason, side="sell")
|
||||||
# ---- EOF def _exec_forcesell ----
|
# ---- EOF def _exec_forcesell ----
|
||||||
|
|
||||||
if self._freqtrade.state != State.RUNNING:
|
if self._freqtrade.state != State.RUNNING:
|
||||||
|
@ -2651,6 +2651,7 @@ def test_handle_cancel_exit_cancel_exception(mocker, default_conf) -> None:
|
|||||||
assert freqtrade.handle_cancel_exit(trade, order, reason) == 'error cancelling order'
|
assert freqtrade.handle_cancel_exit(trade, order, reason) == 'error cancelling order'
|
||||||
|
|
||||||
|
|
||||||
|
# TODO-lev: Add short tests
|
||||||
def test_execute_trade_exit_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> None:
|
def test_execute_trade_exit_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> None:
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -2679,15 +2680,16 @@ def test_execute_trade_exit_up(default_conf, ticker, fee, ticker_sell_up, mocker
|
|||||||
fetch_ticker=ticker_sell_up
|
fetch_ticker=ticker_sell_up
|
||||||
)
|
)
|
||||||
# Prevented sell ...
|
# Prevented sell ...
|
||||||
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'],
|
# TODO-lev: side="buy"
|
||||||
|
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], side="sell",
|
||||||
sell_reason=SellCheckTuple(sell_type=SellType.ROI))
|
sell_reason=SellCheckTuple(sell_type=SellType.ROI))
|
||||||
assert rpc_mock.call_count == 0
|
assert rpc_mock.call_count == 0
|
||||||
assert freqtrade.strategy.confirm_trade_exit.call_count == 1
|
assert freqtrade.strategy.confirm_trade_exit.call_count == 1
|
||||||
|
|
||||||
# Repatch with true
|
# Repatch with true
|
||||||
freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True)
|
freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True)
|
||||||
|
# TODO-lev: side="buy"
|
||||||
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'],
|
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], side="sell",
|
||||||
sell_reason=SellCheckTuple(sell_type=SellType.ROI))
|
sell_reason=SellCheckTuple(sell_type=SellType.ROI))
|
||||||
assert freqtrade.strategy.confirm_trade_exit.call_count == 1
|
assert freqtrade.strategy.confirm_trade_exit.call_count == 1
|
||||||
|
|
||||||
@ -2739,8 +2741,8 @@ def test_execute_trade_exit_down(default_conf, ticker, fee, ticker_sell_down, mo
|
|||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
fetch_ticker=ticker_sell_down
|
fetch_ticker=ticker_sell_down
|
||||||
)
|
)
|
||||||
|
# TODO-lev: side="buy"
|
||||||
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'],
|
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], side="sell",
|
||||||
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
|
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
|
||||||
|
|
||||||
assert rpc_mock.call_count == 2
|
assert rpc_mock.call_count == 2
|
||||||
@ -2800,8 +2802,8 @@ def test_execute_trade_exit_custom_exit_price(default_conf, ticker, fee, ticker_
|
|||||||
|
|
||||||
# Set a custom exit price
|
# Set a custom exit price
|
||||||
freqtrade.strategy.custom_exit_price = lambda **kwargs: 1.170e-05
|
freqtrade.strategy.custom_exit_price = lambda **kwargs: 1.170e-05
|
||||||
|
# TODO-lev: side="buy"
|
||||||
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'],
|
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], side="sell",
|
||||||
sell_reason=SellCheckTuple(sell_type=SellType.SELL_SIGNAL))
|
sell_reason=SellCheckTuple(sell_type=SellType.SELL_SIGNAL))
|
||||||
|
|
||||||
# Sell price must be different to default bid price
|
# Sell price must be different to default bid price
|
||||||
@ -2863,7 +2865,8 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(default_conf, tick
|
|||||||
# Setting trade stoploss to 0.01
|
# Setting trade stoploss to 0.01
|
||||||
|
|
||||||
trade.stop_loss = 0.00001099 * 0.99
|
trade.stop_loss = 0.00001099 * 0.99
|
||||||
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'],
|
# TODO-lev: side="buy"
|
||||||
|
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], side="sell",
|
||||||
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
|
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
|
||||||
|
|
||||||
assert rpc_mock.call_count == 2
|
assert rpc_mock.call_count == 2
|
||||||
@ -2919,7 +2922,8 @@ def test_execute_trade_exit_sloe_cancel_exception(
|
|||||||
freqtrade.config['dry_run'] = False
|
freqtrade.config['dry_run'] = False
|
||||||
trade.stoploss_order_id = "abcd"
|
trade.stoploss_order_id = "abcd"
|
||||||
|
|
||||||
freqtrade.execute_trade_exit(trade=trade, limit=1234,
|
# TODO-lev: side="buy"
|
||||||
|
freqtrade.execute_trade_exit(trade=trade, limit=1234, side="sell",
|
||||||
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
|
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
|
||||||
assert create_order_mock.call_count == 2
|
assert create_order_mock.call_count == 2
|
||||||
assert log_has('Could not cancel stoploss order abcd', caplog)
|
assert log_has('Could not cancel stoploss order abcd', caplog)
|
||||||
@ -2970,7 +2974,8 @@ def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee,
|
|||||||
fetch_ticker=ticker_sell_up
|
fetch_ticker=ticker_sell_up
|
||||||
)
|
)
|
||||||
|
|
||||||
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'],
|
# TODO-lev: side="buy"
|
||||||
|
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], side="sell",
|
||||||
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
|
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
@ -3078,7 +3083,8 @@ def test_execute_trade_exit_market_order(default_conf, ticker, fee,
|
|||||||
)
|
)
|
||||||
freqtrade.config['order_types']['sell'] = 'market'
|
freqtrade.config['order_types']['sell'] = 'market'
|
||||||
|
|
||||||
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'],
|
# TODO-lev: side="buy"
|
||||||
|
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'], side="sell",
|
||||||
sell_reason=SellCheckTuple(sell_type=SellType.ROI))
|
sell_reason=SellCheckTuple(sell_type=SellType.ROI))
|
||||||
|
|
||||||
assert not trade.is_open
|
assert not trade.is_open
|
||||||
@ -3137,8 +3143,9 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf, ticker, fee,
|
|||||||
)
|
)
|
||||||
|
|
||||||
sell_reason = SellCheckTuple(sell_type=SellType.ROI)
|
sell_reason = SellCheckTuple(sell_type=SellType.ROI)
|
||||||
|
# TODO-lev: side="buy"
|
||||||
assert not freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'],
|
assert not freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_up()['bid'],
|
||||||
sell_reason=sell_reason)
|
sell_reason=sell_reason, side="sell")
|
||||||
assert mock_insuf.call_count == 1
|
assert mock_insuf.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
@ -3394,7 +3401,8 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo
|
|||||||
fetch_ticker=ticker_sell_down
|
fetch_ticker=ticker_sell_down
|
||||||
)
|
)
|
||||||
|
|
||||||
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'],
|
# TODO-lev: side="buy"
|
||||||
|
freqtrade.execute_trade_exit(trade=trade, limit=ticker_sell_down()['bid'], side="sell",
|
||||||
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
|
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
|
||||||
trade.close(ticker_sell_down()['bid'])
|
trade.close(ticker_sell_down()['bid'])
|
||||||
assert freqtrade.strategy.is_pair_locked(trade.pair)
|
assert freqtrade.strategy.is_pair_locked(trade.pair)
|
||||||
|
Loading…
Reference in New Issue
Block a user