@discomedia/utils 1.0.32 → 1.0.33

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.
@@ -13882,6 +13882,7 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
13882
13882
  dataURL;
13883
13883
  apiURL;
13884
13884
  v1beta1url;
13885
+ v1beta3url;
13885
13886
  stockStreamUrl = 'wss://stream.data.alpaca.markets/v2/sip'; // production values
13886
13887
  optionStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/options'; // production values
13887
13888
  stockWs = null;
@@ -13924,6 +13925,7 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
13924
13925
  ? 'https://paper-api.alpaca.markets/v2'
13925
13926
  : 'https://api.alpaca.markets/v2'; // used by some, e.g. getAssets
13926
13927
  this.v1beta1url = 'https://data.alpaca.markets/v1beta1'; // used for options endpoints
13928
+ this.v1beta3url = 'https://data.alpaca.markets/v1beta3'; // used for crypto endpoints
13927
13929
  this.setMode('production'); // sets stockStreamUrl and optionStreamUrl
13928
13930
  this.headers = {
13929
13931
  'APCA-API-KEY-ID': process.env.ALPACA_API_KEY,
@@ -14061,7 +14063,9 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
14061
14063
  }
14062
14064
  }
14063
14065
  async makeRequest(endpoint, method = 'GET', params, baseUrlName = 'data') {
14064
- const baseUrl = baseUrlName === 'data' ? this.dataURL : baseUrlName === 'api' ? this.apiURL : this.v1beta1url;
14066
+ const baseUrl = baseUrlName === 'data' ? this.dataURL :
14067
+ baseUrlName === 'api' ? this.apiURL :
14068
+ baseUrlName === 'v1beta1' ? this.v1beta1url : this.v1beta3url;
14065
14069
  const url = new URL(`${baseUrl}${endpoint}`);
14066
14070
  try {
14067
14071
  if (params) {
@@ -14769,6 +14773,253 @@ class AlpacaMarketDataAPI extends require$$0$3.EventEmitter {
14769
14773
  }
14770
14774
  return newsArticles;
14771
14775
  }
14776
+ // ===== CRYPTO MARKET DATA METHODS =====
14777
+ /**
14778
+ * Get historical OHLCV bars for crypto symbols
14779
+ * Automatically handles pagination to fetch all available data
14780
+ * @param params Parameters for crypto historical bars request
14781
+ * @returns Historical bars data with all pages combined
14782
+ */
14783
+ async getCryptoHistoricalBars(params) {
14784
+ const symbols = params.symbols;
14785
+ const symbolsStr = symbols.join(',');
14786
+ let allBars = {};
14787
+ let pageToken = null;
14788
+ let hasMorePages = true;
14789
+ let totalBarsCount = 0;
14790
+ let pageCount = 0;
14791
+ // Initialize bar arrays for each symbol
14792
+ symbols.forEach((symbol) => {
14793
+ allBars[symbol] = [];
14794
+ });
14795
+ log(`Starting crypto historical bars fetch for ${symbols.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
14796
+ while (hasMorePages) {
14797
+ pageCount++;
14798
+ const requestParams = {
14799
+ ...params,
14800
+ symbols: symbolsStr,
14801
+ ...(pageToken && { page_token: pageToken }),
14802
+ };
14803
+ const response = await this.makeRequest('/crypto/us/bars', 'GET', requestParams, 'v1beta3');
14804
+ if (!response.bars) {
14805
+ log(`No crypto bars data found in response for ${symbols.length} symbols`, { type: 'warn' });
14806
+ break;
14807
+ }
14808
+ // Combine bars for each symbol
14809
+ let pageBarsCount = 0;
14810
+ Object.entries(response.bars).forEach(([symbol, bars]) => {
14811
+ if (bars && bars.length > 0) {
14812
+ allBars[symbol] = [...allBars[symbol], ...bars];
14813
+ pageBarsCount += bars.length;
14814
+ }
14815
+ });
14816
+ totalBarsCount += pageBarsCount;
14817
+ pageToken = response.next_page_token || null;
14818
+ hasMorePages = !!pageToken;
14819
+ log(`Page ${pageCount}: Fetched ${pageBarsCount.toLocaleString()} crypto bars (total: ${totalBarsCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
14820
+ // Prevent infinite loops
14821
+ if (pageCount > 1000) {
14822
+ log(`Stopping crypto bars pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
14823
+ break;
14824
+ }
14825
+ }
14826
+ log(`Crypto historical bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages`, { type: 'info' });
14827
+ return {
14828
+ bars: allBars,
14829
+ next_page_token: null, // Always null since we fetch all pages
14830
+ };
14831
+ }
14832
+ /**
14833
+ * Get the most recent minute bar for requested crypto symbols
14834
+ * @param symbols Array of crypto symbols to query
14835
+ * @returns Latest bar data for each symbol
14836
+ */
14837
+ async getCryptoLatestBars(symbols) {
14838
+ return this.makeRequest('/crypto/us/latest/bars', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
14839
+ }
14840
+ /**
14841
+ * Get historical quotes for crypto symbols
14842
+ * Automatically handles pagination to fetch all available data
14843
+ * @param params Parameters for crypto historical quotes request
14844
+ * @returns Historical quotes data with all pages combined
14845
+ */
14846
+ async getCryptoHistoricalQuotes(params) {
14847
+ const symbols = params.symbols;
14848
+ const symbolsStr = symbols.join(',');
14849
+ let allQuotes = {};
14850
+ let pageToken = null;
14851
+ let hasMorePages = true;
14852
+ let totalQuotesCount = 0;
14853
+ let pageCount = 0;
14854
+ // Initialize quotes arrays for each symbol
14855
+ symbols.forEach((symbol) => {
14856
+ allQuotes[symbol] = [];
14857
+ });
14858
+ log(`Starting crypto historical quotes fetch for ${symbols.length} symbols (${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
14859
+ while (hasMorePages) {
14860
+ pageCount++;
14861
+ const requestParams = {
14862
+ ...params,
14863
+ symbols: symbolsStr,
14864
+ ...(pageToken && { page_token: pageToken }),
14865
+ };
14866
+ const response = await this.makeRequest('/crypto/us/quotes', 'GET', requestParams, 'v1beta3');
14867
+ if (!response.quotes) {
14868
+ log(`No crypto quotes data found in response for ${symbols.length} symbols`, { type: 'warn' });
14869
+ break;
14870
+ }
14871
+ // Combine quotes for each symbol
14872
+ let pageQuotesCount = 0;
14873
+ Object.entries(response.quotes).forEach(([symbol, quotes]) => {
14874
+ if (quotes && quotes.length > 0) {
14875
+ allQuotes[symbol] = [...allQuotes[symbol], ...quotes];
14876
+ pageQuotesCount += quotes.length;
14877
+ }
14878
+ });
14879
+ totalQuotesCount += pageQuotesCount;
14880
+ pageToken = response.next_page_token || null;
14881
+ hasMorePages = !!pageToken;
14882
+ log(`Page ${pageCount}: Fetched ${pageQuotesCount.toLocaleString()} crypto quotes (total: ${totalQuotesCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
14883
+ // Prevent infinite loops
14884
+ if (pageCount > 1000) {
14885
+ log(`Stopping crypto quotes pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
14886
+ break;
14887
+ }
14888
+ }
14889
+ log(`Crypto historical quotes fetch complete: ${totalQuotesCount.toLocaleString()} total quotes across ${pageCount} pages`, { type: 'info' });
14890
+ return {
14891
+ quotes: allQuotes,
14892
+ next_page_token: null, // Always null since we fetch all pages
14893
+ };
14894
+ }
14895
+ /**
14896
+ * Get the most recent quotes for requested crypto symbols
14897
+ * @param symbols Array of crypto symbols to query
14898
+ * @returns Latest quote data for each symbol
14899
+ */
14900
+ async getCryptoLatestQuotes(symbols) {
14901
+ if (!symbols || symbols.length === 0) {
14902
+ log('No symbols provided to getCryptoLatestQuotes, returning empty response', { type: 'warn' });
14903
+ return { quotes: {} };
14904
+ }
14905
+ return this.makeRequest('/crypto/us/latest/quotes', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
14906
+ }
14907
+ /**
14908
+ * Get historical trades for crypto symbols
14909
+ * Automatically handles pagination to fetch all available data
14910
+ * @param params Parameters for crypto historical trades request
14911
+ * @returns Historical trades data with all pages combined
14912
+ */
14913
+ async getCryptoHistoricalTrades(params) {
14914
+ const symbols = params.symbols;
14915
+ const symbolsStr = symbols.join(',');
14916
+ let allTrades = {};
14917
+ let pageToken = null;
14918
+ let hasMorePages = true;
14919
+ let totalTradesCount = 0;
14920
+ let pageCount = 0;
14921
+ // Initialize trades arrays for each symbol
14922
+ symbols.forEach((symbol) => {
14923
+ allTrades[symbol] = [];
14924
+ });
14925
+ log(`Starting crypto historical trades fetch for ${symbols.length} symbols (${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
14926
+ while (hasMorePages) {
14927
+ pageCount++;
14928
+ const requestParams = {
14929
+ ...params,
14930
+ symbols: symbolsStr,
14931
+ ...(pageToken && { page_token: pageToken }),
14932
+ };
14933
+ const response = await this.makeRequest('/crypto/us/trades', 'GET', requestParams, 'v1beta3');
14934
+ if (!response.trades) {
14935
+ log(`No crypto trades data found in response for ${symbols.length} symbols`, { type: 'warn' });
14936
+ break;
14937
+ }
14938
+ // Combine trades for each symbol
14939
+ let pageTradesCount = 0;
14940
+ Object.entries(response.trades).forEach(([symbol, trades]) => {
14941
+ if (trades && trades.length > 0) {
14942
+ allTrades[symbol] = [...allTrades[symbol], ...trades];
14943
+ pageTradesCount += trades.length;
14944
+ }
14945
+ });
14946
+ totalTradesCount += pageTradesCount;
14947
+ pageToken = response.next_page_token || null;
14948
+ hasMorePages = !!pageToken;
14949
+ log(`Page ${pageCount}: Fetched ${pageTradesCount.toLocaleString()} crypto trades (total: ${totalTradesCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
14950
+ // Prevent infinite loops
14951
+ if (pageCount > 1000) {
14952
+ log(`Stopping crypto trades pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
14953
+ break;
14954
+ }
14955
+ }
14956
+ log(`Crypto historical trades fetch complete: ${totalTradesCount.toLocaleString()} total trades across ${pageCount} pages`, { type: 'info' });
14957
+ return {
14958
+ trades: allTrades,
14959
+ next_page_token: null, // Always null since we fetch all pages
14960
+ };
14961
+ }
14962
+ /**
14963
+ * Get the most recent trades for requested crypto symbols
14964
+ * @param symbols Array of crypto symbols to query
14965
+ * @returns Latest trade data for each symbol
14966
+ */
14967
+ async getCryptoLatestTrades(symbols) {
14968
+ if (!symbols || symbols.length === 0) {
14969
+ log('No symbols provided to getCryptoLatestTrades, returning empty response', { type: 'warn' });
14970
+ return { trades: {} };
14971
+ }
14972
+ return this.makeRequest('/crypto/us/latest/trades', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
14973
+ }
14974
+ /**
14975
+ * Get snapshots for crypto symbols
14976
+ * Returns the latest trade, latest quote, latest minute bar, latest daily bar, and previous daily bar data
14977
+ * @param symbols Array of crypto symbols to query
14978
+ * @returns Snapshot data for each symbol
14979
+ */
14980
+ async getCryptoSnapshots(symbols) {
14981
+ if (!symbols || symbols.length === 0) {
14982
+ log('No symbols provided to getCryptoSnapshots, returning empty response', { type: 'warn' });
14983
+ return { snapshots: {} };
14984
+ }
14985
+ return this.makeRequest('/crypto/us/snapshots', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
14986
+ }
14987
+ /**
14988
+ * Get the latest orderbook for requested crypto symbols
14989
+ * @param symbols Array of crypto symbols to query
14990
+ * @returns Latest orderbook data for each symbol
14991
+ */
14992
+ async getCryptoLatestOrderbooks(symbols) {
14993
+ if (!symbols || symbols.length === 0) {
14994
+ log('No symbols provided to getCryptoLatestOrderbooks, returning empty response', { type: 'warn' });
14995
+ return { orderbooks: {} };
14996
+ }
14997
+ return this.makeRequest('/crypto/us/latest/orderbooks', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
14998
+ }
14999
+ /**
15000
+ * Analyzes an array of crypto bars and returns a summary string
15001
+ * @param bars Array of crypto bars to analyze
15002
+ * @returns A string summarizing the crypto price data
15003
+ */
15004
+ static analyzeCryptoBars(bars) {
15005
+ if (!bars || bars.length === 0) {
15006
+ return 'No crypto price data available';
15007
+ }
15008
+ const firstBar = bars[0];
15009
+ const lastBar = bars[bars.length - 1];
15010
+ const priceChange = lastBar.c - firstBar.o;
15011
+ const percentChange = (priceChange / firstBar.o) * 100;
15012
+ const volumeChange = lastBar.v - firstBar.v;
15013
+ const percentVolumeChange = firstBar.v > 0 ? (volumeChange / firstBar.v) * 100 : 0;
15014
+ const high = Math.max(...bars.map((bar) => bar.h));
15015
+ const low = Math.min(...bars.map((bar) => bar.l));
15016
+ const totalVolume = bars.reduce((sum, bar) => sum + bar.v, 0);
15017
+ const avgVolume = totalVolume / bars.length;
15018
+ return (`Crypto Price: $${firstBar.o.toFixed(6)} -> $${lastBar.c.toFixed(6)} (${percentChange.toFixed(2)}%), ` +
15019
+ `Volume: ${firstBar.v.toLocaleString()} -> ${lastBar.v.toLocaleString()} (${percentVolumeChange.toFixed(2)}%), ` +
15020
+ `High: $${high.toFixed(6)}, Low: $${low.toFixed(6)}, ` +
15021
+ `Avg Volume: ${avgVolume.toLocaleString()}`);
15022
+ }
14772
15023
  }
14773
15024
  // Export the singleton instance
14774
15025
  const marketDataAPI = AlpacaMarketDataAPI.getInstance();