first commit
This commit is contained in:
87
docs/money/finance/currency-provider-kraken/README.md
Normal file
87
docs/money/finance/currency-provider-kraken/README.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# @suaveplan/currency-provider-kraken - Universal Kraken public-ticker rate provider for @suaveplan/currency. Wraps Kraken's free no-API-key /0/public/Ticker endpoint into a CurrencyProvider with the slim core's shared httpRequest helper, typed CurrencyProviderError (HTTP_ERROR / PARSE_ERROR / UPSTREAM_ERROR / UNKNOWN_CURRENCY), and the 12-symbol BTC/ETH/SOL/USDT/DOT/ADA/LINK/AVAX/ATOM/LTC/DOGE/XRP coverage matched against Kraken's non-standard pair names.
|
||||
|
||||
Universal Kraken public-ticker rate provider for `@suaveplan/currency`.
|
||||
Wraps Kraken's free, no-API-key `/0/public/Ticker` endpoint into a
|
||||
`CurrencyProvider` so a `CurrencyConverter` can price the major crypto
|
||||
pairs against USD without callers ever seeing Kraken's non-standard pair
|
||||
names (`XXBTZUSD`, `XETHZUSD`, `XDGUSD`, …).
|
||||
|
||||
Concrete sibling of the slim `@suaveplan/currency-providers` core — uses
|
||||
the shared `httpRequest()` helper, raises the shared `CurrencyProviderError`
|
||||
codes (`HTTP_ERROR`, `PARSE_ERROR`, `UPSTREAM_ERROR`, `UNKNOWN_CURRENCY`),
|
||||
and accepts the shared `NoKeyConfig` (optional `baseUrl`, optional injectable
|
||||
`FetchLike`) plus an optional `extraSymbols` map for caller-supplied
|
||||
extensions.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
## Documentation tree
|
||||
|
||||
- [provider](./src/provider/provider.md) — `KrakenProvider` is the universal `CurrencyProvider` implementation for Kraken's free `/0/public/Ticker` endpoint.…
|
||||
- [provider](./src/provider/provider.md) — `KrakenProvider` is the universal `CurrencyProvider` implementation for Kraken's free `/0/public/Ticker` endpoint.…
|
||||
- [symbols](./src/symbols/symbols.md) — The Kraken public ticker addresses pairs by non-standard names — `XXBTZUSD` for BTC/USD, `XETHZUSD` for ETH/USD, `XDGUS…
|
||||
- [symbols](./src/symbols/symbols.md) — The Kraken public ticker addresses pairs by non-standard names — `XXBTZUSD` for BTC/USD, `XETHZUSD` for ETH/USD, `XDGUS…
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @suaveplan/currency-provider-kraken @suaveplan/currency @suaveplan/currency-providers
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```ts
|
||||
import { CurrencyConverter } from "@suaveplan/currency";
|
||||
import { KrakenProvider } from "@suaveplan/currency-provider-kraken";
|
||||
|
||||
const provider = new KrakenProvider();
|
||||
const converter = new CurrencyConverter({ provider });
|
||||
|
||||
const btcPerUsd = await converter.getRate("USD", "BTC"); // e.g. 0.0000149
|
||||
const ethPerUsd = await converter.getRate("USD", "ETH"); // e.g. 0.0002857
|
||||
```
|
||||
|
||||
Or register through the slim core's registry:
|
||||
|
||||
```ts
|
||||
import { CurrencyProviderRegistry } from "@suaveplan/currency-providers";
|
||||
import { KrakenProvider } from "@suaveplan/currency-provider-kraken";
|
||||
|
||||
const registry = new CurrencyProviderRegistry();
|
||||
registry.register(new KrakenProvider());
|
||||
|
||||
const provider = registry.get("kraken");
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- No API key — Kraken's public endpoint is free (per-IP rate-limited)
|
||||
- 12 high-volume symbols out of the box: BTC, ETH, SOL, USDT, DOT, ADA,
|
||||
LINK, AVAX, ATOM, LTC, DOGE, XRP
|
||||
- Optional `extraSymbols` for newly-listed pairs without forking
|
||||
- Reciprocal-of-USD rate model — `rates[X] = 1 / lastTradeClosedPriceUsd`
|
||||
- Single-round-trip batch fetch (Kraken supports a comma-separated `pair=`
|
||||
query)
|
||||
- Uses the slim core's `httpRequest()` so HTTP errors, timeouts, and caller
|
||||
`AbortSignal` propagation are uniform across providers
|
||||
- Maps wire-level failure modes to typed `CurrencyProviderError`:
|
||||
`HTTP_ERROR`, `UPSTREAM_ERROR`, `PARSE_ERROR`, `UNKNOWN_CURRENCY`
|
||||
- Universal — no `node:`/`bun:`/DOM imports; runs unchanged under
|
||||
Node ≥ 18, Bun, modern browsers (subject to Kraken's CORS policy), Web
|
||||
Workers
|
||||
|
||||
## Modules
|
||||
|
||||
- [`src/provider/provider.md`](./src/provider/provider.md) —
|
||||
`KrakenProvider` class, `KrakenProviderConfig`, `DEFAULT_KRAKEN_BASE_URL`
|
||||
- [`src/symbols/symbols.md`](./src/symbols/symbols.md) —
|
||||
`KRAKEN_SYMBOL_MAP`, `KRAKEN_PAIR_TO_SYMBOL`, `mergeSymbolMap`
|
||||
|
||||
## Status
|
||||
|
||||
`0.1.0` (pre-graduation). native sibling of the slim
|
||||
`@suaveplan/currency-providers` core landed by
|
||||
the design document. See
|
||||
the design proposal for
|
||||
the migration parity table and source mapping.
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
# `provider` — KrakenProvider class
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`KrakenProvider` is the universal `CurrencyProvider` implementation
|
||||
for Kraken's free `/0/public/Ticker` endpoint. It fetches real-time
|
||||
last-trade-closed prices for a fixed set of high-volume crypto pairs and
|
||||
returns them as reciprocal-of-USD rates, so a downstream `CurrencyConverter`
|
||||
can translate `USD → BTC` (and friends) without callers ever seeing
|
||||
Kraken's non-standard pair names.
|
||||
|
||||
The class is the canonical sibling-package surface defined by the slim
|
||||
`@suaveplan/currency-providers` core: it wraps a single upstream endpoint,
|
||||
maps every documented failure mode to a typed `CurrencyProviderError`, and
|
||||
exposes nothing that consumers can't already obtain from the shared core
|
||||
helpers.
|
||||
|
||||
## Features
|
||||
|
||||
- No API key required — Kraken's public endpoint is free and rate-limited
|
||||
per IP
|
||||
- 12 high-volume symbols out of the box (BTC, ETH, SOL, USDT, DOT, ADA,
|
||||
LINK, AVAX, ATOM, LTC, DOGE, XRP); extensible via constructor
|
||||
`extraSymbols`
|
||||
- Reciprocal-of-USD rate model — `1 USD = (1 / krakenLastTradePrice)` of the
|
||||
target crypto, matching the shape every `CurrencyConverter` consumer expects
|
||||
- Uses the slim core's `httpRequest()` helper so HTTP failures, timeouts,
|
||||
and caller abort-signal propagation are handled uniformly
|
||||
- Maps the four wire-level failure modes to typed `CurrencyProviderError`:
|
||||
`HTTP_ERROR` (non-2xx), `UPSTREAM_ERROR` (Kraken `error[]` array),
|
||||
`PARSE_ERROR` (malformed JSON / shape mismatch), `UNKNOWN_CURRENCY` (all
|
||||
requested targets unmapped)
|
||||
- Universal — no `node:`/`bun:`/DOM imports; runs unchanged under
|
||||
Node ≥ 18, Bun, modern browsers (subject to Kraken's CORS), Web Workers
|
||||
- Optional injectable `FetchLike` (defaults to `globalThis.fetch`) for
|
||||
recording tests, proxying, or staging URLs
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { KrakenProvider } from "@suaveplan/currency-provider-kraken/provider/provider";
|
||||
|
||||
const provider = new KrakenProvider();
|
||||
const result = await provider.fetchRates({ base: "USD" });
|
||||
|
||||
console.log(result.rates.BTC); // e.g. 0.0000149 (≈ 1 / 67000)
|
||||
console.log(result.rates.ETH); // e.g. 0.0002857 (≈ 1 / 3500)
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { CurrencyConverter } from "@suaveplan/currency";
|
||||
import { CurrencyProviderRegistry } from "@suaveplan/currency-providers";
|
||||
import { KrakenProvider } from "@suaveplan/currency-provider-kraken/provider/provider";
|
||||
|
||||
// Use the slim core's registry — wire Kraken plus any other sibling
|
||||
// providers your app supports. The Kraken instance extends coverage with
|
||||
// a newly-listed pair via `extraSymbols`, and reuses a custom fetch
|
||||
// closure that adds a proxy header.
|
||||
const registry = new CurrencyProviderRegistry();
|
||||
registry.register(
|
||||
new KrakenProvider({
|
||||
baseUrl: "https://kraken.proxy.example.com/0/public",
|
||||
fetch: async (url, init) =>
|
||||
fetch(url, {
|
||||
...init,
|
||||
headers: {
|
||||
...(init?.headers ?? {}),
|
||||
"x-proxy-token": process.env.PROXY_TOKEN ?? "",
|
||||
},
|
||||
}),
|
||||
extraSymbols: { MATIC: "MATICUSD" },
|
||||
}),
|
||||
);
|
||||
|
||||
const converter = new CurrencyConverter({
|
||||
provider: registry.get("kraken"),
|
||||
});
|
||||
const btcRate = await converter.getRate("USD", "BTC");
|
||||
const maticRate = await converter.getRate("USD", "MATIC");
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
| Export | Kind | Description |
|
||||
|---|---|---|
|
||||
| `DEFAULT_KRAKEN_BASE_URL` | `string` | `"https://api.kraken.com/0/public"` — overridable via `config.baseUrl`. |
|
||||
| `KrakenProviderConfig` | `interface` extends `NoKeyConfig` | Optional `baseUrl`, optional `fetch` (`FetchLike`), optional `extraSymbols` (`Record<string, string>`). |
|
||||
| `KrakenProvider` | `class implements CurrencyProvider` | Fixed `name = "kraken"`, `supportsCrypto = true`, `supportsFiat = false`. Constructor takes an optional `KrakenProviderConfig`. |
|
||||
| `KrakenProvider.fetchRates(options?)` | `(RateFetchOptions?) => Promise<RateFetchResult>` | Issues one `GET /Ticker?pair=<csv>` via the injected fetch. Returns `{ base, rates, timestamp }`. Throws `CurrencyProviderError` on every documented failure path. |
|
||||
|
||||
### Error codes raised
|
||||
|
||||
| Code | When |
|
||||
|---|---|
|
||||
| `CURRENCY_PROVIDER.HTTP_ERROR` | Non-2xx response (raised by the shared `httpRequest`). Carries `status`. |
|
||||
| `CURRENCY_PROVIDER.UPSTREAM_ERROR` | Kraken response carries a non-empty `error[]` array. Carries `upstreamMessage`. |
|
||||
| `CURRENCY_PROVIDER.PARSE_ERROR` | Body fails JSON-parse, OR the parsed body does not match the `{ error: string[], result: object }` shape. Carries `cause` for the JSON-parse case. |
|
||||
| `CURRENCY_PROVIDER.UNKNOWN_CURRENCY` | Caller supplied `options.targets` but none of the requested symbols resolved to a mapped Kraken pair. |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The pair list sent in the `pair=` query is the value list of the merged
|
||||
symbol map — Kraken supports a comma-separated batch so the whole supported
|
||||
set is fetched in a single round trip. This is more efficient than one
|
||||
request per symbol AND matches what Kraken's documentation recommends.
|
||||
- The rate model is `1 / Number.parseFloat(ticker.c[0])` where `c[0]` is
|
||||
Kraken's "last trade closed" price. Entries whose price does not parse
|
||||
to a positive finite number are skipped (not raised) — Kraken occasionally
|
||||
returns `"0.00"` for freshly-listed pairs. If the entire mapped set drops,
|
||||
`options.targets` filtering will subsequently raise `UNKNOWN_CURRENCY`
|
||||
on the caller's behalf.
|
||||
- `options.signal` is forwarded into `httpRequest()` so the caller's
|
||||
`AbortController` composes with any future timeout the helper applies.
|
||||
- The provider does **not** retry, cache, or rate-limit. Those concerns
|
||||
belong in wrapper providers (the slim core's `## Out of scope` defers
|
||||
them) — keeping this class focused on Kraken's wire protocol keeps the
|
||||
failure surface auditable.
|
||||
- All HTTP error mapping flows through the shared `httpRequest()` helper
|
||||
exported from `@suaveplan/currency-providers/http/http`, so the response
|
||||
is never read on a non-2xx path — Kraken's body on a 5xx is typically
|
||||
HTML and would otherwise raise a confusing `PARSE_ERROR`.
|
||||
- Tier purity: no `node:` imports, no DOM imports, no `setTimeout`. The
|
||||
helper uses `AbortSignal.timeout` for timeouts (universal in Node 17.3+,
|
||||
Bun, modern browsers).
|
||||
@@ -0,0 +1,90 @@
|
||||
# `symbols` — Kraken symbol-mapping table
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
The Kraken public ticker addresses pairs by non-standard names — `XXBTZUSD`
|
||||
for BTC/USD, `XETHZUSD` for ETH/USD, `XDGUSD` for DOGE/USD — using the
|
||||
asset-prefix convention (`X` for native crypto assets, `Z` for fiat) inherited
|
||||
from its early-2014 instrument schema. Every consumer of the `KrakenProvider`
|
||||
wants to address rates by the Suaveplan-standard symbol (`BTC`, `ETH`,
|
||||
`DOGE`), so the provider needs a stable bidirectional lookup table that
|
||||
normalises both directions in O(1).
|
||||
|
||||
This module owns that table. It exports the frozen forward map
|
||||
(`KRAKEN_SYMBOL_MAP`), the derived frozen reverse map
|
||||
(`KRAKEN_PAIR_TO_SYMBOL`), and a `mergeSymbolMap` helper for callers who
|
||||
need to extend coverage without forking the package.
|
||||
|
||||
## Features
|
||||
|
||||
- 12 canonical Suaveplan symbols mapped to their Kraken pair names: BTC,
|
||||
ETH, SOL, USDT, DOT, ADA, LINK, AVAX, ATOM, LTC, DOGE, XRP
|
||||
- Forward + reverse maps both `Object.freeze`-d for identity stability and
|
||||
cheap memoised lookups in the hot path
|
||||
- `mergeSymbolMap(extras)` for caller-supplied extensions — returns a fresh
|
||||
forward + reverse pair without mutating the canonical maps
|
||||
- Zero runtime dependencies, universal
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import {
|
||||
KRAKEN_PAIR_TO_SYMBOL,
|
||||
KRAKEN_SYMBOL_MAP,
|
||||
} from "@suaveplan/currency-provider-kraken/symbols/symbols";
|
||||
|
||||
console.log(KRAKEN_SYMBOL_MAP.BTC); // "XXBTZUSD"
|
||||
console.log(KRAKEN_PAIR_TO_SYMBOL.XXBTZUSD); // "BTC"
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { mergeSymbolMap } from "@suaveplan/currency-provider-kraken/symbols/symbols";
|
||||
|
||||
// Extend with a pair Kraken added after this release shipped.
|
||||
const { forward, reverse } = mergeSymbolMap({
|
||||
MATIC: "MATICUSD",
|
||||
APT: "APTUSD",
|
||||
});
|
||||
|
||||
console.log(forward.MATIC); // "MATICUSD"
|
||||
console.log(reverse.MATICUSD); // "MATIC"
|
||||
|
||||
// Original maps are unchanged — `mergeSymbolMap` never mutates them.
|
||||
console.log("MATIC" in KRAKEN_SYMBOL_MAP); // false
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
| Export | Kind | Description |
|
||||
|---|---|---|
|
||||
| `KRAKEN_SYMBOL_MAP` | `Readonly<Record<string, string>>` | Frozen forward map: 12 Suaveplan symbols → Kraken pair name. |
|
||||
| `KRAKEN_PAIR_TO_SYMBOL` | `Readonly<Record<string, string>>` | Frozen reverse map derived at module load from the forward map. |
|
||||
| `MergedSymbolMaps` | `interface` | Shape returned by `mergeSymbolMap` — `{ forward, reverse }`, both `Readonly<Record<string, string>>`. |
|
||||
| `mergeSymbolMap(extras?)` | `(Readonly<Record<string, string>> \| undefined) => MergedSymbolMaps` | Returns the canonical maps when `extras` is empty/undefined; otherwise returns fresh forward + reverse maps with the extras merged on top. |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The Kraken instrument schema mixes legacy `X<base>Z<quote>` pairs (`XXBTZUSD`)
|
||||
with flat `<base>USD` pairs (`SOLUSD`, `DOTUSD`) added after the schema
|
||||
redesign. The map captures both forms so callers don't have to.
|
||||
- The forward map is intentionally small. Kraken lists hundreds of pairs;
|
||||
the 12 included here are the high-volume subset that match the upstream
|
||||
9-provider bundle's coverage. Callers needing more symbols use the
|
||||
`extraSymbols` constructor field on `KrakenProvider` (which calls
|
||||
`mergeSymbolMap` internally) or call `mergeSymbolMap` directly when they
|
||||
want the maps for their own logic.
|
||||
- `mergeSymbolMap` is a hot-path-aware fast path: when `extras` is empty or
|
||||
undefined, it returns the canonical maps by reference without allocating.
|
||||
- Both canonical maps are frozen, so `Object.assign(KRAKEN_SYMBOL_MAP, …)`
|
||||
silently fails in non-strict mode and throws in strict mode — making
|
||||
accidental mutation impossible.
|
||||
- Determinism: iteration order of `Object.entries(KRAKEN_SYMBOL_MAP)` is
|
||||
property-insertion order per the ES2015 spec, so derived structures
|
||||
(including the reverse map and merged outputs) have deterministic ordering
|
||||
for golden-file tests.
|
||||
Reference in New Issue
Block a user