@adaptic/utils 0.0.930 → 0.0.931

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
@@ -5447,8 +5447,49 @@ async function validateMassiveApiKey(apiKey) {
5447
5447
 
5448
5448
  const DEFAULT_CURRENCY = "USD";
5449
5449
  const DEFAULT_FEED = "sip";
5450
+ /**
5451
+ * Known quote currencies that signal a crypto pair when found as a suffix.
5452
+ * E.g. "BTCUSD" → crypto, "AAPL" → equity.
5453
+ */
5454
+ const CRYPTO_QUOTE_CURRENCIES = ["USD", "USDT", "USDC", "BTC"];
5455
+ /**
5456
+ * Detect whether a symbol looks like a crypto pair (e.g. "BTCUSD", "DOGEUSD",
5457
+ * "BTC/USD"). Equity tickers never end with these suffixes because Alpaca
5458
+ * equity symbols are plain tickers without a quote currency.
5459
+ */
5460
+ function isCryptoSymbol(symbol) {
5461
+ if (symbol.includes("/"))
5462
+ return true;
5463
+ const upper = symbol.toUpperCase();
5464
+ return CRYPTO_QUOTE_CURRENCIES.some((qc) => {
5465
+ if (!upper.endsWith(qc))
5466
+ return false;
5467
+ const base = upper.slice(0, -qc.length);
5468
+ // Require at least 2-char base (e.g. "BT" from "BTUSD" is valid, "U" from "UUSD" is unlikely but acceptable)
5469
+ return base.length >= 2;
5470
+ });
5471
+ }
5472
+ /**
5473
+ * Convert a flat crypto symbol (e.g. "DOGEUSD") to the slash-separated
5474
+ * format the Alpaca crypto data API expects ("DOGE/USD").
5475
+ */
5476
+ function normalizeCryptoSymbolForApi(symbol) {
5477
+ if (symbol.includes("/"))
5478
+ return symbol.toUpperCase();
5479
+ const upper = symbol.toUpperCase();
5480
+ for (const qc of CRYPTO_QUOTE_CURRENCIES) {
5481
+ if (upper.endsWith(qc)) {
5482
+ const base = upper.slice(0, -qc.length);
5483
+ if (base.length >= 2)
5484
+ return `${base}/${qc}`;
5485
+ }
5486
+ }
5487
+ return `${upper}/USD`;
5488
+ }
5450
5489
  /**
5451
5490
  * Get the most recent quotes for requested symbols.
5491
+ * Automatically routes crypto symbols to the crypto data API and equity
5492
+ * symbols to the stocks data API, then merges the results.
5452
5493
  * @param auth - The authentication details for Alpaca
5453
5494
  * @param params - Parameters including symbols array, optional feed and currency
5454
5495
  * @returns Latest quote data for each symbol
@@ -5466,16 +5507,67 @@ async function getLatestQuotes$1(auth, params) {
5466
5507
  currency: currency || DEFAULT_CURRENCY,
5467
5508
  };
5468
5509
  }
5469
- const queryParams = new URLSearchParams();
5470
- queryParams.append("symbols", symbols.join(","));
5471
- queryParams.append("feed", feed || DEFAULT_FEED);
5472
- queryParams.append("currency", currency || DEFAULT_CURRENCY);
5473
- return makeRequest(auth, {
5474
- endpoint: "/v2/stocks/quotes/latest",
5475
- method: "GET",
5476
- queryString: `?${queryParams.toString()}`,
5477
- apiBaseUrl: MARKET_DATA_API.STOCKS.replace("/v2", ""),
5478
- });
5510
+ // Partition symbols into crypto and equity groups
5511
+ const equitySymbols = [];
5512
+ const cryptoSymbols = [];
5513
+ for (const sym of symbols) {
5514
+ if (isCryptoSymbol(sym)) {
5515
+ cryptoSymbols.push(sym);
5516
+ }
5517
+ else {
5518
+ equitySymbols.push(sym);
5519
+ }
5520
+ }
5521
+ // Fetch from both endpoints concurrently
5522
+ const results = {
5523
+ quotes: {},
5524
+ currency: currency || DEFAULT_CURRENCY,
5525
+ };
5526
+ const fetchPromises = [];
5527
+ // Equity quotes from stocks API
5528
+ if (equitySymbols.length > 0) {
5529
+ const equityPromise = (async () => {
5530
+ const queryParams = new URLSearchParams();
5531
+ queryParams.append("symbols", equitySymbols.join(","));
5532
+ queryParams.append("feed", feed || DEFAULT_FEED);
5533
+ queryParams.append("currency", currency || DEFAULT_CURRENCY);
5534
+ const response = await makeRequest(auth, {
5535
+ endpoint: "/v2/stocks/quotes/latest",
5536
+ method: "GET",
5537
+ queryString: `?${queryParams.toString()}`,
5538
+ apiBaseUrl: MARKET_DATA_API.STOCKS.replace("/v2", ""),
5539
+ });
5540
+ Object.assign(results.quotes, response.quotes);
5541
+ })();
5542
+ fetchPromises.push(equityPromise);
5543
+ }
5544
+ // Crypto quotes from crypto API
5545
+ if (cryptoSymbols.length > 0) {
5546
+ const cryptoPromise = (async () => {
5547
+ // Alpaca crypto data API expects slash-separated symbols (BTC/USD)
5548
+ const normalizedMap = new Map();
5549
+ for (const sym of cryptoSymbols) {
5550
+ normalizedMap.set(normalizeCryptoSymbolForApi(sym), sym);
5551
+ }
5552
+ const normalizedSymbols = [...normalizedMap.keys()];
5553
+ const queryParams = new URLSearchParams();
5554
+ queryParams.append("symbols", normalizedSymbols.join(","));
5555
+ const response = await makeRequest(auth, {
5556
+ endpoint: "/v1beta3/crypto/us/latest/quotes",
5557
+ method: "GET",
5558
+ queryString: `?${queryParams.toString()}`,
5559
+ apiBaseUrl: MARKET_DATA_API.CRYPTO.replace("/v1beta3", ""),
5560
+ });
5561
+ // Map response keys back to original symbol format
5562
+ for (const [normalizedSym, quote] of Object.entries(response.quotes)) {
5563
+ const originalSym = normalizedMap.get(normalizedSym) ?? normalizedSym;
5564
+ results.quotes[originalSym] = quote;
5565
+ }
5566
+ })();
5567
+ fetchPromises.push(cryptoPromise);
5568
+ }
5569
+ await Promise.all(fetchPromises);
5570
+ return results;
5479
5571
  }
5480
5572
  /**
5481
5573
  * Fetches news articles from Alpaca API for specified symbols.