How I Built a Free Real-Time Stock Alert System with Finnhub WebSockets

Last month I got burned. I set a limit order on NVDA at $118, went to make coffee, and came back to find it had dipped to $117.40 and bounced back to $122 in under three minutes. My broker’s mobile notification arrived 47 seconds late. That’s when I decided to build my own alert system.

Why Most Free Alert Tools Are Garbage

I tried Yahoo Finance alerts, TradingView free tier, and Robinhood notifications. They all have the same problem: polling intervals. Yahoo checks every 15 minutes. TradingView free gives you delayed data. Robinhood’s push notifications route through Apple/Google’s notification servers, adding 10-40 seconds of latency.

What I wanted: sub-second price alerts delivered directly to my phone via Telegram. No middlemen, no delays, no monthly fees.

The Stack: Finnhub WebSocket + Node.js + Telegram Bot

Finnhub offers a free WebSocket API that streams real-time US stock trades. Free tier gives you 30 symbols simultaneously โ€” more than enough for an active watchlist. The connection stays open, so you get trade data the moment it hits the exchange.

Here’s the core of the alert engine:

const WebSocket = require('ws');
const socket = new WebSocket('wss://ws.finnhub.io?token=YOUR_FREE_KEY');

const alerts = {
  NVDA: { below: 118, above: 135 },
  AAPL: { below: 185, above: 200 },
  TSLA: { below: 160, above: 190 }
};

// Cooldown map โ€” don't spam yourself
const lastFired = {};
const COOLDOWN_MS = 300000; // 5 minutes between repeat alerts

socket.on('open', () => {
  Object.keys(alerts).forEach(sym => {
    socket.send(JSON.stringify({ type: 'subscribe', symbol: sym }));
  });
});

socket.on('message', (data) => {
  const msg = JSON.parse(data);
  if (msg.type !== 'trade') return;

  for (const trade of msg.data) {
    const { s: symbol, p: price, t: timestamp } = trade;
    const rule = alerts[symbol];
    if (!rule) continue;

    const key = symbol + (price < rule.below ? '_low' : '_high');
    if (lastFired[key] && Date.now() - lastFired[key] < COOLDOWN_MS) continue;

    if (price <= rule.below || price >= rule.above) {
      sendTelegramAlert(symbol, price, timestamp);
      lastFired[key] = Date.now();
    }
  }
});

Latency Numbers: How Fast Is This Really?

I ran this for two weeks and measured end-to-end latency โ€” from Finnhub WebSocket message received to Telegram notification on my phone:

  • Finnhub WebSocket delivery: 50-200ms from exchange (they aggregate trades into small batches)
  • Alert logic processing: <1ms
  • Telegram Bot API send: 100-400ms
  • Telegram push to phone: 200-800ms

Total: 350ms to 1.4 seconds. Compare that to Robinhood’s 10-47 second delay. I’m getting alerts before my broker even knows the price moved.

The Telegram Delivery Piece

const BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
const CHAT_ID = process.env.TELEGRAM_CHAT_ID;

async function sendTelegramAlert(symbol, price, timestamp) {
  const direction = price <= alerts[symbol].below ? '๐Ÿ”ป BELOW' : '๐Ÿ”บ ABOVE';
  const text = `${direction} target\n${symbol}: $${price.toFixed(2)}\nTime: ${new Date(timestamp).toLocaleTimeString()}`;

  await fetch(`https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ chat_id: CHAT_ID, text, parse_mode: 'HTML' })
  });
}

I run this on a $4/month VPS. It uses about 12MB of RAM and essentially zero CPU when idle. The WebSocket connection stays alive with Finnhub’s built-in ping/pong frames.

Gotchas I Hit Along the Way

WebSocket disconnects. Finnhub drops connections after 24 hours of inactivity on free tier. I added a reconnection handler with exponential backoff โ€” starts at 1 second, maxes at 30 seconds. Haven’t missed an alert since.

Trade volume spikes. During market open (9:30-9:35 AM ET), you’ll get thousands of messages per second for popular tickers. My first version tried to check every single trade and fell behind. The fix: only check the last trade in each batch, since you only care about the current price, not every fill.

Extended hours data. Finnhub free tier includes pre-market and after-hours trades. I initially got woken up at 4 AM by a pre-market alert. Added a time filter โ€” alerts only fire 9:30 AM to 4:00 PM ET unless I explicitly mark a symbol as “watch extended.”

Running It on a Raspberry Pi

If you don’t want to pay for a VPS, this runs perfectly fine on a Raspberry Pi 4. I tested it on a Pi Zero 2 W and it used 18MB RAM. Set it up as a systemd service and forget about it. The only requirement is a stable internet connection โ€” if your home internet drops, you’ll miss alerts until it reconnects.

For reliability, I run mine on a Raspberry Pi 5 (affiliate link) with a UPS hat. Total hardware cost: about $80, then it runs forever with zero monthly fees.

Extending It: Multi-Condition Alerts

The basic version watches for price levels. But since you have real-time trade data, you can build more interesting triggers:

// Volume spike detection โ€” alert when 5x normal volume hits in 1 minute
const volumeWindow = {}; // { symbol: [{ ts, vol }] }

function checkVolumeSpike(symbol, volume, timestamp) {
  if (!volumeWindow[symbol]) volumeWindow[symbol] = [];
  volumeWindow[symbol].push({ ts: timestamp, vol: volume });

  // Keep only last 60 seconds
  const cutoff = timestamp - 60000;
  volumeWindow[symbol] = volumeWindow[symbol].filter(t => t.ts > cutoff);

  const totalVol = volumeWindow[symbol].reduce((s, t) => s + t.vol, 0);
  // Compare against historical 1-min average (pre-calculated)
  if (totalVol > historicalAvg[symbol] * 5) {
    sendTelegramAlert(symbol, price, timestamp, '๐Ÿ“Š VOLUME SPIKE 5x');
  }
}

I’ve caught three earnings pre-announcements this way โ€” unusual volume 10-15 minutes before news hits the wire.

Why Not Just Pay for a Service?

Fair question. TradingView Pro at $15/month gives you real-time alerts. But it caps you at 20 active alerts, and the notification delivery is still through their servers. My system has no alert limit (just the 30 symbol cap on Finnhub free), and I control the entire delivery pipeline.

Plus, once you have the WebSocket connection, you can build anything on top: a dedicated dashboard on a small monitor (affiliate link), automated trading via Alpaca’s API, or even voice alerts through a smart speaker.

The full source code is about 150 lines. I keep it in a single file with a JSON config for my watchlist. No frameworks, no dependencies beyond the ws package.

Getting Started

Sign up for a free Finnhub API key at finnhub.io โ€” takes 30 seconds. Create a Telegram bot via @BotFather. Clone the script, set your environment variables, run node alerts.js. You’ll have real-time stock alerts in under 10 minutes.

If you want pre-built market intelligence without setting up infrastructure, join Alpha Signal on Telegram โ€” I publish free daily market analysis including the signals this system helps me spot.

๐Ÿ“ง Get weekly insights on security, trading, and tech. No spam, unsubscribe anytime.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Also by us: StartCaaS — AI Company OS · Hype2You — AI Tech Trends