nsekit 0.1.2 → 0.3.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/README.md +106 -37
- package/dist/brokers/dhan/DhanBroker.d.ts +10 -4
- package/dist/brokers/dhan/DhanBroker.d.ts.map +1 -1
- package/dist/brokers/dhan/dhan-instruments.d.ts +1 -11
- package/dist/brokers/dhan/dhan-instruments.d.ts.map +1 -1
- package/dist/brokers/finvasia/FinvasiaBroker.d.ts +10 -4
- package/dist/brokers/finvasia/FinvasiaBroker.d.ts.map +1 -1
- package/dist/brokers/finvasia/finvasia-instruments.d.ts +1 -11
- package/dist/brokers/finvasia/finvasia-instruments.d.ts.map +1 -1
- package/dist/brokers/paper/PaperBroker.d.ts +11 -3
- package/dist/brokers/paper/PaperBroker.d.ts.map +1 -1
- package/dist/brokers/zerodha/ZerodhaBroker.d.ts +10 -4
- package/dist/brokers/zerodha/ZerodhaBroker.d.ts.map +1 -1
- package/dist/brokers/zerodha/zerodha-instruments.d.ts +0 -12
- package/dist/brokers/zerodha/zerodha-instruments.d.ts.map +1 -1
- package/dist/index.d.ts +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +312 -138
- package/dist/instruments/instrument-master.d.ts +69 -16
- package/dist/instruments/instrument-master.d.ts.map +1 -1
- package/dist/interfaces/broker.interface.d.ts +8 -3
- package/dist/interfaces/broker.interface.d.ts.map +1 -1
- package/dist/types/broker.d.ts +9 -1
- package/dist/types/broker.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/instruments.d.ts +1 -6
- package/dist/types/instruments.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12626,10 +12626,10 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
12626
12626
|
}
|
|
12627
12627
|
}
|
|
12628
12628
|
var CanceledError$1 = CanceledError;
|
|
12629
|
-
function settle(
|
|
12629
|
+
function settle(resolve2, reject, response) {
|
|
12630
12630
|
const validateStatus = response.config.validateStatus;
|
|
12631
12631
|
if (!response.status || !validateStatus || validateStatus(response.status)) {
|
|
12632
|
-
|
|
12632
|
+
resolve2(response);
|
|
12633
12633
|
} else {
|
|
12634
12634
|
reject(new AxiosError$1("Request failed with status code " + response.status, [AxiosError$1.ERR_BAD_REQUEST, AxiosError$1.ERR_BAD_RESPONSE][Math.floor(response.status / 100) - 4], response.config, response.request, response));
|
|
12635
12635
|
}
|
|
@@ -13203,7 +13203,7 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
13203
13203
|
}
|
|
13204
13204
|
var isHttpAdapterSupported = typeof process !== "undefined" && utils$1.kindOf(process) === "process";
|
|
13205
13205
|
var wrapAsync = (asyncExecutor) => {
|
|
13206
|
-
return new Promise((
|
|
13206
|
+
return new Promise((resolve2, reject) => {
|
|
13207
13207
|
let onDone;
|
|
13208
13208
|
let isDone;
|
|
13209
13209
|
const done = (value, isRejected) => {
|
|
@@ -13214,7 +13214,7 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
13214
13214
|
};
|
|
13215
13215
|
const _resolve = (value) => {
|
|
13216
13216
|
done(value);
|
|
13217
|
-
|
|
13217
|
+
resolve2(value);
|
|
13218
13218
|
};
|
|
13219
13219
|
const _reject = (reason) => {
|
|
13220
13220
|
done(reason, true);
|
|
@@ -13266,7 +13266,7 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
13266
13266
|
}
|
|
13267
13267
|
};
|
|
13268
13268
|
var httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
|
|
13269
|
-
return wrapAsync(async function dispatchHttpRequest(
|
|
13269
|
+
return wrapAsync(async function dispatchHttpRequest(resolve2, reject, onDone) {
|
|
13270
13270
|
let { data, lookup, family, httpVersion = 1, http2Options } = config;
|
|
13271
13271
|
const { responseType, responseEncoding } = config;
|
|
13272
13272
|
const method = config.method.toUpperCase();
|
|
@@ -13347,7 +13347,7 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
13347
13347
|
}
|
|
13348
13348
|
let convertedData;
|
|
13349
13349
|
if (method !== "GET") {
|
|
13350
|
-
return settle(
|
|
13350
|
+
return settle(resolve2, reject, {
|
|
13351
13351
|
status: 405,
|
|
13352
13352
|
statusText: "method not allowed",
|
|
13353
13353
|
headers: {},
|
|
@@ -13369,7 +13369,7 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
13369
13369
|
} else if (responseType === "stream") {
|
|
13370
13370
|
convertedData = stream__default["default"].Readable.from(convertedData);
|
|
13371
13371
|
}
|
|
13372
|
-
return settle(
|
|
13372
|
+
return settle(resolve2, reject, {
|
|
13373
13373
|
data: convertedData,
|
|
13374
13374
|
status: 200,
|
|
13375
13375
|
statusText: "OK",
|
|
@@ -13556,7 +13556,7 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
13556
13556
|
};
|
|
13557
13557
|
if (responseType === "stream") {
|
|
13558
13558
|
response.data = responseStream;
|
|
13559
|
-
settle(
|
|
13559
|
+
settle(resolve2, reject, response);
|
|
13560
13560
|
} else {
|
|
13561
13561
|
const responseBuffer = [];
|
|
13562
13562
|
let totalResponseBytes = 0;
|
|
@@ -13595,7 +13595,7 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
13595
13595
|
} catch (err) {
|
|
13596
13596
|
return reject(AxiosError$1.from(err, null, config, response.request, response));
|
|
13597
13597
|
}
|
|
13598
|
-
settle(
|
|
13598
|
+
settle(resolve2, reject, response);
|
|
13599
13599
|
});
|
|
13600
13600
|
}
|
|
13601
13601
|
abortEmitter.once("abort", (err) => {
|
|
@@ -13815,7 +13815,7 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
13815
13815
|
};
|
|
13816
13816
|
var isXHRAdapterSupported = typeof XMLHttpRequest !== "undefined";
|
|
13817
13817
|
var xhrAdapter = isXHRAdapterSupported && function(config) {
|
|
13818
|
-
return new Promise(function dispatchXhrRequest(
|
|
13818
|
+
return new Promise(function dispatchXhrRequest(resolve2, reject) {
|
|
13819
13819
|
const _config = resolveConfig(config);
|
|
13820
13820
|
let requestData = _config.data;
|
|
13821
13821
|
const requestHeaders = AxiosHeaders$1.from(_config.headers).normalize();
|
|
@@ -13847,7 +13847,7 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
13847
13847
|
request
|
|
13848
13848
|
};
|
|
13849
13849
|
settle(function _resolve(value) {
|
|
13850
|
-
|
|
13850
|
+
resolve2(value);
|
|
13851
13851
|
done();
|
|
13852
13852
|
}, function _reject(err) {
|
|
13853
13853
|
reject(err);
|
|
@@ -14198,8 +14198,8 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
14198
14198
|
responseType = responseType || "text";
|
|
14199
14199
|
let responseData = await resolvers[utils$1.findKey(resolvers, responseType) || "text"](response, config);
|
|
14200
14200
|
!isStreamResponse && unsubscribe && unsubscribe();
|
|
14201
|
-
return await new Promise((
|
|
14202
|
-
settle(
|
|
14201
|
+
return await new Promise((resolve2, reject) => {
|
|
14202
|
+
settle(resolve2, reject, {
|
|
14203
14203
|
data: responseData,
|
|
14204
14204
|
headers: AxiosHeaders$1.from(response.headers),
|
|
14205
14205
|
status: response.status,
|
|
@@ -14547,8 +14547,8 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
14547
14547
|
throw new TypeError("executor must be a function.");
|
|
14548
14548
|
}
|
|
14549
14549
|
let resolvePromise;
|
|
14550
|
-
this.promise = new Promise(function promiseExecutor(
|
|
14551
|
-
resolvePromise =
|
|
14550
|
+
this.promise = new Promise(function promiseExecutor(resolve2) {
|
|
14551
|
+
resolvePromise = resolve2;
|
|
14552
14552
|
});
|
|
14553
14553
|
const token = this;
|
|
14554
14554
|
this.promise.then((cancel) => {
|
|
@@ -14562,9 +14562,9 @@ var require_axios = __commonJS((exports, module) => {
|
|
|
14562
14562
|
});
|
|
14563
14563
|
this.promise.then = (onfulfilled) => {
|
|
14564
14564
|
let _resolve;
|
|
14565
|
-
const promise = new Promise((
|
|
14566
|
-
token.subscribe(
|
|
14567
|
-
_resolve =
|
|
14565
|
+
const promise = new Promise((resolve2) => {
|
|
14566
|
+
token.subscribe(resolve2);
|
|
14567
|
+
_resolve = resolve2;
|
|
14568
14568
|
}).then(onfulfilled);
|
|
14569
14569
|
promise.cancel = function reject() {
|
|
14570
14570
|
token.unsubscribe(_resolve);
|
|
@@ -18454,7 +18454,7 @@ var require_connect2 = __commonJS((exports) => {
|
|
|
18454
18454
|
return `${this.default_login_uri}?api_key=${this.api_key}&v=3`;
|
|
18455
18455
|
}
|
|
18456
18456
|
generateSession(request_token, api_secret) {
|
|
18457
|
-
return new Promise((
|
|
18457
|
+
return new Promise((resolve2, reject) => {
|
|
18458
18458
|
const checksum = (0, sha256_1.default)(this.api_key + request_token + api_secret).toString();
|
|
18459
18459
|
const p = this._post("api.token", {
|
|
18460
18460
|
api_key: this.api_key,
|
|
@@ -18465,7 +18465,7 @@ var require_connect2 = __commonJS((exports) => {
|
|
|
18465
18465
|
if (resp && resp.access_token) {
|
|
18466
18466
|
this.setAccessToken(resp.access_token);
|
|
18467
18467
|
}
|
|
18468
|
-
return
|
|
18468
|
+
return resolve2(resp);
|
|
18469
18469
|
}).catch(function(err) {
|
|
18470
18470
|
return reject(err);
|
|
18471
18471
|
});
|
|
@@ -18478,7 +18478,7 @@ var require_connect2 = __commonJS((exports) => {
|
|
|
18478
18478
|
});
|
|
18479
18479
|
}
|
|
18480
18480
|
renewAccessToken(refresh_token, api_secret) {
|
|
18481
|
-
return new Promise((
|
|
18481
|
+
return new Promise((resolve2, reject) => {
|
|
18482
18482
|
const checksum = (0, sha256_1.default)(this.api_key + refresh_token + api_secret).toString();
|
|
18483
18483
|
const p = this._post("api.token.renew", {
|
|
18484
18484
|
api_key: this.api_key,
|
|
@@ -18489,7 +18489,7 @@ var require_connect2 = __commonJS((exports) => {
|
|
|
18489
18489
|
if (resp && resp.access_token) {
|
|
18490
18490
|
this.setAccessToken(resp.access_token);
|
|
18491
18491
|
}
|
|
18492
|
-
return
|
|
18492
|
+
return resolve2(resp);
|
|
18493
18493
|
}).catch(function(err) {
|
|
18494
18494
|
return reject(err);
|
|
18495
18495
|
});
|
|
@@ -19297,44 +19297,63 @@ class InstrumentNotFoundError extends BrokerError {
|
|
|
19297
19297
|
}
|
|
19298
19298
|
}
|
|
19299
19299
|
// src/instruments/instrument-master.ts
|
|
19300
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
19301
|
+
import { join, resolve } from "path";
|
|
19302
|
+
var DEFAULT_INSTRUMENT_PATH = "./InstrumentDump";
|
|
19303
|
+
|
|
19300
19304
|
class InstrumentMaster {
|
|
19301
19305
|
instruments = new Map;
|
|
19302
19306
|
tokenIndex = new Map;
|
|
19303
19307
|
syncMeta = new Map;
|
|
19304
19308
|
redis;
|
|
19309
|
+
instrumentFilePath;
|
|
19305
19310
|
constructor(options) {
|
|
19306
19311
|
this.redis = options?.redis ?? null;
|
|
19312
|
+
this.instrumentFilePath = resolve(options?.instrumentFilePath ?? DEFAULT_INSTRUMENT_PATH);
|
|
19307
19313
|
}
|
|
19308
19314
|
async syncBroker(brokerId, brokerInstruments, force = false) {
|
|
19309
19315
|
try {
|
|
19310
|
-
|
|
19311
|
-
|
|
19312
|
-
|
|
19313
|
-
|
|
19314
|
-
|
|
19315
|
-
|
|
19316
|
-
|
|
19317
|
-
|
|
19316
|
+
if (!force) {
|
|
19317
|
+
const diskEntries = this.loadFromDisk(brokerId);
|
|
19318
|
+
if (diskEntries) {
|
|
19319
|
+
this.clearBrokerEntries(brokerId);
|
|
19320
|
+
for (const entry of diskEntries) {
|
|
19321
|
+
this.mergeEntry(brokerId, entry);
|
|
19322
|
+
}
|
|
19323
|
+
const meta2 = {
|
|
19324
|
+
brokerId,
|
|
19325
|
+
lastSyncAt: Date.now(),
|
|
19326
|
+
instrumentCount: diskEntries.length,
|
|
19327
|
+
source: "disk"
|
|
19328
|
+
};
|
|
19329
|
+
this.syncMeta.set(brokerId, meta2);
|
|
19330
|
+
if (this.redis) {
|
|
19331
|
+
this.cacheInstrumentsToRedis();
|
|
19332
|
+
}
|
|
19333
|
+
return Ok(meta2);
|
|
19318
19334
|
}
|
|
19319
19335
|
}
|
|
19320
19336
|
this.clearBrokerEntries(brokerId);
|
|
19321
|
-
|
|
19337
|
+
const entries = [];
|
|
19322
19338
|
if (brokerInstruments.capabilities.bulkDump && brokerInstruments.streamDump) {
|
|
19323
19339
|
for await (const raw of brokerInstruments.streamDump()) {
|
|
19324
19340
|
const entry = brokerInstruments.normalize(raw);
|
|
19341
|
+
entries.push(entry);
|
|
19325
19342
|
this.mergeEntry(brokerId, entry);
|
|
19326
|
-
count++;
|
|
19327
19343
|
}
|
|
19328
19344
|
}
|
|
19345
|
+
if (this.redis) {
|
|
19346
|
+
this.cacheInstrumentsToRedis();
|
|
19347
|
+
} else {
|
|
19348
|
+
this.saveToDisk(brokerId, entries);
|
|
19349
|
+
}
|
|
19329
19350
|
const meta = {
|
|
19330
19351
|
brokerId,
|
|
19331
19352
|
lastSyncAt: Date.now(),
|
|
19332
|
-
instrumentCount:
|
|
19353
|
+
instrumentCount: entries.length,
|
|
19354
|
+
source: "network"
|
|
19333
19355
|
};
|
|
19334
19356
|
this.syncMeta.set(brokerId, meta);
|
|
19335
|
-
if (this.redis) {
|
|
19336
|
-
this.cacheInstrumentsToRedis();
|
|
19337
|
-
}
|
|
19338
19357
|
return Ok(meta);
|
|
19339
19358
|
} catch (err) {
|
|
19340
19359
|
return Err(err instanceof Error ? err : new Error(String(err)));
|
|
@@ -19348,9 +19367,8 @@ class InstrumentMaster {
|
|
|
19348
19367
|
this.syncMeta.set(brokerId, {
|
|
19349
19368
|
brokerId,
|
|
19350
19369
|
lastSyncAt: Date.now(),
|
|
19351
|
-
|
|
19352
|
-
|
|
19353
|
-
instrumentCount: (existing?.instrumentCount ?? 0) + entries.length
|
|
19370
|
+
instrumentCount: (existing?.instrumentCount ?? 0) + entries.length,
|
|
19371
|
+
source: "network"
|
|
19354
19372
|
});
|
|
19355
19373
|
}
|
|
19356
19374
|
search(query, exchange, instrumentType, limit = 50) {
|
|
@@ -19396,6 +19414,52 @@ class InstrumentMaster {
|
|
|
19396
19414
|
return;
|
|
19397
19415
|
return this.instruments.get(entry.key);
|
|
19398
19416
|
}
|
|
19417
|
+
resolve(input) {
|
|
19418
|
+
const { exchange, symbol } = this.parseInput(input);
|
|
19419
|
+
if (exchange) {
|
|
19420
|
+
const key = this.makeKey(exchange, symbol);
|
|
19421
|
+
const inst = this.instruments.get(key);
|
|
19422
|
+
if (inst)
|
|
19423
|
+
return Ok(inst);
|
|
19424
|
+
return Err(new Error(`Symbol "${symbol}" not found on ${exchange}`));
|
|
19425
|
+
}
|
|
19426
|
+
const matches = [];
|
|
19427
|
+
for (const inst of this.instruments.values()) {
|
|
19428
|
+
if (inst.tradingSymbol === symbol)
|
|
19429
|
+
matches.push(inst);
|
|
19430
|
+
}
|
|
19431
|
+
if (matches.length === 0) {
|
|
19432
|
+
return Err(new Error(`Symbol "${symbol}" not found`));
|
|
19433
|
+
}
|
|
19434
|
+
if (matches.length === 1) {
|
|
19435
|
+
return Ok(matches[0]);
|
|
19436
|
+
}
|
|
19437
|
+
if (matches.length === 2) {
|
|
19438
|
+
const exchanges = new Set(matches.map((m) => m.exchange));
|
|
19439
|
+
if (exchanges.has("NSE") && exchanges.has("BSE")) {
|
|
19440
|
+
return Ok(matches.find((m) => m.exchange === "NSE"));
|
|
19441
|
+
}
|
|
19442
|
+
}
|
|
19443
|
+
const list = matches.slice(0, 5).map((m) => `${m.exchange}:${m.tradingSymbol}`).join(", ");
|
|
19444
|
+
return Err(new Error(`Multiple matches found. Specify exchange: ${list}`));
|
|
19445
|
+
}
|
|
19446
|
+
async resolveAsync(input, brokerId, brokerInstruments) {
|
|
19447
|
+
const local = this.resolve(input);
|
|
19448
|
+
if (local.ok)
|
|
19449
|
+
return local;
|
|
19450
|
+
if (brokerInstruments.capabilities.searchAPI && brokerInstruments.searchAPI) {
|
|
19451
|
+
const { symbol } = this.parseInput(input);
|
|
19452
|
+
const apiResult = await brokerInstruments.searchAPI(symbol);
|
|
19453
|
+
if (apiResult.ok && apiResult.value.length > 0) {
|
|
19454
|
+
for (const entry of apiResult.value)
|
|
19455
|
+
this.mergeEntry(brokerId, entry);
|
|
19456
|
+
if (this.redis)
|
|
19457
|
+
this.cacheInstrumentsToRedis();
|
|
19458
|
+
return this.resolve(input);
|
|
19459
|
+
}
|
|
19460
|
+
}
|
|
19461
|
+
return Err(new Error(`Symbol "${input}" not found`));
|
|
19462
|
+
}
|
|
19399
19463
|
async getOptionChainCached(underlying, expiry) {
|
|
19400
19464
|
const expiryDate = expiry.toISOString().slice(0, 10);
|
|
19401
19465
|
const cacheKey = `optchain:${underlying.toUpperCase()}:${expiryDate}`;
|
|
@@ -19442,6 +19506,9 @@ class InstrumentMaster {
|
|
|
19442
19506
|
get size() {
|
|
19443
19507
|
return this.instruments.size;
|
|
19444
19508
|
}
|
|
19509
|
+
get filePath() {
|
|
19510
|
+
return this.instrumentFilePath;
|
|
19511
|
+
}
|
|
19445
19512
|
getSyncMeta(brokerId) {
|
|
19446
19513
|
return this.syncMeta.get(brokerId);
|
|
19447
19514
|
}
|
|
@@ -19465,8 +19532,39 @@ class InstrumentMaster {
|
|
|
19465
19532
|
this.tokenIndex.clear();
|
|
19466
19533
|
this.syncMeta.clear();
|
|
19467
19534
|
}
|
|
19535
|
+
getDiskCachePath(brokerId) {
|
|
19536
|
+
return join(this.instrumentFilePath, `${brokerId}-instruments.json`);
|
|
19537
|
+
}
|
|
19538
|
+
ensureDirectory() {
|
|
19539
|
+
if (!existsSync(this.instrumentFilePath)) {
|
|
19540
|
+
mkdirSync(this.instrumentFilePath, { recursive: true });
|
|
19541
|
+
}
|
|
19542
|
+
}
|
|
19543
|
+
loadFromDisk(brokerId) {
|
|
19544
|
+
const filePath = this.getDiskCachePath(brokerId);
|
|
19545
|
+
try {
|
|
19546
|
+
if (!existsSync(filePath))
|
|
19547
|
+
return null;
|
|
19548
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
19549
|
+
const parsed = JSON.parse(raw);
|
|
19550
|
+
if (!Array.isArray(parsed))
|
|
19551
|
+
return null;
|
|
19552
|
+
return parsed;
|
|
19553
|
+
} catch {
|
|
19554
|
+
return null;
|
|
19555
|
+
}
|
|
19556
|
+
}
|
|
19557
|
+
saveToDisk(brokerId, entries) {
|
|
19558
|
+
try {
|
|
19559
|
+
this.ensureDirectory();
|
|
19560
|
+
const filePath = this.getDiskCachePath(brokerId);
|
|
19561
|
+
const stripped = entries.map(({ raw, ...rest }) => rest);
|
|
19562
|
+
writeFileSync(filePath, JSON.stringify(stripped), "utf-8");
|
|
19563
|
+
} catch {}
|
|
19564
|
+
}
|
|
19468
19565
|
mergeEntry(brokerId, entry) {
|
|
19469
|
-
const
|
|
19566
|
+
const canonical = this.buildCanonicalSymbol(entry.underlying, entry.instrumentType, entry.strike, entry.expiry);
|
|
19567
|
+
const key = this.makeKey(entry.exchange, canonical);
|
|
19470
19568
|
let instrument = this.instruments.get(key);
|
|
19471
19569
|
if (!instrument) {
|
|
19472
19570
|
instrument = {
|
|
@@ -19477,7 +19575,7 @@ class InstrumentMaster {
|
|
|
19477
19575
|
strike: entry.strike,
|
|
19478
19576
|
lotSize: entry.lotSize,
|
|
19479
19577
|
tickSize: entry.tickSize,
|
|
19480
|
-
tradingSymbol:
|
|
19578
|
+
tradingSymbol: canonical,
|
|
19481
19579
|
brokerTokens: {}
|
|
19482
19580
|
};
|
|
19483
19581
|
this.instruments.set(key, instrument);
|
|
@@ -19571,6 +19669,35 @@ class InstrumentMaster {
|
|
|
19571
19669
|
}
|
|
19572
19670
|
return true;
|
|
19573
19671
|
}
|
|
19672
|
+
formatExpiry(date) {
|
|
19673
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
19674
|
+
const dd = String(d.getDate()).padStart(2, "0");
|
|
19675
|
+
const mmm = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"][d.getMonth()];
|
|
19676
|
+
const yy = String(d.getFullYear()).slice(-2);
|
|
19677
|
+
return `${dd}${mmm}${yy}`;
|
|
19678
|
+
}
|
|
19679
|
+
buildCanonicalSymbol(underlying, instrumentType, strike, expiry) {
|
|
19680
|
+
const name = underlying.toUpperCase();
|
|
19681
|
+
if (instrumentType === "EQ")
|
|
19682
|
+
return name;
|
|
19683
|
+
const exp = expiry ? this.formatExpiry(expiry) : "";
|
|
19684
|
+
if (instrumentType === "FUT")
|
|
19685
|
+
return `${name}-FUT-${exp}`;
|
|
19686
|
+
const s = strike != null ? String(strike) : "0";
|
|
19687
|
+
return `${name}-${s}-${instrumentType}-${exp}`;
|
|
19688
|
+
}
|
|
19689
|
+
parseInput(input) {
|
|
19690
|
+
const upper = input.trim().toUpperCase();
|
|
19691
|
+
const VALID = new Set(["NSE", "BSE", "NFO", "BFO", "MCX", "CDS"]);
|
|
19692
|
+
const idx = upper.indexOf(":");
|
|
19693
|
+
if (idx >= 2 && idx <= 3) {
|
|
19694
|
+
const prefix = upper.slice(0, idx);
|
|
19695
|
+
if (VALID.has(prefix)) {
|
|
19696
|
+
return { exchange: prefix, symbol: upper.slice(idx + 1) };
|
|
19697
|
+
}
|
|
19698
|
+
}
|
|
19699
|
+
return { symbol: upper };
|
|
19700
|
+
}
|
|
19574
19701
|
makeKey(exchange, tradingSymbol) {
|
|
19575
19702
|
return `${exchange}:${tradingSymbol}`;
|
|
19576
19703
|
}
|
|
@@ -20455,33 +20582,6 @@ class ZerodhaInstruments {
|
|
|
20455
20582
|
yield row;
|
|
20456
20583
|
}
|
|
20457
20584
|
}
|
|
20458
|
-
async checkDumpChanged(lastSync) {
|
|
20459
|
-
const response = await fetch(KITE_INSTRUMENTS_URL, { method: "HEAD" });
|
|
20460
|
-
if (!response.ok)
|
|
20461
|
-
return true;
|
|
20462
|
-
const etag = response.headers.get("etag");
|
|
20463
|
-
const lastModified = response.headers.get("last-modified");
|
|
20464
|
-
if (etag && lastSync.etag)
|
|
20465
|
-
return etag !== lastSync.etag;
|
|
20466
|
-
if (lastModified && lastSync.lastModified)
|
|
20467
|
-
return lastModified !== lastSync.lastModified;
|
|
20468
|
-
return true;
|
|
20469
|
-
}
|
|
20470
|
-
async resolve(symbol, exchange) {
|
|
20471
|
-
try {
|
|
20472
|
-
for await (const raw of this.streamDump()) {
|
|
20473
|
-
const fields = raw;
|
|
20474
|
-
const rowSymbol = fields["tradingsymbol"] ?? "";
|
|
20475
|
-
const rowExchange = fields["exchange"] ?? "";
|
|
20476
|
-
if (rowSymbol === symbol && rowExchange === exchange) {
|
|
20477
|
-
return Ok(this.normalize(raw));
|
|
20478
|
-
}
|
|
20479
|
-
}
|
|
20480
|
-
return Err(new Error(`Instrument ${symbol} not found on ${exchange} (Zerodha)`));
|
|
20481
|
-
} catch (err) {
|
|
20482
|
-
return Err(err instanceof Error ? err : new Error(String(err)));
|
|
20483
|
-
}
|
|
20484
|
-
}
|
|
20485
20585
|
normalize(raw) {
|
|
20486
20586
|
const fields = raw;
|
|
20487
20587
|
const tradingSymbol = fields["tradingsymbol"] ?? fields[COL.TRADINGSYMBOL] ?? "";
|
|
@@ -20515,13 +20615,23 @@ class ZerodhaBroker {
|
|
|
20515
20615
|
id = "zerodha";
|
|
20516
20616
|
name = "Zerodha";
|
|
20517
20617
|
instruments;
|
|
20618
|
+
has = {
|
|
20619
|
+
bulkDump: true,
|
|
20620
|
+
searchAPI: false,
|
|
20621
|
+
optionChain: false,
|
|
20622
|
+
historicalCandles: true,
|
|
20623
|
+
websocket: true,
|
|
20624
|
+
pnlReport: false
|
|
20625
|
+
};
|
|
20518
20626
|
session = null;
|
|
20519
20627
|
kite = null;
|
|
20520
20628
|
socket = null;
|
|
20521
20629
|
apiKey = "";
|
|
20522
20630
|
KiteConnect = null;
|
|
20523
|
-
|
|
20631
|
+
_iMaster;
|
|
20632
|
+
constructor(options) {
|
|
20524
20633
|
this.instruments = new ZerodhaInstruments;
|
|
20634
|
+
this._iMaster = new InstrumentMaster(options);
|
|
20525
20635
|
}
|
|
20526
20636
|
async authenticate(creds) {
|
|
20527
20637
|
this.apiKey = creds.apiKey;
|
|
@@ -20544,10 +20654,34 @@ class ZerodhaBroker {
|
|
|
20544
20654
|
return null;
|
|
20545
20655
|
return new Date(this.session.expiresAt);
|
|
20546
20656
|
}
|
|
20657
|
+
async sync(force) {
|
|
20658
|
+
return this._iMaster.syncBroker(this.id, this.instruments, force);
|
|
20659
|
+
}
|
|
20660
|
+
resolve(input) {
|
|
20661
|
+
return this._iMaster.resolve(input);
|
|
20662
|
+
}
|
|
20663
|
+
search(query, exchange, instrumentType, limit) {
|
|
20664
|
+
return this._iMaster.search(query, exchange, instrumentType, limit);
|
|
20665
|
+
}
|
|
20547
20666
|
async placeOrder(params) {
|
|
20548
20667
|
try {
|
|
20668
|
+
let resolvedParams = params;
|
|
20669
|
+
if (!params.exchange) {
|
|
20670
|
+
const resolved = this._iMaster.resolve(params.tradingSymbol);
|
|
20671
|
+
if (!resolved.ok) {
|
|
20672
|
+
const asyncResolved = await this._iMaster.resolveAsync(params.tradingSymbol, this.id, this.instruments);
|
|
20673
|
+
if (!asyncResolved.ok) {
|
|
20674
|
+
return Err(new Error(`Cannot resolve "${params.tradingSymbol}". Either run broker.sync() first to use unified nsekit symbols, or provide exchange and the broker-native tradingSymbol directly.`));
|
|
20675
|
+
}
|
|
20676
|
+
const nativeSymbol = asyncResolved.value.brokerTokens.zerodha?.tradingsymbol ?? params.tradingSymbol;
|
|
20677
|
+
resolvedParams = { ...params, exchange: asyncResolved.value.exchange, tradingSymbol: nativeSymbol };
|
|
20678
|
+
} else {
|
|
20679
|
+
const nativeSymbol = resolved.value.brokerTokens.zerodha?.tradingsymbol ?? params.tradingSymbol;
|
|
20680
|
+
resolvedParams = { ...params, exchange: resolved.value.exchange, tradingSymbol: nativeSymbol };
|
|
20681
|
+
}
|
|
20682
|
+
}
|
|
20549
20683
|
const kite = await this.ensureKite();
|
|
20550
|
-
const kiteParams = toOrderParams(
|
|
20684
|
+
const kiteParams = toOrderParams(resolvedParams);
|
|
20551
20685
|
const res = await kite.placeOrder("regular", kiteParams);
|
|
20552
20686
|
return Ok({
|
|
20553
20687
|
orderId: res.order_id,
|
|
@@ -21466,25 +21600,7 @@ class FinvasiaInstruments {
|
|
|
21466
21600
|
}
|
|
21467
21601
|
}
|
|
21468
21602
|
}
|
|
21469
|
-
async
|
|
21470
|
-
const firstFile = Object.values(SHOONYA_INSTRUMENT_FILES)[0];
|
|
21471
|
-
const url = `${SHOONYA_INSTRUMENTS_BASE}${firstFile}`;
|
|
21472
|
-
try {
|
|
21473
|
-
const response = await fetch(url, { method: "HEAD" });
|
|
21474
|
-
if (!response.ok)
|
|
21475
|
-
return true;
|
|
21476
|
-
const etag = response.headers.get("etag");
|
|
21477
|
-
const lastModified = response.headers.get("last-modified");
|
|
21478
|
-
if (etag && lastSync.etag)
|
|
21479
|
-
return etag !== lastSync.etag;
|
|
21480
|
-
if (lastModified && lastSync.lastModified)
|
|
21481
|
-
return lastModified !== lastSync.lastModified;
|
|
21482
|
-
return true;
|
|
21483
|
-
} catch {
|
|
21484
|
-
return true;
|
|
21485
|
-
}
|
|
21486
|
-
}
|
|
21487
|
-
async search(query, exchange) {
|
|
21603
|
+
async searchAPI(query, exchange) {
|
|
21488
21604
|
if (!this.accessToken) {
|
|
21489
21605
|
return Err(new Error("Finvasia search requires authentication. Call setCredentials() first."));
|
|
21490
21606
|
}
|
|
@@ -21524,16 +21640,6 @@ class FinvasiaInstruments {
|
|
|
21524
21640
|
return Err(err instanceof Error ? err : new Error(String(err)));
|
|
21525
21641
|
}
|
|
21526
21642
|
}
|
|
21527
|
-
async resolve(symbol, exchange) {
|
|
21528
|
-
const result = await this.search(symbol, exchange);
|
|
21529
|
-
if (!result.ok)
|
|
21530
|
-
return result;
|
|
21531
|
-
const match = result.value.find((e) => e.tradingSymbol === symbol && e.exchange === exchange);
|
|
21532
|
-
if (!match) {
|
|
21533
|
-
return Err(new Error(`Instrument ${symbol} not found on ${exchange} (Finvasia)`));
|
|
21534
|
-
}
|
|
21535
|
-
return Ok(match);
|
|
21536
|
-
}
|
|
21537
21643
|
normalize(raw) {
|
|
21538
21644
|
const fields = raw;
|
|
21539
21645
|
const exchangeRaw = fields["exchange"] ?? fields["exch"] ?? fields["_exchange"] ?? "NSE";
|
|
@@ -21569,12 +21675,22 @@ class FinvasiaBroker {
|
|
|
21569
21675
|
id = "finvasia";
|
|
21570
21676
|
name = "Finvasia (Shoonya)";
|
|
21571
21677
|
instruments;
|
|
21678
|
+
has = {
|
|
21679
|
+
bulkDump: true,
|
|
21680
|
+
searchAPI: true,
|
|
21681
|
+
optionChain: true,
|
|
21682
|
+
historicalCandles: true,
|
|
21683
|
+
websocket: true,
|
|
21684
|
+
pnlReport: false
|
|
21685
|
+
};
|
|
21572
21686
|
session = null;
|
|
21573
21687
|
socket = null;
|
|
21574
21688
|
instrumentsImpl;
|
|
21575
|
-
|
|
21689
|
+
_iMaster;
|
|
21690
|
+
constructor(options) {
|
|
21576
21691
|
this.instrumentsImpl = new FinvasiaInstruments;
|
|
21577
21692
|
this.instruments = this.instrumentsImpl;
|
|
21693
|
+
this._iMaster = new InstrumentMaster(options);
|
|
21578
21694
|
}
|
|
21579
21695
|
async authenticate(creds) {
|
|
21580
21696
|
const result = await authenticate2(creds);
|
|
@@ -21595,10 +21711,34 @@ class FinvasiaBroker {
|
|
|
21595
21711
|
return null;
|
|
21596
21712
|
return new Date(this.session.expiresAt);
|
|
21597
21713
|
}
|
|
21714
|
+
async sync(force) {
|
|
21715
|
+
return this._iMaster.syncBroker(this.id, this.instruments, force);
|
|
21716
|
+
}
|
|
21717
|
+
resolve(input) {
|
|
21718
|
+
return this._iMaster.resolve(input);
|
|
21719
|
+
}
|
|
21720
|
+
search(query, exchange, instrumentType, limit) {
|
|
21721
|
+
return this._iMaster.search(query, exchange, instrumentType, limit);
|
|
21722
|
+
}
|
|
21598
21723
|
async placeOrder(params) {
|
|
21599
21724
|
try {
|
|
21725
|
+
let resolvedParams = params;
|
|
21726
|
+
if (!params.exchange) {
|
|
21727
|
+
const resolved = this._iMaster.resolve(params.tradingSymbol);
|
|
21728
|
+
if (!resolved.ok) {
|
|
21729
|
+
const asyncResolved = await this._iMaster.resolveAsync(params.tradingSymbol, this.id, this.instruments);
|
|
21730
|
+
if (!asyncResolved.ok) {
|
|
21731
|
+
return Err(new Error(`Cannot resolve "${params.tradingSymbol}". Either run broker.sync() first to use unified nsekit symbols, or provide exchange and the broker-native tradingSymbol directly.`));
|
|
21732
|
+
}
|
|
21733
|
+
const nativeSymbol = asyncResolved.value.brokerTokens.finvasia?.tsym ?? params.tradingSymbol;
|
|
21734
|
+
resolvedParams = { ...params, exchange: asyncResolved.value.exchange, tradingSymbol: nativeSymbol };
|
|
21735
|
+
} else {
|
|
21736
|
+
const nativeSymbol = resolved.value.brokerTokens.finvasia?.tsym ?? params.tradingSymbol;
|
|
21737
|
+
resolvedParams = { ...params, exchange: resolved.value.exchange, tradingSymbol: nativeSymbol };
|
|
21738
|
+
}
|
|
21739
|
+
}
|
|
21600
21740
|
const uid = this.session?.userId ?? "";
|
|
21601
|
-
const shoonyaParams = toOrderParams2(
|
|
21741
|
+
const shoonyaParams = toOrderParams2(resolvedParams, uid, uid);
|
|
21602
21742
|
const res = await this.post("PlaceOrder", shoonyaParams);
|
|
21603
21743
|
if (res.stat !== "Ok" || !res.norenordno) {
|
|
21604
21744
|
return Err(new Error(`Order placement failed: ${res.emsg ?? "Unknown error"}`));
|
|
@@ -22585,23 +22725,7 @@ class DhanInstruments {
|
|
|
22585
22725
|
yield row;
|
|
22586
22726
|
}
|
|
22587
22727
|
}
|
|
22588
|
-
async
|
|
22589
|
-
try {
|
|
22590
|
-
const response = await fetch(DHAN_INSTRUMENTS_URL, { method: "HEAD" });
|
|
22591
|
-
if (!response.ok)
|
|
22592
|
-
return true;
|
|
22593
|
-
const etag = response.headers.get("etag");
|
|
22594
|
-
const lastModified = response.headers.get("last-modified");
|
|
22595
|
-
if (etag && lastSync.etag)
|
|
22596
|
-
return etag !== lastSync.etag;
|
|
22597
|
-
if (lastModified && lastSync.lastModified)
|
|
22598
|
-
return lastModified !== lastSync.lastModified;
|
|
22599
|
-
return true;
|
|
22600
|
-
} catch {
|
|
22601
|
-
return true;
|
|
22602
|
-
}
|
|
22603
|
-
}
|
|
22604
|
-
async search(query, exchange) {
|
|
22728
|
+
async searchAPI(query, exchange) {
|
|
22605
22729
|
if (!this.accessToken) {
|
|
22606
22730
|
return Err(new Error("Dhan search requires authentication. Call setCredentials() first."));
|
|
22607
22731
|
}
|
|
@@ -22644,16 +22768,6 @@ class DhanInstruments {
|
|
|
22644
22768
|
return Err(err instanceof Error ? err : new Error(String(err)));
|
|
22645
22769
|
}
|
|
22646
22770
|
}
|
|
22647
|
-
async resolve(symbol, exchange) {
|
|
22648
|
-
const result = await this.search(symbol, exchange);
|
|
22649
|
-
if (!result.ok)
|
|
22650
|
-
return result;
|
|
22651
|
-
const match = result.value.find((e) => e.tradingSymbol === symbol && e.exchange === exchange);
|
|
22652
|
-
if (!match) {
|
|
22653
|
-
return Err(new Error(`Instrument ${symbol} not found on ${exchange} (Dhan)`));
|
|
22654
|
-
}
|
|
22655
|
-
return Ok(match);
|
|
22656
|
-
}
|
|
22657
22771
|
normalize(raw) {
|
|
22658
22772
|
const fields = raw;
|
|
22659
22773
|
const securityId = fields["SEM_SMST_SECURITY_ID"] ?? fields["securityId"] ?? "";
|
|
@@ -22712,13 +22826,23 @@ class DhanBroker {
|
|
|
22712
22826
|
id = "dhan";
|
|
22713
22827
|
name = "Dhan";
|
|
22714
22828
|
instruments;
|
|
22829
|
+
has = {
|
|
22830
|
+
bulkDump: true,
|
|
22831
|
+
searchAPI: true,
|
|
22832
|
+
optionChain: true,
|
|
22833
|
+
historicalCandles: true,
|
|
22834
|
+
websocket: true,
|
|
22835
|
+
pnlReport: false
|
|
22836
|
+
};
|
|
22715
22837
|
session = null;
|
|
22716
22838
|
socket = null;
|
|
22717
22839
|
clientId = "";
|
|
22718
22840
|
instrumentsImpl;
|
|
22719
|
-
|
|
22841
|
+
_iMaster;
|
|
22842
|
+
constructor(options) {
|
|
22720
22843
|
this.instrumentsImpl = new DhanInstruments;
|
|
22721
22844
|
this.instruments = this.instrumentsImpl;
|
|
22845
|
+
this._iMaster = new InstrumentMaster(options);
|
|
22722
22846
|
}
|
|
22723
22847
|
async authenticate(creds) {
|
|
22724
22848
|
this.clientId = creds.userId ?? "";
|
|
@@ -22740,9 +22864,31 @@ class DhanBroker {
|
|
|
22740
22864
|
return null;
|
|
22741
22865
|
return new Date(this.session.expiresAt);
|
|
22742
22866
|
}
|
|
22867
|
+
async sync(force) {
|
|
22868
|
+
return this._iMaster.syncBroker(this.id, this.instruments, force);
|
|
22869
|
+
}
|
|
22870
|
+
resolve(input) {
|
|
22871
|
+
return this._iMaster.resolve(input);
|
|
22872
|
+
}
|
|
22873
|
+
search(query, exchange, instrumentType, limit) {
|
|
22874
|
+
return this._iMaster.search(query, exchange, instrumentType, limit);
|
|
22875
|
+
}
|
|
22743
22876
|
async placeOrder(params) {
|
|
22744
22877
|
try {
|
|
22745
|
-
|
|
22878
|
+
let resolvedParams = params;
|
|
22879
|
+
if (!params.exchange) {
|
|
22880
|
+
const resolved = this._iMaster.resolve(params.tradingSymbol);
|
|
22881
|
+
if (!resolved.ok) {
|
|
22882
|
+
const asyncResolved = await this._iMaster.resolveAsync(params.tradingSymbol, this.id, this.instruments);
|
|
22883
|
+
if (!asyncResolved.ok) {
|
|
22884
|
+
return Err(new Error(`Cannot resolve "${params.tradingSymbol}". Either run broker.sync() first to use unified nsekit symbols, or provide exchange and the broker-native tradingSymbol directly.`));
|
|
22885
|
+
}
|
|
22886
|
+
resolvedParams = { ...params, exchange: asyncResolved.value.exchange };
|
|
22887
|
+
} else {
|
|
22888
|
+
resolvedParams = { ...params, exchange: resolved.value.exchange };
|
|
22889
|
+
}
|
|
22890
|
+
}
|
|
22891
|
+
const dhanParams = toOrderParams3(resolvedParams, this.clientId);
|
|
22746
22892
|
const res = await this.request("POST", "/orders", dhanParams);
|
|
22747
22893
|
return Ok({
|
|
22748
22894
|
orderId: res.orderId,
|
|
@@ -23396,6 +23542,14 @@ class PaperBroker {
|
|
|
23396
23542
|
id = "paper";
|
|
23397
23543
|
name = "Paper Trading";
|
|
23398
23544
|
instruments;
|
|
23545
|
+
has = {
|
|
23546
|
+
bulkDump: false,
|
|
23547
|
+
searchAPI: false,
|
|
23548
|
+
optionChain: false,
|
|
23549
|
+
historicalCandles: false,
|
|
23550
|
+
websocket: false,
|
|
23551
|
+
pnlReport: true
|
|
23552
|
+
};
|
|
23399
23553
|
session = null;
|
|
23400
23554
|
engine;
|
|
23401
23555
|
positions = new Map;
|
|
@@ -23406,6 +23560,7 @@ class PaperBroker {
|
|
|
23406
23560
|
initialBalance;
|
|
23407
23561
|
subscriptions = new Map;
|
|
23408
23562
|
connectionState = "DISCONNECTED";
|
|
23563
|
+
_iMaster;
|
|
23409
23564
|
dataSource = null;
|
|
23410
23565
|
dataSourceSub = null;
|
|
23411
23566
|
subIdCounter = 0;
|
|
@@ -23414,6 +23569,10 @@ class PaperBroker {
|
|
|
23414
23569
|
this.initialBalance = config?.initialBalance ?? 1e7;
|
|
23415
23570
|
this.balance = this.initialBalance;
|
|
23416
23571
|
this.engine = new PaperFillEngine(config?.fillConfig);
|
|
23572
|
+
this._iMaster = new InstrumentMaster({
|
|
23573
|
+
redis: config?.redis,
|
|
23574
|
+
instrumentFilePath: config?.instrumentFilePath
|
|
23575
|
+
});
|
|
23417
23576
|
this.engine.setFillCallback((orderId, update) => {
|
|
23418
23577
|
this.onOrderFill(orderId, update);
|
|
23419
23578
|
});
|
|
@@ -23421,6 +23580,20 @@ class PaperBroker {
|
|
|
23421
23580
|
setDataSource(broker) {
|
|
23422
23581
|
this.dataSource = broker;
|
|
23423
23582
|
}
|
|
23583
|
+
async sync(_force) {
|
|
23584
|
+
return Ok({
|
|
23585
|
+
brokerId: "paper",
|
|
23586
|
+
lastSyncAt: Date.now(),
|
|
23587
|
+
instrumentCount: 0,
|
|
23588
|
+
source: "network"
|
|
23589
|
+
});
|
|
23590
|
+
}
|
|
23591
|
+
resolve(input) {
|
|
23592
|
+
return this._iMaster.resolve(input);
|
|
23593
|
+
}
|
|
23594
|
+
search(query, exchange, instrumentType, limit) {
|
|
23595
|
+
return this._iMaster.search(query, exchange, instrumentType, limit);
|
|
23596
|
+
}
|
|
23424
23597
|
async authenticate(_creds) {
|
|
23425
23598
|
this.session = {
|
|
23426
23599
|
accessToken: "paper-session",
|
|
@@ -23688,7 +23861,8 @@ class PaperBroker {
|
|
|
23688
23861
|
for (const sub of this.subscriptions.values()) {}
|
|
23689
23862
|
}
|
|
23690
23863
|
updatePositionOnFill(order, update) {
|
|
23691
|
-
const { tradingSymbol,
|
|
23864
|
+
const { tradingSymbol, side, product } = order.params;
|
|
23865
|
+
const exchange = order.params.exchange ?? "NSE";
|
|
23692
23866
|
const posKey = `${exchange}:${tradingSymbol}:${product}`;
|
|
23693
23867
|
if (!this.positions.has(posKey)) {
|
|
23694
23868
|
this.positions.set(posKey, {
|
|
@@ -23745,7 +23919,7 @@ class PaperBroker {
|
|
|
23745
23919
|
brokerOrderId: pending.orderId,
|
|
23746
23920
|
broker: "paper",
|
|
23747
23921
|
tradingSymbol: pending.params.tradingSymbol,
|
|
23748
|
-
exchange: pending.params.exchange,
|
|
23922
|
+
exchange: pending.params.exchange ?? "NSE",
|
|
23749
23923
|
side: pending.params.side,
|
|
23750
23924
|
type: pending.params.type,
|
|
23751
23925
|
product: pending.params.product,
|
|
@@ -23764,17 +23938,17 @@ class PaperBroker {
|
|
|
23764
23938
|
}
|
|
23765
23939
|
}
|
|
23766
23940
|
// src/index.ts
|
|
23767
|
-
var VERSION = "0.
|
|
23768
|
-
function createBroker(brokerId) {
|
|
23941
|
+
var VERSION = "0.3.0";
|
|
23942
|
+
function createBroker(brokerId, options) {
|
|
23769
23943
|
switch (brokerId) {
|
|
23770
23944
|
case "zerodha":
|
|
23771
|
-
return new ZerodhaBroker;
|
|
23945
|
+
return new ZerodhaBroker(options);
|
|
23772
23946
|
case "finvasia":
|
|
23773
|
-
return new FinvasiaBroker;
|
|
23947
|
+
return new FinvasiaBroker(options);
|
|
23774
23948
|
case "dhan":
|
|
23775
|
-
return new DhanBroker;
|
|
23949
|
+
return new DhanBroker(options);
|
|
23776
23950
|
case "paper":
|
|
23777
|
-
return new PaperBroker;
|
|
23951
|
+
return new PaperBroker(options);
|
|
23778
23952
|
default: {
|
|
23779
23953
|
const exhaustive = brokerId;
|
|
23780
23954
|
throw new Error(`Unknown broker: ${exhaustive}`);
|