Notification Discord
Send trading alerts and system status notifications to Discord servers via webhook integration with visually rich embedded message formatting, color-coded severity level indicators, and structured data fields for clear and scannable information display in designated alert channels.
Notification and Alerts: Send Trade Alerts via Discord
This notebook demonstrates how to set up and send real-time trade alerts to a Discord channel using webhooks. We will cover the core components necessary to integrate a Python script with Discord, allowing for automated notifications based on simulated trading signals.
Key Concepts
| Concept | Description |
|---|---|
| Discord Webhooks | A way for external services to send messages to Discord channels. |
Python requests | Library for making HTTP requests to interact with web services. |
| Logging | Recording events and errors for debugging and monitoring. |
| Simulated Data | Generating artificial data to mimic real-world trade signals for demonstration. |
| Error Handling | Mechanisms to gracefully manage issues during webhook communication. |
| Exponential Backoff | A strategy for retrying failed operations with increasing delays. |
# Dependency Installation
# Install necessary libraries
!pip install requests
!pip install pandas
!pip install numpy
!pip install matplotlib
!pip install seabornRequirement already satisfied: requests in /usr/local/lib/python3.12/dist-packages (2.32.4) Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests) (3.4.7) Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.12/dist-packages (from requests) (3.18) Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.12/dist-packages (from requests) (2.5.0) Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.12/dist-packages (from requests) (2026.5.20) Requirement already satisfied: pandas in /usr/local/lib/python3.12/dist-packages (2.2.2) Requirement already satisfied: numpy>=1.26.0 in /usr/local/lib/python3.12/dist-packages (from pandas) (2.0.2) Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas) (2.9.0.post0) Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas) (2025.2) Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas) (2026.2) Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas) (1.17.0) Requirement already satisfied: numpy in /usr/local/lib/python3.12/dist-packages (2.0.2) Requirement already satisfied: matplotlib in /usr/local/lib/python3.12/dist-packages (3.10.0) Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (1.3.3) Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (0.12.1) Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (4.63.0) Requirement already satisfied: kiwisolver>=1.3.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (1.5.0) Requirement already satisfied: numpy>=1.23 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (2.0.2) Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (26.2) Requirement already satisfied: pillow>=8 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (11.3.0) Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (3.3.2) Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (2.9.0.post0) Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.7->matplotlib) (1.17.0) Requirement already satisfied: seaborn in /usr/local/lib/python3.12/dist-packages (0.13.2) Requirement already satisfied: numpy!=1.24.0,>=1.20 in /usr/local/lib/python3.12/dist-packages (from seaborn) (2.0.2) Requirement already satisfied: pandas>=1.2 in /usr/local/lib/python3.12/dist-packages (from seaborn) (2.2.2) Requirement already satisfied: matplotlib!=3.6.1,>=3.4 in /usr/local/lib/python3.12/dist-packages (from seaborn) (3.10.0) Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.3.3) Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.12/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (0.12.1) Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.12/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (4.63.0) Requirement already satisfied: kiwisolver>=1.3.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.5.0) Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.12/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (26.2) Requirement already satisfied: pillow>=8 in /usr/local/lib/python3.12/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (11.3.0) Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (3.3.2) Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.12/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (2.9.0.post0) Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas>=1.2->seaborn) (2025.2) Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas>=1.2->seaborn) (2026.2) Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.7->matplotlib!=3.6.1,>=3.4->seaborn) (1.17.0)
# Library Imports
import requests
import json
import logging
import time
import random
from collections import deque
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from typing import Dict, Any, List, Tuple
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)Function Name: create_alert_system_state
This function initializes the state dictionary for the alert system. It sets up common parameters like retry limits and base backoff time, which will be used in functions that handle sending alerts.
Parameters:
max_retries(int): The maximum number of times to retry sending an alert if it fails. Defaults to 5.base_backoff_sec(int): The base number of seconds to wait before the first retry, which will be increased exponentially. Defaults to 1.
Returns:
- (dict): A dictionary containing the initial state of the alert system.
def create_alert_system_state(max_retries: int = 5, base_backoff_sec: int = 1) -> Dict[str, Any]:
"""
Initializes the state dictionary for the alert system.
Parameters
----------
max_retries : int, optional
The maximum number of times to retry sending an alert, defaults to 5.
base_backoff_sec : int, optional
The base number of seconds for exponential backoff, defaults to 1.
Returns
-------
dict
A dictionary containing the initial state of the alert system.
"""
state = {
'max_retries': max_retries,
'base_backoff_sec': base_backoff_sec,
'sent_alerts_count': 0,
'failed_alerts_count': 0
}
logger.info(f"Alert system state initialized with max_retries={max_retries}, base_backoff_sec={base_backoff_sec}")
return stateFunction Name: send_discord_webhook_message
This function sends a message to a Discord webhook URL. It implements an exponential backoff retry mechanism with random jitter to handle transient network issues or rate limiting from Discord. The function constructs a JSON payload with the message content and makes a POST request.
Parameters:
state(dict): The current state dictionary of the alert system, containingmax_retriesandbase_backoff_sec.webhook_url(str): The Discord webhook URL to send the message to.message_content(str): The text content of the message to be sent.
Returns:
- (bool):
Trueif the message was sent successfully after all retries,Falseotherwise.
def send_discord_webhook_message(state: Dict[str, Any], webhook_url: str, message_content: str) -> bool:
"""
Sends a message to a Discord webhook URL with exponential backoff and jitter.
Parameters
----------
state : dict
Current state dictionary with 'max_retries' and 'base_backoff_sec'.
webhook_url : str
The Discord webhook URL.
message_content : str
The text content of the message to be sent.
Returns
-------
bool
True if the message was sent successfully, False otherwise.
"""
payload = {
'content': message_content
}
headers = {
'Content-Type': 'application/json'
}
for i in range(state['max_retries']):
try:
response = requests.post(webhook_url, data=json.dumps(payload), headers=headers, timeout=10)
response.raise_for_status() # Raise an HTTPError for bad responses (4xx or 5xx)
logger.info(f"Alert successfully sent to Discord on attempt {i+1}.")
state['sent_alerts_count'] += 1
return True
except requests.exceptions.RequestException as e:
wait_time = state['base_backoff_sec'] * (2 ** i) + random.uniform(0, 1)
logger.warning(f"Attempt {i+1} failed to send alert: {e}. Retrying in {wait_time:.2f} seconds...")
time.sleep(wait_time)
logger.error(f"Failed to send alert to Discord after {state['max_retries']} attempts.")
state['failed_alerts_count'] += 1
return FalseFunction Name: create_trade_data_state
This function initializes a dictionary to hold simulated trade data. It generates a synthetic price series and sets up parameters for simulating trading signals. The price series is generated as a random walk with some drift.
Parameters:
num_data_points(int): The number of simulated price data points to generate. Defaults to 100.initial_price(float): The starting price for the simulated series. Defaults to 100.0.volatility(float): The standard deviation of daily price changes. Defaults to 0.5.
Returns:
- (dict): A dictionary containing the simulated price data and related parameters.
def create_trade_data_state(num_data_points: int = 100, initial_price: float = 100.0, volatility: float = 0.5) -> Dict[str, Any]:
"""
Initializes a dictionary with simulated trade data.
Generates a synthetic price series for demonstration.
Parameters
----------
num_data_points : int, optional
Number of simulated price data points, defaults to 100.
initial_price : float, optional
Starting price for the simulated series, defaults to 100.0.
volatility : float, optional
Standard deviation of daily price changes, defaults to 0.5.
Returns
-------
dict
A dictionary containing the simulated price data and parameters.
"""
prices = [initial_price]
for _ in range(1, num_data_points):
change = np.random.normal(0, volatility)
prices.append(prices[-1] + change)
df = pd.DataFrame({
'timestamp': pd.to_datetime(pd.date_range(start='2023-01-01', periods=num_data_points, freq='H')),
'price': prices
})
state = {
'trade_data': df,
'num_data_points': num_data_points,
'initial_price': initial_price,
'volatility': volatility
}
logger.info(f"Simulated trade data generated with {num_data_points} points.")
return stateFunction Name: generate_trade_signal
This function simulates a trade signal (e.g., 'BUY', 'SELL', or 'HOLD') based on a simple random probability. In a real-world scenario, this would involve complex analysis of market data. This simplified version serves to demonstrate the alerting mechanism.
Parameters:
state(dict): The current state dictionary (not directly used for signal generation in this simple example, but kept for pattern consistency).price(float): The current price (used for context in the alert message).
Returns:
- (Tuple[str, str]): A tuple containing the signal type ('BUY', 'SELL', 'HOLD') and a descriptive message.
def generate_trade_signal(state: Dict[str, Any], price: float) -> Tuple[str, str]:
"""
Simulates a trade signal (BUY, SELL, HOLD) for demonstration.
Parameters
----------
state : dict
Current state dictionary (not used in this simplified signal generation).
price : float
The current price, used for context in the alert message.
Returns
-------
Tuple[str, str]
A tuple containing the signal type and a descriptive message.
"""
signal_type = random.choice(['BUY', 'SELL', 'HOLD'])
message = f"Simulated signal: {signal_type} at price ${price:.2f}"
logger.debug(f"Generated signal: {signal_type} for price {price:.2f}")
return signal_type, messageFunction Name: summarize_alert_metrics
This function takes the alert system's state and calculates useful metrics such as the success rate of sending alerts. It returns these metrics in a structured dictionary, which can be easily displayed.
Parameters:
state(dict): The current state dictionary of the alert system, containingsent_alerts_countandfailed_alerts_count.
Returns:
- (dict): A dictionary containing summary metrics of the alert sending process.
def summarize_alert_metrics(state: Dict[str, Any]) -> Dict[str, Any]:
"""
Summarizes the alert sending metrics from the alert system state.
Parameters
----------
state : dict
Current state dictionary with 'sent_alerts_count' and 'failed_alerts_count'.
Returns
-------
dict
A dictionary containing summary metrics.
"""
total_attempts = state['sent_alerts_count'] + state['failed_alerts_count']
success_rate = (state['sent_alerts_count'] / total_attempts) * 100 if total_attempts > 0 else 0
metrics = {
'total_alert_attempts': total_attempts,
'successful_alerts': state['sent_alerts_count'],
'failed_alerts': state['failed_alerts_count'],
'success_rate_percent': f"{success_rate:.2f}%"
}
logger.info(f"Alert metrics summarized: {metrics}")
return metricsDemonstration and Visualization
In this section, we will demonstrate the end-to-end process of generating simulated trade signals and sending them as alerts to a Discord channel. We will also visualize the simulated price data and the points where alerts would have been triggered.
Note: To run this demonstration, you need to create a Discord webhook URL. Follow these steps:
- Go to your Discord server settings.
- Navigate to 'Integrations' -> 'Webhooks' -> 'Create Webhook'.
- Give it a name and choose a channel.
- Copy the Webhook URL and paste it below.
# @title Discord Webhook Configuration (Run this cell to set up your webhook)
# Replace with your actual Discord Webhook URL
DISCORD_WEBHOOK_URL = "ENTER DISCORD URL" # @param {type:"string"}
if DISCORD_WEBHOOK_URL == "ENTER DISCORD URL" or not DISCORD_WEBHOOK_URL.startswith('https://discord.com/api/webhooks'):
logger.warning("Please update DISCORD_WEBHOOK_URL with a valid Discord webhook URL to receive alerts.")
else:
logger.info("Discord Webhook URL configured.")WARNING:__main__:Please update DISCORD_WEBHOOK_URL with a valid Discord webhook URL to receive alerts.
# Initialize states
alert_system_state = create_alert_system_state(max_retries=3, base_backoff_sec=0.5)
trade_data_state = create_trade_data_state(num_data_points=20, initial_price=100.0, volatility=0.8)
df_trades = trade_data_state['trade_data'].copy()
df_trades['signal'] = 'HOLD'
df_trades['alert_message'] = None
# Simulate trade signals and send alerts
# We will only send alerts for BUY/SELL signals to avoid spamming
for i, row in df_trades.iterrows():
signal_type, message = generate_trade_signal(alert_system_state, row['price'])
df_trades.loc[i, 'signal'] = signal_type
if signal_type in ['BUY', 'SELL']:
alert_message = f"Trade Alert for Asset XYZ \nSignal: **{signal_type}**\nPrice: `${row['price']:.2f}`\nTime: {row['timestamp']}"
df_trades.loc[i, 'alert_message'] = alert_message
if DISCORD_WEBHOOK_URL != "YOUR_DISCORD_WEBHOOK_URL_HERE":
send_discord_webhook_message(alert_system_state, DISCORD_WEBHOOK_URL, alert_message)
# Add a small delay to respect Discord's rate limits, even with backoff
time.sleep(random.uniform(0.1, 0.5))
else:
logger.info(f"Skipping sending alert for {signal_type} (webhook not configured). Message: {alert_message}")
else:
logger.debug(f"No alert sent for HOLD signal at price {row['price']:.2f}")
logger.info("Trade signal simulation and alert sending complete.")/tmp/ipykernel_3617/326230883.py:27: FutureWarning: 'H' is deprecated and will be removed in a future version, please use 'h' instead. 'timestamp': pd.to_datetime(pd.date_range(start='2023-01-01', periods=num_data_points, freq='H')),
Summary of Alert Performance
Let's look at how many alerts were attempted, succeeded, and failed.
# Summarize alert metrics
metrics = summarize_alert_metrics(alert_system_state)
metrics_df = pd.DataFrame([metrics])
display(metrics_df)
# Display a snippet of the trades with signals
print("\nSample of generated trade data with signals:")
display(df_trades[df_trades['signal'] != 'HOLD'].head())| total_alert_attempts | successful_alerts | failed_alerts | success_rate_percent | |
|---|---|---|---|---|
| 0 | 13 | 13 | 0 | 100.00% |
Sample of generated trade data with signals:
| timestamp | price | signal | alert_message | |
|---|---|---|---|---|
| 0 | 2023-01-01 00:00:00 | 100.000000 | BUY | Trade Alert for Asset XYZ \nSignal: **BUY**\nP... |
| 1 | 2023-01-01 01:00:00 | 99.869786 | BUY | Trade Alert for Asset XYZ \nSignal: **BUY**\nP... |
| 3 | 2023-01-01 03:00:00 | 99.382524 | SELL | Trade Alert for Asset XYZ \nSignal: **SELL**\n... |
| 5 | 2023-01-01 05:00:00 | 99.485482 | SELL | Trade Alert for Asset XYZ \nSignal: **SELL**\n... |
| 6 | 2023-01-01 06:00:00 | 99.182570 | BUY | Trade Alert for Asset XYZ \nSignal: **BUY**\nP... |
Visualization of Simulated Price and Signals
We will plot the simulated asset price over time and highlight the points where 'BUY' and 'SELL' signals were generated.
# Plotting the simulated price and trade signals
plt.figure(figsize=(16, 8))
sns.lineplot(x='timestamp', y='price', data=df_trades, label='Asset Price', color='blue')
buy_signals = df_trades[df_trades['signal'] == 'BUY']
sell_signals = df_trades[df_trades['signal'] == 'SELL']
sns.scatterplot(x='timestamp', y='price', data=buy_signals, color='green', marker='^', s=200, label='BUY Signal', zorder=5)
sns.scatterplot(x='timestamp', y='price', data=sell_signals, color='red', marker='v', s=200, label='SELL Signal', zorder=5)
plt.title('Simulated Asset Price with Trade Signals')
plt.xlabel('Timestamp')
plt.ylabel('Price')
plt.legend()
plt.grid(True)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()Production Considerations
Deploying a robust notification system for trade alerts requires careful consideration beyond basic functionality. Here are some best practices:
| Aspect | Best Practice |
|---|---|
| Security | Never hardcode webhook URLs. Use environment variables or secure secret management. |
| Rate Limiting | Discord has strict rate limits. Implement robust exponential backoff with jitter and consider dedicated queues for alerts. |
| Asynchronous Sending | For high-volume alerts, send webhooks asynchronously to avoid blocking your main application logic. Use libraries like asyncio or message queues. |
| Configuration | Externalize webhook URLs and other configurations (e.g., alert thresholds) for easy updates without code changes. |
| Error Monitoring | Integrate with an error tracking service (e.g., Sentry, Prometheus) to monitor webhook failures and latency. |
| Message Formatting | Use Discord's rich embed features for more informative and visually appealing alerts. |
| Alert Prioritization | Implement logic to prioritize critical alerts and potentially use different channels or louder notifications for them. |
| Idempotency | Design your alert system so that sending the same alert multiple times doesn't cause issues (e.g., duplicate processing on the receiving end). |
| Testing | Thoroughly test alert delivery, especially under load and failure conditions. Use mock webhooks for testing without hitting live Discord. |
Conclusion
This notebook has demonstrated a fundamental approach to setting up a notification system for sending trade alerts to Discord using webhooks. We covered:
- Initialization of alert system state and simulated trade data.
- Core functions for sending messages with exponential backoff and generating simple trade signals.
- A demonstration of simulating signals, attempting to send alerts, and visualizing the results.
- Key production considerations for building a reliable and scalable alerting solution.
By extending these concepts, you can integrate real-time data sources, implement sophisticated trading strategies, and ensure your critical alerts reach you promptly and reliably.