Update SellCheckTuple to new naming

This commit is contained in:
Matthias 2022-03-25 06:46:29 +01:00
parent 62e8c7b5b7
commit 8d111d357a
7 changed files with 87 additions and 87 deletions

View File

@ -27,7 +27,7 @@ from freqtrade.plugins.pairlistmanager import PairListManager
from freqtrade.plugins.protectionmanager import ProtectionManager from freqtrade.plugins.protectionmanager import ProtectionManager
from freqtrade.resolvers import ExchangeResolver, StrategyResolver from freqtrade.resolvers import ExchangeResolver, StrategyResolver
from freqtrade.rpc import RPCManager from freqtrade.rpc import RPCManager
from freqtrade.strategy.interface import IStrategy, SellCheckTuple from freqtrade.strategy.interface import IStrategy, ExitCheckTuple
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
from freqtrade.wallets import Wallets from freqtrade.wallets import Wallets
@ -976,8 +976,8 @@ 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=SellCheckTuple( self.execute_trade_exit(trade, trade.stop_loss, exit_check=ExitCheckTuple(
sell_type=SellType.EMERGENCY_SELL)) exit_type=SellType.EMERGENCY_SELL))
except ExchangeError: except ExchangeError:
trade.stoploss_order_id = None trade.stoploss_order_id = None
@ -1101,7 +1101,7 @@ class FreqtradeBot(LoggingMixin):
""" """
Check and execute trade exit Check and execute trade exit
""" """
should_exit: SellCheckTuple = self.strategy.should_exit( should_exit: ExitCheckTuple = self.strategy.should_exit(
trade, trade,
exit_rate, exit_rate,
datetime.now(timezone.utc), datetime.now(timezone.utc),
@ -1110,8 +1110,8 @@ class FreqtradeBot(LoggingMixin):
force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0 force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0
) )
if should_exit.sell_flag: if should_exit.exit_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
@ -1158,7 +1158,7 @@ class FreqtradeBot(LoggingMixin):
try: try:
self.execute_trade_exit( self.execute_trade_exit(
trade, order.get('price'), trade, order.get('price'),
exit_check=SellCheckTuple(sell_type=SellType.EMERGENCY_SELL)) exit_check=ExitCheckTuple(exit_type=SellType.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}')
@ -1333,7 +1333,7 @@ class FreqtradeBot(LoggingMixin):
self, self,
trade: Trade, trade: Trade,
limit: float, limit: float,
exit_check: SellCheckTuple, exit_check: ExitCheckTuple,
*, *,
exit_tag: Optional[str] = None, exit_tag: Optional[str] = None,
ordertype: Optional[str] = None, ordertype: Optional[str] = None,
@ -1352,7 +1352,7 @@ class FreqtradeBot(LoggingMixin):
open_date=trade.open_date, open_date=trade.open_date,
) )
exit_type = 'exit' exit_type = 'exit'
if exit_check.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS): if exit_check.exit_type in (SellType.STOP_LOSS, SellType.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,
@ -1376,7 +1376,7 @@ class FreqtradeBot(LoggingMixin):
trade = self.cancel_stoploss_on_exchange(trade) trade = self.cancel_stoploss_on_exchange(trade)
order_type = ordertype or self.strategy.order_types[exit_type] order_type = ordertype or self.strategy.order_types[exit_type]
if exit_check.sell_type == SellType.EMERGENCY_SELL: if exit_check.exit_type == SellType.EMERGENCY_SELL:
# Emergency sells (default to market!) # Emergency sells (default to market!)
order_type = self.strategy.order_types.get("emergencyexit", "market") order_type = self.strategy.order_types.get("emergencyexit", "market")
@ -1385,8 +1385,8 @@ class FreqtradeBot(LoggingMixin):
if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( if 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_check.sell_reason, time_in_force=time_in_force, exit_reason=exit_check.exit_reason,
sell_reason=exit_check.sell_reason, # sellreason -> compatibility sell_reason=exit_check.exit_reason, # sellreason -> compatibility
current_time=datetime.now(timezone.utc)): current_time=datetime.now(timezone.utc)):
logger.info(f"User requested abortion of exiting {trade.pair}") logger.info(f"User requested abortion of exiting {trade.pair}")
return False return False
@ -1415,7 +1415,7 @@ class FreqtradeBot(LoggingMixin):
trade.open_order_id = order['id'] trade.open_order_id = order['id']
trade.sell_order_status = '' trade.sell_order_status = ''
trade.close_rate_requested = limit trade.close_rate_requested = limit
trade.sell_reason = exit_tag or exit_check.sell_reason trade.sell_reason = exit_tag or exit_check.exit_reason
# Lock pair for one candle to prevent immediate re-trading # Lock pair for one candle to prevent immediate re-trading
self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc), self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc),

