@adaptic/utils 0.0.959 → 0.0.961
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 +156 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +157 -26
- package/dist/index.mjs.map +1 -1
- package/dist/types/adaptic.d.ts +9 -0
- package/dist/types/adaptic.d.ts.map +1 -1
- package/dist/types/alpaca/legacy/positions.d.ts +2 -2
- package/dist/types/alpaca/legacy/positions.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -6
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/massive.d.ts.map +1 -1
- package/dist/types/price-utils.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import adaptic$1, { setTokenProvider, getApolloClient } from '@adaptic/backend-legacy';
|
|
1
|
+
import adaptic$1, { stopClient, setTokenProvider, getApolloClient } from '@adaptic/backend-legacy';
|
|
2
2
|
import { format, sub, set, add, startOfDay, endOfDay, isBefore, differenceInMilliseconds } from 'date-fns';
|
|
3
3
|
import { formatInTimeZone, toZonedTime, fromZonedTime } from 'date-fns-tz';
|
|
4
4
|
import require$$0$4, { EventEmitter } from 'events';
|
|
@@ -307,6 +307,22 @@ const fetchAssetOverview = async (symbol) => {
|
|
|
307
307
|
};
|
|
308
308
|
}
|
|
309
309
|
};
|
|
310
|
+
/**
|
|
311
|
+
* Gracefully disconnects the shared Apollo client and releases its resources.
|
|
312
|
+
* Call this during process shutdown to close keep-alive HTTP connections
|
|
313
|
+
* and drain in-flight operations.
|
|
314
|
+
*
|
|
315
|
+
* After calling `disconnectClient()`, the next call to `getSharedApolloClient()`
|
|
316
|
+
* will create a fresh instance.
|
|
317
|
+
*/
|
|
318
|
+
const disconnectClient = () => {
|
|
319
|
+
if (apolloClientInstance) {
|
|
320
|
+
apolloClientInstance = null;
|
|
321
|
+
getLogger().info("[adaptic] Shared Apollo client reference cleared");
|
|
322
|
+
}
|
|
323
|
+
// Delegate to backend-legacy's stopClient() which calls .stop() on the underlying singleton
|
|
324
|
+
stopClient();
|
|
325
|
+
};
|
|
310
326
|
|
|
311
327
|
const ANSI_BACKGROUND_OFFSET = 10;
|
|
312
328
|
|
|
@@ -5555,7 +5571,7 @@ const CRYPTO_QUOTE_CURRENCIES$1 = ["USD", "USDT", "USDC", "BTC"];
|
|
|
5555
5571
|
* "BTC/USD"). Equity tickers never end with these suffixes because Alpaca
|
|
5556
5572
|
* equity symbols are plain tickers without a quote currency.
|
|
5557
5573
|
*/
|
|
5558
|
-
function isCryptoSymbol$
|
|
5574
|
+
function isCryptoSymbol$2(symbol) {
|
|
5559
5575
|
if (symbol.includes("/"))
|
|
5560
5576
|
return true;
|
|
5561
5577
|
const upper = symbol.toUpperCase();
|
|
@@ -5609,7 +5625,7 @@ async function getLatestQuotes$1(auth, params) {
|
|
|
5609
5625
|
const equitySymbols = [];
|
|
5610
5626
|
const cryptoSymbols = [];
|
|
5611
5627
|
for (const sym of symbols) {
|
|
5612
|
-
if (isCryptoSymbol$
|
|
5628
|
+
if (isCryptoSymbol$2(sym)) {
|
|
5613
5629
|
cryptoSymbols.push(sym);
|
|
5614
5630
|
}
|
|
5615
5631
|
else {
|
|
@@ -5817,7 +5833,7 @@ const CRYPTO_QUOTE_CURRENCIES = ["USD", "USDT", "USDC", "BTC"];
|
|
|
5817
5833
|
* "BTC/USD"). Equity tickers never end with these suffixes because Alpaca
|
|
5818
5834
|
* equity symbols are plain tickers without a quote currency.
|
|
5819
5835
|
*/
|
|
5820
|
-
function isCryptoSymbol(symbol) {
|
|
5836
|
+
function isCryptoSymbol$1(symbol) {
|
|
5821
5837
|
if (symbol.includes("/"))
|
|
5822
5838
|
return true;
|
|
5823
5839
|
const upper = symbol.toUpperCase();
|
|
@@ -5869,7 +5885,7 @@ async function fetchPosition(auth, symbolOrAssetId) {
|
|
|
5869
5885
|
const { APIKey, APISecret, type } = await validateAuth(auth);
|
|
5870
5886
|
const apiBaseUrl = getTradingApiUrl(type);
|
|
5871
5887
|
// Normalize crypto symbols for Alpaca API compatibility
|
|
5872
|
-
const normalizedSymbol = isCryptoSymbol(symbolOrAssetId)
|
|
5888
|
+
const normalizedSymbol = isCryptoSymbol$1(symbolOrAssetId)
|
|
5873
5889
|
? symbolOrAssetId.replace(/[-/]/g, "")
|
|
5874
5890
|
: symbolOrAssetId;
|
|
5875
5891
|
const response = await fetch(`${apiBaseUrl}/positions/${normalizedSymbol}`, {
|
|
@@ -5906,7 +5922,7 @@ async function fetchPosition(auth, symbolOrAssetId) {
|
|
|
5906
5922
|
* @param auth - The authentication details for Alpaca
|
|
5907
5923
|
* @param symbolOrAssetId - The symbol or asset ID of the position to close
|
|
5908
5924
|
* @param params - Optional parameters for closing the position
|
|
5909
|
-
* @returns The order created to close the position
|
|
5925
|
+
* @returns The order created to close the position, or null if position doesn't exist (404)
|
|
5910
5926
|
*/
|
|
5911
5927
|
async function closePosition$1(auth, symbolOrAssetId, params) {
|
|
5912
5928
|
try {
|
|
@@ -5915,7 +5931,7 @@ async function closePosition$1(auth, symbolOrAssetId, params) {
|
|
|
5915
5931
|
// Normalize crypto symbols for Alpaca API compatibility.
|
|
5916
5932
|
// Alpaca positions endpoint rejects hyphenated format (e.g., "SOL-USD")
|
|
5917
5933
|
// but accepts concatenated form (e.g., "SOLUSD").
|
|
5918
|
-
const normalizedSymbol = isCryptoSymbol(symbolOrAssetId)
|
|
5934
|
+
const normalizedSymbol = isCryptoSymbol$1(symbolOrAssetId)
|
|
5919
5935
|
? symbolOrAssetId.replace(/[-/]/g, "")
|
|
5920
5936
|
: symbolOrAssetId;
|
|
5921
5937
|
const useLimitOrder = params?.useLimitOrder ?? false;
|
|
@@ -5931,25 +5947,34 @@ async function closePosition$1(auth, symbolOrAssetId, params) {
|
|
|
5931
5947
|
// For crypto, Alpaca stores orders under "SOL/USD" (slash format) but the
|
|
5932
5948
|
// symbols filter may not match across formats reliably. Fetch all open
|
|
5933
5949
|
// orders without symbol filter and match client-side via normalization.
|
|
5934
|
-
const openOrders = isCryptoSymbol(symbolOrAssetId)
|
|
5950
|
+
const openOrders = isCryptoSymbol$1(symbolOrAssetId)
|
|
5935
5951
|
? await getOrders$1(auth, { status: "open" })
|
|
5936
|
-
: await getOrders$1(auth, {
|
|
5952
|
+
: await getOrders$1(auth, {
|
|
5953
|
+
status: "open",
|
|
5954
|
+
symbols: [normalizedSymbol],
|
|
5955
|
+
});
|
|
5937
5956
|
let cancelledCount = 0;
|
|
5938
5957
|
for (const order of openOrders) {
|
|
5939
5958
|
const orderSymbolNorm = order.symbol.replace(/[-/]/g, "");
|
|
5940
5959
|
if (orderSymbolNorm === normalizedSymbol) {
|
|
5941
|
-
getLogger().info(`Cancelling order ${order.id} (${order.symbol}) for ${normalizedSymbol}`, {
|
|
5960
|
+
getLogger().info(`Cancelling order ${order.id} (${order.symbol}) for ${normalizedSymbol}`, {
|
|
5961
|
+
account: auth.adapticAccountId || "direct",
|
|
5962
|
+
symbol: normalizedSymbol,
|
|
5963
|
+
});
|
|
5942
5964
|
await cancelOrder$1(auth, order.id);
|
|
5943
5965
|
cancelledCount++;
|
|
5944
5966
|
}
|
|
5945
5967
|
}
|
|
5946
5968
|
if (cancelledCount > 0) {
|
|
5947
|
-
getLogger().info(`Cancelled ${cancelledCount} open orders for ${normalizedSymbol}`, {
|
|
5969
|
+
getLogger().info(`Cancelled ${cancelledCount} open orders for ${normalizedSymbol}`, {
|
|
5970
|
+
account: auth.adapticAccountId || "direct",
|
|
5971
|
+
symbol: normalizedSymbol,
|
|
5972
|
+
});
|
|
5948
5973
|
}
|
|
5949
5974
|
}
|
|
5950
5975
|
// Crypto positions cannot use limit orders with SIP quotes or time_in_force="day".
|
|
5951
5976
|
// Use direct DELETE (market order) for crypto regardless of useLimitOrder flag.
|
|
5952
|
-
if (useLimitOrder && !isCryptoSymbol(symbolOrAssetId)) {
|
|
5977
|
+
if (useLimitOrder && !isCryptoSymbol$1(symbolOrAssetId)) {
|
|
5953
5978
|
// Attempt limit order closure; if quotes are unavailable (after-hours, IEX gaps),
|
|
5954
5979
|
// fall back to market order (DELETE) so the position still gets closed.
|
|
5955
5980
|
try {
|
|
@@ -5999,7 +6024,9 @@ async function closePosition$1(auth, symbolOrAssetId, params) {
|
|
|
5999
6024
|
catch (limitOrderError) {
|
|
6000
6025
|
// Quote unavailable or invalid price — fall back to market order (DELETE)
|
|
6001
6026
|
// so the position still gets closed rather than leaving it open
|
|
6002
|
-
const errMsg = limitOrderError instanceof Error
|
|
6027
|
+
const errMsg = limitOrderError instanceof Error
|
|
6028
|
+
? limitOrderError.message
|
|
6029
|
+
: String(limitOrderError);
|
|
6003
6030
|
getLogger().warn(`Limit order closure failed for ${symbolOrAssetId} (${errMsg}), falling back to market order`, {
|
|
6004
6031
|
account: auth.adapticAccountId || "direct",
|
|
6005
6032
|
symbol: symbolOrAssetId,
|
|
@@ -6010,8 +6037,11 @@ async function closePosition$1(auth, symbolOrAssetId, params) {
|
|
|
6010
6037
|
}
|
|
6011
6038
|
// Market order (DELETE) path — used when limit orders are not requested,
|
|
6012
6039
|
// for crypto symbols, or as a fallback when limit order quotes are unavailable
|
|
6013
|
-
if (isCryptoSymbol(symbolOrAssetId)) {
|
|
6014
|
-
getLogger().info(`Closing crypto position ${normalizedSymbol} via market order (DELETE endpoint)`, {
|
|
6040
|
+
if (isCryptoSymbol$1(symbolOrAssetId)) {
|
|
6041
|
+
getLogger().info(`Closing crypto position ${normalizedSymbol} via market order (DELETE endpoint)`, {
|
|
6042
|
+
account: auth.adapticAccountId || "direct",
|
|
6043
|
+
symbol: normalizedSymbol,
|
|
6044
|
+
});
|
|
6015
6045
|
}
|
|
6016
6046
|
const queryParams = new URLSearchParams();
|
|
6017
6047
|
if (params?.qty !== undefined) {
|
|
@@ -6031,6 +6061,15 @@ async function closePosition$1(auth, symbolOrAssetId, params) {
|
|
|
6031
6061
|
});
|
|
6032
6062
|
if (!response.ok) {
|
|
6033
6063
|
const errorText = await response.text();
|
|
6064
|
+
// Handle 404 (position not found) gracefully - position may have already been closed
|
|
6065
|
+
// or never existed. Return null instead of throwing to allow callers to handle this.
|
|
6066
|
+
if (response.status === 404) {
|
|
6067
|
+
getLogger().info(`Position ${normalizedSymbol} not found in Alpaca (404) - may already be closed`, {
|
|
6068
|
+
account: auth.adapticAccountId || "direct",
|
|
6069
|
+
symbol: normalizedSymbol,
|
|
6070
|
+
});
|
|
6071
|
+
return null;
|
|
6072
|
+
}
|
|
6034
6073
|
throw new Error(`Failed to close position: ${response.status} ${response.statusText} ${errorText}`);
|
|
6035
6074
|
}
|
|
6036
6075
|
return (await response.json());
|
|
@@ -6078,8 +6117,8 @@ async function closeAllPositions$1(auth, params = { cancel_orders: true, useLimi
|
|
|
6078
6117
|
return [];
|
|
6079
6118
|
}
|
|
6080
6119
|
// Separate crypto and equity positions — crypto cannot use SIP quotes or time_in_force="day"
|
|
6081
|
-
const equityPositions = allPositions.filter((p) => !isCryptoSymbol(p.symbol));
|
|
6082
|
-
const cryptoPositions = allPositions.filter((p) => isCryptoSymbol(p.symbol));
|
|
6120
|
+
const equityPositions = allPositions.filter((p) => !isCryptoSymbol$1(p.symbol));
|
|
6121
|
+
const cryptoPositions = allPositions.filter((p) => isCryptoSymbol$1(p.symbol));
|
|
6083
6122
|
getLogger().info(`Found ${allPositions.length} positions to close (${equityPositions.length} equity, ${cryptoPositions.length} crypto)`, { account: auth.adapticAccountId || "direct" });
|
|
6084
6123
|
// Close crypto positions via direct DELETE (market order) — no SIP quotes needed
|
|
6085
6124
|
for (const position of cryptoPositions) {
|
|
@@ -6102,11 +6141,17 @@ async function closeAllPositions$1(auth, params = { cancel_orders: true, useLimi
|
|
|
6102
6141
|
}
|
|
6103
6142
|
else {
|
|
6104
6143
|
const errorText = await response.text();
|
|
6105
|
-
getLogger().warn(`Failed to close crypto position ${position.symbol}: ${response.status} ${errorText}`, {
|
|
6144
|
+
getLogger().warn(`Failed to close crypto position ${position.symbol}: ${response.status} ${errorText}`, {
|
|
6145
|
+
account: auth.adapticAccountId || "direct",
|
|
6146
|
+
symbol: position.symbol,
|
|
6147
|
+
});
|
|
6106
6148
|
}
|
|
6107
6149
|
}
|
|
6108
6150
|
catch (cryptoError) {
|
|
6109
|
-
getLogger().warn(`Error closing crypto position ${position.symbol}: ${cryptoError instanceof Error ? cryptoError.message : String(cryptoError)}`, {
|
|
6151
|
+
getLogger().warn(`Error closing crypto position ${position.symbol}: ${cryptoError instanceof Error ? cryptoError.message : String(cryptoError)}`, {
|
|
6152
|
+
account: auth.adapticAccountId || "direct",
|
|
6153
|
+
symbol: position.symbol,
|
|
6154
|
+
});
|
|
6110
6155
|
}
|
|
6111
6156
|
}
|
|
6112
6157
|
// Close equity positions via limit orders with SIP quotes
|
|
@@ -6199,8 +6244,8 @@ async function closeAllPositionsAfterHours$1(auth, params = { cancel_orders: tru
|
|
|
6199
6244
|
return;
|
|
6200
6245
|
}
|
|
6201
6246
|
// Separate crypto and equity positions
|
|
6202
|
-
const equityPositions = allPositions.filter((p) => !isCryptoSymbol(p.symbol));
|
|
6203
|
-
const cryptoPositions = allPositions.filter((p) => isCryptoSymbol(p.symbol));
|
|
6247
|
+
const equityPositions = allPositions.filter((p) => !isCryptoSymbol$1(p.symbol));
|
|
6248
|
+
const cryptoPositions = allPositions.filter((p) => isCryptoSymbol$1(p.symbol));
|
|
6204
6249
|
getLogger().info(`Found ${allPositions.length} positions to close after hours (${equityPositions.length} equity, ${cryptoPositions.length} crypto)`, { account: auth.adapticAccountId || "direct" });
|
|
6205
6250
|
if (cancel_orders) {
|
|
6206
6251
|
await cancelAllOrders$1(auth);
|
|
@@ -6229,7 +6274,10 @@ async function closeAllPositionsAfterHours$1(auth, params = { cancel_orders: tru
|
|
|
6229
6274
|
}
|
|
6230
6275
|
else {
|
|
6231
6276
|
const errorText = await response.text();
|
|
6232
|
-
getLogger().warn(`Failed to close crypto position ${position.symbol}: ${response.status} ${errorText}`, {
|
|
6277
|
+
getLogger().warn(`Failed to close crypto position ${position.symbol}: ${response.status} ${errorText}`, {
|
|
6278
|
+
account: auth.adapticAccountId || "direct",
|
|
6279
|
+
symbol: position.symbol,
|
|
6280
|
+
});
|
|
6233
6281
|
}
|
|
6234
6282
|
}
|
|
6235
6283
|
catch (cryptoError) {
|
|
@@ -7871,6 +7919,84 @@ const massiveLimit = pLimit(MASSIVE_CONCURRENCY_LIMIT);
|
|
|
7871
7919
|
* request settles, so subsequent calls after resolution make a fresh request.
|
|
7872
7920
|
*/
|
|
7873
7921
|
const fetchLastTradeInflight = new Map();
|
|
7922
|
+
/**
|
|
7923
|
+
* Check if a symbol is a crypto pair based on common patterns.
|
|
7924
|
+
* Crypto symbols typically end in USD, USDT, USDC, or contain a hyphen with USD.
|
|
7925
|
+
* Examples: BTCUSD, BTC-USD, BTC/USD, LINKUSD, SOL-USD
|
|
7926
|
+
*
|
|
7927
|
+
* @param symbol - The ticker symbol to check
|
|
7928
|
+
* @returns True if the symbol appears to be a crypto pair
|
|
7929
|
+
*/
|
|
7930
|
+
function isCryptoSymbol(symbol) {
|
|
7931
|
+
// Pattern: ends with USD/USDT/USDC and has 3-4 letter base (e.g., BTCUSD, LINKUSD)
|
|
7932
|
+
if (/^[A-Z]{2,5}(USD[TC]?)$/i.test(symbol)) {
|
|
7933
|
+
return true;
|
|
7934
|
+
}
|
|
7935
|
+
// Pattern: contains hyphen or slash with USD (e.g., BTC-USD, BTC/USD)
|
|
7936
|
+
if (/^[A-Z]{2,5}[-/]USD[TC]?$/i.test(symbol)) {
|
|
7937
|
+
return true;
|
|
7938
|
+
}
|
|
7939
|
+
// Pattern: already has X: prefix (e.g., X:BTC-USD)
|
|
7940
|
+
if (symbol.startsWith("X:")) {
|
|
7941
|
+
return true;
|
|
7942
|
+
}
|
|
7943
|
+
return false;
|
|
7944
|
+
}
|
|
7945
|
+
/**
|
|
7946
|
+
* Normalize a symbol for the Massive.com API.
|
|
7947
|
+
* Crypto symbols must be prefixed with "X:" and use hyphen format (e.g., X:BTC-USD).
|
|
7948
|
+
* Stock symbols are passed through unchanged.
|
|
7949
|
+
*
|
|
7950
|
+
* @param symbol - The raw ticker symbol
|
|
7951
|
+
* @returns The symbol formatted for Massive.com API
|
|
7952
|
+
*/
|
|
7953
|
+
function normalizeMassiveSymbol(symbol) {
|
|
7954
|
+
// If already has X: prefix, ensure hyphen format
|
|
7955
|
+
if (symbol.startsWith("X:")) {
|
|
7956
|
+
return symbol;
|
|
7957
|
+
}
|
|
7958
|
+
// Check if it's a crypto symbol
|
|
7959
|
+
if (!isCryptoSymbol(symbol)) {
|
|
7960
|
+
return symbol; // Stock symbol - return unchanged
|
|
7961
|
+
}
|
|
7962
|
+
// Normalize crypto symbol to X:BASE-QUOTE format
|
|
7963
|
+
// Handle formats: BTCUSD, BTC-USD, BTC/USD -> X:BTC-USD
|
|
7964
|
+
let base;
|
|
7965
|
+
let quote;
|
|
7966
|
+
if (symbol.includes("-")) {
|
|
7967
|
+
// Format: BTC-USD
|
|
7968
|
+
const parts = symbol.split("-");
|
|
7969
|
+
base = parts[0];
|
|
7970
|
+
quote = parts.slice(1).join("-");
|
|
7971
|
+
}
|
|
7972
|
+
else if (symbol.includes("/")) {
|
|
7973
|
+
// Format: BTC/USD
|
|
7974
|
+
const parts = symbol.split("/");
|
|
7975
|
+
base = parts[0];
|
|
7976
|
+
quote = parts.slice(1).join("/");
|
|
7977
|
+
}
|
|
7978
|
+
else {
|
|
7979
|
+
// Format: BTCUSD - need to extract base and quote
|
|
7980
|
+
// Common quote currencies: USD, USDT, USDC
|
|
7981
|
+
if (symbol.endsWith("USDT")) {
|
|
7982
|
+
base = symbol.slice(0, -4);
|
|
7983
|
+
quote = "USDT";
|
|
7984
|
+
}
|
|
7985
|
+
else if (symbol.endsWith("USDC")) {
|
|
7986
|
+
base = symbol.slice(0, -4);
|
|
7987
|
+
quote = "USDC";
|
|
7988
|
+
}
|
|
7989
|
+
else if (symbol.endsWith("USD")) {
|
|
7990
|
+
base = symbol.slice(0, -3);
|
|
7991
|
+
quote = "USD";
|
|
7992
|
+
}
|
|
7993
|
+
else {
|
|
7994
|
+
// Unknown format, return as-is
|
|
7995
|
+
return symbol;
|
|
7996
|
+
}
|
|
7997
|
+
}
|
|
7998
|
+
return `X:${base.toUpperCase()}-${quote.toUpperCase()}`;
|
|
7999
|
+
}
|
|
7874
8000
|
// Use to update general information about stocks
|
|
7875
8001
|
/**
|
|
7876
8002
|
* Fetches general information about a stock ticker.
|
|
@@ -7992,7 +8118,9 @@ const fetchLastTradeImpl = async (symbol, options) => {
|
|
|
7992
8118
|
}
|
|
7993
8119
|
const apiKey = options?.apiKey || MASSIVE_API_KEY;
|
|
7994
8120
|
validateMassiveApiKey$1(apiKey);
|
|
7995
|
-
|
|
8121
|
+
// Normalize crypto symbols to Massive.com API format (e.g., LINK-USD -> X:LINK-USD)
|
|
8122
|
+
const normalizedSymbol = normalizeMassiveSymbol(symbol);
|
|
8123
|
+
const baseUrl = `https://api.massive.com/v3/trades/${encodeURIComponent(normalizedSymbol)}`;
|
|
7996
8124
|
const params = new URLSearchParams({
|
|
7997
8125
|
apiKey,
|
|
7998
8126
|
limit: "1",
|
|
@@ -8028,9 +8156,10 @@ const fetchLastTradeImpl = async (symbol, options) => {
|
|
|
8028
8156
|
}
|
|
8029
8157
|
catch (error) {
|
|
8030
8158
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
8031
|
-
const contextualMessage = `Error fetching last trade for ${symbol}`;
|
|
8159
|
+
const contextualMessage = `Error fetching last trade for ${symbol}${normalizedSymbol !== symbol ? ` (normalized: ${normalizedSymbol})` : ""}`;
|
|
8032
8160
|
getLogger().error(`${contextualMessage}: ${errorMessage}`, {
|
|
8033
8161
|
symbol,
|
|
8162
|
+
normalizedSymbol,
|
|
8034
8163
|
errorType: error instanceof Error && error.message.includes("AUTH_ERROR")
|
|
8035
8164
|
? "AUTH_ERROR"
|
|
8036
8165
|
: error instanceof Error && error.message.includes("RATE_LIMIT")
|
|
@@ -8725,10 +8854,11 @@ const calculateFees = async (action, trade, alpacaAccount) => {
|
|
|
8725
8854
|
};
|
|
8726
8855
|
const computeTotalFees = async (trade) => {
|
|
8727
8856
|
let totalFees = 0;
|
|
8728
|
-
//
|
|
8857
|
+
// Use the shared singleton Apollo client to avoid creating orphaned connections
|
|
8858
|
+
const client = await getSharedApolloClient();
|
|
8729
8859
|
const alpacaAccount = (await adaptic$1.alpacaAccount.get({
|
|
8730
8860
|
id: trade.alpacaAccountId,
|
|
8731
|
-
}));
|
|
8861
|
+
}, client));
|
|
8732
8862
|
if (!alpacaAccount)
|
|
8733
8863
|
return totalFees;
|
|
8734
8864
|
const feePromises = trade?.actions?.map((action) => calculateFees(action, trade, alpacaAccount));
|
|
@@ -67783,6 +67913,7 @@ const adaptic = {
|
|
|
67783
67913
|
getApolloClient: getSharedApolloClient,
|
|
67784
67914
|
configureAuth: configureAuth,
|
|
67785
67915
|
isAuthConfigured: isAuthConfigured,
|
|
67916
|
+
disconnectClient: disconnectClient,
|
|
67786
67917
|
},
|
|
67787
67918
|
alpaca: {
|
|
67788
67919
|
// New SDK-based client factory (RECOMMENDED)
|