fyers-api-v3 1.1.1 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/HSM/datasocket.js +1276 -0
- package/apiService/apiService.js +33 -0
- package/config/config.js +2 -1
- package/index.js +1 -1
- package/package.json +1 -1
- package/HSM/datasocket.min.js +0 -1
|
@@ -0,0 +1,1276 @@
|
|
|
1
|
+
let HSWebSocket = require('../HSM_Package/hslib.js');
|
|
2
|
+
let mapdata = require("./mapper.js");
|
|
3
|
+
let ErrorHandler = require("../errorHandler/errorHandler.js");
|
|
4
|
+
let axios = require("axios")
|
|
5
|
+
let Logger = require("../logger/log.js")
|
|
6
|
+
let { Config } = require("../config/config");
|
|
7
|
+
let jwt = require('jsonwebtoken');
|
|
8
|
+
|
|
9
|
+
const floatkeys = ["ltp", "op", "c", "lo", "h", "cng", "nc", "yh", "yl", "ap", "to", "lcl", "ucl", "bp", "sp", "mul", "prec", "openingPrice", "lowPrice", "highPrice", "ic", "iv","bp1","bp2","bp3","bp4","sp1","sp2","sp3","sp4"]
|
|
10
|
+
const intkeys = ["ltq", "v", "tbq", "tsq", "oi", "bq", "bs","bq1","bq2","bq3","bq4","bs1","bs2","bs3","bs4","bno1","bno2","bno3","bno4","bno5","sno1","sno2","sno3","sno4","sno5"]
|
|
11
|
+
const datekeys = ["ltt", "fdtm","tvalue"]
|
|
12
|
+
const exchmapping = { "1010": 'nse_cm', "1011": 'nse_fo', "1120": 'mcx_fo', "1210": 'bse_cm', "1012": 'cde_fo', "1211": 'bse_fo' ,"1020": 'nse_com', "1212": 'bcs_fo'}
|
|
13
|
+
var fulldata = {};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* returns the overall count of symbol subscribed across all channels.
|
|
17
|
+
* @param {Object} symbolcounter - symbolcounter object which has subscription type as keys and values as object having key as channel number and values as list of exchange token of symbols subscribed on channel.
|
|
18
|
+
* @returns {Number} number of symbols subscribed over all channels.
|
|
19
|
+
*/
|
|
20
|
+
function countValues(symbolcounter) {
|
|
21
|
+
return Object.values(symbolcounter).reduce((count, innerObject) => {
|
|
22
|
+
return count + Object.values(innerObject).reduce((sum, list) => {
|
|
23
|
+
return sum + list.length;
|
|
24
|
+
}, 0);
|
|
25
|
+
}, 0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* used to send http request to quotes api to get FYtoken.
|
|
30
|
+
* @param {Array} urls - premade urls with querys.
|
|
31
|
+
* @param {string} AuthrizationToken - AccessToken of user.
|
|
32
|
+
* @param {Function} callback - the callback function.
|
|
33
|
+
* @param {Logger} logger - logger class.
|
|
34
|
+
* @returns {Object} with key as symbol ticker and value as fytoken
|
|
35
|
+
*/
|
|
36
|
+
async function sendRequests(urls, AuthrizationToken, callback, logger) {
|
|
37
|
+
var funcname = "sendRequests"
|
|
38
|
+
var symboltokenmap = {}
|
|
39
|
+
var resp = undefined
|
|
40
|
+
for (const url of urls) {
|
|
41
|
+
try {
|
|
42
|
+
const response = await axios.get((url), {
|
|
43
|
+
headers: {
|
|
44
|
+
Authorization: AuthrizationToken,
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
resp = response.data
|
|
48
|
+
} catch (error) {
|
|
49
|
+
var err = new ErrorHandler(error).getError()
|
|
50
|
+
logger.error("error fetching symbol token", err, funcname)
|
|
51
|
+
err['type'] = "sub"
|
|
52
|
+
callback(err)
|
|
53
|
+
resp = { 'd': [] }
|
|
54
|
+
}
|
|
55
|
+
resp = resp.d
|
|
56
|
+
resp.forEach(function (element) {//make a object with symbol as key and fytoken as value
|
|
57
|
+
if (element.s === 'ok') {
|
|
58
|
+
symboltokenmap[element.n] = element.v.fyToken
|
|
59
|
+
} else {
|
|
60
|
+
callback({ "code": element.v.code, "type": "sub", "message": element.v.errmsg, "s": element.v.s, "symbol": element.n })
|
|
61
|
+
logger.error("error fetching symbol token for specific symbol", element, funcname)
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
return symboltokenmap
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* used to send http request to quotes api to get FYtoken.
|
|
69
|
+
* @param {Array} urls - premade urls with querys.
|
|
70
|
+
* @param {string} AuthrizationToken - AccessToken of user.
|
|
71
|
+
* @param {Function} callback - the callback function.
|
|
72
|
+
* @param {Logger} logger - logger class.
|
|
73
|
+
* @returns {Object} with key as symbol ticker and value as fytoken
|
|
74
|
+
*/
|
|
75
|
+
async function getFyToken(url , AuthrizationToken , bodies , logger ,callback) {
|
|
76
|
+
var resp = null;
|
|
77
|
+
var returnResponse = {}
|
|
78
|
+
const funcname = "getFyToken";
|
|
79
|
+
for (const body of bodies){
|
|
80
|
+
var requestBody = JSON.stringify(body)
|
|
81
|
+
try {
|
|
82
|
+
const response = await axios.post((url),requestBody, {
|
|
83
|
+
headers: {
|
|
84
|
+
Authorization: AuthrizationToken,
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
resp = response.data
|
|
88
|
+
for (key in resp['validSymbol']){
|
|
89
|
+
returnResponse[key] = resp['validSymbol'][key]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
} catch (error) {
|
|
93
|
+
var err = new ErrorHandler(error).getError()
|
|
94
|
+
logger.error("error fetching symbol token", err, funcname)
|
|
95
|
+
err['type'] = "sub"
|
|
96
|
+
callback(err)
|
|
97
|
+
resp = { 'd': [] }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return returnResponse
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* function called to check if one single symbol is not subscribed on multiple channels before deleting its in-memory object.
|
|
105
|
+
* @param {Object} obj - premade urls with querys.
|
|
106
|
+
* @param {string} value - AccessToken of user.
|
|
107
|
+
* @returns {boolean} true if symbol found subscribed on multiple channels
|
|
108
|
+
*/
|
|
109
|
+
function checkValueInLists(obj, value) {
|
|
110
|
+
let count = 0;
|
|
111
|
+
let foundMultiple = false;
|
|
112
|
+
|
|
113
|
+
for (const key in obj) {
|
|
114
|
+
if (Array.isArray(obj[key]) && obj[key].includes(value)) {
|
|
115
|
+
count++;
|
|
116
|
+
if (count > 1) {
|
|
117
|
+
foundMultiple = true;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return !foundMultiple;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* appends data to Array dropping duplicates
|
|
128
|
+
* @param {Array} mainList - array to which value is to be appened.
|
|
129
|
+
* @param {any} valueToAppend - value to be appended to array.
|
|
130
|
+
* @returns {Array} Appened array.
|
|
131
|
+
*/
|
|
132
|
+
function appendToList(mainList, valueToAppend) {
|
|
133
|
+
if (!mainList.includes(valueToAppend)) {
|
|
134
|
+
mainList.push(valueToAppend);
|
|
135
|
+
}
|
|
136
|
+
return mainList;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* return array dropping value passed in channelnumber from default channel list or whatever array passed
|
|
141
|
+
* @param {any} channelnumber - value to be dropped from array.
|
|
142
|
+
* @param {Array} channellist - array from which valued to be dropped.
|
|
143
|
+
* @returns {Array} array dropping the value.
|
|
144
|
+
*/
|
|
145
|
+
function returnstopchannelarray(channelnumber, channellist = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]) {
|
|
146
|
+
for (var i = 0; i < channellist.length; i++) {
|
|
147
|
+
|
|
148
|
+
if (channellist[i] === channelnumber) {
|
|
149
|
+
|
|
150
|
+
channellist.splice(i, 1);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return channellist
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* return array as array of arrays splitting input array to length of limit
|
|
158
|
+
* @param {Array} array - array to be split.
|
|
159
|
+
* @param {Number} limit - array length limit.
|
|
160
|
+
* @returns {Array<Array<any>>} array dropping the value.
|
|
161
|
+
*/
|
|
162
|
+
function splitArray(array, limit) {
|
|
163
|
+
const result = [];
|
|
164
|
+
let currentArray = [];
|
|
165
|
+
|
|
166
|
+
for (const item of array) {
|
|
167
|
+
currentArray.push(item);
|
|
168
|
+
|
|
169
|
+
if (currentArray.length === limit) {
|
|
170
|
+
result.push(currentArray);
|
|
171
|
+
currentArray = [];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (currentArray.length > 0) {
|
|
176
|
+
result.push(currentArray);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* return array as array of arrays splitting input array to length of limit
|
|
184
|
+
* @param {string} datestring - datestring to convert to epoch time.
|
|
185
|
+
* @returns {Number} epoch time.
|
|
186
|
+
*/
|
|
187
|
+
function DateTimeStringToEpoch(datestring) {
|
|
188
|
+
process.env.TZ = 'Asia/Kolkata';
|
|
189
|
+
// Parse the date string into its components
|
|
190
|
+
const [day, month, year, hours, minutes, seconds] = datestring.match(/\d+/g);
|
|
191
|
+
// Create a new Date object with the components (months are zero-based)
|
|
192
|
+
const date = new Date(year, month - 1, day, hours, minutes, seconds);
|
|
193
|
+
// Get the epoch time in seconds by dividing by 1000 and flooring the result
|
|
194
|
+
const epochTimeSec = Math.floor(date.getTime() / 1000);
|
|
195
|
+
return epochTimeSec
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Class to make a queue datatype object(makes a first in first out system using array).
|
|
199
|
+
* @class
|
|
200
|
+
*/
|
|
201
|
+
class Queuesystem {
|
|
202
|
+
constructor() {
|
|
203
|
+
this.queue = [];
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* used to add data to queue
|
|
207
|
+
* @param {Object | Object[]} data - data to be sent to queue.
|
|
208
|
+
*/
|
|
209
|
+
send(data) {
|
|
210
|
+
this.queue.push(data);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* used to add data to queue
|
|
215
|
+
* @returns {Promise} A promise that resolves and send data out of queue or undefined in case queue is empty
|
|
216
|
+
*/
|
|
217
|
+
async receive() {
|
|
218
|
+
if (this.queue.length > 0) {
|
|
219
|
+
return this.queue.shift();
|
|
220
|
+
} else {
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* clears the queue
|
|
226
|
+
*/
|
|
227
|
+
clear() {
|
|
228
|
+
this.queue = []
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* function to rename keys of the object
|
|
234
|
+
* @param {Object} obj - object in which key is to be replaced.
|
|
235
|
+
* @param {string} oldKey - the key which is to be renamed.
|
|
236
|
+
* @param {string} newKey - new key name.
|
|
237
|
+
*/
|
|
238
|
+
function renameObjectKey(obj, oldKey, newKey) {
|
|
239
|
+
if (oldKey !== newKey && obj.hasOwnProperty(oldKey)) {
|
|
240
|
+
obj[newKey] = obj[oldKey];
|
|
241
|
+
delete obj[oldKey];
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* maps data from HSM fromat to our format based on channel mode(full/lite) Note:it deletes key if mapping is not found in mapper object.
|
|
247
|
+
* @param {Object} hsminputdata - data from HSM.
|
|
248
|
+
* @param {Boolean} islitemode - true if channel in lite mode else false.
|
|
249
|
+
*/
|
|
250
|
+
function Mapper(hsminputdata, islitemode) {
|
|
251
|
+
var datatobemapped = { ...hsminputdata }
|
|
252
|
+
var feed_type = datatobemapped.name
|
|
253
|
+
if (feed_type === undefined) {
|
|
254
|
+
feed_type = "confirmationmapper"
|
|
255
|
+
}
|
|
256
|
+
if (islitemode) {
|
|
257
|
+
var mode = "lite"
|
|
258
|
+
} else {
|
|
259
|
+
var mode = "full"
|
|
260
|
+
}
|
|
261
|
+
for (const key in datatobemapped) {
|
|
262
|
+
if (!(mapdata[feed_type] === undefined)) {
|
|
263
|
+
if (!(mapdata[feed_type][mode][key] === undefined)) {
|
|
264
|
+
renameObjectKey(datatobemapped, key, mapdata[feed_type][mode][key]);
|
|
265
|
+
if (feed_type=='sf' && mode=='full') {
|
|
266
|
+
datatobemapped['lower_ckt']=0
|
|
267
|
+
datatobemapped['upper_ckt']=0
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
else if (key != "symbol") {
|
|
271
|
+
delete datatobemapped[key]
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return datatobemapped
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
Array.prototype.contains = function (obj) {
|
|
279
|
+
var i = this.length;
|
|
280
|
+
while (i--) {
|
|
281
|
+
if (this[i] === obj) {
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
let reviver = function (key, value) {
|
|
289
|
+
if (floatkeys.contains(key)) {
|
|
290
|
+
return parseFloat(value)
|
|
291
|
+
} else if (intkeys.contains(key)) {
|
|
292
|
+
return parseInt(value)
|
|
293
|
+
} else if (datekeys.contains(key)) {
|
|
294
|
+
return DateTimeStringToEpoch(value)
|
|
295
|
+
} else {
|
|
296
|
+
return value
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function decodeJWTAndExtractHSMKey(AccessToken,Logger) {
|
|
301
|
+
const funcname="decodeJWTAndExtractHSMKey"
|
|
302
|
+
try {
|
|
303
|
+
const tokenParts = AccessToken.split(':');
|
|
304
|
+
const jwtToken = tokenParts[tokenParts.length - 1];
|
|
305
|
+
const decodedToken = jwt.decode(jwtToken);
|
|
306
|
+
|
|
307
|
+
if (!decodedToken || !decodedToken.hsm_key) {
|
|
308
|
+
Logger.error("HSM key not found in token", decodedToken, funcname)
|
|
309
|
+
throw new Error('Invalid JWT: "hsm_key" missing or token is invalid.');
|
|
310
|
+
}
|
|
311
|
+
if (decodedToken.exp && Date.now() >= decodedToken.exp * 1000) {
|
|
312
|
+
Logger.error("Token expired", decodedToken, funcname)
|
|
313
|
+
throw new Error('You are passing an expired token');
|
|
314
|
+
}
|
|
315
|
+
return decodedToken.hsm_key;
|
|
316
|
+
} catch (error) {
|
|
317
|
+
Logger.error("HSM key not found in token", error.message, funcname)
|
|
318
|
+
throw new Error(`Failed to decode JWT: ${error.message}`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const _receiver = Symbol('receiver');
|
|
323
|
+
const _recurssivesend = Symbol('recursivesend');
|
|
324
|
+
|
|
325
|
+
const DataSocket = (() => {
|
|
326
|
+
/**
|
|
327
|
+
* The current channel being used.
|
|
328
|
+
* @private
|
|
329
|
+
* @type {Number}
|
|
330
|
+
*/
|
|
331
|
+
let currentchannel = undefined;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* @typedef {object} symbolcounterobj
|
|
335
|
+
* @property {number} channel - The number of channel.
|
|
336
|
+
* @property {Array} symbols - symbols subscribed on channel.
|
|
337
|
+
*/
|
|
338
|
+
/**
|
|
339
|
+
* The symbol subscribed on a particular channel by subsciption type(dp-depth,if-index,sf-equity & FNO).
|
|
340
|
+
* @private
|
|
341
|
+
* @type {symbolcounterobj}
|
|
342
|
+
*/
|
|
343
|
+
let symbolcounter = {
|
|
344
|
+
"if": { 1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: [], 8: [], 9: [], 10: [], 11: [], 12: [], 13: [], 14: [], 15: [], 16: [], 17: [], 18: [], 19: [], 20: [], 21: [], 22: [], 23: [], 24: [], 25: [], 26: [], 27: [], 28: [], 29: [], 30: [] },
|
|
345
|
+
"sf": { 1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: [], 8: [], 9: [], 10: [], 11: [], 12: [], 13: [], 14: [], 15: [], 16: [], 17: [], 18: [], 19: [], 20: [], 21: [], 22: [], 23: [], 24: [], 25: [], 26: [], 27: [], 28: [], 29: [], 30: [] },
|
|
346
|
+
"dp": { 1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: [], 8: [], 9: [], 10: [], 11: [], 12: [], 13: [], 14: [], 15: [], 16: [], 17: [], 18: [], 19: [], 20: [], 21: [], 22: [], 23: [], 24: [], 25: [], 26: [], 27: [], 28: [], 29: [], 30: [] }
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* @typedef {object} litemodestatusobj
|
|
351
|
+
* @property {number} channel - The number of channel.
|
|
352
|
+
* @property {Boolean} litemode - true if channel in lite mode.
|
|
353
|
+
*/
|
|
354
|
+
/**
|
|
355
|
+
* object stating if the channel is in lite mode or not.
|
|
356
|
+
* @private
|
|
357
|
+
* @type {litemodestatusobj}
|
|
358
|
+
*/
|
|
359
|
+
let litemodestatus = {
|
|
360
|
+
1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true, 8: true, 9: true, 10: true, 11: false, 12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false, 20: false, 21: false, 22: false, 23: false, 24: false, 25: false, 26: false, 27: false, 28: false, 29: false, 30: false
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* AccessToken of user.
|
|
365
|
+
* @private
|
|
366
|
+
* @type {String}
|
|
367
|
+
*/
|
|
368
|
+
let AccessToken = undefined
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* HSMtoken of user.
|
|
372
|
+
* @private
|
|
373
|
+
* @type {String}
|
|
374
|
+
*/
|
|
375
|
+
let HSMToken = undefined
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* WebsocketURL.
|
|
379
|
+
* @private
|
|
380
|
+
* @type {String}
|
|
381
|
+
*/
|
|
382
|
+
let wsURL = Config['HSM_SOCKET'];
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* number of max reconnections allowed.
|
|
386
|
+
* @private
|
|
387
|
+
* @type {Number}
|
|
388
|
+
*/
|
|
389
|
+
let maxreconnectiontries = 0;
|
|
390
|
+
|
|
391
|
+
let reconnectiontries = 0;
|
|
392
|
+
/**
|
|
393
|
+
* count of number of request sent to socket in a second reset to zero every second.
|
|
394
|
+
* @private
|
|
395
|
+
* @type {Number}
|
|
396
|
+
*/
|
|
397
|
+
let secondcounter = 0;
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* HSM websocket object.
|
|
401
|
+
* @private
|
|
402
|
+
* @type {HSWebSocket}
|
|
403
|
+
*/
|
|
404
|
+
let hsSocket = new HSWebSocket();
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* makes a queue variable to push data to send to websocket.
|
|
408
|
+
* @private
|
|
409
|
+
* @type {Queuesystem}
|
|
410
|
+
*/
|
|
411
|
+
let queue = new Queuesystem();
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Class to access symboldata websocket.
|
|
415
|
+
* @class
|
|
416
|
+
*/
|
|
417
|
+
class DataSocket {
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Create a new DataSocket object to acess symboldata websocket.
|
|
421
|
+
* @constructor
|
|
422
|
+
* @param {Object} params - The parameters for initializing the DataSocket.
|
|
423
|
+
* @param {string} Token - The access token for authentication.
|
|
424
|
+
* @param {string} [path] - The path for log files.
|
|
425
|
+
*/
|
|
426
|
+
constructor(Token,path,loggingFlag=true) {
|
|
427
|
+
|
|
428
|
+
if (DataSocket.instance) {
|
|
429
|
+
throw new Error("Only one instance of DataSocket is allowed. Use getInstance() instead.");
|
|
430
|
+
}
|
|
431
|
+
var self = this;
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* variable to be passed to make channel run in full mode.
|
|
435
|
+
* @type {string}
|
|
436
|
+
*/
|
|
437
|
+
self.FullMode = "ful"
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* filepath where are logs are to be saved.
|
|
441
|
+
* @type {string}
|
|
442
|
+
*/
|
|
443
|
+
self.LogPath = path;
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Flag to enable or disable logging.
|
|
447
|
+
* @type {boolean}
|
|
448
|
+
*/
|
|
449
|
+
self.LoggingFlag = loggingFlag;
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* object used to write logs.
|
|
453
|
+
* @type {Logger}
|
|
454
|
+
*/
|
|
455
|
+
self.Logger = new Logger(this.LogPath,this.LoggingFlag)
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* variable to be passed to make channel run in lite mode.
|
|
459
|
+
* @type {string}
|
|
460
|
+
*/
|
|
461
|
+
self.LiteMode = "lit"
|
|
462
|
+
AccessToken = Token;
|
|
463
|
+
HSMToken = decodeJWTAndExtractHSMKey(AccessToken,self.Logger);
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* saves the final onopen function after adding necessary changes to callback function provided by user.
|
|
467
|
+
* @type {Function}
|
|
468
|
+
*/
|
|
469
|
+
self.socketonOpen = undefined;
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* saves the final onmessage function after adding necessary changes to callback function provided by user.
|
|
473
|
+
* @type {Function}
|
|
474
|
+
*/
|
|
475
|
+
self.onticks = undefined;
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* saves the final onerror function after adding necessary changes to callback function provided by user.
|
|
479
|
+
* @type {Function}
|
|
480
|
+
*/
|
|
481
|
+
self.socketonerror = undefined;
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* saves the final onclose function after adding necessary changes to callback function provided by user.
|
|
485
|
+
* @type {Function}
|
|
486
|
+
*/
|
|
487
|
+
self.socketonclose = undefined;
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* saves the raw on message function provided by user.
|
|
491
|
+
* @type {Function}
|
|
492
|
+
*/
|
|
493
|
+
self.callbackfunction = function (message) { console.log({ "SocketMessage": message }) };
|
|
494
|
+
|
|
495
|
+
//resets second counter every second.
|
|
496
|
+
self.secondcountertimer= setInterval(() => {
|
|
497
|
+
secondcounter = 0;
|
|
498
|
+
}, 1000);
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* saves set interval timer that runs pushing data out of the queue(wont push data out if ratelimit is breached) to send it to socket.
|
|
502
|
+
* @type {NodeJS.Timer}
|
|
503
|
+
*/
|
|
504
|
+
self.interval = undefined
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* saves set interval timer that runs autoreconnect mechanism in case user has enabled auto reconnect.
|
|
508
|
+
* @type {NodeJS.Timer}
|
|
509
|
+
*/
|
|
510
|
+
self.autreconnecttimer = undefined
|
|
511
|
+
|
|
512
|
+
self.autoreconnectflag = false
|
|
513
|
+
|
|
514
|
+
self.isUserClosed = false
|
|
515
|
+
|
|
516
|
+
DataSocket.instance = this;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* @returns {DataSocket}
|
|
520
|
+
*/
|
|
521
|
+
|
|
522
|
+
static getInstance(Token,path,loggingFlag=true) {
|
|
523
|
+
if (!DataSocket.instance) {
|
|
524
|
+
DataSocket.instance = new DataSocket(Token,path,loggingFlag);
|
|
525
|
+
|
|
526
|
+
}
|
|
527
|
+
return DataSocket.instance;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* pushes data out of queue.
|
|
532
|
+
* @returns {Promise<void>} - A promise that resolves when data is recieved from queue.
|
|
533
|
+
*/
|
|
534
|
+
async[_receiver]() {
|
|
535
|
+
const data = await queue.receive();
|
|
536
|
+
return data
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* interval variable of class runs this function to send data to socket that is pushed out of queue.
|
|
541
|
+
*/
|
|
542
|
+
[_recurssivesend]() {
|
|
543
|
+
const funcname = "recurssivesend"
|
|
544
|
+
const logger = this.Logger
|
|
545
|
+
try {
|
|
546
|
+
if (secondcounter > 10 || !(hsSocket.isConnected())) {//if second ratelimit is breached
|
|
547
|
+
return
|
|
548
|
+
} else {
|
|
549
|
+
this[_receiver]().then(async (response) => {//pushes data out of queue and recieves
|
|
550
|
+
if (response === undefined) {
|
|
551
|
+
return
|
|
552
|
+
} else {
|
|
553
|
+
if (response.constructor === Array) {//if pushed data is array iterate over array and send over socket(some request grouped as array to stop race condition as whenever subscribed to a new channel it is resumed and rest of channels are paused multiple subscribe or unsubscibe at a time raised resuming of unrequired channel)
|
|
554
|
+
response.forEach(async element => {
|
|
555
|
+
if (element.type === 'ifs' || element.type === 'ifu' || element.type === 'mws' || element.type === 'mwu' || element.type === 'dps' || element.type === 'dpu') {//whenever any subscribe unsubscribe request made make its channel as current channel
|
|
556
|
+
currentchannel = element.channelnum
|
|
557
|
+
}
|
|
558
|
+
await this.waitFor(500)
|
|
559
|
+
hsSocket.send(JSON.stringify(element))
|
|
560
|
+
secondcounter++
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
if (response.type === 'ifs' || response.type === 'ifu' || response.type === 'mws' || response.type === 'mwu' || response.type === 'dps' || response.type === 'dpu') {//whenever any subscribe unsubscribe request made make its channel as current channel
|
|
565
|
+
currentchannel = response.channelnum
|
|
566
|
+
}
|
|
567
|
+
await this.waitFor(500)
|
|
568
|
+
hsSocket.send(JSON.stringify(response))
|
|
569
|
+
secondcounter++
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
catch (error) {
|
|
576
|
+
logger.error("error in recurrsivefunction", error, funcname)
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
waitFor = function wait(ms){
|
|
581
|
+
return new Promise(resolve => {
|
|
582
|
+
setTimeout(resolve, ms);
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* used to define onmessage,onerror,onopen,onclose for websocket.
|
|
588
|
+
* @param {string} onwhat - defines for what the callback function is.
|
|
589
|
+
* @param {Function} callback - the callback function.
|
|
590
|
+
* @throws error message if onwhat is not valid.
|
|
591
|
+
*/
|
|
592
|
+
on = function (onwhat, callback) {
|
|
593
|
+
const funcname = "on"
|
|
594
|
+
const logger = this.Logger
|
|
595
|
+
try {
|
|
596
|
+
var que = queue
|
|
597
|
+
if (onwhat === "connect") {
|
|
598
|
+
var akstkn = HSMToken
|
|
599
|
+
this.socketonOpen = function () {
|
|
600
|
+
reconnectiontries = 0 //resets reconnection count if reconnected
|
|
601
|
+
que.send({ "type": "cn", "sessionid": akstkn, "mode": "P" })//sends message on queue to authorize user
|
|
602
|
+
callback()
|
|
603
|
+
}
|
|
604
|
+
hsSocket.onOpen = this.socketonOpen//set classes socketonOpen varibale to hssocket.onOpen
|
|
605
|
+
}
|
|
606
|
+
else if (onwhat === "message") {
|
|
607
|
+
|
|
608
|
+
this.callbackfunction = callback
|
|
609
|
+
var errorcallback = this.socketonerror
|
|
610
|
+
this.onticks = function (message) {
|
|
611
|
+
try {
|
|
612
|
+
var Objmessage = JSON.parse(message, reviver) // parses incoming data as object
|
|
613
|
+
logger.debug("HSM raw data", Objmessage, "conversion")
|
|
614
|
+
Objmessage.forEach(function (element) {
|
|
615
|
+
if (element.stat === undefined) {
|
|
616
|
+
if (element.tk != "") {
|
|
617
|
+
delete element.ts // deleteing symbol provided by HSM as we are mapping our own format symbol
|
|
618
|
+
fulldata[element.tk][element.name] = Object.assign({}, fulldata[element.tk][element.name], element)//updates data to fulldata variable which has full tick data stored in memory
|
|
619
|
+
var ouptput_to_be_sent = Mapper(fulldata[element.tk][element.name], litemodestatus[currentchannel])//Maps data from HSM format to our format and format data according to full/lite mode
|
|
620
|
+
callback(ouptput_to_be_sent)
|
|
621
|
+
}
|
|
622
|
+
} else {
|
|
623
|
+
|
|
624
|
+
var returnmessage = Mapper(JSON.parse(message)[0], litemodestatus[currentchannel])//format success/error response to our format from HSM
|
|
625
|
+
if (returnmessage["s"] === 'Ok') {
|
|
626
|
+
returnmessage["s"] = 'ok'
|
|
627
|
+
callback(returnmessage)
|
|
628
|
+
} else {
|
|
629
|
+
returnmessage["s"] = 'error'
|
|
630
|
+
errorcallback(returnmessage)
|
|
631
|
+
return
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
})
|
|
635
|
+
|
|
636
|
+
} catch (error) {
|
|
637
|
+
logger.error("error converting message", {"error":error}, "conversion")
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
hsSocket.onMessage = this.onticks
|
|
641
|
+
}
|
|
642
|
+
else if (onwhat === "error") {
|
|
643
|
+
this.socketonerror = function (message) {
|
|
644
|
+
callback(message)
|
|
645
|
+
}
|
|
646
|
+
hsSocket.onError = this.socketonerror
|
|
647
|
+
}
|
|
648
|
+
else if (onwhat === "close") {
|
|
649
|
+
this.socketonclose = function () {
|
|
650
|
+
callback()
|
|
651
|
+
clearInterval(DataSocket.instance.interval)
|
|
652
|
+
if(!DataSocket.instance.isUserClosed && (reconnectiontries < maxreconnectiontries)){
|
|
653
|
+
DataSocket.instance._autoreconnect()
|
|
654
|
+
}else{
|
|
655
|
+
clearInterval(DataSocket.instance.secondcountertimer)
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
hsSocket.onClose = this.socketonclose
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
console.error("invalid parameter:", onwhat)
|
|
662
|
+
logger.error("invalid parameter:", { "input": onwhat }, funcname)
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
catch (error) {
|
|
666
|
+
logger.error("error in on function", error, funcname)
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* called to connect to the data socket
|
|
672
|
+
*/
|
|
673
|
+
connect = function () {
|
|
674
|
+
const funcname = "connect"
|
|
675
|
+
const logger = this.Logger
|
|
676
|
+
try {
|
|
677
|
+
this.interval = setInterval(() => {//start [_recurssivesend] function to get data from queue and send to websocket
|
|
678
|
+
this[_recurssivesend]();
|
|
679
|
+
}, 1);
|
|
680
|
+
var akstkn = this.HSMToken
|
|
681
|
+
var que = queue
|
|
682
|
+
if (this.socketonOpen === undefined) {//default callback function incase not passed by user
|
|
683
|
+
hsSocket.onOpen = function () {
|
|
684
|
+
console.log("connected")
|
|
685
|
+
reconnectiontries = 0
|
|
686
|
+
que.send({ "type": "cn", "sessionid": akstkn, "mode": "P" })
|
|
687
|
+
}
|
|
688
|
+
this.socketonOpen = hsSocket.onOpen
|
|
689
|
+
} else {
|
|
690
|
+
hsSocket.onOpen = this.socketonOpen
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if (this.onticks === undefined) {//default callback function incase not passed by user
|
|
694
|
+
hsSocket.onMessage = function (message) {
|
|
695
|
+
var Objmessage = JSON.parse(message, reviver)
|
|
696
|
+
Objmessage.forEach(function (element) {
|
|
697
|
+
if (element.stat === undefined) {
|
|
698
|
+
delete element.ts
|
|
699
|
+
fulldata[element.tk][element.name] = Object.assign({}, fulldata[element.tk][element.name], element)
|
|
700
|
+
|
|
701
|
+
var ouptput_to_be_sent = Mapper(fulldata[element.tk][element.name], litemodestatus[currentchannel])
|
|
702
|
+
console.log({ "SocketMessage": ouptput_to_be_sent })
|
|
703
|
+
|
|
704
|
+
} else {
|
|
705
|
+
var returnmessage = [Mapper(JSON.parse(message)[0], litemodestatus[currentchannel])]
|
|
706
|
+
if (returnmessage[0]["s"] === 'Ok') {
|
|
707
|
+
returnmessage[0]["s"] = 'ok'
|
|
708
|
+
console.log({ "SocketMessage": returnmessage })
|
|
709
|
+
} else {
|
|
710
|
+
returnmessage[0]["s"] = 'error'
|
|
711
|
+
console.log({ "SocketMessage": returnmessage })
|
|
712
|
+
return
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
})
|
|
716
|
+
}
|
|
717
|
+
} else {
|
|
718
|
+
hsSocket.onMessage = this.onticks
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
if (this.socketonclose === undefined) {//default callback function incase not passed by user
|
|
722
|
+
hsSocket.onClose = function () {
|
|
723
|
+
clearInterval(DataSocket.instance.interval);
|
|
724
|
+
console.log("ws closed")
|
|
725
|
+
if(!DataSocket.instance.isUserClosed && (reconnectiontries < maxreconnectiontries)){
|
|
726
|
+
DataSocket.instance._autoreconnect()
|
|
727
|
+
}else{
|
|
728
|
+
clearInterval(DataSocket.instance.secondcountertimer)
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
this.socketonclose = hsSocket.onClose
|
|
732
|
+
} else {
|
|
733
|
+
hsSocket.onClose = this.socketonclose
|
|
734
|
+
}
|
|
735
|
+
if (this.socketonerror === undefined) {//default callback function incase not passed by user
|
|
736
|
+
hsSocket.onError = function (message) {
|
|
737
|
+
console.log("error occoured")
|
|
738
|
+
if (message != undefined) {
|
|
739
|
+
console.log({ "Socketerror": message })
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
this.socketonerror = hsSocket.onError
|
|
743
|
+
} else {
|
|
744
|
+
hsSocket.onError = this.socketonerror
|
|
745
|
+
}
|
|
746
|
+
hsSocket.connect(wsURL); //connects to websocket
|
|
747
|
+
}
|
|
748
|
+
catch (error) {
|
|
749
|
+
logger.error(("error in on connect function", error, funcname))
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Subscribe to a symbol.
|
|
755
|
+
* @param {Array} req - array of symbols to subscribe to.
|
|
756
|
+
* @param {Boolean} [depth=false] - true if you want to subscribe for marketdepth data do not pass it true for index as they dont have marketdepth data.
|
|
757
|
+
* @param {Number} [channelnumber=11] - Channel you want to subscribe on(Warning do not pass any value for this if you dont know the significance).
|
|
758
|
+
*/
|
|
759
|
+
subscribe = async (req, depth = false, channelnumber = 11) => {
|
|
760
|
+
const funcname = "subscribe"
|
|
761
|
+
const logger = this.Logger
|
|
762
|
+
try {
|
|
763
|
+
var callback = this.socketonerror
|
|
764
|
+
if (channelnumber <= 0 || channelnumber > 30) {
|
|
765
|
+
callback({ "code": -99, "type": "sub", "message": "channel can be in range 1 to 30", "s": "error" })//sends this on error callback
|
|
766
|
+
logger.error("channel passed out of range", { "code": -99, "type": "sub", "message": "channel can be in range 1 to 30", "s": "error" }, funcname)
|
|
767
|
+
return
|
|
768
|
+
}
|
|
769
|
+
var subscribeCountOverAllChannels = countValues(symbolcounter)
|
|
770
|
+
if (req.length + subscribeCountOverAllChannels > 5000) {
|
|
771
|
+
logger.error("symbol limit passed", { "code": -351, "type": "sub", "message": `subscription limit exceeding you have ${5000 - subscribeCountOverAllChannels} ws subscription left across all channels`, "s": "error" }, funcname)
|
|
772
|
+
callback({ "code": -351, "type": "sub", "message": `subscription limit exceeding you have ${5000 - subscribeCountOverAllChannels} ws subscription left across all channels`, "s": "error", "count": 5000 - subscribeCountOverAllChannels })
|
|
773
|
+
return
|
|
774
|
+
}
|
|
775
|
+
var alreadySubscribedCount = symbolcounter["if"][channelnumber].length + symbolcounter["dp"][channelnumber].length + symbolcounter["sf"][channelnumber].length
|
|
776
|
+
if (req.length + alreadySubscribedCount > 5000) {
|
|
777
|
+
logger.error("symbol limit passed", { "code": -351, "type": "sub", "message": `subscription limit exceeding you have ${5000 - alreadySubscribedCount} ws subscription left`, "s": "error" }, funcname)
|
|
778
|
+
callback({ "code": -351, "type": "sub", "message": `subscription limit exceeding you have ${5000 - alreadySubscribedCount} ws subscription left`, "s": "error", "count": 5000 - alreadySubscribedCount })
|
|
779
|
+
return
|
|
780
|
+
}
|
|
781
|
+
var indexdata = []
|
|
782
|
+
var symboldata = []
|
|
783
|
+
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
|
|
784
|
+
if (hsSocket === undefined) {
|
|
785
|
+
await delay(1000)
|
|
786
|
+
} else if (!(hsSocket.isConnected())) {//waits in case socket is not open till very first subscribe call
|
|
787
|
+
await delay(1000)
|
|
788
|
+
}
|
|
789
|
+
if (hsSocket === undefined) {
|
|
790
|
+
callback({ "code": -99, "type": "sub", "message": "initalize the socket first", "s": "error" })
|
|
791
|
+
logger.error("socket not initalized", { "code": -99, "type": "sub", "message": "initalize the socket first", "s": "error" }, funcname)
|
|
792
|
+
return
|
|
793
|
+
} else if (!(hsSocket.isConnected())) {
|
|
794
|
+
logger.error("socket is disconnected", { "code": -99, "type": "sub", "message": "socket is disconnected", "s": "error" }, funcname)
|
|
795
|
+
callback({ "code": -99, "type": "sub", "message": "socket is disconnected", "s": "error" })
|
|
796
|
+
return
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
var AuthrizationToken = AccessToken
|
|
800
|
+
var splitlist = splitArray(req, 500)
|
|
801
|
+
var bodies = []
|
|
802
|
+
splitlist.forEach(element=>{
|
|
803
|
+
var body = {
|
|
804
|
+
"symbols":element
|
|
805
|
+
}
|
|
806
|
+
bodies.push(body)
|
|
807
|
+
})
|
|
808
|
+
|
|
809
|
+
const url = "https://api-t1.fyers.in/data/symbol-token"
|
|
810
|
+
var symboltokenmap = await getFyToken(url, AuthrizationToken, bodies , logger, callback)
|
|
811
|
+
|
|
812
|
+
for (const [key, value] of Object.entries(symboltokenmap)) {
|
|
813
|
+
if(value==undefined){
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
var splitkey = key.split('-')
|
|
817
|
+
if (splitkey[splitkey.length - 1] === "INDEX") {
|
|
818
|
+
var instrexg = value.slice(0, 4)//take exchange and segment from fytoken
|
|
819
|
+
var exch = exchmapping[instrexg]//decide exchange name according to HSM based on exchange & segment
|
|
820
|
+
var exchangetoken = mapdata["index_dict"][key]//take symbol name for index required by HSM from our own symbol ticker
|
|
821
|
+
var scripvalue = exch + '|' + exchangetoken
|
|
822
|
+
if (exchangetoken === undefined) {
|
|
823
|
+
callback({ "code": -15, "type": "sub", "message": "Please provide valid symbol", "s": "error", "symbol": key })
|
|
824
|
+
logger.error("map key not found for index", { "symbol": key }, funcname)
|
|
825
|
+
} else {
|
|
826
|
+
if (depth) {
|
|
827
|
+
callback({ "code": -99, "type": "sub", "message": "Index dont have marketdepth", "s": "error" })
|
|
828
|
+
logger.error("map key not found for index", { "code": -99, "type": "sub", "message": "Index dont have marketdepth", "s": "error" }, funcname)
|
|
829
|
+
} else {
|
|
830
|
+
if (fulldata[exchangetoken] === undefined) {
|
|
831
|
+
fulldata[exchangetoken] = {}
|
|
832
|
+
}
|
|
833
|
+
if (fulldata[exchangetoken]['if'] === undefined) {
|
|
834
|
+
fulldata[exchangetoken]['if'] = { 'symbol': key }
|
|
835
|
+
}
|
|
836
|
+
if (fulldata[exchangetoken]['if']['symbol'] === undefined) {
|
|
837
|
+
fulldata[exchangetoken]['if']['symbol'] = key
|
|
838
|
+
}
|
|
839
|
+
indexdata.push(scripvalue)//push data to indexdata array
|
|
840
|
+
symbolcounter["if"][channelnumber] = appendToList(symbolcounter["if"][channelnumber], exchangetoken)//add to symbol counter
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
} else {
|
|
844
|
+
var exchangetoken = value.substring(10);//take exchange token from fytoken
|
|
845
|
+
var instrexg = value.slice(0, 4)// taking exchange & segment values from fytoken
|
|
846
|
+
var exch = exchmapping[instrexg]
|
|
847
|
+
var scripvalue = exch + '|' + exchangetoken
|
|
848
|
+
if (depth) {
|
|
849
|
+
var typeis = "dp"
|
|
850
|
+
// make mapping in fulldata
|
|
851
|
+
if (fulldata[exchangetoken] === undefined) {
|
|
852
|
+
fulldata[exchangetoken] = {}
|
|
853
|
+
}
|
|
854
|
+
if (fulldata[exchangetoken][typeis] === undefined) {
|
|
855
|
+
fulldata[exchangetoken][typeis] = { 'symbol': key }
|
|
856
|
+
}
|
|
857
|
+
if (fulldata[exchangetoken][typeis]['symbol'] === undefined) {
|
|
858
|
+
fulldata[exchangetoken][typeis]['symbol'] = key
|
|
859
|
+
}
|
|
860
|
+
} else {
|
|
861
|
+
var typeis = "sf"
|
|
862
|
+
if (fulldata[exchangetoken] === undefined) {
|
|
863
|
+
fulldata[exchangetoken] = {}
|
|
864
|
+
}
|
|
865
|
+
if (fulldata[exchangetoken][typeis] === undefined) {
|
|
866
|
+
fulldata[exchangetoken][typeis] = { 'symbol': key }
|
|
867
|
+
}
|
|
868
|
+
if (fulldata[exchangetoken][typeis]['symbol'] === undefined) {
|
|
869
|
+
fulldata[exchangetoken][typeis]['symbol'] = key
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
symboldata.push(scripvalue)//push to symbol data
|
|
873
|
+
//adding to symbol counter to know how many symbols are subscribed on each channel for each type that is index/depth/regular&FNO
|
|
874
|
+
symbolcounter[typeis][channelnumber] = appendToList(symbolcounter[typeis][channelnumber], exchangetoken)
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
var requests = []
|
|
878
|
+
//if there is any index to subscribe to
|
|
879
|
+
if (indexdata.length != 0) {
|
|
880
|
+
//split array bigger than length 1500 into array of length 1500
|
|
881
|
+
const splitindexdata = splitArray(indexdata, 1500)
|
|
882
|
+
splitindexdata.forEach(element => {
|
|
883
|
+
var scripstring = ""
|
|
884
|
+
element.forEach(subelement => {
|
|
885
|
+
//making & seperated string of symbols to send on socket
|
|
886
|
+
scripstring = scripstring + subelement + "&"
|
|
887
|
+
});
|
|
888
|
+
scripstring = scripstring.slice(0, -1)
|
|
889
|
+
//push it to the queue to be sent to socket
|
|
890
|
+
requests.push({
|
|
891
|
+
"type": "ifs",
|
|
892
|
+
"scrips": scripstring,
|
|
893
|
+
"channelnum": channelnumber
|
|
894
|
+
})
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
//if there is any stock or FNO to subscribe to
|
|
898
|
+
if (symboldata.length != 0) {
|
|
899
|
+
//split array bigger than length 1500 into array of length 1500
|
|
900
|
+
const splitsymboldata = splitArray(symboldata, 1500)
|
|
901
|
+
splitsymboldata.forEach(element => {
|
|
902
|
+
var scripstring = ""
|
|
903
|
+
element.forEach(subelement => {
|
|
904
|
+
//making & seperated string of symbols to send on socket
|
|
905
|
+
scripstring = scripstring + subelement + "&"
|
|
906
|
+
});
|
|
907
|
+
scripstring = scripstring.slice(0, -1)
|
|
908
|
+
|
|
909
|
+
if (depth) {
|
|
910
|
+
var typeval = "dps"
|
|
911
|
+
} else {
|
|
912
|
+
var typeval = "mws"
|
|
913
|
+
}
|
|
914
|
+
requests.push({
|
|
915
|
+
"type": typeval,
|
|
916
|
+
"scrips": scripstring,
|
|
917
|
+
"channelnum": channelnumber
|
|
918
|
+
})
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
//once all requests appended to requests array if there is a current channel change we send pause for every other channel and resume for current channel
|
|
922
|
+
if (requests.length != 0) {
|
|
923
|
+
var pauselist = returnstopchannelarray(channelnumber)
|
|
924
|
+
if (channelnumber != currentchannel) {
|
|
925
|
+
requests.push({
|
|
926
|
+
"type": "cp",
|
|
927
|
+
"channelnums": pauselist
|
|
928
|
+
})
|
|
929
|
+
requests.push({
|
|
930
|
+
"type": "cr",
|
|
931
|
+
"channelnums": [channelnumber]
|
|
932
|
+
})
|
|
933
|
+
}
|
|
934
|
+
//sending to queue as a array so one request is sent after another as a group on socket
|
|
935
|
+
queue.send(requests)
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
catch (error) {
|
|
939
|
+
logger.error("error in subscribe function", error, funcname)
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* Unsubscribe from a symbol(s).
|
|
945
|
+
* @param {Array} req - array of symbols to subscribe to.
|
|
946
|
+
* @param {Boolean} [depth=false] - true if you want to subscribe for marketdepth data do not pass it true for index as they dont have marketdepth data.
|
|
947
|
+
* @param {Number} [channelnumber=11] - Channel you want to subscribe on(Warning do not pass any value for this if you dont know the significance).
|
|
948
|
+
*/
|
|
949
|
+
unsubscribe = async (req, depth = false, channelnumber = 11) => {
|
|
950
|
+
const funcname = "unsubscribe"
|
|
951
|
+
const logger = this.Logger
|
|
952
|
+
try {
|
|
953
|
+
var callback = this.socketonerror
|
|
954
|
+
if (channelnumber <= 0 || channelnumber > 30) {
|
|
955
|
+
callback({ "code": -99, "type": "sub", "message": "channel can be in range 1 to 30", "s": "error" })
|
|
956
|
+
logger.error("channel passed out of range", { "code": -99, "type": "sub", "message": "channel can be in range 1 to 30", "s": "error" }, funcname)
|
|
957
|
+
return
|
|
958
|
+
}
|
|
959
|
+
var indexdata = []
|
|
960
|
+
var symboldata = []
|
|
961
|
+
var delobj = []
|
|
962
|
+
if (hsSocket === undefined) {
|
|
963
|
+
callback({ "code": -99, "type": "sub", "message": "initalize the socket first", "s": "error" })
|
|
964
|
+
logger.error("socket not initalized", { "code": -99, "type": "sub", "message": "initalize the socket first", "s": "error" }, funcname)
|
|
965
|
+
return
|
|
966
|
+
}
|
|
967
|
+
else if (!(hsSocket.isConnected())) {
|
|
968
|
+
callback({ "code": -99, "type": "sub", "message": "socket is disconnected", "s": "error" })
|
|
969
|
+
logger.error("socket is disconnected", { "code": -99, "type": "sub", "message": "socket is disconnected", "s": "error" }, funcname)
|
|
970
|
+
return
|
|
971
|
+
}
|
|
972
|
+
var AuthrizationToken = AccessToken
|
|
973
|
+
var splitlist = splitArray(req, 500)
|
|
974
|
+
var bodies = []
|
|
975
|
+
|
|
976
|
+
splitlist.forEach(element=>{
|
|
977
|
+
var body = {
|
|
978
|
+
"symbols":element
|
|
979
|
+
}
|
|
980
|
+
bodies.push(body)
|
|
981
|
+
})
|
|
982
|
+
|
|
983
|
+
const url = "https://api-t1.fyers.in/data/symbol-token"
|
|
984
|
+
var symboltokenmap = await getFyToken(url, AuthrizationToken, bodies , logger, callback)
|
|
985
|
+
for (const [key, value] of Object.entries(symboltokenmap)) {
|
|
986
|
+
if(value==undefined){
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
var splitkey = key.split('-')
|
|
990
|
+
if (splitkey[splitkey.length - 1] === "INDEX") {
|
|
991
|
+
//getting exchange and segement from fytoken and deciding its values as required by HSM
|
|
992
|
+
var instrexg = value.slice(0, 4)
|
|
993
|
+
var exch = exchmapping[instrexg]
|
|
994
|
+
//getting index name in format as required by HSM
|
|
995
|
+
var exchangetoken = mapdata["index_dict"][key]
|
|
996
|
+
var scripvalue = exch + '|' + exchangetoken
|
|
997
|
+
if (exchangetoken === undefined) {
|
|
998
|
+
callback({ "code": -15, "type": "sub", "message": "Please provide valid symbol", "s": "error", "symbol": key })
|
|
999
|
+
logger.error("map key not found for index", { "symbol": key }, funcname)
|
|
1000
|
+
} else {
|
|
1001
|
+
if (depth) {
|
|
1002
|
+
callback({ "code": -99, "type": "sub", "message": "Index dont have marketdepth", "s": "error" })
|
|
1003
|
+
logger.error("map key not found for index", { "code": -99, "type": "sub", "message": "Index dont have marketdepth", "s": "error" }, funcname)
|
|
1004
|
+
} else {
|
|
1005
|
+
var delval = "if"
|
|
1006
|
+
indexdata.push(scripvalue)
|
|
1007
|
+
delobj.push({ "exchangetoken": exchangetoken, "delval": delval })
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
} else {
|
|
1011
|
+
if (depth) {
|
|
1012
|
+
var delval = "dp"
|
|
1013
|
+
} else {
|
|
1014
|
+
var delval = "sf"
|
|
1015
|
+
}
|
|
1016
|
+
//getting exchange token from fytoken
|
|
1017
|
+
var exchangetoken = value.substring(10);
|
|
1018
|
+
var instrexg = value.slice(0, 4)
|
|
1019
|
+
var exch = exchmapping[instrexg]
|
|
1020
|
+
var scripvalue = exch + '|' + exchangetoken
|
|
1021
|
+
symboldata.push(scripvalue)
|
|
1022
|
+
delobj.push({ "exchangetoken": exchangetoken, "delval": delval })
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
var requests = []
|
|
1027
|
+
if (indexdata.length != 0) {
|
|
1028
|
+
// splitting requested array into sub array of length 1500
|
|
1029
|
+
const splitindexdata = splitArray(indexdata, 1500)
|
|
1030
|
+
splitindexdata.forEach(element => {
|
|
1031
|
+
var scripstring = ""
|
|
1032
|
+
element.forEach(subelement => {
|
|
1033
|
+
//making & seperated string of scrips
|
|
1034
|
+
scripstring = scripstring + subelement + "&"
|
|
1035
|
+
});
|
|
1036
|
+
scripstring = scripstring.slice(0, -1)
|
|
1037
|
+
requests.push({
|
|
1038
|
+
"type": "ifu",
|
|
1039
|
+
"scrips": scripstring,
|
|
1040
|
+
"channelnum": channelnumber
|
|
1041
|
+
})
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
if (symboldata.length != 0) {
|
|
1046
|
+
const splitsymboldata = splitArray(symboldata, 1500)
|
|
1047
|
+
|
|
1048
|
+
splitsymboldata.forEach(element => {
|
|
1049
|
+
var scripstring = ""
|
|
1050
|
+
element.forEach(subelement => {
|
|
1051
|
+
scripstring = scripstring + subelement + "&"
|
|
1052
|
+
});
|
|
1053
|
+
scripstring = scripstring.slice(0, -1)
|
|
1054
|
+
if (depth) {
|
|
1055
|
+
var typeval = "dpu"
|
|
1056
|
+
} else {
|
|
1057
|
+
var typeval = "mwu"
|
|
1058
|
+
}
|
|
1059
|
+
requests.push({
|
|
1060
|
+
"type": typeval,
|
|
1061
|
+
"scrips": scripstring,
|
|
1062
|
+
"channelnum": channelnumber
|
|
1063
|
+
})
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
//once all requests appended to requests array if there is a current channel change we send pause for every other channel and resume for current channel
|
|
1068
|
+
if (requests.length != 0) {
|
|
1069
|
+
var pauselist = returnstopchannelarray(channelnumber)
|
|
1070
|
+
if (channelnumber != currentchannel) {
|
|
1071
|
+
//channel pause request
|
|
1072
|
+
requests.push({
|
|
1073
|
+
"type": "cp",
|
|
1074
|
+
"channelnums": pauselist
|
|
1075
|
+
})
|
|
1076
|
+
//channel resume request
|
|
1077
|
+
requests.push({
|
|
1078
|
+
"type": "cr",
|
|
1079
|
+
"channelnums": [channelnumber]
|
|
1080
|
+
})
|
|
1081
|
+
}
|
|
1082
|
+
//sending to queue as a array so one request is sent after another as a group on socket
|
|
1083
|
+
queue.send(requests)
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
//deletes data stored in memory if it is unsubscribed and not subscribed on any other channel
|
|
1087
|
+
if (delobj.length != 0) {
|
|
1088
|
+
delobj.forEach(element => {
|
|
1089
|
+
if (!(fulldata[element.exchangetoken] === undefined) && checkValueInLists(symbolcounter[delval], element.exchangetoken)) {
|
|
1090
|
+
delete fulldata[element.exchangetoken][element.delval]
|
|
1091
|
+
}
|
|
1092
|
+
symbolcounter[delval][channelnumber] = returnstopchannelarray(element.exchangetoken, symbolcounter[delval][channelnumber])
|
|
1093
|
+
})
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
catch (error) {
|
|
1097
|
+
logger.error("error in unsubscribe function", error, funcname)
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
/**
|
|
1102
|
+
* call to close the datasocket.
|
|
1103
|
+
*/
|
|
1104
|
+
close = function () {
|
|
1105
|
+
const funcname = "close"
|
|
1106
|
+
const logger = this.Logger
|
|
1107
|
+
try {
|
|
1108
|
+
if (hsSocket === undefined) {
|
|
1109
|
+
console.log("socket already closed")
|
|
1110
|
+
return
|
|
1111
|
+
} else if (!(hsSocket.isConnected())) {
|
|
1112
|
+
console.log("socket already closed")
|
|
1113
|
+
return
|
|
1114
|
+
} else {
|
|
1115
|
+
//stop autoreconnection
|
|
1116
|
+
clearInterval(this.autreconnecttimer)
|
|
1117
|
+
clearInterval(this.interval)
|
|
1118
|
+
clearInterval(this.secondcountertimer)
|
|
1119
|
+
|
|
1120
|
+
this.isUserClosed = true
|
|
1121
|
+
|
|
1122
|
+
hsSocket.close();
|
|
1123
|
+
// clears inmemory socket data stored
|
|
1124
|
+
fulldata = {}
|
|
1125
|
+
//clears queue so incase socket resumed by user queue is cleared
|
|
1126
|
+
queue.clear()
|
|
1127
|
+
logger.debug("socket closed by function", {}, funcname)
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
catch (error) {
|
|
1131
|
+
logger.error("error in close function", error, funcname)
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
/**
|
|
1136
|
+
* call to check if datasocket is connected
|
|
1137
|
+
*/
|
|
1138
|
+
isConnected = function () {
|
|
1139
|
+
if (hsSocket === undefined) {
|
|
1140
|
+
return false
|
|
1141
|
+
} else if (!(hsSocket.isConnected())) {
|
|
1142
|
+
return false
|
|
1143
|
+
} else {
|
|
1144
|
+
return true
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* call to resume a channel pauses all other channel
|
|
1150
|
+
* @param {Number} channelnum - channel number you want to resume.
|
|
1151
|
+
*/
|
|
1152
|
+
channelresume = function (channelnum) {
|
|
1153
|
+
const funcname = "channelresume"
|
|
1154
|
+
const logger = this.Logger
|
|
1155
|
+
try {
|
|
1156
|
+
if (channelnum.constructor === Number) {
|
|
1157
|
+
if (channel > 30 || channel < 1) {
|
|
1158
|
+
console.log("channel out of range channel should be between 1 and 30")
|
|
1159
|
+
logger.error("channel out of range channel should be between 1 and 30", null, funcname)
|
|
1160
|
+
return
|
|
1161
|
+
}
|
|
1162
|
+
var pauselist = returnstopchannelarray(channelnum)
|
|
1163
|
+
queue.send([{
|
|
1164
|
+
"type": "cp",
|
|
1165
|
+
"channelnums": pauselist
|
|
1166
|
+
}, {
|
|
1167
|
+
"type": "cr",
|
|
1168
|
+
"channelnums": [channelnum]
|
|
1169
|
+
}])
|
|
1170
|
+
} else {
|
|
1171
|
+
console.log("pass channel as a number")
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
catch (error) {
|
|
1175
|
+
logger.error("error in channelresume function", error, funcname)
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
/**
|
|
1180
|
+
* call to set channel to full mode or lite mode
|
|
1181
|
+
* @param {string} modestring - The value parameter that can be either object.FullMode or object.LiteMode.
|
|
1182
|
+
* @param {Number|Number[]} channel - channel number you want to set mode for as a number or array of numbers.
|
|
1183
|
+
*/
|
|
1184
|
+
mode = function (modestring, channel = 11) {
|
|
1185
|
+
const funcname = "mode"
|
|
1186
|
+
const logger = this.Logger
|
|
1187
|
+
try {
|
|
1188
|
+
if (channel.constructor === Array) {
|
|
1189
|
+
channel.forEach(element => {
|
|
1190
|
+
if (element > 30 || element < 1) {
|
|
1191
|
+
console.log("channel out of range channel should be between 1 and 30")
|
|
1192
|
+
logger.error("channel out of range channel should be between 1 and 30", null, funcname)
|
|
1193
|
+
return
|
|
1194
|
+
}
|
|
1195
|
+
});
|
|
1196
|
+
queue.send({
|
|
1197
|
+
"type": modestring,
|
|
1198
|
+
"channelnums": channel
|
|
1199
|
+
})
|
|
1200
|
+
if (modestring === "lit") {
|
|
1201
|
+
channel.forEach(element => {
|
|
1202
|
+
litemodestatus[element] = true
|
|
1203
|
+
});
|
|
1204
|
+
} else {
|
|
1205
|
+
channel.forEach(element => {
|
|
1206
|
+
litemodestatus[element] = false
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
} else if (channel.constructor === Number) {
|
|
1210
|
+
if (channel > 30 || channel < 1) {
|
|
1211
|
+
console.log("channel out of range channel should be between 1 and 30")
|
|
1212
|
+
logger.error("channel out of range channel should be between 1 and 30", null, funcname)
|
|
1213
|
+
return
|
|
1214
|
+
}
|
|
1215
|
+
queue.send({
|
|
1216
|
+
"type": modestring,
|
|
1217
|
+
"channelnums": [channel]
|
|
1218
|
+
})
|
|
1219
|
+
if (modestring === "lit") {
|
|
1220
|
+
litemodestatus[channel] = true
|
|
1221
|
+
} else {
|
|
1222
|
+
litemodestatus[channel] = false
|
|
1223
|
+
}
|
|
1224
|
+
} else {
|
|
1225
|
+
console.log("please pass channel as number or array of numbers")
|
|
1226
|
+
logger.error("please pass channel as number or array of numbers", null, funcname)
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
catch (error) {
|
|
1230
|
+
logger.error("error in mode function", error, funcname)
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
/**
|
|
1235
|
+
* call to enable autoreconnect functionality of websocket.
|
|
1236
|
+
*/
|
|
1237
|
+
_autoreconnect = function(){
|
|
1238
|
+
const funcname = "autoreconnect"
|
|
1239
|
+
const logger = this.Logger
|
|
1240
|
+
if (this.autoreconnectflag){
|
|
1241
|
+
var waitSeconds = Math.floor((reconnectiontries+5)/5);
|
|
1242
|
+
waitSeconds *= 5;
|
|
1243
|
+
|
|
1244
|
+
try {
|
|
1245
|
+
this.autreconnecttimer = setTimeout(() => {
|
|
1246
|
+
if ((!(hsSocket.isConnected())) && (reconnectiontries < maxreconnectiontries)) {
|
|
1247
|
+
console.log("trying to reconnect ", reconnectiontries + 1)
|
|
1248
|
+
reconnectiontries++;
|
|
1249
|
+
symbolcounter = {
|
|
1250
|
+
"if": { 1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: [], 8: [], 9: [], 10: [], 11: [], 12: [], 13: [], 14: [], 15: [], 16: [], 17: [], 18: [], 19: [], 20: [], 21: [], 22: [], 23: [], 24: [], 25: [], 26: [], 27: [], 28: [], 29: [], 30: [] },
|
|
1251
|
+
"sf": { 1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: [], 8: [], 9: [], 10: [], 11: [], 12: [], 13: [], 14: [], 15: [], 16: [], 17: [], 18: [], 19: [], 20: [], 21: [], 22: [], 23: [], 24: [], 25: [], 26: [], 27: [], 28: [], 29: [], 30: [] },
|
|
1252
|
+
"dp": { 1: [], 2: [], 3: [], 4: [], 5: [], 6: [], 7: [], 8: [], 9: [], 10: [], 11: [], 12: [], 13: [], 14: [], 15: [], 16: [], 17: [], 18: [], 19: [], 20: [], 21: [], 22: [], 23: [], 24: [], 25: [], 26: [], 27: [], 28: [], 29: [], 30: [] }
|
|
1253
|
+
};
|
|
1254
|
+
this.connect()
|
|
1255
|
+
} else if (reconnectiontries >= maxreconnectiontries) {
|
|
1256
|
+
console.log("max autoconnect tries exceeded")
|
|
1257
|
+
reconnectiontries = 0
|
|
1258
|
+
}
|
|
1259
|
+
}, (waitSeconds)*1000);
|
|
1260
|
+
}
|
|
1261
|
+
catch (error) {
|
|
1262
|
+
logger.error("error in autoreconnect function", error, funcname)
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
autoreconnect=function(reConnectTriesCount=5){
|
|
1268
|
+
this.autoreconnectflag=true
|
|
1269
|
+
maxreconnectiontries = (reConnectTriesCount>50) ? 50 : reConnectTriesCount
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
return DataSocket
|
|
1273
|
+
})();
|
|
1274
|
+
|
|
1275
|
+
|
|
1276
|
+
module.exports = DataSocket
|