tardis-dev
Version:
Convenient access to tick-level historical and real-time cryptocurrency market data via Node.js
296 lines (261 loc) • 8.65 kB
text/typescript
import { upperCaseSymbols } from '../handy'
import { BookChange, BookTicker, DerivativeTicker, Liquidation, Trade } from '../types'
import { Mapper, PendingTickerInfoHelper } from './mapper'
// https://www.cryptofacilities.com/resources/hc/en-us/categories/115000132213-API
export const cryptofacilitiesTradesMapper: Mapper<'cryptofacilities', Trade> = {
canHandle(message: CryptofacilitiesTrade | CryptofacilitiesTicker | CryptofacilitiesBookSnapshot | CryptofacilitiesBookUpdate) {
return message.feed === 'trade' && message.event === undefined
},
getFilters(symbols?: string[]) {
symbols = upperCaseSymbols(symbols)
return [
{
channel: 'trade',
symbols
}
]
},
*map(trade: CryptofacilitiesTrade, localTimestamp: Date): IterableIterator<Trade> {
yield {
type: 'trade',
symbol: trade.product_id,
exchange: 'cryptofacilities',
id: trade.uid,
price: trade.price,
amount: trade.qty,
side: trade.side,
timestamp: new Date(trade.time),
localTimestamp: localTimestamp
}
}
}
const mapBookLevel = ({ price, qty }: CryptofacilitiesBookLevel) => {
return { price, amount: qty < 0 ? 0 : qty }
}
export const cryptofacilitiesBookChangeMapper: Mapper<'cryptofacilities', BookChange> = {
canHandle(message: CryptofacilitiesTrade | CryptofacilitiesTicker | CryptofacilitiesBookSnapshot | CryptofacilitiesBookUpdate) {
return message.event === undefined && (message.feed === 'book' || message.feed === 'book_snapshot')
},
getFilters(symbols?: string[]) {
symbols = upperCaseSymbols(symbols)
return [
{
channel: 'book',
symbols
},
{
channel: 'book_snapshot',
symbols
}
]
},
*map(message: CryptofacilitiesBookSnapshot | CryptofacilitiesBookUpdate, localTimestamp: Date): IterableIterator<BookChange> {
if (message.feed === 'book_snapshot') {
yield {
type: 'book_change',
symbol: message.product_id,
exchange: 'cryptofacilities',
isSnapshot: true,
bids: message.bids.map(mapBookLevel),
asks: message.asks.map(mapBookLevel),
timestamp: message.timestamp !== undefined ? new Date(message.timestamp) : localTimestamp,
localTimestamp: localTimestamp
}
} else {
const isAsk = message.side === 'sell'
const update = [
{
price: message.price,
amount: message.qty < 0 ? 0 : message.qty
}
]
yield {
type: 'book_change',
symbol: message.product_id,
exchange: 'cryptofacilities',
isSnapshot: false,
bids: isAsk ? [] : update,
asks: isAsk ? update : [],
timestamp: message.timestamp !== undefined ? new Date(message.timestamp) : localTimestamp,
localTimestamp: localTimestamp
}
}
}
}
export class CryptofacilitiesDerivativeTickerMapper implements Mapper<'cryptofacilities', DerivativeTicker> {
constructor(private readonly _useRelativeFundingRate: boolean) {}
private readonly pendingTickerInfoHelper = new PendingTickerInfoHelper()
canHandle(message: CryptofacilitiesTrade | CryptofacilitiesTicker | CryptofacilitiesBookSnapshot | CryptofacilitiesBookUpdate) {
return message.feed === 'ticker' && message.event === undefined
}
getFilters(symbols?: string[]) {
symbols = upperCaseSymbols(symbols)
return [
{
channel: 'ticker',
symbols
} as const
]
}
*map(ticker: CryptofacilitiesTicker, localTimestamp: Date): IterableIterator<DerivativeTicker> {
const pendingTickerInfo = this.pendingTickerInfoHelper.getPendingTickerInfo(ticker.product_id, 'cryptofacilities')
if (ticker.next_funding_rate_time === 0) {
return
}
if (this._useRelativeFundingRate) {
pendingTickerInfo.updateFundingRate(ticker.relative_funding_rate)
pendingTickerInfo.updatePredictedFundingRate(ticker.relative_funding_rate_prediction)
} else {
pendingTickerInfo.updateFundingRate(ticker.funding_rate)
pendingTickerInfo.updatePredictedFundingRate(ticker.funding_rate_prediction)
}
pendingTickerInfo.updateFundingTimestamp(
ticker.next_funding_rate_time !== undefined ? new Date(ticker.next_funding_rate_time) : undefined
)
pendingTickerInfo.updateIndexPrice(ticker.index)
pendingTickerInfo.updateMarkPrice(ticker.markPrice)
pendingTickerInfo.updateOpenInterest(ticker.openInterest)
pendingTickerInfo.updateLastPrice(ticker.last)
pendingTickerInfo.updateTimestamp(new Date(ticker.time))
if (pendingTickerInfo.hasChanged()) {
yield pendingTickerInfo.getSnapshot(localTimestamp)
}
}
}
export const cryptofacilitiesLiquidationsMapper: Mapper<'cryptofacilities', Liquidation> = {
canHandle(message: CryptofacilitiesTrade | CryptofacilitiesTicker | CryptofacilitiesBookSnapshot | CryptofacilitiesBookUpdate) {
return message.feed === 'trade' && message.event === undefined && message.type === 'liquidation'
},
getFilters(symbols?: string[]) {
symbols = upperCaseSymbols(symbols)
return [
{
channel: 'trade',
symbols
}
]
},
*map(liquidationTrade: CryptofacilitiesTrade, localTimestamp: Date): IterableIterator<Liquidation> {
yield {
type: 'liquidation',
symbol: liquidationTrade.product_id,
exchange: 'cryptofacilities',
id: liquidationTrade.uid,
price: liquidationTrade.price,
amount: liquidationTrade.qty,
side: liquidationTrade.side,
timestamp: new Date(liquidationTrade.time),
localTimestamp: localTimestamp
}
}
}
export const cryptofacilitiesBookTickerMapper: Mapper<'cryptofacilities', BookTicker> = {
canHandle(message: CryptofacilitiesTicker) {
return message.feed === 'ticker' && message.event === undefined
},
getFilters(symbols?: string[]) {
symbols = upperCaseSymbols(symbols)
return [
{
channel: 'ticker',
symbols
}
]
},
*map(cryptofacilitiesTicker: CryptofacilitiesTicker, localTimestamp: Date): IterableIterator<BookTicker> {
const ticker: BookTicker = {
type: 'book_ticker',
symbol: cryptofacilitiesTicker.product_id,
exchange: 'cryptofacilities',
askAmount: cryptofacilitiesTicker.ask_size,
askPrice: cryptofacilitiesTicker.ask,
bidPrice: cryptofacilitiesTicker.bid,
bidAmount: cryptofacilitiesTicker.bid_size,
timestamp: new Date(cryptofacilitiesTicker.time),
localTimestamp: localTimestamp
}
yield ticker
}
}
type CryptofacilitiesTrade = {
feed: 'trade'
type: 'liquidation' | 'fill'
uid: string | undefined
event: undefined
product_id: string
side: 'buy' | 'sell'
time: number
qty: number
price: number
}
type CryptofacilitiesTicker =
| {
feed: 'ticker'
event: undefined
product_id: string
index: number
last: number
openInterest: number
markPrice: number
funding_rate: number | undefined
funding_rate_prediction: number | undefined
next_funding_rate_time: number | undefined
time: number
bid: number | undefined
ask: number | undefined
bid_size: number | undefined
ask_size: number | undefined
relative_funding_rate: undefined
relative_funding_rate_prediction: undefined
}
| {
time: 1680307200005
product_id: 'PF_1INCHUSD'
event: undefined
funding_rate: -1.861241614653e-6
funding_rate_prediction: -4.87669653882e-6
relative_funding_rate: number | undefined
relative_funding_rate_prediction: number | undefined
next_funding_rate_time: 1680307200000
feed: 'ticker'
bid: 0.5609
ask: 0.5621
bid_size: 1123.0
ask_size: 8931.0
volume: 10902.0
dtm: 0
leverage: '10x'
index: 0.56158
premium: -0.0
last: 0.5594
change: -1.0086710316758118
suspended: false
tag: 'perpetual'
pair: '1INCH:USD'
openInterest: 27481.0
markPrice: 0.56147544277
maturityTime: 0
post_only: false
volumeQuote: 6028.1795
}
type CryptofacilitiesBookLevel = {
price: number
qty: number
}
type CryptofacilitiesBookSnapshot = {
feed: 'book_snapshot'
event: undefined
product_id: string
timestamp: number | undefined
bids: CryptofacilitiesBookLevel[]
asks: CryptofacilitiesBookLevel[]
}
type CryptofacilitiesBookUpdate = {
feed: 'book'
event: undefined
product_id: string
side: 'buy' | 'sell'
price: number
qty: number
timestamp: number | undefined
}