xts-marketdata-api
Version:
The official JS client library for the Symphony Fintech MarketData API's
1,008 lines (877 loc) • 45.1 kB
JavaScript
var settings = require('./config/app/settings.json');
var events = require('events');
var inMemoryStore = require('./inMemoryStore');
var linq = require("linq");
var socketIoClient = require("socket.io-client");
var logger = require('./logger');
var config = require('./config/app/config.json');
var merge = require('deepmerge');
var pako = require('pako');
const OpenInterestEventMessage = require("./OpenInterestEventMessage");
const MarketDepthEventMessage = require("./MarketDepthEvent");
const TouchlineEventMessage = require("./TouchlineDataEventMessage");
function handleMessage(messageCode, dataView, count) {
let parsedObject = null;
switch (messageCode) {
case settings.enums.marketDataPorts.openInterestEvent:
{
const openInterestEvent = new OpenInterestEventMessage();
count = openInterestEvent.deserialize(dataView, count);
parsedObject = openInterestEvent;
}
break;
case settings.enums.marketDataPorts.marketDepthEvent:
{
const marketDepthEvent = new MarketDepthEventMessage();
count = marketDepthEvent.deserialize(dataView, count);
parsedObject = marketDepthEvent;
}
break;
case settings.enums.marketDataPorts.touchlineEvent:
{
const touchlineEvent = new TouchlineEventMessage();
count = touchlineEvent.deserialize(dataView, count);
parsedObject = touchlineEvent;
}
break;
default:
console.warn("Unknown message code:", messageCode);
break;
}
return parsedObject;
}
module.exports = class MDEmitter {
/**
* Constructs an XTSEmitter instance to enable data transfer via socket related events.
*
* @constructor
*
* @param {String} url
* url parameter is used to connect to the particular server.
*
*/
constructor(url) {
this.url = url === undefined ? config.url : url;
let arr = this.url.split("/");
this.url = arr[0] + '/' + arr[1] + '/' + arr[2];
this.path = '/';
for (let i = 3; i < arr.length; i++) {
if (i === 3) {
this.path = this.path + arr[i];
}
else {
this.path = this.path + '/' + arr[i];
}
}
this.path = this.path + '/' + 'socket.io';
this.socketMD = {
isConnected: false,
socketMarketData: null,
interval: null,
};
this.eventEmitter = new events.EventEmitter();
}
/**
* set the token value by providing the token in the input
*
* @param {string} token
* token parameter will be generated after successful login and will be used in other private API's
*
*/
set token(token) {
this._token = token;
}
/**
* Returns the token generated after successful logIn
*
*
* @return
* the value of token generated after successful logIn
*/
get token() {
return this._token;
}
/**
* set the userID value by providing the userID in the input
*
* @param {string} userID
* userID for the particular user
*/
set userID(userID) {
this._userID = userID;
}
/**
* Returns userID for the particular user
*
*
* @return
* the userID for the particular user
*/
get userID() {
return this._userID;
}
/**
* set the publishFormat(JSON|Binary) required for the user
*
* @param {string} publishFormat
* publishFormat (JSON|Binary) required for the user
*/
set publishFormat(publishFormat) {
this._publishFormat = publishFormat;
}
/**
* Returns publishFormat(JSON|Binary) required for the user
*
*
* @return
* the publishFormat(JSON|Binary) required for the user
*/
get publishFormat() {
return this._publishFormat;
}
/**
* set the broadcastMode (Full|partial) required for the user
*
* @param {string} broadcastMode
* broadcastMode (Full|partial) required for the user
*/
set broadcastMode(broadcastMode) {
this._broadcastMode = broadcastMode;
}
/**
* Returns broadcastMode (Full|partial) required for the user
*
*
* @return
* the broadcastMode (Full|partial) required for the user
*/
get broadcastMode() {
return this._broadcastMode;
}
/**
* set the url value by providing the url in the input
*
* @param {string} url
* url parameter is used to connect to the particular server.
*/
set url(url) {
this._url = url;
}
/**
* Returns url used to connect to the particular server.
*
*
* @return
* the url used to connect to the particular server.
*/
get url() {
return this._url;
}
/**
* Initalizes the socket by accepting userID and token as input parameters
*
* @param {Object} reqObject request object.
*
* @param {string} reqObject.userID
* userID for the particular user.
*
* @param {string} reqObject.token
* token parameter will be generated after successful login and will be used in other private API's
*
* @param {string} reqObject.publishFormat
* publishFormat(JSON|Binary) required for the user
*
* @param {string} reqObject.broadcastMode
* broadcastMode (Full|partial) required for the user
*
*/
init(reqObject) {
this.userID = reqObject.userID;
this.token = reqObject.token;
this.publishFormat = reqObject.publishFormat;
this.broadcastMode = reqObject.broadcastMode;
if (this.socketMD.socketMarketData) {
this.socketMD.socketMarketData.destroy();
delete this.socketMD.socketMarketData;
this.socketMD.socketMarketData = null;
}
// path: "/interactive/socket.io",
this.socketMD.socketMarketData = socketIoClient(
this.url,
{
// path: `/${config.endpoints.apiBinaryMarketData}`+"/socket.io",
path: this.path,
reconnection: false,
query:
{
token: this.token,
userID: this.userID,
publishFormat: this.publishFormat,
broadcastMode: this.broadcastMode
},
});
logger.logFile("socket is initialized with the following parameters url " + this.url + " token " + this.token + " userID " + this.userID);
/**
* Listener of the connect event via socket and emit the connect event via event Emitter
*
*
* @event connect
*/
this.socketMD.socketMarketData.on(settings.socket.connect, (data) => {
this.socketMD.isConnected = true;
console.info("socket connected successfully");
this.eventEmitter.emit(settings.socket.connect, data);
});
/**
* Listener of the joined event via socket and emit the joined event via event Emitter
*
*
* @event joined
*/
this.socketMD.socketMarketData.on(settings.socket.joined, (data) => {
console.info("socket joined successfully");
this.eventEmitter.emit(settings.socket.joined, data);
});
/**
* Listener of the error event via socket and emit the error event via event Emitter
*
*
* @event error
*/
this.socketMD.socketMarketData.on(settings.socket.error, (data) => {
console.info("socket error occurred");
this.eventEmitter.emit(settings.socket.error, data);
});
/**
* Listener of the disconnect event via socket and emit the disconnect event via event Emitter
*
*
* @event disconnect
*/
this.socketMD.socketMarketData.on(settings.socket.disconnect, (data) => {
console.info("socket got disconnected");
this.socketMD.isConnected = false;
this.socketMD.interval = setInterval(() => {
if (this.socketMD.isConnected) {
clearInterval(this.socketMD.interval);
this.socketMD.interval = null;
return;
}
let reqObject = {
userID: this.userID,
token: this.token,
publishFormat: this.publishFormat,
broadcastMode: this.broadcastMode
}
this.init(reqObject);
}, 5000);
this.eventEmitter.emit(settings.socket.disconnect, data);
});
/**
* Listener of the 1505-json-full event via socket and emit the candleDataEvent via event Emitter
*
*
* @event 1505-json-full
*/
this.socketMD.socketMarketData.on("1505-json-full", async (data) => {
let marketDataObject = await this.constructFullCandleDataObject(settings.enums.marketDataPorts.candleDataEvent, data);
this.eventEmitter.emit(settings.socket.candleDataEvent, marketDataObject);
});
/**
* Listener of the 1505-json-partial event via socket and emit the candleDataEvent via event Emitter
*
*
* @event 1505-json-partial
*/
this.socketMD.socketMarketData.on("1505-json-partial", async (data) => {
let marketDataObject = await this.constructPartialCandleDataObject(settings.enums.marketDataPorts.candleDataEvent, data);
this.eventEmitter.emit(settings.socket.candleDataEvent, marketDataObject);
});
/**
* Listener of the xts-binary-packet event via socket and emit the instrumentPropertyChangeEvent via event Emitter
*
*
* @event xts-binary-packet
*/
this.socketMD.socketMarketData.on("xts-binary-packet", async (data) => {
try {
// const dataViewHeader = new DataView(data.buffer);
const dataViewHeader = new DataView(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
let offset = 0;
let count = 0;
let isNextPacket = true;
while (isNextPacket && offset < data.byteLength) {
const isGzipCompressed = dataViewHeader.getInt8(offset, true);
offset++;
if (isGzipCompressed) {
// Extract compressed packet metadata
const messageCode = dataViewHeader.getUint16(offset, true);
offset += 2;
const exchangeSegment = dataViewHeader.getInt16(offset, true);
offset += 2;
const exchangeInstrumentId = dataViewHeader.getInt32(offset, true);
offset += 4;
const bookType = dataViewHeader.getInt16(offset, true);
offset += 2;
const marketType = dataViewHeader.getInt16(offset, true);
offset += 2;
const uncompressedPacketSize = dataViewHeader.getUint16(offset, true);
offset += 2;
const compressedPacketSize = dataViewHeader.getUint16(offset, true);
offset += 2;
// Decompress data using pako
// const compressedData = new Uint8Array(data.buffer, offset, compressedPacketSize);
if (compressedPacketSize <= 0 || offset + compressedPacketSize > data.byteLength) {
console.error("Invalid compressedPacketSize:", compressedPacketSize);
return;
}
const compressedData = data.subarray(offset, offset + compressedPacketSize);
const decompressedData = pako.inflateRaw(compressedData);
const dataView = new DataView(decompressedData.buffer);
let dataByteLength = decompressedData.byteLength;
let currentSize = compressedPacketSize + offset;
isNextPacket = currentSize < data.byteLength;
offset = isNextPacket ? currentSize : offset;
if (dataByteLength > 9 && isGzipCompressed) {
let parsedData = handleMessage(messageCode, dataView, count);
if (this.broadcastMode === "Full") {
if ( messageCode === 1501 ){
let marketDataObject = await this.constructFullMarketDepthObjectBinaryData(settings.enums.marketDataPorts.touchlineEvent, parsedData);
// console.log("parsed data and ltp is: ", parsedData.ExchangeSegment , "Instrument id is: ", parsedData.ExchangeInstrumentID, "ltp is ", parsedData.Touchline.LastTradedPrice);
this.eventEmitter.emit(settings.socket.xtsBinaryPacketEvent, marketDataObject);
}
else if ( messageCode === 1502 ){
let marketDataObject = await this.constructFullMarketDepthObjectBinaryData(settings.enums.marketDataPorts.marketDepthEvent, parsedData);
// console.log("parsed data and ltp is: ", parsedData.ExchangeSegment , "Instrument id is: ", parsedData.ExchangeInstrumentID, "ltp is ", parsedData.Touchline.LastTradedPrice);
this.eventEmitter.emit(settings.socket.xtsBinaryPacketEvent, marketDataObject);
}
else {
let marketDataObject = await this.constructFullMarketDepthObjectBinaryData(settings.enums.marketDataPorts.openInterestEvent, parsedData);
this.eventEmitter.emit(settings.socket.xtsBinaryPacketEvent, marketDataObject);
}
}
else {
if ( messageCode === 1501 ){
let marketDataObject = await this.constructPartialTouchlineBinaryData(settings.enums.marketDataPorts.touchlineEvent, parsedData);
this.eventEmitter.emit(settings.socket.xtsBinaryPacketEvent, marketDataObject);
}
else if ( messageCode === 1502 ){
let marketDataObject = await this.constructPartialMarketDepthObjectBinaryData(settings.enums.marketDataPorts.marketDepthEvent, parsedData);
// console.log("parsed data and ltp is: ", parsedData.ExchangeSegment , "Instrument id is: ", parsedData.ExchangeInstrumentID, "ltp is ", parsedData.Touchline.LastTradedPrice);
this.eventEmitter.emit(settings.socket.xtsBinaryPacketEvent, marketDataObject);
}
else {
let marketDataObject = await this.constructBinaryPartialOpenIntrestObject(settings.enums.marketDataPorts.openInterestEvent, parsedData);
this.eventEmitter.emit(settings.socket.xtsBinaryPacketEvent, marketDataObject);
}
}
}
} else {
let dataByteLength = data.byteLength;
let dataView = dataViewHeader;
const messageCode = dataView.getUint16(offset, true);
offset += 2;
const exchangeSegment = dataView.getInt16(offset, true);
offset += 2;
const exchangeInstrumentId = dataView.getInt32(offset, true);
offset += 4;
const bookType = dataView.getInt16(offset, true);
offset += 2;
const marketType = dataView.getInt16(offset, true);
offset += 2;
const uncompressedPacketSize = dataView.getUint16(offset, true);
offset += 2;
count = offset;
let parsedData = handleMessage(messageCode, dataView, count);
isNextPacket = count < dataByteLength;
offset = isNextPacket ? offset + uncompressedPacketSize : 0;
if (this.broadcastMode === "Full") {
if ( messageCode === 1501 ){
let marketDataObject = await this.constructFullMarketDepthObjectBinaryData(settings.enums.marketDataPorts.touchlineEvent, parsedData);
// console.log("parsed data and ltp is: ", parsedData.ExchangeSegment , "Instrument id is: ", parsedData.ExchangeInstrumentID, "ltp is ", parsedData.Touchline.LastTradedPrice);
this.eventEmitter.emit(settings.socket.xtsBinaryPacketEvent, marketDataObject);
}
else if ( messageCode === 1502 ){
let marketDataObject = await this.constructFullMarketDepthObjectBinaryData(settings.enums.marketDataPorts.marketDepthEvent, parsedData);
// console.log("parsed data and ltp is: ", parsedData.ExchangeSegment , "Instrument id is: ", parsedData.ExchangeInstrumentID, "ltp is ", parsedData.Touchline.LastTradedPrice);
this.eventEmitter.emit(settings.socket.xtsBinaryPacketEvent, marketDataObject);
}
else {
let marketDataObject = await this.constructFullMarketDepthObjectBinaryData(settings.enums.marketDataPorts.openInterestEvent, parsedData);
this.eventEmitter.emit(settings.socket.xtsBinaryPacketEvent, marketDataObject);
}
}
else {
if ( messageCode === 1501 ){
let marketDataObject = await this.constructPartialTouchlineBinaryData(settings.enums.marketDataPorts.touchlineEvent, parsedData);
this.eventEmitter.emit(settings.socket.xtsBinaryPacketEvent, marketDataObject);
}
else if ( messageCode === 1502 ){
let marketDataObject = await this.constructPartialMarketDepthObjectBinaryData(settings.enums.marketDataPorts.marketDepthEvent, parsedData);
this.eventEmitter.emit(settings.socket.xtsBinaryPacketEvent, marketDataObject);
}
else {
let marketDataObject = await this.constructBinaryPartialOpenIntrestObject(settings.enums.marketDataPorts.openInterestEvent, parsedData);
this.eventEmitter.emit(settings.socket.xtsBinaryPacketEvent, marketDataObject);
}
}
}
}
} catch (e) {
console.error("Error processing binary packet:", e);
}
});
/**
* Listener of the logout event via socket and emit the logout event via event Emitter
*
*
* @event logout
*/
this.socketMD.socketMarketData.on(settings.socket.logout, (data) => {
console.info("socket logout successfully");
this.eventEmitter.emit(settings.socket.logout, data);
});
}
/**
* connect listener for event emitter
*
*/
onConnect(fn) {
this.eventEmitter.on(settings.socket.connect, (data) => {
fn(data)
});
}
/**
* joined listener for event emitter
*
*/
onJoined(fn) {
this.eventEmitter.on(settings.socket.joined, (data) => {
fn(data)
});
}
/**
* error listener for event emitter
*
*/
onError(fn) {
this.eventEmitter.on(settings.socket.error, (data) => {
fn(data)
});
}
/**
* disconnect listener for event emitter
*
*/
onDisconnect(fn) {
this.eventEmitter.on(settings.socket.disconnect, (data) => {
fn(data)
});
}
/**
* candleDataEvent listener for event emitter
*
*/
onCandleDataEvent(fn) {
this.eventEmitter.on(settings.socket.candleDataEvent, (data) => {
fn(data);
});
}
/**
* xts-binary-packet listener for event emitter
*
*/
onXTSBinaryPacketEvent(fn) {
this.eventEmitter.on(settings.socket.xtsBinaryPacketEvent, (data) => {
fn(data);
});
}
/**
* logout listener for event emitter
*
*/
onLogout(fn) {
this.eventEmitter.on(settings.socket.logout, (data) => {
fn(data);
});
}
async constructFullMarketDepthObjectBinaryData(messageCode, data) {
var marketData = typeof data === "string" ? JSON.parse(data) : data;
if (typeof marketData.ExchangeTimeStamp === "bigint") {
marketData.ExchangeTimeStamp = Number(marketData.ExchangeTimeStamp);
}
if (typeof marketData.SequenceNumber === "bigint") {
marketData.SequenceNumber = Number(marketData.SequenceNumber);
}
if (marketData.Touchline) {
if (typeof marketData.Touchline.LastTradedTime === "bigint") {
// marketData.Touchline.LastTradedTime = this.getBarTimeFromTouchLineInfo(
// Number(marketData.Touchline.LastTradedTime),
// Number(marketData.ExchangeSegment)
// );
marketData.Touchline.LastTradedTime = Number(marketData.Touchline.LastTradedTime);
}
if (typeof marketData.Touchline.LastUpdateTime === "bigint") {
// marketData.Touchline.LastUpdateTime = this.getBarTimeFromTouchLineInfo(
// Number(marketData.Touchline.LastUpdateTime),
// Number(marketData.ExchangeSegment)
// );
marketData.Touchline.LastUpdateTime = Number(marketData.Touchline.LastUpdateTime);
}
}
// inMemoryStore.loadInMemory(messageCode, marketData.ExchangeSegment, marketData.ExchangeInstrumentID, marketData);
return marketData;
}
async constructPartialTouchlineBinaryData(messageCode, data) {
if (typeof data === "string") {
try {
data = JSON.parse(data);
} catch (e) {
console.error("Failed to parse data:", e);
return null;
}
}
var marketData = {};
marketData.t = data.ExchangeSegment + "_" + data.ExchangeInstrumentID; // Unique identifier
// marketData.tl = {};
// marketData.Bids = [];
// marketData.Asks = [];
// Extract Touchline data
if (data.Touchline && typeof data.Touchline === "object") {
let touchline = data.Touchline;
if (touchline.BidInfo) {
let bidInfo = touchline.BidInfo;
// marketData.Bids.push({ Index: 0, Size: bidInfo.Size, Price: bidInfo.Price, TotalOrders: bidInfo.TotalOrders });
// marketData.bi = { s: bidInfo.Size, p: bidInfo.Price, o: bidInfo.TotalOrders };
marketData.bi = `${bidInfo.Size}|${bidInfo.Price}|${bidInfo.TotalOrders}|${bidInfo.BuyBackMarketMaker}` ;
}
if (touchline.AskInfo) {
let askInfo = touchline.AskInfo;
// marketData.Asks.push({ Index: 0, Size: askInfo.Size, Price: askInfo.Price, TotalOrders: askInfo.TotalOrders });
// marketData.ai = { s: askInfo.Size, p: askInfo.Price, o: askInfo.TotalOrders };
marketData.ai = `${askInfo.Size}|${askInfo.Price}|${askInfo.TotalOrders}|${askInfo.BuyBackMarketMaker}` ;
}
if (touchline.LastTradedPrice !== undefined) marketData.ltp = touchline.LastTradedPrice;
if (touchline.LastTradedQuantity !== undefined) marketData.ltq = touchline.LastTradedQuantity;
if (touchline.TotalBuyQuantity !== undefined) marketData.tb = touchline.TotalBuyQuantity;
if (touchline.TotalSellQuantity !== undefined) marketData.ts = touchline.TotalSellQuantity;
if (touchline.TotalTradedQuantity !== undefined) marketData.v = touchline.TotalTradedQuantity;
if (touchline.AverageTradedPrice !== undefined) marketData.ap = touchline.AverageTradedPrice;
if (touchline.LastTradedTime !== undefined) marketData.ltt = Number(touchline.LastTradedTime);
if (touchline.LastUpdateTime !== undefined) marketData.lut = Number(touchline.LastUpdateTime);
if (touchline.PercentChange !== undefined) marketData.pc = touchline.PercentChange;
if (touchline.Open !== undefined) marketData.o = touchline.Open;
if (touchline.High !== undefined) marketData.h = touchline.High;
if (touchline.Low !== undefined) marketData.l = touchline.Low;
if (touchline.Close !== undefined) marketData.c = touchline.Close;
if (touchline.TotalValueTraded !== undefined) marketData.vp = touchline.TotalValueTraded;
}
// Convert timestamps
// if (marketData.ltt)
// marketData.ltt = this.getBarTimeFromTouchLineInfo(parseInt(marketData.ltt), parseInt(marketData.t.split("_")[0]));
// if (marketData.lut)
// marketData.lut = this.getBarTimeFromTouchLineInfo(parseInt(marketData.lut), parseInt(marketData.t.split("_")[0]));
return marketData;
// var subscribedStock = inMemoryStore.getFromInMemory(messageCode, marketData.t.split("_")[0], marketData.t.split("_")[1]);
// if (subscribedStock) {
// if (subscribedStock.tl)
// subscribedStock.tl = Object.assign(subscribedStock.tl, marketData.tl);
// if (marketData.Bids.length > 0) {
// if (!subscribedStock.Bids) subscribedStock.Bids = [];
// marketData.Bids.forEach((bid, index) => {
// if (bid.Index == 0) {
// if (bid.Size !== '') subscribedStock.tl.bi.s = bid.Size;
// if (bid.Price !== '') subscribedStock.tl.bi.p = bid.Price;
// if (bid.TotalOrders !== '') subscribedStock.tl.bi.o = bid.TotalOrders;
// }
// if (!subscribedStock.Bids[index]) subscribedStock.Bids[index] = {};
// if (bid.Size !== '') subscribedStock.Bids[index].Size = bid.Size;
// if (bid.Price !== '') subscribedStock.Bids[index].Price = bid.Price;
// if (bid.TotalOrders !== '') subscribedStock.Bids[index].TotalOrders = bid.TotalOrders;
// });
// }
// if (marketData.Asks.length > 0) {
// if (!subscribedStock.Asks) subscribedStock.Asks = [];
// marketData.Asks.forEach((ask, index) => {
// if (ask.Index == 0) {
// if (ask.Size !== '') subscribedStock.tl.ai.s = ask.Size;
// if (ask.Price !== '') subscribedStock.tl.ai.p = ask.Price;
// if (ask.TotalOrders !== '') subscribedStock.tl.ai.o = ask.TotalOrders;
// }
// if (!subscribedStock.Asks[index]) subscribedStock.Asks[index] = {};
// if (ask.Size !== '') subscribedStock.Asks[index].Size = ask.Size;
// if (ask.Price !== '') subscribedStock.Asks[index].Price = ask.Price;
// if (ask.TotalOrders !== '') subscribedStock.Asks[index].TotalOrders = ask.TotalOrders;
// });
// }
// var finalSubscribedStock = Object.assign({}, subscribedStock);
// if (finalSubscribedStock.tl.ltp && finalSubscribedStock.tl.pc) {
// finalSubscribedStock.tl.pc = parseFloat(finalSubscribedStock.tl.ltp) == 0 ? "0.00" : parseFloat(finalSubscribedStock.tl.pc).toFixed(2);
// }
// inMemoryStore.loadInMemory(messageCode, finalSubscribedStock.t.split("_")[0], finalSubscribedStock.t.split("_")[1], finalSubscribedStock);
// return finalSubscribedStock;
// } else {
// inMemoryStore.loadInMemory(messageCode, marketData.t.split("_")[0], marketData.t.split("_")[1], marketData);
// return marketData;
// }
}
async constructPartialMarketDepthObjectBinaryData(messageCode, data) {
if (typeof data === "string") {
try {
data = JSON.parse(data);
} catch (e) {
console.error("Failed to parse data:", e);
return null;
}
}
var marketData = {};
marketData.t = data.ExchangeSegment + "_" + data.ExchangeInstrumentID; // Unique identifier
// marketData.tl = {};
// marketData.Bids = [];
// marketData.Asks = [];
var depthLength = 5;
// Extract Touchline data
if (data.Touchline && typeof data.Touchline === "object") {
let touchline = data.Touchline;
marketData.ai = '';
marketData.bi = '';
if (Array.isArray(data.Asks)) {
marketData.ai = data.Asks.slice(0, 5).map(ask =>
`${ask.Size}|${ask.Price}|${ask.TotalOrders}|${ask.BuyBackMarketMaker}`
).join("|");
}
if (Array.isArray(data.Bids)) {
marketData.bi = data.Bids.slice(0, 5).map(bid =>
`${bid.Size}|${bid.Price}|${bid.TotalOrders}|${bid.BuyBackMarketMaker}`
).join("|");
}
// if (touchline.BidInfo) {
// let bidInfo = touchline.BidInfo;
// // marketData.Bids.push({ Index: 0, Size: bidInfo.Size, Price: bidInfo.Price, TotalOrders: bidInfo.TotalOrders });
// marketData.bi = { s: bidInfo.Size, p: bidInfo.Price, o: bidInfo.TotalOrders };
// }
// if (touchline.AskInfo) {
// let askInfo = touchline.AskInfo;
// // marketData.Asks.push({ Index: 0, Size: askInfo.Size, Price: askInfo.Price, TotalOrders: askInfo.TotalOrders });
// marketData.ai = { s: askInfo.Size, p: askInfo.Price, o: askInfo.TotalOrders };
// }
if (touchline.LastTradedPrice !== undefined) marketData.ltp = touchline.LastTradedPrice;
if (touchline.LastTradedQuantity !== undefined) marketData.ltq = touchline.LastTradedQuantity;
if (touchline.TotalBuyQuantity !== undefined) marketData.tb = touchline.TotalBuyQuantity;
if (touchline.TotalSellQuantity !== undefined) marketData.ts = touchline.TotalSellQuantity;
if (touchline.TotalTradedQuantity !== undefined) marketData.v = touchline.TotalTradedQuantity;
if (touchline.AverageTradedPrice !== undefined) marketData.ap = touchline.AverageTradedPrice;
if (touchline.LastTradedTime !== undefined) marketData.ltt = Number(touchline.LastTradedTime);
if (touchline.LastUpdateTime !== undefined) marketData.lut = Number(touchline.LastUpdateTime);
if (touchline.PercentChange !== undefined) marketData.pc = touchline.PercentChange;
if (touchline.Open !== undefined) marketData.o = touchline.Open;
if (touchline.High !== undefined) marketData.h = touchline.High;
if (touchline.Low !== undefined) marketData.l = touchline.Low;
if (touchline.Close !== undefined) marketData.c = touchline.Close;
if (touchline.TotalValueTraded !== undefined) marketData.vp = touchline.TotalValueTraded;
}
// if (data.Bids && Array.isArray(data.Bids)) {
// marketData.Bids = data.Bids.slice(0, depthLength).map((bid, index) => ({
// Index: index,
// Size: bid.Size,
// Price: bid.Price,
// TotalOrders: bid.TotalOrders
// }));
// }
// marketData.Bids = Array.isArray(data.Bids)
// ? data.Bids.slice(0, depthLength).map((bid, index) => ({
// Index: index,
// Size: bid.Size,
// Price: bid.Price,
// TotalOrders: bid.TotalOrders
// }))
// : [];
// if (data.Asks && Array.isArray(data.Asks)) {
// marketData.Asks = data.Asks.slice(0, depthLength).map((ask, index) => ({
// Index: index,
// Size: ask.Size,
// Price: ask.Price,
// TotalOrders: ask.TotalOrders
// }));
// }
// marketData.Asks = Array.isArray(data.Asks)
// ? data.Asks.slice(0, depthLength).map((ask, index) => ({
// Index: index,
// Size: ask.Size,
// Price: ask.Price,
// TotalOrders: ask.TotalOrders
// }))
// : [];
// if (marketData.ltt)
// marketData.ltt = this.getBarTimeFromTouchLineInfo(parseInt(marketData.ltt), parseInt(marketData.t.split("_")[0]));
// if (marketData.lut)
// marketData.lut = this.getBarTimeFromTouchLineInfo(parseInt(marketData.lut), parseInt(marketData.t.split("_")[0]));
return marketData;
// var subscribedStock = inMemoryStore.getFromInMemory(messageCode, marketData.t.split("_")[0], marketData.t.split("_")[1]);
// if (subscribedStock) {
// if (subscribedStock.tl)
// subscribedStock.tl = Object.assign(subscribedStock.tl, marketData.tl);
// // subscribedStock.Bids = marketData.Bids;
// // subscribedStock.Asks = marketData.Asks;
// // subscribedStock.Bids = [...marketData.Bids];
// // subscribedStock.Asks = [...marketData.Asks];
// if (marketData.Bids.length > 0) {
// if (!subscribedStock.Bids) subscribedStock.Bids = [];
// marketData.Bids.forEach((bid, index) => {
// if (bid.Index == 0) {
// if (bid.Size !== '') subscribedStock.tl.bi.s = bid.Size;
// if (bid.Price !== '') subscribedStock.tl.bi.p = bid.Price;
// if (bid.TotalOrders !== '') subscribedStock.tl.bi.o = bid.TotalOrders;
// }
// if (!subscribedStock.Bids[index]) subscribedStock.Bids[index] = {};
// if (bid.Size !== '') subscribedStock.Bids[index].Size = bid.Size;
// if (bid.Price !== '') subscribedStock.Bids[index].Price = bid.Price;
// if (bid.TotalOrders !== '') subscribedStock.Bids[index].TotalOrders = bid.TotalOrders;
// });
// }
// if (marketData.Asks.length > 0) {
// if (!subscribedStock.Asks) subscribedStock.Asks = [];
// marketData.Asks.forEach((ask, index) => {
// if (ask.Index == 0) {
// if (ask.Size !== '') subscribedStock.tl.ai.s = ask.Size;
// if (ask.Price !== '') subscribedStock.tl.ai.p = ask.Price;
// if (ask.TotalOrders !== '') subscribedStock.tl.ai.o = ask.TotalOrders;
// }
// if (!subscribedStock.Asks[index]) subscribedStock.Asks[index] = {};
// if (ask.Size !== '') subscribedStock.Asks[index].Size = ask.Size;
// if (ask.Price !== '') subscribedStock.Asks[index].Price = ask.Price;
// if (ask.TotalOrders !== '') subscribedStock.Asks[index].TotalOrders = ask.TotalOrders;
// });
// }
// var finalSubscribedStock = Object.assign({}, subscribedStock);
// if (finalSubscribedStock.tl.ltp && finalSubscribedStock.tl.pc) {
// finalSubscribedStock.tl.pc = parseFloat(finalSubscribedStock.tl.ltp) == 0 ? "0.00" : parseFloat(finalSubscribedStock.tl.pc).toFixed(2);
// }
// inMemoryStore.loadInMemory(messageCode, finalSubscribedStock.t.split("_")[0], finalSubscribedStock.t.split("_")[1], finalSubscribedStock);
// return finalSubscribedStock;
// } else {
// inMemoryStore.loadInMemory(messageCode, marketData.t.split("_")[0], marketData.t.split("_")[1], marketData);
// return marketData;
// }
}
getBarTimeFromTouchLineInfo(lastTradedTime, exchangeSegment) {
var date = this.getBaseReferenceDate(exchangeSegment);
return new Date(date.setSeconds(date.getSeconds() + lastTradedTime));
}
async constructBinaryPartialOpenIntrestObject(messageCode, data) {
if (typeof data === "string") {
try {
data = JSON.parse(data);
} catch (e) {
console.error("Failed to parse data:", e);
return null;
}
}
var marketData = {};
if (data) {
marketData.t = data.ExchangeSegment + "_" + data.ExchangeInstrumentID; // Unique identifier
if (data.OpenInterest !== undefined) marketData.o = data.OpenInterest;
if (data.XTSMarketType !== undefined) marketData.mt = data.XTSMarketType;
if (data.UnderlyingExchangeSegment !== undefined) marketData.uex = data.UnderlyingExchangeSegment;
if (data.ExchangeTimeStamp !== undefined) marketData.et = data.ExchangeTimeStamp;
if (data.UnderlyingInstrumentID !== undefined) marketData.uid = data.UnderlyingInstrumentID;
if (data.UnderlyingTotalOpenInterest !== undefined) marketData.toi = data.UnderlyingTotalOpenInterest;
}
return marketData;
// var subscribedStock = inMemoryStore.getFromInMemory(messageCode, marketData.t.split("_")[0], marketData.t.split("_")[1]);
// if (subscribedStock) {
// var finalSubscribedStock = merge(subscribedStock, marketData);
// inMemoryStore.loadInMemory(messageCode, finalSubscribedStock.t.split("_")[0], finalSubscribedStock.t.split("_")[1], finalSubscribedStock);
// return finalSubscribedStock;
// } else {
// inMemoryStore.loadInMemory(messageCode, marketData.t.split("_")[0], marketData.t.split("_")[1], marketData);
// return marketData;
// }
}
async constructPartialPropertyChangeEventObject(messageCode, data) {
var marketData = {};
linq.from(data.split(',')).select(function (keyValuePair) {
var key = keyValuePair.split(':')[0];
var value = keyValuePair.split(':')[1];
switch (key) {
case 't':
marketData.ExchangeSegment = value.split('_')[0];
marketData.ExchangeInstrumentID = value.split('_')[1];
break;
case (settings.enums.InstrumentPropName.LowPriceRange).toString():
marketData.LowPriceRange = value;
break;
case (settings.enums.InstrumentPropName.HighPriceRange).toString():
marketData.HighPriceRange = value;
break;
}
}).toArray();
var subscribedStock = inMemoryStore.getFromInMemory(messageCode, marketData.ExchangeSegment, marketData.ExchangeInstrumentID);
if (subscribedStock) {
var finalSubscribedStock = merge(subscribedStock, marketData);
inMemoryStore.loadInMemory(finalSubscribedStock.messageCode, finalSubscribedStock.ExchangeSegment, finalSubscribedStock.ExchangeInstrumentID, finalSubscribedStock);
return finalSubscribedStock;
} else {
inMemoryStore.loadInMemory(messageCode, marketData.ExchangeSegment, marketData.ExchangeInstrumentID, marketData);
return marketData;
}
}
async constructPartialCandleDataObject(messageCode, data) {
var marketData = {};
linq.from(data.split(',')).select(function (keyValuePair) {
var key = keyValuePair.split(':')[0];
var value = keyValuePair.split(':')[1];
switch (key) {
case 't':
marketData.t = value.split('_')[0] + "_" + value.split('_')[1];
break;
case 'o':
marketData.o = value;
break;
case 'h':
marketData.h = value;
break;
case 'l':
marketData.l = value;
break;
case 'c':
marketData.c = value;
break;
case 'bt':
marketData.bt = value;
break;
case 'bv':
marketData.bv = value;
break;
case 'pv':
marketData.pv = value;
break;
}
}).toArray();
return marketData;
// var subscribedStock = inMemoryStore.getFromInMemory(messageCode, marketData.ExchangeSegment, marketData.ExchangeInstrumentID);
// if (subscribedStock) {
// var finalSubscribedStock = merge(subscribedStock, marketData);
// inMemoryStore.loadInMemory(finalSubscribedStock.messageCode, finalSubscribedStock.ExchangeSegment, finalSubscribedStock.ExchangeInstrumentID, finalSubscribedStock);
// return finalSubscribedStock;
// } else {
// inMemoryStore.loadInMemory(messageCode, marketData.ExchangeSegment, marketData.ExchangeInstrumentID, marketData);
// return marketData;
// }
}
async constructFullCandleDataObject(messageCode, data) {
var marketData = JSON.parse(data);
// inMemoryStore.loadInMemory(messageCode, marketData.ExchangeSegment, marketData.ExchangeInstrumentID, marketData);
return marketData;
}
getBaseReferenceDate(exchangeSegment) {
switch (exchangeSegment) {
case settings.enums.segments.NSECM:
case settings.enums.segments.NSEFO:
case settings.enums.segments.NSECD:
return new Date(1980, 0, 1);
default:
return new Date(1970, 0, 1);
}
}
}