polaris-data 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/errors.ts","../src/utils.ts","../src/storage.ts","../src/aggregator.ts"],"sourcesContent":["// Client\nexport { PolarisClient } from \"./client\";\n\n// Errors\nexport {\n PolarisError,\n UnauthorizedError,\n NotFoundError,\n RateLimitedError,\n StreamDecodeError,\n DownloadNotAllowedError,\n} from \"./errors\";\n\n// Aggregator (for advanced local use)\nexport { OhlcvAggregator } from \"./aggregator\";\n\n// Types – re-export everything so consumers can import types in one place\nexport type {\n TimeInput,\n FetchLike,\n AuthMode,\n PaginatedResponse,\n CatalogResponse,\n CatalogSource,\n CatalogMarket,\n StandardEvent,\n TradeData,\n TradeEvent,\n OhlcvInterval,\n OhlcvBar,\n TradingViewCandle,\n TradingViewVolume,\n TradingViewOhlcvResponse,\n SnapshotEntry,\n SnapshotsResponse,\n DownloadUrlResponse,\n PolarisClientOptions,\n CatalogOptions,\n HistoricalQueryOptions,\n RawQueryOptions,\n OhlcvOptions,\n ListSnapshotsOptions,\n ReplayOptions,\n DownloadSnapshotOptions,\n} from \"./types\";\n","import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\nimport { decompress } from \"fzstd\";\n\nimport type {\n AuthMode,\n CatalogOptions,\n CatalogResponse,\n DownloadSnapshotOptions,\n DownloadUrlResponse,\n FetchLike,\n HistoricalQueryOptions,\n ListSnapshotsOptions,\n OhlcvBar,\n OhlcvOptions,\n PaginatedResponse,\n PolarisClientOptions,\n RawQueryOptions,\n ReplayOptions,\n SnapshotEntry,\n TradingViewOhlcvResponse,\n} from \"./types\";\n\nimport {\n PolarisError,\n UnauthorizedError,\n NotFoundError,\n RateLimitedError,\n} from \"./errors\";\n\nimport { toIso8601, toEpochUs, datesInRange } from \"./utils\";\nimport {\n resolveRoot,\n ensureLayout,\n dataFilePath,\n dailyFilePath,\n linkOrCopy,\n fileExists,\n type StorageLayout,\n} from \"./storage\";\nimport { OhlcvAggregator } from \"./aggregator\";\n\n// ---------------------------------------------------------------------------\n// SDK version – bumped manually during releases\n// ---------------------------------------------------------------------------\n\nconst VERSION = \"0.1.0\";\n\n// ---------------------------------------------------------------------------\n// Internal shorthand\n// ---------------------------------------------------------------------------\n\ntype Json = Record<string, unknown>;\n\ninterface FetchOptions {\n params?: Record<string, string>;\n auth?: AuthMode;\n headers?: Record<string, string>;\n}\n\n// ===========================================================================\n// PolarisClient\n// ===========================================================================\n\nexport class PolarisClient {\n private readonly _apiKey: string | undefined;\n private readonly _baseUrl: URL;\n private readonly _timeout: number;\n private readonly _fetch: FetchLike;\n private readonly _root: string;\n private _layout: StorageLayout | undefined;\n\n constructor(options: PolarisClientOptions = {}) {\n this._apiKey = options.apiKey ?? readEnvApiKey();\n this._baseUrl = new URL(options.baseUrl ?? \"https://api.polaris.supply\");\n this._timeout = options.timeout ?? 30_000;\n this._fetch = options.fetch ?? globalThis.fetch;\n this._root = resolveRoot(options.datasetRoot);\n }\n\n // -----------------------------------------------------------------------\n // Discovery\n // -----------------------------------------------------------------------\n\n /** Check API availability. */\n async health(): Promise<Json> {\n return this._getJson(\"/health\", { auth: \"none\" });\n }\n\n /**\n * Browse supported sources and markets.\n *\n * If neither `source` nor `market` is provided, returns the full catalog.\n * `market` requires `source`.\n */\n async catalog(options: CatalogOptions = {}): Promise<CatalogResponse> {\n const params: Record<string, string> = {};\n if (options.source) params.source = options.source;\n if (options.market) params.market = options.market;\n return this._getJson(\"/catalog\", { params, auth: \"if-available\" });\n }\n\n /**\n * List available snapshot files for a source and market over a time range.\n * Auto-paginates to return **all** matching entries.\n */\n async listSnapshots(options: ListSnapshotsOptions): Promise<SnapshotEntry[]> {\n const entries: SnapshotEntry[] = [];\n let cursor: string | undefined;\n\n do {\n const params = buildSnapshotParams(options);\n if (cursor) params.cursor = cursor;\n\n const res = await this._getJson<{\n snapshots: SnapshotEntry[];\n next_cursor: string | null;\n has_more: boolean;\n }>(\"/snapshots\", { params, auth: \"if-available\" });\n\n entries.push(...res.snapshots);\n cursor = res.has_more ? (res.next_cursor ?? undefined) : undefined;\n } while (cursor);\n\n return entries;\n }\n\n // -----------------------------------------------------------------------\n // Historical data – snapshot-first\n // -----------------------------------------------------------------------\n\n /**\n * Return all standardised historical events for a time range.\n *\n * Reads from locally-cached daily `.jsonl.zst` snapshot files.\n * Missing daily artifacts are discovered via `GET /snapshots` and\n * downloaded automatically.\n */\n async events(options: HistoricalQueryOptions): Promise<Json[]> {\n const fromUs = toEpochUs(options.from);\n const toUs = toEpochUs(options.to);\n const result: Json[] = [];\n for await (const event of this._readDailyEvents(\n options.source,\n options.market,\n fromUs,\n toUs,\n )) {\n result.push(event);\n }\n return result;\n }\n\n /**\n * Return all standardised trade events for a time range.\n *\n * Reads from locally-cached daily snapshot files, filtering to\n * `type === \"trade\"`.\n */\n async trades(options: HistoricalQueryOptions): Promise<Json[]> {\n const fromUs = toEpochUs(options.from);\n const toUs = toEpochUs(options.to);\n const result: Json[] = [];\n for await (const event of this._readDailyEvents(\n options.source,\n options.market,\n fromUs,\n toUs,\n (e) => e.type === \"trade\",\n )) {\n result.push(event);\n }\n return result;\n }\n\n /**\n * Aggregate OHLCV bars from standardised trade data.\n *\n * Reads from locally-cached daily snapshot files and aggregates in memory\n * using the same interval-bucketing strategy as the Python SDK.\n */\n async ohlcv(options: OhlcvOptions): Promise<OhlcvBar[]> {\n const fromUs = toEpochUs(options.from);\n const toUs = toEpochUs(options.to);\n const agg = new OhlcvAggregator(options.interval);\n\n for await (const event of this._readDailyEvents(\n options.source,\n options.market,\n fromUs,\n toUs,\n (e) => e.type === \"trade\",\n )) {\n const data = event.data as { price: number; quantity: number };\n agg.add(event.timestamp as number, data.price, data.quantity);\n }\n\n return agg.finish();\n }\n\n /**\n * Return a TradingView-shaped OHLCV response.\n *\n * Aggregates bars from local snapshot data and reshapes to\n * `{ candles, volumes }`.\n */\n async ohlcvTradingView(\n options: OhlcvOptions,\n ): Promise<TradingViewOhlcvResponse> {\n const bars = await this.ohlcv(options);\n return {\n candles: bars.map((b) => ({\n t: b.timestamp,\n o: b.open,\n h: b.high,\n l: b.low,\n c: b.close,\n })),\n volumes: bars.map((b) => ({\n t: b.timestamp,\n v: b.volume,\n trades: b.trades,\n })),\n };\n }\n\n /**\n * Stream historical events as an async iterable.\n *\n * Defaults to standardised events from local snapshots (`standard: true`).\n * Pass `standard: false` to stream raw payloads via the `/raw` endpoint.\n *\n * @example\n * ```ts\n * for await (const row of client.replay({\n * source: \"binance\",\n * market: \"BTC-USDT\",\n * from: \"2024-01-01T00:00:00Z\",\n * to: \"2024-01-01T01:00:00Z\",\n * })) {\n * console.log(row);\n * }\n * ```\n */\n async *replay(options: ReplayOptions): AsyncGenerator<Json> {\n if (options.standard !== false) {\n const fromUs = toEpochUs(options.from);\n const toUs = toEpochUs(options.to);\n yield* this._readDailyEvents(\n options.source,\n options.market,\n fromUs,\n toUs,\n );\n } else {\n yield* this._streamRaw(options);\n }\n }\n\n // -----------------------------------------------------------------------\n // Raw (API-only, not snapshot-first)\n // -----------------------------------------------------------------------\n\n /**\n * Return raw venue-native payloads for a time range.\n * Requires an API key. Uses the `/raw` endpoint with pagination.\n */\n async raw(options: RawQueryOptions): Promise<Json[]> {\n const params = buildRawParams(options);\n return this._paginateAll(\"/raw\", params, \"required\");\n }\n\n // -----------------------------------------------------------------------\n // Downloads\n // -----------------------------------------------------------------------\n\n /**\n * Download a single snapshot file by key.\n *\n * Returns the native `Response` so you can consume the body as needed\n * (`.arrayBuffer()`, `.blob()`, or pipe to a writable stream).\n */\n async downloadSnapshot(\n options: DownloadSnapshotOptions,\n ): Promise<Response> {\n const params: Record<string, string> = { key: options.key };\n if (options.filename) params.filename = options.filename;\n if (options.mode) params.mode = options.mode;\n\n const { response, body } = await this._fetchRaw(\"/snapshots/download\", {\n params,\n auth: \"if-available\",\n });\n assertOk(response, body);\n return response;\n }\n\n /**\n * Get a pre-signed download URL for a snapshot file\n * without fetching the file itself.\n */\n async getSnapshotDownloadUrl(\n options: DownloadSnapshotOptions,\n ): Promise<DownloadUrlResponse> {\n const params: Record<string, string> = { key: options.key, mode: \"url\" };\n if (options.filename) params.filename = options.filename;\n return this._getJson(\"/snapshots/download\", {\n params,\n auth: \"if-available\",\n });\n }\n\n // -----------------------------------------------------------------------\n // Lifecycle\n // -----------------------------------------------------------------------\n\n /** Release resources. Currently a no-op (reserved for future use). */\n close(): void {}\n\n /** Async disposable support (Node ≥ 18 / TypeScript ≥ 5.2). */\n async [Symbol.asyncDispose](): Promise<void> {\n this.close();\n }\n\n // -----------------------------------------------------------------------\n // Internals – snapshot-first local reads\n // -----------------------------------------------------------------------\n\n /**\n * Core routine: ensure daily `.jsonl.zst` artifacts exist, decompress them,\n * and yield matching events one at a time.\n */\n private async *_readDailyEvents(\n source: string,\n market: string,\n fromUs: number,\n toUs: number,\n filter?: (event: Json) => boolean,\n ): AsyncGenerator<Json> {\n const layout = await this._getLayout();\n const dates = datesInRange(fromUs, toUs);\n const filePaths = await this._ensureDailyArtifacts(\n source,\n market,\n dates,\n layout,\n );\n\n for (const filePath of filePaths) {\n const lines = await readDailyLines(filePath);\n for (const line of lines) {\n let event: Json;\n try {\n event = JSON.parse(line) as Json;\n } catch {\n continue;\n }\n\n const ts = event.timestamp as number;\n if (ts < fromUs || ts >= toUs) continue;\n if (filter && !filter(event)) continue;\n\n yield event;\n }\n }\n }\n\n /**\n * Ensure every requested date has a materialised daily artifact.\n * Downloads missing snapshots and materialises them into `daily/`.\n */\n private async _ensureDailyArtifacts(\n source: string,\n market: string,\n dates: string[],\n layout: StorageLayout,\n ): Promise<string[]> {\n const paths: string[] = [];\n\n for (const date of dates) {\n const dailyPath = dailyFilePath(layout.dailyDir, source, market, date);\n\n if (await fileExists(dailyPath)) {\n paths.push(dailyPath);\n continue;\n }\n\n // Discover and download the snapshot for this date\n const snapshots = await this.listSnapshots({\n source,\n market,\n from: `${date}T00:00:00Z`,\n to: `${date}T23:59:59Z`,\n });\n\n const entry = snapshots.find((s) => s.date === date);\n if (!entry) {\n throw new PolarisError(\n `No snapshot available for ${source}/${market} on ${date}`,\n );\n }\n\n await this._downloadAndMaterialise(entry.key, dailyPath, layout);\n paths.push(dailyPath);\n }\n\n return paths;\n }\n\n /**\n * Download a snapshot to `data/` and create a hardlink (or copy) into\n * `daily/`.\n */\n private async _downloadAndMaterialise(\n key: string,\n dailyPath: string,\n layout: StorageLayout,\n ): Promise<void> {\n const dataPath = dataFilePath(layout.dataDir, key);\n\n // Download if we don't already have it in data/\n if (!(await fileExists(dataPath))) {\n const urlInfo = await this.getSnapshotDownloadUrl({ key });\n const response = await this._fetch(urlInfo.url);\n\n if (!response.ok) {\n throw new PolarisError(\n `Failed to download snapshot ${key}: HTTP ${response.status}`,\n );\n }\n\n const buffer = Buffer.from(await response.arrayBuffer());\n await mkdir(dirname(dataPath), { recursive: true });\n await writeFile(dataPath, buffer);\n }\n\n await linkOrCopy(dataPath, dailyPath);\n }\n\n // -----------------------------------------------------------------------\n // Internals – raw endpoint streaming\n // -----------------------------------------------------------------------\n\n private async *_streamRaw(\n options: ReplayOptions,\n ): AsyncGenerator<Json> {\n const params: Record<string, string> = {\n source: options.source,\n market: options.market,\n from: toIso8601(options.from),\n to: toIso8601(options.to),\n };\n let cursor: string | undefined;\n\n do {\n if (cursor) params.cursor = cursor;\n const res = await this._getJson<PaginatedResponse<Json>>(\"/raw\", {\n params,\n auth: \"required\",\n });\n for (const item of res.data) {\n yield item;\n }\n cursor = res.next_cursor ?? undefined;\n } while (cursor);\n }\n\n // -----------------------------------------------------------------------\n // Internals – HTTP layer\n // -----------------------------------------------------------------------\n\n private async _getJson<T = Json>(\n path: string,\n opts: FetchOptions = {},\n ): Promise<T> {\n const { response, body } = await this._fetchRaw(path, {\n ...opts,\n headers: { Accept: \"application/json\", ...opts.headers },\n });\n\n assertOk(response, body);\n\n let json: unknown;\n try {\n json = JSON.parse(body);\n } catch {\n throw new PolarisError(\"Failed to parse response as JSON\");\n }\n\n if (typeof json !== \"object\" || json === null || Array.isArray(json)) {\n throw new PolarisError(\"Expected a JSON object response\");\n }\n\n return json as T;\n }\n\n private async _fetchRaw(\n path: string,\n opts: FetchOptions = {},\n ): Promise<{ response: Response; body: string }> {\n const headers = this._buildHeaders(opts.auth ?? \"none\", opts.headers);\n const url = this._buildUrl(path, opts.params);\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this._timeout);\n\n let response: Response;\n try {\n response = await this._fetch(url, {\n headers,\n signal: controller.signal,\n redirect: \"follow\",\n });\n } catch (e) {\n clearTimeout(timer);\n if (e instanceof Error && e.name === \"AbortError\") {\n throw new PolarisError(\"Request timed out\");\n }\n throw new PolarisError(`Request failed: ${e}`);\n }\n\n clearTimeout(timer);\n const body = await response.text();\n return { response, body };\n }\n\n private _buildHeaders(\n authMode: AuthMode,\n extra?: Record<string, string>,\n ): Record<string, string> {\n const out: Record<string, string> = {\n \"User-Agent\": `polaris-ts/${VERSION}`,\n ...extra,\n };\n\n if (authMode === \"required\" && !this._apiKey) {\n throw new UnauthorizedError(\"API key is required for this endpoint\");\n }\n\n if (this._apiKey && authMode !== \"none\") {\n out[\"Authorization\"] = `Bearer ${this._apiKey}`;\n }\n\n return out;\n }\n\n private _buildUrl(\n path: string,\n params?: Record<string, string>,\n ): string {\n const url = new URL(path, this._baseUrl);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined) url.searchParams.set(k, v);\n }\n }\n return url.toString();\n }\n\n // -----------------------------------------------------------------------\n // Internals – pagination\n // -----------------------------------------------------------------------\n\n private async _paginateAll<T = Json>(\n path: string,\n baseParams: Record<string, string>,\n auth: AuthMode,\n ): Promise<T[]> {\n const items: T[] = [];\n let cursor: string | undefined;\n\n do {\n const params = cursor ? { ...baseParams, cursor } : { ...baseParams };\n const res = await this._getJson<PaginatedResponse<T>>(path, {\n params,\n auth,\n });\n items.push(...res.data);\n cursor = res.next_cursor ?? undefined;\n } while (cursor);\n\n return items;\n }\n\n // -----------------------------------------------------------------------\n // Internals – layout lazy init\n // -----------------------------------------------------------------------\n\n private async _getLayout(): Promise<StorageLayout> {\n if (!this._layout) this._layout = await ensureLayout(this._root);\n return this._layout;\n }\n}\n\n// ===========================================================================\n// Module-level helpers (not exported)\n// ===========================================================================\n\nfunction readEnvApiKey(): string | undefined {\n try {\n return process?.env?.POLARIS_API_KEY;\n } catch {\n return undefined;\n }\n}\n\nfunction buildRawParams(options: RawQueryOptions): Record<string, string> {\n const p: Record<string, string> = {\n source: options.source,\n market: options.market,\n };\n if (options.from !== undefined) p.from = toIso8601(options.from);\n if (options.to !== undefined) p.to = toIso8601(options.to);\n if (options.limit !== undefined) p.limit = String(options.limit);\n if (options.format) p.format = options.format;\n return p;\n}\n\nfunction buildSnapshotParams(\n options: ListSnapshotsOptions,\n): Record<string, string> {\n const p: Record<string, string> = {\n source: options.source,\n market: options.market,\n };\n if (options.from !== undefined) p.from = toIso8601(options.from);\n if (options.to !== undefined) p.to = toIso8601(options.to);\n if (options.limit !== undefined) p.limit = String(options.limit);\n return p;\n}\n\nfunction assertOk(response: Response, body: string): void {\n if (response.ok) return;\n\n let message = `HTTP ${response.status}`;\n let resetAt: string | undefined;\n\n try {\n const json = JSON.parse(body);\n if (typeof json === \"object\" && json !== null) {\n message = String(json.error ?? json.message ?? message);\n resetAt = json.reset_at;\n }\n } catch {\n /* non-JSON body – use default message */\n }\n\n switch (response.status) {\n case 401:\n throw new UnauthorizedError(message, response.status, body);\n case 404:\n throw new NotFoundError(message, response.status, body);\n case 429:\n throw new RateLimitedError(message, response.status, body, resetAt);\n default:\n throw new PolarisError(message, response.status, body);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Daily file reading (zstd + NDJSON)\n// ---------------------------------------------------------------------------\n\nasync function readDailyLines(filePath: string): Promise<string[]> {\n const compressed = await readFile(filePath);\n const decompressed = decompress(compressed);\n const text = new TextDecoder().decode(decompressed);\n return text.split(\"\\n\").filter((l) => l.trim().length > 0);\n}\n","// ---------------------------------------------------------------------------\n// Base\n// ---------------------------------------------------------------------------\n\nexport class PolarisError extends Error {\n override readonly name: string = \"PolarisError\";\n\n /** HTTP status code, if the error originated from an API response. */\n readonly statusCode: number | undefined;\n\n /** Raw response body string. */\n readonly body: string | undefined;\n\n constructor(message: string, statusCode?: number, body?: string) {\n super(message);\n this.statusCode = statusCode;\n this.body = body;\n }\n\n override toString(): string {\n return this.statusCode != null\n ? `${this.message} (status=${this.statusCode})`\n : this.message;\n }\n}\n\n// ---------------------------------------------------------------------------\n// 401\n// ---------------------------------------------------------------------------\n\nexport class UnauthorizedError extends PolarisError {\n override readonly name: string = \"UnauthorizedError\";\n}\n\n// ---------------------------------------------------------------------------\n// 404\n// ---------------------------------------------------------------------------\n\nexport class NotFoundError extends PolarisError {\n override readonly name: string = \"NotFoundError\";\n}\n\n// ---------------------------------------------------------------------------\n// 429\n// ---------------------------------------------------------------------------\n\nexport class RateLimitedError extends PolarisError {\n override readonly name: string = \"RateLimitedError\";\n\n /** ISO 8601 timestamp (or epoch) indicating when the rate limit resets. */\n readonly resetAt: string | undefined;\n\n constructor(\n message: string,\n statusCode?: number,\n body?: string,\n resetAt?: string,\n ) {\n super(message, statusCode, body);\n this.resetAt = resetAt;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Stream / decode failures\n// ---------------------------------------------------------------------------\n\nexport class StreamDecodeError extends PolarisError {\n override readonly name: string = \"StreamDecodeError\";\n}\n\n// ---------------------------------------------------------------------------\n// Download blocked by server policy\n// ---------------------------------------------------------------------------\n\nexport class DownloadNotAllowedError extends PolarisError {\n override readonly name: string = \"DownloadNotAllowedError\";\n}\n","import type { TimeInput } from \"./types\";\n\nconst EPOCH_RE = /^\\d+$/;\n\n// ---------------------------------------------------------------------------\n// toIso8601\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a {@link TimeInput} value to an ISO 8601 UTC string with a `Z` suffix.\n *\n * - `string` → returned as-is (assumed ISO 8601), unless it looks like an\n * epoch microsecond integer (13+ digits) in which case it is converted.\n * - `Date` → converted via `toISOString()`.\n * - `number` → treated as **epoch microseconds** when `> 1e12`,\n * otherwise as **milliseconds**.\n */\nexport function toIso8601(value: TimeInput): string {\n if (typeof value === \"string\") {\n if (EPOCH_RE.test(value) && value.length >= 13) return epochUsToIso(Number(value));\n return value;\n }\n if (value instanceof Date) return dateToIso(value);\n return value > 1e12 ? epochUsToIso(value) : epochMsToIso(value);\n}\n\n// ---------------------------------------------------------------------------\n// toEpochUs\n// ---------------------------------------------------------------------------\n\n/** Convert a {@link TimeInput} to **epoch microseconds** (integer). */\nexport function toEpochUs(value: TimeInput): number {\n if (typeof value === \"number\") return value > 1e12 ? value : value * 1000;\n if (typeof value === \"string\") {\n if (EPOCH_RE.test(value) && value.length >= 13) return Number(value);\n return Math.round(new Date(value).getTime() * 1000);\n }\n return Math.round(value.getTime() * 1000);\n}\n\n// ---------------------------------------------------------------------------\n// datesInRange\n// ---------------------------------------------------------------------------\n\n/**\n * Return an array of `\"YYYY-MM-DD\"` strings covering every UTC calendar date\n * that intersects `[fromUs, toUs)` (epoch microseconds, inclusive start /\n * exclusive end).\n */\nexport function datesInRange(fromUs: number, toUs: number): string[] {\n const dates: string[] = [];\n const start = new Date(fromUs / 1000);\n const end = new Date(toUs / 1000);\n\n const cur = new Date(\n Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate()),\n );\n const last = new Date(\n Date.UTC(end.getUTCFullYear(), end.getUTCMonth(), end.getUTCDate()),\n );\n\n while (cur <= last) {\n dates.push(formatUtcDate(cur));\n cur.setUTCDate(cur.getUTCDate() + 1);\n }\n\n return dates;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction epochUsToIso(us: number): string {\n return epochMsToIso(us / 1000);\n}\n\nfunction epochMsToIso(ms: number): string {\n return new Date(ms).toISOString().replace(/\\.000Z$/, \"Z\");\n}\n\nfunction dateToIso(d: Date): string {\n return d.toISOString().replace(/\\.000Z$/, \"Z\");\n}\n\nfunction formatUtcDate(d: Date): string {\n const y = d.getUTCFullYear();\n const m = String(d.getUTCMonth() + 1).padStart(2, \"0\");\n const day = String(d.getUTCDate()).padStart(2, \"0\");\n return `${y}-${m}-${day}`;\n}\n","import { mkdir, stat, link, copyFile } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\nimport { homedir, platform } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// StorageLayout\n// ---------------------------------------------------------------------------\n\nexport interface StorageLayout {\n readonly root: string;\n readonly dataDir: string;\n readonly dailyDir: string;\n readonly tmpDir: string;\n readonly cacheDir: string;\n}\n\n// ---------------------------------------------------------------------------\n// Root resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the local dataset root.\n *\n * Priority: explicit → `POLARIS_ROOT` → `POLARIS_DATASET_DOWNLOAD_DIR`\n * (deprecated) → platform default.\n */\nexport function resolveRoot(explicit?: string): string {\n if (explicit) return explicit;\n if (process.env.POLARIS_ROOT) return process.env.POLARIS_ROOT;\n if (process.env.POLARIS_DATASET_DOWNLOAD_DIR)\n return process.env.POLARIS_DATASET_DOWNLOAD_DIR;\n return defaultRoot();\n}\n\nfunction defaultRoot(): string {\n switch (platform()) {\n case \"darwin\":\n return join(\n homedir(),\n \"Library\",\n \"Application Support\",\n \"polaris\",\n );\n case \"win32\":\n return join(\n process.env.APPDATA ||\n join(homedir(), \"AppData\", \"Roaming\"),\n \"polaris\",\n );\n default:\n return (\n process.env.XDG_DATA_HOME\n ? join(process.env.XDG_DATA_HOME, \"polaris\")\n : join(homedir(), \".local\", \"share\", \"polaris\")\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Layout bootstrapping\n// ---------------------------------------------------------------------------\n\n/** Ensure the standard sub-directory tree exists and return the layout. */\nexport async function ensureLayout(root: string): Promise<StorageLayout> {\n const dataDir = join(root, \"data\");\n const dailyDir = join(root, \"daily\");\n const tmpDir = join(root, \"tmp\");\n const cacheDir = join(root, \"cache\");\n\n await Promise.all([\n mkdir(dataDir, { recursive: true }),\n mkdir(dailyDir, { recursive: true }),\n mkdir(tmpDir, { recursive: true }),\n mkdir(cacheDir, { recursive: true }),\n ]);\n\n return { root, dataDir, dailyDir, tmpDir, cacheDir };\n}\n\n// ---------------------------------------------------------------------------\n// Path helpers\n// ---------------------------------------------------------------------------\n\n/** Path for a downloaded snapshot file in the `data/` tree. */\nexport function dataFilePath(dataDir: string, key: string): string {\n return join(dataDir, key);\n}\n\n/** Path for a materialised daily artifact in the `daily/` tree. */\nexport function dailyFilePath(\n dailyDir: string,\n source: string,\n market: string,\n date: string,\n): string {\n return join(dailyDir, source, market, `${date}.jsonl.zst`);\n}\n\n// ---------------------------------------------------------------------------\n// Filesystem utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Create a hardlink from `src` to `dest`, falling back to a copy.\n * Creates parent directories of `dest` as needed.\n */\nexport async function linkOrCopy(src: string, dest: string): Promise<void> {\n await mkdir(dirname(dest), { recursive: true });\n try {\n await link(src, dest);\n } catch {\n await copyFile(src, dest);\n }\n}\n\n/** Return `true` when the file at `path` exists. */\nexport async function fileExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n","import type { OhlcvBar, OhlcvInterval } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Interval → microseconds\n// ---------------------------------------------------------------------------\n\nfunction intervalToUs(interval: OhlcvInterval): number {\n const m = interval.match(/^(\\d+)(ms|s|m|h)$/);\n if (!m) throw new Error(`Invalid OHLCV interval: ${interval}`);\n const n = parseInt(m[1], 10);\n switch (m[2]) {\n case \"ms\": return n * 1_000;\n case \"s\": return n * 1_000_000;\n case \"m\": return n * 60_000_000;\n case \"h\": return n * 3_600_000_000;\n default: throw new Error(`Unreachable`);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal bucket\n// ---------------------------------------------------------------------------\n\ninterface _Bucket {\n ts: number;\n open: number;\n high: number;\n low: number;\n close: number;\n /** Scaled by 1e12 to avoid floating-point precision loss on accumulation. */\n volScaled: number;\n trades: number;\n}\n\n// ---------------------------------------------------------------------------\n// OhlcvAggregator\n// ---------------------------------------------------------------------------\n\n/**\n * In-memory OHLCV bar aggregator that buckets trades by timestamp into\n * fixed-width intervals.\n *\n * Volume is accumulated as `quantity × 1e12` (rounded to integer) to match the\n * precision strategy used by the Python SDK's `_LocalOhlcvAggregator`.\n */\nexport class OhlcvAggregator {\n private readonly _intervalUs: number;\n private readonly _bars = new Map<number, _Bucket>();\n\n constructor(interval: OhlcvInterval) {\n this._intervalUs = intervalToUs(interval);\n }\n\n /** Ingest a single trade event. */\n add(timestamp: number, price: number, quantity: number): void {\n const bucketTs =\n Math.floor(timestamp / this._intervalUs) * this._intervalUs;\n\n let b = this._bars.get(bucketTs);\n if (!b) {\n b = {\n ts: bucketTs,\n open: price,\n high: price,\n low: price,\n close: price,\n volScaled: 0,\n trades: 0,\n };\n this._bars.set(bucketTs, b);\n }\n\n if (price > b.high) b.high = price;\n if (price < b.low) b.low = price;\n b.close = price;\n b.volScaled += Math.round(quantity * 1e12);\n b.trades += 1;\n }\n\n /**\n * Finalise the aggregation and return sorted bars.\n *\n * The `open` of each subsequent bar is overwritten with the `close` of the\n * previous bar, matching the convention used by the Python SDK.\n */\n finish(): OhlcvBar[] {\n const bars = Array.from(this._bars.values()).sort(\n (a, b) => a.ts - b.ts,\n );\n\n for (let i = 1; i < bars.length; i++) {\n bars[i].open = bars[i - 1].close;\n }\n\n return bars.map((b) => ({\n timestamp: b.ts,\n open: b.open,\n high: b.high,\n low: b.low,\n close: b.close,\n volume: b.volScaled / 1e12,\n trades: b.trades,\n }));\n }\n}\n"],"mappings":"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","names":["import_promises","import_node_path"]}
@@ -0,0 +1,323 @@
1
+ /** Accepts ISO 8601 strings, `Date` instances, or Unix epoch microseconds. */
2
+ type TimeInput = string | Date | number;
3
+ /** Shape of the global `fetch` function so consumers can inject a custom one. */
4
+ type FetchLike = typeof globalThis.fetch;
5
+ type AuthMode = "none" | "if-available" | "required";
6
+ interface PaginatedResponse<T = Record<string, unknown>> {
7
+ data: T[];
8
+ next_cursor: string | null;
9
+ has_more: boolean;
10
+ }
11
+ interface CatalogResponse {
12
+ sources: CatalogSource[];
13
+ updatedAt: string;
14
+ }
15
+ interface CatalogSource {
16
+ id: string;
17
+ markets: CatalogMarket[];
18
+ }
19
+ interface CatalogMarket {
20
+ id: string;
21
+ start?: string;
22
+ end?: string;
23
+ source?: string;
24
+ categories?: string[];
25
+ access?: {
26
+ status: string;
27
+ public_cutoff_date?: string;
28
+ };
29
+ }
30
+ interface StandardEvent {
31
+ timestamp: number;
32
+ venue: string;
33
+ symbol: string;
34
+ type: string;
35
+ data: Record<string, unknown>;
36
+ }
37
+ interface TradeData {
38
+ price: number;
39
+ quantity: number;
40
+ side: string;
41
+ [key: string]: unknown;
42
+ }
43
+ interface TradeEvent extends StandardEvent {
44
+ type: "trade";
45
+ data: TradeData;
46
+ }
47
+ type OhlcvInterval = "100ms" | "1s" | "10s" | "1m" | "5m" | "15m" | "1h";
48
+ interface OhlcvBar {
49
+ timestamp: number;
50
+ open: number;
51
+ high: number;
52
+ low: number;
53
+ close: number;
54
+ volume: number;
55
+ trades: number;
56
+ }
57
+ interface TradingViewCandle {
58
+ t: number;
59
+ o: number;
60
+ h: number;
61
+ l: number;
62
+ c: number;
63
+ }
64
+ interface TradingViewVolume {
65
+ t: number;
66
+ v: number;
67
+ trades?: number;
68
+ }
69
+ interface TradingViewOhlcvResponse {
70
+ candles: TradingViewCandle[];
71
+ volumes: TradingViewVolume[];
72
+ }
73
+ interface SnapshotEntry {
74
+ date: string;
75
+ key: string;
76
+ filename: string;
77
+ }
78
+ interface SnapshotsResponse {
79
+ source: string;
80
+ market: string;
81
+ access?: {
82
+ status: string;
83
+ public_cutoff_date?: string;
84
+ };
85
+ total: number;
86
+ total_bytes: number;
87
+ limit: number;
88
+ has_more: boolean;
89
+ next_cursor: string | null;
90
+ snapshots: SnapshotEntry[];
91
+ }
92
+ interface DownloadUrlResponse {
93
+ url: string;
94
+ filename?: string;
95
+ expires_in_seconds?: number;
96
+ }
97
+ interface PolarisClientOptions {
98
+ /** Polaris API key. Falls back to `POLARIS_API_KEY` env var. */
99
+ apiKey?: string;
100
+ /** API base URL. Defaults to `https://api.polaris.supply`. */
101
+ baseUrl?: string;
102
+ /** Request timeout in milliseconds. Defaults to `30 000` (30 s). */
103
+ timeout?: number;
104
+ /** Custom fetch implementation (useful for testing or proxies). */
105
+ fetch?: FetchLike;
106
+ /**
107
+ * Override the local dataset root directory.
108
+ * Defaults to the platform-specific Polaris app-data directory,
109
+ * overridable globally via `POLARIS_ROOT` env var.
110
+ */
111
+ datasetRoot?: string;
112
+ }
113
+ interface CatalogOptions {
114
+ source?: string;
115
+ market?: string;
116
+ }
117
+ /** Options for snapshot-based historical data methods. `from` and `to` are required. */
118
+ interface HistoricalQueryOptions {
119
+ source: string;
120
+ market: string;
121
+ from: TimeInput;
122
+ to: TimeInput;
123
+ }
124
+ interface ListSnapshotsOptions {
125
+ source: string;
126
+ market: string;
127
+ from?: TimeInput;
128
+ to?: TimeInput;
129
+ limit?: number;
130
+ }
131
+ /** Options for the /raw endpoint. `from`/`to` are optional (defaults to 24 h lookback). */
132
+ interface RawQueryOptions {
133
+ source: string;
134
+ market: string;
135
+ from?: TimeInput;
136
+ to?: TimeInput;
137
+ limit?: number;
138
+ format?: string;
139
+ }
140
+ interface OhlcvOptions extends HistoricalQueryOptions {
141
+ interval: OhlcvInterval;
142
+ }
143
+ interface ReplayOptions {
144
+ source: string;
145
+ market: string;
146
+ from: TimeInput;
147
+ to: TimeInput;
148
+ /** `true` (default) streams standardised events from local snapshots. */
149
+ standard?: boolean;
150
+ }
151
+ interface DownloadSnapshotOptions {
152
+ key: string;
153
+ filename?: string;
154
+ mode?: "url" | "json";
155
+ }
156
+
157
+ type Json = Record<string, unknown>;
158
+ declare class PolarisClient {
159
+ private readonly _apiKey;
160
+ private readonly _baseUrl;
161
+ private readonly _timeout;
162
+ private readonly _fetch;
163
+ private readonly _root;
164
+ private _layout;
165
+ constructor(options?: PolarisClientOptions);
166
+ /** Check API availability. */
167
+ health(): Promise<Json>;
168
+ /**
169
+ * Browse supported sources and markets.
170
+ *
171
+ * If neither `source` nor `market` is provided, returns the full catalog.
172
+ * `market` requires `source`.
173
+ */
174
+ catalog(options?: CatalogOptions): Promise<CatalogResponse>;
175
+ /**
176
+ * List available snapshot files for a source and market over a time range.
177
+ * Auto-paginates to return **all** matching entries.
178
+ */
179
+ listSnapshots(options: ListSnapshotsOptions): Promise<SnapshotEntry[]>;
180
+ /**
181
+ * Return all standardised historical events for a time range.
182
+ *
183
+ * Reads from locally-cached daily `.jsonl.zst` snapshot files.
184
+ * Missing daily artifacts are discovered via `GET /snapshots` and
185
+ * downloaded automatically.
186
+ */
187
+ events(options: HistoricalQueryOptions): Promise<Json[]>;
188
+ /**
189
+ * Return all standardised trade events for a time range.
190
+ *
191
+ * Reads from locally-cached daily snapshot files, filtering to
192
+ * `type === "trade"`.
193
+ */
194
+ trades(options: HistoricalQueryOptions): Promise<Json[]>;
195
+ /**
196
+ * Aggregate OHLCV bars from standardised trade data.
197
+ *
198
+ * Reads from locally-cached daily snapshot files and aggregates in memory
199
+ * using the same interval-bucketing strategy as the Python SDK.
200
+ */
201
+ ohlcv(options: OhlcvOptions): Promise<OhlcvBar[]>;
202
+ /**
203
+ * Return a TradingView-shaped OHLCV response.
204
+ *
205
+ * Aggregates bars from local snapshot data and reshapes to
206
+ * `{ candles, volumes }`.
207
+ */
208
+ ohlcvTradingView(options: OhlcvOptions): Promise<TradingViewOhlcvResponse>;
209
+ /**
210
+ * Stream historical events as an async iterable.
211
+ *
212
+ * Defaults to standardised events from local snapshots (`standard: true`).
213
+ * Pass `standard: false` to stream raw payloads via the `/raw` endpoint.
214
+ *
215
+ * @example
216
+ * ```ts
217
+ * for await (const row of client.replay({
218
+ * source: "binance",
219
+ * market: "BTC-USDT",
220
+ * from: "2024-01-01T00:00:00Z",
221
+ * to: "2024-01-01T01:00:00Z",
222
+ * })) {
223
+ * console.log(row);
224
+ * }
225
+ * ```
226
+ */
227
+ replay(options: ReplayOptions): AsyncGenerator<Json>;
228
+ /**
229
+ * Return raw venue-native payloads for a time range.
230
+ * Requires an API key. Uses the `/raw` endpoint with pagination.
231
+ */
232
+ raw(options: RawQueryOptions): Promise<Json[]>;
233
+ /**
234
+ * Download a single snapshot file by key.
235
+ *
236
+ * Returns the native `Response` so you can consume the body as needed
237
+ * (`.arrayBuffer()`, `.blob()`, or pipe to a writable stream).
238
+ */
239
+ downloadSnapshot(options: DownloadSnapshotOptions): Promise<Response>;
240
+ /**
241
+ * Get a pre-signed download URL for a snapshot file
242
+ * without fetching the file itself.
243
+ */
244
+ getSnapshotDownloadUrl(options: DownloadSnapshotOptions): Promise<DownloadUrlResponse>;
245
+ /** Release resources. Currently a no-op (reserved for future use). */
246
+ close(): void;
247
+ /** Async disposable support (Node ≥ 18 / TypeScript ≥ 5.2). */
248
+ [Symbol.asyncDispose](): Promise<void>;
249
+ /**
250
+ * Core routine: ensure daily `.jsonl.zst` artifacts exist, decompress them,
251
+ * and yield matching events one at a time.
252
+ */
253
+ private _readDailyEvents;
254
+ /**
255
+ * Ensure every requested date has a materialised daily artifact.
256
+ * Downloads missing snapshots and materialises them into `daily/`.
257
+ */
258
+ private _ensureDailyArtifacts;
259
+ /**
260
+ * Download a snapshot to `data/` and create a hardlink (or copy) into
261
+ * `daily/`.
262
+ */
263
+ private _downloadAndMaterialise;
264
+ private _streamRaw;
265
+ private _getJson;
266
+ private _fetchRaw;
267
+ private _buildHeaders;
268
+ private _buildUrl;
269
+ private _paginateAll;
270
+ private _getLayout;
271
+ }
272
+
273
+ declare class PolarisError extends Error {
274
+ readonly name: string;
275
+ /** HTTP status code, if the error originated from an API response. */
276
+ readonly statusCode: number | undefined;
277
+ /** Raw response body string. */
278
+ readonly body: string | undefined;
279
+ constructor(message: string, statusCode?: number, body?: string);
280
+ toString(): string;
281
+ }
282
+ declare class UnauthorizedError extends PolarisError {
283
+ readonly name: string;
284
+ }
285
+ declare class NotFoundError extends PolarisError {
286
+ readonly name: string;
287
+ }
288
+ declare class RateLimitedError extends PolarisError {
289
+ readonly name: string;
290
+ /** ISO 8601 timestamp (or epoch) indicating when the rate limit resets. */
291
+ readonly resetAt: string | undefined;
292
+ constructor(message: string, statusCode?: number, body?: string, resetAt?: string);
293
+ }
294
+ declare class StreamDecodeError extends PolarisError {
295
+ readonly name: string;
296
+ }
297
+ declare class DownloadNotAllowedError extends PolarisError {
298
+ readonly name: string;
299
+ }
300
+
301
+ /**
302
+ * In-memory OHLCV bar aggregator that buckets trades by timestamp into
303
+ * fixed-width intervals.
304
+ *
305
+ * Volume is accumulated as `quantity × 1e12` (rounded to integer) to match the
306
+ * precision strategy used by the Python SDK's `_LocalOhlcvAggregator`.
307
+ */
308
+ declare class OhlcvAggregator {
309
+ private readonly _intervalUs;
310
+ private readonly _bars;
311
+ constructor(interval: OhlcvInterval);
312
+ /** Ingest a single trade event. */
313
+ add(timestamp: number, price: number, quantity: number): void;
314
+ /**
315
+ * Finalise the aggregation and return sorted bars.
316
+ *
317
+ * The `open` of each subsequent bar is overwritten with the `close` of the
318
+ * previous bar, matching the convention used by the Python SDK.
319
+ */
320
+ finish(): OhlcvBar[];
321
+ }
322
+
323
+ export { type AuthMode, type CatalogMarket, type CatalogOptions, type CatalogResponse, type CatalogSource, DownloadNotAllowedError, type DownloadSnapshotOptions, type DownloadUrlResponse, type FetchLike, type HistoricalQueryOptions, type ListSnapshotsOptions, NotFoundError, OhlcvAggregator, type OhlcvBar, type OhlcvInterval, type OhlcvOptions, type PaginatedResponse, PolarisClient, type PolarisClientOptions, PolarisError, RateLimitedError, type RawQueryOptions, type ReplayOptions, type SnapshotEntry, type SnapshotsResponse, type StandardEvent, StreamDecodeError, type TimeInput, type TradeData, type TradeEvent, type TradingViewCandle, type TradingViewOhlcvResponse, type TradingViewVolume, UnauthorizedError };