SPB Git forge

spb/market-atlas

Public
12commits 1branches 0releases
1.1 MBsize
maindefault branch
10 days agolast push
TypeScript 96.7% SQL 1.6% CSS 0.8% JavaScript 0.5%
3.4 KB · 103 lines typescript
Raw Blame History
1import type { InstrumentHint, NormalizedObservation, RealtimeStatus, RightsStatus, TimestampTrust } from "@market-atlas/market-model";2import type { ProposedInstrument } from "@market-atlas/connector-sdk";34export const CRYPTO_NAMES: Record<string, string> = {5  BTC: "Bitcoin",6  ETH: "Ether",7  SOL: "Solana",8  XRP: "XRP",9  ADA: "Cardano",10  DOGE: "Dogecoin",11  LTC: "Litecoin",12  LINK: "Chainlink",13  AVAX: "Avalanche",14  DOT: "Polkadot",15  BNB: "BNB",16  TRX: "TRON",17  MATIC: "Polygon",18  POL: "Polygon",19  UNI: "Uniswap",20  ATOM: "Cosmos",21  XLM: "Stellar",22  BCH: "Bitcoin Cash",23  ETC: "Ethereum Classic",24  AAVE: "Aave",25  NEAR: "NEAR Protocol",26  SUI: "Sui",27  TON: "Toncoin",28  SHIB: "Shiba Inu",29  PAXG: "PAX Gold",30};3132/** Major coins observed across venues. */33export const MAJOR_BASES = ["BTC", "ETH", "SOL", "XRP", "ADA", "DOGE", "LTC", "LINK", "AVAX", "DOT", "BCH", "XLM", "UNI", "AAVE", "NEAR", "SUI", "PAXG"];3435export function cryptoHint(base: string, quote: string, exchangeId: string): InstrumentHint {36  const b = base.toUpperCase();37  const q = quote.toUpperCase();38  return {39    assetClass: "CRYPTO",40    name: `${CRYPTO_NAMES[b] ?? b} / ${q}`,41    exchangeId,42    currency: q,43    country: "XX",44    base: b,45    quote: q,46    securityType: "SPOT",47    metadata: { featured: ["BTC", "ETH", "SOL", "XRP"].includes(b) && (q === "USD" || q === "USDT") },48  };49}5051export function cryptoSeeds(pairs: Array<[string, string]>, exchangeId: string, toSymbol: (b: string, q: string) => string): ProposedInstrument[] {52  return pairs.map(([b, q]) => ({ symbol: toSymbol(b, q), hint: cryptoHint(b, q, exchangeId), aliases: [`${b}-${q}`, `${b}/${q}`, `${b}${q}`] }));53}5455export interface TickerFields {56  last?: unknown;57  open?: unknown;58  high?: unknown;59  low?: unknown;60  bid?: unknown;61  ask?: unknown;62  bidSize?: unknown;63  askSize?: unknown;64  volume?: unknown;65  vwap?: unknown;66}6768const num = (v: unknown): number | null => {69  if (v == null || v === "") return null;70  const n = typeof v === "number" ? v : Number(v);71  return Number.isFinite(n) ? n : null;72};7374/** Expand a venue ticker into field observations, skipping missing/invalid values. */75export function tickerObservations(76  symbol: string,77  base: string,78  quote: string,79  exchangeId: string,80  f: TickerFields,81  meta: { sourceTimestamp: number | null; timestampTrust: TimestampTrust; sequence?: number | string | null; rightsStatus: RightsStatus; realtimeStatus: RealtimeStatus },82): NormalizedObservation[] {83  const hint = cryptoHint(base, quote, exchangeId);84  const out: NormalizedObservation[] = [];85  const push = (field: NormalizedObservation["field"], v: unknown) => {86    const n = num(v);87    if (n == null) return;88    if (field !== "VOLUME" && field !== "BID_SIZE" && field !== "ASK_SIZE" && n <= 0) return;89    out.push({ symbol, instrumentHint: hint, field, value: n, currency: quote.toUpperCase(), observationType: field === "BID" || field === "ASK" || field === "BID_SIZE" || field === "ASK_SIZE" ? "QUOTE" : "TRADE", sourceTimestamp: meta.sourceTimestamp, timestampTrust: meta.timestampTrust, sequence: meta.sequence ?? null, rightsStatus: meta.rightsStatus, realtimeStatus: meta.realtimeStatus });90  };91  push("LAST_PRICE", f.last);92  push("OPEN", f.open);93  push("HIGH", f.high);94  push("LOW", f.low);95  push("BID", f.bid);96  push("ASK", f.ask);97  push("BID_SIZE", f.bidSize);98  push("ASK_SIZE", f.askSize);99  push("VOLUME", f.volume);100  push("VWAP", f.vwap);101  return out;102}103