DEV Community

Paarthurnax
Paarthurnax

Posted on

How to Get Telegram Crypto Alerts from Your Local AI Agent

How to Get Telegram Crypto Alerts from Your Local AI Agent

Your AI agent runs analysis 24/7. Your phone is with you 24/7. The missing piece is a reliable bridge between the two. Telegram is that bridge — free, instant, and surprisingly powerful for crypto alerts.

This guide walks you through building a complete Telegram alerting system that integrates with OpenClaw. By the end, you'll receive formatted price alerts, RSI signals, regime change notifications, and daily summaries — all delivered to your phone by a local AI agent that never sleeps.

Not financial advice. Paper trading only.


Why Telegram for Crypto Alerts?

You have options: email, SMS, push notifications, Discord. Telegram wins for several reasons:

It's free forever. No API costs. No monthly limits. The Telegram Bot API is completely free with generous rate limits (30 messages/second to different users).

It has a real Bot API. Unlike email (complex SMTP), Telegram bots are trivially easy to create and control via HTTP.

It supports rich formatting. Markdown, bold, code blocks, inline buttons — your alerts can be beautifully formatted, not just plain text.

It's cross-platform. Desktop, iOS, Android — your alerts follow you everywhere.

It's private. Your bot messages go directly to you. No shared Discord server where your signals are public.


Step 1: Create Your Telegram Bot

This takes 2 minutes:

  1. Open Telegram and search for @BotFather
  2. Send /newbot
  3. Choose a name (e.g., "CryptoWatch Agent")
  4. Choose a username ending in bot (e.g., cryptowatch_agent_bot)
  5. BotFather gives you a token — copy it, keep it safe

Your token looks like: 6782456789:AAEtKnX8YVmP...

Get Your Chat ID

Next you need your personal chat ID (so the bot knows where to send messages):

  1. Start a conversation with your new bot
  2. Send /start
  3. Visit: https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates
  4. Find "chat": {"id": YOUR_CHAT_ID} in the JSON response

Store both values:

# Windows
set TELEGRAM_BOT_TOKEN=6782456789:AAEtKnX8YVmP...
set TELEGRAM_CHAT_ID=123456789

# Or store in a .env file
echo TELEGRAM_BOT_TOKEN=6782456789:AAEtKnX8YVmP... > .env
echo TELEGRAM_CHAT_ID=123456789 >> .env
Enter fullscreen mode Exit fullscreen mode

Step 2: Build the Alert Functions

# telegram_alerts.py
import json
import urllib.request
import urllib.error
from datetime import datetime

TELEGRAM_API = "https://api.telegram.org/bot{token}/{method}"

def send_message(token: str, chat_id: str, text: str, 
                  parse_mode: str = "Markdown") -> bool:
    """Send a Telegram message."""
    url = TELEGRAM_API.format(token=token, method="sendMessage")
    payload = {
        "chat_id": chat_id,
        "text": text,
        "parse_mode": parse_mode,
        "disable_web_page_preview": True,
    }

    data = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(
        url, data=data,
        headers={"Content-Type": "application/json"}
    )

    try:
        with urllib.request.urlopen(req, timeout=10) as resp:
            result = json.loads(resp.read())
            return result.get("ok", False)
    except urllib.error.HTTPError as e:
        print(f"Telegram error: {e.code}{e.read().decode()}")
        return False
    except Exception as e:
        print(f"Telegram error: {e}")
        return False


def send_price_alert(token, chat_id, symbol, price, change_24h, signal=None):
    """Send a formatted price alert."""
    direction = "📈" if change_24h > 0 else "📉"
    signal_line = f"\n⚡ *Signal:* {signal}" if signal else ""

    text = f"""
{direction} *{symbol} Price Alert*
💰 Price: `${price:,.2f}`
📊 24h Change: `{change_24h:+.2f}%`
🕐 Time: `{datetime.utcnow().strftime('%H:%M UTC')}`{signal_line}
    """.strip()

    return send_message(token, chat_id, text)


def send_rsi_alert(token, chat_id, symbol, rsi, price, condition):
    """Send RSI-based alert."""
    if condition == "oversold":
        emoji = "🟢"
        label = "OVERSOLD (Potential Buy)"
    elif condition == "overbought":
        emoji = "🔴"
        label = "OVERBOUGHT (Potential Sell)"
    else:
        emoji = ""
        label = condition.upper()

    text = f"""
{emoji} *RSI Alert — {symbol}*
📊 RSI(14): `{rsi:.1f}` — {label}
💰 Price: `${price:,.2f}`
🕐 Time: `{datetime.utcnow().strftime('%H:%M UTC')}`

_Not financial advice. Paper trading only._
    """.strip()

    return send_message(token, chat_id, text)


