On the wire · 14:56:29 UTC Live 119 · Prematch 8,509 · Markets 34,184 Vol. I · No. 1 · Monday 9 November 2026 wss://pinnfeed.io/v1/stream
PinnFeed
Anleitung · Telegram-Bot · Node

Einen Telegram-Tipp-Bot mit Pinnacle-Live-Quoten bauen

Aktualisiert am · PinnFeed · 4 Min. Lesezeit

Dieser Bot beobachtet Pinnacles Preise im PinnFeed-WebSocket und postet eine Nachricht in einen Telegram-Kanal, sobald ein Markt Steam zeigt, also wenn seine Quote in zwei Minuten um 5 % oder mehr fällt, in einem Markt mit vernünftigem Maximaleinsatz. Es ist eine einzige Datei mit rund 70 Zeilen, ohne Telegram-Bibliothek: Sie ruft die Bot API direkt auf.

Schritt 1 — Den Bot anlegen#

  1. Öffne in Telegram @BotFather, sende /newbot und folge den Anweisungen. Kopiere das Token.
  2. Leg einen Kanal an und füge den Bot als Administrator mit Berechtigung zum Posten hinzu.
  3. Nutze den öffentlichen Kanalnamen (@mychannel) als Chat-id oder die numerische id für einen privaten Chat.

Schritt 2 — Die Umgebung einrichten#

export PINNFEED_KEY="pf_live_..."        # Builder-Plan (WebSocket)
export TELEGRAM_TOKEN="123456:ABC..."    # von @BotFather
export TELEGRAM_CHAT="@mychannel"
npm install ws

Lässt du TELEGRAM_TOKEN weg, läuft der Bot im Testmodus: Er gibt die Nachrichten aus, statt sie zu senden.

Schritt 3 — Der Bot#

Als telegram-picks-bot.mjs speichern:

// telegram-picks-bot.mjs: postet Pinnacle-Steam-Moves als "Picks" in einen Telegram-Kanal.
// npm install ws    Node 18+ (global fetch)
// Umgebung: PINNFEED_KEY, TELEGRAM_TOKEN (von @BotFather), TELEGRAM_CHAT (z. B. @mychannel oder eine Chat-id)
import WebSocket from 'ws';

const WS_URL = process.env.PINNFEED_WS || 'wss://pinnfeed.io/v1/stream';
const { PINNFEED_KEY, TELEGRAM_TOKEN, TELEGRAM_CHAT } = process.env;
const MIN_DROP = Number(process.env.MIN_DROP || 0.05);   // 5 % Quotensturz...
const WINDOW_MS = Number(process.env.WINDOW_MS || 120000); // ...innerhalb von 2 Minuten
const SIDES = new Set(['home', 'away', 'draw', 'over', 'under']);
const events = new Map();
const highs = new Map();   // "marketId|outcome" -> [{t, p}]
const sent = new Map();    // "marketId|outcome" -> Zeit des letzten Alarms

async function post(text) {
  if (!TELEGRAM_TOKEN) return console.log('[dry run]', text);
  const r = await fetch(`https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage`, {
    method: 'POST', headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ chat_id: TELEGRAM_CHAT, text, disable_web_page_preview: true }),
  });
  if (!r.ok) console.error('telegram', r.status, await r.text());
}

function check(m) {
  const ev = events.get(m.eventId);
  if (!ev || (m.maxStake ?? 0) < 500) return;
  const now = Date.now();
  for (const o of m.outcomes) {
    if (!SIDES.has(o.name)) continue;
    const k = `${m.id}|${o.name}`;
    const h = (highs.get(k) || []).filter((x) => now - x.t < WINDOW_MS);
    h.push({ t: now, p: o.decimal });
    highs.set(k, h);
    const high = Math.max(...h.map((x) => x.p));
    const drop = 1 - o.decimal / high;
    if (drop < MIN_DROP || now - (sent.get(k) || 0) < WINDOW_MS) continue;
    sent.set(k, now);
    const pick = o.name === 'home' ? ev.home : o.name === 'away' ? ev.away : o.name;
    const line = o.points == null ? '' : m.type === 'total' ? ` ${o.points}` : ` ${o.points > 0 ? '+' : ''}${o.points}`;
    post(`📉 ${ev.sport.toUpperCase()} · ${ev.league?.name ?? ''}\n${ev.home} v ${ev.away}\n` +
      `Pick: ${pick} (${m.type}${line})\nPinnacle ${high.toFixed(2)} → ${o.decimal.toFixed(2)} (−${(drop * 100).toFixed(1)}%)\n` +
      `Fair now ${o.fair.toFixed(2)} · max stake ${m.maxStake}`);
  }
}

