Telegram Alerts for Trading Bots: A Notification System Design

🌐 한국어

Trading bots usually run headless, quietly, in a minimized console. The problem is you have no real-time sense of what's happening. Did it enter? Did it get stopped out? Did an order fail? You can't open a log file every time you want to know. Telegram alerts solve this at the lowest possible cost — you make the bot talk to your phone directly.

You Don't Need an SDK — It's Just HTTP

Telegram bot notifications don't require a heavy library. One form POST to sendMessage with your bot token is the entire thing. The client is stateless, so concurrent calls are safe.

type Telegram struct {

    Token  string

    ChatID string

    client *http.Client

}

func (t *Telegram) Enabled() bool {

    return t.Token != "" && t.ChatID != ""

}

func (t *Telegram) Send(text string) error {

    // Unconfigured -> fall back quietly to logs; the bot runs without alerts

    if !t.Enabled() {

        log.Printf("[telegram] not configured — logging instead: %s", text)

        return nil

    }

    apiURL := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", t.Token)

    params := url.Values{

        "chat_id":    {t.ChatID},

        "text":       {text},

        "parse_mode": {"HTML"}, // enables <b>, <code> formatting

    }

    resp, err := t.client.PostForm(apiURL, params)

    if err != nil {

        return fmt.Errorf("telegram send failed: %w", err)

    }

    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {

        return fmt.Errorf("telegram HTTP %d", resp.StatusCode)

    }

    return nil

}

One key design decision: with no token, fall back silently to logging. The bot has to run fine whether or not you've set up alerts. Notifications are a convenience, not a survival requirement.

Alerts Must Never Block the Bot — Send Asynchronously

Here's where beginners slip. Send an alert synchronously inside the trading loop and a slow Telegram server stalls the whole bot for exactly that long. The market doesn't wait. So alerts are always fire-and-forget — thrown into a separate goroutine and forgotten.

func (t *Telegram) sendAsync(text string) {

    go func() {

        if err := t.Send(text); err != nil {

            log.Printf("[telegram] send error: %v", err)

        }

    }()

}

A failed send doesn't touch the trading logic. The failure goes to the log and the bot carries on with what it was doing.

Per-Event Alerts — What to Send and When

Alert on everything and you get notification fatigue. Pick only the decisive moments and build a type for each: entry, exit, error, and informational.

// Entry alert — direction/leverage/entry/TP/SL at a glance

func (t *Telegram) SendEntry(symbol, direction string,

    entry, sl, tp float64, leverage int) {

    emoji := "📈"

    if direction == "SHORT" {

        emoji = "📉"

    }

    msg := fmt.Sprintf(

        "%s <b>[ENTRY]</b> %s %s (x%d)\n"+

            "Entry: <code>%.6g</code>\n"+

            "TP: <code>%.6g</code> / SL: <code>%.6g</code>",

        emoji, symbol, direction, leverage, entry, tp, sl)

    t.sendAsync(msg)

}

// Exit alert — flip the emoji/sign with the P&L so it reads at a glance

func (t *Telegram) SendExit(symbol, direction string,

    pnlUSD, pnlRatio float64, hold time.Duration, reason string) {

    emoji, sign := "✅", "+"

    if pnlUSD < 0 {

        emoji, sign = "❌", ""

    }

    msg := fmt.Sprintf(

        "%s <b>[EXIT]</b> %s %s\n"+

            "P&L: <code>%s%.4f USDT (%s%.2f%%)</code>\n"+

            "Reason: <code>%s</code>",

        emoji, symbol, direction, sign, pnlUSD, sign, pnlRatio*100, reason)

    t.sendAsync(msg)

}

func (t *Telegram) SendError(errMsg string) {

    t.sendAsync(fmt.Sprintf("⚠️ <b>[ERROR]</b>\n<code>%s</code>", errMsg))

}

Using HTML parse mode lets you emphasize with <b> and render numbers in a monospace font with <code>, which reads far better on a phone.

The Alert That Matters Most — "the Stop-Loss Failed"

The most dangerous situation in an on-chain or exchange bot is a forced exit or stop-loss order failing silently. The position is open and the safety net isn't attached. That failure has to be reported immediately and unmissably.

if err := strat.Close(symbol, "stop-loss"); err != nil {

    // A failed exit must never pass quietly — top-priority alert

    tg.SendError(fmt.Sprintf("EXIT FAILED! %s needs manual check: %v", symbol, err))

}

Summary

  • Telegram alerts need one sendMessage POST, no SDK
  • With no token configured, fall back to logs — alerts aren't a survival requirement for the bot
  • Always send asynchronously (goroutine) — notifications must not block the trading loop
  • Build separate types for entry, exit, and error, and give failed exits and stop-losses top priority

A good notification system doesn't tell you everything — it tells you only what you need to look at right now. Get that boundary right and you can trust the bot enough to take your hands off it.

This article is for educational and informational purposes and is not investment advice. Automated trading carries a risk of principal loss, and the outcomes are your own responsibility.

댓글

이 블로그의 인기 게시물

한국투자증권 KIS API로 실시간 시세 받기 (WebSocket 실전)

파이썬으로 업비트 API 연동하기 — 시세 조회부터 주문까지 기초

Go로 자동매매 신호봇 프레임워크 설계하기