@discomedia/utils 1.0.26 → 1.0.28

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