@sha3/polymarket-snapshot
v2.2.0
Published
Flat Polymarket snapshot stream for direct persistence into ClickHouse.
Readme
@sha3/polymarket-snapshot
Flat Polymarket snapshot stream for direct persistence into ClickHouse.
TL;DR
npm install @sha3/polymarket-snapshotimport { SnapshotService } from "@sha3/polymarket-snapshot";
const snapshotService = new SnapshotService(500);
snapshotService.addSnapshotListener({
listener(snapshot) {
console.log(snapshot.generated_at, snapshot.btc_binance_price, snapshot.btc_5m_slug, snapshot.btc_5m_up_price);
},
});Why
Use this package when you want one flat snapshot object, with snake_case columns, ready to map directly into a wide ClickHouse table.
Main Capabilities
- Emits one flat snapshot that includes all configured assets and all configured windows.
- Keeps crypto fields asset-scoped and Polymarket fields asset-and-window-scoped.
- Fills
asset_window_price_to_beatcolumns as soon as Polymarket exposes them for the active market. - Includes
asset_window_market_startandasset_window_market_endas ISO timestamps for the active market. - Omits market-window columns when
generated_atis outside the market interval.
Installation
npm install @sha3/polymarket-snapshotUsage
import { SnapshotService } from "@sha3/polymarket-snapshot";
const snapshotService = new SnapshotService(500);
snapshotService.addSnapshotListener({
listener(snapshot) {
console.log(snapshot.btc_binance_price, snapshot.btc_5m_slug, snapshot.btc_5m_up_price);
},
});
const latestSnapshot = snapshotService.getSnapshot();
await snapshotService.disconnect();Examples
Read the current snapshot on demand:
import { SnapshotService } from "@sha3/polymarket-snapshot";
const snapshotService = new SnapshotService();
const snapshot = snapshotService.getSnapshot();
console.log(snapshot?.btc_5m_slug, snapshot?.btc_binance_price);
await snapshotService.disconnect();Public API
SnapshotService
new SnapshotService(snapshotIntervalMs?: number)Creates and maintains the live snapshot runtime.
addSnapshotListener()
- Registers a listener.
- Starts the shared runtime on first use.
removeSnapshotListener()
- Removes one listener.
- Stops the runtime after the last listener is removed.
getSnapshot()
- Returns the latest flat snapshot.
- Returns
nullbefore activation and after shutdown.
disconnect()
- Clears listeners.
- Closes timers, transports, and subscriptions.
AddSnapshotListenerOptions
type AddSnapshotListenerOptions = { listener: SnapshotListener };SnapshotListener
type SnapshotListener = (snapshot: Snapshot) => void;Snapshot
type Snapshot = {
generated_at: number;
} & Record<string, number | string | null>;Crypto fields are asset-scoped. The same field set appears for every configured asset prefix such as btc_*, eth_*, sol_*, and xrp_*:
type CryptoFields = {
btc_binance_price: number | null;
btc_binance_order_book_json: string | null;
btc_binance_event_ts: number | null;
btc_coinbase_price: number | null;
btc_coinbase_order_book_json: string | null;
btc_coinbase_event_ts: number | null;
btc_kraken_price: number | null;
btc_kraken_order_book_json: string | null;
btc_kraken_event_ts: number | null;
btc_okx_price: number | null;
btc_okx_order_book_json: string | null;
btc_okx_event_ts: number | null;
btc_chainlink_price: number | null;
btc_chainlink_event_ts: number | null;
};Market fields are asset-and-window-scoped. They are included only when generated_at is inside that market interval:
type LiveMarketFields = {
btc_5m_slug: string | null;
btc_5m_market_start: string | null;
btc_5m_market_end: string | null;
btc_5m_price_to_beat: number | null;
btc_5m_up_asset_id: string | null;
btc_5m_up_price: number | null;
btc_5m_up_order_book_json: string | null;
btc_5m_up_event_ts: number | null;
btc_5m_down_asset_id: string | null;
btc_5m_down_price: number | null;
btc_5m_down_order_book_json: string | null;
btc_5m_down_event_ts: number | null;
};The same market field set can appear for every configured asset_window prefix such as btc_15m_*, eth_5m_*, eth_15m_*, and so on.
Compatibility
- Node.js 20+
- ESM consumers
- Strict TypeScript projects
Configuration
Configuration lives in config.ts.
PACKAGE_NAME: package identifier used by the internal logger.ALLOWED_SNAPSHOT_INTERVALS_MS: accepted constructor values forsnapshotIntervalMs.DEFAULT_SNAPSHOT_INTERVAL_MS: default snapshot interval when the constructor receives no value.DEFAULT_SUPPORTED_ASSETS: asset prefixes included in the snapshot by default.DEFAULT_SUPPORTED_WINDOWS: Polymarket windows included in the snapshot by default.DEFAULT_PRICE_TO_BEAT_INITIAL_DELAY_MS: initial wait before the firstprice_to_beatlookup for a newly activated market.DEFAULT_PRICE_TO_BEAT_RETRY_INTERVAL_MS: retry wait whenprice_to_beatis still unavailable.MARKET_BOUNDARY_DELAY_MS: delay applied after a market boundary before reloading the next market.MARKET_ACTIVATION_RETRY_INTERVAL_MS: retry delay after a market activation failure.
Scripts
npm run standards:checknpm run lintnpm run format:checknpm run typechecknpm run testnpm run checknpm run test:real-snapshot: runs the optional live console dashboard for 60 seconds
Structure
src/snapshot/: snapshot runtime and flattening logicsrc/config.ts: package configurationsrc/index.ts: public exportstest/snapshot.test.ts: deterministic snapshot behaviortest/real-snapshot.test.ts: optional live integration check
Troubleshooting
If getSnapshot() returns null, add at least one listener first. The runtime is lazy and does not start until a listener is registered.
If market columns such as btc_5m_slug or btc_5m_up_price are missing, generated_at is outside the current market interval for that asset/window.
If you want to inspect the live stream visually, run npm run test:real-snapshot. It renders a small console dashboard with crypto prices, current market slugs, market start/end values, price_to_beat, and up/down prices.
If npm run check fails, run npm run fix and then rerun npm run check.
AI Workflow
- Read
AGENTS.md,ai/contract.json, and the active assistant adapter before editing code. - Do not edit managed files under
ai/orprompts/during normal feature work. - Run
npm run standards:checkbefore and after implementation when feasible. - Finish with
npm run check.
