Tooling · 6 min read

Telegram alerts for trading bots

How to wire up a Telegram bot for trade alerts using the bot-monitor.js pattern -- bot token, chat_id, and simple POST to api.telegram.org.

Why Telegram

When a bot is running unattended overnight, you need instant notification when something goes wrong -- or right. Telegram is ideal because:

  • The API is simple (single HTTP POST, no SDK needed).
  • Messages arrive in under a second.
  • Works on mobile, desktop, and web.
  • The bot API is free with no rate limit for normal use.

Creating a bot

  1. Message @BotFather on Telegram.
  2. Send /newbot, follow the prompts, and note the bot token (format: 123456789:ABCdef...).
  3. Start a conversation with your new bot.
  4. Get your chat ID: send any message to the bot, then call https://api.telegram.org/bot<TOKEN>/getUpdates. The chat.id field in the result is your numeric chat ID.

Store both values in secrets.json:

{
  "telegramBotToken": "123456789:ABCdef...",
  "telegramChatId": "-100123456789"
}

The send function

async function sendTelegram(token, chatId, message) {
  const url = `https://api.telegram.org/bot${token}/sendMessage`
  const body = {
    chat_id: chatId,
    text: message,
    parse_mode: 'HTML',
    disable_web_page_preview: true,
  }
  const res = await fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  })
  if (!res.ok) {
    const err = await res.text()
    console.error('Telegram send failed:', err)
  }
}

HTML formatting lets you bold key numbers and add line breaks cleanly.

The bot-monitor.js pattern

Our bots separate trade execution from alerting. The bot writes structured JSON lines to a log file; bot-monitor.js tails the log and sends Telegram messages when it sees certain patterns (BUY, WIN, LOSS, EXIT).

This decoupling means:

  • The trading bot never blocks on a Telegram API call.
  • You can restart the monitor without affecting trading.
  • Multiple bots can be monitored by a single monitor process.

Log format written by bots:

function logTrade(event, data) {
  const line = JSON.stringify({
    ts: Date.now(),
    event,      // 'BUY' | 'WIN' | 'LOSS' | 'EXIT'
    ...data,
  })
  fs.appendFileSync(LOG_PATH, line + '\n')
}

Monitor reads and tails:

const { Tail } = require('tail')

function startMonitor(logPath, telegramToken, chatId) {
  const tail = new Tail(logPath)
  tail.on('line', async (line) => {
    try {
      const event = JSON.parse(line)
      const msg = formatAlert(event)
      if (msg) await sendTelegram(telegramToken, chatId, msg)
    } catch (_) {}
  })
}

function formatAlert(event) {
  if (event.event === 'BUY') {
    return `<b>BUY</b> ${event.asset} ${event.shares} shares @ $${event.price.toFixed(2)}\nMarket: ${event.market}`
  }
  if (event.event === 'WIN') {
    return `<b>WIN</b> +$${event.pnl.toFixed(2)} | ${event.asset}`
  }
  if (event.event === 'LOSS') {
    return `<b>LOSS</b> -$${Math.abs(event.pnl).toFixed(2)} | ${event.asset}`
  }
  return null
}

Direct alerts for polycopy

The polycopy bot skips the log-tail pattern and sends Telegram messages directly. When it copies a whale trade, it posts immediately:

async function alertCopiedTrade(token, chatId, whale, market, shares, price) {
  const msg = [
    `<b>COPY TRADE</b>`,
    `Whale: <code>${whale.slice(0, 8)}...</code>`,
    `Market: ${market}`,
    `${shares} shares @ $${price.toFixed(2)}`,
  ].join('\n')
  await sendTelegram(token, chatId, msg)
}

Rate limiting

Telegram limits bots to 30 messages per second to the same chat, and 20 per minute to the same group. For a bot firing many trades quickly, batch alerts or add a minimum interval:

let lastAlertTs = 0
const MIN_ALERT_INTERVAL_MS = 1000

async function throttledAlert(token, chatId, msg) {
  const now = Date.now()
  if (now - lastAlertTs < MIN_ALERT_INTERVAL_MS) {
    await sleep(MIN_ALERT_INTERVAL_MS - (now - lastAlertTs))
  }
  await sendTelegram(token, chatId, msg)
  lastAlertTs = Date.now()
}

Startup and shutdown notifications

Always send a startup message so you know when the bot (re)started, and a shutdown message on clean exit. This makes it easy to spot unexpected restarts in your Telegram history.

async function main() {
  await sendTelegram(TOKEN, CHAT_ID, `<b>BOT STARTED</b> poly5m-v4 | ${new Date().toLocaleString('fr-FR', { timeZone: 'Europe/Paris' })}`)
  process.on('SIGTERM', async () => {
    await sendTelegram(TOKEN, CHAT_ID, '<b>BOT STOPPED</b> poly5m-v4 (SIGTERM)')
    process.exit(0)
  })
  // ... bot loop
}

Summary

  • Create a bot via @BotFather, store token and chat ID in secrets.json.
  • Use a single sendTelegram(token, chatId, htmlMessage) function with parse_mode: 'HTML'.
  • Prefer the log-tail pattern for trading bots: decouple execution from alerting.
  • Send startup and shutdown notifications to track unexpected restarts.
  • Throttle to 1 message/second to stay within Telegram rate limits.

Related bots