@discomedia/utils 1.0.26 → 1.0.27

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/index.mjs CHANGED
@@ -1370,51 +1370,47 @@ function pLimit(concurrency) {
1370
1370
  let activeCount = 0;
1371
1371
 
1372
1372
  const resumeNext = () => {
1373
+ // Process the next queued function if we're under the concurrency limit
1373
1374
  if (activeCount < concurrency && queue.size > 0) {
1374
- queue.dequeue()();
1375
- // Since `pendingCount` has been decreased by one, increase `activeCount` by one.
1376
1375
  activeCount++;
1376
+ queue.dequeue()();
1377
1377
  }
1378
1378
  };
1379
1379
 
1380
1380
  const next = () => {
1381
1381
  activeCount--;
1382
-
1383
1382
  resumeNext();
1384
1383
  };
1385
1384
 
1386
1385
  const run = async (function_, resolve, arguments_) => {
1386
+ // Execute the function and capture the result promise
1387
1387
  const result = (async () => function_(...arguments_))();
1388
1388
 
1389
+ // Resolve immediately with the promise (don't wait for completion)
1389
1390
  resolve(result);
1390
1391
 
1392
+ // Wait for the function to complete (success or failure)
1393
+ // We catch errors here to prevent unhandled rejections,
1394
+ // but the original promise rejection is preserved for the caller
1391
1395
  try {
1392
1396
  await result;
1393
1397
  } catch {}
1394
1398
 
1399
+ // Decrement active count and process next queued function
1395
1400
  next();
1396
1401
  };
1397
1402
 
1398
1403
  const enqueue = (function_, resolve, arguments_) => {
1399
- // Queue `internalResolve` instead of the `run` function
1400
- // to preserve asynchronous context.
1401
- new Promise(internalResolve => {
1404
+ // Queue the internal resolve function instead of the run function
1405
+ // to preserve the asynchronous execution context.
1406
+ new Promise(internalResolve => { // eslint-disable-line promise/param-names
1402
1407
  queue.enqueue(internalResolve);
1403
- }).then(
1404
- run.bind(undefined, function_, resolve, arguments_),
1405
- );
1406
-
1407
- (async () => {
1408
- // This function needs to wait until the next microtask before comparing
1409
- // `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
1410
- // after the `internalResolve` function is dequeued and called. The comparison in the if-statement
1411
- // needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
1412
- await Promise.resolve();
1413
-
1414
- if (activeCount < concurrency) {
1415
- resumeNext();
1416
- }
1417
- })();
1408
+ }).then(run.bind(undefined, function_, resolve, arguments_)); // eslint-disable-line promise/prefer-await-to-then
1409
+
1410
+ // Start processing immediately if we haven't reached the concurrency limit
1411
+ if (activeCount < concurrency) {
1412
+ resumeNext();
1413
+ }
1418
1414
  };
1419
1415
 
1420
1416
  const generator = (function_, ...arguments_) => new Promise(resolve => {
@@ -1448,6 +1444,12 @@ function pLimit(concurrency) {
1448
1444
  });
1449
1445
  },
1450
1446
  },
1447
+ map: {
1448
+ async value(array, function_) {
1449
+ const promises = array.map(value => this(function_, value));
1450
+ return Promise.all(promises);
1451
+ },
1452
+ },
1451
1453
  });
1452
1454
 
1453
1455
  return generator;
@@ -17109,6 +17111,18 @@ Websocket example
17109
17111
  this.log(`Received trade update: event ${update.event} for an order to ${update.order.side} ${update.order.qty} of ${update.order.symbol}`);
17110
17112
  });
17111
17113
  alpacaAPI.connectWebsocket(); // necessary to connect to the WebSocket
17114
+
17115
+ Portfolio History examples
17116
+ // Get standard portfolio history
17117
+ const portfolioHistory = await alpacaAPI.getPortfolioHistory({
17118
+ timeframe: '1D',
17119
+ period: '1M'
17120
+ });
17121
+
17122
+ // Get daily portfolio history with current day included (if available from hourly data)
17123
+ const dailyHistory = await alpacaAPI.getPortfolioDailyHistory({
17124
+ period: '1M'
17125
+ });
17112
17126
  */
