Merge branch 'develop' into db_keep_orders

This commit is contained in:
Matthias
2020-09-06 15:22:13 +02:00
26 changed files with 547 additions and 420 deletions

View File

@@ -338,9 +338,12 @@ SCHEMA_MINIMAL_REQUIRED = [
CANCEL_REASON = {
"TIMEOUT": "cancelled due to timeout",
"PARTIALLY_FILLED": "partially filled - keeping order open",
"PARTIALLY_FILLED_KEEP_OPEN": "partially filled - keeping order open",
"PARTIALLY_FILLED": "partially filled",
"FULLY_CANCELLED": "fully cancelled",
"ALL_CANCELLED": "cancelled (all unfilled and partially filled open orders cancelled)",
"CANCELLED_ON_EXCHANGE": "cancelled on exchange",
"FORCE_SELL": "forcesold",
}
# List of pairs with their timeframes

View File

@@ -975,7 +975,12 @@ class Exchange:
@retrier
def cancel_order(self, order_id: str, pair: str) -> Dict:
if self._config['dry_run']:
return {}
order = self._dry_run_open_orders.get(order_id)
if order:
order.update({'status': 'canceled', 'filled': 0.0, 'remaining': order['amount']})
return order
else:
return {}
try:
return self._api.cancel_order(order_id, pair)

View File

@@ -727,7 +727,7 @@ class FreqtradeBot:
# Send the message
self.rpc.send_msg(msg)
def _notify_buy_cancel(self, trade: Trade, order_type: str) -> None:
def _notify_buy_cancel(self, trade: Trade, order_type: str, reason: str) -> None:
"""
Sends rpc notification when a buy cancel occured.
"""
@@ -746,6 +746,7 @@ class FreqtradeBot:
'amount': trade.amount,
'open_date': trade.open_date,
'current_rate': current_rate,
'reason': reason,
}
# Send the message
@@ -1096,7 +1097,6 @@ class FreqtradeBot:
# Cancelled orders may have the status of 'canceled' or 'closed'
if order['status'] not in ('canceled', 'closed'):
reason = constants.CANCEL_REASON['TIMEOUT']
corder = self.exchange.cancel_order_with_result(trade.open_order_id, trade.pair,
trade.amount)
# Avoid race condition where the order could not be cancelled coz its already filled.
@@ -1114,12 +1114,12 @@ class FreqtradeBot:
# Using filled to determine the filled amount
filled_amount = safe_value_fallback2(corder, order, 'filled', 'filled')
if isclose(filled_amount, 0.0, abs_tol=constants.MATH_CLOSE_PREC):
logger.info('Buy order fully cancelled. Removing %s from database.', trade)
# if trade is not partially completed, just delete the trade
trade.delete()
was_trade_fully_canceled = True
reason += f", {constants.CANCEL_REASON['FULLY_CANCELLED']}"
else:
# if trade is partially complete, edit the stake details for the trade
# and close the order
@@ -1132,13 +1132,11 @@ class FreqtradeBot:
trade.open_order_id = None
logger.info('Partial buy order timeout for %s.', trade)
self.rpc.send_msg({
'type': RPCMessageType.STATUS_NOTIFICATION,
'status': f'Remaining buy order for {trade.pair} cancelled due to timeout'
})
reason += f", {constants.CANCEL_REASON['PARTIALLY_FILLED']}"
self.wallets.update()
self._notify_buy_cancel(trade, order_type=self.strategy.order_types['buy'])
self._notify_buy_cancel(trade, order_type=self.strategy.order_types['buy'],
reason=reason)
return was_trade_fully_canceled
def handle_cancel_sell(self, trade: Trade, order: Dict, reason: str) -> str:
@@ -1169,7 +1167,7 @@ class FreqtradeBot:
trade.open_order_id = None
else:
# TODO: figure out how to handle partially complete sell orders
reason = constants.CANCEL_REASON['PARTIALLY_FILLED']
reason = constants.CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN']
self.wallets.update()
self._notify_sell_cancel(

View File

@@ -14,7 +14,7 @@ from freqtrade.pairlist.IPairList import IPairList
logger = logging.getLogger(__name__)
SORT_VALUES = ['askVolume', 'bidVolume', 'quoteVolume']
SORT_VALUES = ['quoteVolume']
class VolumePairList(IPairList):
@@ -45,11 +45,6 @@ class VolumePairList(IPairList):
raise OperationalException(
f'key {self._sort_key} not in {SORT_VALUES}')
if self._sort_key != 'quoteVolume':
logger.warning(
"DEPRECATED: using any key other than quoteVolume for VolumePairList is deprecated."
)
@property
def needstickers(self) -> bool:
"""

View File

@@ -59,7 +59,7 @@ class IResolver:
module = importlib.util.module_from_spec(spec)
try:
spec.loader.exec_module(module) # type: ignore # importlib does not use typehints
except (ModuleNotFoundError, SyntaxError) as err:
except (ModuleNotFoundError, SyntaxError, ImportError) as err:
# Catch errors in case a specific module is not installed
logger.warning(f"Could not import {module_path} due to '{err}'")
if enum_failed:

View File

@@ -11,6 +11,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union
import arrow
from numpy import NAN, mean
from freqtrade.constants import CANCEL_REASON
from freqtrade.exceptions import ExchangeError, PricingError
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_msecs
from freqtrade.loggers import bufferHandler
@@ -223,7 +224,8 @@ class RPC:
Trade.close_date >= profitday,
Trade.close_date < (profitday + timedelta(days=1))
]).order_by(Trade.close_date).all()
curdayprofit = sum(trade.close_profit_abs for trade in trades)
curdayprofit = sum(
trade.close_profit_abs for trade in trades if trade.close_profit_abs is not None)
profit_days[profitday] = {
'amount': curdayprofit,
'trades': len(trades)
@@ -434,7 +436,7 @@ class RPC:
def _rpc_reload_config(self) -> Dict[str, str]:
""" Handler for reload_config. """
self._freqtrade.state = State.RELOAD_CONFIG
return {'status': 'reloading config ...'}
return {'status': 'Reloading config ...'}
def _rpc_stopbuy(self) -> Dict[str, str]:
"""
@@ -453,29 +455,22 @@ class RPC:
"""
def _exec_forcesell(trade: Trade) -> None:
# Check if there is there is an open order
fully_canceled = False
if trade.open_order_id:
order = self._freqtrade.exchange.fetch_order(trade.open_order_id, trade.pair)
# Cancel open LIMIT_BUY orders and close trade
if order and order['status'] == 'open' \
and order['type'] == 'limit' \
and order['side'] == 'buy':
self._freqtrade.exchange.cancel_order(trade.open_order_id, trade.pair)
trade.close(order.get('price') or trade.open_rate)
# Do the best effort, if we don't know 'filled' amount, don't try selling
if order['filled'] is None:
return
trade.amount = order['filled']
if order['side'] == 'buy':
fully_canceled = self._freqtrade.handle_cancel_buy(
trade, order, CANCEL_REASON['FORCE_SELL'])
# Ignore trades with an attached LIMIT_SELL order
if order and order['status'] == 'open' \
and order['type'] == 'limit' \
and order['side'] == 'sell':
return
if order['side'] == 'sell':
# Cancel order - so it is placed anew with a fresh price.
self._freqtrade.handle_cancel_sell(trade, order, CANCEL_REASON['FORCE_SELL'])
# 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)
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)
# ---- EOF def _exec_forcesell ----
if self._freqtrade.state != State.RUNNING:

View File

@@ -151,7 +151,7 @@ class Telegram(RPC):
elif msg['type'] == RPCMessageType.BUY_CANCEL_NOTIFICATION:
message = ("\N{WARNING SIGN} *{exchange}:* "
"Cancelling Open Buy Order for {pair}".format(**msg))
"Cancelling open buy Order for {pair}. Reason: {reason}.".format(**msg))
elif msg['type'] == RPCMessageType.SELL_NOTIFICATION:
msg['amount'] = round(msg['amount'], 8)