@discomedia/utils 1.0.40 → 1.0.41

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.40",
6
+ "version": "1.0.41",
7
7
  "author": "Disco Media",
8
8
  "description": "Utility functions used in Disco Media apps",
9
9
  "always-build-npm": true,
package/dist/test.js CHANGED
@@ -13731,14 +13731,25 @@ class AlpacaMarketDataAPI extends EventEmitter {
13731
13731
  `Avg Volume: ${avgVolume.toLocaleString()}`);
13732
13732
  }
13733
13733
  /**
13734
- * Get all assets available for trade and data consumption from Alpaca
13735
- * @param params Optional query params: status (e.g. 'active'), asset_class (e.g. 'us_equity', 'crypto')
13734
+ * Get assets available for trade and data consumption from Alpaca
13735
+ * @param params Optional query params
13736
+ * - status: 'active' | 'inactive' (default 'active')
13737
+ * - asset_class: 'us_equity' | 'us_option' | 'crypto' (default 'us_equity')
13738
+ * - shortable: if true (default), filters to assets with shortable=true and easy_to_borrow=true
13736
13739
  * @returns Array of AlpacaAsset objects
13737
13740
  * @see https://docs.alpaca.markets/reference/get-v2-assets-1
13738
13741
  */
13739
13742
  async getAssets(params) {
13740
13743
  // Endpoint: GET /v2/assets
13741
- return this.makeRequest('/assets', 'GET', params, 'api'); // use apiURL
13744
+ const { status = 'active', asset_class = 'us_equity', shortable = true, } = {
13745
+ status: params?.status ?? 'active',
13746
+ asset_class: params?.asset_class ?? 'us_equity',
13747
+ shortable: params?.shortable ?? true,
13748
+ };
13749
+ const assets = await this.makeRequest('/assets', 'GET', { status, asset_class }, 'api');
13750
+ if (!shortable)
13751
+ return assets;
13752
+ return assets.filter((a) => a.shortable === true && a.easy_to_borrow === true);
13742
13753
  }
13743
13754
  /**
13744
13755
  * Get a single asset by symbol or asset_id
@@ -14382,88 +14393,84 @@ class AlpacaMarketDataAPI extends EventEmitter {
14382
14393
  }
14383
14394
  }
14384
14395
  // Export the singleton instance
14385
- AlpacaMarketDataAPI.getInstance();
14396
+ const marketDataAPI = AlpacaMarketDataAPI.getInstance();
14397
+
14398
+ var alpacaMarketDataApi = /*#__PURE__*/Object.freeze({
14399
+ __proto__: null,
14400
+ AlpacaMarketDataAPI: AlpacaMarketDataAPI,
14401
+ marketDataAPI: marketDataAPI
14402
+ });
14386
14403
 
14387
14404
  // Test file for context functionality
