Merge pull request #7146 from freqtrade/fix/liquidation

Update liquidation price handling
This commit is contained in:
Matthias 2022-07-31 08:09:54 +02:00 committed by GitHub
commit 369c6da5d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 142 additions and 106 deletions

View File

@ -623,6 +623,7 @@ class AwesomeStrategy(IStrategy):
!!! Warning !!! Warning
`confirm_trade_exit()` can prevent stoploss exits, causing significant losses as this would ignore stoploss exits. `confirm_trade_exit()` can prevent stoploss exits, causing significant losses as this would ignore stoploss exits.
`confirm_trade_exit()` will not be called for Liquidations - as liquidations are forced by the exchange, and therefore cannot be rejected.
## Adjust trade position ## Adjust trade position

View File

@ -9,6 +9,7 @@ class ExitType(Enum):
STOP_LOSS = "stop_loss" STOP_LOSS = "stop_loss"
STOPLOSS_ON_EXCHANGE = "stoploss_on_exchange" STOPLOSS_ON_EXCHANGE = "stoploss_on_exchange"
TRAILING_STOP_LOSS = "trailing_stop_loss" TRAILING_STOP_LOSS = "trailing_stop_loss"
LIQUIDATION = "liquidation"
EXIT_SIGNAL = "exit_signal" EXIT_SIGNAL = "exit_signal"
FORCE_EXIT = "force_exit" FORCE_EXIT = "force_exit"
EMERGENCY_EXIT = "emergency_exit" EMERGENCY_EXIT = "emergency_exit"

View File

