sell_type -> exit_type
This commit is contained in:
parent
e9d3903827
commit
0037754969
@ -915,7 +915,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, exit_reason=SellCheckTuple(
|
self.execute_trade_exit(trade, trade.stop_loss, exit_reason=SellCheckTuple(
|
||||||
sell_type=ExitType.EMERGENCY_SELL))
|
exit_type=ExitType.EMERGENCY_SELL))
|
||||||
|
|
||||||
except ExchangeError:
|
except ExchangeError:
|
||||||
trade.stoploss_order_id = None
|
trade.stoploss_order_id = None
|
||||||
@ -1040,7 +1040,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.exit_type}'
|
||||||
f'Tag: {exit_tag if exit_tag is not None else "None"}')
|
f'Tag: {exit_tag if exit_tag is not None else "None"}')
|
||||||
self.execute_trade_exit(trade, exit_rate, should_exit, exit_tag=exit_tag)
|
self.execute_trade_exit(trade, exit_rate, should_exit, exit_tag=exit_tag)
|
||||||
return True
|
return True
|
||||||
@ -1102,7 +1102,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
try:
|
try:
|
||||||
self.execute_trade_exit(
|
self.execute_trade_exit(
|
||||||
trade, order.get('price'),
|
trade, order.get('price'),
|
||||||
exit_reason=SellCheckTuple(sell_type=ExitType.EMERGENCY_SELL))
|
exit_reason=SellCheckTuple(exit_type=ExitType.EMERGENCY_SELL))
|
||||||
except DependencyException as exception:
|
except DependencyException as exception:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f'Unable to emergency sell trade {trade.pair}: {exception}')
|
f'Unable to emergency sell trade {trade.pair}: {exception}')
|
||||||
@ -1284,7 +1284,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
trade.open_date
|
trade.open_date
|
||||||
)
|
)
|
||||||
exit_type = 'sell' # TODO-lev: Update to exit
|
exit_type = 'sell' # TODO-lev: Update to exit
|
||||||
if exit_reason.sell_type in (ExitType.STOP_LOSS, ExitType.TRAILING_STOP_LOSS):
|
if exit_reason.exit_type in (ExitType.STOP_LOSS, ExitType.TRAILING_STOP_LOSS):
|
||||||
exit_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,
|
||||||
@ -1314,7 +1314,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
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 = ordertype or self.strategy.order_types[exit_type]
|
order_type = ordertype or self.strategy.order_types[exit_type]
|
||||||
if exit_reason.sell_type == ExitType.EMERGENCY_SELL:
|
if exit_reason.exit_type == ExitType.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")
|
||||||
|
|
||||||
|
@ -314,7 +314,7 @@ class Backtesting:
|
|||||||
Get close rate for backtesting result
|
Get close rate for backtesting result
|
||||||
"""
|
"""
|
||||||
# Special handling if high or low hit STOP_LOSS or ROI
|
# Special handling if high or low hit STOP_LOSS or ROI
|
||||||
if sell.sell_type in (ExitType.STOP_LOSS, ExitType.TRAILING_STOP_LOSS):
|
if sell.exit_type in (ExitType.STOP_LOSS, ExitType.TRAILING_STOP_LOSS):
|
||||||
if trade.stop_loss > sell_row[HIGH_IDX]:
|
if trade.stop_loss > sell_row[HIGH_IDX]:
|
||||||
# our stoploss was already higher than candle high,
|
# our stoploss was already higher than candle high,
|
||||||
# possibly due to a cancelled trade exit.
|
# possibly due to a cancelled trade exit.
|
||||||
@ -324,7 +324,7 @@ class Backtesting:
|
|||||||
# Special case: trailing triggers within same candle as trade opened. Assume most
|
# Special case: trailing triggers within same candle as trade opened. Assume most
|
||||||
# pessimistic price movement, which is moving just enough to arm stoploss and
|
# pessimistic price movement, which is moving just enough to arm stoploss and
|
||||||
# immediately going down to stop price.
|
# immediately going down to stop price.
|
||||||
if sell.sell_type == ExitType.TRAILING_STOP_LOSS and trade_dur == 0:
|
if sell.exit_type == ExitType.TRAILING_STOP_LOSS and trade_dur == 0:
|
||||||
if (
|
if (
|
||||||
not self.strategy.use_custom_stoploss and self.strategy.trailing_stop
|
not self.strategy.use_custom_stoploss and self.strategy.trailing_stop
|
||||||
and self.strategy.trailing_only_offset_is_reached
|
and self.strategy.trailing_only_offset_is_reached
|
||||||
@ -345,7 +345,7 @@ class Backtesting:
|
|||||||
|
|
||||||
# Set close_rate to stoploss
|
# Set close_rate to stoploss
|
||||||
return trade.stop_loss
|
return trade.stop_loss
|
||||||
elif sell.sell_type == (ExitType.ROI):
|
elif sell.exit_type == (ExitType.ROI):
|
||||||
roi_entry, roi = self.strategy.min_roi_reached_entry(trade_dur)
|
roi_entry, roi = self.strategy.min_roi_reached_entry(trade_dur)
|
||||||
if roi is not None and roi_entry is not None:
|
if roi is not None and roi_entry is not None:
|
||||||
if roi == -1 and roi_entry % self.timeframe_min == 0:
|
if roi == -1 and roi_entry % self.timeframe_min == 0:
|
||||||
@ -395,7 +395,7 @@ class Backtesting:
|
|||||||
closerate = self._get_close_rate(sell_row, trade, sell, trade_dur)
|
closerate = self._get_close_rate(sell_row, trade, sell, trade_dur)
|
||||||
# call the custom exit price,with default value as previous closerate
|
# call the custom exit price,with default value as previous closerate
|
||||||
current_profit = trade.calc_profit_ratio(closerate)
|
current_profit = trade.calc_profit_ratio(closerate)
|
||||||
if sell.sell_type in (ExitType.SELL_SIGNAL, ExitType.CUSTOM_SELL):
|
if sell.exit_type in (ExitType.SELL_SIGNAL, ExitType.CUSTOM_SELL):
|
||||||
# Custom exit pricing only for sell-signals
|
# Custom exit pricing only for sell-signals
|
||||||
closerate = strategy_safe_wrapper(self.strategy.custom_exit_price,
|
closerate = strategy_safe_wrapper(self.strategy.custom_exit_price,
|
||||||
default_retval=closerate)(
|
default_retval=closerate)(
|
||||||
|
@ -672,7 +672,7 @@ class RPC:
|
|||||||
closing_side = "buy" if trade.is_short else "sell"
|
closing_side = "buy" if trade.is_short else "sell"
|
||||||
current_rate = self._freqtrade.exchange.get_rate(
|
current_rate = self._freqtrade.exchange.get_rate(
|
||||||
trade.pair, refresh=False, side=closing_side)
|
trade.pair, refresh=False, side=closing_side)
|
||||||
exit_reason = SellCheckTuple(sell_type=ExitType.FORCE_SELL)
|
exit_reason = SellCheckTuple(exit_type=ExitType.FORCE_SELL)
|
||||||
order_type = ordertype or self._freqtrade.strategy.order_types.get(
|
order_type = ordertype or self._freqtrade.strategy.order_types.get(
|
||||||
"forcesell", self._freqtrade.strategy.order_types["sell"])
|
"forcesell", self._freqtrade.strategy.order_types["sell"])
|
||||||
|
|
||||||
|
@ -34,16 +34,16 @@ class SellCheckTuple:
|
|||||||
"""
|
"""
|
||||||
NamedTuple for Sell type + reason
|
NamedTuple for Sell type + reason
|
||||||
"""
|
"""
|
||||||
sell_type: ExitType
|
exit_type: ExitType
|
||||||
exit_reason: str = ''
|
exit_reason: str = ''
|
||||||
|
|
||||||
def __init__(self, sell_type: ExitType, exit_reason: str = ''):
|
def __init__(self, exit_type: ExitType, exit_reason: str = ''):
|
||||||
self.sell_type = sell_type
|
self.exit_type = exit_type
|
||||||
self.exit_reason = exit_reason or sell_type.value
|
self.exit_reason = exit_reason or exit_type.value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sell_flag(self):
|
def sell_flag(self):
|
||||||
return self.sell_type != ExitType.NONE
|
return self.exit_type != ExitType.NONE
|
||||||
|
|
||||||
|
|
||||||
class IStrategy(ABC, HyperStrategyMixin):
|
class IStrategy(ABC, HyperStrategyMixin):
|
||||||
@ -813,26 +813,26 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
custom_reason = None
|
custom_reason = None
|
||||||
if sell_signal in (ExitType.CUSTOM_SELL, ExitType.SELL_SIGNAL):
|
if sell_signal in (ExitType.CUSTOM_SELL, ExitType.SELL_SIGNAL):
|
||||||
logger.debug(f"{trade.pair} - Sell signal received. "
|
logger.debug(f"{trade.pair} - Sell signal received. "
|
||||||
f"sell_type=ExitType.{sell_signal.name}" +
|
f"exit_type=ExitType.{sell_signal.name}" +
|
||||||
(f", custom_reason={custom_reason}" if custom_reason else ""))
|
(f", custom_reason={custom_reason}" if custom_reason else ""))
|
||||||
return SellCheckTuple(sell_type=sell_signal, exit_reason=custom_reason)
|
return SellCheckTuple(exit_type=sell_signal, exit_reason=custom_reason)
|
||||||
|
|
||||||
# Sequence:
|
# Sequence:
|
||||||
# Exit-signal
|
# Exit-signal
|
||||||
# ROI (if not stoploss)
|
# ROI (if not stoploss)
|
||||||
# Stoploss
|
# Stoploss
|
||||||
if roi_reached and stoplossflag.sell_type != ExitType.STOP_LOSS:
|
if roi_reached and stoplossflag.exit_type != ExitType.STOP_LOSS:
|
||||||
logger.debug(f"{trade.pair} - Required profit reached. sell_type=ExitType.ROI")
|
logger.debug(f"{trade.pair} - Required profit reached. exit_type=ExitType.ROI")
|
||||||
return SellCheckTuple(sell_type=ExitType.ROI)
|
return SellCheckTuple(exit_type=ExitType.ROI)
|
||||||
|
|
||||||
if stoplossflag.sell_flag:
|
if stoplossflag.sell_flag:
|
||||||
|
|
||||||
logger.debug(f"{trade.pair} - Stoploss hit. sell_type={stoplossflag.sell_type}")
|
logger.debug(f"{trade.pair} - Stoploss hit. exit_type={stoplossflag.exit_type}")
|
||||||
return stoplossflag
|
return stoplossflag
|
||||||
|
|
||||||
# This one is noisy, commented out...
|
# This one is noisy, commented out...
|
||||||
# logger.debug(f"{trade.pair} - No exit signal.")
|
# logger.debug(f"{trade.pair} - No exit signal.")
|
||||||
return SellCheckTuple(sell_type=ExitType.NONE)
|
return SellCheckTuple(exit_type=ExitType.NONE)
|
||||||
|
|
||||||
def stop_loss_reached(self, current_rate: float, trade: Trade,
|
def stop_loss_reached(self, current_rate: float, trade: Trade,
|
||||||
current_time: datetime, current_profit: float,
|
current_time: datetime, current_profit: float,
|
||||||
@ -896,11 +896,11 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
if ((sl_higher_short or sl_lower_long) and
|
if ((sl_higher_short or sl_lower_long) and
|
||||||
(not self.order_types.get('stoploss_on_exchange') or self.config['dry_run'])):
|
(not self.order_types.get('stoploss_on_exchange') or self.config['dry_run'])):
|
||||||
|
|
||||||
sell_type = ExitType.STOP_LOSS
|
exit_type = ExitType.STOP_LOSS
|
||||||
|
|
||||||
# If initial stoploss is not the same as current one then it is trailing.
|
# If initial stoploss is not the same as current one then it is trailing.
|
||||||
if trade.initial_stop_loss != trade.stop_loss:
|
if trade.initial_stop_loss != trade.stop_loss:
|
||||||
sell_type = ExitType.TRAILING_STOP_LOSS
|
exit_type = ExitType.TRAILING_STOP_LOSS
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"{trade.pair} - HIT STOP: current price at "
|
f"{trade.pair} - HIT STOP: current price at "
|
||||||
f"{((high if trade.is_short else low) or current_rate):.6f}, "
|
f"{((high if trade.is_short else low) or current_rate):.6f}, "
|
||||||
@ -915,9 +915,9 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
logger.debug(f"{trade.pair} - Trailing stop saved "
|
logger.debug(f"{trade.pair} - Trailing stop saved "
|
||||||
f"{new_stoploss:.6f}")
|
f"{new_stoploss:.6f}")
|
||||||
|
|
||||||
return SellCheckTuple(sell_type=sell_type)
|
return SellCheckTuple(exit_type=exit_type)
|
||||||
|
|
||||||
return SellCheckTuple(sell_type=ExitType.NONE)
|
return SellCheckTuple(exit_type=ExitType.NONE)
|
||||||
|
|
||||||
def min_roi_reached_entry(self, trade_dur: int) -> Tuple[Optional[int], Optional[float]]:
|
def min_roi_reached_entry(self, trade_dur: int) -> Tuple[Optional[int], Optional[float]]:
|
||||||
"""
|
"""
|
||||||
|
@ -440,7 +440,7 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili
|
|||||||
current_time=now, current_profit=profit,
|
current_time=now, current_profit=profit,
|
||||||
force_stoploss=0, high=None)
|
force_stoploss=0, high=None)
|
||||||
assert isinstance(sl_flag, SellCheckTuple)
|
assert isinstance(sl_flag, SellCheckTuple)
|
||||||
assert sl_flag.sell_type == expected
|
assert sl_flag.exit_type == expected
|
||||||
if expected == ExitType.NONE:
|
if expected == ExitType.NONE:
|
||||||
assert sl_flag.sell_flag is False
|
assert sl_flag.sell_flag is False
|
||||||
else:
|
else:
|
||||||
@ -450,7 +450,7 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili
|
|||||||
sl_flag = strategy.stop_loss_reached(current_rate=trade.open_rate * (1 + profit2), trade=trade,
|
sl_flag = strategy.stop_loss_reached(current_rate=trade.open_rate * (1 + profit2), trade=trade,
|
||||||
current_time=now, current_profit=profit2,
|
current_time=now, current_profit=profit2,
|
||||||
force_stoploss=0, high=None)
|
force_stoploss=0, high=None)
|
||||||
assert sl_flag.sell_type == expected2
|
assert sl_flag.exit_type == expected2
|
||||||
if expected2 == ExitType.NONE:
|
if expected2 == ExitType.NONE:
|
||||||
assert sl_flag.sell_flag is False
|
assert sl_flag.sell_flag is False
|
||||||
else:
|
else:
|
||||||
@ -480,14 +480,14 @@ def test_custom_sell(default_conf, fee, caplog) -> None:
|
|||||||
low=None, high=None)
|
low=None, high=None)
|
||||||
|
|
||||||
assert res.sell_flag is False
|
assert res.sell_flag is False
|
||||||
assert res.sell_type == ExitType.NONE
|
assert res.exit_type == ExitType.NONE
|
||||||
|
|
||||||
strategy.custom_sell = MagicMock(return_value=True)
|
strategy.custom_sell = MagicMock(return_value=True)
|
||||||
res = strategy.should_exit(trade, 1, now,
|
res = strategy.should_exit(trade, 1, now,
|
||||||
enter=False, exit_=False,
|
enter=False, exit_=False,
|
||||||
low=None, high=None)
|
low=None, high=None)
|
||||||
assert res.sell_flag is True
|
assert res.sell_flag is True
|
||||||
assert res.sell_type == ExitType.CUSTOM_SELL
|
assert res.exit_type == ExitType.CUSTOM_SELL
|
||||||
assert res.exit_reason == 'custom_sell'
|
assert res.exit_reason == 'custom_sell'
|
||||||
|
|
||||||
strategy.custom_sell = MagicMock(return_value='hello world')
|
strategy.custom_sell = MagicMock(return_value='hello world')
|
||||||
@ -495,7 +495,7 @@ def test_custom_sell(default_conf, fee, caplog) -> None:
|
|||||||
res = strategy.should_exit(trade, 1, now,
|
res = strategy.should_exit(trade, 1, now,
|
||||||
enter=False, exit_=False,
|
enter=False, exit_=False,
|
||||||
low=None, high=None)
|
low=None, high=None)
|
||||||
assert res.sell_type == ExitType.CUSTOM_SELL
|
assert res.exit_type == ExitType.CUSTOM_SELL
|
||||||
assert res.sell_flag is True
|
assert res.sell_flag is True
|
||||||
assert res.exit_reason == 'hello world'
|
assert res.exit_reason == 'hello world'
|
||||||
|
|
||||||
@ -504,7 +504,7 @@ def test_custom_sell(default_conf, fee, caplog) -> None:
|
|||||||
res = strategy.should_exit(trade, 1, now,
|
res = strategy.should_exit(trade, 1, now,
|
||||||
enter=False, exit_=False,
|
enter=False, exit_=False,
|
||||||
low=None, high=None)
|
low=None, high=None)
|
||||||
assert res.sell_type == ExitType.CUSTOM_SELL
|
assert res.exit_type == ExitType.CUSTOM_SELL
|
||||||
assert res.sell_flag is True
|
assert res.sell_flag is True
|
||||||
assert res.exit_reason == 'h' * 64
|
assert res.exit_reason == 'h' * 64
|
||||||
assert log_has_re('Custom sell reason returned from custom_sell is too long.*', caplog)
|
assert log_has_re('Custom sell reason returned from custom_sell is too long.*', caplog)
|
||||||
|
@ -2093,7 +2093,7 @@ def test_handle_trade_roi(default_conf_usdt, ticker_usdt, limit_order_open, fee,
|
|||||||
caplog.clear()
|
caplog.clear()
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
assert freqtrade.handle_trade(trade)
|
assert freqtrade.handle_trade(trade)
|
||||||
assert log_has("ETH/USDT - Required profit reached. sell_type=ExitType.ROI",
|
assert log_has("ETH/USDT - Required profit reached. exit_type=ExitType.ROI",
|
||||||
caplog)
|
caplog)
|
||||||
|
|
||||||
|
|
||||||
@ -2135,7 +2135,7 @@ def test_handle_trade_use_exit_signal(
|
|||||||
else:
|
else:
|
||||||
patch_get_signal(freqtrade, enter_long=False, exit_long=True)
|
patch_get_signal(freqtrade, enter_long=False, exit_long=True)
|
||||||
assert freqtrade.handle_trade(trade)
|
assert freqtrade.handle_trade(trade)
|
||||||
assert log_has("ETH/USDT - Sell signal received. sell_type=ExitType.SELL_SIGNAL",
|
assert log_has("ETH/USDT - Sell signal received. exit_type=ExitType.SELL_SIGNAL",
|
||||||
caplog)
|
caplog)
|
||||||
|
|
||||||
|
|
||||||
@ -2855,7 +2855,7 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_
|
|||||||
freqtrade.execute_trade_exit(
|
freqtrade.execute_trade_exit(
|
||||||
trade=trade,
|
trade=trade,
|
||||||
limit=(ticker_usdt_sell_down()['ask'] if is_short else ticker_usdt_sell_up()['bid']),
|
limit=(ticker_usdt_sell_down()['ask'] if is_short else ticker_usdt_sell_up()['bid']),
|
||||||
exit_reason=SellCheckTuple(sell_type=ExitType.ROI)
|
exit_reason=SellCheckTuple(exit_type=ExitType.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
|
||||||
@ -2867,7 +2867,7 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_
|
|||||||
freqtrade.execute_trade_exit(
|
freqtrade.execute_trade_exit(
|
||||||
trade=trade,
|
trade=trade,
|
||||||
limit=(ticker_usdt_sell_down()['ask'] if is_short else ticker_usdt_sell_up()['bid']),
|
limit=(ticker_usdt_sell_down()['ask'] if is_short else ticker_usdt_sell_up()['bid']),
|
||||||
exit_reason=SellCheckTuple(sell_type=ExitType.ROI)
|
exit_reason=SellCheckTuple(exit_type=ExitType.ROI)
|
||||||
)
|
)
|
||||||
assert freqtrade.strategy.confirm_trade_exit.call_count == 1
|
assert freqtrade.strategy.confirm_trade_exit.call_count == 1
|
||||||
|
|
||||||
@ -2928,7 +2928,7 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd
|
|||||||
)
|
)
|
||||||
freqtrade.execute_trade_exit(
|
freqtrade.execute_trade_exit(
|
||||||
trade=trade, limit=(ticker_usdt_sell_up if is_short else ticker_usdt_sell_down)()['bid'],
|
trade=trade, limit=(ticker_usdt_sell_up if is_short else ticker_usdt_sell_down)()['bid'],
|
||||||
exit_reason=SellCheckTuple(sell_type=ExitType.STOP_LOSS))
|
exit_reason=SellCheckTuple(exit_type=ExitType.STOP_LOSS))
|
||||||
|
|
||||||
assert rpc_mock.call_count == 2
|
assert rpc_mock.call_count == 2
|
||||||
last_msg = rpc_mock.call_args_list[-1][0][0]
|
last_msg = rpc_mock.call_args_list[-1][0][0]
|
||||||
@ -3003,7 +3003,7 @@ def test_execute_trade_exit_custom_exit_price(
|
|||||||
freqtrade.execute_trade_exit(
|
freqtrade.execute_trade_exit(
|
||||||
trade=trade,
|
trade=trade,
|
||||||
limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
|
limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
|
||||||
exit_reason=SellCheckTuple(sell_type=ExitType.SELL_SIGNAL)
|
exit_reason=SellCheckTuple(exit_type=ExitType.SELL_SIGNAL)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Sell price must be different to default bid price
|
# Sell price must be different to default bid price
|
||||||
@ -3074,7 +3074,7 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(
|
|||||||
trade.stop_loss = 2.0 * 1.01 if is_short else 2.0 * 0.99
|
trade.stop_loss = 2.0 * 1.01 if is_short else 2.0 * 0.99
|
||||||
freqtrade.execute_trade_exit(
|
freqtrade.execute_trade_exit(
|
||||||
trade=trade, limit=(ticker_usdt_sell_up if is_short else ticker_usdt_sell_down())['bid'],
|
trade=trade, limit=(ticker_usdt_sell_up if is_short else ticker_usdt_sell_down())['bid'],
|
||||||
exit_reason=SellCheckTuple(sell_type=ExitType.STOP_LOSS))
|
exit_reason=SellCheckTuple(exit_type=ExitType.STOP_LOSS))
|
||||||
|
|
||||||
assert rpc_mock.call_count == 2
|
assert rpc_mock.call_count == 2
|
||||||
last_msg = rpc_mock.call_args_list[-1][0][0]
|
last_msg = rpc_mock.call_args_list[-1][0][0]
|
||||||
@ -3134,7 +3134,7 @@ def test_execute_trade_exit_sloe_cancel_exception(
|
|||||||
trade.stoploss_order_id = "abcd"
|
trade.stoploss_order_id = "abcd"
|
||||||
|
|
||||||
freqtrade.execute_trade_exit(trade=trade, limit=1234,
|
freqtrade.execute_trade_exit(trade=trade, limit=1234,
|
||||||
exit_reason=SellCheckTuple(sell_type=ExitType.STOP_LOSS))
|
exit_reason=SellCheckTuple(exit_type=ExitType.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)
|
||||||
|
|
||||||
@ -3189,7 +3189,7 @@ def test_execute_trade_exit_with_stoploss_on_exchange(
|
|||||||
freqtrade.execute_trade_exit(
|
freqtrade.execute_trade_exit(
|
||||||
trade=trade,
|
trade=trade,
|
||||||
limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
|
limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
|
||||||
exit_reason=SellCheckTuple(sell_type=ExitType.STOP_LOSS)
|
exit_reason=SellCheckTuple(exit_type=ExitType.STOP_LOSS)
|
||||||
)
|
)
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
@ -3328,7 +3328,7 @@ def test_execute_trade_exit_market_order(
|
|||||||
freqtrade.execute_trade_exit(
|
freqtrade.execute_trade_exit(
|
||||||
trade=trade,
|
trade=trade,
|
||||||
limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
|
limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
|
||||||
exit_reason=SellCheckTuple(sell_type=ExitType.ROI)
|
exit_reason=SellCheckTuple(exit_type=ExitType.ROI)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert not trade.is_open
|
assert not trade.is_open
|
||||||
@ -3392,7 +3392,7 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf_usdt, ticker_u
|
|||||||
fetch_ticker=ticker_usdt_sell_up
|
fetch_ticker=ticker_usdt_sell_up
|
||||||
)
|
)
|
||||||
|
|
||||||
exit_reason = SellCheckTuple(sell_type=ExitType.ROI)
|
exit_reason = SellCheckTuple(exit_type=ExitType.ROI)
|
||||||
assert not freqtrade.execute_trade_exit(
|
assert not freqtrade.execute_trade_exit(
|
||||||
trade=trade,
|
trade=trade,
|
||||||
limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
|
limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
|
||||||
@ -3401,7 +3401,7 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf_usdt, ticker_u
|
|||||||
assert mock_insuf.call_count == 1
|
assert mock_insuf.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('profit_only,bid,ask,handle_first,handle_second,sell_type,is_short', [
|
@pytest.mark.parametrize('profit_only,bid,ask,handle_first,handle_second,exit_type,is_short', [
|
||||||
# Enable profit
|
# Enable profit
|
||||||
(True, 2.18, 2.2, False, True, ExitType.SELL_SIGNAL.value, False),
|
(True, 2.18, 2.2, False, True, ExitType.SELL_SIGNAL.value, False),
|
||||||
(True, 2.18, 2.2, False, True, ExitType.SELL_SIGNAL.value, True),
|
(True, 2.18, 2.2, False, True, ExitType.SELL_SIGNAL.value, True),
|
||||||
@ -3418,7 +3418,7 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf_usdt, ticker_u
|
|||||||
])
|
])
|
||||||
def test_sell_profit_only(
|
def test_sell_profit_only(
|
||||||
default_conf_usdt, limit_order, limit_order_open, is_short,
|
default_conf_usdt, limit_order, limit_order_open, is_short,
|
||||||
fee, mocker, profit_only, bid, ask, handle_first, handle_second, sell_type) -> None:
|
fee, mocker, profit_only, bid, ask, handle_first, handle_second, exit_type) -> None:
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -3441,11 +3441,11 @@ def test_sell_profit_only(
|
|||||||
})
|
})
|
||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
|
||||||
if sell_type == ExitType.SELL_SIGNAL.value:
|
if exit_type == ExitType.SELL_SIGNAL.value:
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
else:
|
else:
|
||||||
freqtrade.strategy.stop_loss_reached = MagicMock(return_value=SellCheckTuple(
|
freqtrade.strategy.stop_loss_reached = MagicMock(return_value=SellCheckTuple(
|
||||||
sell_type=ExitType.NONE))
|
exit_type=ExitType.NONE))
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
@ -3561,7 +3561,7 @@ def test_locked_pairs(default_conf_usdt, ticker_usdt, fee,
|
|||||||
freqtrade.execute_trade_exit(
|
freqtrade.execute_trade_exit(
|
||||||
trade=trade,
|
trade=trade,
|
||||||
limit=ticker_usdt_sell_down()['ask' if is_short else 'bid'],
|
limit=ticker_usdt_sell_down()['ask' if is_short else 'bid'],
|
||||||
exit_reason=SellCheckTuple(sell_type=ExitType.STOP_LOSS)
|
exit_reason=SellCheckTuple(exit_type=ExitType.STOP_LOSS)
|
||||||
)
|
)
|
||||||
trade.close(ticker_usdt_sell_down()['bid'])
|
trade.close(ticker_usdt_sell_down()['bid'])
|
||||||
assert freqtrade.strategy.is_pair_locked(trade.pair)
|
assert freqtrade.strategy.is_pair_locked(trade.pair)
|
||||||
@ -4920,7 +4920,7 @@ def test_update_funding_fees(
|
|||||||
trade=trade,
|
trade=trade,
|
||||||
# The values of the next 2 params are irrelevant for this test
|
# The values of the next 2 params are irrelevant for this test
|
||||||
limit=ticker_usdt_sell_up()['bid'],
|
limit=ticker_usdt_sell_up()['bid'],
|
||||||
exit_reason=SellCheckTuple(sell_type=ExitType.ROI)
|
exit_reason=SellCheckTuple(exit_type=ExitType.ROI)
|
||||||
)
|
)
|
||||||
assert trade.funding_fees == pytest.approx(sum(
|
assert trade.funding_fees == pytest.approx(sum(
|
||||||
trade.amount *
|
trade.amount *
|
||||||
|
@ -52,8 +52,8 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
|||||||
side_effect=[stoploss_order_closed, stoploss_order_open, stoploss_order_open])
|
side_effect=[stoploss_order_closed, stoploss_order_open, stoploss_order_open])
|
||||||
# Sell 3rd trade (not called for the first trade)
|
# Sell 3rd trade (not called for the first trade)
|
||||||
should_sell_mock = MagicMock(side_effect=[
|
should_sell_mock = MagicMock(side_effect=[
|
||||||
SellCheckTuple(sell_type=ExitType.NONE),
|
SellCheckTuple(exit_type=ExitType.NONE),
|
||||||
SellCheckTuple(sell_type=ExitType.SELL_SIGNAL)]
|
SellCheckTuple(exit_type=ExitType.SELL_SIGNAL)]
|
||||||
)
|
)
|
||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss)
|
mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss)
|
||||||
@ -157,11 +157,11 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, limit_buy_order, moc
|
|||||||
_notify_exit=MagicMock(),
|
_notify_exit=MagicMock(),
|
||||||
)
|
)
|
||||||
should_sell_mock = MagicMock(side_effect=[
|
should_sell_mock = MagicMock(side_effect=[
|
||||||
SellCheckTuple(sell_type=ExitType.NONE),
|
SellCheckTuple(exit_type=ExitType.NONE),
|
||||||
SellCheckTuple(sell_type=ExitType.SELL_SIGNAL),
|
SellCheckTuple(exit_type=ExitType.SELL_SIGNAL),
|
||||||
SellCheckTuple(sell_type=ExitType.NONE),
|
SellCheckTuple(exit_type=ExitType.NONE),
|
||||||
SellCheckTuple(sell_type=ExitType.NONE),
|
SellCheckTuple(exit_type=ExitType.NONE),
|
||||||
SellCheckTuple(sell_type=ExitType.NONE)]
|
SellCheckTuple(exit_type=ExitType.NONE)]
|
||||||
)
|
)
|
||||||
mocker.patch("freqtrade.strategy.interface.IStrategy.should_exit", should_sell_mock)
|
mocker.patch("freqtrade.strategy.interface.IStrategy.should_exit", should_sell_mock)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user