Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add crossed orderbook resolving example using websocket #301

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
246 changes: 246 additions & 0 deletions v4-client-js/examples/websocket_orderbook_example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
import { Network } from '../src/clients/constants';
import { SocketClient } from '../src/clients/socket-client';

function test(): void {
let orderBookBidList: [number, number, number][] = [];
let orderBookAskList: [number, number, number][] = [];

const mySocket = new SocketClient(
Network.mainnet().indexerConfig,
() => {
console.log('socket opened');

mySocket.subscribeToOrderbook('ETH-USD');
},
() => {
console.log('socket closed');
},
(message) => {
try {
if (typeof message.data === "string") {
const jsonString = message.data as string;
const parsingData = JSON.parse(jsonString);
const messageId = parsingData.message_id;
const orderBookDataList = parsingData.contents;

if (orderBookDataList instanceof Array) {
// common orderBook data;
[orderBookBidList, orderBookAskList] = updateOrderBook(
orderBookDataList,
orderBookBidList,
orderBookAskList,
messageId,
);

// sort
orderBookBidList = sortByNthElementDesc(
orderBookBidList,
0,
);
orderBookAskList = sortByNthElementAsc(
orderBookAskList,
0,
);

// resolving crossed orderBook
if (orderBookBidList[0][0] >= orderBookAskList[0][0]) {
[orderBookBidList, orderBookAskList] =
resolveCrossedOrderBook(
orderBookBidList,
orderBookAskList,
);
}

printOrderBook(orderBookBidList, orderBookAskList);

if (orderBookBidList.length > 300) {
orderBookBidList.splice(50);
}

if (orderBookAskList.length > 300) {
orderBookAskList.splice(50);
}
} else if (
orderBookDataList !== null &&
orderBookDataList !== undefined
) {
// initial OrderBook data
setInitialOrderBook(
orderBookDataList,
orderBookBidList,
orderBookAskList,
messageId,
);
}
}
} catch (e) {
console.error('Error parsing JSON message:', e);
}
},
(event) => {
console.error('Encountered error:', event.message);
},
);

mySocket.connect();
}

const sortByNthElementAsc = (arr: [number, number, number][], n: number): [number, number, number][] => {
return arr.sort((a, b) => {
if (a[n] < b[n]) return -1;
if (a[n] > b[n]) return 1;
return 0;
});
};
const sortByNthElementDesc = (arr: [number, number, number][], n: number): [number, number, number][] => {
return arr.sort((a, b) => {
if (a[n] > b[n]) return -1;
if (a[n] < b[n]) return 1;
return 0;
});
};

const printOrderBook = (
orderBookBidList: [number, number, number][],
orderBookAskList: [number, number, number][],
): void => {
// print
console.log(`OrderBook for ETH-USD:`);
console.log(`Price Qty`);
for (let i = 4; i > -1; i--) {
const priceStr = String(orderBookAskList[i][0]);
const spaces = createSpaces(10 - priceStr.length);
console.log(`${priceStr}${spaces}${orderBookAskList[i][1]}`);
}
console.log('---------------------');
for (let i = 0; i < 5; i++) {
const priceStr = String(orderBookAskList[i][0]);
const spaces = createSpaces(10 - priceStr.length);
console.log(`${priceStr}${spaces}${orderBookBidList[i][1]}`);
}
console.log('');
};

function createSpaces(count: number): string {
if(count <= 0 ){
return "";
}

let spaces = "";
for (let i = 0; i < count; i++) {
spaces += " ";
}
return spaces;
}

const resolveCrossedOrderBook = (
orderBookBidList: [number, number, number][],
orderBookAskList: [number, number, number][],
): [[number, number, number][], [number, number, number][]] => {
while (orderBookBidList[0][0] >= orderBookAskList[0][0]) {
if (orderBookBidList[0][2] < orderBookAskList[0][2]) {
orderBookBidList.shift();
} else if (orderBookBidList[0][2] > orderBookAskList[0][2]) {
orderBookAskList.shift();
} else {
if (orderBookBidList[0][1] > orderBookAskList[0][1]) {
orderBookBidList[0][1] -= orderBookAskList[0][1];
orderBookAskList.shift();
} else if (orderBookBidList[0][1] < orderBookAskList[0][1]) {
orderBookAskList[0][1] -= orderBookBidList[0][1];
orderBookBidList.shift();
} else {
orderBookAskList.shift();
orderBookBidList.shift();
}
}
}

return [orderBookBidList, orderBookAskList];
};
const setInitialOrderBook = (
orderBookDataList: { bids: []; asks: [] },
orderBookBidList: [number, number, number][],
orderBookAskList: [number, number, number][],
messageId: number,
): void => {
orderBookDataList.bids.forEach((item: { price: string; size: string }) => {
orderBookBidList.push([Number(item.price), Number(item.size), messageId]);
});

orderBookDataList.asks.forEach((item: { price: string; size: string }) => {
orderBookAskList.push([Number(item.price), Number(item.size), messageId]);
});
};

const updateOrderBook = (
orderBookDataList: { bids: [[]]; asks: [[]] }[],
orderBookBidList: [number, number, number][],
orderBookAskList: [number, number, number][],
messageId: number,
): [[number, number, number][], [number, number, number][]] => {
orderBookDataList.forEach((entry: { bids: [[]]; asks: [[]] }) => {
if (entry.bids !== null && entry.bids !== undefined) {
const flattened = entry.bids.reduce((acc: any[], val: any[]) => acc.concat(val), []);
const entryBidPrice = Number(flattened[0]);
const entryBidSize = Number(flattened[1]);

// remove prices with zero Qty
if (entryBidSize === 0) {
for (let i = orderBookBidList.length - 1; i >= 0; i--) {
if (orderBookBidList[i][0] === entryBidPrice) {
orderBookBidList.splice(i, 1);
}
}
} else {
// The price that already exists in the order book is modified only Qty
if (
orderBookBidList.some((innerArray) => innerArray[0] === entryBidPrice)
) {
orderBookBidList.forEach((item, index) => {
if (item[0] === entryBidPrice) {
orderBookBidList[index][1] = entryBidSize;
orderBookBidList[index][2] = messageId;
}
});
} else {
// Add new data to order book
orderBookBidList.push([entryBidPrice, entryBidSize, messageId]);
}
}
}
if (entry.asks !== null && entry.asks !== undefined) {
const flattened = entry.asks.reduce((acc: any[], val: any[]) => acc.concat(val), []);
const entryAskPrice = Number(flattened[0]);
const entryAskSize = Number(flattened[1]);

if (entryAskSize === 0) {
// remove prices with zero Qty
for (let i = orderBookAskList.length - 1; i >= 0; i--) {
if (orderBookAskList[i][0] === entryAskPrice) {
orderBookAskList.splice(i, 1);
}
}
} else {
// The price that already exists in the order book is modified only Qty
if (
orderBookAskList.some((innerArray) => innerArray[0] === entryAskPrice)
) {
orderBookAskList.forEach((item, index) => {
if (item[0] === entryAskPrice) {
orderBookAskList[index][1] = entryAskSize;
orderBookAskList[index][2] = messageId;
}
});
} else {
// Add new data to order book
orderBookAskList.push([entryAskPrice, entryAskSize, messageId]);
}
}
}
});

return [orderBookBidList, orderBookAskList];
};

test();