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/client.ts","../src/errors.ts","../src/utils.ts","../src/storage.ts","../src/aggregator.ts"],"sourcesContent":["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":["mkdir","dirname","mkdir","dirname"]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "polaris-data",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript SDK for the Polaris market data API",
5
+ "license": "MIT",
6
+ "author": "Polaris <hello@polaris.supply>",
7
+ "homepage": "https://polaris.supply",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/polaris-data/polaris-ts"
11
+ },
12
+ "type": "module",
13
+ "main": "./dist/index.cjs",
14
+ "module": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "require": "./dist/index.cjs"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "sideEffects": false,
27
+ "engines": {
28
+ "node": ">=18.0.0"
29
+ },
30
+ "scripts": {
31
+ "build": "tsup",
32
+ "dev": "tsup --watch",
33
+ "clean": "rm -rf dist",
34
+ "prepublishOnly": "npm run clean && npm run build",
35
+ "typecheck": "tsc --noEmit"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^20.0.0",
39
+ "tsup": "^8.0.0",
40
+ "typescript": "^5.5.0"
41
+ },
42
+ "keywords": [
43
+ "polaris",
44
+ "market-data",
45
+ "crypto",
46
+ "defi",
47
+ "trading",
48
+ "api",
49
+ "sdk",
50
+ "ohlcv",
51
+ "trades",
52
+ "events"
53
+ ],
54
+ "dependencies": {
55
+ "fzstd": "^0.1.1"
56
+ }
57
+ }