def send_regime_change(token, chat_id, symbol, old_regime, new_regime, analysis):
    """Alert when market regime changes."""
    regime_emoji = {
        "BULL": "🐂",
        "BEAR": "🐻", 
        "SIDEWAYS": "↔️",
        "UNCERTAIN": "",
    }

    old_em = regime_emoji.get(old_regime, "")
    new_em = regime_emoji.get(new_regime, "")

    text = f"""
🔄 *Regime Change — {symbol}*
{old_em} {old_regime}{new_em} *{new_regime}*

📋 Analysis:
_{analysis}_

🕐 Time: `{datetime.utcnow().strftime('%Y-%m-%d %H:%M UTC')}`
    """.strip()

    return send_message(token, chat_id, text)


def send_daily_summary(token, chat_id, portfolio_value, daily_pnl, 
                        signals_today, watchlist_data):
    """Send end-of-day summary."""
    pnl_emoji = "" if daily_pnl >= 0 else ""

    watchlist_lines = []
    for item in watchlist_data[:5]:  # Max 5 in summary
        chg = item["change_24h"]
        em = "📈" if chg > 0 else "📉"
        watchlist_lines.append(f"  {em} {item['symbol']}: `${item['price']:,.2f}` ({chg:+.1f}%)")

    watchlist_text = "\n".join(watchlist_lines) if watchlist_lines else "  No data"
    signals_text = signals_today if signals_today else "None today"

    text = f"""
📋 *Daily Summary*
━━━━━━━━━━━━━━━━
{pnl_emoji} Portfolio: `${portfolio_value:,.2f}` ({daily_pnl:+.2f}% today)

📊 *Watchlist:*
{watchlist_text}

⚡ *Signals Today:* {signals_text}

🕐 `{datetime.utcnow().strftime('%Y-%m-%d %H:%M UTC')}`
    """.strip()

    return send_message(token, chat_id, text)
Enter fullscreen mode Exit fullscreen mode

Step 3: Build the Signal Monitor

Now connect the alert functions to real market data:

# signal_monitor.py
import ccxt
import pandas as pd
import ta
import os
import json
import time
from telegram_alerts import send_rsi_alert, send_regime_change, send_daily_summary
from datetime import datetime

TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
CHAT_ID = os.environ["TELEGRAM_CHAT_ID"]
STATE_FILE = "monitor_state.json"

WATCHLIST = ["BTC/USDT", "ETH/USDT", "SOL/USDT"]

def load_state():
    if os.path.exists(STATE_FILE):
        with open(STATE_FILE) as f:
            return json.load(f)
    return {"last_regimes": {}, "last_rsi_alert": {}}

def save_state(state):
    with open(STATE_FILE, "w") as f:
        json.dump(state, f, indent=2)

def run_monitor():
    exchange = ccxt.binance({"enableRateLimit": True})
    state = load_state()
    watchlist_data = []

    for symbol in WATCHLIST:
        try:
            # Fetch data
            raw = exchange.fetch_ohlcv(symbol, "4h", limit=220)
            df = pd.DataFrame(raw, columns=["ts","open","high","low","close","volume"])
            df["datetime"] = pd.to_datetime(df["ts"], unit="ms")
            df.set_index("datetime", inplace=True)

            # Calculate indicators
            df["rsi"] = ta.momentum.RSIIndicator(df["close"], 14).rsi()
            df["ema_200"] = ta.trend.EMAIndicator(df["close"], 200).ema_indicator()
            df["ema_21"] = ta.trend.EMAIndicator(df["close"], 21).ema_indicator()
            adx_obj = ta.trend.ADXIndicator(df["high"], df["low"], df["close"], 14)
            df["adx"] = adx_obj.adx()
            df.dropna(inplace=True)

            latest = df.iloc[-1]
            price = latest["close"]
            rsi = latest["rsi"]

            # Get 24h change
            raw_1d = exchange.fetch_ohlcv(symbol, "1d", limit=2)
            change_24h = (raw_1d[-1][4] - raw_1d[-2][4]) / raw_1d[-2][4] * 100

            watchlist_data.append({
                "symbol": symbol,
                "price": price,
                "change_24h": change_24h,
            })

            # RSI alerts (only if not alerted in last 6 hours)
            last_rsi = state["last_rsi_alert"].get(symbol, 0)
            now_ts = time.time()

            if now_ts - last_rsi > 21600:  # 6 hours
                if rsi < 30:
                    send_rsi_alert(TOKEN, CHAT_ID, symbol, rsi, price, "oversold")
                    state["last_rsi_alert"][symbol] = now_ts
                elif rsi > 70:
                    send_rsi_alert(TOKEN, CHAT_ID, symbol, rsi, price, "overbought")
                    state["last_rsi_alert"][symbol] = now_ts

            # Regime detection
            if latest["close"] > latest["ema_21"] > latest["ema_200"] and latest["adx"] > 25:
                current_regime = "BULL"
            elif latest["close"] < latest["ema_21"] < latest["ema_200"] and latest["adx"] > 25:
                current_regime = "BEAR"
            elif latest["adx"] < 20:
                current_regime = "SIDEWAYS"
            else:
                current_regime = "UNCERTAIN"

            old_regime = state["last_regimes"].get(symbol, "UNKNOWN")
            if old_regime != current_regime and old_regime != "UNKNOWN":
                send_regime_change(TOKEN, CHAT_ID, symbol, old_regime, current_regime,
                                   f"ADX: {latest['adx']:.1f}, RSI: {rsi:.1f}")

            state["last_regimes"][symbol] = current_regime

            time.sleep(1)  # Be nice to the exchange API

        except Exception as e:
            print(f"Error processing {symbol}: {e}")

    save_state(state)
    return watchlist_data

