shoonya-sdk 0.3.6 → 0.4.0
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/dist/shoonya-sdk.d.mts +25 -0
- package/dist/shoonya-sdk.d.ts +25 -0
- package/dist/shoonya-sdk.js +68 -19
- package/dist/shoonya-sdk.mjs +68 -19
- package/package.json +1 -1
package/dist/shoonya-sdk.d.mts
CHANGED
|
@@ -204,6 +204,11 @@ interface OrderBookFail {
|
|
|
204
204
|
}
|
|
205
205
|
type OrderBook = OrderBookSuccess[] | OrderBookFail;
|
|
206
206
|
|
|
207
|
+
type ShoonyaWSEvents = "connect" | "open" | "priceUpdate" | "orderUpdate" | "stopped" | "close" | "reconnect" | "error";
|
|
208
|
+
declare interface Shoonya {
|
|
209
|
+
on(eventName: ShoonyaWSEvents, listener: (...args: any[]) => void): this;
|
|
210
|
+
emit(eventName: ShoonyaWSEvents, ...args: any[]): boolean;
|
|
211
|
+
}
|
|
207
212
|
declare class Shoonya extends EventEmitter {
|
|
208
213
|
accessToken: string;
|
|
209
214
|
userId: string;
|
|
@@ -220,8 +225,21 @@ declare class Shoonya extends EventEmitter {
|
|
|
220
225
|
private apiKey;
|
|
221
226
|
private cronJobRunning;
|
|
222
227
|
private scripList;
|
|
228
|
+
private reconnectTimeout;
|
|
229
|
+
private heartbeatTimeout;
|
|
230
|
+
private lastWsMsgAt;
|
|
223
231
|
constructor(options?: {
|
|
224
232
|
logging: boolean;
|
|
233
|
+
/**
|
|
234
|
+
* check for connection every x ms
|
|
235
|
+
* @default 30000
|
|
236
|
+
*/
|
|
237
|
+
reconnectTimeout?: number;
|
|
238
|
+
/**
|
|
239
|
+
* send heartbeat msg in every x ms
|
|
240
|
+
* @default 3000
|
|
241
|
+
*/
|
|
242
|
+
heartbeatTimeout?: number;
|
|
225
243
|
});
|
|
226
244
|
request<T>(path: Path, body: {
|
|
227
245
|
data: RequestDataType;
|
|
@@ -319,6 +337,13 @@ declare class Shoonya extends EventEmitter {
|
|
|
319
337
|
}>;
|
|
320
338
|
private getFormattedExpiry;
|
|
321
339
|
private getNextFormattedDates;
|
|
340
|
+
getLastWSMessage(): Date;
|
|
341
|
+
getWSState(): {
|
|
342
|
+
OPEN: boolean;
|
|
343
|
+
CLOSED: boolean;
|
|
344
|
+
CLOSING: boolean;
|
|
345
|
+
CONNECTING: boolean;
|
|
346
|
+
};
|
|
322
347
|
connectWS(): void;
|
|
323
348
|
/**
|
|
324
349
|
*
|
package/dist/shoonya-sdk.d.ts
CHANGED
|
@@ -204,6 +204,11 @@ interface OrderBookFail {
|
|
|
204
204
|
}
|
|
205
205
|
type OrderBook = OrderBookSuccess[] | OrderBookFail;
|
|
206
206
|
|
|
207
|
+
type ShoonyaWSEvents = "connect" | "open" | "priceUpdate" | "orderUpdate" | "stopped" | "close" | "reconnect" | "error";
|
|
208
|
+
declare interface Shoonya {
|
|
209
|
+
on(eventName: ShoonyaWSEvents, listener: (...args: any[]) => void): this;
|
|
210
|
+
emit(eventName: ShoonyaWSEvents, ...args: any[]): boolean;
|
|
211
|
+
}
|
|
207
212
|
declare class Shoonya extends EventEmitter {
|
|
208
213
|
accessToken: string;
|
|
209
214
|
userId: string;
|
|
@@ -220,8 +225,21 @@ declare class Shoonya extends EventEmitter {
|
|
|
220
225
|
private apiKey;
|
|
221
226
|
private cronJobRunning;
|
|
222
227
|
private scripList;
|
|
228
|
+
private reconnectTimeout;
|
|
229
|
+
private heartbeatTimeout;
|
|
230
|
+
private lastWsMsgAt;
|
|
223
231
|
constructor(options?: {
|
|
224
232
|
logging: boolean;
|
|
233
|
+
/**
|
|
234
|
+
* check for connection every x ms
|
|
235
|
+
* @default 30000
|
|
236
|
+
*/
|
|
237
|
+
reconnectTimeout?: number;
|
|
238
|
+
/**
|
|
239
|
+
* send heartbeat msg in every x ms
|
|
240
|
+
* @default 3000
|
|
241
|
+
*/
|
|
242
|
+
heartbeatTimeout?: number;
|
|
225
243
|
});
|
|
226
244
|
request<T>(path: Path, body: {
|
|
227
245
|
data: RequestDataType;
|
|
@@ -319,6 +337,13 @@ declare class Shoonya extends EventEmitter {
|
|
|
319
337
|
}>;
|
|
320
338
|
private getFormattedExpiry;
|
|
321
339
|
private getNextFormattedDates;
|
|
340
|
+
getLastWSMessage(): Date;
|
|
341
|
+
getWSState(): {
|
|
342
|
+
OPEN: boolean;
|
|
343
|
+
CLOSED: boolean;
|
|
344
|
+
CLOSING: boolean;
|
|
345
|
+
CONNECTING: boolean;
|
|
346
|
+
};
|
|
322
347
|
connectWS(): void;
|
|
323
348
|
/**
|
|
324
349
|
*
|
package/dist/shoonya-sdk.js
CHANGED
|
@@ -158,13 +158,22 @@ var Shoonya = class extends import_events.EventEmitter {
|
|
|
158
158
|
apiKey;
|
|
159
159
|
cronJobRunning = false;
|
|
160
160
|
scripList;
|
|
161
|
+
reconnectTimeout;
|
|
162
|
+
heartbeatTimeout;
|
|
163
|
+
lastWsMsgAt;
|
|
161
164
|
constructor(options) {
|
|
162
|
-
const {
|
|
165
|
+
const {
|
|
166
|
+
logging = false,
|
|
167
|
+
reconnectTimeout = 3e4,
|
|
168
|
+
heartbeatTimeout = 3e3
|
|
169
|
+
} = options || {};
|
|
163
170
|
super({ captureRejections: true });
|
|
164
171
|
this.userId = "";
|
|
165
172
|
this.accessToken = "";
|
|
166
173
|
this.accountId = this.userId;
|
|
167
174
|
this.logging = logging;
|
|
175
|
+
this.reconnectTimeout = { timer: null, timeout: reconnectTimeout };
|
|
176
|
+
this.heartbeatTimeout = { timer: null, timeout: heartbeatTimeout };
|
|
168
177
|
if (logging) {
|
|
169
178
|
this.logger = new logger_default(
|
|
170
179
|
`./logs/log_${Date.now().toString()}.log`,
|
|
@@ -472,23 +481,34 @@ var Shoonya = class extends import_events.EventEmitter {
|
|
|
472
481
|
return `${day}${newMonth}${newYear}`;
|
|
473
482
|
}
|
|
474
483
|
getNextFormattedDates(expiry) {
|
|
475
|
-
let
|
|
484
|
+
let multiplier = 1;
|
|
476
485
|
if (expiry === "next" || expiry === "more-than-1-day")
|
|
477
|
-
|
|
486
|
+
multiplier = 2;
|
|
478
487
|
if (expiry === "next-next")
|
|
479
|
-
|
|
488
|
+
multiplier = 3;
|
|
480
489
|
const dates = [];
|
|
481
490
|
let currentDate = /* @__PURE__ */ new Date();
|
|
482
|
-
for (let i = 0; i <= 7 *
|
|
491
|
+
for (let i = 0; i <= 7 * multiplier; i++) {
|
|
483
492
|
const formattedDate = this.getFormattedExpiry(currentDate);
|
|
484
493
|
dates.push([formattedDate, currentDate]);
|
|
485
494
|
currentDate = new Date(currentDate.setDate(currentDate.getDate() + 1));
|
|
486
495
|
}
|
|
487
496
|
return dates;
|
|
488
497
|
}
|
|
498
|
+
getLastWSMessage() {
|
|
499
|
+
return this.lastWsMsgAt;
|
|
500
|
+
}
|
|
501
|
+
getWSState() {
|
|
502
|
+
return {
|
|
503
|
+
OPEN: this.ws && this.ws.readyState === this.ws.OPEN,
|
|
504
|
+
CLOSED: this.ws && this.ws.readyState === this.ws.CLOSED,
|
|
505
|
+
CLOSING: this.ws && this.ws.readyState === this.ws.CLOSING,
|
|
506
|
+
CONNECTING: this.ws && this.ws.readyState === this.ws.CONNECTING
|
|
507
|
+
};
|
|
508
|
+
}
|
|
489
509
|
// WebSocket API
|
|
490
510
|
connectWS() {
|
|
491
|
-
if (this.
|
|
511
|
+
if (this.getWSState().OPEN || this.getWSState().CONNECTING) {
|
|
492
512
|
return;
|
|
493
513
|
}
|
|
494
514
|
this.ws = new import_ws.WebSocket(this.wsBaseUrl);
|
|
@@ -501,15 +521,21 @@ var Shoonya = class extends import_events.EventEmitter {
|
|
|
501
521
|
susertoken: this.accessToken
|
|
502
522
|
};
|
|
503
523
|
this.ws.send(JSON.stringify(msg));
|
|
524
|
+
this.emit("open", "opened ws connection");
|
|
525
|
+
clearInterval(this.heartbeatTimeout.timer);
|
|
526
|
+
this.heartbeatTimeout.timer = setInterval(() => {
|
|
527
|
+
const heartbeatMsg = '{"t":"h"}';
|
|
528
|
+
this.getWSState().OPEN && this.ws.send(heartbeatMsg);
|
|
529
|
+
this.logging && this.logger.log(`sent heartbeat message: ${heartbeatMsg}`);
|
|
530
|
+
}, this.heartbeatTimeout.timeout);
|
|
504
531
|
if (this.cronJobRunning) {
|
|
505
532
|
this.subscribe(this.scripList);
|
|
506
533
|
return;
|
|
507
534
|
}
|
|
508
|
-
import_node_cron.default.schedule(
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
if (hour === 3 && min === 43) {
|
|
535
|
+
import_node_cron.default.schedule(
|
|
536
|
+
"13 9 * * 1-5",
|
|
537
|
+
async () => {
|
|
538
|
+
this.cronJobRunning = true;
|
|
513
539
|
try {
|
|
514
540
|
const cred = {
|
|
515
541
|
apiKey: this.apiKey,
|
|
@@ -518,21 +544,21 @@ var Shoonya = class extends import_events.EventEmitter {
|
|
|
518
544
|
userId: this.userId
|
|
519
545
|
};
|
|
520
546
|
await this.login(cred);
|
|
521
|
-
this.
|
|
547
|
+
this.logging && this.logger.log("Token Refreshed");
|
|
522
548
|
} catch (err) {
|
|
523
549
|
console.error(err);
|
|
524
550
|
const errMessage = `Token refreshing failed. ERR: ${err.message}`;
|
|
525
551
|
this.logging && this.logger.log(errMessage, "ERROR");
|
|
552
|
+
this.getWSState().OPEN && this.ws.close();
|
|
526
553
|
this.emit("stopped", errMessage);
|
|
527
554
|
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
}
|
|
532
|
-
});
|
|
555
|
+
},
|
|
556
|
+
{ timezone: "Asia/Kolkata" }
|
|
557
|
+
);
|
|
533
558
|
});
|
|
534
559
|
this.ws.addEventListener("message", (e) => {
|
|
535
560
|
const result = JSON.parse(e.data.toString());
|
|
561
|
+
this.lastWsMsgAt = /* @__PURE__ */ new Date();
|
|
536
562
|
if (result.t === "dk") {
|
|
537
563
|
this.logging && this.logger.log(`Subscribed to ${result.ts}. Listening...`);
|
|
538
564
|
}
|
|
@@ -550,6 +576,19 @@ var Shoonya = class extends import_events.EventEmitter {
|
|
|
550
576
|
}, 3e3);
|
|
551
577
|
return;
|
|
552
578
|
}
|
|
579
|
+
clearTimeout(this.reconnectTimeout.timer);
|
|
580
|
+
this.reconnectTimeout.timer = setTimeout(() => {
|
|
581
|
+
let msg = {
|
|
582
|
+
t: "c",
|
|
583
|
+
uid: this.userId,
|
|
584
|
+
actid: this.accountId,
|
|
585
|
+
source: "API",
|
|
586
|
+
susertoken: this.accessToken
|
|
587
|
+
};
|
|
588
|
+
this.subscribe(this.scripList);
|
|
589
|
+
this.getWSState().OPEN && this.ws.send(JSON.stringify(msg));
|
|
590
|
+
this.emit("reconnect", "reconnected with shoonya ws");
|
|
591
|
+
}, this.reconnectTimeout.timeout);
|
|
553
592
|
clearTimeout(this.disconnectTimer);
|
|
554
593
|
this.disconnectTimer = setTimeout(() => {
|
|
555
594
|
const day = (/* @__PURE__ */ new Date()).getDay();
|
|
@@ -558,7 +597,16 @@ var Shoonya = class extends import_events.EventEmitter {
|
|
|
558
597
|
const err = "API have stopped to respond! there could be a problem with API or market has closed";
|
|
559
598
|
this.logging && this.logger.log(err, "WARN");
|
|
560
599
|
this.emit("stopped", err);
|
|
561
|
-
|
|
600
|
+
this.getWSState().OPEN && this.ws.close();
|
|
601
|
+
}, 6e4 * 60 * 18 - 1e4);
|
|
602
|
+
});
|
|
603
|
+
this.ws.addEventListener("error", (e) => {
|
|
604
|
+
this.logging && this.logger.log("some error in ws");
|
|
605
|
+
this.emit("error", e);
|
|
606
|
+
this.getWSState().OPEN && this.ws.close();
|
|
607
|
+
});
|
|
608
|
+
this.ws.addEventListener("close", (e) => {
|
|
609
|
+
this.emit("close", e);
|
|
562
610
|
});
|
|
563
611
|
}
|
|
564
612
|
/**
|
|
@@ -609,7 +657,8 @@ var Shoonya = class extends import_events.EventEmitter {
|
|
|
609
657
|
}
|
|
610
658
|
}
|
|
611
659
|
disconnect() {
|
|
612
|
-
if (this.
|
|
660
|
+
if (this.getWSState().OPEN || this.getWSState().CONNECTING) {
|
|
661
|
+
clearInterval(this.heartbeatTimeout.timer);
|
|
613
662
|
this.ws.close();
|
|
614
663
|
this.logging && this.logger.log("Websocket Connection with Shoonya API is now closed!");
|
|
615
664
|
this.emit("close");
|
package/dist/shoonya-sdk.mjs
CHANGED
|
@@ -132,13 +132,22 @@ var Shoonya = class extends EventEmitter {
|
|
|
132
132
|
apiKey;
|
|
133
133
|
cronJobRunning = false;
|
|
134
134
|
scripList;
|
|
135
|
+
reconnectTimeout;
|
|
136
|
+
heartbeatTimeout;
|
|
137
|
+
lastWsMsgAt;
|
|
135
138
|
constructor(options) {
|
|
136
|
-
const {
|
|
139
|
+
const {
|
|
140
|
+
logging = false,
|
|
141
|
+
reconnectTimeout = 3e4,
|
|
142
|
+
heartbeatTimeout = 3e3
|
|
143
|
+
} = options || {};
|
|
137
144
|
super({ captureRejections: true });
|
|
138
145
|
this.userId = "";
|
|
139
146
|
this.accessToken = "";
|
|
140
147
|
this.accountId = this.userId;
|
|
141
148
|
this.logging = logging;
|
|
149
|
+
this.reconnectTimeout = { timer: null, timeout: reconnectTimeout };
|
|
150
|
+
this.heartbeatTimeout = { timer: null, timeout: heartbeatTimeout };
|
|
142
151
|
if (logging) {
|
|
143
152
|
this.logger = new logger_default(
|
|
144
153
|
`./logs/log_${Date.now().toString()}.log`,
|
|
@@ -446,23 +455,34 @@ var Shoonya = class extends EventEmitter {
|
|
|
446
455
|
return `${day}${newMonth}${newYear}`;
|
|
447
456
|
}
|
|
448
457
|
getNextFormattedDates(expiry) {
|
|
449
|
-
let
|
|
458
|
+
let multiplier = 1;
|
|
450
459
|
if (expiry === "next" || expiry === "more-than-1-day")
|
|
451
|
-
|
|
460
|
+
multiplier = 2;
|
|
452
461
|
if (expiry === "next-next")
|
|
453
|
-
|
|
462
|
+
multiplier = 3;
|
|
454
463
|
const dates = [];
|
|
455
464
|
let currentDate = /* @__PURE__ */ new Date();
|
|
456
|
-
for (let i = 0; i <= 7 *
|
|
465
|
+
for (let i = 0; i <= 7 * multiplier; i++) {
|
|
457
466
|
const formattedDate = this.getFormattedExpiry(currentDate);
|
|
458
467
|
dates.push([formattedDate, currentDate]);
|
|
459
468
|
currentDate = new Date(currentDate.setDate(currentDate.getDate() + 1));
|
|
460
469
|
}
|
|
461
470
|
return dates;
|
|
462
471
|
}
|
|
472
|
+
getLastWSMessage() {
|
|
473
|
+
return this.lastWsMsgAt;
|
|
474
|
+
}
|
|
475
|
+
getWSState() {
|
|
476
|
+
return {
|
|
477
|
+
OPEN: this.ws && this.ws.readyState === this.ws.OPEN,
|
|
478
|
+
CLOSED: this.ws && this.ws.readyState === this.ws.CLOSED,
|
|
479
|
+
CLOSING: this.ws && this.ws.readyState === this.ws.CLOSING,
|
|
480
|
+
CONNECTING: this.ws && this.ws.readyState === this.ws.CONNECTING
|
|
481
|
+
};
|
|
482
|
+
}
|
|
463
483
|
// WebSocket API
|
|
464
484
|
connectWS() {
|
|
465
|
-
if (this.
|
|
485
|
+
if (this.getWSState().OPEN || this.getWSState().CONNECTING) {
|
|
466
486
|
return;
|
|
467
487
|
}
|
|
468
488
|
this.ws = new WS(this.wsBaseUrl);
|
|
@@ -475,15 +495,21 @@ var Shoonya = class extends EventEmitter {
|
|
|
475
495
|
susertoken: this.accessToken
|
|
476
496
|
};
|
|
477
497
|
this.ws.send(JSON.stringify(msg));
|
|
498
|
+
this.emit("open", "opened ws connection");
|
|
499
|
+
clearInterval(this.heartbeatTimeout.timer);
|
|
500
|
+
this.heartbeatTimeout.timer = setInterval(() => {
|
|
501
|
+
const heartbeatMsg = '{"t":"h"}';
|
|
502
|
+
this.getWSState().OPEN && this.ws.send(heartbeatMsg);
|
|
503
|
+
this.logging && this.logger.log(`sent heartbeat message: ${heartbeatMsg}`);
|
|
504
|
+
}, this.heartbeatTimeout.timeout);
|
|
478
505
|
if (this.cronJobRunning) {
|
|
479
506
|
this.subscribe(this.scripList);
|
|
480
507
|
return;
|
|
481
508
|
}
|
|
482
|
-
cron.schedule(
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
if (hour === 3 && min === 43) {
|
|
509
|
+
cron.schedule(
|
|
510
|
+
"13 9 * * 1-5",
|
|
511
|
+
async () => {
|
|
512
|
+
this.cronJobRunning = true;
|
|
487
513
|
try {
|
|
488
514
|
const cred = {
|
|
489
515
|
apiKey: this.apiKey,
|
|
@@ -492,21 +518,21 @@ var Shoonya = class extends EventEmitter {
|
|
|
492
518
|
userId: this.userId
|
|
493
519
|
};
|
|
494
520
|
await this.login(cred);
|
|
495
|
-
this.
|
|
521
|
+
this.logging && this.logger.log("Token Refreshed");
|
|
496
522
|
} catch (err) {
|
|
497
523
|
console.error(err);
|
|
498
524
|
const errMessage = `Token refreshing failed. ERR: ${err.message}`;
|
|
499
525
|
this.logging && this.logger.log(errMessage, "ERROR");
|
|
526
|
+
this.getWSState().OPEN && this.ws.close();
|
|
500
527
|
this.emit("stopped", errMessage);
|
|
501
528
|
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}
|
|
506
|
-
});
|
|
529
|
+
},
|
|
530
|
+
{ timezone: "Asia/Kolkata" }
|
|
531
|
+
);
|
|
507
532
|
});
|
|
508
533
|
this.ws.addEventListener("message", (e) => {
|
|
509
534
|
const result = JSON.parse(e.data.toString());
|
|
535
|
+
this.lastWsMsgAt = /* @__PURE__ */ new Date();
|
|
510
536
|
if (result.t === "dk") {
|
|
511
537
|
this.logging && this.logger.log(`Subscribed to ${result.ts}. Listening...`);
|
|
512
538
|
}
|
|
@@ -524,6 +550,19 @@ var Shoonya = class extends EventEmitter {
|
|
|
524
550
|
}, 3e3);
|
|
525
551
|
return;
|
|
526
552
|
}
|
|
553
|
+
clearTimeout(this.reconnectTimeout.timer);
|
|
554
|
+
this.reconnectTimeout.timer = setTimeout(() => {
|
|
555
|
+
let msg = {
|
|
556
|
+
t: "c",
|
|
557
|
+
uid: this.userId,
|
|
558
|
+
actid: this.accountId,
|
|
559
|
+
source: "API",
|
|
560
|
+
susertoken: this.accessToken
|
|
561
|
+
};
|
|
562
|
+
this.subscribe(this.scripList);
|
|
563
|
+
this.getWSState().OPEN && this.ws.send(JSON.stringify(msg));
|
|
564
|
+
this.emit("reconnect", "reconnected with shoonya ws");
|
|
565
|
+
}, this.reconnectTimeout.timeout);
|
|
527
566
|
clearTimeout(this.disconnectTimer);
|
|
528
567
|
this.disconnectTimer = setTimeout(() => {
|
|
529
568
|
const day = (/* @__PURE__ */ new Date()).getDay();
|
|
@@ -532,7 +571,16 @@ var Shoonya = class extends EventEmitter {
|
|
|
532
571
|
const err = "API have stopped to respond! there could be a problem with API or market has closed";
|
|
533
572
|
this.logging && this.logger.log(err, "WARN");
|
|
534
573
|
this.emit("stopped", err);
|
|
535
|
-
|
|
574
|
+
this.getWSState().OPEN && this.ws.close();
|
|
575
|
+
}, 6e4 * 60 * 18 - 1e4);
|
|
576
|
+
});
|
|
577
|
+
this.ws.addEventListener("error", (e) => {
|
|
578
|
+
this.logging && this.logger.log("some error in ws");
|
|
579
|
+
this.emit("error", e);
|
|
580
|
+
this.getWSState().OPEN && this.ws.close();
|
|
581
|
+
});
|
|
582
|
+
this.ws.addEventListener("close", (e) => {
|
|
583
|
+
this.emit("close", e);
|
|
536
584
|
});
|
|
537
585
|
}
|
|
538
586
|
/**
|
|
@@ -583,7 +631,8 @@ var Shoonya = class extends EventEmitter {
|
|
|
583
631
|
}
|
|
584
632
|
}
|
|
585
633
|
disconnect() {
|
|
586
|
-
if (this.
|
|
634
|
+
if (this.getWSState().OPEN || this.getWSState().CONNECTING) {
|
|
635
|
+
clearInterval(this.heartbeatTimeout.timer);
|
|
587
636
|
this.ws.close();
|
|
588
637
|
this.logging && this.logger.log("Websocket Connection with Shoonya API is now closed!");
|
|
589
638
|
this.emit("close");
|