updated tests and telegram

This commit is contained in:
Aezo Teo 2021-12-29 21:24:12 +08:00
parent 6e24274dca
commit 1f773671ed
8 changed files with 302 additions and 108 deletions

View File

@ -98,13 +98,13 @@ Different payloads can be configured for different events. Not all fields are ne
### Webhookbuy ### Webhookbuy
The fields in `webhook.webhookbuy` are filled when the bot executes a buy. Parameters are filled using string.format. The fields in `webhook.webhookbuy` are filled when the bot executes a long/short. Parameters are filled using string.format.
Possible parameters are: Possible parameters are:
* `trade_id` * `trade_id`
* `exchange` * `exchange`
* `pair` * `pair`
* `is_short` * `direction`
* `leverage` * `leverage`
* ~~`limit` # Deprecated - should no longer be used.~~ * ~~`limit` # Deprecated - should no longer be used.~~
* `open_rate` * `open_rate`
@ -116,18 +116,17 @@ Possible parameters are:
* `order_type` * `order_type`
* `current_rate` * `current_rate`
* `enter_tag` * `enter_tag`
* `gain`
* `profit_amount`
* `profit_ratio`
### Webhookbuycancel ### Webhookbuycancel
The fields in `webhook.webhookbuycancel` are filled when the bot cancels a buy order. Parameters are filled using string.format. The fields in `webhook.webhookbuycancel` are filled when the bot cancels a long/short order. Parameters are filled using string.format.
Possible parameters are: Possible parameters are:
* `trade_id` * `trade_id`
* `exchange` * `exchange`
* `pair` * `pair`
* `direction`
* `leverage`
* `limit` * `limit`
* `amount` * `amount`
* `open_date` * `open_date`
@ -140,13 +139,13 @@ Possible parameters are:
### Webhookbuyfill ### Webhookbuyfill
The fields in `webhook.webhookbuyfill` are filled when the bot filled a buy order. Parameters are filled using string.format. The fields in `webhook.webhookbuyfill` are filled when the bot filled a long/short order. Parameters are filled using string.format.
Possible parameters are: Possible parameters are:
* `trade_id` * `trade_id`
* `exchange` * `exchange`
* `pair` * `pair`
* `is_short` * `direction`
* `leverage` * `leverage`
* `open_rate` * `open_rate`
* `amount` * `amount`
@ -157,9 +156,6 @@ Possible parameters are:
* `order_type` * `order_type`
* `current_rate` * `current_rate`
* `enter_tag` * `enter_tag`
* `gain`
* `profit_amount`
* `profit_ratio`
### Webhooksell ### Webhooksell
The fields in `webhook.webhooksell` are filled when the bot sells a trade. Parameters are filled using string.format. The fields in `webhook.webhooksell` are filled when the bot sells a trade. Parameters are filled using string.format.
@ -168,7 +164,7 @@ Possible parameters are:
* `trade_id` * `trade_id`
* `exchange` * `exchange`
* `pair` * `pair`
* `is_short` * `direction`
* `leverage` * `leverage`
* `gain` * `gain`
* `limit` * `limit`
@ -191,7 +187,7 @@ Possible parameters are:
* `trade_id` * `trade_id`
* `exchange` * `exchange`
* `pair` * `pair`
* `is_short` * `direction`
* `leverage` * `leverage`
* `gain` * `gain`
* `close_rate` * `close_rate`
@ -215,6 +211,8 @@ Possible parameters are:
* `trade_id` * `trade_id`
* `exchange` * `exchange`
* `pair` * `pair`
* `direction`
* `leverage`
* `gain` * `gain`
* `limit` * `limit`
* `amount` * `amount`

View File

@ -775,6 +775,8 @@ class FreqtradeBot(LoggingMixin):
'enter_tag': trade.enter_tag, 'enter_tag': trade.enter_tag,
'exchange': self.exchange.name.capitalize(), 'exchange': self.exchange.name.capitalize(),
'pair': trade.pair, 'pair': trade.pair,
'leverage': trade.leverage if trade.leverage else None,
'direction': 'Short' if trade.is_short else 'Long',
'limit': trade.open_rate, # Deprecated (?) 'limit': trade.open_rate, # Deprecated (?)
'open_rate': trade.open_rate, 'open_rate': trade.open_rate,
'order_type': order_type, 'order_type': order_type,
@ -802,6 +804,8 @@ class FreqtradeBot(LoggingMixin):
'enter_tag': trade.enter_tag, 'enter_tag': trade.enter_tag,
'exchange': self.exchange.name.capitalize(), 'exchange': self.exchange.name.capitalize(),
'pair': trade.pair, 'pair': trade.pair,
'leverage': trade.leverage,
'direction': 'Short' if trade.is_short else 'Long',
'limit': trade.open_rate, 'limit': trade.open_rate,
'order_type': order_type, 'order_type': order_type,
'stake_amount': trade.stake_amount, 'stake_amount': trade.stake_amount,
@ -1376,6 +1380,8 @@ class FreqtradeBot(LoggingMixin):
'trade_id': trade.id, 'trade_id': trade.id,
'exchange': trade.exchange.capitalize(), 'exchange': trade.exchange.capitalize(),
'pair': trade.pair, 'pair': trade.pair,
'leverage': trade.leverage,
'direction': 'Short' if trade.is_short else 'Long',
'gain': gain, 'gain': gain,
'limit': profit_rate, 'limit': profit_rate,
'order_type': order_type, 'order_type': order_type,
@ -1422,6 +1428,8 @@ class FreqtradeBot(LoggingMixin):
'trade_id': trade.id, 'trade_id': trade.id,
'exchange': trade.exchange.capitalize(), 'exchange': trade.exchange.capitalize(),
'pair': trade.pair, 'pair': trade.pair,
'leverage': trade.leverage,
'direction': 'Short' if trade.is_short else 'Long',
'gain': gain, 'gain': gain,
'limit': profit_rate or 0, 'limit': profit_rate or 0,
'order_type': order_type, 'order_type': order_type,

View File

