On the wire · 04:48:25 UTC Live 550 · Prematch 14,373 · Markets 56,999 Vol. I · No. 5 · Friday 13 November 2026 wss://pinnfeed.io/v1/stream
PinnFeed
Antwort · Postgres · Speicherung

Pinnacle-Quoten in Postgres speichern

Aktualisiert am · PinnFeed · 2 Min. Lesezeit

Antwort: führe eine reine Anhänge-Tabelle mit Preisticks, geschlüsselt nach Markt-ID, Ausgang und Zeitstempel, und schreibe jedes upsert eines Markts aus dem PinnFeed-WebSocket hinein. Snapshots liefern den Startzustand, delta-Frames jede Änderung. Später kannst du Zeilen aus /v1/closing für den CLV ergänzen. Funktioniert mit jedem Postgres (selbst gehostet, Supabase, Neon).

Schema#

create table if not exists odds_ticks (
  ts          timestamptz not null default now(),
  event_id    bigint      not null,
  market_id   text        not null,
  market      text        not null,   -- moneyline | spread | total | team_total
  period      int,
  line        numeric,
  outcome     text        not null,
  decimal     numeric     not null,
  fair        numeric,
  max_stake   numeric,
  primary key (market_id, outcome, ts)
);
create index if not exists odds_ticks_event on odds_ticks (event_id, ts);

Node-Worker (Builder)#

// npm i ws pg   ·   PINNFEED_KEY=ps_live_... DATABASE_URL=postgres://... node worker.js
import WebSocket from 'ws';
import pg from 'pg';

const db = new pg.Pool({ connectionString: process.env.DATABASE_URL });
const sql = `insert into odds_ticks (event_id, market_id, market, period, line, outcome, decimal, fair, max_stake)
             values ($1,$2,$3,$4,$5,$6,$7,$8,$9) on conflict do nothing`;

async function save(m) {
  for (const o of m.outcomes || []) {
    await db.query(sql, [m.eventId, m.id, m.type, m.period, m.line ?? null, o.name, o.decimal, o.fair ?? null, m.maxStake ?? null]);
  }
}

function connect() {
  const ws = new WebSocket('wss://pinnfeed.io/v1/stream', { headers: { 'x-api-key': process.env.PINNFEED_KEY } });
  ws.on('open', () => ws.send(JSON.stringify({ type: 'subscribe', sports: ['soccer'], markets: ['moneyline', 'total'], main: true })));
  ws.on('message', async (raw) => {
    const msg = JSON.parse(raw);
    if (msg.type === 'ping') return ws.send(JSON.stringify({ type: 'pong' }));
    if (msg.type === 'snapshot') for (const m of msg.markets || []) await save(m);
    if (msg.type === 'delta') for (const ch of msg.changes) if (ch.kind === 'market' && ch.op === 'upsert') await save(ch.market);
  });
  ws.on('close', (code) => { console.log('closed', code); setTimeout(connect, 2000); });
}
connect();

Wenn die Inserts hinter dem Stream zurückbleiben, kann der Server mit 4008 slow_consumer schließen: Inserts bündeln (oder in eine Queue legen) und weniger Märkte abonnieren. Vollständiger Reconnect: Reconnect in Node.

Beispielabfragen#

-- last price per outcome
select distinct on (market_id, outcome) market_id, outcome, decimal, ts
from odds_ticks order by market_id, outcome, ts desc;

Nutze die Tabelle für Backtests gegen Schlussquoten und Charts der Linienbewegung.

Tarife#

Streaming braucht Builder (99 €/Monat) oder Pro; die Schlussquoten-Historie ist in Builder enthalten. Mit Free oder Hobby kannst du /v1/odds per Polling in dieselbe Tabelle schreiben. Testphase 3 Tage; eine Karte ist nötig und wird nach Tag 3 belastet, wenn du nicht kündigst.

Bildungsinhalt für Erwachsene (18+). Daten für Forschung, keine Wettberatung. In Deutschland nur bei Anbietern auf der GGL-Whitelist wetten.

Häufige Fragen#

Soll ich den Snapshot speichern?#

Ja, als erste Zeile je Ausgang. Sonst enthält die Tabelle nur Preise, die sich nach dem Verbinden geändert haben.

Wie vermeide ich Duplikate nach einem Reconnect?#

Der Snapshot nach dem Reconnect wiederholt die aktuellen Preise; on conflict do nothing mit einem Zeitschlüssel speichert sie als neue Ticks. Willst du nur Änderungen, vergleiche vor dem Insert mit dem zuletzt gespeicherten Preis.

TimescaleDB?#

Funktioniert genauso: mach odds_ticks zu einer Hypertable auf ts.

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.

One field. No password, no card: enter your email, click the link we send, and your key is on screen.

By continuing you agree to the Terms and Privacy Policy.

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