Repository: freqtrade/freqtrade

Issue:
dry_run + webhook results in Fatal exception
## Step 1: Have you search for this issue before posting it?
yes

## Step 2: Describe your environment

  * Operating system: ubuntu 16.04
  * Python Version: Python 3.6.8
  * CCXT version: ccxt==1.18.1043
  * Branch: freqtrade 2019.7-dev
  * Last Commit ID: 161db08745baad1155b790ad4b1380dc2c53e556
  
## Step 3: Describe the problem:

* running webhook w dry-run:true dies upon start with fatal exception.


### Steps to reproduce:
webhook.enabled: true (no error when false)
dry-run: true (false went untested)
telegram.enabled: (confirmed error present with true|false)
  
### Observed Results:
* What happened?
fatal exception.

* What did you expect to happen?
Endless account growth of intangible virtual currency.  Simulated of course. 



### Steps to resolve(ish)
rpc/webhook.py:~39
```
""" Send a message to telegram channel """
try:
if msg['type'] == RPCMessageType.BUY_NOTIFICATION:
	valuedict = self._config['webhook'].get('webhookbuy', None)
elif msg['type'] == RPCMessageType.SELL_NOTIFICATION:
	valuedict = self._config['webhook'].get('webhooksell', None)
elif msg['type'] == RPCMessageType.STATUS_NOTIFICATION:
	valuedict = self._config['webhook'].get('webhookstatus', None)
++ elif msg['type'] == RPCMessageType.WARNING_NOTIFICATION:
++   valuedict = self._config['webhook'].get('webhookstatus', None)
++ elif msg['type'] == RPCMessageType.CUSTOM_NOTIFICATION:
++   valuedict = self._config['webhook'].get('webhookstatus', None)
else:
	raise NotImplementedError('Unknown message type: {}'.format(msg['type']))
if not valuedict:
	logger.info("Message type %s not configured for webhooks", msg['type'])
	return
```