@ -1016,7 +1016,7 @@ class FreqtradeBot(LoggingMixin):
trade.stoploss_order_id = None trade.stoploss_order_id = None
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_check=ExitCheckTuple( self.execute_trade_exit(trade, stop_price, exit_check=ExitCheckTuple(
exit_type=ExitType.EMERGENCY_EXIT)) exit_type=ExitType.EMERGENCY_EXIT))
except ExchangeError: except ExchangeError:
@ -1086,7 +1086,7 @@ class FreqtradeBot(LoggingMixin):
if (trade.is_open if (trade.is_open
and stoploss_order and stoploss_order
and stoploss_order['status'] in ('canceled', 'cancelled')): 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.stoploss_or_liquidation):
return False return False
else: else:
trade.stoploss_order_id = None trade.stoploss_order_id = None
@ -1115,7 +1115,7 @@ class FreqtradeBot(LoggingMixin):
:param order: Current on exchange stoploss order :param order: Current on exchange stoploss order
:return: None :return: None
""" """
stoploss_norm = self.exchange.price_to_precision(trade.pair, trade.stop_loss) stoploss_norm = self.exchange.price_to_precision(trade.pair, trade.stoploss_or_liquidation)
if self.exchange.stoploss_adjust(stoploss_norm, order, side=trade.exit_side): if self.exchange.stoploss_adjust(stoploss_norm, order, side=trade.exit_side):
# we check if the update is necessary # we check if the update is necessary
@ -1133,7 +1133,7 @@ class FreqtradeBot(LoggingMixin):
f"for pair {trade.pair}") f"for pair {trade.pair}")
# Create new stoploss order # Create new stoploss order
if not self.create_stoploss_order(trade=trade, stop_price=trade.stop_loss): if not self.create_stoploss_order(trade=trade, stop_price=stoploss_norm):
logger.warning(f"Could not create trailing stoploss order " logger.warning(f"Could not create trailing stoploss order "
f"for pair {trade.pair}.") f"for pair {trade.pair}.")
@ -1432,14 +1432,15 @@ class FreqtradeBot(LoggingMixin):
) )
exit_type = 'exit' exit_type = 'exit'
exit_reason = exit_tag or exit_check.exit_reason exit_reason = exit_tag or exit_check.exit_reason
if exit_check.exit_type in (ExitType.STOP_LOSS, ExitType.TRAILING_STOP_LOSS): if exit_check.exit_type in (
ExitType.STOP_LOSS, ExitType.TRAILING_STOP_LOSS, ExitType.LIQUIDATION):
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,
# we consider the sell price stop price # we consider the sell price stop price
if (self.config['dry_run'] and exit_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.stoploss_or_liquidation
# set custom_exit_price if available # set custom_exit_price if available
proposed_limit_rate = limit proposed_limit_rate = limit
@ -1464,11 +1465,12 @@ class FreqtradeBot(LoggingMixin):
amount = self._safe_exit_amount(trade.pair, trade.amount) amount = self._safe_exit_amount(trade.pair, trade.amount)
time_in_force = self.strategy.order_time_in_force['exit'] time_in_force = self.strategy.order_time_in_force['exit']
if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( if (exit_check.exit_type != ExitType.LIQUIDATION and not strategy_safe_wrapper(
self.strategy.confirm_trade_exit, default_retval=True)(
pair=trade.pair, trade=trade, order_type=order_type, amount=amount, rate=limit, pair=trade.pair, trade=trade, order_type=order_type, amount=amount, rate=limit,
time_in_force=time_in_force, exit_reason=exit_reason, time_in_force=time_in_force, exit_reason=exit_reason,
sell_reason=exit_reason, # sellreason -> compatibility sell_reason=exit_reason, # sellreason -> compatibility
current_time=datetime.now(timezone.utc)): current_time=datetime.now(timezone.utc))):
logger.info(f"User denied exit for {trade.pair}.") logger.info(f"User denied exit for {trade.pair}.")
return False return False
@ -1661,7 +1663,7 @@ class FreqtradeBot(LoggingMixin):
trade = self.cancel_stoploss_on_exchange(trade) trade = self.cancel_stoploss_on_exchange(trade)
# TODO: Margin will need to use interest_rate as well. # TODO: Margin will need to use interest_rate as well.
# interest_rate = self.exchange.get_interest_rate() # interest_rate = self.exchange.get_interest_rate()
trade.set_isolated_liq(self.exchange.get_liquidation_price( trade.set_liquidation_price(self.exchange.get_liquidation_price(
leverage=trade.leverage, leverage=trade.leverage,
pair=trade.pair, pair=trade.pair,
amount=trade.amount, amount=trade.amount,

View File

@ -381,7 +381,8 @@ 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 exit.exit_type in (ExitType.STOP_LOSS, ExitType.TRAILING_STOP_LOSS): if exit.exit_type in (
ExitType.STOP_LOSS, ExitType.TRAILING_STOP_LOSS, ExitType.LIQUIDATION):
return self._get_close_rate_for_stoploss(row, trade, exit, trade_dur) return self._get_close_rate_for_stoploss(row, trade, exit, trade_dur)
elif exit.exit_type == (ExitType.ROI): elif exit.exit_type == (ExitType.ROI):
return self._get_close_rate_for_roi(row, trade, exit, trade_dur) return self._get_close_rate_for_roi(row, trade, exit, trade_dur)
@ -396,11 +397,16 @@ class Backtesting:
is_short = trade.is_short or False is_short = trade.is_short or False
leverage = trade.leverage or 1.0 leverage = trade.leverage or 1.0
side_1 = -1 if is_short else 1 side_1 = -1 if is_short else 1
if exit.exit_type == ExitType.LIQUIDATION and trade.liquidation_price:
stoploss_value = trade.liquidation_price
else:
stoploss_value = trade.stop_loss
if is_short: if is_short:
if trade.stop_loss < row[LOW_IDX]: if stoploss_value < row[LOW_IDX]:
return row[OPEN_IDX] return row[OPEN_IDX]
else: else:
if trade.stop_loss > row[HIGH_IDX]: if stoploss_value > row[HIGH_IDX]:
return row[OPEN_IDX] return row[OPEN_IDX]
# Special case: trailing triggers within same candle as trade opened. Assume most # Special case: trailing triggers within same candle as trade opened. Assume most
@ -433,7 +439,7 @@ class Backtesting:
return max(row[LOW_IDX], stop_rate) return max(row[LOW_IDX], stop_rate)
# Set close_rate to stoploss # Set close_rate to stoploss
return trade.stop_loss return stoploss_value
def _get_close_rate_for_roi(self, row: Tuple, trade: LocalTrade, exit: ExitCheckTuple, def _get_close_rate_for_roi(self, row: Tuple, trade: LocalTrade, exit: ExitCheckTuple,
trade_dur: int) -> float: trade_dur: int) -> float:
@ -592,7 +598,8 @@ class Backtesting:
# Confirm trade exit: # Confirm trade exit:
time_in_force = self.strategy.order_time_in_force['exit'] time_in_force = self.strategy.order_time_in_force['exit']
if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( if (exit_.exit_type != ExitType.LIQUIDATION and not strategy_safe_wrapper(
self.strategy.confirm_trade_exit, default_retval=True)(
pair=trade.pair, pair=trade.pair,
trade=trade, # type: ignore[arg-type] trade=trade, # type: ignore[arg-type]
order_type='limit', order_type='limit',
@ -601,7 +608,7 @@ class Backtesting:
time_in_force=time_in_force, time_in_force=time_in_force,
sell_reason=exit_reason, # deprecated sell_reason=exit_reason, # deprecated
exit_reason=exit_reason, exit_reason=exit_reason,
current_time=exit_candle_time): current_time=exit_candle_time)):
return None return None
trade.exit_reason = exit_reason trade.exit_reason = exit_reason
@ -807,7 +814,7 @@ class Backtesting:
trade.adjust_stop_loss(trade.open_rate, self.strategy.stoploss, initial=True) trade.adjust_stop_loss(trade.open_rate, self.strategy.stoploss, initial=True)
trade.set_isolated_liq(self.exchange.get_liquidation_price( trade.set_liquidation_price(self.exchange.get_liquidation_price(
pair=pair, pair=pair,
open_rate=propose_rate, open_rate=propose_rate,
amount=amount, amount=amount,

View File

@ -302,6 +302,16 @@ class LocalTrade():
# Futures properties # Futures properties
funding_fees: Optional[float] = None funding_fees: Optional[float] = None
@property
def stoploss_or_liquidation(self) -> float:
if self.liquidation_price:
if self.is_short:
return min(self.stop_loss, self.liquidation_price)
else:
return max(self.stop_loss, self.liquidation_price)
return self.stop_loss
@property @property
def buy_tag(self) -> Optional[str]: def buy_tag(self) -> Optional[str]:
""" """
@ -497,7 +507,7 @@ class LocalTrade():
self.max_rate = max(current_price, self.max_rate or self.open_rate) self.max_rate = max(current_price, self.max_rate or self.open_rate)
self.min_rate = min(current_price_low, self.min_rate or self.open_rate) self.min_rate = min(current_price_low, self.min_rate or self.open_rate)
def set_isolated_liq(self, liquidation_price: Optional[float]): def set_liquidation_price(self, liquidation_price: Optional[float]):
""" """
Method you should use to set self.liquidation price. Method you should use to set self.liquidation price.
Assures stop_loss is not passed the liquidation price Assures stop_loss is not passed the liquidation price
@ -506,22 +516,13 @@ class LocalTrade():
return return
self.liquidation_price = liquidation_price self.liquidation_price = liquidation_price
def _set_stop_loss(self, stop_loss: float, percent: float): def __set_stop_loss(self, stop_loss: float, percent: float):
""" """
Method you should use to set self.stop_loss. Method used internally to set self.stop_loss.
Assures stop_loss is not passed the liquidation price
""" """
if self.liquidation_price is not None:
if self.is_short:
sl = min(stop_loss, self.liquidation_price)
else:
sl = max(stop_loss, self.liquidation_price)
else:
sl = stop_loss
if not self.stop_loss: if not self.stop_loss:
self.initial_stop_loss = sl self.initial_stop_loss = stop_loss
self.stop_loss = sl self.stop_loss = stop_loss
self.stop_loss_pct = -1 * abs(percent) self.stop_loss_pct = -1 * abs(percent)
self.stoploss_last_update = datetime.utcnow() self.stoploss_last_update = datetime.utcnow()
@ -543,18 +544,12 @@ class LocalTrade():
leverage = self.leverage or 1.0 leverage = self.leverage or 1.0
if self.is_short: if self.is_short:
new_loss = float(current_price * (1 + abs(stoploss / leverage))) new_loss = float(current_price * (1 + abs(stoploss / leverage)))
# If trading with leverage, don't set the stoploss below the liquidation price
if self.liquidation_price:
new_loss = min(self.liquidation_price, new_loss)
else: else:
new_loss = float(current_price * (1 - abs(stoploss / leverage))) new_loss = float(current_price * (1 - abs(stoploss / leverage)))
# If trading with leverage, don't set the stoploss below the liquidation price
if self.liquidation_price:
new_loss = max(self.liquidation_price, new_loss)
# no stop loss assigned yet # no stop loss assigned yet
if self.initial_stop_loss_pct is None or refresh: if self.initial_stop_loss_pct is None or refresh:
self._set_stop_loss(new_loss, stoploss) self.__set_stop_loss(new_loss, stoploss)
self.initial_stop_loss = new_loss self.initial_stop_loss = new_loss
self.initial_stop_loss_pct = -1 * abs(stoploss) self.initial_stop_loss_pct = -1 * abs(stoploss)
@ -569,7 +564,7 @@ class LocalTrade():
# ? decreasing the minimum stoploss # ? decreasing the minimum stoploss
if (higher_stop and not self.is_short) or (lower_stop and self.is_short): if (higher_stop and not self.is_short) or (lower_stop and self.is_short):
logger.debug(f"{self.pair} - Adjusting stoploss...") logger.debug(f"{self.pair} - Adjusting stoploss...")
self._set_stop_loss(new_loss, stoploss) self.__set_stop_loss(new_loss, stoploss)
else: else:
logger.debug(f"{self.pair} - Keeping current stoploss...") logger.debug(f"{self.pair} - Keeping current stoploss...")

View File

@ -49,7 +49,7 @@ class StoplossGuard(IProtection):
trades1 = Trade.get_trades_proxy(pair=pair, is_open=False, close_date=look_back_until) trades1 = Trade.get_trades_proxy(pair=pair, is_open=False, close_date=look_back_until)
trades = [trade for trade in trades1 if (str(trade.exit_reason) in ( trades = [trade for trade in trades1 if (str(trade.exit_reason) in (
ExitType.TRAILING_STOP_LOSS.value, ExitType.STOP_LOSS.value, ExitType.TRAILING_STOP_LOSS.value, ExitType.STOP_LOSS.value,
ExitType.STOPLOSS_ON_EXCHANGE.value) ExitType.STOPLOSS_ON_EXCHANGE.value, ExitType.LIQUIDATION.value)
and trade.close_profit and trade.close_profit < self._profit_limit)] and trade.close_profit and trade.close_profit < self._profit_limit)]
if self._only_per_side: if self._only_per_side:

View File

@ -963,7 +963,7 @@ class IStrategy(ABC, HyperStrategyMixin):
# ROI # ROI
# Trailing stoploss # Trailing stoploss
if stoplossflag.exit_type == ExitType.STOP_LOSS: if stoplossflag.exit_type in (ExitType.STOP_LOSS, ExitType.LIQUIDATION):
logger.debug(f"{trade.pair} - Stoploss hit. exit_type={stoplossflag.exit_type}") logger.debug(f"{trade.pair} - Stoploss hit. exit_type={stoplossflag.exit_type}")
exits.append(stoplossflag) exits.append(stoplossflag)
@ -1035,6 +1035,17 @@ class IStrategy(ABC, HyperStrategyMixin):
sl_higher_long = (trade.stop_loss >= (low or current_rate) and not trade.is_short) sl_higher_long = (trade.stop_loss >= (low or current_rate) and not trade.is_short)
sl_lower_short = (trade.stop_loss <= (high or current_rate) and trade.is_short) sl_lower_short = (trade.stop_loss <= (high or current_rate) and trade.is_short)
liq_higher_long = (trade.liquidation_price
and trade.liquidation_price >= (low or current_rate)
and not trade.is_short)
liq_lower_short = (trade.liquidation_price
and trade.liquidation_price <= (high or current_rate)
and trade.is_short)
if (liq_higher_long or liq_lower_short):
logger.debug(f"{trade.pair} - Liquidation price hit. exit_type=ExitType.LIQUIDATION")
return ExitCheckTuple(exit_type=ExitType.LIQUIDATION)
# evaluate if the stoploss was hit if stoploss is not on exchange # evaluate if the stoploss was hit if stoploss is not on exchange
# in Dry-Run, this handles stoploss logic as well, as the logic will not be different to # in Dry-Run, this handles stoploss logic as well, as the logic will not be different to
# regular stoploss handling. # regular stoploss handling.
@ -1052,13 +1063,6 @@ class IStrategy(ABC, HyperStrategyMixin):
f"stoploss is {trade.stop_loss:.6f}, " f"stoploss is {trade.stop_loss:.6f}, "
f"initial stoploss was at {trade.initial_stop_loss:.6f}, " f"initial stoploss was at {trade.initial_stop_loss:.6f}, "
f"trade opened at {trade.open_rate:.6f}") f"trade opened at {trade.open_rate:.6f}")
new_stoploss = (
trade.stop_loss + trade.initial_stop_loss
if trade.is_short else
trade.stop_loss - trade.initial_stop_loss
)
logger.debug(f"{trade.pair} - Trailing stop saved "
f"{new_stoploss:.6f}")
return ExitCheckTuple(exit_type=exit_type) return ExitCheckTuple(exit_type=exit_type)

View File

@ -408,28 +408,31 @@ def test_min_roi_reached3(default_conf, fee) -> None:
@pytest.mark.parametrize( @pytest.mark.parametrize(
'profit,adjusted,expected,trailing,custom,profit2,adjusted2,expected2,custom_stop', [ 'profit,adjusted,expected,liq,trailing,custom,profit2,adjusted2,expected2,custom_stop', [
# Profit, adjusted stoploss(absolute), profit for 2nd call, enable trailing, # Profit, adjusted stoploss(absolute), profit for 2nd call, enable trailing,
# enable custom stoploss, expected after 1st call, expected after 2nd call # enable custom stoploss, expected after 1st call, expected after 2nd call
(0.2, 0.9, ExitType.NONE, False, False, 0.3, 0.9, ExitType.NONE, None), (0.2, 0.9, ExitType.NONE, None, False, False, 0.3, 0.9, ExitType.NONE, None),
(0.2, 0.9, ExitType.NONE, False, False, -0.2, 0.9, ExitType.STOP_LOSS, None), (0.2, 0.9, ExitType.NONE, None, False, False, -0.2, 0.9, ExitType.STOP_LOSS, None),
(0.2, 1.14, ExitType.NONE, True, False, 0.05, 1.14, ExitType.TRAILING_STOP_LOSS, None), (0.2, 0.9, ExitType.NONE, 0.8, False, False, -0.2, 0.9, ExitType.LIQUIDATION, None),
(0.01, 0.96, ExitType.NONE, True, False, 0.05, 1, ExitType.NONE, None), (0.2, 1.14, ExitType.NONE, None, True, False, 0.05, 1.14, ExitType.TRAILING_STOP_LOSS,
(0.05, 1, ExitType.NONE, True, False, -0.01, 1, ExitType.TRAILING_STOP_LOSS, None), None),
(0.01, 0.96, ExitType.NONE, None, True, False, 0.05, 1, ExitType.NONE, None),
(0.05, 1, ExitType.NONE, None, True, False, -0.01, 1, ExitType.TRAILING_STOP_LOSS, None),
# Default custom case - trails with 10% # Default custom case - trails with 10%
(0.05, 0.95, ExitType.NONE, False, True, -0.02, 0.95, ExitType.NONE, None), (0.05, 0.95, ExitType.NONE, None, False, True, -0.02, 0.95, ExitType.NONE, None),
(0.05, 0.95, ExitType.NONE, False, True, -0.06, 0.95, ExitType.TRAILING_STOP_LOSS, None), (0.05, 0.95, ExitType.NONE, None, False, True, -0.06, 0.95, ExitType.TRAILING_STOP_LOSS,
(0.05, 1, ExitType.NONE, False, True, -0.06, 1, ExitType.TRAILING_STOP_LOSS, None),
(0.05, 1, ExitType.NONE, None, False, True, -0.06, 1, ExitType.TRAILING_STOP_LOSS,
lambda **kwargs: -0.05), lambda **kwargs: -0.05),
(0.05, 1, ExitType.NONE, False, True, 0.09, 1.04, ExitType.NONE, (0.05, 1, ExitType.NONE, None, False, True, 0.09, 1.04, ExitType.NONE,
lambda **kwargs: -0.05), lambda **kwargs: -0.05),
(0.05, 0.95, ExitType.NONE, False, True, 0.09, 0.98, ExitType.NONE, (0.05, 0.95, ExitType.NONE, None, False, True, 0.09, 0.98, ExitType.NONE,
lambda current_profit, **kwargs: -0.1 if current_profit < 0.6 else -(current_profit * 2)), lambda current_profit, **kwargs: -0.1 if current_profit < 0.6 else -(current_profit * 2)),
# Error case - static stoploss in place # Error case - static stoploss in place
(0.05, 0.9, ExitType.NONE, False, True, 0.09, 0.9, ExitType.NONE, (0.05, 0.9, ExitType.NONE, None, False, True, 0.09, 0.9, ExitType.NONE,
lambda **kwargs: None), lambda **kwargs: None),
]) ])
def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, trailing, custom, def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, liq, trailing, custom,
profit2, adjusted2, expected2, custom_stop) -> None: profit2, adjusted2, expected2, custom_stop) -> None:
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
@ -442,6 +445,7 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili
fee_close=fee.return_value, fee_close=fee.return_value,
exchange='binance', exchange='binance',
open_rate=1, open_rate=1,
liquidation_price=liq,
) )
trade.adjust_min_max_rates(trade.open_rate, trade.open_rate) trade.adjust_min_max_rates(trade.open_rate, trade.open_rate)
strategy.trailing_stop = trailing strategy.trailing_stop = trailing

View File

@ -99,7 +99,7 @@ def test_enter_exit_side(fee, is_short):
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_set_stop_loss_isolated_liq(fee): def test_set_stop_loss_liquidation(fee):
trade = Trade( trade = Trade(
id=2, id=2,
pair='ADA/USDT', pair='ADA/USDT',
@ -115,73 +115,94 @@ def test_set_stop_loss_isolated_liq(fee):
leverage=2.0, leverage=2.0,
trading_mode=margin trading_mode=margin
) )
trade.set_isolated_liq(0.09) trade.set_liquidation_price(0.09)
assert trade.liquidation_price == 0.09 assert trade.liquidation_price == 0.09
assert trade.stop_loss is None assert trade.stop_loss is None
assert trade.initial_stop_loss is None assert trade.initial_stop_loss is None
trade._set_stop_loss(0.1, (1.0 / 9.0)) trade.adjust_stop_loss(2.0, 0.2, True)
assert trade.liquidation_price == 0.09 assert trade.liquidation_price == 0.09
assert trade.stop_loss == 0.1 assert trade.stop_loss == 1.8
assert trade.initial_stop_loss == 0.1 assert trade.initial_stop_loss == 1.8
trade.set_isolated_liq(0.08) trade.set_liquidation_price(0.08)
assert trade.liquidation_price == 0.08 assert trade.liquidation_price == 0.08
assert trade.stop_loss == 0.1 assert trade.stop_loss == 1.8
assert trade.initial_stop_loss == 0.1 assert trade.initial_stop_loss == 1.8
trade.set_isolated_liq(0.11) trade.set_liquidation_price(0.11)
trade._set_stop_loss(0.1, 0) trade.adjust_stop_loss(2.0, 0.2)
assert trade.liquidation_price == 0.11 assert trade.liquidation_price == 0.11
assert trade.stop_loss == 0.11 # Stoploss does not change from liquidation price
assert trade.initial_stop_loss == 0.1 assert trade.stop_loss == 1.8
assert trade.initial_stop_loss == 1.8
# lower stop doesn't move stoploss # lower stop doesn't move stoploss
trade._set_stop_loss(0.1, 0) trade.adjust_stop_loss(1.8, 0.2)
assert trade.liquidation_price == 0.11 assert trade.liquidation_price == 0.11
assert trade.stop_loss == 0.11 assert trade.stop_loss == 1.8
assert trade.initial_stop_loss == 0.1 assert trade.initial_stop_loss == 1.8
# higher stop does move stoploss
trade.adjust_stop_loss(2.1, 0.1)
assert trade.liquidation_price == 0.11
assert pytest.approx(trade.stop_loss) == 1.994999
assert trade.initial_stop_loss == 1.8
assert trade.stoploss_or_liquidation == trade.stop_loss
trade.stop_loss = None trade.stop_loss = None
trade.liquidation_price = None trade.liquidation_price = None
trade.initial_stop_loss = None trade.initial_stop_loss = None
trade.initial_stop_loss_pct = None
trade._set_stop_loss(0.07, 0) trade.adjust_stop_loss(2.0, 0.1, True)
assert trade.liquidation_price is None assert trade.liquidation_price is None
assert trade.stop_loss == 0.07 assert trade.stop_loss == 1.9
assert trade.initial_stop_loss == 0.07 assert trade.initial_stop_loss == 1.9
assert trade.stoploss_or_liquidation == 1.9
trade.is_short = True trade.is_short = True
trade.recalc_open_trade_value() trade.recalc_open_trade_value()
trade.stop_loss = None trade.stop_loss = None
trade.initial_stop_loss = None trade.initial_stop_loss = None
trade.initial_stop_loss_pct = None
trade.set_isolated_liq(0.09) trade.set_liquidation_price(3.09)
assert trade.liquidation_price == 0.09 assert trade.liquidation_price == 3.09
assert trade.stop_loss is None assert trade.stop_loss is None
assert trade.initial_stop_loss is None assert trade.initial_stop_loss is None
trade._set_stop_loss(0.08, (1.0 / 9.0)) trade.adjust_stop_loss(2.0, 0.2)
assert trade.liquidation_price == 0.09 assert trade.liquidation_price == 3.09
assert trade.stop_loss == 0.08 assert trade.stop_loss == 2.2
assert trade.initial_stop_loss == 0.08 assert trade.initial_stop_loss == 2.2
assert trade.stoploss_or_liquidation == 2.2
trade.set_isolated_liq(0.1) trade.set_liquidation_price(3.1)
assert trade.liquidation_price == 0.1 assert trade.liquidation_price == 3.1
assert trade.stop_loss == 0.08 assert trade.stop_loss == 2.2
assert trade.initial_stop_loss == 0.08 assert trade.initial_stop_loss == 2.2
assert trade.stoploss_or_liquidation == 2.2
trade.set_isolated_liq(0.07) trade.set_liquidation_price(3.8)
trade._set_stop_loss(0.1, (1.0 / 8.0)) assert trade.liquidation_price == 3.8
assert trade.liquidation_price == 0.07 # Stoploss does not change from liquidation price
assert trade.stop_loss == 0.07 assert trade.stop_loss == 2.2
assert trade.initial_stop_loss == 0.08 assert trade.initial_stop_loss == 2.2
# Stop doesn't move stop higher # Stop doesn't move stop higher
trade._set_stop_loss(0.1, (1.0 / 9.0)) trade.adjust_stop_loss(2.0, 0.3)
assert trade.liquidation_price == 0.07 assert trade.liquidation_price == 3.8
assert trade.stop_loss == 0.07 assert trade.stop_loss == 2.2
assert trade.initial_stop_loss == 0.08 assert trade.initial_stop_loss == 2.2
# Stoploss does move lower
trade.set_liquidation_price(1.5)
trade.adjust_stop_loss(1.8, 0.1)
assert trade.liquidation_price == 1.5
assert pytest.approx(trade.stop_loss) == 1.89
assert trade.initial_stop_loss == 2.2
assert trade.stoploss_or_liquidation == 1.5
@pytest.mark.parametrize('exchange,is_short,lev,minutes,rate,interest,trading_mode', [ @pytest.mark.parametrize('exchange,is_short,lev,minutes,rate,interest,trading_mode', [
@ -1537,26 +1558,26 @@ def test_adjust_stop_loss(fee):
# Get percent of profit with a custom rate (Higher than open rate) # Get percent of profit with a custom rate (Higher than open rate)
trade.adjust_stop_loss(1.3, -0.1) trade.adjust_stop_loss(1.3, -0.1)
assert round(trade.stop_loss, 8) == 1.17 assert pytest.approx(trade.stop_loss) == 1.17
assert trade.stop_loss_pct == -0.1 assert trade.stop_loss_pct == -0.1
assert trade.initial_stop_loss == 0.95 assert trade.initial_stop_loss == 0.95
assert trade.initial_stop_loss_pct == -0.05 assert trade.initial_stop_loss_pct == -0.05
# current rate lower again ... should not change # current rate lower again ... should not change
trade.adjust_stop_loss(1.2, 0.1) trade.adjust_stop_loss(1.2, 0.1)
assert round(trade.stop_loss, 8) == 1.17 assert pytest.approx(trade.stop_loss) == 1.17
assert trade.initial_stop_loss == 0.95 assert trade.initial_stop_loss == 0.95
assert trade.initial_stop_loss_pct == -0.05 assert trade.initial_stop_loss_pct == -0.05
# current rate higher... should raise stoploss # current rate higher... should raise stoploss
trade.adjust_stop_loss(1.4, 0.1) trade.adjust_stop_loss(1.4, 0.1)
assert round(trade.stop_loss, 8) == 1.26 assert pytest.approx(trade.stop_loss) == 1.26
assert trade.initial_stop_loss == 0.95 assert trade.initial_stop_loss == 0.95
assert trade.initial_stop_loss_pct == -0.05 assert trade.initial_stop_loss_pct == -0.05
# Initial is true but stop_loss set - so doesn't do anything # Initial is true but stop_loss set - so doesn't do anything
trade.adjust_stop_loss(1.7, 0.1, True) trade.adjust_stop_loss(1.7, 0.1, True)
assert round(trade.stop_loss, 8) == 1.26 assert pytest.approx(trade.stop_loss) == 1.26
assert trade.initial_stop_loss == 0.95 assert trade.initial_stop_loss == 0.95
assert trade.initial_stop_loss_pct == -0.05 assert trade.initial_stop_loss_pct == -0.05
assert trade.stop_loss_pct == -0.1 assert trade.stop_loss_pct == -0.1
@ -1609,9 +1630,10 @@ def test_adjust_stop_loss_short(fee):
assert trade.initial_stop_loss == 1.05 assert trade.initial_stop_loss == 1.05
assert trade.initial_stop_loss_pct == -0.05 assert trade.initial_stop_loss_pct == -0.05
assert trade.stop_loss_pct == -0.1 assert trade.stop_loss_pct == -0.1
trade.set_isolated_liq(0.63) # Liquidation price is lower than stoploss - so liquidation would trigger first.
trade.set_liquidation_price(0.63)
trade.adjust_stop_loss(0.59, -0.1) trade.adjust_stop_loss(0.59, -0.1)
assert trade.stop_loss == 0.63 assert trade.stop_loss == 0.649
assert trade.liquidation_price == 0.63 assert trade.liquidation_price == 0.63
@ -2009,10 +2031,10 @@ def test_stoploss_reinitialization_short(default_conf, fee):
assert trade_adj.initial_stop_loss == 1.01 assert trade_adj.initial_stop_loss == 1.01
assert trade_adj.initial_stop_loss_pct == -0.05 assert trade_adj.initial_stop_loss_pct == -0.05
# Stoploss can't go above liquidation price # Stoploss can't go above liquidation price
trade_adj.set_isolated_liq(0.985) trade_adj.set_liquidation_price(0.985)
trade.adjust_stop_loss(0.9799, -0.05) trade.adjust_stop_loss(0.9799, -0.05)
assert trade_adj.stop_loss == 0.985 assert trade_adj.stop_loss == 0.989699
assert trade_adj.stop_loss == 0.985 assert trade_adj.liquidation_price == 0.985
def test_update_fee(fee): def test_update_fee(fee):