nsekit 0.1.1 → 0.2.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 CHANGED
@@ -6,7 +6,13 @@ Unified broker abstraction for Indian stock markets. Write your trading logic on
6
6
  npm install nsekit
7
7
  ```
8
8
 
9
- [GitHub](https://github.com/ilampirai/nsekit) | [docs/broker-credentials.md](docs/broker-credentials.md) | [docs/adding-a-broker.md](docs/adding-a-broker.md)
9
+ [AI-adding-a-broker.md](docs/AI-adding-a-broker.md) | [adding-a-broker.md](docs/adding-a-broker.md) | [broker-credentials.md](docs/broker-credentials.md)
10
+
11
+ | Document | Description |
12
+ |----------|-------------|
13
+ | [AI-adding-a-broker.md](docs/AI-adding-a-broker.md) | Self-contained prompt for any AI assistant to implement a new broker adapter end to end — from reading API docs to running tests to submitting a PR. |
14
+ | [adding-a-broker.md](docs/adding-a-broker.md) | Step-by-step manual guide covering file structure, implementation order, common pitfalls, the 13-checkpoint integration test, and pull request checklist. |
15
+ | [broker-credentials.md](docs/broker-credentials.md) | Per-broker credential reference — which fields are required, where to get them, auth flow details, and security notes. |
10
16
 
11
17
  ---
12
18
 
@@ -28,45 +34,102 @@ Before authenticating, review the credential fields each broker requires: [docs/
28
34
  ```typescript
29
35
  import { createBroker } from 'nsekit';
30
36
 
31
- // 1. Create a broker instance
37
+ const broker = createBroker('finvasia'); // or 'zerodha', 'dhan', 'paper'
38
+ const auth = await broker.authenticate({ userId: '...', password: '...', totpSecret: '...', apiKey: '...', vendorCode: '...' });
39
+ const order = await broker.placeOrder({ tradingSymbol: 'RELIANCE', exchange: 'NSE', side: 'BUY', quantity: 1, type: 'LIMIT', product: 'INTRADAY', price: 2400, validity: 'DAY' });
40
+ const cancel = await broker.cancelOrder(order.value.orderId);
41
+ ```
42
+
43
+ See [docs/broker-credentials.md](docs/broker-credentials.md) for which credential fields each broker requires.
44
+
45
+ ---
46
+
47
+ ## Instrument Master
48
+
49
+ Before placing orders, searching symbols, or subscribing to ticks, sync the broker's instrument master. This downloads ~180K instruments once and indexes them in memory. Every lookup after that is instant — no API calls, no delays.
50
+
51
+ **It's always better to sync first, then run everything else.**
52
+
53
+ ```typescript
54
+ import { createBroker, InstrumentMaster } from 'nsekit';
55
+
32
56
  const broker = createBroker('finvasia');
57
+ await broker.authenticate({ /* ... */ });
33
58
 
