UNPKG

tardis-dev

Version:

Convenient access to tick-level historical and real-time cryptocurrency market data via Node.js

190 lines (172 loc) 6.2 kB
import { getOptions } from './options.ts' import type { SymbolType } from './exchangedetails.ts' import type { Exchange } from './types.ts' import { getJSON } from './handy.ts' export async function getInstrumentInfo(exchange: Exchange): Promise<InstrumentInfo[]> export async function getInstrumentInfo(exchange: Exchange | Exchange[], filter: InstrumentInfoFilter): Promise<InstrumentInfo[]> export async function getInstrumentInfo(exchange: Exchange, symbol: string): Promise<InstrumentInfo> export async function getInstrumentInfo(exchange: Exchange | Exchange[], filterOrSymbol?: InstrumentInfoFilter | string) { if (Array.isArray(exchange)) { const exchanges = exchange const results = await Promise.all(exchanges.map((e) => getInstrumentInfoForExchange(e, filterOrSymbol))) return results.flat() } else { return getInstrumentInfoForExchange(exchange, filterOrSymbol) } } async function getInstrumentInfoForExchange(exchange: Exchange, filterOrSymbol?: InstrumentInfoFilter | string) { const options = getOptions() let url = `${options.endpoint}/instruments/${exchange}` if (typeof filterOrSymbol === 'string') { url += `/${encodeURIComponent(filterOrSymbol)}` } else if (typeof filterOrSymbol === 'object') { url += `?filter=${encodeURIComponent(JSON.stringify(filterOrSymbol))}` } try { const { data } = await getJSON(url, { headers: { Authorization: `Bearer ${options.apiKey}` } }) return data } catch (e: any) { // expose 400 error message from server if (e.response?.statusCode === 400) { let err: { code: Number; message: string } try { err = JSON.parse(e.response.body) } catch { throw e } throw err ? new Error(`${err.message} (${err.code})`) : e } else { throw e } } } export async function findInstrumentSymbols( exchanges: Exchange[], filter: InstrumentInfoFilter, selector: InstrumentSymbolSelector = 'id' ): Promise<InstrumentSymbols[]> { if (selector !== 'id' && selector !== 'datasetId') { throw new Error("Invalid selector. Supported values are 'id' and 'datasetId'.") } return await Promise.all( exchanges.map(async (exchange) => { const instruments = (await getInstrumentInfoForExchange(exchange, filter)) as InstrumentInfo[] return { exchange, symbols: instruments.map((instrument) => (selector === 'datasetId' ? (instrument.datasetId ?? instrument.id) : instrument.id)) } }) ) } export type InstrumentSymbolSelector = 'id' | 'datasetId' export type InstrumentSymbols = { exchange: Exchange symbols: string[] } export type InstrumentInfoFilter = { baseCurrency?: string | string[] quoteCurrency?: string | string[] type?: SymbolType | SymbolType[] contractType?: ContractType | ContractType[] underlyingType?: UnderlyingType | UnderlyingType[] active?: boolean availableSince?: string availableTo?: string } export type ContractType = | 'move' | 'linear_future' | 'inverse_future' | 'quanto_future' | 'linear_perpetual' | 'inverse_perpetual' | 'quanto_perpetual' | 'put_option' | 'call_option' | 'turbo_put_option' | 'turbo_call_option' | 'spread' | 'interest_rate_swap' | 'repo' | 'index' export type UnderlyingType = 'native' | 'equity' | 'commodity' | 'fixed_income' | 'fx' | 'index' | 'pre_market' export interface InstrumentInfo { /** symbol id */ id: string /** dataset symbol id, may differ from id */ datasetId?: string /** exchange id */ exchange: string /** normalized, so for example bitmex XBTUSD has base currency set to BTC not XBT */ baseCurrency: string /** normalized, so for example bitfinex BTCUST has quote currency set to USDT, not UST */ quoteCurrency: string type: SymbolType /** derivative contract type */ contractType?: ContractType /** indicates if the instrument can currently be traded. */ active: boolean /** date in ISO format */ availableSince: string /** date in ISO format */ availableTo?: string /** date in ISO format, when the instrument was first listed on the exchange */ listing?: string /** in ISO format, only for futures and options */ expiry?: string /** expiration schedule type */ expirationType?: 'daily' | 'weekly' | 'next_week' | 'quarter' | 'next_quarter' /** the underlying index for derivatives */ underlyingIndex?: string /** underlying asset class */ underlyingType?: UnderlyingType /** price tick size, price precision can be calculated from it */ priceIncrement: number /** amount tick size, amount/size precision can be calculated from it */ amountIncrement: number /** min order size */ minTradeAmount: number /** minimum notional value */ minNotional?: number /** consider it as illustrative only, as it depends in practice on account traded volume levels, different categories, VIP levels, owning exchange currency etc */ makerFee: number /** consider it as illustrative only, as it depends in practice on account traded volume levels, different categories, VIP levels, owning exchange currency etc */ takerFee: number /** only for derivatives */ inverse?: boolean /** only for derivatives */ contractMultiplier?: number /** only for quanto instruments */ quanto?: boolean /** only for quanto instruments as settlement currency is different base/quote currency */ settlementCurrency?: string /** strike price, only for options */ strikePrice?: number /** option type, only for options */ optionType?: 'call' | 'put' /** margin mode */ marginMode?: 'isolated' | 'cross' /** whether margin trading is supported (spot) */ margin?: boolean /** if this instrument is an alias for another */ aliasFor?: string /** historical changes to instrument parameters */ changes?: { until: string priceIncrement?: number amountIncrement?: number contractMultiplier?: number minTradeAmount?: number makerFee?: number takerFee?: number quanto?: boolean inverse?: boolean settlementCurrency?: string underlyingIndex?: string contractType?: ContractType quoteCurrency?: string type?: string }[] }