View File

@ -31,7 +31,7 @@ from freqtrade.persistence import LocalTrade, Order, PairLocks, Trade
from freqtrade.plugins.pairlistmanager import PairListManager from freqtrade.plugins.pairlistmanager import PairListManager
from freqtrade.plugins.protectionmanager import ProtectionManager from freqtrade.plugins.protectionmanager import ProtectionManager
from freqtrade.resolvers import ExchangeResolver, StrategyResolver from freqtrade.resolvers import ExchangeResolver, StrategyResolver
from freqtrade.strategy.interface import IStrategy, SellCheckTuple from freqtrade.strategy.interface import IStrategy, ExitCheckTuple
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
from freqtrade.wallets import Wallets from freqtrade.wallets import Wallets
@ -352,20 +352,20 @@ class Backtesting:
data[pair] = df_analyzed[headers].values.tolist() data[pair] = df_analyzed[headers].values.tolist()
return data return data
def _get_close_rate(self, sell_row: Tuple, trade: LocalTrade, sell: SellCheckTuple, def _get_close_rate(self, sell_row: Tuple, trade: LocalTrade, sell: ExitCheckTuple,
trade_dur: int) -> float: trade_dur: int) -> float:
""" """
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 (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS): if sell.exit_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS):
return self._get_close_rate_for_stoploss(sell_row, trade, sell, trade_dur) return self._get_close_rate_for_stoploss(sell_row, trade, sell, trade_dur)
elif sell.sell_type == (SellType.ROI): elif sell.exit_type == (SellType.ROI):
return self._get_close_rate_for_roi(sell_row, trade, sell, trade_dur) return self._get_close_rate_for_roi(sell_row, trade, sell, trade_dur)
else: else:
return sell_row[OPEN_IDX] return sell_row[OPEN_IDX]
def _get_close_rate_for_stoploss(self, sell_row: Tuple, trade: LocalTrade, sell: SellCheckTuple, def _get_close_rate_for_stoploss(self, sell_row: Tuple, trade: LocalTrade, sell: ExitCheckTuple,
trade_dur: int) -> float: trade_dur: int) -> float:
# our stoploss was already lower than candle high, # our stoploss was already lower than candle high,
# possibly due to a cancelled trade exit. # possibly due to a cancelled trade exit.
@ -383,7 +383,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 == SellType.TRAILING_STOP_LOSS and trade_dur == 0: if sell.exit_type == SellType.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
@ -413,7 +413,7 @@ class Backtesting:
# Set close_rate to stoploss # Set close_rate to stoploss
return trade.stop_loss return trade.stop_loss
def _get_close_rate_for_roi(self, sell_row: Tuple, trade: LocalTrade, sell: SellCheckTuple, def _get_close_rate_for_roi(self, sell_row: Tuple, trade: LocalTrade, sell: ExitCheckTuple,
trade_dur: int) -> float: trade_dur: int) -> float:
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
@ -521,7 +521,7 @@ class Backtesting:
low=sell_row[LOW_IDX], high=sell_row[HIGH_IDX] low=sell_row[LOW_IDX], high=sell_row[HIGH_IDX]
) )
if sell.sell_flag: if sell.exit_flag:
trade.close_date = sell_candle_time trade.close_date = sell_candle_time
trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60) trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60)
@ -532,7 +532,7 @@ class Backtesting:
# 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)
order_type = self.strategy.order_types['exit'] order_type = self.strategy.order_types['exit']
if sell.sell_type in (SellType.SELL_SIGNAL, SellType.CUSTOM_SELL): if sell.exit_type in (SellType.SELL_SIGNAL, SellType.CUSTOM_SELL):
# Custom exit pricing only for sell-signals # Custom exit pricing only for sell-signals
if order_type == 'limit': if order_type == 'limit':
closerate = strategy_safe_wrapper(self.strategy.custom_exit_price, closerate = strategy_safe_wrapper(self.strategy.custom_exit_price,
@ -553,12 +553,12 @@ class Backtesting:
pair=trade.pair, trade=trade, order_type='limit', amount=trade.amount, pair=trade.pair, trade=trade, order_type='limit', amount=trade.amount,
rate=closerate, rate=closerate,
time_in_force=time_in_force, time_in_force=time_in_force,
sell_reason=sell.sell_reason, # deprecated sell_reason=sell.exit_reason, # deprecated
exit_reason=sell.sell_reason, exit_reason=sell.exit_reason,
current_time=sell_candle_time): current_time=sell_candle_time):
return None return None
trade.sell_reason = sell.sell_reason trade.sell_reason = sell.exit_reason
# Checks and adds an exit tag, after checking that the length of the # Checks and adds an exit tag, after checking that the length of the
# sell_row has the length for an exit tag column # sell_row has the length for an exit tag column