14388
- //testOpenRouter();
14389
- //testGetMarketStatus();
14390
- //testCryptoMarketData();
14391
- //testGetPortfolioDailyHistory();
14392
- async function testWebSocketConnectAndDisconnect() {
14393
- console.log('\n--- Testing WebSocket Connect and Disconnect ---');
14405
+ async function testGetAssetsShortableFilter() {
14406
+ console.log('\n--- Testing getAssets params and shortable filter ---');
14394
14407
  const log = (message, options = { type: 'info' }) => {
14395
14408
  log$1(message, { ...options, source: 'Test' });
14396
14409
  };
14397
- // Ensure market data env vars are populated from TRADING_* if needed
14398
- if (!process.env.ALPACA_API_KEY && process.env.ALPACA_TRADING_API_KEY) {
14399
- process.env.ALPACA_API_KEY = process.env.ALPACA_TRADING_API_KEY;
14400
- }
14401
- if (!process.env.ALPACA_SECRET_KEY && process.env.ALPACA_TRADING_SECRET_KEY) {
14402
- process.env.ALPACA_SECRET_KEY = process.env.ALPACA_TRADING_SECRET_KEY;
14403
- }
14404
- if (!process.env.ALPACA_ACCOUNT_TYPE && process.env.ALPACA_TRADING_ACCOUNT_TYPE) {
14405
- process.env.ALPACA_ACCOUNT_TYPE = process.env.ALPACA_TRADING_ACCOUNT_TYPE;
14406
- }
14407
- const apiKey = process.env.ALPACA_API_KEY;
14408
- const secretKey = process.env.ALPACA_SECRET_KEY;
14409
- if (!apiKey || !secretKey) {
14410
- console.error('Missing Alpaca API credentials. Check .env ALPACA_TRADING_API_KEY/SECRET or ALPACA_API_KEY/SECRET.');
14410
+ if (!process.env.ALPACA_API_KEY || !process.env.ALPACA_SECRET_KEY) {
14411
+ console.log('Skipping getAssets test: Missing environment variables (ALPACA_API_KEY, ALPACA_SECRET_KEY)');
14411
14412
  return;
14412
14413
  }
14413
- const md = AlpacaMarketDataAPI.getInstance();
14414
- const symbols = ['SPY', 'TSLA', 'AAPL'];
14415
- // Track received minute bars per symbol
14416
- const barCounts = Object.fromEntries(symbols.map((s) => [s, 0]));
14417
- const firstSeenAt = {};
14418
- // Connect and subscribe to minute bars
14419
- log(`Connecting stock stream and subscribing to minute bars for ${symbols.join(', ')}`);
14420
- md.connectStockStream();
14421
- md.subscribe('stock', { bars: symbols });
14422
- const formatNY = (d) => d.toLocaleString('en-US', { timeZone: 'America/New_York' });
14423
- const onBar = (msg) => {
14424
- if (!symbols.includes(msg.S))
14425
- return;
14426
- barCounts[msg.S] += 1;
14427
- if (!firstSeenAt[msg.S]) {
14428
- firstSeenAt[msg.S] = formatNY(new Date(msg.t));
14414
+ try {
14415
+ const { marketDataAPI } = await Promise.resolve().then(function () { return alpacaMarketDataApi; });
14416
+ // 1) Default call: should filter to shortable && easy_to_borrow and default to active us_equity
14417
+ log('Calling getAssets() with defaults');
14418
+ const filtered = await marketDataAPI.getAssets();
14419
+ console.log(`Received ${filtered.length} filtered assets (default params)`);
14420
+ const allFilteredValid = filtered.every((a) => a.shortable === true && a.easy_to_borrow === true);
14421
+ if (!allFilteredValid) {
14422
+ console.error(' Default getAssets() included assets that are not shortable and easy_to_borrow');
14429
14423
  }
14430
- log(`Bar ${msg.S} o=${msg.o.toFixed(2)} h=${msg.h.toFixed(2)} l=${msg.l.toFixed(2)} c=${msg.c.toFixed(2)} @ ${formatNY(new Date(msg.t))}`, { type: 'debug', symbol: msg.S });
14431
- };
14432
- md.on('stock-b', onBar);
14433
- // Wait until we have at least 2 bars per symbol, or timeout after 3 minutes
14434
- const minBarsPerSymbol = 2;
14435
- const timeoutMs = 3 * 60 * 1000;
14436
- const done = await new Promise((resolve) => {
14437
- const startedAt = Date.now();
14438
- const interval = setInterval(() => {
14439
- const hasAll = symbols.every((s) => barCounts[s] >= minBarsPerSymbol);
14440
- const elapsed = Date.now() - startedAt;
14441
- if (hasAll) {
14442
- clearInterval(interval);
14443
- resolve(true);
14444
- }
14445
- else if (elapsed >= timeoutMs) {
14446
- clearInterval(interval);
14447
- resolve(false);
14448
- }
14449
- }, 1000);
14450
- });
14451
- // Unsubscribe and disconnect
14452
- md.unsubscribe('stock', { bars: symbols });
14453
- md.disconnectStockStream();
14454
- md.removeListener('stock-b', onBar);
14455
- // Report results
14456
- const nowNY = formatNY(new Date());
14457
- console.log('\nStock bars summary:');
14458
- symbols.forEach((s) => {
14459
- console.log(` ${s}: ${barCounts[s]} bars (first at ${firstSeenAt[s] ?? 'n/a'}; finished at ${nowNY})`);
14460
- });
14461
- if (done) {
14462
- console.log('✓ Received minimum bars for all symbols and disconnected.');
14463
- }
14464
- else {
14465
- console.warn('Completed with timeout before receiving minimum bars for all symbols.');
14466
- }
14467
- }
14468
- testWebSocketConnectAndDisconnect();
14424
+ else {
14425
+ console.log('✓ Default getAssets() correctly filtered shortable and easy_to_borrow assets');
14426
+ }
14427
+ // 2) Unfiltered call: shortable=false
14428
+ log('Calling getAssets({ shortable: false })');
14429
+ const unfiltered = await marketDataAPI.getAssets({ shortable: false });
14430
+ console.log(`Received ${unfiltered.length} unfiltered assets`);
14431
+ if (unfiltered.length < filtered.length) {
14432
+ console.warn('Unfiltered list is smaller than filtered. This is unexpected but not fatal.');
14433
+ }
14434
+ else {
14435
+ console.log('✓ Unfiltered list length is >= filtered list length');
14436
+ }
14437
+ // 3) Specific constraints: inactive crypto, not asserting non-empty
14438
+ log("Calling getAssets({ status: 'inactive', asset_class: 'crypto', shortable: false })");
14439
+ const inactiveCrypto = await marketDataAPI.getAssets({ status: 'inactive', asset_class: 'crypto', shortable: false });
14440
+ console.log(`Received ${inactiveCrypto.length} inactive crypto assets`);
14441
+ if (inactiveCrypto.length > 0) {
14442
+ const valid = inactiveCrypto.every((a) => a.class === 'crypto' && a.status === 'inactive');
14443
+ if (!valid) {
14444
+ console.error('✗ Inactive crypto request returned assets with mismatched class or status');
14445
+ }
14446
+ else {
14447
+ console.log('✓ Inactive crypto request returned only crypto with inactive status');
14448
+ }
14449
+ }
14450
+ else {
14451
+ console.log('No inactive crypto assets returned; skipping class/status assertions.');
14452
+ }
14453
+ }
14454
+ catch (error) {
14455
+ console.error('✗ getAssets test failed:', error);
14456
+ if (error instanceof Error) {
14457
+ console.error('Error message:', error.message);
14458
+ console.error('Stack trace:', error.stack);
14459
+ }
14460
+ }
14461
+ }
14462
+ // testGetTradingDate();
14463
+ // testGetTradingStartAndEndDates();
14464
+ // testGetLastFullTradingDate();
14465
+ // testGetMarketOpenClose();
14466
+ // testGetNYTimeZone();
14467
+ // testGetNextMarketDay();
14468
+ // testCountTradingDays();
14469
+ // testGetPreviousMarketDay();
14470
+ // testOpenRouter();
14471
+ // testGetMarketStatus();
14472
+ // testCryptoMarketData();
14473
+ // testGetPortfolioDailyHistory();
14474
+ // testWebSocketConnectAndDisconnect();
14475
+ testGetAssetsShortableFilter();
14469
14476
  //# sourceMappingURL=test.js.map