Avoid double notifications in case of partially filled buy orders
This commit is contained in:
parent
5e75caa917
commit
9c0a3fffd7
@ -338,7 +338,9 @@ SCHEMA_MINIMAL_REQUIRED = [
|
|||||||
|
|
||||||
CANCEL_REASON = {
|
CANCEL_REASON = {
|
||||||
"TIMEOUT": "cancelled due to timeout",
|
"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)",
|
"ALL_CANCELLED": "cancelled (all unfilled and partially filled open orders cancelled)",
|
||||||
"CANCELLED_ON_EXCHANGE": "cancelled on exchange",
|
"CANCELLED_ON_EXCHANGE": "cancelled on exchange",
|
||||||
"FORCE_SELL": "forcesold",
|
"FORCE_SELL": "forcesold",
|
||||||
|
@ -618,7 +618,7 @@ class FreqtradeBot:
|
|||||||
# Send the message
|
# Send the message
|
||||||
self.rpc.send_msg(msg)
|
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.
|
Sends rpc notification when a buy cancel occured.
|
||||||
"""
|
"""
|
||||||
@ -637,6 +637,7 @@ class FreqtradeBot:
|
|||||||
'amount': trade.amount,
|
'amount': trade.amount,
|
||||||
'open_date': trade.open_date,
|
'open_date': trade.open_date,
|
||||||
'current_rate': current_rate,
|
'current_rate': current_rate,
|
||||||
|
'reason': reason,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Send the message
|
# Send the message
|
||||||
@ -993,13 +994,13 @@ class FreqtradeBot:
|
|||||||
|
|
||||||
# Using filled to determine the filled amount
|
# Using filled to determine the filled amount
|
||||||
filled_amount = safe_value_fallback2(corder, order, 'filled', 'filled')
|
filled_amount = safe_value_fallback2(corder, order, 'filled', 'filled')
|
||||||
|
|
||||||
if isclose(filled_amount, 0.0, abs_tol=constants.MATH_CLOSE_PREC):
|
if isclose(filled_amount, 0.0, abs_tol=constants.MATH_CLOSE_PREC):
|
||||||
logger.info('Buy order fully cancelled. Removing %s from database.', trade)
|
logger.info('Buy order fully cancelled. Removing %s from database.', trade)
|
||||||
# if trade is not partially completed, just delete the trade
|
# if trade is not partially completed, just delete the trade
|
||||||
Trade.session.delete(trade)
|
Trade.session.delete(trade)
|
||||||
Trade.session.flush()
|
Trade.session.flush()
|
||||||
was_trade_fully_canceled = True
|
was_trade_fully_canceled = True
|
||||||
|
reason += f", {constants.CANCEL_REASON['FULLY_CANCELLED']}"
|
||||||
else:
|
else:
|
||||||
# if trade is partially complete, edit the stake details for the trade
|
# if trade is partially complete, edit the stake details for the trade
|
||||||
# and close the order
|
# and close the order
|
||||||
@ -1012,13 +1013,11 @@ class FreqtradeBot:
|
|||||||
|
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
logger.info('Partial buy order timeout for %s.', trade)
|
logger.info('Partial buy order timeout for %s.', trade)
|
||||||
self.rpc.send_msg({
|
reason += f", {constants.CANCEL_REASON['PARTIALLY_FILLED']}"
|
||||||
'type': RPCMessageType.STATUS_NOTIFICATION,
|
|
||||||
'status': f'Remaining buy order for {trade.pair} cancelled due to timeout'
|
|
||||||
})
|
|
||||||
|
|
||||||
self.wallets.update()
|
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
|
return was_trade_fully_canceled
|
||||||
|
|
||||||
def handle_cancel_sell(self, trade: Trade, order: Dict, reason: str) -> str:
|
def handle_cancel_sell(self, trade: Trade, order: Dict, reason: str) -> str:
|
||||||
@ -1049,7 +1048,7 @@ class FreqtradeBot:
|
|||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
else:
|
else:
|
||||||
# TODO: figure out how to handle partially complete sell orders
|
# 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.wallets.update()
|
||||||
self._notify_sell_cancel(
|
self._notify_sell_cancel(
|
||||||
|
@ -149,7 +149,7 @@ class Telegram(RPC):
|
|||||||
|
|
||||||
elif msg['type'] == RPCMessageType.BUY_CANCEL_NOTIFICATION:
|
elif msg['type'] == RPCMessageType.BUY_CANCEL_NOTIFICATION:
|
||||||
message = ("\N{WARNING SIGN} *{exchange}:* "
|
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:
|
elif msg['type'] == RPCMessageType.SELL_NOTIFICATION:
|
||||||
msg['amount'] = round(msg['amount'], 8)
|
msg['amount'] = round(msg['amount'], 8)
|
||||||
|
@ -14,6 +14,7 @@ from telegram import Chat, Message, Update
|
|||||||
from telegram.error import NetworkError
|
from telegram.error import NetworkError
|
||||||
|
|
||||||
from freqtrade import __version__
|
from freqtrade import __version__
|
||||||
|
from freqtrade.constants import CANCEL_REASON
|
||||||
from freqtrade.edge import PairInfo
|
from freqtrade.edge import PairInfo
|
||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
@ -1310,9 +1311,10 @@ def test_send_msg_buy_cancel_notification(default_conf, mocker) -> None:
|
|||||||
'type': RPCMessageType.BUY_CANCEL_NOTIFICATION,
|
'type': RPCMessageType.BUY_CANCEL_NOTIFICATION,
|
||||||
'exchange': 'Bittrex',
|
'exchange': 'Bittrex',
|
||||||
'pair': 'ETH/BTC',
|
'pair': 'ETH/BTC',
|
||||||
|
'reason': CANCEL_REASON['TIMEOUT']
|
||||||
})
|
})
|
||||||
assert msg_mock.call_args[0][0] \
|
assert (msg_mock.call_args[0][0] == '\N{WARNING SIGN} *Bittrex:* '
|
||||||
== ('\N{WARNING SIGN} *Bittrex:* Cancelling Open Buy Order for ETH/BTC')
|
'Cancelling open buy Order for ETH/BTC. Reason: cancelled due to timeout.')
|
||||||
|
|
||||||
|
|
||||||
def test_send_msg_sell_notification(default_conf, mocker) -> None:
|
def test_send_msg_sell_notification(default_conf, mocker) -> None:
|
||||||
|
@ -2527,13 +2527,13 @@ def test_handle_cancel_sell_limit(mocker, default_conf, fee) -> None:
|
|||||||
send_msg_mock.reset_mock()
|
send_msg_mock.reset_mock()
|
||||||
|
|
||||||
order['amount'] = 2
|
order['amount'] = 2
|
||||||
assert freqtrade.handle_cancel_sell(trade, order, reason) == CANCEL_REASON['PARTIALLY_FILLED']
|
assert freqtrade.handle_cancel_sell(trade, order, reason) == CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN']
|
||||||
# Assert cancel_order was not called (callcount remains unchanged)
|
# Assert cancel_order was not called (callcount remains unchanged)
|
||||||
assert cancel_order_mock.call_count == 1
|
assert cancel_order_mock.call_count == 1
|
||||||
assert send_msg_mock.call_count == 1
|
assert send_msg_mock.call_count == 1
|
||||||
assert freqtrade.handle_cancel_sell(trade, order, reason) == CANCEL_REASON['PARTIALLY_FILLED']
|
assert freqtrade.handle_cancel_sell(trade, order, reason) == CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN']
|
||||||
# Message should not be iterated again
|
# Message should not be iterated again
|
||||||
assert trade.sell_order_status == CANCEL_REASON['PARTIALLY_FILLED']
|
assert trade.sell_order_status == CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN']
|
||||||
assert send_msg_mock.call_count == 1
|
assert send_msg_mock.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user