34
- // 2. Authenticate
35
- const auth = await broker.authenticate({
36
- userId: 'FA12345',
37
- password: 'your_password',
38
- totpSecret: 'YOUR_BASE32_SECRET',
39
- apiKey: 'your_api_secret',
40
- vendorCode: 'FA12345_U',
41
- });
42
- // auth.value => { accessToken: '...', userId: 'FA12345', expiresAt: 1707523199000, ... }
43
-
44
- // 3. Place an order
45
- const order = await broker.placeOrder({
46
- tradingSymbol: 'RELIANCE-EQ',
47
- exchange: 'NSE',
48
- side: 'BUY',
49
- quantity: 1,
50
- type: 'LIMIT',
51
- product: 'INTRADAY',
52
- price: 2400,
53
- validity: 'DAY',
59
+ // Sync once at startup
60
+ const imaster = new InstrumentMaster();
61
+ await imaster.syncBroker('finvasia', broker.instruments);
62
+ // => { instrumentCount: 184302, source: 'network' }
63
+
64
+ // Now resolve, search, or trade
65
+ const inst = imaster.resolve('NIFTY-24000-CE-27FEB26'); // exact canonical lookup
66
+ const results = imaster.search('NIFTY', 'NFO', 'CE', 10); // fuzzy search
67
+ ```
68
+
69
+ ### Where instruments are stored
70
+
71
+ The first sync downloads instruments from the broker and saves them to disk as JSON. On subsequent startups, `syncBroker()` loads from this cache — no network call, near-instant.
72
+
73
+ ```
74
+ ./InstrumentDump/ (default location)
75
+ finvasia-instruments.json (one file per broker)
76
+ zerodha-instruments.json
77
+ ```
78
+
79
+ Custom path:
80
+
81
+ ```typescript
82
+ const imaster = new InstrumentMaster({
83
+ instrumentFilePath: './data/instruments',
54
84
  });
55
- // order.value => { orderId: '26020900479551', status: 'PENDING', timestamp: 1707480649000 }
85
+ ```
56
86
 
57
- // 4. Get positions
58
- const positions = await broker.getPositions();
59
- // positions.value => [{ tradingSymbol: 'RELIANCE-EQ', side: 'LONG', quantity: 1, pnl: 12.50, ... }]
87
+ To force a fresh download (skip cache):
60
88
 
61
- // 5. Cancel the order
62
- const cancel = await broker.cancelOrder(order.value.orderId);
63
- // cancel.value => { orderId: '26020900479551', status: 'CANCELLED' }
89
+ ```typescript
90
+ await imaster.syncBroker('finvasia', broker.instruments, true);
91
+ ```
92
+
93
+ ### Multi-broker sync
94
+
95
+ Syncing multiple brokers merges instruments into one index. The same instrument from different brokers (e.g. NIFTY options) becomes a single entry with both broker tokens preserved.
96
+
97
+ ```typescript
98
+ await imaster.syncBroker('zerodha', zerodhaBroker.instruments);
99
+ await imaster.syncBroker('finvasia', finvasiaBroker.instruments);
100
+ // NIFTY-24000-CE-27FEB26 now has both zerodha and finvasia tokens
101
+ ```
102
+
103
+ ### Optional: Redis
104
+
105
+ Pass an ioredis-compatible client to cache instruments in Redis. Useful when multiple processes (user bots, strategy bots) need instrument data without each loading 180K entries from disk.
106
+
107
+ ```typescript
108
+ import Redis from 'ioredis';
109
+
110
+ const imaster = new InstrumentMaster({ redis: new Redis() });
111
+ await imaster.syncBroker('finvasia', broker.instruments);
112
+ // All instruments now in Redis — any process with the same Redis gets O(1) lookups
64
113
  ```
65
114
 
66
- Switch to any other broker by changing a single line:
115
+ ### Canonical symbol format
116
+
117
+ All instruments are indexed using a standardized format, regardless of which broker provided them:
118
+
119
+ ```
120
+ EQ: TATAMOTORS (just underlying)
121
+ FUT: NIFTY-FUT-27FEB26 (underlying-FUT-DDMMMYY)
122
+ CE: NIFTY-24000-CE-27FEB26 (underlying-strike-CE-DDMMMYY)
123
+ PE: NIFTY-24000-PE-27FEB26 (underlying-strike-PE-DDMMMYY)
124
+ ```
125
+
126
+ Zerodha's `NIFTY26FEB24000CE` and Finvasia's `NIFTY26FEB26C24000` both resolve to `NFO:NIFTY-24000-CE-27FEB26`. You never need to think about broker-specific symbol formats.
67
127
 
68
128
  ```typescript
69
- const broker = createBroker('zerodha'); // or 'dhan', 'paper'
129
+ imaster.resolve('TATAMOTORS'); // NSE equity (NSE preferred over BSE)
130
+ imaster.resolve('BSE:TATAMOTORS'); // explicit exchange
131
+ imaster.resolve('NIFTY-24000-CE-27FEB26'); // NFO option
132
+ imaster.resolve('NFO:NIFTY-FUT-27FEB26'); // with exchange prefix
70
133
  ```
71
134
 
72
135
  ---
@@ -119,13 +182,7 @@ const order = unwrap(result);
119
182
 
120
183
  ### Instrument Master
121
184
 
122
- The `InstrumentMaster` maintains a normalized lookup table of all tradeable instruments across brokers. Resolution follows a three-tier strategy:
123
-
124
- 1. Redis hot cache (O(1) lookup for recently used instruments)
125
- 2. In-memory index (built from broker dump files)
126
- 3. Broker search API fallback (for brokers that support it)
127
-
128
- Each broker implements `IBrokerInstruments` with `streamDump()`, `normalize()`, `search()`, and `resolve()` methods.
185
+ See the [Instrument Master](#instrument-master) section above for full usage. In short: each broker implements `IBrokerInstruments` with `streamDump()` and `normalize()`. The `normalize()` function sets `underlying`, `instrumentType`, `strike`, and `expiry` `InstrumentMaster` builds canonical symbols automatically and merges entries across brokers.
129
186
 
130
187
  ### Paper Broker
131
188
 
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- export declare const VERSION = "0.1.1";
1
+ export declare const VERSION = "0.2.0";
2
2
  export * from './types/index';
3
3
  export * from './errors/index';
4
4
  export type { IBroker } from './interfaces/broker.interface';
5
5
  export { InstrumentMaster } from './instruments/instrument-master';
6
+ export type { InstrumentMasterOptions } from './instruments/instrument-master';
6
7
  export { WSManager } from './websocket/ws-manager';
7
8
  export { SessionManager } from './session/session-manager';
8
9
  export { ZerodhaBroker } from './brokers/zerodha/ZerodhaBroker';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,OAAO,UAAU,CAAC;AAG/B,cAAc,eAAe,CAAC;AAG9B,cAAc,gBAAgB,CAAC;AAG/B,YAAY,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAG7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAG3D,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAI1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAM7D;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAexD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,OAAO,UAAU,CAAC;AAG/B,cAAc,eAAe,CAAC;AAG9B,cAAc,gBAAgB,CAAC;AAG/B,YAAY,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAG7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,YAAY,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAG3D,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAI1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAM7D;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAexD"}
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(resolve, reject, response) {
12629
+ function settle(resolve2, reject, response) {
12630
12630
  const validateStatus = response.config.validateStatus;
12631
12631
  if (!response.status || !validateStatus || validateStatus(response.status)) {
12632
- resolve(response);
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((resolve, reject) => {
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
- resolve(value);
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(resolve, reject, onDone) {
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(resolve, reject, {
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(resolve, reject, {
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(resolve, reject, response);
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(resolve, reject, response);
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(resolve, reject) {
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
- resolve(value);
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((resolve, reject) => {
14202
- settle(resolve, reject, {
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(resolve) {
14551
- resolvePromise = resolve;
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((resolve) => {
14566
- token.subscribe(resolve);
14567
- _resolve = 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((resolve, reject) => {
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 resolve(resp);
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((resolve, reject) => {
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 resolve(resp);
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
- const existingMeta = this.syncMeta.get(brokerId);
19311
- if (!force && existingMeta && brokerInstruments.checkDumpChanged) {
19312
- const changed = await brokerInstruments.checkDumpChanged({
19313
- etag: existingMeta.etag,
19314
- lastModified: existingMeta.lastModified
19315
- });
19316
- if (!changed) {
19317
- return Ok(existingMeta);
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
- let count = 0;
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: count
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
- etag: existing?.etag,
19352
- lastModified: existing?.lastModified,
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,35 @@ 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
+ }
19399
19446
  async getOptionChainCached(underlying, expiry) {
19400
19447
  const expiryDate = expiry.toISOString().slice(0, 10);
19401
19448
  const cacheKey = `optchain:${underlying.toUpperCase()}:${expiryDate}`;
@@ -19442,6 +19489,9 @@ class InstrumentMaster {
19442
19489
  get size() {
19443
19490
  return this.instruments.size;
19444
19491
  }
19492
+ get filePath() {
19493
+ return this.instrumentFilePath;
19494
+ }
19445
19495
  getSyncMeta(brokerId) {
19446
19496
  return this.syncMeta.get(brokerId);
19447
19497
  }
@@ -19465,8 +19515,39 @@ class InstrumentMaster {
19465
19515
  this.tokenIndex.clear();
19466
19516
  this.syncMeta.clear();
19467
19517
  }
19518
+ getDiskCachePath(brokerId) {
19519
+ return join(this.instrumentFilePath, `${brokerId}-instruments.json`);
19520
+ }
19521
+ ensureDirectory() {
19522
+ if (!existsSync(this.instrumentFilePath)) {
19523
+ mkdirSync(this.instrumentFilePath, { recursive: true });
19524
+ }
19525
+ }
19526
+ loadFromDisk(brokerId) {
19527
+ const filePath = this.getDiskCachePath(brokerId);
19528
+ try {
19529
+ if (!existsSync(filePath))
19530
+ return null;
19531
+ const raw = readFileSync(filePath, "utf-8");
19532
+ const parsed = JSON.parse(raw);
19533
+ if (!Array.isArray(parsed))
19534
+ return null;
19535
+ return parsed;
19536
+ } catch {
19537
+ return null;
19538
+ }
19539
+ }
19540
+ saveToDisk(brokerId, entries) {
19541
+ try {
19542
+ this.ensureDirectory();
19543
+ const filePath = this.getDiskCachePath(brokerId);
19544
+ const stripped = entries.map(({ raw, ...rest }) => rest);
19545
+ writeFileSync(filePath, JSON.stringify(stripped), "utf-8");
19546
+ } catch {}
19547
+ }
19468
19548
  mergeEntry(brokerId, entry) {
19469
- const key = this.makeKey(entry.exchange, entry.tradingSymbol);
19549
+ const canonical = this.buildCanonicalSymbol(entry.underlying, entry.instrumentType, entry.strike, entry.expiry);
19550
+ const key = this.makeKey(entry.exchange, canonical);
19470
19551
  let instrument = this.instruments.get(key);
19471
19552
  if (!instrument) {
19472
19553
  instrument = {
@@ -19477,7 +19558,7 @@ class InstrumentMaster {
19477
19558
  strike: entry.strike,
19478
19559
  lotSize: entry.lotSize,
19479
19560
  tickSize: entry.tickSize,
19480
- tradingSymbol: entry.tradingSymbol,
19561
+ tradingSymbol: canonical,
19481
19562
  brokerTokens: {}
19482
19563
  };
19483
19564
  this.instruments.set(key, instrument);
@@ -19571,6 +19652,35 @@ class InstrumentMaster {
19571
19652
  }
19572
19653
  return true;
19573
19654
  }
19655
+ formatExpiry(date) {
19656
+ const d = date instanceof Date ? date : new Date(date);
19657
+ const dd = String(d.getDate()).padStart(2, "0");
19658
+ const mmm = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"][d.getMonth()];
19659
+ const yy = String(d.getFullYear()).slice(-2);
19660
+ return `${dd}${mmm}${yy}`;
19661
+ }
19662
+ buildCanonicalSymbol(underlying, instrumentType, strike, expiry) {
19663
+ const name = underlying.toUpperCase();
19664
+ if (instrumentType === "EQ")
19665
+ return name;
19666
+ const exp = expiry ? this.formatExpiry(expiry) : "";
19667
+ if (instrumentType === "FUT")
19668
+ return `${name}-FUT-${exp}`;
19669
+ const s = strike != null ? String(strike) : "0";
19670
+ return `${name}-${s}-${instrumentType}-${exp}`;
19671
+ }
19672
+ parseInput(input) {
19673
+ const upper = input.trim().toUpperCase();
19674
+ const VALID = new Set(["NSE", "BSE", "NFO", "BFO", "MCX", "CDS"]);
19675
+ const idx = upper.indexOf(":");
19676
+ if (idx >= 2 && idx <= 3) {
19677
+ const prefix = upper.slice(0, idx);
19678
+ if (VALID.has(prefix)) {
19679
+ return { exchange: prefix, symbol: upper.slice(idx + 1) };
19680
+ }
19681
+ }
19682
+ return { symbol: upper };
19683
+ }
19574
19684
  makeKey(exchange, tradingSymbol) {
19575
19685
  return `${exchange}:${tradingSymbol}`;
19576
19686
  }
@@ -23764,7 +23874,7 @@ class PaperBroker {
23764
23874
  }
23765
23875
  }
23766
23876
  // src/index.ts
23767
- var VERSION = "0.1.1";
23877
+ var VERSION = "0.2.0";
23768
23878
  function createBroker(brokerId) {
23769
23879
  switch (brokerId) {
23770
23880
  case "zerodha":
@@ -3,13 +3,13 @@
3
3
  *
4
4
  * Maintains a Map<string, UnifiedInstrument> keyed by `{exchange}:{tradingSymbol}`.
5
5
  * Supports:
6
- * - syncBroker(): stream dump from any IBrokerInstruments, normalize, merge
6
+ * - syncBroker(): download + cache instruments, or load from existing cache
7
7
  * - search(): fuzzy text search with optional exchange/type filters
8
8
  * - getBySymbol(): exact lookup by tradingSymbol + exchange (Redis -> memory)
9
9
  * - getByToken(): lookup by broker-specific token
10
10
  * - getOptionChain(): filter CE/PE instruments for a given underlying + expiry
11
+ * - Disk cache layer: saves normalized entries to `instrumentFilePath`
11
12
  * - Optional Redis cache layer for distributed lookups
12
- * - ETag tracking for change detection across re-syncs
13
13
  */
14
14
  import type { BrokerId, Exchange, InstrumentType } from '../types/common';
15
15
  import type { UnifiedInstrument, IBrokerInstruments, BrokerInstrumentEntry } from '../types/instruments';
@@ -23,13 +23,21 @@ export interface RedisClient {
23
23
  interface SyncMeta {
24
24
  brokerId: BrokerId;
25
25
  lastSyncAt: number;
26
- etag?: string;
27
- lastModified?: string;
28
26
  instrumentCount: number;
27
+ source: 'network' | 'disk' | 'redis';
29
28
  }
30
29
  export interface InstrumentMasterOptions {
31
30
  /** Optional Redis client (ioredis-compatible) for distributed caching. */
32
31
  redis?: RedisClient;
32
+ /**
33
+ * Path to store downloaded instrument dump files.
34
+ * Each broker's instruments are saved as `{brokerId}-instruments.json`.
35
+ *
36
+ * If not provided, defaults to `./InstrumentDump` relative to cwd.
37
+ * Make sure the path is valid and writable, or instrument dumps will
38
+ * be saved in the default `./InstrumentDump` folder.
39
+ */
40
+ instrumentFilePath?: string;
33
41
  }
34
42
  export declare class InstrumentMaster {
35
43
  /** Primary store: `{exchange}:{tradingSymbol}` -> UnifiedInstrument */
@@ -40,14 +48,24 @@ export declare class InstrumentMaster {
40
48
  private syncMeta;
41
49
  /** Optional Redis client for distributed cache. */
42
50
  private readonly redis;
51
+ /** Resolved absolute path for instrument dump files. */
52
+ private readonly instrumentFilePath;
43
53
  constructor(options?: InstrumentMasterOptions);
44
54
  /**
45
- * Stream a broker's instrument dump, normalize each entry, and merge
46
- * into the unified index. Skips re-sync if the ETag has not changed.
55
+ * Sync a broker's instruments into the unified index.
56
+ *
57
+ * By default, checks for existing cached data first:
58
+ * - Redis available → check Redis
59
+ * - No Redis → check disk (`{instrumentFilePath}/{brokerId}-instruments.json`)
60
+ * - No cache → download from network via broker's streamDump()
61
+ *
62
+ * After download, saves to Redis (if available) or disk (if no Redis).
63
+ * Pass `force = true` to skip cache and always re-download from network.
64
+ * Cache refresh is the user's responsibility — call with `force = true` when needed.
47
65
  *
48
66
  * @param brokerId - Identifier for the broker being synced
49
67
  * @param brokerInstruments - The broker's IBrokerInstruments implementation
50
- * @param force - Skip ETag check and force re-sync
68
+ * @param force - Skip cache check, force re-download from network
51
69
  */
52
70
  syncBroker(brokerId: BrokerId, brokerInstruments: IBrokerInstruments, force?: boolean): Promise<Result<SyncMeta>>;
53
71
  /**
@@ -78,25 +96,30 @@ export declare class InstrumentMaster {
78
96
  * Get an instrument by a broker-specific token.
79
97
  */
80
98
  getByToken(brokerToken: string, brokerId: BrokerId): UnifiedInstrument | undefined;
99
+ /**
100
+ * Resolve a user-friendly input to a UnifiedInstrument.
101
+ * Accepts formats like `"TATAMOTORS"`, `"NSE:TATAMOTORS"`, `"NIFTY-24000-CE-27FEB26"`.
102
+ *
103
+ * When no exchange is specified and the symbol exists on both NSE and BSE only,
104
+ * NSE is preferred. If multiple matches span other exchanges, an error is returned
105
+ * listing them so the caller can be explicit.
106
+ */
107
+ resolve(input: string): Result<UnifiedInstrument>;
81
108
  /**
82
109
  * Get all CE and PE instruments for a given underlying and expiry date.
83
110
  * Returns instruments sorted by strike price.
84
111
  * Results are cached in Redis (10 min TTL) when available.
85
- *
86
- * @param underlying - The underlying symbol (e.g. "NIFTY", "BANKNIFTY")
87
- * @param expiry - The expiry date to match
88
112
  */
89
113
  getOptionChainCached(underlying: string, expiry: Date): Promise<UnifiedInstrument[]>;
90
114
  /**
91
115
  * Get all CE and PE instruments for a given underlying and expiry date.
92
116
  * Returns instruments sorted by strike price (in-memory only).
93
- *
94
- * @param underlying - The underlying symbol (e.g. "NIFTY", "BANKNIFTY")
95
- * @param expiry - The expiry date to match
96
117
  */
97
118
  getOptionChain(underlying: string, expiry: Date): UnifiedInstrument[];
98
119
  /** Total number of instruments in the index. */
99
120
  get size(): number;
121
+ /** Get the resolved instrument file path. */
122
+ get filePath(): string;
100
123
  /** Get sync metadata for a specific broker. */
101
124
  getSyncMeta(brokerId: BrokerId): SyncMeta | undefined;
102
125
  /** Get sync metadata for all brokers. */
@@ -105,8 +128,25 @@ export declare class InstrumentMaster {
105
128
  getAll(): UnifiedInstrument[];
106
129
  /** Get all instruments for a specific exchange. */
107
130
  getByExchange(exchange: Exchange): UnifiedInstrument[];
108
- /** Clear the entire index. */
131
+ /** Clear the entire index (memory + optionally disk/Redis). */
109
132
  clear(): void;
133
+ /**
134
+ * Get the disk cache file path for a broker.
135
+ */
136
+ private getDiskCachePath;
137
+ /**
138
+ * Ensure the instrument dump directory exists.
139
+ */
140
+ private ensureDirectory;
141
+ /**
142
+ * Load normalized instrument entries from disk cache.
143
+ * Returns null if file doesn't exist or is corrupt.
144
+ */
145
+ private loadFromDisk;
146
+ /**
147
+ * Save normalized instrument entries to disk.
148
+ */
149
+ private saveToDisk;
110
150
  /**
111
151
  * Merge a single BrokerInstrumentEntry into the unified index.
112
152
  * If the instrument already exists (from another broker), the broker token
@@ -122,6 +162,12 @@ export declare class InstrumentMaster {
122
162
  private scoreMatch;
123
163
  /** Check if all characters of needle appear in haystack in order. */
124
164
  private fuzzyMatch;
165
+ /** Format a Date as DDMMMYY (e.g. 27FEB26). */
166
+ private formatExpiry;
167
+ /** Build a canonical symbol: EQ → `TATA`, FUT → `NIFTY-FUT-27FEB26`, CE/PE → `NIFTY-24000-CE-27FEB26` */
168
+ private buildCanonicalSymbol;
169
+ /** Parse user input, splitting optional exchange prefix (e.g. `NSE:TATAMOTORS`). */
170
+ private parseInput;
125
171
  /** Build the canonical map key for an instrument. */
126
172
  private makeKey;
127
173
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"instrument-master.d.ts","sourceRoot":"","sources":["../../src/instruments/instrument-master.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAIxD,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChF,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACzC;AAID,UAAU,QAAQ;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAYD,MAAM,WAAW,uBAAuB;IACtC,0EAA0E;IAC1E,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAID,qBAAa,gBAAgB;IAC3B,uEAAuE;IACvE,OAAO,CAAC,WAAW,CAAwC;IAE3D,kEAAkE;IAClE,OAAO,CAAC,UAAU,CAAsC;IAExD,+BAA+B;IAC/B,OAAO,CAAC,QAAQ,CAAiC;IAEjD,mDAAmD;IACnD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;gBAE/B,OAAO,CAAC,EAAE,uBAAuB;IAM7C;;;;;;;OAOG;IACG,UAAU,CACd,QAAQ,EAAE,QAAQ,EAClB,iBAAiB,EAAE,kBAAkB,EACrC,KAAK,UAAQ,GACZ,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IA8C5B;;;OAGG;IACH,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,EAAE,GAAG,IAAI;IAiBtE;;;;;;;;OAQG;IACH,MAAM,CACJ,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,QAAQ,EACnB,cAAc,CAAC,EAAE,cAAc,EAC/B,KAAK,SAAK,GACT,iBAAiB,EAAE;IA2BtB;;;OAGG;IACG,gBAAgB,CACpB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAiBzC;;OAEG;IACH,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,iBAAiB,GAAG,SAAS;IAKrF;;OAEG;IACH,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,iBAAiB,GAAG,SAAS;IASlF;;;;;;;OAOG;IACG,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IA8B1F;;;;;;OAMG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,iBAAiB,EAAE;IA+BrE,gDAAgD;IAChD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,+CAA+C;IAC/C,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS;IAIrD,yCAAyC;IACzC,cAAc,IAAI,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAIzC,gDAAgD;IAChD,MAAM,IAAI,iBAAiB,EAAE;IAI7B,mDAAmD;IACnD,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,iBAAiB,EAAE;IAUtD,8BAA8B;IAC9B,KAAK,IAAI,IAAI;IAQb;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAmDlB,qEAAqE;IACrE,OAAO,CAAC,kBAAkB;IA6B1B;;;OAGG;IACH,OAAO,CAAC,UAAU;IA4BlB,qEAAqE;IACrE,OAAO,CAAC,UAAU;IAkBlB,qDAAqD;IACrD,OAAO,CAAC,OAAO;IAIf;;;OAGG;YACW,uBAAuB;CA+BtC"}
1
+ {"version":3,"file":"instrument-master.d.ts","sourceRoot":"","sources":["../../src/instruments/instrument-master.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nsekit",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Unified broker abstraction for Indian stock markets - CCXT-like interface for NSE/BSE brokers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",