View File

@ -27,7 +27,7 @@ from freqtrade.persistence import PairLocks, Trade
from freqtrade.persistence.models import PairLock from freqtrade.persistence.models import PairLock
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
from freqtrade.strategy.interface import SellCheckTuple from freqtrade.strategy.interface import ExitCheckTuple
from freqtrade.wallets import PositionWallet, Wallet from freqtrade.wallets import PositionWallet, Wallet
@ -707,7 +707,7 @@ class RPC:
# Get current rate and execute sell # Get current rate and execute sell
current_rate = self._freqtrade.exchange.get_rate( current_rate = self._freqtrade.exchange.get_rate(
trade.pair, refresh=False, side=trade.exit_side) trade.pair, refresh=False, side=trade.exit_side)
exit_check = SellCheckTuple(sell_type=SellType.FORCE_SELL) exit_check = ExitCheckTuple(exit_type=SellType.FORCE_SELL)
order_type = ordertype or self._freqtrade.strategy.order_types.get( order_type = ordertype or self._freqtrade.strategy.order_types.get(
"forceexit", self._freqtrade.strategy.order_types["exit"]) "forceexit", self._freqtrade.strategy.order_types["exit"])

View File

@ -32,20 +32,20 @@ logger = logging.getLogger(__name__)
CUSTOM_EXIT_MAX_LENGTH = 64 CUSTOM_EXIT_MAX_LENGTH = 64
class SellCheckTuple: class ExitCheckTuple:
""" """
NamedTuple for Sell type + reason NamedTuple for Exit type + reason
""" """
sell_type: SellType exit_type: SellType
sell_reason: str = '' exit_reason: str = ''
def __init__(self, sell_type: SellType, sell_reason: str = ''): def __init__(self, exit_type: SellType, exit_reason: str = ''):
self.sell_type = sell_type self.exit_type = exit_type
self.sell_reason = sell_reason or sell_type.value self.exit_reason = exit_reason or exit_type.value
@property @property
def sell_flag(self): def exit_flag(self):
return self.sell_type != SellType.NONE return self.exit_type != SellType.NONE
class IStrategy(ABC, HyperStrategyMixin): class IStrategy(ABC, HyperStrategyMixin):
@ -848,7 +848,7 @@ class IStrategy(ABC, HyperStrategyMixin):
def should_exit(self, trade: Trade, rate: float, current_time: datetime, *, def should_exit(self, trade: Trade, rate: float, current_time: datetime, *,
enter: bool, exit_: bool, enter: bool, exit_: bool,
low: float = None, high: float = None, low: float = None, high: float = None,
force_stoploss: float = 0) -> SellCheckTuple: force_stoploss: float = 0) -> ExitCheckTuple:
""" """
This function evaluates if one of the conditions required to trigger an exit order This function evaluates if one of the conditions required to trigger an exit order
has been reached, which can either be a stop-loss, ROI or exit-signal. has been reached, which can either be a stop-loss, ROI or exit-signal.
@ -908,29 +908,29 @@ class IStrategy(ABC, HyperStrategyMixin):
logger.debug(f"{trade.pair} - Sell signal received. " logger.debug(f"{trade.pair} - Sell signal received. "
f"sell_type=SellType.{sell_signal.name}" + f"sell_type=SellType.{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, sell_reason=custom_reason) return ExitCheckTuple(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 != SellType.STOP_LOSS: if roi_reached and stoplossflag.exit_type != SellType.STOP_LOSS:
logger.debug(f"{trade.pair} - Required profit reached. sell_type=SellType.ROI") logger.debug(f"{trade.pair} - Required profit reached. sell_type=SellType.ROI")
return SellCheckTuple(sell_type=SellType.ROI) return ExitCheckTuple(exit_type=SellType.ROI)
if stoplossflag.sell_flag: if stoplossflag.exit_flag:
logger.debug(f"{trade.pair} - Stoploss hit. sell_type={stoplossflag.sell_type}") logger.debug(f"{trade.pair} - Stoploss hit. sell_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=SellType.NONE) return ExitCheckTuple(exit_type=SellType.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,
force_stoploss: float, low: float = None, force_stoploss: float, low: float = None,
high: float = None) -> SellCheckTuple: high: float = None) -> ExitCheckTuple:
""" """
Based on current profit of the trade and configured (trailing) stoploss, Based on current profit of the trade and configured (trailing) stoploss,
decides to exit or not decides to exit or not
@ -1008,9 +1008,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 ExitCheckTuple(exit_type=sell_type)
return SellCheckTuple(sell_type=SellType.NONE) return ExitCheckTuple(exit_type=SellType.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]]:
""" """

