@hackape/tardis-dev
Version:
Convenient access to tick-level historical and real-time cryptocurrency market data via Node.js
178 lines (149 loc) • 4.75 kB
text/typescript
import { Mapper, PendingTickerInfoHelper } from './mapper'
import { Trade, BookChange, DerivativeTicker } from '../types'
// phemex provides timestamps in nanoseconds
const fromNanoSecondsToDate = (nanos: number) => {
const microtimestamp = Math.floor(nanos / 1000)
const timestamp = new Date(microtimestamp / 1000)
timestamp.μs = microtimestamp % 1000
return timestamp
}
function getPriceScale(symbol: string) {
if (symbol.startsWith('s')) {
return 1e8
}
return 1e4
}
function getQtyScale(symbol: string) {
if (symbol.startsWith('s')) {
return 1e8
}
return 1
}
function getSymbols(symbols?: string[]) {
if (symbols === undefined) {
return
}
return symbols.map((symbol) => {
if (symbol.startsWith('S')) {
return symbol.charAt(0).toLowerCase() + symbol.slice(1)
}
return symbol
})
}
export const phemexTradesMapper: Mapper<'phemex', Trade> = {
canHandle(message: PhemexTradeMessage) {
return 'trades' in message && message.type === 'incremental'
},
getFilters(symbols?: string[]) {
return [
{
channel: 'trades',
symbols: getSymbols(symbols)
} as const
]
},
*map(message: PhemexTradeMessage, localTimestamp: Date): IterableIterator<Trade> {
for (const [timestamp, side, priceEp, qty] of message.trades) {
const symbol = message.symbol
yield {
type: 'trade',
symbol: symbol.toUpperCase(),
exchange: 'phemex',
id: undefined,
price: priceEp / getPriceScale(symbol),
amount: qty / getQtyScale(symbol),
side: side === 'Buy' ? 'buy' : 'sell',
timestamp: fromNanoSecondsToDate(timestamp),
localTimestamp: localTimestamp
}
}
}
}
const mapBookLevelForSymbol = (symbol: string) => ([priceEp, qty]: PhemexBookLevel) => {
return {
price: priceEp / getPriceScale(symbol),
amount: qty / getQtyScale(symbol)
}
}
export const phemexBookChangeMapper: Mapper<'phemex', BookChange> = {
canHandle(message: PhemexBookMessage) {
return 'book' in message
},
getFilters(symbols?: string[]) {
return [
{
channel: 'book',
symbols: getSymbols(symbols)
} as const
]
},
*map(message: PhemexBookMessage, localTimestamp: Date): IterableIterator<BookChange> {
const symbol = message.symbol
const mapBookLevel = mapBookLevelForSymbol(symbol)
yield {
type: 'book_change',
symbol: symbol.toUpperCase(),
exchange: 'phemex',
isSnapshot: message.type === 'snapshot',
bids: message.book.bids.map(mapBookLevel),
asks: message.book.asks.map(mapBookLevel),
timestamp: fromNanoSecondsToDate(message.timestamp),
localTimestamp
}
}
}
export class PhemexDerivativeTickerMapper implements Mapper<'phemex', DerivativeTicker> {
private readonly pendingTickerInfoHelper = new PendingTickerInfoHelper()
canHandle(message: PhemexTicker) {
return 'market24h' in message
}
getFilters(symbols?: string[]) {
return [
{
channel: 'market24h',
symbols
} as const
]
}
*map(message: PhemexTicker, localTimestamp: Date): IterableIterator<DerivativeTicker> {
const pendingTickerInfo = this.pendingTickerInfoHelper.getPendingTickerInfo(message.market24h.symbol, 'phemex')
const phemexTicker = message.market24h
pendingTickerInfo.updateFundingRate(phemexTicker.fundingRate / 100000000)
pendingTickerInfo.updatePredictedFundingRate(phemexTicker.predFundingRate / 100000000)
pendingTickerInfo.updateIndexPrice(phemexTicker.indexPrice / 10000)
pendingTickerInfo.updateMarkPrice(phemexTicker.markPrice / 10000)
pendingTickerInfo.updateOpenInterest(phemexTicker.openInterest)
pendingTickerInfo.updateLastPrice(phemexTicker.close / 10000)
pendingTickerInfo.updateTimestamp(fromNanoSecondsToDate(message.timestamp))
if (pendingTickerInfo.hasChanged()) {
yield pendingTickerInfo.getSnapshot(localTimestamp)
}
}
}
type PhemexTradeMessage = {
symbol: string
trades: [[number, 'Buy' | 'Sell', number, number]]
type: 'incremental' | 'snapshot'
}
type PhemexBookLevel = [number, number]
type PhemexBookMessage = {
book: {
asks: PhemexBookLevel[]
bids: PhemexBookLevel[]
}
symbol: string
timestamp: number
type: 'incremental' | 'snapshot'
}
type PhemexTicker = {
market24h: {
fundingRate: number
indexPrice: number
markPrice: number
openInterest: number
predFundingRate: number
symbol: string
close: number
}
timestamp: number
}