tardis-machine
Version:
Locally runnable server with built-in data caching, providing both tick-level historical and consolidated real-time cryptocurrency market data via HTTP and WebSocket APIs
211 lines (175 loc) • 5.2 kB
JavaScript
const fetch = require('node-fetch')
const split2 = require('split2')
const WebSocket = require('ws')
const serialize = (options) => {
return encodeURIComponent(JSON.stringify(options))
}
class SimpleWebsocketClient {
constructor(url, onMessageCB, onOpen) {
this._socket = new WebSocket(url)
this._socket.on('open', onOpen)
this._socket.on('message', onMessageCB)
this._socket.on('error', (err) => {
console.log('SimpleWebsocketClient error', err)
})
}
send(payload) {
this._socket.send(JSON.stringify(payload))
}
async closed() {
await new Promise((resolve) => {
this._socket.on('close', () => {
resolve()
})
})
}
}
const EXCHANGE = 'bitmex'
const SYMBOL = 'XBTUSD'
const TRADES_AND_BOOK_FILTERS = [
{
channel: 'trade',
symbols: [SYMBOL]
},
{
channel: 'orderBookL2',
symbols: [SYMBOL]
}
]
const TRADES_AND_BOOK_SUBSCRIPTION_MESSAGES = [
{
op: 'subscribe',
args: [`trade:${SYMBOL}`, `orderBookL2:${SYMBOL}`]
}
]
const FROM_DATE = '2020-02-01'
const TO_DATE = '2020-02-02'
async function httpReplayBenchmark({ JSONParseResponse }) {
const options = {
exchange: EXCHANGE,
filters: TRADES_AND_BOOK_FILTERS,
from: FROM_DATE,
to: TO_DATE
}
const response = await fetch(`http://localhost:8000/replay?options=${serialize(options)}`)
const messagesStream = response.body.pipe(split2())
let messagesCount = 0
let startTime = new Date()
for await (let line of messagesStream) {
if (JSONParseResponse) {
JSON.parse(line)
}
messagesCount++
}
const elapsedSeconds = (new Date() - startTime) / 1000
const messagesPerSecond = Math.round(messagesCount / elapsedSeconds)
console.log('HTTP /replay finished', {
JSONParseResponse,
messagesPerSecond,
messagesCount,
elapsedSeconds
})
}
async function httpReplayNormalizedBenchmark({ computeTBTBookSnapshots }) {
const options = {
exchange: EXCHANGE,
symbols: [SYMBOL],
from: FROM_DATE,
to: TO_DATE,
dataTypes: ['trade', 'book_change']
}
if (computeTBTBookSnapshots) {
options.dataTypes.push('book_snapshot_50_0ms')
}
const response = await fetch(`http://localhost:8000/replay-normalized?options=${serialize(options)}`)
const messagesStream = response.body.pipe(split2())
let messagesCount = 0
let startTime = new Date()
for await (let line of messagesStream) {
messagesCount++
}
const elapsedSeconds = (new Date() - startTime) / 1000
const messagesPerSecond = Math.round(messagesCount / elapsedSeconds)
console.log('HTTP /replay-normalized finished', {
computeTBTBookSnapshots,
messagesPerSecond,
messagesCount,
elapsedSeconds
})
}
async function wsReplayBenchmark({ JSONParseResponse }) {
let messagesCount = 0
let startTime
const simpleBitmexWSClient = new SimpleWebsocketClient(
`ws://localhost:8001/ws-replay?exchange=${EXCHANGE}&from=${FROM_DATE}&to=${TO_DATE}`,
(message) => {
if (!startTime) {
startTime = new Date()
}
if (JSONParseResponse) {
JSON.parse(message)
}
messagesCount++
},
() => {
for (const sub of TRADES_AND_BOOK_SUBSCRIPTION_MESSAGES) {
simpleBitmexWSClient.send(sub)
}
}
)
await simpleBitmexWSClient.closed()
const elapsedSeconds = (new Date() - startTime) / 1000
const messagesPerSecond = Math.round(messagesCount / elapsedSeconds)
console.log('WS /ws-replay finished', {
JSONParseResponse,
messagesPerSecond,
messagesCount,
elapsedSeconds
})
}
async function wsReplayNormalizedBenchmark({ computeTBTBookSnapshots }) {
const options = {
exchange: EXCHANGE,
symbols: [SYMBOL],
from: FROM_DATE,
to: TO_DATE,
dataTypes: ['trade', 'book_change']
}
if (computeTBTBookSnapshots) {
options.dataTypes.push('book_snapshot_50_0ms')
}
let messagesCount = 0
let startTime
const simpleBitmexWSClient = new SimpleWebsocketClient(
`ws://localhost:8001/ws-replay-normalized?options=${serialize(options)}`,
(message) => {
if (!startTime) {
startTime = new Date()
}
messagesCount++
},
() => {}
)
await simpleBitmexWSClient.closed()
const elapsedSeconds = (new Date() - startTime) / 1000
const messagesPerSecond = Math.round(messagesCount / elapsedSeconds)
console.log('WS /ws-replay-normalized finished', {
computeTBTBookSnapshots,
messagesPerSecond,
messagesCount,
elapsedSeconds
})
}
async function runBenchmarks() {
console.log(`tardis-machine benchmark for ${EXCHANGE} from ${FROM_DATE} to ${TO_DATE}`)
console.log('\n')
await httpReplayBenchmark({ JSONParseResponse: false })
await httpReplayBenchmark({ JSONParseResponse: true })
await wsReplayBenchmark({ JSONParseResponse: true })
await httpReplayNormalizedBenchmark({ computeTBTBookSnapshots: false })
await httpReplayNormalizedBenchmark({ computeTBTBookSnapshots: true })
await wsReplayNormalizedBenchmark({ computeTBTBookSnapshots: false })
await wsReplayNormalizedBenchmark({ computeTBTBookSnapshots: true })
}
// assumes tardis-machine server is running
runBenchmarks()