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