On the wire · 04:48:11 UTC Live 550 · Prematch 14,373 · Markets 57,037 Vol. I · No. 5 · Friday 13 November 2026 wss://pinnfeed.io/v1/stream
PinnFeed
Risposta · Postgres · Storage

Memorizzare le quote Pinnacle in Postgres

Aggiornato il · PinnFeed · 2 min di lettura

Risposta: tieni una tabella append-only di tick di prezzo con chiave market id, esito e timestamp, e scrivi ogni upsert market del WebSocket PinnFeed. Gli snapshot danno lo stato iniziale; i frame delta ogni cambio. Aggiungi poi le righe /v1/closing per il CLV. Funziona con qualsiasi Postgres (self-hosted, 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);

Worker Node (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();

Se gli insert restano indietro rispetto allo stream, il server può chiudere con 4008 slow_consumer; raggruppa gli insert (o mettili in coda) e iscriviti a pochi mercati. Riconnessione completa: riconnessione Node.

Esempi di query#

-- 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;

Usa la tabella per backtest contro le closing line e grafici di movimento linea (EN).

Piani#

Lo streaming richiede Builder (99 €/mese) o Pro; lo storico di chiusura è incluso in Builder. Su Free o Hobby puoi fare polling di /v1/odds nella stessa tabella. Piani a pagamento: prova di 3 giorni; carta richiesta, addebitata dopo il giorno 3 salvo disdetta.

Contenuto educativo per maggiorenni (18+): non pronostici né promesse di guadagno. In Italia scommetti solo con operatori autorizzati ADM.

Domande frequenti#

Devo memorizzare lo snapshot?#

Sì, come prima riga per esito. Altrimenti la tabella tiene solo i prezzi cambiati dopo la connessione.

Come evitare i duplicati dopo una riconnessione?#

Lo snapshot dopo la riconnessione ripete i prezzi correnti; on conflict do nothing con chiave timestamp li tiene come nuovi tick. Se vuoi solo i cambi, confronta con l'ultimo prezzo memorizzato prima di inserire.

TimescaleDB?#

Stesso approccio; rendi odds_ticks un'hypertable su ts.

Il piano WebSocket: Builder, 99 €/mese.

Quote Pinnacle in tempo reale su un unico WebSocket: tutti i 13 sport, tutti i mercati, quote eque senza margine e quote di chiusura. 3 giorni gratis, disdici con un clic.

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.