@ -257,7 +257,7 @@ class RPC:
if self._fiat_converter: if self._fiat_converter:
profitcol += " (" + fiat_display_currency + ")" profitcol += " (" + fiat_display_currency + ")"
columns = ['ID', 'Pair', 'Since', profitcol] columns = ['ID', 'Pair (L/S)', 'Since', profitcol]
return trades_list, columns, fiat_profit_sum return trades_list, columns, fiat_profit_sum
def _rpc_daily_profit( def _rpc_daily_profit(

View File

@ -221,20 +221,24 @@ class Telegram(RPCHandler):
msg['stake_amount'], msg['stake_currency'], msg['fiat_currency']) msg['stake_amount'], msg['stake_currency'], msg['fiat_currency'])
else: else:
msg['stake_amount_fiat'] = 0 msg['stake_amount_fiat'] = 0
is_fill = msg['type'] == RPCMessageType.BUY_FILL is_fill = msg['type'] in [RPCMessageType.BUY_FILL, RPCMessageType.SHORT_FILL]
emoji = '\N{CHECK MARK}' if is_fill else '\N{LARGE BLUE CIRCLE}' emoji = '\N{CHECK MARK}' if is_fill else '\N{LARGE BLUE CIRCLE}'
enter_side = {'enter': 'Long', 'entered': 'Longed'} if msg['type'] \
in [RPCMessageType.BUY_FILL, RPCMessageType.BUY] \
else {'enter': 'Short', 'entered': 'Shorted'}
message = ( message = (
f"{emoji} *{msg['exchange']}:* {'Bought' if is_fill else 'Buying'} {msg['pair']}" f"{emoji} *{msg['exchange']}:*"
f" {enter_side['entered'] if is_fill else enter_side['enter']} {msg['pair']}"
f" (#{msg['trade_id']})\n" f" (#{msg['trade_id']})\n"
) )
message += f"*Enter Tag:* `{msg['enter_tag']}`\n" if msg.get('enter_tag', None) else "" message += f"*Enter Tag:* `{msg['enter_tag']}`\n" if msg.get('enter_tag', None) else ""
message += f"*Leverage:* `{msg['leverage']}`\n" if msg.get('leverage', None) else ""
message += f"*Amount:* `{msg['amount']:.8f}`\n" message += f"*Amount:* `{msg['amount']:.8f}`\n"
if msg['type'] == RPCMessageType.BUY_FILL: if msg['type'] in [RPCMessageType.BUY_FILL, RPCMessageType.SHORT_FILL]:
message += f"*Open Rate:* `{msg['open_rate']:.8f}`\n" message += f"*Open Rate:* `{msg['open_rate']:.8f}`\n"
elif msg['type'] in [RPCMessageType.BUY, RPCMessageType.SHORT]:
elif msg['type'] == RPCMessageType.BUY:
message += f"*Open Rate:* `{msg['limit']:.8f}`\n"\ message += f"*Open Rate:* `{msg['limit']:.8f}`\n"\
f"*Current Rate:* `{msg['current_rate']:.8f}`\n" f"*Current Rate:* `{msg['current_rate']:.8f}`\n"
@ -268,17 +272,21 @@ class Telegram(RPCHandler):
else: else:
msg['profit_extra'] = '' msg['profit_extra'] = ''
is_fill = msg['type'] == RPCMessageType.SELL_FILL is_fill = msg['type'] == RPCMessageType.SELL_FILL
message = ( message = [
f"{msg['emoji']} *{msg['exchange']}:* " f"{msg['emoji']} *{msg['exchange']}:* ",
f"{'Sold' if is_fill else 'Selling'} {msg['pair']} (#{msg['trade_id']})\n" f"{'Exited' if is_fill else 'Exiting'} {msg['pair']} (#{msg['trade_id']})\n",
f"*{'Profit' if is_fill else 'Unrealized Profit'}:* " f"*{'Profit' if is_fill else 'Unrealized Profit'}:* ",
f"`{msg['profit_ratio']:.2%}{msg['profit_extra']}`\n" f"`{msg['profit_ratio']:.2%}{msg['profit_extra']}`\n",
f"*Enter Tag:* `{msg['enter_tag']}`\n" f"*Enter Tag:* `{msg['enter_tag']}`\n",
f"*Sell Reason:* `{msg['sell_reason']}`\n" f"*Exit Reason:* `{msg['sell_reason']}`\n",
f"*Duration:* `{msg['duration']} ({msg['duration_min']:.1f} min)`\n" f"*Duration:* `{msg['duration']} ({msg['duration_min']:.1f} min)`\n",
f"*Amount:* `{msg['amount']:.8f}`\n" f"*Direction:* `{msg['direction']}`\n",
f"*Open Rate:* `{msg['open_rate']:.8f}`\n") f"*Leverage:* `{msg['leverage']:.1f}`\n" if
msg.get('leverage', None) is not None else "",
f"*Amount:* `{msg['amount']:.8f}`\n",
f"*Open Rate:* `{msg['open_rate']:.8f}`\n"
]
message = "".join(message)
if msg['type'] == RPCMessageType.SELL: if msg['type'] == RPCMessageType.SELL:
message += (f"*Current Rate:* `{msg['current_rate']:.8f}`\n" message += (f"*Current Rate:* `{msg['current_rate']:.8f}`\n"
f"*Close Rate:* `{msg['limit']:.8f}`") f"*Close Rate:* `{msg['limit']:.8f}`")
@ -289,16 +297,19 @@ class Telegram(RPCHandler):
return message return message
def compose_message(self, msg: Dict[str, Any], msg_type: RPCMessageType) -> str: def compose_message(self, msg: Dict[str, Any], msg_type: RPCMessageType) -> str:
if msg_type in [RPCMessageType.BUY, RPCMessageType.BUY_FILL]: if msg_type in [RPCMessageType.BUY, RPCMessageType.BUY_FILL, RPCMessageType.SHORT,
RPCMessageType.SHORT_FILL]:
message = self._format_buy_msg(msg) message = self._format_buy_msg(msg)
elif msg_type in [RPCMessageType.SELL, RPCMessageType.SELL_FILL]: elif msg_type in [RPCMessageType.SELL, RPCMessageType.SELL_FILL]:
message = self._format_sell_msg(msg) message = self._format_sell_msg(msg)
elif msg_type in (RPCMessageType.BUY_CANCEL, RPCMessageType.SELL_CANCEL): elif msg_type in (RPCMessageType.BUY_CANCEL, RPCMessageType.SHORT_CANCEL,
msg['message_side'] = 'buy' if msg_type == RPCMessageType.BUY_CANCEL else 'sell' RPCMessageType.SELL_CANCEL):
msg['message_side'] = 'enter' if msg_type in [RPCMessageType.BUY_CANCEL,
RPCMessageType.SHORT_CANCEL] else 'exit'
message = ("\N{WARNING SIGN} *{exchange}:* " message = ("\N{WARNING SIGN} *{exchange}:* "
"Cancelling open {message_side} Order for {pair} (#{trade_id}). " "Cancelling {message_side} Order for {pair} (#{trade_id}). "
"Reason: {reason}.".format(**msg)) "Reason: {reason}.".format(**msg))
elif msg_type == RPCMessageType.PROTECTION_TRIGGER: elif msg_type == RPCMessageType.PROTECTION_TRIGGER:
@ -398,8 +409,8 @@ class Telegram(RPCHandler):
lines = [ lines = [
"*Trade ID:* `{trade_id}` `(since {open_date_hum})`", "*Trade ID:* `{trade_id}` `(since {open_date_hum})`",
"*Current Pair:* {pair}", "*Current Pair:* {pair}",
"*Direction:* " + ("`Short`" if r['is_short'] else "`Long`"), "*Direction:* " + ("`Short`" if r.get('is_short') else "`Long`"),
"*Leverage:* `{leverage}`" if r['leverage'] else "", "*Leverage:* `{leverage}`" if r.get('leverage') else "",
"*Amount:* `{amount} ({stake_amount} {base_currency})`", "*Amount:* `{amount} ({stake_amount} {base_currency})`",
"*Enter Tag:* `{enter_tag}`" if r['enter_tag'] else "", "*Enter Tag:* `{enter_tag}`" if r['enter_tag'] else "",
"*Open Rate:* `{open_rate:.8f}`", "*Open Rate:* `{open_rate:.8f}`",

View File

@ -44,11 +44,11 @@ class Webhook(RPCHandler):
""" Send a message to telegram channel """ """ Send a message to telegram channel """
try: try:
if msg['type'] == RPCMessageType.BUY: if msg['type'] in [RPCMessageType.BUY, RPCMessageType.SHORT]:
valuedict = self._config['webhook'].get('webhookbuy', None) valuedict = self._config['webhook'].get('webhookbuy', None)
elif msg['type'] == RPCMessageType.BUY_CANCEL: elif msg['type'] in [RPCMessageType.BUY_CANCEL, RPCMessageType.SHORT_CANCEL]:
valuedict = self._config['webhook'].get('webhookbuycancel', None) valuedict = self._config['webhook'].get('webhookbuycancel', None)
elif msg['type'] == RPCMessageType.BUY_FILL: elif msg['type'] in [RPCMessageType.BUY_FILL, RPCMessageType.SHORT_FILL]:
valuedict = self._config['webhook'].get('webhookbuyfill', None) valuedict = self._config['webhook'].get('webhookbuyfill', None)
elif msg['type'] == RPCMessageType.SELL: elif msg['type'] == RPCMessageType.SELL:
valuedict = self._config['webhook'].get('webhooksell', None) valuedict = self._config['webhook'].get('webhooksell', None)

View File

@ -202,7 +202,8 @@ def test_telegram_status(default_conf, update, mocker) -> None:
'stoploss_current_dist_ratio': -0.0002, 'stoploss_current_dist_ratio': -0.0002,
'stop_loss_ratio': -0.0001, 'stop_loss_ratio': -0.0001,
'open_order': '(limit buy rem=0.00000000)', 'open_order': '(limit buy rem=0.00000000)',
'is_open': True 'is_open': True,
'is_short': False
}]), }]),
) )
@ -946,11 +947,13 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'gain': 'profit', 'gain': 'profit',
'leverage': 1.0,
'limit': 1.173e-05, 'limit': 1.173e-05,
'amount': 91.07468123, 'amount': 91.07468123,
'order_type': 'limit', 'order_type': 'limit',
'open_rate': 1.098e-05, 'open_rate': 1.098e-05,
'current_rate': 1.173e-05, 'current_rate': 1.173e-05,
'direction': 'Long',
'profit_amount': 6.314e-05, 'profit_amount': 6.314e-05,
'profit_ratio': 0.0629778, 'profit_ratio': 0.0629778,
'stake_currency': 'BTC', 'stake_currency': 'BTC',
@ -1011,11 +1014,13 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'gain': 'loss', 'gain': 'loss',
'leverage': 1.0,
'limit': 1.043e-05, 'limit': 1.043e-05,
'amount': 91.07468123, 'amount': 91.07468123,
'order_type': 'limit', 'order_type': 'limit',
'open_rate': 1.098e-05, 'open_rate': 1.098e-05,
'current_rate': 1.043e-05, 'current_rate': 1.043e-05,
'direction': 'Long',
'profit_amount': -5.497e-05, 'profit_amount': -5.497e-05,
'profit_ratio': -0.05482878, 'profit_ratio': -0.05482878,
'stake_currency': 'BTC', 'stake_currency': 'BTC',
@ -1066,11 +1071,13 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'gain': 'loss', 'gain': 'loss',
'leverage': 1.0,
'limit': 1.099e-05, 'limit': 1.099e-05,
'amount': 91.07468123, 'amount': 91.07468123,
'order_type': 'limit', 'order_type': 'limit',
'open_rate': 1.098e-05, 'open_rate': 1.098e-05,
'current_rate': 1.099e-05, 'current_rate': 1.099e-05,
'direction': 'Long',
'profit_amount': -4.09e-06, 'profit_amount': -4.09e-06,
'profit_ratio': -0.00408133, 'profit_ratio': -0.00408133,
'stake_currency': 'BTC', 'stake_currency': 'BTC',
@ -1643,14 +1650,20 @@ def test_show_config_handle(default_conf, update, mocker) -> None:
assert '*Initial Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0] assert '*Initial Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0]
def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None: @pytest.mark.parametrize(
'message_type,enter,enter_signal,leverage',
[(RPCMessageType.BUY, 'Long', 'long_signal_01', None),
(RPCMessageType.SHORT, 'Short', 'short_signal_01', 2.0)])
def test_send_msg_buy_notification(default_conf, mocker, caplog, message_type,
enter, enter_signal, leverage) -> None:
msg = { msg = {
'type': RPCMessageType.BUY, 'type': message_type,
'trade_id': 1, 'trade_id': 1,
'enter_tag': 'buy_signal_01', 'enter_tag': enter_signal,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'leverage': leverage,
'limit': 1.099e-05, 'limit': 1.099e-05,
'order_type': 'limit', 'order_type': 'limit',
'stake_amount': 0.001, 'stake_amount': 0.001,
@ -1664,13 +1677,15 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None:
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
telegram.send_msg(msg) telegram.send_msg(msg)
assert msg_mock.call_args[0][0] \ message = [f'\N{LARGE BLUE CIRCLE} *Binance:* {enter} ETH/BTC (#1)\n',
== '\N{LARGE BLUE CIRCLE} *Binance:* Buying ETH/BTC (#1)\n' \ f'*Enter Tag:* `{enter_signal}`\n',
'*Enter Tag:* `buy_signal_01`\n' \ f'*Leverage:* `{leverage}`\n' if leverage else '',
'*Amount:* `1333.33333333`\n' \ '*Amount:* `1333.33333333`\n',
'*Open Rate:* `0.00001099`\n' \ '*Open Rate:* `0.00001099`\n',
'*Current Rate:* `0.00001099`\n' \ '*Current Rate:* `0.00001099`\n',
'*Total:* `(0.00100000 BTC, 12.345 USD)`' '*Total:* `(0.00100000 BTC, 12.345 USD)`']
assert msg_mock.call_args[0][0] == "".join(message)
freqtradebot.config['telegram']['notification_settings'] = {'buy': 'off'} freqtradebot.config['telegram']['notification_settings'] = {'buy': 'off'}
caplog.clear() caplog.clear()
@ -1688,20 +1703,25 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None:
msg_mock.call_args_list[0][1]['disable_notification'] is True msg_mock.call_args_list[0][1]['disable_notification'] is True
def test_send_msg_buy_cancel_notification(default_conf, mocker) -> None: @pytest.mark.parametrize(
'message_type,enter,enter_signal',
[(RPCMessageType.BUY_CANCEL, 'Long', 'long_signal_01'),
(RPCMessageType.SHORT_CANCEL, 'Short', 'short_signal_01')])
def test_send_msg_buy_cancel_notification(default_conf, mocker, message_type,
enter, enter_signal) -> None:
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
telegram.send_msg({ telegram.send_msg({
'type': RPCMessageType.BUY_CANCEL, 'type': message_type,
'enter_tag': 'buy_signal_01', 'enter_tag': enter_signal,
'trade_id': 1, 'trade_id': 1,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'reason': CANCEL_REASON['TIMEOUT'] 'reason': CANCEL_REASON['TIMEOUT']
}) })
assert (msg_mock.call_args[0][0] == '\N{WARNING SIGN} *Binance:* ' assert (msg_mock.call_args[0][0] == '\N{WARNING SIGN} *Binance:* '
'Cancelling open buy Order for ETH/BTC (#1). ' 'Cancelling enter Order for ETH/BTC (#1). '
'Reason: cancelled due to timeout.') 'Reason: cancelled due to timeout.')
@ -1733,17 +1753,23 @@ def test_send_msg_protection_notification(default_conf, mocker, time_machine) ->
"*All pairs* will be locked until `2021-09-01 06:45:00`.") "*All pairs* will be locked until `2021-09-01 06:45:00`.")
def test_send_msg_buy_fill_notification(default_conf, mocker) -> None: @pytest.mark.parametrize(
'message_type,entered,enter_signal,leverage',
[(RPCMessageType.BUY_FILL, 'Longed', 'long_signal_01', None),
(RPCMessageType.SHORT_FILL, 'Shorted', 'short_signal_01', 2.0)])
def test_send_msg_buy_fill_notification(default_conf, mocker, message_type, entered,
enter_signal, leverage) -> None:
default_conf['telegram']['notification_settings']['buy_fill'] = 'on' default_conf['telegram']['notification_settings']['buy_fill'] = 'on'
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
telegram.send_msg({ telegram.send_msg({
'type': RPCMessageType.BUY_FILL, 'type': message_type,
'trade_id': 1, 'trade_id': 1,
'enter_tag': 'buy_signal_01', 'enter_tag': enter_signal,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'leverage': leverage,
'stake_amount': 0.001, 'stake_amount': 0.001,
# 'stake_amount_fiat': 0.0, # 'stake_amount_fiat': 0.0,
'stake_currency': 'BTC', 'stake_currency': 'BTC',
@ -1752,13 +1778,15 @@ def test_send_msg_buy_fill_notification(default_conf, mocker) -> None:
'amount': 1333.3333333333335, 'amount': 1333.3333333333335,
'open_date': arrow.utcnow().shift(hours=-1) 'open_date': arrow.utcnow().shift(hours=-1)
}) })
message = [f'\N{CHECK MARK} *Binance:* {entered} ETH/BTC (#1)\n',
f'*Enter Tag:* `{enter_signal}`\n',
f'*Leverage:* `{leverage}`\n' if leverage else '',
'*Amount:* `1333.33333333`\n',
'*Open Rate:* `0.00001099`\n',
'*Total:* `(0.00100000 BTC, 12.345 USD)`']
# raise ValueError(msg_mock.call_args[0][0])
assert msg_mock.call_args[0][0] \ assert msg_mock.call_args[0][0] \
== '\N{CHECK MARK} *Binance:* Bought ETH/BTC (#1)\n' \ == "".join(message)
'*Enter Tag:* `buy_signal_01`\n' \
'*Amount:* `1333.33333333`\n' \
'*Open Rate:* `0.00001099`\n' \
'*Total:* `(0.00100000 BTC, 12.345 USD)`'
def test_send_msg_sell_notification(default_conf, mocker) -> None: def test_send_msg_sell_notification(default_conf, mocker) -> None:
@ -1772,6 +1800,8 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
'trade_id': 1, 'trade_id': 1,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'KEY/ETH', 'pair': 'KEY/ETH',
'leverage': 1.0,
'direction': 'Long',
'gain': 'loss', 'gain': 'loss',
'limit': 3.201e-05, 'limit': 3.201e-05,
'amount': 1333.3333333333335, 'amount': 1333.3333333333335,
@ -1788,11 +1818,13 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
'close_date': arrow.utcnow(), 'close_date': arrow.utcnow(),
}) })
assert msg_mock.call_args[0][0] \ assert msg_mock.call_args[0][0] \
== ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n' == ('\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n'
'*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n' '*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n'
'*Enter Tag:* `buy_signal1`\n' '*Enter Tag:* `buy_signal1`\n'
'*Sell Reason:* `stop_loss`\n' '*Exit Reason:* `stop_loss`\n'
'*Duration:* `1:00:00 (60.0 min)`\n' '*Duration:* `1:00:00 (60.0 min)`\n'
'*Direction:* `Long`\n'
'*Leverage:* `1.0`\n'
'*Amount:* `1333.33333333`\n' '*Amount:* `1333.33333333`\n'
'*Open Rate:* `0.00007500`\n' '*Open Rate:* `0.00007500`\n'
'*Current Rate:* `0.00003201`\n' '*Current Rate:* `0.00003201`\n'
@ -1805,6 +1837,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
'trade_id': 1, 'trade_id': 1,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'KEY/ETH', 'pair': 'KEY/ETH',
'direction': 'Long',
'gain': 'loss', 'gain': 'loss',
'limit': 3.201e-05, 'limit': 3.201e-05,
'amount': 1333.3333333333335, 'amount': 1333.3333333333335,
@ -1820,11 +1853,12 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
'close_date': arrow.utcnow(), 'close_date': arrow.utcnow(),
}) })
assert msg_mock.call_args[0][0] \ assert msg_mock.call_args[0][0] \
== ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n' == ('\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n'
'*Unrealized Profit:* `-57.41%`\n' '*Unrealized Profit:* `-57.41%`\n'
'*Enter Tag:* `buy_signal1`\n' '*Enter Tag:* `buy_signal1`\n'
'*Sell Reason:* `stop_loss`\n' '*Exit Reason:* `stop_loss`\n'
'*Duration:* `1 day, 2:30:00 (1590.0 min)`\n' '*Duration:* `1 day, 2:30:00 (1590.0 min)`\n'
'*Direction:* `Long`\n'
'*Amount:* `1333.33333333`\n' '*Amount:* `1333.33333333`\n'
'*Open Rate:* `0.00007500`\n' '*Open Rate:* `0.00007500`\n'
'*Current Rate:* `0.00003201`\n' '*Current Rate:* `0.00003201`\n'
@ -1848,7 +1882,7 @@ def test_send_msg_sell_cancel_notification(default_conf, mocker) -> None:
'reason': 'Cancelled on exchange' 'reason': 'Cancelled on exchange'
}) })
assert msg_mock.call_args[0][0] \ assert msg_mock.call_args[0][0] \
== ('\N{WARNING SIGN} *Binance:* Cancelling open sell Order for KEY/ETH (#1).' == ('\N{WARNING SIGN} *Binance:* Cancelling exit Order for KEY/ETH (#1).'
' Reason: Cancelled on exchange.') ' Reason: Cancelled on exchange.')
msg_mock.reset_mock() msg_mock.reset_mock()
@ -1860,13 +1894,18 @@ def test_send_msg_sell_cancel_notification(default_conf, mocker) -> None:
'reason': 'timeout' 'reason': 'timeout'
}) })
assert msg_mock.call_args[0][0] \ assert msg_mock.call_args[0][0] \
== ('\N{WARNING SIGN} *Binance:* Cancelling open sell Order for KEY/ETH (#1).' == ('\N{WARNING SIGN} *Binance:* Cancelling exit Order for KEY/ETH (#1).'
' Reason: timeout.') ' Reason: timeout.')
# Reset singleton function to avoid random breaks # Reset singleton function to avoid random breaks
telegram._rpc._fiat_converter.convert_amount = old_convamount telegram._rpc._fiat_converter.convert_amount = old_convamount
def test_send_msg_sell_fill_notification(default_conf, mocker) -> None: @pytest.mark.parametrize(
'direction,enter_signal,leverage',
[('Long', 'long_signal_01', None),
('Short', 'short_signal_01', 2.0)])
def test_send_msg_sell_fill_notification(default_conf, mocker, direction,
enter_signal, leverage) -> None:
default_conf['telegram']['notification_settings']['sell_fill'] = 'on' default_conf['telegram']['notification_settings']['sell_fill'] = 'on'
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
@ -1876,6 +1915,8 @@ def test_send_msg_sell_fill_notification(default_conf, mocker) -> None:
'trade_id': 1, 'trade_id': 1,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'KEY/ETH', 'pair': 'KEY/ETH',
'leverage': leverage,
'direction': direction,
'gain': 'loss', 'gain': 'loss',
'limit': 3.201e-05, 'limit': 3.201e-05,
'amount': 1333.3333333333335, 'amount': 1333.3333333333335,
@ -1885,21 +1926,23 @@ def test_send_msg_sell_fill_notification(default_conf, mocker) -> None:
'profit_amount': -0.05746268, 'profit_amount': -0.05746268,
'profit_ratio': -0.57405275, 'profit_ratio': -0.57405275,
'stake_currency': 'ETH', 'stake_currency': 'ETH',
'enter_tag': 'buy_signal1', 'enter_tag': enter_signal,
'sell_reason': SellType.STOP_LOSS.value, 'sell_reason': SellType.STOP_LOSS.value,
'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30), 'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30),
'close_date': arrow.utcnow(), 'close_date': arrow.utcnow(),
}) })
message = ['\N{WARNING SIGN} *Binance:* Exited KEY/ETH (#1)\n',
'*Profit:* `-57.41%`\n',
f'*Enter Tag:* `{enter_signal}`\n',
'*Exit Reason:* `stop_loss`\n',
'*Duration:* `1 day, 2:30:00 (1590.0 min)`\n',
f"*Direction:* `{direction}`\n",
f"*Leverage:* `{leverage:.1f}`\n" if leverage is not None else "",
'*Amount:* `1333.33333333`\n',
'*Open Rate:* `0.00007500`\n',
'*Close Rate:* `0.00003201`']
assert msg_mock.call_args[0][0] \ assert msg_mock.call_args[0][0] \
== ('\N{WARNING SIGN} *Binance:* Sold KEY/ETH (#1)\n' == "".join(message)
'*Profit:* `-57.41%`\n'
'*Enter Tag:* `buy_signal1`\n'
'*Sell Reason:* `stop_loss`\n'
'*Duration:* `1 day, 2:30:00 (1590.0 min)`\n'
'*Amount:* `1333.33333333`\n'
'*Open Rate:* `0.00007500`\n'
'*Close Rate:* `0.00003201`'
)
def test_send_msg_status_notification(default_conf, mocker) -> None: def test_send_msg_status_notification(default_conf, mocker) -> None:
@ -1938,16 +1981,22 @@ def test_send_msg_unknown_type(default_conf, mocker) -> None:
}) })
def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None: @pytest.mark.parametrize(
'message_type,enter,enter_signal,leverage',
[(RPCMessageType.BUY, 'Long', 'long_signal_01', None),
(RPCMessageType.SHORT, 'Short', 'short_signal_01', 2.0)])
def test_send_msg_buy_notification_no_fiat(default_conf, mocker, message_type,
enter, enter_signal, leverage) -> None:
del default_conf['fiat_display_currency'] del default_conf['fiat_display_currency']
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
telegram.send_msg({ telegram.send_msg({
'type': RPCMessageType.BUY, 'type': message_type,
'enter_tag': 'buy_signal_01', 'enter_tag': enter_signal,
'trade_id': 1, 'trade_id': 1,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'leverage': leverage,
'limit': 1.099e-05, 'limit': 1.099e-05,
'order_type': 'limit', 'order_type': 'limit',
'stake_amount': 0.001, 'stake_amount': 0.001,
@ -1958,15 +2007,23 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None:
'amount': 1333.3333333333335, 'amount': 1333.3333333333335,
'open_date': arrow.utcnow().shift(hours=-1) 'open_date': arrow.utcnow().shift(hours=-1)
}) })
assert msg_mock.call_args[0][0] == ('\N{LARGE BLUE CIRCLE} *Binance:* Buying ETH/BTC (#1)\n'
'*Enter Tag:* `buy_signal_01`\n' message = [f'\N{LARGE BLUE CIRCLE} *Binance:* {enter} ETH/BTC (#1)\n',
'*Amount:* `1333.33333333`\n' f'*Enter Tag:* `{enter_signal}`\n',
'*Open Rate:* `0.00001099`\n' f'*Leverage:* `{leverage}`\n' if leverage else '',
'*Current Rate:* `0.00001099`\n' '*Amount:* `1333.33333333`\n',
'*Total:* `(0.00100000 BTC)`') '*Open Rate:* `0.00001099`\n',
'*Current Rate:* `0.00001099`\n',
'*Total:* `(0.00100000 BTC)`']
assert msg_mock.call_args[0][0] == "".join(message)
def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None: @pytest.mark.parametrize(
'direction,enter_signal,leverage',
[('Long', 'long_signal_01', None),
('Short', 'short_signal_01', 2.0)])
def test_send_msg_sell_notification_no_fiat(default_conf, mocker, direction,
enter_signal, leverage) -> None:
del default_conf['fiat_display_currency'] del default_conf['fiat_display_currency']
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
@ -1976,6 +2033,8 @@ def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None:
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'KEY/ETH', 'pair': 'KEY/ETH',
'gain': 'loss', 'gain': 'loss',
'leverage': leverage,
'direction': direction,
'limit': 3.201e-05, 'limit': 3.201e-05,
'amount': 1333.3333333333335, 'amount': 1333.3333333333335,
'order_type': 'limit', 'order_type': 'limit',
@ -1985,21 +2044,25 @@ def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None:
'profit_ratio': -0.57405275, 'profit_ratio': -0.57405275,
'stake_currency': 'ETH', 'stake_currency': 'ETH',
'fiat_currency': 'USD', 'fiat_currency': 'USD',
'enter_tag': 'buy_signal1', 'enter_tag': enter_signal,
'sell_reason': SellType.STOP_LOSS.value, 'sell_reason': SellType.STOP_LOSS.value,
'open_date': arrow.utcnow().shift(hours=-2, minutes=-35, seconds=-3), 'open_date': arrow.utcnow().shift(hours=-2, minutes=-35, seconds=-3),
'close_date': arrow.utcnow(), 'close_date': arrow.utcnow(),
}) })
assert msg_mock.call_args[0][0] == ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n'
'*Unrealized Profit:* `-57.41%`\n' message = ['\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n',
'*Enter Tag:* `buy_signal1`\n' '*Unrealized Profit:* `-57.41%`\n',
'*Sell Reason:* `stop_loss`\n' f'*Enter Tag:* `{enter_signal}`\n',
'*Duration:* `2:35:03 (155.1 min)`\n' '*Exit Reason:* `stop_loss`\n',
'*Amount:* `1333.33333333`\n' '*Duration:* `2:35:03 (155.1 min)`\n',
'*Open Rate:* `0.00007500`\n' f'*Direction:* `{direction}`\n',
'*Current Rate:* `0.00003201`\n' f'*Leverage:* `{leverage}`\n' if leverage else '',
'*Close Rate:* `0.00003201`' '*Amount:* `1333.33333333`\n',
) '*Open Rate:* `0.00007500`\n',
'*Current Rate:* `0.00003201`\n',
'*Close Rate:* `0.00003201`',
]
assert msg_mock.call_args[0][0] == "".join(message)
@pytest.mark.parametrize('msg,expected', [ @pytest.mark.parametrize('msg,expected', [

View File

@ -18,17 +18,23 @@ def get_webhook_dict() -> dict:
"webhookbuy": { "webhookbuy": {
"value1": "Buying {pair}", "value1": "Buying {pair}",
"value2": "limit {limit:8f}", "value2": "limit {limit:8f}",
"value3": "{stake_amount:8f} {stake_currency}" "value3": "{stake_amount:8f} {stake_currency}",
"value4": "leverage {leverage:.1f}",
"value5": "direction {direction}"
}, },
"webhookbuycancel": { "webhookbuycancel": {
"value1": "Cancelling Open Buy Order for {pair}", "value1": "Cancelling Open Buy Order for {pair}",
"value2": "limit {limit:8f}", "value2": "limit {limit:8f}",
"value3": "{stake_amount:8f} {stake_currency}" "value3": "{stake_amount:8f} {stake_currency}",
"value4": "leverage {leverage:.1f}",
"value5": "direction {direction}"
}, },
"webhookbuyfill": { "webhookbuyfill": {
"value1": "Buy Order for {pair} filled", "value1": "Buy Order for {pair} filled",
"value2": "at {open_rate:8f}", "value2": "at {open_rate:8f}",
"value3": "{stake_amount:8f} {stake_currency}" "value3": "{stake_amount:8f} {stake_currency}",
"value4": "leverage {leverage:.1f}",
"value5": "direction {direction}"
}, },
"webhooksell": { "webhooksell": {
"value1": "Selling {pair}", "value1": "Selling {pair}",
@ -71,6 +77,8 @@ def test_send_msg_webhook(default_conf, mocker):
'type': RPCMessageType.BUY, 'type': RPCMessageType.BUY,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'leverage': 1.0,
'direction': 'Long',
'limit': 0.005, 'limit': 0.005,
'stake_amount': 0.8, 'stake_amount': 0.8,
'stake_amount_fiat': 500, 'stake_amount_fiat': 500,
@ -85,6 +93,37 @@ def test_send_msg_webhook(default_conf, mocker):
default_conf["webhook"]["webhookbuy"]["value2"].format(**msg)) default_conf["webhook"]["webhookbuy"]["value2"].format(**msg))
assert (msg_mock.call_args[0][0]["value3"] == assert (msg_mock.call_args[0][0]["value3"] ==
default_conf["webhook"]["webhookbuy"]["value3"].format(**msg)) default_conf["webhook"]["webhookbuy"]["value3"].format(**msg))
assert (msg_mock.call_args[0][0]["value4"] ==
default_conf["webhook"]["webhookbuy"]["value4"].format(**msg))
assert (msg_mock.call_args[0][0]["value5"] ==
default_conf["webhook"]["webhookbuy"]["value5"].format(**msg))
# Test short
msg_mock.reset_mock()
msg = {
'type': RPCMessageType.SHORT,
'exchange': 'Binance',
'pair': 'ETH/BTC',
'leverage': 2.0,
'direction': 'Short',
'limit': 0.005,
'stake_amount': 0.8,
'stake_amount_fiat': 500,
'stake_currency': 'BTC',
'fiat_currency': 'EUR'
}
webhook.send_msg(msg=msg)
assert msg_mock.call_count == 1
assert (msg_mock.call_args[0][0]["value1"] ==
default_conf["webhook"]["webhookbuy"]["value1"].format(**msg))
assert (msg_mock.call_args[0][0]["value2"] ==
default_conf["webhook"]["webhookbuy"]["value2"].format(**msg))
assert (msg_mock.call_args[0][0]["value3"] ==
default_conf["webhook"]["webhookbuy"]["value3"].format(**msg))
assert (msg_mock.call_args[0][0]["value4"] ==
default_conf["webhook"]["webhookbuy"]["value4"].format(**msg))
assert (msg_mock.call_args[0][0]["value5"] ==
default_conf["webhook"]["webhookbuy"]["value5"].format(**msg))
# Test buy cancel # Test buy cancel
msg_mock.reset_mock() msg_mock.reset_mock()
@ -92,6 +131,8 @@ def test_send_msg_webhook(default_conf, mocker):
'type': RPCMessageType.BUY_CANCEL, 'type': RPCMessageType.BUY_CANCEL,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'leverage': 1.0,
'direction': 'Long',
'limit': 0.005, 'limit': 0.005,
'stake_amount': 0.8, 'stake_amount': 0.8,
'stake_amount_fiat': 500, 'stake_amount_fiat': 500,
@ -106,6 +147,33 @@ def test_send_msg_webhook(default_conf, mocker):
default_conf["webhook"]["webhookbuycancel"]["value2"].format(**msg)) default_conf["webhook"]["webhookbuycancel"]["value2"].format(**msg))
assert (msg_mock.call_args[0][0]["value3"] == assert (msg_mock.call_args[0][0]["value3"] ==
default_conf["webhook"]["webhookbuycancel"]["value3"].format(**msg)) default_conf["webhook"]["webhookbuycancel"]["value3"].format(**msg))
# Test short cancel
msg_mock.reset_mock()
msg = {
'type': RPCMessageType.SHORT_CANCEL,
'exchange': 'Binance',
'pair': 'ETH/BTC',
'leverage': 2.0,
'direction': 'Short',
'limit': 0.005,
'stake_amount': 0.8,
'stake_amount_fiat': 500,
'stake_currency': 'BTC',
'fiat_currency': 'EUR'
}
webhook.send_msg(msg=msg)
assert msg_mock.call_count == 1
assert (msg_mock.call_args[0][0]["value1"] ==
default_conf["webhook"]["webhookbuycancel"]["value1"].format(**msg))
assert (msg_mock.call_args[0][0]["value2"] ==
default_conf["webhook"]["webhookbuycancel"]["value2"].format(**msg))
assert (msg_mock.call_args[0][0]["value3"] ==
default_conf["webhook"]["webhookbuycancel"]["value3"].format(**msg))
assert (msg_mock.call_args[0][0]["value4"] ==
default_conf["webhook"]["webhookbuycancel"]["value4"].format(**msg))
assert (msg_mock.call_args[0][0]["value5"] ==
default_conf["webhook"]["webhookbuycancel"]["value5"].format(**msg))
# Test buy fill # Test buy fill
msg_mock.reset_mock() msg_mock.reset_mock()
@ -113,6 +181,8 @@ def test_send_msg_webhook(default_conf, mocker):
'type': RPCMessageType.BUY_FILL, 'type': RPCMessageType.BUY_FILL,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'leverage': 1.0,
'direction': 'Long',
'open_rate': 0.005, 'open_rate': 0.005,
'stake_amount': 0.8, 'stake_amount': 0.8,
'stake_amount_fiat': 500, 'stake_amount_fiat': 500,
@ -127,8 +197,40 @@ def test_send_msg_webhook(default_conf, mocker):
default_conf["webhook"]["webhookbuyfill"]["value2"].format(**msg)) default_conf["webhook"]["webhookbuyfill"]["value2"].format(**msg))
assert (msg_mock.call_args[0][0]["value3"] == assert (msg_mock.call_args[0][0]["value3"] ==
default_conf["webhook"]["webhookbuyfill"]["value3"].format(**msg)) default_conf["webhook"]["webhookbuyfill"]["value3"].format(**msg))
assert (msg_mock.call_args[0][0]["value4"] ==
default_conf["webhook"]["webhookbuycancel"]["value4"].format(**msg))
assert (msg_mock.call_args[0][0]["value5"] ==
default_conf["webhook"]["webhookbuycancel"]["value5"].format(**msg))
# Test short fill
msg_mock.reset_mock()
msg = {
'type': RPCMessageType.SHORT_FILL,
'exchange': 'Binance',
'pair': 'ETH/BTC',
'leverage': 2.0,
'direction': 'Short',
'open_rate': 0.005,
'stake_amount': 0.8,
'stake_amount_fiat': 500,
'stake_currency': 'BTC',
'fiat_currency': 'EUR'
}
webhook.send_msg(msg=msg)
assert msg_mock.call_count == 1
assert (msg_mock.call_args[0][0]["value1"] ==
default_conf["webhook"]["webhookbuyfill"]["value1"].format(**msg))
assert (msg_mock.call_args[0][0]["value2"] ==
default_conf["webhook"]["webhookbuyfill"]["value2"].format(**msg))
assert (msg_mock.call_args[0][0]["value3"] ==
default_conf["webhook"]["webhookbuyfill"]["value3"].format(**msg))
assert (msg_mock.call_args[0][0]["value4"] ==
default_conf["webhook"]["webhookbuycancel"]["value4"].format(**msg))
assert (msg_mock.call_args[0][0]["value5"] ==
default_conf["webhook"]["webhookbuycancel"]["value5"].format(**msg))
# Test sell # Test sell
msg_mock.reset_mock() msg_mock.reset_mock()
msg = { msg = {
'type': RPCMessageType.SELL, 'type': RPCMessageType.SELL,
'exchange': 'Binance', 'exchange': 'Binance',

View File

@ -2873,6 +2873,8 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_
'amount': amt, 'amount': amt,
'order_type': 'limit', 'order_type': 'limit',
'buy_tag': None, 'buy_tag': None,
'direction': 'Short' if trade.is_short else 'Long',
'leverage': 1.0,
'enter_tag': None, 'enter_tag': None,
'open_rate': open_rate, 'open_rate': open_rate,
'current_rate': 2.01 if is_short else 2.3, 'current_rate': 2.01 if is_short else 2.3,
@ -2925,6 +2927,8 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd
'trade_id': 1, 'trade_id': 1,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/USDT', 'pair': 'ETH/USDT',
'direction': 'Short' if trade.is_short else 'Long',
'leverage': 1.0,
'gain': 'loss', 'gain': 'loss',
'limit': 2.2 if is_short else 2.01, 'limit': 2.2 if is_short else 2.01,
'amount': 29.70297029 if is_short else 30.0, 'amount': 29.70297029 if is_short else 30.0,
@ -2945,13 +2949,15 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd
@pytest.mark.parametrize( @pytest.mark.parametrize(
"is_short,amount,open_rate,current_rate,limit,profit_amount,profit_ratio,profit_or_loss", [ "is_short,amount,open_rate,current_rate,limit,profit_amount,"
"profit_ratio,profit_or_loss", [
(False, 30, 2.0, 2.3, 2.25, 7.18125, 0.11938903, 'profit'), (False, 30, 2.0, 2.3, 2.25, 7.18125, 0.11938903, 'profit'),
(True, 29.70297029, 2.02, 2.2, 2.25, -7.14876237, -0.11944465, 'loss'), # TODO-lev (True, 29.70297029, 2.02, 2.2, 2.25, -7.14876237, -0.11944465, 'loss'), # TODO-lev
]) ])
def test_execute_trade_exit_custom_exit_price( def test_execute_trade_exit_custom_exit_price(
default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, is_short, amount, open_rate, default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, is_short, amount, open_rate,
current_rate, limit, profit_amount, profit_ratio, profit_or_loss, mocker) -> None: current_rate, limit, profit_amount, profit_ratio, profit_or_loss, mocker
) -> None:
rpc_mock = patch_RPCManager(mocker) rpc_mock = patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
mocker.patch.multiple( mocker.patch.multiple(
@ -3003,6 +3009,8 @@ def test_execute_trade_exit_custom_exit_price(
'type': RPCMessageType.SELL, 'type': RPCMessageType.SELL,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/USDT', 'pair': 'ETH/USDT',
'direction': 'Short' if trade.is_short else 'Long',
'leverage': 1.0,
'gain': profit_or_loss, 'gain': profit_or_loss,
'limit': limit, 'limit': limit,
'amount': amount, 'amount': amount,
@ -3068,6 +3076,8 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(
'trade_id': 1, 'trade_id': 1,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/USDT', 'pair': 'ETH/USDT',
'direction': 'Short' if trade.is_short else 'Long',
'leverage': 1.0,
'gain': 'loss', 'gain': 'loss',
'limit': 2.02 if is_short else 1.98, 'limit': 2.02 if is_short else 1.98,
'amount': 29.70297029 if is_short else 30.0, 'amount': 29.70297029 if is_short else 30.0,
@ -3180,7 +3190,7 @@ def test_execute_trade_exit_with_stoploss_on_exchange(
assert cancel_order.call_count == 1 assert cancel_order.call_count == 1
assert rpc_mock.call_count == 3 assert rpc_mock.call_count == 3
# TODO-lev: add short, RPC short, short fill
def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(default_conf_usdt, ticker_usdt, fee, def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(default_conf_usdt, ticker_usdt, fee,
mocker) -> None: mocker) -> None:
default_conf_usdt['exchange']['name'] = 'binance' default_conf_usdt['exchange']['name'] = 'binance'
@ -3322,6 +3332,8 @@ def test_execute_trade_exit_market_order(
'trade_id': 1, 'trade_id': 1,
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/USDT', 'pair': 'ETH/USDT',
'direction': 'Short' if trade.is_short else 'Long',
'leverage': 1.0,
'gain': profit_or_loss, 'gain': profit_or_loss,
'limit': limit, 'limit': limit,
'amount': round(amount, 9), 'amount': round(amount, 9),