@adaptic/utils 0.0.930 → 0.0.932
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
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
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.
|