### Relevant code exceptions or logs:
```
source .env/bin/activate; freqtrade -c config-Strategy110.json -s Strategy110 --strategy-path /home/freqtrade/freqtrade/user_data/strategies/
2019-08-29 22:59:24,229 - freqtrade.loggers - INFO - Verbosity set to 0
2019-08-29 22:59:24,230 - freqtrade.configuration.configuration - INFO - Using additional Strategy lookup path: /home/freqtrade/freqtrade/user_data/strategies/
2019-08-29 22:59:24,230 - freqtrade.configuration.configuration - INFO - Dry run is enabled
2019-08-29 22:59:24,230 - freqtrade.configuration.configuration - INFO - Using DB: "sqlite:///strategy1110.dryrun.sqlite"
2019-08-29 22:59:24,230 - freqtrade.configuration.configuration - WARNING - `forcebuy` RPC message enabled.
2019-08-29 22:59:24,230 - freqtrade.configuration.check_exchange - INFO - Checking exchange...
2019-08-29 22:59:24,230 - freqtrade.configuration.check_exchange - INFO - Exchange "binance" is officially supported by the Freqtrade development team.
2019-08-29 22:59:24,230 - freqtrade.configuration.configuration - INFO - Using max_open_trades: 2 ...
2019-08-29 22:59:24,230 - freqtrade.configuration.configuration - INFO - Using data directory: user_data/data/binance ...
2019-08-29 22:59:24,230 - freqtrade.configuration.configuration - INFO - Runmode set to RunMode.DRY_RUN.
2019-08-29 22:59:24,230 - freqtrade.freqtradebot - INFO - Starting freqtrade 2019.7-dev
2019-08-29 22:59:24,234 - freqtrade.resolvers.iresolver - INFO - Using resolved strategy Strategy110 from '/home/freqtrade/freqtrade/user_data/strategies/Strategy110.py'...
-- (redaction) --
2019-08-29 22:59:24,236 - freqtrade.rpc.rpc_manager - INFO - Enabling rpc.telegram ...
2019-08-29 22:59:24,273 - freqtrade.rpc.telegram - INFO - rpc.telegram is listening for following commands: [['status'], ['profit'], ['balance'], ['start'], ['stop'], ['forcesell'], ['forcebuy'], ['performance'], ['daily'], ['count'], ['reload_conf'], ['stopbuy'], ['whitelist'], ['blacklist'], ['edge'], ['help'], ['version']]
2019-08-29 22:59:24,284 - freqtrade.rpc.rpc_manager - INFO - Enabling rpc.webhook ...
2019-08-29 22:59:24,285 - freqtrade.rpc.rpc_manager - INFO - Enabling rpc.api_server
2019-08-29 22:59:24,325 - freqtrade.rpc.api_server - INFO - Starting HTTP Server at 127.0.0.1:10110
2019-08-29 22:59:24,325 - freqtrade.exchange.exchange - INFO - Instance is running with dry_run enabled
2019-08-29 22:59:24,326 - freqtrade.rpc.api_server - INFO - Starting Local Rest Server.
2019-08-29 22:59:24,326 - freqtrade.exchange.exchange - INFO - Applying additional ccxt config: {'enableRateLimit': True}
2019-08-29 22:59:24,327 - freqtrade.exchange.exchange - INFO - Applying additional ccxt config: {'enableRateLimit': True, 'rateLimit': 200}
2019-08-29 22:59:24,334 - freqtrade.exchange.exchange - INFO - Using Exchange "Binance"
2019-08-29 22:59:24,738 - freqtrade.resolvers.exchange_resolver - INFO - Using resolved exchange 'Binance'...
2019-08-29 22:59:24,738 - freqtrade.wallets - INFO - Wallets synced.
2019-08-29 22:59:24,738 - freqtrade.resolvers.iresolver - WARNING - Path "/home/freqtrade/freqtrade/user_data/pairlist" does not exist.
2019-08-29 22:59:24,739 - freqtrade.resolvers.iresolver - INFO - Using resolved pairlist StaticPairList from '/home/freqtrade/freqtrade/freqtrade/pairlist/StaticPairList.py'...
2019-08-29 22:59:24,748 - freqtrade.rpc.rpc_manager - INFO - Sending rpc message: {'type': status, 'status': 'running'}
2019-08-29 22:59:25,442 - freqtrade.worker - INFO - Changing state to: RUNNING
2019-08-29 22:59:25,442 - freqtrade.rpc.rpc_manager - INFO - Sending rpc message: {'type': warning, 'status': 'Dry run is enabled. All trades are simulated.'}
2019-08-29 22:59:25,699 - freqtrade - ERROR - Fatal exception!
Traceback (most recent call last):
  File "/home/freqtrade/freqtrade/freqtrade/main.py", line 49, in main
    worker.run()
  File "/home/freqtrade/freqtrade/freqtrade/worker.py", line 72, in run
    state = self._worker(old_state=state)
  File "/home/freqtrade/freqtrade/freqtrade/worker.py", line 94, in _worker
    self.freqtrade.startup()
  File "/home/freqtrade/freqtrade/freqtrade/freqtradebot.py", line 103, in startup
    self.rpc.startup_messages(self.config, self.pairlists)
  File "/home/freqtrade/freqtrade/freqtrade/rpc/rpc_manager.py", line 65, in startup_messages
    'status': 'Dry run is enabled. All trades are simulated.'
  File "/home/freqtrade/freqtrade/freqtrade/rpc/rpc_manager.py", line 59, in send_msg
    mod.send_msg(msg)
  File "/home/freqtrade/freqtrade/freqtrade/rpc/webhook.py", line 49, in send_msg
    raise NotImplementedError('Unknown message type: {}'.format(msg['type']))
NotImplementedError: Unknown message type: RPCMessageType.WARNING_NOTIFICATION
2019-08-29 22:59:25,699 - freqtrade.rpc.rpc_manager - INFO - Sending rpc message: {'type': status, 'status': 'process died'}
2019-08-29 22:59:26,006 - freqtrade.freqtradebot - INFO - Cleaning up modules ...
2019-08-29 22:59:26,006 - freqtrade.rpc.rpc_manager - INFO - Cleaning up rpc modules ...
2019-08-29 22:59:26,006 - freqtrade.rpc.api_server - INFO - Stopping API Server
2019-08-29 22:59:26,330 - freqtrade.rpc.api_server - INFO - Local Rest Server started.

```

related, redacted(ish), config:
```
{

"dry_run": true,

"telegram": {
	"enabled": true,
	"token": "redacted",
	"chat_id": "redacted"
},

"api_server": {
	"enabled": true,
	"listen_ip_address": "127.0.0.1",
	"listen_port": 10110,
 	"username": "strategy110",
 	"password": "clearttextpasswordstrategy110"
},

"webhook": {
	"enabled": true,
	"url": "http://REDACTED/wh/strategy110/",
	"webhookbuy": {
		"v": "{exchange}|{pair}|{limit:8f}|{stake_amount:8f}|{stake_currency}|{fiat_currency}|{order_type}"
	},
	"webhooksell": {
		"v": "{exchange}|{pair}|{limit:8f}|{amount:8f}|{stake_currency}|{fiat_currency}|{order_type}|{open_rate}|{current_rate}|{gain}|{profit_amount:8f}|{profit_percent}|{sell_reason}"
	},
	"webhookstatus": {
		"v": "{status}"
	}
},

}

```



Discussion:

Resolving patch:
diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py
--- a/freqtrade/rpc/rpc_manager.py
+++ b/freqtrade/rpc/rpc_manager.py
@@ -56,7 +56,10 @@ def send_msg(self, msg: Dict[str, Any]) -> None:
         logger.info('Sending rpc message: %s', msg)
         for mod in self.registered_modules:
             logger.debug('Forwarding message to rpc.%s', mod.name)
-            mod.send_msg(msg)
+            try:
+                mod.send_msg(msg)
+            except NotImplementedError:
+                logger.error(f"Message type {msg['type']} not implemented by handler {mod.name}.")
 
     def startup_messages(self, config, pairlist) -> None:
         if config.get('dry_run', False):
