@discomedia/utils 1.0.42 → 1.0.44

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.0.42",
6
+ "version": "1.0.44",
7
7
  "author": "Disco Media",
8
8
  "description": "Utility functions used in Disco Media apps",
9
9
  "always-build-npm": true,
@@ -33,17 +33,17 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "dotenv": "^17.2.3",
36
- "openai": "^6.5.0",
36
+ "openai": "^6.7.0",
37
37
  "p-limit": "^7.2.0",
38
38
  "tslib": "^2.8.1",
39
39
  "ws": "^8.18.3"
40
40
  },
41
41
  "license": "ISC",
42
42
  "devDependencies": {
43
- "@rollup/plugin-commonjs": "^28.0.8",
43
+ "@rollup/plugin-commonjs": "^28.0.9",
44
44
  "@rollup/plugin-json": "^6.1.0",
45
45
  "@rollup/plugin-node-resolve": "^16.0.3",
46
- "@rollup/plugin-typescript": "^12.1.4",
46
+ "@rollup/plugin-typescript": "^12.3.0",
47
47
  "@types/ws": "^8.18.1",
48
48
  "lightweight-charts": "^5.0.9",
49
49
  "rollup": "^4.52.5",
package/dist/test.js CHANGED
@@ -799,7 +799,7 @@ const safeJSON = (text) => {
799
799
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
800
800
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
801
801
 
802
- const VERSION = '6.5.0'; // x-release-please-version
802
+ const VERSION = '6.7.0'; // x-release-please-version
803
803
 
804
804
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
805
805
  const isRunningInBrowser = () => {
@@ -5641,20 +5641,19 @@ let Files$1 = class Files extends APIResource {
5641
5641
  * up to 512 MB, and the size of all files uploaded by one organization can be up
5642
5642
  * to 1 TB.
5643
5643
  *
5644
- * The Assistants API supports files up to 2 million tokens and of specific file
5645
- * types. See the
5646
- * [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) for
5647
- * details.
5648
- *
5649
- * The Fine-tuning API only supports `.jsonl` files. The input also has certain
5650
- * required formats for fine-tuning
5651
- * [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input) or
5652
- * [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input)
5653
- * models.
5654
- *
5655
- * The Batch API only supports `.jsonl` files up to 200 MB in size. The input also
5656
- * has a specific required
5657
- * [format](https://platform.openai.com/docs/api-reference/batch/request-input).
5644
+ * - The Assistants API supports files up to 2 million tokens and of specific file
5645
+ * types. See the
5646
+ * [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools)
5647
+ * for details.
5648
+ * - The Fine-tuning API only supports `.jsonl` files. The input also has certain
5649
+ * required formats for fine-tuning
5650
+ * [chat](https://platform.openai.com/docs/api-reference/fine-tuning/chat-input)
5651
+ * or
5652
+ * [completions](https://platform.openai.com/docs/api-reference/fine-tuning/completions-input)
5653
+ * models.
5654
+ * - The Batch API only supports `.jsonl` files up to 200 MB in size. The input
5655
+ * also has a specific required
5656
+ * [format](https://platform.openai.com/docs/api-reference/batch/request-input).
5658
5657
  *
5659
5658
  * Please [contact us](https://help.openai.com/) if you need to increase these
5660
5659
  * storage limits.
@@ -6557,11 +6556,27 @@ class InputItems extends APIResource {
6557
6556
  }
6558
6557
  }
6559
6558
 
6559
+ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
6560
+ class InputTokens extends APIResource {
6561
+ /**
6562
+ * Get input token counts
6563
+ *
6564
+ * @example
6565
+ * ```ts
6566
+ * const response = await client.responses.inputTokens.count();
6567
+ * ```
6568
+ */
6569
+ count(body = {}, options) {
6570
+ return this._client.post('/responses/input_tokens', { body, ...options });
6571
+ }
6572
+ }
6573
+
6560
6574
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
6561
6575
  class Responses extends APIResource {
6562
6576
  constructor() {
6563
6577
  super(...arguments);
6564
6578
  this.inputItems = new InputItems(this._client);
6579
+ this.inputTokens = new InputTokens(this._client);
6565
6580
  }
6566
6581
  create(body, options) {
6567
6582
  return this._client.post('/responses', { body, ...options, stream: body.stream ?? false })._thenUnwrap((rsp) => {
@@ -6627,6 +6642,7 @@ class Responses extends APIResource {
6627
6642
  }
6628
6643
  }
6629
6644
  Responses.InputItems = InputItems;
6645
+ Responses.InputTokens = InputTokens;
6630
6646
 
6631
6647
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
6632
6648
  class Parts extends APIResource {
@@ -13458,7 +13474,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
13458
13474
  */
13459
13475
  async getHistoricalBars(params) {
13460
13476
  const symbols = params.symbols;
13461
- const symbolsStr = symbols.join(',');
13477
+ symbols.join(',');
13462
13478
  let allBars = {};
13463
13479
  let pageToken = null;
13464
13480
  let hasMorePages = true;
@@ -13469,7 +13485,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
13469
13485
  symbols.forEach((symbol) => {
13470
13486
  allBars[symbol] = [];
13471
13487
  });
13472
- log(`Starting historical bars fetch for ${symbolsStr.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`);
13488
+ log(`Starting historical bars fetch for ${symbols.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`);
13473
13489
  while (hasMorePages) {
13474
13490
  pageCount++;
13475
13491
  const requestParams = {
@@ -13480,7 +13496,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
13480
13496
  };
13481
13497
  const response = await this.makeRequest('/stocks/bars', 'GET', requestParams);
13482
13498
  if (!response.bars) {
13483
- log(`No bars data found in response for ${symbolsStr.length} symbols`, { type: 'warn' });
13499
+ log(`No bars data found in response for ${symbols.length} symbols`, { type: 'warn' });
13484
13500
  break;
13485
13501
  }
13486
13502
  // Track currency from first response
@@ -13522,10 +13538,10 @@ class AlpacaMarketDataAPI extends EventEmitter {
13522
13538
  }
13523
13539
  }
13524
13540
  // Final summary
13525
- const symbolsJoined = Object.entries(allBars)
13541
+ const symbolCounts = Object.entries(allBars)
13526
13542
  .map(([symbol, bars]) => `${symbol}: ${bars.length}`)
13527
13543
  .join(', ');
13528
- log(`Bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages for ${symbolsJoined.length} symbols'}`);
13544
+ log(`Bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages for ${symbols.length} symbols (${symbolCounts})`);
13529
13545
  return {
13530
13546
  bars: allBars,
13531
13547
  next_page_token: null, // Always null since we fetch all pages
@@ -13796,7 +13812,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
13796
13812
  */
13797
13813
  async getHistoricalOptionsBars(params) {
13798
13814
  const symbols = params.symbols;
13799
- const symbolsStr = symbols.join(',');
13815
+ symbols.join(',');
13800
13816
  let allBars = {};
13801
13817
  let pageToken = null;
13802
13818
  let hasMorePages = true;
@@ -13806,7 +13822,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
13806
13822
  symbols.forEach((symbol) => {
13807
13823
  allBars[symbol] = [];
13808
13824
  });
13809
- log(`Starting historical options bars fetch for ${symbolsStr.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`);
13825
+ log(`Starting historical options bars fetch for ${symbols.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`);
13810
13826
  while (hasMorePages) {
13811
13827
  pageCount++;
13812
13828
  const requestParams = {
@@ -13815,7 +13831,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
13815
13831
  };
13816
13832
  const response = await this.makeRequest('/options/bars', 'GET', requestParams, 'v1beta1');
13817
13833
  if (!response.bars) {
13818
- log(`No options bars data found in response for ${symbolsStr.length} symbols`, { type: 'warn' });
13834
+ log(`No options bars data found in response for ${symbols.length} symbols`, { type: 'warn' });
13819
13835
  break;
13820
13836
  }
13821
13837
  // Combine bars for each symbol
@@ -13845,7 +13861,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
13845
13861
  const dateRangeStr = earliestTimestamp && latestTimestamp
13846
13862
  ? `${earliestTimestamp.toLocaleDateString('en-US', { timeZone: 'America/New_York' })} to ${latestTimestamp.toLocaleDateString('en-US', { timeZone: 'America/New_York' })}`
13847
13863
  : 'unknown range';
13848
- log(`Page ${pageCount}: Fetched ${pageBarsCount.toLocaleString()} option bars (total: ${totalBarsCount.toLocaleString()}) for ${symbolsStr}, date range: ${dateRangeStr}${hasMorePages ? ', more pages available' : ', complete'}`, {
13864
+ log(`Page ${pageCount}: Fetched ${pageBarsCount.toLocaleString()} option bars (total: ${totalBarsCount.toLocaleString()}) for ${symbols.length} symbols, date range: ${dateRangeStr}${hasMorePages ? ', more pages available' : ', complete'}`, {
13849
13865
  type: 'debug',
13850
13866
  });
13851
13867
  // Prevent infinite loops
@@ -13874,7 +13890,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
13874
13890
  */
13875
13891
  async getHistoricalOptionsTrades(params) {
13876
13892
  const symbols = params.symbols;
13877
- const symbolsStr = symbols.join(',');
13893
+ symbols.join(',');
13878
13894
  let allTrades = {};
13879
13895
  let pageToken = null;
13880
13896
  let hasMorePages = true;
@@ -13884,7 +13900,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
13884
13900
  symbols.forEach((symbol) => {
13885
13901
  allTrades[symbol] = [];
13886
13902
  });
13887
- log(`Starting historical options trades fetch for ${symbolsStr.length} symbols (${params.start || 'no start'} to ${params.end || 'no end'})`);
13903
+ log(`Starting historical options trades fetch for ${symbols.length} symbols (${params.start || 'no start'} to ${params.end || 'no end'})`);
13888
13904
  while (hasMorePages) {
13889
13905
  pageCount++;
13890
13906
  const requestParams = {
@@ -13893,7 +13909,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
13893
13909
  };
13894
13910
  const response = await this.makeRequest('/options/trades', 'GET', requestParams, 'v1beta1');
13895
13911
  if (!response.trades) {
13896
- log(`No options trades data found in response for ${symbolsStr.length} symbols`, { type: 'warn' });
13912
+ log(`No options trades data found in response for ${symbols.length} symbols`, { type: 'warn' });
13897
13913
  break;
13898
13914
  }
13899
13915
  // Combine trades for each symbol
@@ -13923,7 +13939,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
13923
13939
  const dateRangeStr = earliestTimestamp && latestTimestamp
13924
13940
  ? `${earliestTimestamp.toLocaleDateString('en-US', { timeZone: 'America/New_York' })} to ${latestTimestamp.toLocaleDateString('en-US', { timeZone: 'America/New_York' })}`
13925
13941
  : 'unknown range';
13926
- log(`Page ${pageCount}: Fetched ${pageTradesCount.toLocaleString()} option trades (total: ${totalTradesCount.toLocaleString()}) for ${symbolsStr.length} symbols, date range: ${dateRangeStr}${hasMorePages ? ', more pages available' : ', complete'}`, {
13942
+ log(`Page ${pageCount}: Fetched ${pageTradesCount.toLocaleString()} option trades (total: ${totalTradesCount.toLocaleString()}) for ${symbols.length} symbols, date range: ${dateRangeStr}${hasMorePages ? ', more pages available' : ', complete'}`, {
13927
13943
  type: 'debug',
13928
13944
  });
13929
13945
  // Prevent infinite loops
@@ -14383,57 +14399,318 @@ var alpacaMarketDataApi = /*#__PURE__*/Object.freeze({
14383
14399
  });
14384
14400
 
14385
14401
  // Test file for context functionality
14386
- async function testGetAssetsShortableFilter() {
14387
- console.log('\n--- Testing getAssets params and shortable filter ---');
14388
- const log = (message, options = { type: 'info' }) => {
14389
- log$1(message, { ...options, source: 'Test' });
14390
- };
14402
+ async function testMarketDataAPI() {
14403
+ console.log('\n--- Testing Market Data API ---');
14391
14404
  if (!process.env.ALPACA_API_KEY || !process.env.ALPACA_SECRET_KEY) {
14392
- console.log('Skipping getAssets test: Missing environment variables (ALPACA_API_KEY, ALPACA_SECRET_KEY)');
14405
+ console.log('Skipping Market Data API tests: Missing environment variables (ALPACA_API_KEY, ALPACA_SECRET_KEY)');
14393
14406
  return;
14394
14407
  }
14395
14408
  try {
14396
14409
  const { marketDataAPI } = await Promise.resolve().then(function () { return alpacaMarketDataApi; });
14397
- // 1) Default call: should filter to shortable && easy_to_borrow and default to active us_equity
14398
- log('Calling getAssets() with defaults');
14399
- const filtered = await marketDataAPI.getAssets();
14400
- console.log(`Received ${filtered.length} filtered assets (default params)`);
14401
- const allFilteredValid = filtered.every((a) => a.shortable === true && a.easy_to_borrow === true);
14402
- if (!allFilteredValid) {
14403
- console.error('✗ Default getAssets() included assets that are not shortable and easy_to_borrow');
14410
+ // Test 1: Single symbol request - verify only 1 symbol is returned
14411
+ console.log('\n[Test 1] Single symbol historical bars');
14412
+ try {
14413
+ const testSymbol = 'AAPL';
14414
+ const endDate = new Date('2023-02-28T23:59:59.999Z');
14415
+ const startDate = new Date('2023-02-24T00:00:00.000Z');
14416
+ const response = await marketDataAPI.getHistoricalBars({
14417
+ symbols: [testSymbol],
14418
+ timeframe: '1Min',
14419
+ start: startDate.toISOString(),
14420
+ end: endDate.toISOString(),
14421
+ });
14422
+ const symbolsInResponse = Object.keys(response.bars);
14423
+ const barsCount = response.bars[testSymbol]?.length || 0;
14424
+ console.log(` Requested symbols: 1 (${testSymbol})`);
14425
+ console.log(` Symbols in response: ${symbolsInResponse.length} (${symbolsInResponse.join(', ')})`);
14426
+ console.log(` Bars for ${testSymbol}: ${barsCount}`);
14427
+ if (symbolsInResponse.length !== 1) {
14428
+ console.error(` ✗ FAILED: Expected 1 symbol in response, got ${symbolsInResponse.length}`);
14429
+ }
14430
+ else if (symbolsInResponse[0] !== testSymbol) {
14431
+ console.error(` ✗ FAILED: Expected symbol ${testSymbol}, got ${symbolsInResponse[0]}`);
14432
+ }
14433
+ else {
14434
+ console.log(` ✓ Single symbol request correctly returned 1 symbol`);
14435
+ }
14404
14436
  }
14405
- else {
14406
- console.log(' Default getAssets() correctly filtered shortable and easy_to_borrow assets');
14437
+ catch (error) {
14438
+ console.error(' Test 1 failed:', error);
14407
14439
  }
14408
- // 2) Unfiltered call: shortable=false
14409
- log('Calling getAssets({ shortable: false })');
14410
- const unfiltered = await marketDataAPI.getAssets({ shortable: false });
14411
- console.log(`Received ${unfiltered.length} unfiltered assets`);
14412
- if (unfiltered.length < filtered.length) {
14413
- console.warn('Unfiltered list is smaller than filtered. This is unexpected but not fatal.');
14440
+ // Test 2: Multiple symbols request - verify correct count
14441
+ console.log('\n[Test 2] Multiple symbols (3) historical bars');
14442
+ try {
14443
+ const testSymbols = ['AAPL', 'MSFT', 'GOOGL'];
14444
+ const endDate = new Date('2023-02-28T23:59:59.999Z');
14445
+ const startDate = new Date('2023-02-24T00:00:00.000Z');
14446
+ const response = await marketDataAPI.getHistoricalBars({
14447
+ symbols: testSymbols,
14448
+ timeframe: '1Min',
14449
+ start: startDate.toISOString(),
14450
+ end: endDate.toISOString(),
14451
+ });
14452
+ const symbolsInResponse = Object.keys(response.bars);
14453
+ const totalBars = Object.values(response.bars).reduce((sum, bars) => sum + bars.length, 0);
14454
+ console.log(` Requested symbols: ${testSymbols.length} (${testSymbols.join(', ')})`);
14455
+ console.log(` Symbols in response: ${symbolsInResponse.length} (${symbolsInResponse.join(', ')})`);
14456
+ console.log(` Total bars: ${totalBars}`);
14457
+ // Check each requested symbol
14458
+ const missingSymbols = testSymbols.filter((s) => !symbolsInResponse.includes(s));
14459
+ const extraSymbols = symbolsInResponse.filter((s) => !testSymbols.includes(s));
14460
+ if (missingSymbols.length > 0) {
14461
+ console.error(` ✗ FAILED: Missing symbols: ${missingSymbols.join(', ')}`);
14462
+ }
14463
+ if (extraSymbols.length > 0) {
14464
+ console.error(` ✗ FAILED: Extra symbols not requested: ${extraSymbols.join(', ')}`);
14465
+ }
14466
+ if (symbolsInResponse.length !== testSymbols.length) {
14467
+ console.error(` ✗ FAILED: Expected ${testSymbols.length} symbols in response, got ${symbolsInResponse.length}`);
14468
+ }
14469
+ else if (missingSymbols.length === 0 && extraSymbols.length === 0) {
14470
+ console.log(` ✓ Multiple symbols request correctly returned ${testSymbols.length} symbols`);
14471
+ }
14472
+ // Show bars per symbol
14473
+ testSymbols.forEach((symbol) => {
14474
+ const count = response.bars[symbol]?.length || 0;
14475
+ console.log(` ${symbol}: ${count} bars`);
14476
+ });
14414
14477
  }
14415
- else {
14416
- console.log(' Unfiltered list length is >= filtered list length');
14478
+ catch (error) {
14479
+ console.error(' Test 2 failed:', error);
14417
14480
  }
14418
- // 3) Specific constraints: inactive crypto, not asserting non-empty
14419
- log("Calling getAssets({ status: 'inactive', asset_class: 'crypto', shortable: false })");
14420
- const inactiveCrypto = await marketDataAPI.getAssets({ status: 'inactive', asset_class: 'crypto', shortable: false });
14421
- console.log(`Received ${inactiveCrypto.length} inactive crypto assets`);
14422
- if (inactiveCrypto.length > 0) {
14423
- const valid = inactiveCrypto.every((a) => a.class === 'crypto' && a.status === 'inactive');
14424
- if (!valid) {
14425
- console.error('✗ Inactive crypto request returned assets with mismatched class or status');
14481
+ // Test 3: Latest bars - single symbol
14482
+ console.log('\n[Test 3] Latest bars for single symbol');
14483
+ try {
14484
+ const testSymbol = 'TSLA';
14485
+ const response = await marketDataAPI.getLatestBars([testSymbol]);
14486
+ const symbolsInResponse = Object.keys(response.bars);
14487
+ console.log(` Requested symbols: 1 (${testSymbol})`);
14488
+ console.log(` Symbols in response: ${symbolsInResponse.length} (${symbolsInResponse.join(', ')})`);
14489
+ if (symbolsInResponse.length !== 1) {
14490
+ console.error(` ✗ FAILED: Expected 1 symbol in response, got ${symbolsInResponse.length}`);
14491
+ }
14492
+ else if (symbolsInResponse[0] !== testSymbol) {
14493
+ console.error(` ✗ FAILED: Expected symbol ${testSymbol}, got ${symbolsInResponse[0]}`);
14426
14494
  }
14427
14495
  else {
14428
- console.log('Inactive crypto request returned only crypto with inactive status');
14496
+ console.log(` Latest bars correctly returned 1 symbol`);
14429
14497
  }
14430
14498
  }
14431
- else {
14432
- console.log('No inactive crypto assets returned; skipping class/status assertions.');
14499
+ catch (error) {
14500
+ console.error(' Test 3 failed:', error);
14501
+ }
14502
+ // Test 4: Latest bars - multiple symbols
14503
+ console.log('\n[Test 4] Latest bars for multiple symbols');
14504
+ try {
14505
+ const testSymbols = ['SPY', 'QQQ', 'IWM', 'DIA'];
14506
+ const response = await marketDataAPI.getLatestBars(testSymbols);
14507
+ const symbolsInResponse = Object.keys(response.bars);
14508
+ console.log(` Requested symbols: ${testSymbols.length} (${testSymbols.join(', ')})`);
14509
+ console.log(` Symbols in response: ${symbolsInResponse.length} (${symbolsInResponse.join(', ')})`);
14510
+ const missingSymbols = testSymbols.filter((s) => !symbolsInResponse.includes(s));
14511
+ const extraSymbols = symbolsInResponse.filter((s) => !testSymbols.includes(s));
14512
+ if (missingSymbols.length > 0) {
14513
+ console.error(` ✗ FAILED: Missing symbols: ${missingSymbols.join(', ')}`);
14514
+ }
14515
+ if (extraSymbols.length > 0) {
14516
+ console.error(` ✗ FAILED: Extra symbols not requested: ${extraSymbols.join(', ')}`);
14517
+ }
14518
+ if (symbolsInResponse.length !== testSymbols.length) {
14519
+ console.error(` ✗ FAILED: Expected ${testSymbols.length} symbols in response, got ${symbolsInResponse.length}`);
14520
+ }
14521
+ else if (missingSymbols.length === 0 && extraSymbols.length === 0) {
14522
+ console.log(` ✓ Latest bars correctly returned ${testSymbols.length} symbols`);
14523
+ }
14524
+ }
14525
+ catch (error) {
14526
+ console.error(' ✗ Test 4 failed:', error);
14527
+ }
14528
+ // Test 5: Latest quotes - verify symbol count
14529
+ console.log('\n[Test 5] Latest quotes for multiple symbols');
14530
+ try {
14531
+ const testSymbols = ['AAPL', 'NVDA', 'AMD'];
14532
+ const response = await marketDataAPI.getLatestQuotes(testSymbols);
14533
+ const symbolsInResponse = Object.keys(response.quotes);
14534
+ console.log(` Requested symbols: ${testSymbols.length} (${testSymbols.join(', ')})`);
14535
+ console.log(` Symbols in response: ${symbolsInResponse.length} (${symbolsInResponse.join(', ')})`);
14536
+ const missingSymbols = testSymbols.filter((s) => !symbolsInResponse.includes(s));
14537
+ const extraSymbols = symbolsInResponse.filter((s) => !testSymbols.includes(s));
14538
+ if (missingSymbols.length > 0) {
14539
+ console.error(` ✗ FAILED: Missing symbols: ${missingSymbols.join(', ')}`);
14540
+ }
14541
+ if (extraSymbols.length > 0) {
14542
+ console.error(` ✗ FAILED: Extra symbols not requested: ${extraSymbols.join(', ')}`);
14543
+ }
14544
+ if (symbolsInResponse.length !== testSymbols.length) {
14545
+ console.error(` ✗ FAILED: Expected ${testSymbols.length} symbols in response, got ${symbolsInResponse.length}`);
14546
+ }
14547
+ else if (missingSymbols.length === 0 && extraSymbols.length === 0) {
14548
+ console.log(` ✓ Latest quotes correctly returned ${testSymbols.length} symbols`);
14549
+ }
14550
+ }
14551
+ catch (error) {
14552
+ console.error(' ✗ Test 5 failed:', error);
14553
+ }
14554
+ // Test 6: Latest trades - verify symbol count
14555
+ console.log('\n[Test 6] Latest trades for multiple symbols');
14556
+ try {
14557
+ const testSymbols = ['META', 'AMZN'];
14558
+ const response = await marketDataAPI.getLatestTrades(testSymbols);
14559
+ const symbolsInResponse = Object.keys(response.trades);
14560
+ console.log(` Requested symbols: ${testSymbols.length} (${testSymbols.join(', ')})`);
14561
+ console.log(` Symbols in response: ${symbolsInResponse.length} (${symbolsInResponse.join(', ')})`);
14562
+ const missingSymbols = testSymbols.filter((s) => !symbolsInResponse.includes(s));
14563
+ const extraSymbols = symbolsInResponse.filter((s) => !testSymbols.includes(s));
14564
+ if (missingSymbols.length > 0) {
14565
+ console.error(` ✗ FAILED: Missing symbols: ${missingSymbols.join(', ')}`);
14566
+ }
14567
+ if (extraSymbols.length > 0) {
14568
+ console.error(` ✗ FAILED: Extra symbols not requested: ${extraSymbols.join(', ')}`);
14569
+ }
14570
+ if (symbolsInResponse.length !== testSymbols.length) {
14571
+ console.error(` ✗ FAILED: Expected ${testSymbols.length} symbols in response, got ${symbolsInResponse.length}`);
14572
+ }
14573
+ else if (missingSymbols.length === 0 && extraSymbols.length === 0) {
14574
+ console.log(` ✓ Latest trades correctly returned ${testSymbols.length} symbols`);
14575
+ }
14576
+ }
14577
+ catch (error) {
14578
+ console.error(' ✗ Test 6 failed:', error);
14579
+ }
14580
+ // Test 7: Empty symbols array
14581
+ console.log('\n[Test 7] Empty symbols array handling');
14582
+ try {
14583
+ const response = await marketDataAPI.getLatestQuotes([]);
14584
+ const symbolsInResponse = Object.keys(response.quotes);
14585
+ console.log(` Requested symbols: 0 (empty array)`);
14586
+ console.log(` Symbols in response: ${symbolsInResponse.length}`);
14587
+ if (symbolsInResponse.length === 0) {
14588
+ console.log(` ✓ Empty symbols array correctly returned empty response`);
14589
+ }
14590
+ else {
14591
+ console.error(` ✗ FAILED: Expected 0 symbols in response, got ${symbolsInResponse.length}`);
14592
+ }
14593
+ }
14594
+ catch (error) {
14595
+ console.error(' ✗ Test 7 failed:', error);
14596
+ }
14597
+ // Test 8: Historical bars with date range that spans multiple pages
14598
+ console.log('\n[Test 8] Historical bars with pagination (longer date range)');
14599
+ try {
14600
+ const testSymbols = ['SPY'];
14601
+ const endDate = new Date('2023-03-15T23:59:59.999Z');
14602
+ const startDate = new Date('2023-03-01T01:00:00.000Z');
14603
+ const response = await marketDataAPI.getHistoricalBars({
14604
+ symbols: testSymbols,
14605
+ timeframe: '1Min',
14606
+ start: startDate.toISOString(),
14607
+ end: endDate.toISOString(),
14608
+ });
14609
+ const symbolsInResponse = Object.keys(response.bars);
14610
+ const totalBars = Object.values(response.bars).reduce((sum, bars) => sum + bars.length, 0);
14611
+ console.log(` Requested symbols: ${testSymbols.length} (${testSymbols.join(', ')})`);
14612
+ console.log(` Symbols in response: ${symbolsInResponse.length} (${symbolsInResponse.join(', ')})`);
14613
+ console.log(` Total bars: ${totalBars}`);
14614
+ const missingSymbols = testSymbols.filter((s) => !symbolsInResponse.includes(s));
14615
+ const extraSymbols = symbolsInResponse.filter((s) => !testSymbols.includes(s));
14616
+ if (missingSymbols.length > 0) {
14617
+ console.error(` ✗ FAILED: Missing symbols: ${missingSymbols.join(', ')}`);
14618
+ }
14619
+ if (extraSymbols.length > 0) {
14620
+ console.error(` ✗ FAILED: Extra symbols not requested: ${extraSymbols.join(', ')}`);
14621
+ }
14622
+ if (symbolsInResponse.length !== testSymbols.length) {
14623
+ console.error(` ✗ FAILED: Expected ${testSymbols.length} symbols in response, got ${symbolsInResponse.length}`);
14624
+ }
14625
+ else if (missingSymbols.length === 0 && extraSymbols.length === 0) {
14626
+ console.log(` ✓ Paginated request correctly returned ${testSymbols.length} symbols`);
14627
+ }
14628
+ }
14629
+ catch (error) {
14630
+ console.error(' ✗ Test 8 failed:', error);
14631
+ }
14632
+ // Test 9: Check for duplicate bars across pagination
14633
+ console.log('\n[Test 9] Check for duplicate bars in paginated response');
14634
+ try {
14635
+ const testSymbol = 'AAPL';
14636
+ const endDate = new Date('2023-02-28T23:59:59.999Z');
14637
+ const startDate = new Date('2023-02-24T00:00:00.000Z');
14638
+ const response = await marketDataAPI.getHistoricalBars({
14639
+ symbols: [testSymbol],
14640
+ timeframe: '1Min',
14641
+ start: startDate.toISOString(),
14642
+ end: endDate.toISOString(),
14643
+ });
14644
+ const bars = response.bars[testSymbol] || [];
14645
+ const timestamps = bars.map((bar) => bar.t);
14646
+ const uniqueTimestamps = new Set(timestamps);
14647
+ console.log(` Total bars: ${bars.length}`);
14648
+ console.log(` Unique timestamps: ${uniqueTimestamps.size}`);
14649
+ if (bars.length !== uniqueTimestamps.size) {
14650
+ const duplicateCount = bars.length - uniqueTimestamps.size;
14651
+ console.error(` ✗ FAILED: Found ${duplicateCount} duplicate bars`);
14652
+ // Show some duplicate examples
14653
+ const timestampCounts = new Map();
14654
+ timestamps.forEach((ts) => {
14655
+ timestampCounts.set(ts, (timestampCounts.get(ts) || 0) + 1);
14656
+ });
14657
+ const duplicates = Array.from(timestampCounts.entries())
14658
+ .filter(([_, count]) => count > 1)
14659
+ .slice(0, 5);
14660
+ if (duplicates.length > 0) {
14661
+ console.error(` Examples of duplicates:`);
14662
+ duplicates.forEach(([ts, count]) => {
14663
+ console.error(` ${ts}: ${count} occurrences`);
14664
+ });
14665
+ }
14666
+ }
14667
+ else {
14668
+ console.log(` ✓ No duplicate bars found`);
14669
+ }
14670
+ }
14671
+ catch (error) {
14672
+ console.error(' ✗ Test 9 failed:', error);
14673
+ }
14674
+ // Test 10: Verify logging accuracy - track what logs say vs actual data
14675
+ console.log('\n[Test 10] Verify logging accuracy');
14676
+ try {
14677
+ const testSymbols = ['AAPL', 'MSFT', 'GOOGL'];
14678
+ // Capture logs
14679
+ const originalLog = console.log;
14680
+ const logs = [];
14681
+ console.log = (...args) => {
14682
+ logs.push(args.join(' '));
14683
+ originalLog(...args);
14684
+ };
14685
+ const response = await marketDataAPI.getHistoricalBars({
14686
+ symbols: testSymbols,
14687
+ timeframe: '5Min',
14688
+ start: new Date('2023-02-27T09:30:00.000Z').toISOString(),
14689
+ end: new Date('2023-02-27T16:00:00.000Z').toISOString(),
14690
+ });
14691
+ console.log = originalLog;
14692
+ const symbolsInResponse = Object.keys(response.bars);
14693
+ console.log(` Requested symbols: ${testSymbols.length}`);
14694
+ console.log(` Actual symbols in response: ${symbolsInResponse.length}`);
14695
+ // Check if any logs mention incorrect symbol counts
14696
+ const logLines = logs.filter((log) => log.includes('symbols') && (log.includes('Starting') || log.includes('complete')));
14697
+ logLines.forEach((line) => {
14698
+ console.log(` Log: ${line}`);
14699
+ });
14700
+ if (symbolsInResponse.length === testSymbols.length) {
14701
+ console.log(` ✓ Response contains correct number of symbols`);
14702
+ }
14703
+ else {
14704
+ console.error(` ✗ FAILED: Expected ${testSymbols.length} symbols, got ${symbolsInResponse.length} (${symbolsInResponse.join(', ')})`);
14705
+ }
14706
+ }
14707
+ catch (error) {
14708
+ console.error(' ✗ Test 10 failed:', error);
14433
14709
  }
14710
+ console.log('\n✓ All Market Data API tests completed!');
14434
14711
  }
14435
14712
  catch (error) {
14436
- console.error('✗ getAssets test failed:', error);
14713
+ console.error('✗ Market Data API test setup failed:', error);
14437
14714
  if (error instanceof Error) {
14438
14715
  console.error('Error message:', error.message);
14439
14716
  console.error('Stack trace:', error.stack);
@@ -14453,5 +14730,6 @@ async function testGetAssetsShortableFilter() {
14453
14730
  // testCryptoMarketData();
14454
14731
  // testGetPortfolioDailyHistory();
14455
14732
  // testWebSocketConnectAndDisconnect();
14456
- testGetAssetsShortableFilter();
14733
+ // testGetAssetsShortableFilter();
14734
+ testMarketDataAPI();
14457
14735
  //# sourceMappingURL=test.js.map