17113
17127
  class AlpacaTradingAPI {
17114
17128
  static new(credentials) {
@@ -17808,6 +17822,57 @@ class AlpacaTradingAPI {
17808
17822
  const response = await this.makeRequest(`/account/portfolio/history?${queryParams.toString()}`);
17809
17823
  return response;
17810
17824
  }
17825
+ /**
17826
+ * Get portfolio daily history for the account, ensuring the most recent day is included
17827
+ * by combining daily and hourly history if needed.
17828
+ *
17829
+ * This function performs two API calls:
17830
+ * 1. Retrieves daily portfolio history
17831
+ * 2. Retrieves hourly portfolio history to check for more recent data
17832
+ *
17833
+ * If hourly history has timestamps more recent than the last timestamp in daily history,
17834
+ * it appends one additional day to the daily history using the most recent hourly values.
17835
+ *
17836
+ * @param params Parameters for the portfolio history request (same as getPortfolioHistory except timeframe is forced to '1D')
17837
+ * @returns Portfolio history data with daily timeframe, including the most recent day if available from hourly data
17838
+ */
17839
+ async getPortfolioDailyHistory(params) {
17840
+ // Get daily history
17841
+ const dailyParams = { ...params, timeframe: '1D' };
17842
+ const dailyHistory = await this.getPortfolioHistory(dailyParams);
17843
+ // Get hourly history for the last day to check for more recent data
17844
+ const hourlyParams = { timeframe: '1H', period: '1D' };
17845
+ const hourlyHistory = await this.getPortfolioHistory(hourlyParams);
17846
+ // If no hourly history, return daily as-is
17847
+ if (!hourlyHistory.timestamp || hourlyHistory.timestamp.length === 0) {
17848
+ return dailyHistory;
17849
+ }
17850
+ // Get the last timestamp from daily history
17851
+ const lastDailyTimestamp = dailyHistory.timestamp[dailyHistory.timestamp.length - 1];
17852
+ // Check if hourly history has more recent data
17853
+ const recentHourlyData = hourlyHistory.timestamp
17854
+ .map((timestamp, index) => ({ timestamp, index }))
17855
+ .filter(({ timestamp }) => timestamp > lastDailyTimestamp);
17856
+ // If no more recent hourly data, return daily history as-is
17857
+ if (recentHourlyData.length === 0) {
17858
+ return dailyHistory;
17859
+ }
17860
+ // Get the most recent hourly data point
17861
+ const mostRecentHourly = recentHourlyData[recentHourlyData.length - 1];
17862
+ const mostRecentIndex = mostRecentHourly.index;
17863
+ // Calculate the timestamp for the new daily entry (most recent day + 1 day worth of seconds)
17864
+ const oneDayInSeconds = 24 * 60 * 60;
17865
+ const newDailyTimestamp = mostRecentHourly.timestamp + oneDayInSeconds;
17866
+ // Create a new daily history entry with the most recent hourly values
17867
+ const updatedDailyHistory = {
17868
+ ...dailyHistory,
17869
+ timestamp: [...dailyHistory.timestamp, newDailyTimestamp],
17870
+ equity: [...dailyHistory.equity, hourlyHistory.equity[mostRecentIndex]],
17871
+ profit_loss: [...dailyHistory.profit_loss, hourlyHistory.profit_loss[mostRecentIndex]],
17872
+ profit_loss_pct: [...dailyHistory.profit_loss_pct, hourlyHistory.profit_loss_pct[mostRecentIndex]],
17873
+ };
17874
+ return updatedDailyHistory;
17875
+ }
17811
17876
  /**
17812
17877
  * Get option contracts based on specified parameters
17813
17878
  * @param params Parameters to filter option contracts