@adaptic/utils 0.0.946 → 0.0.948
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 +80 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +80 -65
- package/dist/index.mjs.map +1 -1
- package/dist/types/alpaca/legacy/positions.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5837,75 +5837,90 @@ async function closePosition$1(auth, symbolOrAssetId, params) {
|
|
|
5837
5837
|
// Crypto positions cannot use limit orders with SIP quotes or time_in_force="day".
|
|
5838
5838
|
// Use direct DELETE (market order) for crypto regardless of useLimitOrder flag.
|
|
5839
5839
|
if (useLimitOrder && !isCryptoSymbol(symbolOrAssetId)) {
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5840
|
+
// Attempt limit order closure; if quotes are unavailable (after-hours, IEX gaps),
|
|
5841
|
+
// fall back to market order (DELETE) so the position still gets closed.
|
|
5842
|
+
try {
|
|
5843
|
+
const { position } = await fetchPosition(auth, symbolOrAssetId);
|
|
5844
|
+
if (!position) {
|
|
5845
|
+
throw new Error(`Position not found for ${symbolOrAssetId}`);
|
|
5846
|
+
}
|
|
5847
|
+
// Use the passed auth for quote fetching so multi-account setups
|
|
5848
|
+
// use the correct credentials per account
|
|
5849
|
+
const quotesResponse = await getLatestQuotes$1(auth, {
|
|
5850
|
+
symbols: [symbolOrAssetId],
|
|
5851
|
+
});
|
|
5852
|
+
const quote = quotesResponse.quotes[symbolOrAssetId];
|
|
5853
|
+
if (!quote) {
|
|
5854
|
+
throw new Error(`No quote available for ${symbolOrAssetId}`);
|
|
5855
|
+
}
|
|
5856
|
+
let qty = Math.abs(parseFloat(position.qty));
|
|
5857
|
+
if (params?.qty !== undefined) {
|
|
5858
|
+
qty = params.qty;
|
|
5859
|
+
}
|
|
5860
|
+
else if (params?.percentage !== undefined) {
|
|
5861
|
+
qty = Math.abs(parseFloat(position.qty)) * (params.percentage / 100);
|
|
5862
|
+
}
|
|
5863
|
+
const side = position.side === "long" ? "sell" : "buy";
|
|
5864
|
+
const positionIntent = side === "sell" ? "sell_to_close" : "buy_to_close";
|
|
5865
|
+
const currentPrice = side === "sell" ? quote.bp : quote.ap;
|
|
5866
|
+
if (!currentPrice) {
|
|
5867
|
+
throw new Error(`No valid price available for ${symbolOrAssetId}`);
|
|
5868
|
+
}
|
|
5869
|
+
const limitSlippage = slippagePercent1 / 100;
|
|
5870
|
+
const limitPrice = side === "sell"
|
|
5871
|
+
? roundPriceForAlpaca$5(currentPrice * (1 - limitSlippage))
|
|
5872
|
+
: roundPriceForAlpaca$5(currentPrice * (1 + limitSlippage));
|
|
5873
|
+
getLogger().info(`Creating limit order to close ${symbolOrAssetId} position: ${side} ${qty} shares at ${limitPrice.toFixed(2)}`, {
|
|
5874
|
+
account: auth.adapticAccountId || "direct",
|
|
5875
|
+
symbol: symbolOrAssetId,
|
|
5876
|
+
});
|
|
5877
|
+
return await createLimitOrder(auth, {
|
|
5878
|
+
symbol: symbolOrAssetId,
|
|
5879
|
+
qty,
|
|
5880
|
+
side,
|
|
5881
|
+
limitPrice,
|
|
5882
|
+
position_intent: positionIntent,
|
|
5883
|
+
extended_hours: extendedHours,
|
|
5884
|
+
});
|
|
5859
5885
|
}
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5886
|
+
catch (limitOrderError) {
|
|
5887
|
+
// Quote unavailable or invalid price — fall back to market order (DELETE)
|
|
5888
|
+
// so the position still gets closed rather than leaving it open
|
|
5889
|
+
const errMsg = limitOrderError instanceof Error ? limitOrderError.message : String(limitOrderError);
|
|
5890
|
+
getLogger().warn(`Limit order closure failed for ${symbolOrAssetId} (${errMsg}), falling back to market order`, {
|
|
5891
|
+
account: auth.adapticAccountId || "direct",
|
|
5892
|
+
symbol: symbolOrAssetId,
|
|
5893
|
+
type: "warn",
|
|
5894
|
+
});
|
|
5895
|
+
// Fall through to the DELETE (market order) path below
|
|
5865
5896
|
}
|
|
5866
|
-
const limitSlippage = slippagePercent1 / 100;
|
|
5867
|
-
const limitPrice = side === "sell"
|
|
5868
|
-
? roundPriceForAlpaca$5(currentPrice * (1 - limitSlippage))
|
|
5869
|
-
: roundPriceForAlpaca$5(currentPrice * (1 + limitSlippage));
|
|
5870
|
-
getLogger().info(`Creating limit order to close ${symbolOrAssetId} position: ${side} ${qty} shares at ${limitPrice.toFixed(2)}`, {
|
|
5871
|
-
account: auth.adapticAccountId || "direct",
|
|
5872
|
-
symbol: symbolOrAssetId,
|
|
5873
|
-
});
|
|
5874
|
-
return await createLimitOrder(auth, {
|
|
5875
|
-
symbol: symbolOrAssetId,
|
|
5876
|
-
qty,
|
|
5877
|
-
side,
|
|
5878
|
-
limitPrice,
|
|
5879
|
-
position_intent: positionIntent,
|
|
5880
|
-
extended_hours: extendedHours,
|
|
5881
|
-
});
|
|
5882
5897
|
}
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
}
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
if (params?.percentage !== undefined) {
|
|
5892
|
-
queryParams.append("percentage", params.percentage.toString());
|
|
5893
|
-
}
|
|
5894
|
-
const queryString = queryParams.toString();
|
|
5895
|
-
const url = `${apiBaseUrl}/positions/${encodeURIComponent(symbolOrAssetId)}${queryString ? `?${queryString}` : ""}`;
|
|
5896
|
-
const response = await fetch(url, {
|
|
5897
|
-
method: "DELETE",
|
|
5898
|
-
headers: {
|
|
5899
|
-
"APCA-API-KEY-ID": APIKey,
|
|
5900
|
-
"APCA-API-SECRET-KEY": APISecret,
|
|
5901
|
-
},
|
|
5902
|
-
});
|
|
5903
|
-
if (!response.ok) {
|
|
5904
|
-
const errorText = await response.text();
|
|
5905
|
-
throw new Error(`Failed to close position: ${response.status} ${response.statusText} ${errorText}`);
|
|
5906
|
-
}
|
|
5907
|
-
return (await response.json());
|
|
5898
|
+
// Market order (DELETE) path — used when limit orders are not requested,
|
|
5899
|
+
// for crypto symbols, or as a fallback when limit order quotes are unavailable
|
|
5900
|
+
if (isCryptoSymbol(symbolOrAssetId)) {
|
|
5901
|
+
getLogger().info(`Closing crypto position ${symbolOrAssetId} via market order (DELETE endpoint)`, { account: auth.adapticAccountId || "direct", symbol: symbolOrAssetId });
|
|
5902
|
+
}
|
|
5903
|
+
const queryParams = new URLSearchParams();
|
|
5904
|
+
if (params?.qty !== undefined) {
|
|
5905
|
+
queryParams.append("qty", params.qty.toString());
|
|
5908
5906
|
}
|
|
5907
|
+
if (params?.percentage !== undefined) {
|
|
5908
|
+
queryParams.append("percentage", params.percentage.toString());
|
|
5909
|
+
}
|
|
5910
|
+
const queryString = queryParams.toString();
|
|
5911
|
+
const url = `${apiBaseUrl}/positions/${encodeURIComponent(symbolOrAssetId)}${queryString ? `?${queryString}` : ""}`;
|
|
5912
|
+
const response = await fetch(url, {
|
|
5913
|
+
method: "DELETE",
|
|
5914
|
+
headers: {
|
|
5915
|
+
"APCA-API-KEY-ID": APIKey,
|
|
5916
|
+
"APCA-API-SECRET-KEY": APISecret,
|
|
5917
|
+
},
|
|
5918
|
+
});
|
|
5919
|
+
if (!response.ok) {
|
|
5920
|
+
const errorText = await response.text();
|
|
5921
|
+
throw new Error(`Failed to close position: ${response.status} ${response.statusText} ${errorText}`);
|
|
5922
|
+
}
|
|
5923
|
+
return (await response.json());
|
|
5909
5924
|
}
|
|
5910
5925
|
catch (error) {
|
|
5911
5926
|
getLogger().error("Error in closePosition:", error);
|