UNPKG

@suyotech-dev/tradesmartapi-js

Version:

Trade Smart API build by suyotech.com for internal use

180 lines (154 loc) 4.7 kB
import { WebSocket } from "ws"; import { TradeSmartAPI } from "./TradeSmart_API.js"; export class TradeSmartWS { /** * @type {WebSocket} */ #socket = null; #reconnectInterval = 10000; #reconnectAttempts = 200; #currentReconnectAttempts = 0; #heartBeatInterval = null; #heartBeatDuration = 3000; #debug = false; #uid = ""; #actid = ""; #susertoken = ""; #socketConnecting = false; #disconnectedManually = false; #webSocketURL = "wss://v2api.tradesmartonline.in/NorenWSTP/"; #onDataCallback; #onOrderCallback; /** * @param {string} name * @param {TradeSmartAPI} api */ constructor(name = "socket", api) { if (!name || !api) { throw new Error("websocket required name and api"); } this.#uid = api.uid; this.#actid = api.actid; this.#susertoken = api.susertoken; } async connect() { if (this.#socketConnecting) return; this.#socketConnecting = true; this.#disconnectedManually = false; return new Promise((resolve, reject) => { try { this.#socket = new WebSocket(this.#webSocketURL, { rejectUnauthorized: false }); this.#socket.onopen = () => { this.#currentReconnectAttempts = 0; // Reset reconnect attempts on successful connection this.#setHeartBeat(); const connectData = { t: "c", uid: this.#uid, actid: this.#actid, susertoken: this.#susertoken }; this.#socket.send(JSON.stringify(connectData)); console.log("websocket connected"); resolve(); }; this.#socket.onerror = e => { console.error("WebSocket Error:", e); reject(e); }; this.#socket.onclose = e => { this.#clearHeartBeat(); console.log("WebSocket Closed"); if (this.#debug) { console.log(e); } if (!this.#disconnectedManually) { this.#attemptReconnect(); } }; this.#socket.onmessage = e => { // @ts-ignore const data = JSON.parse(e?.data); if (["tk", "tf", "df", "dk"].includes(data.t) && this.#onDataCallback !== null) { this.#onDataCallback(data); } else if (["om"].includes(data.t) && this.#onOrderCallback) { this.#onOrderCallback(data); } else if (this.#debug) { console.log("Message Recived", data); } }; } catch (error) { reject(error); } }).finally(() => { this.#socketConnecting = false; }); } #attemptReconnect() { if (this.#currentReconnectAttempts < this.#reconnectAttempts) { this.#currentReconnectAttempts++; console.log(`Reconnect attempt ${this.#currentReconnectAttempts}...`); setTimeout(() => { this.connect().catch(err => { console.error("Reconnect failed:", err); }); }, this.#reconnectInterval); } else { console.error("Max reconnect attempts reached. Unable to reconnect."); } } #setHeartBeat() { this.#heartBeatInterval = setInterval(() => { if (this.#socket && this.#socket.readyState === WebSocket.OPEN) { this.#socket.send(JSON.stringify({ t: "h" })); } }, this.#heartBeatDuration); } #clearHeartBeat() { if (this.#heartBeatInterval) { clearInterval(this.#heartBeatInterval); this.#heartBeatInterval = null; } } /** * * @param {Function} callback */ onData(callback) { this.#onDataCallback = callback; } /** * * @param {Function} callback */ onOrder(callback) { this.#onOrderCallback = callback; } disconnect() { if (this.#socket) { this.#disconnectedManually = true; this.#clearHeartBeat(); this.#socket.close(); this.#socket = null; } } /** * * @param {import("./TradeSmart_Instruments").Instrument[]} instruments */ async subscribe(instruments = []) { if (!this.#socket || this.#socket.readyState !== WebSocket.OPEN) { throw new Error("WebSocket is not connected."); } if (!instruments || !instruments.length) { throw new Error("instruments invalid"); } const subString = instruments .map(i => { return `${i.Exchange}|${i.Token}`; }) .join("#"); // console.log("subString", subString); const subscribeData = { t: "t", k: subString }; this.#socket.send(JSON.stringify(subscribeData)); } }