if __name__ == "__main__":
    data = run_monitor()
    print(f"Monitored {len(data)} symbols")
Enter fullscreen mode Exit fullscreen mode

Step 4: Schedule with OpenClaw

Add this to your OpenClaw heartbeat configuration:

# In OpenClaw skill — runs every 4 hours
def crypto_heartbeat():
    from signal_monitor import run_monitor
    watchlist_data = run_monitor()
    print(f"Monitoring complete: {len(watchlist_data)} symbols checked")
Enter fullscreen mode Exit fullscreen mode

Or run it directly as a scheduled task:

# Windows Task Scheduler (every 4 hours)
schtasks /create /tn "CryptoMonitor" /tr "python C:\path\to\signal_monitor.py" /sc hourly /mo 4
Enter fullscreen mode Exit fullscreen mode

Step 5: Add Interactive Commands

Make your bot two-way — send commands to get instant data:

def handle_commands(token, updates):
    """Process incoming Telegram commands."""
    for update in updates:
        msg = update.get("message", {})
        text = msg.get("text", "")
        chat_id = msg.get("chat", {}).get("id")

        if text == "/price":
            # Get current BTC price
            exchange = ccxt.binance({"enableRateLimit": True})
            ticker = exchange.fetch_ticker("BTC/USDT")
            send_message(token, chat_id, 
                f"BTC: `${ticker['last']:,.2f}` ({ticker['percentage']:+.2f}%)")

        elif text == "/status":
            state = load_state()
            lines = [f"*Current Regimes:*"]
            for sym, regime in state["last_regimes"].items():
                lines.append(f"  {sym}: {regime}")
            send_message(token, chat_id, "\n".join(lines))

        elif text == "/help":
            send_message(token, chat_id,
                "*Available commands:*\n/price — BTC price\n/status — Current regimes\n/help — This message")
Enter fullscreen mode Exit fullscreen mode

Alert Fatigue: Less Is More

The biggest risk with a new alert system is alert fatigue — getting so many notifications that you start ignoring them all.

Rules to avoid this:

  • RSI alerts: Maximum once per 6 hours per symbol
  • Regime alerts: Only on genuine transitions, not minor fluctuations
  • Price alerts: Set meaningful thresholds (±5%, not ±1%)
  • Daily summary: One message per day, at a fixed time
  • No "market is moving" alerts: If you're tracking 10 coins, something is always moving

The goal is that when your phone buzzes with a crypto alert, it means something real.


Get the Full System

The complete Telegram alerting setup — including command handlers, portfolio tracking, multi-coin monitoring, and daily summaries — is in the OpenClaw kit:

👉 OpenClaw Home AI Agent Kit — Full Setup Guide

Your agent runs 24/7. Your alerts should too.



🛠️ Also check out CryptoClaw Skills Hub — browse and install crypto skills for your OpenClaw agent: https://paarthurnax970-debug.github.io/cryptoclawskills/

Not financial advice. Paper trading only. Telegram alerts are for informational purposes. Always make your own trading decisions.

Top comments (0)