diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index a6ec70636..cd75a7229 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -116,8 +116,8 @@ class Exchange(object): api.urls['api'] = api.urls['test'] logger.info("Enabled Sandbox API on %s", name) else: - logger.warning(self, "No Sandbox URL in CCXT, exiting. " - "Please check your config.json") + logger.warning(self._api.name, "No Sandbox URL in CCXT, exiting. " + "Please check your config.json") raise OperationalException(f'Exchange {name} does not provide a sandbox api') def validate_pairs(self, pairs: List[str]) -> None: diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 706435017..a2090d267 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -94,6 +94,8 @@ class FreqtradeBot(object): 'status': f'{state.name.lower()}' }) logger.info('Changing state to: %s', state.name) + if state == State.RUNNING: + self._startup_messages() if state == State.STOPPED: time.sleep(1) @@ -110,6 +112,38 @@ class FreqtradeBot(object): nb_assets=nb_assets) return state + def _startup_messages(self) -> None: + if self.config.get('dry_run', False): + self.rpc.send_msg({ + 'type': RPCMessageType.WARNING_NOTIFICATION, + 'status': 'Dry run is enabled. All trades are simulated.' + }) + stake_currency = self.config['stake_currency'] + stake_amount = self.config['stake_amount'] + minimal_roi = self.config['minimal_roi'] + ticker_interval = self.config['ticker_interval'] + exchange_name = self.config['exchange']['name'] + strategy_name = self.config.get('strategy', '') + self.rpc.send_msg({ + 'type': RPCMessageType.CUSTOM_NOTIFICATION, + 'status': f'*Exchange:* `{exchange_name}`\n' + f'*Stake per trade:* `{stake_amount} {stake_currency}`\n' + f'*Minimum ROI:* `{minimal_roi}`\n' + f'*Ticker Interval:* `{ticker_interval}`\n' + f'*Strategy:* `{strategy_name}`' + }) + if self.config.get('dynamic_whitelist', False): + top_pairs = 'top ' + str(self.config.get('dynamic_whitelist', 20)) + specific_pairs = '' + else: + top_pairs = 'whitelisted' + specific_pairs = '\n' + ', '.join(self.config['exchange'].get('pair_whitelist', '')) + self.rpc.send_msg({ + 'type': RPCMessageType.STATUS_NOTIFICATION, + 'status': f'Searching for {top_pairs} {stake_currency} pairs to buy and sell...' + f'{specific_pairs}' + }) + def _throttle(self, func: Callable[..., Any], min_secs: float, *args, **kwargs) -> Any: """ Throttles the given callable that it diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index f58fbae9a..80bac0dd4 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -24,6 +24,8 @@ logger = logging.getLogger(__name__) class RPCMessageType(Enum): STATUS_NOTIFICATION = 'status' + WARNING_NOTIFICATION = 'warning' + CUSTOM_NOTIFICATION = 'custom' BUY_NOTIFICATION = 'buy' SELL_NOTIFICATION = 'sell' diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 3b5ce3f74..64708ef74 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -154,6 +154,12 @@ class Telegram(RPC): elif msg['type'] == RPCMessageType.STATUS_NOTIFICATION: message = '*Status:* `{status}`'.format(**msg) + elif msg['type'] == RPCMessageType.WARNING_NOTIFICATION: + message = '*Warning:* `{status}`'.format(**msg) + + elif msg['type'] == RPCMessageType.CUSTOM_NOTIFICATION: + message = '{status}'.format(**msg) + else: raise NotImplementedError('Unknown message type: {}'.format(msg['type'])) diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index 4b2fe4cf5..4d2b9cda2 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -1095,6 +1095,38 @@ def test_send_msg_status_notification(default_conf, mocker) -> None: assert msg_mock.call_args[0][0] == '*Status:* `running`' +def test_warning_notification(default_conf, mocker) -> None: + msg_mock = MagicMock() + mocker.patch.multiple( + 'freqtrade.rpc.telegram.Telegram', + _init=MagicMock(), + _send_msg=msg_mock + ) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) + telegram = Telegram(freqtradebot) + telegram.send_msg({ + 'type': RPCMessageType.WARNING_NOTIFICATION, + 'status': 'message' + }) + assert msg_mock.call_args[0][0] == '*Warning:* `message`' + + +def test_custom_notification(default_conf, mocker) -> None: + msg_mock = MagicMock() + mocker.patch.multiple( + 'freqtrade.rpc.telegram.Telegram', + _init=MagicMock(), + _send_msg=msg_mock + ) + freqtradebot = get_patched_freqtradebot(mocker, default_conf) + telegram = Telegram(freqtradebot) + telegram.send_msg({ + 'type': RPCMessageType.CUSTOM_NOTIFICATION, + 'status': '*Custom:* `Hello World`' + }) + assert msg_mock.call_args[0][0] == '*Custom:* `Hello World`' + + def test_send_msg_unknown_type(default_conf, mocker) -> None: msg_mock = MagicMock() mocker.patch.multiple( diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 89adae6ab..fa6bc7c2a 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1876,3 +1876,9 @@ def test_get_real_amount_open_trade(default_conf, mocker): freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) assert freqtrade.get_real_amount(trade, order) == amount + + +def test_startup_messages(default_conf, mocker): + default_conf['dynamic_whitelist'] = 20 + freqtrade = get_patched_freqtradebot(mocker, default_conf) + assert freqtrade.state is State.RUNNING