feat: add support for discord notification
This commit is contained in:
parent
34a44b9dd2
commit
afd8e85835
@ -2,6 +2,7 @@
|
|||||||
Freqtrade is the main module of this bot. It contains the class Freqtrade()
|
Freqtrade is the main module of this bot. It contains the class Freqtrade()
|
||||||
"""
|
"""
|
||||||
import copy
|
import copy
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
from datetime import datetime, time, timezone
|
from datetime import datetime, time, timezone
|
||||||
@ -9,6 +10,7 @@ from math import isclose
|
|||||||
from threading import Lock
|
from threading import Lock
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
import requests
|
||||||
from schedule import Scheduler
|
from schedule import Scheduler
|
||||||
|
|
||||||
from freqtrade import __version__, constants
|
from freqtrade import __version__, constants
|
||||||
@ -34,7 +36,6 @@ from freqtrade.strategy.interface import IStrategy
|
|||||||
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
|
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
|
||||||
from freqtrade.wallets import Wallets
|
from freqtrade.wallets import Wallets
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -379,9 +380,9 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
except ExchangeError:
|
except ExchangeError:
|
||||||
logger.warning(f"Error updating {order.order_id}.")
|
logger.warning(f"Error updating {order.order_id}.")
|
||||||
|
|
||||||
#
|
#
|
||||||
# BUY / enter positions / open trades logic and methods
|
# BUY / enter positions / open trades logic and methods
|
||||||
#
|
#
|
||||||
|
|
||||||
def enter_positions(self) -> int:
|
def enter_positions(self) -> int:
|
||||||
"""
|
"""
|
||||||
@ -489,9 +490,9 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#
|
#
|
||||||
# BUY / increase positions / DCA logic and methods
|
# BUY / increase positions / DCA logic and methods
|
||||||
#
|
#
|
||||||
def process_open_trade_positions(self):
|
def process_open_trade_positions(self):
|
||||||
"""
|
"""
|
||||||
Tries to execute additional buy or sell orders for open trades (positions)
|
Tries to execute additional buy or sell orders for open trades (positions)
|
||||||
@ -885,9 +886,9 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
# Send the message
|
# Send the message
|
||||||
self.rpc.send_msg(msg)
|
self.rpc.send_msg(msg)
|
||||||
|
|
||||||
#
|
#
|
||||||
# SELL / exit positions / close trades logic and methods
|
# SELL / exit positions / close trades logic and methods
|
||||||
#
|
#
|
||||||
|
|
||||||
def exit_positions(self, trades: List[Any]) -> int:
|
def exit_positions(self, trades: List[Any]) -> int:
|
||||||
"""
|
"""
|
||||||
@ -1543,6 +1544,43 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
# Send the message
|
# Send the message
|
||||||
self.rpc.send_msg(msg)
|
self.rpc.send_msg(msg)
|
||||||
|
|
||||||
|
open_date = trade.open_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
close_date = trade.close_date.strftime('%Y-%m-%d %H:%M:%S') if trade.close_date else None
|
||||||
|
|
||||||
|
# Send the message to the discord bot
|
||||||
|
embeds = [{
|
||||||
|
'title': '{} Trade: {}'.format(
|
||||||
|
'Profit' if profit_ratio > 0 else 'Loss',
|
||||||
|
trade.pair),
|
||||||
|
'color': (0x00FF00 if profit_ratio > 0 else 0xFF0000),
|
||||||
|
'fields': [
|
||||||
|
{'name': 'Trade ID', 'value': trade.id, 'inline': True},
|
||||||
|
{'name': 'Exchange', 'value': trade.exchange.capitalize(), 'inline': True},
|
||||||
|
{'name': 'Pair', 'value': trade.pair, 'inline': True},
|
||||||
|
{'name': 'Direction', 'value': 'Short' if trade.is_short else 'Long', 'inline': True},
|
||||||
|
{'name': 'Open rate', 'value': trade.open_rate, 'inline': True},
|
||||||
|
{'name': 'Close rate', 'value': trade.close_rate, 'inline': True},
|
||||||
|
{'name': 'Amount', 'value': trade.amount, 'inline': True},
|
||||||
|
{'name': 'Open order', 'value': trade.open_order_id, 'inline': True},
|
||||||
|
{'name': 'Open date', 'value': open_date, 'inline': True},
|
||||||
|
{'name': 'Close date', 'value': close_date, 'inline': True},
|
||||||
|
{'name': 'Profit', 'value': profit_trade, 'inline': True},
|
||||||
|
{'name': 'Profitability', 'value': '{:.2f}%'.format(profit_ratio * 100), 'inline': True},
|
||||||
|
{'name': 'Stake currency', 'value': self.config['stake_currency'], 'inline': True},
|
||||||
|
{'name': 'Fiat currency', 'value': self.config.get('fiat_display_currency', None), 'inline': True},
|
||||||
|
{'name': 'Buy Tag', 'value': trade.enter_tag, 'inline': True},
|
||||||
|
{'name': 'Sell Reason', 'value': trade.exit_reason, 'inline': True},
|
||||||
|
{'name': 'Strategy', 'value': trade.strategy, 'inline': True},
|
||||||
|
{'name': 'Timeframe', 'value': trade.timeframe, 'inline': True},
|
||||||
|
],
|
||||||
|
}]
|
||||||
|
# convert all value in fields to string
|
||||||
|
for embed in embeds:
|
||||||
|
for field in embed['fields']:
|
||||||
|
field['value'] = str(field['value'])
|
||||||
|
if fill:
|
||||||
|
self.discord_send(embeds)
|
||||||
|
|
||||||
def _notify_exit_cancel(self, trade: Trade, order_type: str, reason: str) -> None:
|
def _notify_exit_cancel(self, trade: Trade, order_type: str, reason: str) -> None:
|
||||||
"""
|
"""
|
||||||
Sends rpc notification when a sell cancel occurred.
|
Sends rpc notification when a sell cancel occurred.
|
||||||
@ -1593,9 +1631,9 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
# Send the message
|
# Send the message
|
||||||
self.rpc.send_msg(msg)
|
self.rpc.send_msg(msg)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Common update trade state methods
|
# Common update trade state methods
|
||||||
#
|
#
|
||||||
|
|
||||||
def update_trade_state(self, trade: Trade, order_id: str, action_order: Dict[str, Any] = None,
|
def update_trade_state(self, trade: Trade, order_id: str, action_order: Dict[str, Any] = None,
|
||||||
stoploss_order: bool = False, send_msg: bool = True) -> bool:
|
stoploss_order: bool = False, send_msg: bool = True) -> bool:
|
||||||
@ -1818,3 +1856,22 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
return max(
|
return max(
|
||||||
min(valid_custom_price, max_custom_price_allowed),
|
min(valid_custom_price, max_custom_price_allowed),
|
||||||
min_custom_price_allowed)
|
min_custom_price_allowed)
|
||||||
|
|
||||||
|
def discord_send(self, embeds):
|
||||||
|
if not 'discord' in self.config or self.config['discord']['enabled'] == False:
|
||||||
|
return
|
||||||
|
if self.config['runmode'].value in ('dry_run', 'live'):
|
||||||
|
webhook_url = self.config['discord']['webhook_url']
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"embeds": embeds
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
requests.post(webhook_url, data=json.dumps(payload), headers=headers)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error sending discord message: {e}")
|
||||||
|
Loading…
Reference in New Issue
Block a user