Reorganize, rename, redescribe and add new functionality

This commit is contained in:
eSeR1805 2022-04-16 15:03:09 +03:00
parent e5d4f7766e
commit 76c545ba0d
No known key found for this signature in database
GPG Key ID: BA53686259B46936

View File

@ -22,6 +22,7 @@ from freqtrade.enums import (ExitCheckTuple, ExitType, RPCMessageType, RunMode,
from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError,
InvalidOrderException, PricingError)
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
from freqtrade.exchange.exchange import timeframe_to_next_date
from freqtrade.misc import safe_value_fallback, safe_value_fallback2
from freqtrade.mixins import LoggingMixin
from freqtrade.persistence import Order, PairLocks, Trade, cleanup_db, init_db
@ -188,8 +189,8 @@ class FreqtradeBot(LoggingMixin):
self.strategy.analyze(self.active_pair_whitelist)
with self._exit_lock:
# Check and handle any timed out open orders
self.check_handle_timedout()
# Check for exchange cancelations, timeouts and user requested replace
self.manage_open_orders()
# Protect from collisions with force_exit.
# Without this, freqtrade my try to recreate stoploss_on_exchange orders
@ -1123,13 +1124,13 @@ class FreqtradeBot(LoggingMixin):
return True
return False
def check_handle_timedout(self) -> None:
def manage_open_orders(self) -> None:
"""
Check if any orders are timed out and cancel if necessary
:param timeoutvalue: Number of minutes until order is considered timed out
Management of open orders on exchange. Unfilled orders might be cancelled if timeout
was met or replaced if there's a new candle and user has requested it.
Timeout setting takes priority over limit order adjustment request.
:return: None
"""
for trade in Trade.get_open_order_trades():
try:
if not trade.open_order_id:
@ -1140,33 +1141,78 @@ class FreqtradeBot(LoggingMixin):
continue
fully_cancelled = self.update_trade_state(trade, trade.open_order_id, order)
is_entering = order['side'] == trade.entry_side
not_closed = order['status'] == 'open' or fully_cancelled
max_timeouts = self.config.get('unfilledtimeout', {}).get('exit_timeout_count', 0)
order_obj = trade.select_order_by_order_id(trade.open_order_id)
if not_closed and (fully_cancelled or (order_obj and self.strategy.ft_check_timed_out(
trade, order_obj, datetime.now(timezone.utc)))
):
if is_entering:
self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['TIMEOUT'])
if not_closed:
if fully_cancelled or (order_obj and self.strategy.ft_check_timed_out(
trade, order_obj, datetime.now(timezone.utc))):
self.handle_timedout_orders(order, trade)
else:
canceled = self.handle_cancel_exit(
trade, order, constants.CANCEL_REASON['TIMEOUT'])
canceled_count = trade.get_exit_order_count()
max_timeouts = self.config.get(
'unfilledtimeout', {}).get('exit_timeout_count', 0)
if canceled and max_timeouts > 0 and canceled_count >= max_timeouts:
logger.warning(f'Emergency exiting trade {trade}, as the exit order '
f'timed out {max_timeouts} times.')
try:
self.execute_trade_exit(
trade, order.get('price'),
exit_check=ExitCheckTuple(exit_type=ExitType.EMERGENCY_EXIT))
except DependencyException as exception:
logger.warning(
f'Unable to emergency sell trade {trade.pair}: {exception}')
self.replace_orders(order, order_obj, trade)
def handle_timedout_orders(self, order: Dict, trade: Trade) -> None:
"""
Check if any orders are timed out and cancel if necessary.
:param order: Order dict grabbed with exchange.fetch_order()
:param trade: Trade object.
:return: None
"""
if order['side'] == trade.entry_side:
self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['TIMEOUT'])
else:
canceled = self.handle_cancel_exit(
trade, order, constants.CANCEL_REASON['TIMEOUT'])
canceled_count = trade.get_exit_order_count()
max_timeouts = self.config.get('unfilledtimeout', {}).get('exit_timeout_count', 0)
if canceled and max_timeouts > 0 and canceled_count >= max_timeouts:
logger.warning(f'Emergency exiting trade {trade}, as the exit order '
f'timed out {max_timeouts} times.')
try:
self.execute_trade_exit(
trade, order['price'],
exit_check=ExitCheckTuple(exit_type=ExitType.EMERGENCY_EXIT))
except DependencyException as exception:
logger.warning(
f'Unable to emergency sell trade {trade.pair}: {exception}')
def replace_orders(self, order: Dict, order_obj: Optional[Order], trade: Trade) -> None:
"""
Check if any orders should be replaced and do so
:param order: Order dict grabbed with exchange.fetch_order()
:param order_obj: Order object.
:param trade: Trade object.
:return: None
"""
analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair,
self.strategy.timeframe)
latest_candle_open_date = analyzed_df.iloc[-1]['date'] if len(analyzed_df) > 0 else None
latest_candle_close_date = timeframe_to_next_date(self.strategy.timeframe,
latest_candle_open_date)
# Check if new candle
if order_obj and latest_candle_close_date.replace(tzinfo=None) > order_obj.order_date:
# New candle
proposed_rate = self.exchange.get_rate(
trade.pair, side='entry', is_short=trade.is_short, refresh=True)
adjusted_entry_price = strategy_safe_wrapper(self.strategy.readjust_entry_price,
default_retval=proposed_rate)(
pair=trade.pair, current_time=datetime.now(timezone.utc),
proposed_rate=proposed_rate, entry_tag=trade.enter_tag,
side=trade.entry_side)
# check if user has requested entry limit adjustment
if proposed_rate != adjusted_entry_price:
# cancel existing order
self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['REPLACE'],
allow_full_cancel=False)
stake = self.wallets.get_trade_stake_amount(trade.pair, self.edge)
# place new order with requested price
self.execute_entry(
pair=trade.pair,
stake_amount=stake,
price=adjusted_entry_price,
trade=trade,
is_short=trade.is_short
)
def cancel_all_open_orders(self) -> None:
"""