mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 10:21:59 +00:00
Make execute_sell() use SellCheckTuple for sell reason.
This commit is contained in:
parent
a90e795695
commit
bfad4e82ad
|
@ -28,7 +28,7 @@ from freqtrade.plugins.protectionmanager import ProtectionManager
|
|||
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
|
||||
from freqtrade.rpc import RPCManager, RPCMessageType
|
||||
from freqtrade.state import State
|
||||
from freqtrade.strategy.interface import IStrategy, SellType
|
||||
from freqtrade.strategy.interface import IStrategy, SellCheckTuple, SellType
|
||||
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
|
||||
from freqtrade.wallets import Wallets
|
||||
|
||||
|
@ -850,7 +850,8 @@ class FreqtradeBot(LoggingMixin):
|
|||
trade.stoploss_order_id = None
|
||||
logger.error(f'Unable to place a stoploss order on exchange. {e}')
|
||||
logger.warning('Selling the trade forcefully')
|
||||
self.execute_sell(trade, trade.stop_loss, sell_reason=SellType.EMERGENCY_SELL)
|
||||
self.execute_sell(trade, trade.stop_loss, sell_reason=SellCheckTuple(
|
||||
sell_flag=True, sell_type=SellType.EMERGENCY_SELL))
|
||||
|
||||
except ExchangeError:
|
||||
trade.stoploss_order_id = None
|
||||
|
@ -961,7 +962,7 @@ class FreqtradeBot(LoggingMixin):
|
|||
|
||||
if should_sell.sell_flag:
|
||||
logger.info(f'Executing Sell for {trade.pair}. Reason: {should_sell.sell_type}')
|
||||
self.execute_sell(trade, sell_rate, should_sell.sell_type, should_sell.sell_reason)
|
||||
self.execute_sell(trade, sell_rate, should_sell)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -1150,8 +1151,7 @@ class FreqtradeBot(LoggingMixin):
|
|||
raise DependencyException(
|
||||
f"Not enough amount to sell. Trade-amount: {amount}, Wallet: {wallet_amount}")
|
||||
|
||||
def execute_sell(self, trade: Trade, limit: float, sell_reason: SellType,
|
||||
custom_reason: Optional[str] = None) -> bool:
|
||||
def execute_sell(self, trade: Trade, limit: float, sell_reason: SellCheckTuple) -> bool:
|
||||
"""
|
||||
Executes a limit sell for the given trade and limit
|
||||
:param trade: Trade instance
|
||||
|
@ -1162,7 +1162,7 @@ class FreqtradeBot(LoggingMixin):
|
|||
:return: True if it succeeds (supported) False (not supported)
|
||||
"""
|
||||
sell_type = 'sell'
|
||||
if sell_reason in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS):
|
||||
if sell_reason.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS):
|
||||
sell_type = 'stoploss'
|
||||
|
||||
# if stoploss is on exchange and we are on dry_run mode,
|
||||
|
@ -1179,10 +1179,10 @@ class FreqtradeBot(LoggingMixin):
|
|||
logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}")
|
||||
|
||||
order_type = self.strategy.order_types[sell_type]
|
||||
if sell_reason == SellType.EMERGENCY_SELL:
|
||||
if sell_reason.sell_type == SellType.EMERGENCY_SELL:
|
||||
# Emergency sells (default to market!)
|
||||
order_type = self.strategy.order_types.get("emergencysell", "market")
|
||||
if sell_reason == SellType.FORCE_SELL:
|
||||
if sell_reason.sell_type == SellType.FORCE_SELL:
|
||||
# Force sells (default to the sell_type defined in the strategy,
|
||||
# but we allow this value to be changed)
|
||||
order_type = self.strategy.order_types.get("forcesell", order_type)
|
||||
|
@ -1193,7 +1193,7 @@ class FreqtradeBot(LoggingMixin):
|
|||
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,
|
||||
time_in_force=time_in_force,
|
||||
sell_reason=sell_reason.value):
|
||||
sell_reason=sell_reason.sell_type.value):
|
||||
logger.info(f"User requested abortion of selling {trade.pair}")
|
||||
return False
|
||||
|
||||
|
@ -1216,7 +1216,7 @@ class FreqtradeBot(LoggingMixin):
|
|||
trade.open_order_id = order['id']
|
||||
trade.sell_order_status = ''
|
||||
trade.close_rate_requested = limit
|
||||
trade.sell_reason = custom_reason or sell_reason.value
|
||||
trade.sell_reason = sell_reason.sell_reason
|
||||
# In case of market sell orders the order can be closed immediately
|
||||
if order.get('status', 'unknown') == 'closed':
|
||||
self.update_trade_state(trade, trade.open_order_id, order)
|
||||
|
|
|
@ -24,7 +24,7 @@ from freqtrade.persistence.models import PairLock
|
|||
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
|
||||
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
|
||||
from freqtrade.state import State
|
||||
from freqtrade.strategy.interface import SellType
|
||||
from freqtrade.strategy.interface import SellCheckTuple, SellType
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -554,7 +554,8 @@ class RPC:
|
|||
if not fully_canceled:
|
||||
# Get current rate and execute sell
|
||||
current_rate = self._freqtrade.get_sell_rate(trade.pair, False)
|
||||
self._freqtrade.execute_sell(trade, current_rate, SellType.FORCE_SELL)
|
||||
sell_reason = SellCheckTuple(sell_flag=True, sell_type=SellType.FORCE_SELL)
|
||||
self._freqtrade.execute_sell(trade, current_rate, sell_reason)
|
||||
# ---- EOF def _exec_forcesell ----
|
||||
|
||||
if self._freqtrade.state != State.RUNNING:
|
||||
|
|
|
@ -7,7 +7,7 @@ import warnings
|
|||
from abc import ABC, abstractmethod
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from enum import Enum
|
||||
from typing import Dict, List, NamedTuple, Optional, Tuple, Union
|
||||
from typing import Dict, List, Optional, Tuple, Union
|
||||
|
||||
import arrow
|
||||
from pandas import DataFrame
|
||||
|
@ -54,13 +54,18 @@ class SellType(Enum):
|
|||
return self.value
|
||||
|
||||
|
||||
class SellCheckTuple(NamedTuple):
|
||||
class SellCheckTuple(object):
|
||||
"""
|
||||
NamedTuple for Sell type + reason
|
||||
"""
|
||||
sell_flag: bool
|
||||
sell_flag: bool # TODO: Remove?
|
||||
sell_type: SellType
|
||||
sell_reason: Optional[str] = None
|
||||
sell_reason: Optional[str]
|
||||
|
||||
def __init__(self, sell_flag: bool, sell_type: SellType, sell_reason: Optional[str] = None):
|
||||
self.sell_flag = sell_flag
|
||||
self.sell_type = sell_type
|
||||
self.sell_reason = sell_reason or sell_type.value
|
||||
|
||||
|
||||
class IStrategy(ABC, HyperStrategyMixin):
|
||||
|
@ -594,7 +599,8 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||
|
||||
if sell_signal != SellType.NONE:
|
||||
logger.debug(f"{trade.pair} - Sell signal received. sell_flag=True, "
|
||||
f"sell_type={sell_signal}, custom_reason={custom_reason}")
|
||||
f"sell_type=SellType.{sell_signal.name}" +
|
||||
(f", custom_reason={custom_reason}" if custom_reason else ""))
|
||||
return SellCheckTuple(sell_flag=True, sell_type=sell_signal, sell_reason=custom_reason)
|
||||
|
||||
if stoplossflag.sell_flag:
|
||||
|
|
|
@ -2606,14 +2606,16 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N
|
|||
fetch_ticker=ticker_sell_up
|
||||
)
|
||||
# Prevented sell ...
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], sell_reason=SellType.ROI)
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'],
|
||||
sell_reason=SellCheckTuple(sell_flag=True, sell_type=SellType.ROI))
|
||||
assert rpc_mock.call_count == 0
|
||||
assert freqtrade.strategy.confirm_trade_exit.call_count == 1
|
||||
|
||||
# Repatch with true
|
||||
freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True)
|
||||
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], sell_reason=SellType.ROI)
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'],
|
||||
sell_reason=SellCheckTuple(sell_flag=True, sell_type=SellType.ROI))
|
||||
assert freqtrade.strategy.confirm_trade_exit.call_count == 1
|
||||
|
||||
assert rpc_mock.call_count == 1
|
||||
|
@ -2665,7 +2667,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker)
|
|||
)
|
||||
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'],
|
||||
sell_reason=SellType.STOP_LOSS)
|
||||
sell_reason=SellCheckTuple(sell_flag=True, sell_type=SellType.STOP_LOSS))
|
||||
|
||||
assert rpc_mock.call_count == 2
|
||||
last_msg = rpc_mock.call_args_list[-1][0][0]
|
||||
|
@ -2722,7 +2724,7 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe
|
|||
|
||||
trade.stop_loss = 0.00001099 * 0.99
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'],
|
||||
sell_reason=SellType.STOP_LOSS)
|
||||
sell_reason=SellCheckTuple(sell_flag=True, sell_type=SellType.STOP_LOSS))
|
||||
|
||||
assert rpc_mock.call_count == 2
|
||||
last_msg = rpc_mock.call_args_list[-1][0][0]
|
||||
|
@ -2774,7 +2776,7 @@ def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, c
|
|||
trade.stoploss_order_id = "abcd"
|
||||
|
||||
freqtrade.execute_sell(trade=trade, limit=1234,
|
||||
sell_reason=SellType.STOP_LOSS)
|
||||
sell_reason=SellCheckTuple(sell_flag=True, sell_type=SellType.STOP_LOSS))
|
||||
assert sellmock.call_count == 1
|
||||
assert log_has('Could not cancel stoploss order abcd', caplog)
|
||||
|
||||
|
@ -2824,7 +2826,7 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticke
|
|||
)
|
||||
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'],
|
||||
sell_reason=SellType.SELL_SIGNAL)
|
||||
sell_reason=SellCheckTuple(sell_flag=True, sell_type=SellType.STOP_LOSS))
|
||||
|
||||
trade = Trade.query.first()
|
||||
assert trade
|
||||
|
@ -2929,7 +2931,8 @@ def test_execute_sell_market_order(default_conf, ticker, fee,
|
|||
)
|
||||
freqtrade.config['order_types']['sell'] = 'market'
|
||||
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], sell_reason=SellType.ROI)
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'],
|
||||
sell_reason=SellCheckTuple(sell_flag=True, sell_type=SellType.ROI))
|
||||
|
||||
assert not trade.is_open
|
||||
assert trade.close_profit == 0.0620716
|
||||
|
@ -2983,8 +2986,9 @@ def test_execute_sell_insufficient_funds_error(default_conf, ticker, fee,
|
|||
fetch_ticker=ticker_sell_up
|
||||
)
|
||||
|
||||
sell_reason = SellCheckTuple(sell_flag=True, sell_type=SellType.ROI)
|
||||
assert not freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'],
|
||||
sell_reason=SellType.ROI)
|
||||
sell_reason=sell_reason)
|
||||
assert mock_insuf.call_count == 1
|
||||
|
||||
|
||||
|
@ -3226,7 +3230,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo
|
|||
)
|
||||
|
||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_down()['bid'],
|
||||
sell_reason=SellType.STOP_LOSS)
|
||||
sell_reason=SellCheckTuple(sell_flag=True, sell_type=SellType.STOP_LOSS))
|
||||
trade.close(ticker_sell_down()['bid'])
|
||||
assert freqtrade.strategy.is_pair_locked(trade.pair)
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user