diff --git a/freqtrade/rpc/webhook.py b/freqtrade/rpc/webhook.py
--- a/freqtrade/rpc/webhook.py
+++ b/freqtrade/rpc/webhook.py
@@ -43,7 +43,9 @@ def send_msg(self, msg: Dict[str, Any]) -> None:
                 valuedict = self._config['webhook'].get('webhookbuy', None)
             elif msg['type'] == RPCMessageType.SELL_NOTIFICATION:
                 valuedict = self._config['webhook'].get('webhooksell', None)
-            elif msg['type'] == RPCMessageType.STATUS_NOTIFICATION:
+            elif msg['type'] in(RPCMessageType.STATUS_NOTIFICATION,
+                                RPCMessageType.CUSTOM_NOTIFICATION,
+                                RPCMessageType.WARNING_NOTIFICATION):
                 valuedict = self._config['webhook'].get('webhookstatus', None)
             else:
                 raise NotImplementedError('Unknown message type: {}'.format(msg['type']))


Test patch:
diff --git a/freqtrade/tests/rpc/test_rpc_manager.py b/freqtrade/tests/rpc/test_rpc_manager.py
--- a/freqtrade/tests/rpc/test_rpc_manager.py
+++ b/freqtrade/tests/rpc/test_rpc_manager.py
@@ -115,6 +115,22 @@ def test_init_webhook_enabled(mocker, default_conf, caplog) -> None:
     assert 'webhook' in [mod.name for mod in rpc_manager.registered_modules]
 
 
+def test_send_msg_webhook_CustomMessagetype(mocker, default_conf, caplog) -> None:
+    caplog.set_level(logging.DEBUG)
+    default_conf['telegram']['enabled'] = False
+    default_conf['webhook'] = {'enabled': True, 'url': "https://DEADBEEF.com"}
+    mocker.patch('freqtrade.rpc.webhook.Webhook.send_msg',
+                 MagicMock(side_effect=NotImplementedError))
+    rpc_manager = RPCManager(get_patched_freqtradebot(mocker, default_conf))
+
+    assert 'webhook' in [mod.name for mod in rpc_manager.registered_modules]
+    rpc_manager.send_msg({'type': RPCMessageType.CUSTOM_NOTIFICATION,
+                          'status': 'TestMessage'})
+    assert log_has(
+        "Message type RPCMessageType.CUSTOM_NOTIFICATION not implemented by handler webhook.",
+        caplog)
+
+
 def test_startupmessages_telegram_enabled(mocker, default_conf, caplog) -> None:
     telegram_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
     mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
diff --git a/freqtrade/tests/rpc/test_rpc_webhook.py b/freqtrade/tests/rpc/test_rpc_webhook.py
--- a/freqtrade/tests/rpc/test_rpc_webhook.py
+++ b/freqtrade/tests/rpc/test_rpc_webhook.py
@@ -91,21 +91,24 @@ def test_send_msg(default_conf, mocker):
     assert (msg_mock.call_args[0][0]["value3"] ==
             default_conf["webhook"]["webhooksell"]["value3"].format(**msg))
 
-    # Test notification
-    msg = {
-        'type': RPCMessageType.STATUS_NOTIFICATION,
-        'status': 'Unfilled sell order for BTC cancelled due to timeout'
-    }
-    msg_mock = MagicMock()
-    mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock)
-    webhook.send_msg(msg)
-    assert msg_mock.call_count == 1
-    assert (msg_mock.call_args[0][0]["value1"] ==
-            default_conf["webhook"]["webhookstatus"]["value1"].format(**msg))
-    assert (msg_mock.call_args[0][0]["value2"] ==
-            default_conf["webhook"]["webhookstatus"]["value2"].format(**msg))
-    assert (msg_mock.call_args[0][0]["value3"] ==
-            default_conf["webhook"]["webhookstatus"]["value3"].format(**msg))
+    for msgtype in [RPCMessageType.STATUS_NOTIFICATION,
+                    RPCMessageType.WARNING_NOTIFICATION,
+                    RPCMessageType.CUSTOM_NOTIFICATION]:
+        # Test notification
+        msg = {
+            'type': msgtype,
+            'status': 'Unfilled sell order for BTC cancelled due to timeout'
+        }
+        msg_mock = MagicMock()
+        mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock)
+        webhook.send_msg(msg)
+        assert msg_mock.call_count == 1
+        assert (msg_mock.call_args[0][0]["value1"] ==
+                default_conf["webhook"]["webhookstatus"]["value1"].format(**msg))
+        assert (msg_mock.call_args[0][0]["value2"] ==
+                default_conf["webhook"]["webhookstatus"]["value2"].format(**msg))
+        assert (msg_mock.call_args[0][0]["value3"] ==
+                default_conf["webhook"]["webhookstatus"]["value3"].format(**msg))
 
 
 def test_exception_send_msg(default_conf, mocker, caplog):