A trader who misses a margin call loses their deposit. An alert that arrives 2 seconds late renders a strategy useless. Imagine holding a 100 ETH position and the market drops suddenly. Your stop-loss triggered, but the notification came a minute later — too late. Standard mass-messaging solutions won't cut it: email providers don't guarantee latency, push providers batch messages. We build a custom architecture where each critical notification goes in parallel through three channels — WebSocket, Telegram Bot, and Push — and waits for confirmation. Potential savings from timely alerts can reach tens of thousands of dollars monthly on large portfolios. In one project, a client saved $35,000 in the first month by avoiding liquidation on 50 ETH.
What problems the notification system solves for traders
Problem 1. Guaranteed delivery. Even if the server or client network goes down, the message must not be lost. We use queues with persistence and retry mechanisms. Problem 2. Latency. For P0 events (liquidation, margin call) delivery under 100ms is required. Only parallel multi-channel sending achieves that. Problem 3. Scaling. As user count grows, channel load increases non-linearly. An asynchronous event bus with sharding is needed.
How to guarantee delivery during liquidation?
A priority queue is the architectural foundation. A P0 event is fanned out to all channels: WebSocket, Push, Telegram. The system waits for at least one acknowledgement. If a channel is unavailable, the message is stored in Redis and delivered upon recovery. This gives sub-100ms latency in 99.9% of cases.
Architecture: event bus and priorities
At the core is an event bus with a priority queue. Each event receives a priority (P0/P1/P2) and a set of channels. For P0, we use synchronous fan-out: send to all channels simultaneously and wait for at least one acknowledgement. For P1 and P2, it's async fire-and-forget. Example router:
from enum import Enum
from dataclasses import dataclass
class NotificationPriority(Enum):
CRITICAL = 0
HIGH = 1
NORMAL = 2
@dataclass
class NotificationEvent:
user_id: str
event_type: str
priority: NotificationPriority
data: dict
channels: list[str] # ['websocket', 'push', 'telegram']
class NotificationRouter:
async def route(self, event: NotificationEvent):
prefs = await self.db.get_notification_prefs(event.user_id)
channels = self.select_channels(event, prefs)
tasks = []
for channel in channels:
handler = self.channel_handlers[channel]
tasks.append(handler.send(event))
if event.priority == NotificationPriority.CRITICAL:
results = await asyncio.gather(*tasks, return_exceptions=True)
await self.log_delivery(event, results)
else:
asyncio.gather(*tasks)
Delivery channels
WebSocket (in-app)
We use an async connection manager. On user connection, we deliver pending notifications. If the connection is broken, messages are stored in Redis for later delivery.
class WebSocketNotificationHandler:
def __init__(self, connection_manager):
self.connections = connection_manager
async def send(self, event: NotificationEvent):
connection = self.connections.get_user_connection(event.user_id)
if not connection:
await self.store_pending(event)
return
try:
await connection.send_json({
'type': 'notification',
'event': event.event_type,
'data': event.data,
'priority': event.priority.value,
'timestamp': datetime.utcnow().isoformat()
})
except ConnectionClosed:
await self.store_pending(event)
async def deliver_pending_on_connect(self, user_id: str, connection):
pending = await self.db.get_pending_notifications(user_id, limit=50)
for notif in pending:
await connection.send_json(notif.to_dict())
await self.db.mark_delivered(user_id, [n.id for n in pending])
Push (Firebase FCM)
For each event, we form a native notification respecting priority. Critical ones use priority=high on Android and apns-priority=10 for iOS. Invalid tokens are automatically cleaned.
import firebase_admin
from firebase_admin import messaging
class PushNotificationHandler:
def __init__(self):
firebase_admin.initialize_app()
async def send(self, event: NotificationEvent):
tokens = await self.db.get_fcm_tokens(event.user_id)
if not tokens:
return
message_data = self.format_push(event)
message = messaging.MulticastMessage(
tokens=tokens,
notification=messaging.Notification(
title=message_data['title'],
body=message_data['body']
),
data={k: str(v) for k, v in event.data.items()},
android=messaging.AndroidConfig(
priority='high' if event.priority == NotificationPriority.CRITICAL else 'normal'
),
apns=messaging.APNSConfig(
headers={'apns-priority': '10' if event.priority.value == 0 else '5'}
)
)
response = messaging.send_each_for_multicast(message)
for i, result in enumerate(response.responses):
if not result.success and 'registration-token-not-registered' in str(result.exception):
await self.db.remove_fcm_token(tokens[i])
Telegram Bot
Telegram is one of the fastest and most reliable channels. The bot sends formatted messages with emojis, and for critical events, reposts them to the personal chat.
from telegram import Bot
class TelegramNotificationHandler:
def __init__(self, bot_token: str):
self.bot = Bot(token=bot_token)
async def send(self, event: NotificationEvent):
telegram_id = await self.db.get_telegram_id(event.user_id)
if not telegram_id:
return
formatters = {
'order_filled': self.format_order_fill_message,
'liquidation': self.format_liquidation_message,
'price_alert': self.format_price_alert_message,
}
formatter = formatters.get(event.event_type, self.format_generic)
text = formatter(event.data)
await self.bot.send_message(chat_id=telegram_id, text=text, parse_mode='Markdown')
Channel comparison
| Channel | Latency | Reliability | Best for |
|---|---|---|---|
| WebSocket (in-app) | <100ms | High (if online) | P0, real-time |
| Push (FCM/APNs) | 1-5s | Medium | P0, P1 mobile |
| Telegram Bot | 1-3s | High | P0, P1 |
| 1-60s | Very High | P2, reports | |
| SMS | 5-30s | High | P0 critical |
Price Alert Engine
Price alerts are cached per symbol. On price update, all triggers are checked and notifications sent. Supports one-time and recurring alerts.
class PriceAlertEngine:
def __init__(self, price_feed, notification_router):
self.price_feed = price_feed
self.router = notification_router
self.alert_cache: dict[str, list] = {}
async def check_alerts(self, symbol: str, current_price: float):
alerts = self.alert_cache.get(symbol, [])
triggered = []
for alert in alerts:
if alert.condition == 'above' and current_price >= alert.target_price:
triggered.append(alert)
elif alert.condition == 'below' and current_price <= alert.target_price:
triggered.append(alert)
for alert in triggered:
alerts.remove(alert)
await self.router.route(NotificationEvent(
user_id=alert.user_id,
event_type='price_alert',
priority=NotificationPriority.HIGH,
data={
'symbol': symbol,
'target_price': alert.target_price,
'current_price': current_price,
'condition': alert.condition
},
channels=['websocket', 'push', 'telegram']
))
if alert.is_recurring:
await self.add_alert(alert)
Flexible user settings
The user can configure each event type: turn on/off, choose channels, set trigger thresholds, and quiet hours. Critical notifications (P0) ignore quiet hours. This flexibility reduces opt-outs and increases satisfaction.
What's included and timelines
| Stage | Duration | Outcome |
|---|---|---|
| Analytics | 3-5 days | Event source schema, latency requirements, load profile |
| Design | 5-7 days | Architecture, stack selection, queue prototype |
| Implementation | 15-25 days | Router development, channel integration, load testing |
| Testing | 5 days | Chaos tests (network failure, provider delays), benchmark |
| Deployment | 2-3 days | Monitoring, CI/CD, documentation |
Total timeline: 30-45 business days depending on number of channels and scale requirements. Our team has 5+ years of experience in blockchain infrastructure and over 30 successful projects.
Why choose us
Operational experience with high-load systems is proven by real projects: we know how to design an architecture that won't fail under peak loads. We use a modern tech stack: Firebase Cloud Messaging for push notifications and Telegram Bot API for instant delivery. Average client savings on liquidations thanks to timely alerts is up to $20,000 per month. If you need a reliable notification system, contact us for a consultation — we'll propose an architecture tailored to your loads and help implement it in the shortest time. Request development now.