function connect(delay = 1000) {
  const ws = new WebSocket(WS_URL, { headers: { 'x-api-key': PINNFEED_KEY }, perMessageDeflate: true });
  ws.on('open', () => {
    delay = 1000;
    ws.send(JSON.stringify({ type: 'subscribe', sports: ['soccer', 'tennis', 'basketball'], markets: ['moneyline', 'spread', 'total'], main: true, specials: false }));
  });
  ws.on('message', (raw) => {
    const msg = JSON.parse(raw);
    if (msg.type === 'ping') return ws.send('{"type":"pong"}');
    if (msg.type === 'snapshot') msg.events.forEach((e) => events.set(e.id, e));
    if (msg.type === 'delta') for (const c of msg.changes) {
      if (c.kind === 'event') c.op === 'upsert' ? events.set(c.event.id, c.event) : events.delete(c.id);
      if (c.kind === 'market' && c.op === 'upsert') check(c.market);
    }
  });
  ws.on('close', () => setTimeout(() => connect(Math.min(delay * 2, 30000)), delay));
  ws.on('error', (e) => console.error('ws error:', e.message));
}
connect();

Schritt 4 — Starten und testen#

node telegram-picks-bot.mjs

Eine Nachricht sieht so aus:

📉 SOCCER · Italy - Serie A
Udinese v Fiorentina
Pick: Udinese (spread +0.25)
Pinnacle 1.88 → 1.70 (−9.5%)
Fair now 1.83 · max stake 500

Die Menge steuerst du mit MIN_DROP und WINDOW_MS, zum Beispiel MIN_DROP=0.08 für weniger, dafür stärkere Alerts.

Schritt 5 — Dauerhaft betreiben#

  • Betreibe ihn mit systemd, pm2 oder einem Container, der bei Absturz neu startet; der Bot verbindet sich selbst wieder mit dem WebSocket.
  • Telegram erlaubt rund 20 Nachrichten pro Minute und Gruppe oder Kanal. Erhöhe MIN_DROP, wenn du 429-Antworten bekommst.
  • Veröffentliche deine Bilanz ehrlich: Protokolliere jeden Pick und miss ihn am Closing Line Value.

Häufige Fragen#

Welchen PinnFeed-Plan braucht der Bot?#

Builder (99 €/Monat) oder höher, weil er den WebSocket nutzt. Für einen langsameren Bot mit Free oder Hobby pollst du /v1/drops jede Minute.

Kann ich die Picks auch in Discord posten?#

Ja. Ersetze die Funktion post durch einen POST an die URL eines Discord-Webhooks mit dem Body {"content": text}.

Ist es erlaubt, Tipps zu veröffentlichen?#

Die Regeln für Wett-Tipps und Glücksspielwerbung unterscheiden sich von Land zu Land; in Deutschland regelt etwa der Glücksspielstaatsvertrag 2021 auch die Werbung für Sportwetten. Prüfe die Regeln dort, wo du und dein Publikum seid, richte dich nur an Erwachsene ab 18 Jahren und stelle Picks nie als sicher dar. Das ist keine Rechtsberatung.

Der WebSocket-Plan: Builder, 99 €/Monat.

Pinnacle-Quoten in Echtzeit über einen einzigen WebSocket: alle 13 Sportarten, alle Märkte, faire Quoten ohne Marge und Schlussquoten. 3 Tage kostenlos, mit einem Klick kündbar.

Form K-1 · Free API key

Get your free API key.

No password. We email you a link; click it and your key is on screen.

One free key per email. Link valid for 20 minutes.