View File

@ -18,7 +18,7 @@ from freqtrade.persistence import PairLocks, Trade
from freqtrade.resolvers import StrategyResolver from freqtrade.resolvers import StrategyResolver
from freqtrade.strategy.hyper import (BaseParameter, BooleanParameter, CategoricalParameter, from freqtrade.strategy.hyper import (BaseParameter, BooleanParameter, CategoricalParameter,
DecimalParameter, IntParameter, RealParameter) DecimalParameter, IntParameter, RealParameter)
from freqtrade.strategy.interface import SellCheckTuple from freqtrade.strategy.interface import ExitCheckTuple
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
from tests.conftest import CURRENT_TEST_STRATEGY, TRADE_SIDES, log_has, log_has_re from tests.conftest import CURRENT_TEST_STRATEGY, TRADE_SIDES, log_has, log_has_re
@ -455,23 +455,23 @@ def test_stop_loss_reached(default_conf, fee, profit, adjusted, expected, traili
sl_flag = strategy.stop_loss_reached(current_rate=current_rate, trade=trade, sl_flag = strategy.stop_loss_reached(current_rate=current_rate, trade=trade,
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, ExitCheckTuple)
assert sl_flag.sell_type == expected assert sl_flag.exit_type == expected
if expected == SellType.NONE: if expected == SellType.NONE:
assert sl_flag.sell_flag is False assert sl_flag.exit_flag is False
else: else:
assert sl_flag.sell_flag is True assert sl_flag.exit_flag is True
assert round(trade.stop_loss, 2) == adjusted assert round(trade.stop_loss, 2) == adjusted
current_rate2 = trade.open_rate * (1 + profit2) current_rate2 = trade.open_rate * (1 + profit2)
sl_flag = strategy.stop_loss_reached(current_rate=current_rate2, trade=trade, sl_flag = strategy.stop_loss_reached(current_rate=current_rate2, 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 == SellType.NONE: if expected2 == SellType.NONE:
assert sl_flag.sell_flag is False assert sl_flag.exit_flag is False
else: else:
assert sl_flag.sell_flag is True assert sl_flag.exit_flag is True
assert round(trade.stop_loss, 2) == adjusted2 assert round(trade.stop_loss, 2) == adjusted2
strategy.custom_stoploss = original_stopvalue strategy.custom_stoploss = original_stopvalue
@ -496,34 +496,34 @@ def test_custom_exit(default_conf, fee, caplog) -> None:
enter=False, exit_=False, enter=False, exit_=False,
low=None, high=None) low=None, high=None)
assert res.sell_flag is False assert res.exit_flag is False
assert res.sell_type == SellType.NONE assert res.exit_type == SellType.NONE
strategy.custom_exit = MagicMock(return_value=True) strategy.custom_exit = 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.exit_flag is True
assert res.sell_type == SellType.CUSTOM_SELL assert res.exit_type == SellType.CUSTOM_SELL
assert res.sell_reason == 'custom_sell' assert res.exit_reason == 'custom_sell'
strategy.custom_exit = MagicMock(return_value='hello world') strategy.custom_exit = MagicMock(return_value='hello world')
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 == SellType.CUSTOM_SELL assert res.exit_type == SellType.CUSTOM_SELL
assert res.sell_flag is True assert res.exit_flag is True
assert res.sell_reason == 'hello world' assert res.exit_reason == 'hello world'
caplog.clear() caplog.clear()
strategy.custom_exit = MagicMock(return_value='h' * 100) strategy.custom_exit = MagicMock(return_value='h' * 100)
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 == SellType.CUSTOM_SELL assert res.exit_type == SellType.CUSTOM_SELL
assert res.sell_flag is True assert res.exit_flag is True
assert res.sell_reason == 'h' * 64 assert res.exit_reason == 'h' * 64
assert log_has_re('Custom sell reason returned from custom_exit is too long.*', caplog) assert log_has_re('Custom sell reason returned from custom_exit is too long.*', caplog)

View File

@ -20,7 +20,7 @@ from freqtrade.exceptions import (DependencyException, ExchangeError, Insufficie
from freqtrade.freqtradebot import FreqtradeBot from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import Order, PairLocks, Trade from freqtrade.persistence import Order, PairLocks, Trade
from freqtrade.persistence.models import PairLock from freqtrade.persistence.models import PairLock
from freqtrade.strategy.interface import SellCheckTuple from freqtrade.strategy.interface import ExitCheckTuple
from freqtrade.worker import Worker from freqtrade.worker import Worker
from tests.conftest import (create_mock_trades, get_patched_freqtradebot, get_patched_worker, from tests.conftest import (create_mock_trades, get_patched_freqtradebot, get_patched_worker,
log_has, log_has_re, patch_edge, patch_exchange, patch_get_signal, log_has, log_has_re, patch_edge, patch_exchange, patch_get_signal,
@ -3100,7 +3100,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_check=SellCheckTuple(sell_type=SellType.ROI) exit_check=ExitCheckTuple(exit_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
@ -3112,7 +3112,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_check=SellCheckTuple(sell_type=SellType.ROI) exit_check=ExitCheckTuple(exit_type=SellType.ROI)
) )
assert freqtrade.strategy.confirm_trade_exit.call_count == 1 assert freqtrade.strategy.confirm_trade_exit.call_count == 1
@ -3173,7 +3173,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_check=SellCheckTuple(sell_type=SellType.STOP_LOSS)) exit_check=ExitCheckTuple(exit_type=SellType.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]
@ -3248,7 +3248,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_check=SellCheckTuple(sell_type=SellType.SELL_SIGNAL) exit_check=ExitCheckTuple(exit_type=SellType.SELL_SIGNAL)
) )
# Sell price must be different to default bid price # Sell price must be different to default bid price
@ -3319,7 +3319,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_check=SellCheckTuple(sell_type=SellType.STOP_LOSS)) exit_check=ExitCheckTuple(exit_type=SellType.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]
@ -3379,7 +3379,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_check=SellCheckTuple(sell_type=SellType.STOP_LOSS)) exit_check=ExitCheckTuple(exit_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)
@ -3434,7 +3434,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_check=SellCheckTuple(sell_type=SellType.STOP_LOSS) exit_check=ExitCheckTuple(exit_type=SellType.STOP_LOSS)
) )
trade = Trade.query.first() trade = Trade.query.first()
@ -3579,7 +3579,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_check=SellCheckTuple(sell_type=SellType.ROI) exit_check=ExitCheckTuple(exit_type=SellType.ROI)
) )
assert not trade.is_open assert not trade.is_open
@ -3643,7 +3643,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
) )
sell_reason = SellCheckTuple(sell_type=SellType.ROI) sell_reason = ExitCheckTuple(exit_type=SellType.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'],
@ -3696,8 +3696,8 @@ def test_sell_profit_only(
if sell_type == SellType.SELL_SIGNAL.value: if sell_type == SellType.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=ExitCheckTuple(
sell_type=SellType.NONE)) exit_type=SellType.NONE))
freqtrade.enter_positions() freqtrade.enter_positions()
trade = Trade.query.first() trade = Trade.query.first()
@ -3816,7 +3816,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_check=SellCheckTuple(sell_type=SellType.STOP_LOSS) exit_check=ExitCheckTuple(exit_type=SellType.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)
@ -5145,7 +5145,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_check=SellCheckTuple(sell_type=SellType.ROI) exit_check=ExitCheckTuple(exit_type=SellType.ROI)
) )
assert trade.funding_fees == pytest.approx(sum( assert trade.funding_fees == pytest.approx(sum(
trade.amount * trade.amount *

View File

@ -6,7 +6,7 @@ from freqtrade.enums import SellType
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.persistence.models import Order from freqtrade.persistence.models import Order
from freqtrade.rpc.rpc import RPC from freqtrade.rpc.rpc import RPC
from freqtrade.strategy.interface import SellCheckTuple from freqtrade.strategy.interface import ExitCheckTuple
from tests.conftest import get_patched_freqtradebot, patch_get_signal from tests.conftest import get_patched_freqtradebot, patch_get_signal
@ -53,8 +53,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=SellType.NONE), ExitCheckTuple(exit_type=SellType.NONE),
SellCheckTuple(sell_type=SellType.SELL_SIGNAL)] ExitCheckTuple(exit_type=SellType.SELL_SIGNAL)]
) )
cancel_order_mock = MagicMock() cancel_order_mock = MagicMock()
mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss) mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss)
@ -161,11 +161,11 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, mocker, balance_rati
_notify_exit=MagicMock(), _notify_exit=MagicMock(),
) )
should_sell_mock = MagicMock(side_effect=[ should_sell_mock = MagicMock(side_effect=[
SellCheckTuple(sell_type=SellType.NONE), ExitCheckTuple(exit_type=SellType.NONE),
SellCheckTuple(sell_type=SellType.SELL_SIGNAL), ExitCheckTuple(exit_type=SellType.SELL_SIGNAL),
SellCheckTuple(sell_type=SellType.NONE), ExitCheckTuple(exit_type=SellType.NONE),
SellCheckTuple(sell_type=SellType.NONE), ExitCheckTuple(exit_type=SellType.NONE),
SellCheckTuple(sell_type=SellType.NONE)] ExitCheckTuple(exit_type=SellType.NONE)]
) )
mocker.patch("freqtrade.strategy.interface.IStrategy.should_exit", should_sell_mock) mocker.patch("freqtrade.strategy.interface.IStrategy.should_exit", should_sell_mock)