make discord notifications fully configurable.
This commit is contained in:
parent
f816c15e1e
commit
fdfa94bcc3
BIN
docs/assets/discord_notification.png
Normal file
BIN
docs/assets/discord_notification.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
@ -239,3 +239,52 @@ Possible parameters are:
|
|||||||
The fields in `webhook.webhookstatus` are used for regular status messages (Started / Stopped / ...). Parameters are filled using string.format.
|
The fields in `webhook.webhookstatus` are used for regular status messages (Started / Stopped / ...). Parameters are filled using string.format.
|
||||||
|
|
||||||
The only possible value here is `{status}`.
|
The only possible value here is `{status}`.
|
||||||
|
|
||||||
|
## Discord
|
||||||
|
|
||||||
|
A special form of webhooks is available for discord.
|
||||||
|
You can configure this as follows:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"discord": {
|
||||||
|
"enabled": true,
|
||||||
|
"webhook_url": "https://discord.com/api/webhooks/<Your webhook URL ...>",
|
||||||
|
"exit_fill": [
|
||||||
|
{"Trade ID": "{trade_id}"},
|
||||||
|
{"Exchange": "{exchange}"},
|
||||||
|
{"Pair": "{pair}"},
|
||||||
|
{"Direction": "{direction}"},
|
||||||
|
{"Open rate": "{open_rate}"},
|
||||||
|
{"Close rate": "{close_rate}"},
|
||||||
|
{"Amount": "{amount}"},
|
||||||
|
{"Open date": "{open_date:%Y-%m-%d %H:%M:%S}"},
|
||||||
|
{"Close date": "{close_date:%Y-%m-%d %H:%M:%S}"},
|
||||||
|
{"Profit": "{profit_amount} {stake_currency}"},
|
||||||
|
{"Profitability": "{profit_ratio:.2%}"},
|
||||||
|
{"Enter tag": "{enter_tag}"},
|
||||||
|
{"Exit Reason": "{exit_reason}"},
|
||||||
|
{"Strategy": "{strategy}"},
|
||||||
|
{"Timeframe": "{timeframe}"},
|
||||||
|
],
|
||||||
|
"entry_fill": [
|
||||||
|
{"Trade ID": "{trade_id}"},
|
||||||
|
{"Exchange": "{exchange}"},
|
||||||
|
{"Pair": "{pair}"},
|
||||||
|
{"Direction": "{direction}"},
|
||||||
|
{"Open rate": "{open_rate}"},
|
||||||
|
{"Amount": "{amount}"},
|
||||||
|
{"Open date": "{open_date:%Y-%m-%d %H:%M:%S}"},
|
||||||
|
{"Enter tag": "{enter_tag}"},
|
||||||
|
{"Strategy": "{strategy} {timeframe}"},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
The above represents the default (`exit_fill` and `entry_fill` are optional and will default to the above configuration) - modifications are obviously possible.
|
||||||
|
|
||||||
|
Available fields correspond to the fields for webhooks and are documented in the corresponding webhook sections.
|
||||||
|
|
||||||
|
The notifications will look as follows by default.
|
||||||
|
|
||||||
|
![discord-notification](assets/discord_notification.png)
|
||||||
|
@ -336,6 +336,47 @@ CONF_SCHEMA = {
|
|||||||
'webhookstatus': {'type': 'object'},
|
'webhookstatus': {'type': 'object'},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'discord': {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'enabled': {'type': 'boolean'},
|
||||||
|
'webhook_url': {'type': 'string'},
|
||||||
|
"exit_fill": {
|
||||||
|
'type': 'array', 'items': {'type': 'object'},
|
||||||
|
'default': [
|
||||||
|
{"Trade ID": "{trade_id}"},
|
||||||
|
{"Exchange": "{exchange}"},
|
||||||
|
{"Pair": "{pair}"},
|
||||||
|
{"Direction": "{direction}"},
|
||||||
|
{"Open rate": "{open_rate}"},
|
||||||
|
{"Close rate": "{close_rate}"},
|
||||||
|
{"Amount": "{amount}"},
|
||||||
|
{"Open date": "{open_date:%Y-%m-%d %H:%M:%S}"},
|
||||||
|
{"Close date": "{close_date:%Y-%m-%d %H:%M:%S}"},
|
||||||
|
{"Profit": "{profit_amount} {stake_currency}"},
|
||||||
|
{"Profitability": "{profit_ratio:.2%}"},
|
||||||
|
{"Enter tag": "{enter_tag}"},
|
||||||
|
{"Exit Reason": "{exit_reason}"},
|
||||||
|
{"Strategy": "{strategy}"},
|
||||||
|
{"Timeframe": "{timeframe}"},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"entry_fill": {
|
||||||
|
'type': 'array', 'items': {'type': 'object'},
|
||||||
|
'default': [
|
||||||
|
{"Trade ID": "{trade_id}"},
|
||||||
|
{"Exchange": "{exchange}"},
|
||||||
|
{"Pair": "{pair}"},
|
||||||
|
{"Direction": "{direction}"},
|
||||||
|
{"Open rate": "{open_rate}"},
|
||||||
|
{"Amount": "{amount}"},
|
||||||
|
{"Open date": "{open_date:%Y-%m-%d %H:%M:%S}"},
|
||||||
|
{"Enter tag": "{enter_tag}"},
|
||||||
|
{"Strategy": "{strategy} {timeframe}"},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
'api_server': {
|
'api_server': {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': {
|
'properties': {
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from freqtrade.constants import DATETIME_PRINT_FORMAT
|
from freqtrade.enums.rpcmessagetype import RPCMessageType
|
||||||
from freqtrade.enums import RPCMessageType
|
|
||||||
from freqtrade.rpc import RPC
|
from freqtrade.rpc import RPC
|
||||||
from freqtrade.rpc.webhook import Webhook
|
from freqtrade.rpc.webhook import Webhook
|
||||||
|
|
||||||
@ -33,46 +32,26 @@ class Discord(Webhook):
|
|||||||
def send_msg(self, msg) -> None:
|
def send_msg(self, msg) -> None:
|
||||||
logger.info(f"Sending discord message: {msg}")
|
logger.info(f"Sending discord message: {msg}")
|
||||||
|
|
||||||
# TODO: handle other message types
|
if msg['type'].value in self.config['discord']:
|
||||||
if msg['type'] == RPCMessageType.EXIT_FILL:
|
|
||||||
profit_ratio = msg.get('profit_ratio')
|
msg['strategy'] = self.strategy
|
||||||
open_date = msg.get('open_date').strftime(DATETIME_PRINT_FORMAT)
|
msg['timeframe'] = self.timeframe
|
||||||
close_date = msg.get('close_date').strftime(
|
fields = self.config['discord'].get(msg['type'].value)
|
||||||
DATETIME_PRINT_FORMAT) if msg.get('close_date') else ''
|
color = 0x0000FF
|
||||||
|
if msg['type'] in (RPCMessageType.EXIT, RPCMessageType.EXIT_FILL):
|
||||||
|
profit_ratio = msg.get('profit_ratio')
|
||||||
|
color = (0x00FF00 if profit_ratio > 0 else 0xFF0000)
|
||||||
|
|
||||||
embeds = [{
|
embeds = [{
|
||||||
'title': '{} Trade: {}'.format(
|
'title': f"Trade: {msg['pair']} {msg['type'].value}",
|
||||||
'Profit' if profit_ratio > 0 else 'Loss',
|
'color': color,
|
||||||
msg.get('pair')),
|
'fields': [],
|
||||||
'color': (0x00FF00 if profit_ratio > 0 else 0xFF0000),
|
|
||||||
'fields': [
|
|
||||||
{'name': 'Trade ID', 'value': msg.get('trade_id'), 'inline': True},
|
|
||||||
{'name': 'Exchange', 'value': msg.get('exchange').capitalize(), 'inline': True},
|
|
||||||
{'name': 'Pair', 'value': msg.get('pair'), 'inline': True},
|
|
||||||
{'name': 'Direction', 'value': 'Short' if msg.get(
|
|
||||||
'is_short') else 'Long', 'inline': True},
|
|
||||||
{'name': 'Open rate', 'value': msg.get('open_rate'), 'inline': True},
|
|
||||||
{'name': 'Close rate', 'value': msg.get('close_rate'), 'inline': True},
|
|
||||||
{'name': 'Amount', 'value': msg.get('amount'), 'inline': True},
|
|
||||||
{'name': 'Open order', 'value': msg.get('open_order_id'), 'inline': True},
|
|
||||||
{'name': 'Open date', 'value': open_date, 'inline': True},
|
|
||||||
{'name': 'Close date', 'value': close_date, 'inline': True},
|
|
||||||
{'name': 'Profit', 'value': msg.get('profit_amount'), 'inline': True},
|
|
||||||
{'name': 'Profitability', 'value': f'{profit_ratio:.2%}', 'inline': True},
|
|
||||||
{'name': 'Stake currency', 'value': msg.get('stake_currency'), 'inline': True},
|
|
||||||
{'name': 'Fiat currency', 'value': msg.get('fiat_display_currency'),
|
|
||||||
'inline': True},
|
|
||||||
{'name': 'Buy Tag', 'value': msg.get('enter_tag'), 'inline': True},
|
|
||||||
{'name': 'Sell Reason', 'value': msg.get('exit_reason'), 'inline': True},
|
|
||||||
{'name': 'Strategy', 'value': self.strategy, 'inline': True},
|
|
||||||
{'name': 'Timeframe', 'value': self.timeframe, 'inline': True},
|
|
||||||
],
|
|
||||||
}]
|
|
||||||
|
|
||||||
# convert all value in fields to string for discord
|
}]
|
||||||
for embed in embeds:
|
for f in fields:
|
||||||
for field in embed['fields']: # type: ignore
|
for k, v in f.items():
|
||||||
field['value'] = str(field['value'])
|
v = v.format(**msg)
|
||||||
|
embeds[0]['fields'].append({'name': k, 'value': v, 'inline': True})
|
||||||
|
|
||||||
# Send the message to discord channel
|
# Send the message to discord channel
|
||||||
payload = {'embeds': embeds}
|
payload = {'embeds': embeds}
|
||||||
|
Loading…
Reference in New Issue
Block a user