UNPKG

upstox-js-sdk

Version:

The official Node Js client for communicating with the Upstox API

177 lines (153 loc) 4.08 kB
import WebSocket from "ws"; import protobuf from "protobufjs"; import path from "path"; import uuid4 from "uuid4"; import { ApiClient } from "../ApiClient"; import Feeder from "./Feeder"; class MarketDataFeeder extends Feeder { //Required enums Mode = Object.freeze({ LTPC: "ltpc", FULL: "full", }); Method = Object.freeze({ SUBSCRIBE: "sub", CHANGE_METHOD: "change_mode", UNSUBSCRIBE: "unsub", }); ws = null; instrumentKeys = []; mode = ""; userClosedWebSocket = false; closingCode = -1; protobufRoot = null; constructor(instrumentKeys = [], mode = this.Mode.FULL) { super(); this.apiClient = ApiClient.instance; this.instrumentKeys = instrumentKeys; this.mode = mode; this.initProtobuf(); } async connect() { // Skip if its already connected if ( this.ws && (this.ws.readyState == ws.CONNECTING || ws.readyState == ws.OPEN) ) return; this.ws = await this.connectWebSocket( `wss://api.upstox.com/v2/feed/market-data-feed`, this.apiClient.authentications["OAUTH2"].accessToken ); this.onOpen(); this.onMessage(); this.onClose(); this.onError(); } shouldReconnect() { return ( this.ws === null || (!this.userClosedWebSocket && this.ws.readyState !== WebSocket.OPEN) ); } onOpen() { this.ws.on("open", () => { this.emit("open"); }); } onMessage() { this.ws.on("message", (data) => { const decodedData = this.decodeProtobuf(data); this.push(JSON.stringify(decodedData)); }); } onClose() { this.ws.on("close", (code) => { this.push(null); this.closingCode = code; if (code === 1000) { this.userClosedWebSocket = true; } }); } onError() { this.ws.on("error", (e) => { this.emit("error", e); }); } disconnect() { this.ws.close(1000); } subscribe(instrumentKeys, mode) { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.ws.send( this.buildRequest(instrumentKeys, this.Method.SUBSCRIBE, mode) ); } else { throw new Error("Failed to subscribe: WebSocket is not open."); } } unsubscribe(instrumentKeys) { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.ws.send(this.buildRequest(instrumentKeys, this.Method.UNSUBSCRIBE)); } } changeMode(instrumentKeys, newMode) { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.ws.send( this.buildRequest(instrumentKeys, this.Method.SUBSCRIBE, newMode) ); } else { throw new Error("Failed to changeMode: WebSocket is not open."); } } // Function to establish WebSocket connection async connectWebSocket(wsUrl, accessToken) { return new Promise((resolve) => { const ws = new WebSocket(wsUrl, { headers: { Authorization: `Bearer ${accessToken}`, }, followRedirects: true, }); resolve(ws); }); } // Function to initialize the protobuf part async initProtobuf() { const protoPath = path.resolve(__dirname, "./proto/MarketDataFeed.proto"); protobuf.load(protoPath, (error, root) => { if (error) { console.error("Error loading .proto file", error); return; } this.protobufRoot = root; }); } // Function to decode protobuf message decodeProtobuf(buffer) { if (!this.protobufRoot) { console.warn("Protobuf part not initialized yet!"); return null; } const FeedResponse = this.protobufRoot.lookupType( "com.upstox.marketdatafeeder.rpc.proto.FeedResponse" ); return FeedResponse.decode(buffer); } buildRequest(instrumentKeys, method, mode) { const requestObj = { guid: uuid4(), method, data: { instrumentKeys, }, }; // Only add 'mode' to 'data' if it is not undefined if (mode !== undefined) { requestObj.data.mode = mode; } return Buffer.from(JSON.stringify(requestObj)); } } export { MarketDataFeeder };