fix: cancel all open orders before sell to release CLOB locked balance
Root cause: auto-sell GTC orders lock all shares in the CLOB's internal
ledger. When executeSell runs, it tried to cancel only position.sellOrderId
but this can fail silently, leaving tokens locked. The CLOB then rejects
the new sell with "not enough balance or allowance" because the balance
is committed to the existing GTC order.
Fix:
- Fetch all open orders for the specific tokenId via client.getOpenOrders()
- Cancel all of them with Promise.allSettled (non-fatal per order)
- Wait 600ms after cancellation so the CLOB updates its locked-balance
ledger before we place the new sell
- Fallback: if getOpenOrders fails, still attempt to cancel by
position.sellOrderId (previous behaviour) then wait 600ms
- Use correct cancelOrder({ orderID }) object form throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
ec2c948a00
commit
097148c501
@@ -306,15 +306,29 @@ export async function executeSell(trade) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel existing auto-sell order if any
|
||||
if (position.sellOrderId) {
|
||||
try {
|
||||
const client = getClient();
|
||||
await client.cancelOrder(position.sellOrderId);
|
||||
logger.info(`Cancelled auto-sell order: ${position.sellOrderId}`);
|
||||
} catch (err) {
|
||||
logger.warn(`Failed to cancel auto-sell: ${err.message}`);
|
||||
// Cancel ALL open orders for this token so the CLOB frees up locked balance.
|
||||
// Only cancelling by sellOrderId is not enough — the cancel can fail silently
|
||||
// and locked tokens cause "not enough balance" on the subsequent sell.
|
||||
const client = getClient();
|
||||
try {
|
||||
const openOrders = await client.getOpenOrders({ asset_id: tokenId });
|
||||
if (Array.isArray(openOrders) && openOrders.length > 0) {
|
||||
logger.info(`Cancelling ${openOrders.length} open order(s) for token before sell`);
|
||||
await Promise.allSettled(
|
||||
openOrders.map((o) => client.cancelOrder({ orderID: o.id ?? o.order_id })),
|
||||
);
|
||||
// Brief pause so the CLOB can update the locked-balance ledger
|
||||
await new Promise((r) => setTimeout(r, 600));
|
||||
}
|
||||
} catch (err) {
|
||||
// Fallback: try to cancel just the tracked auto-sell order ID
|
||||
if (position.sellOrderId) {
|
||||
try {
|
||||
await client.cancelOrder({ orderID: position.sellOrderId });
|
||||
await new Promise((r) => setTimeout(r, 600));
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
logger.warn(`Could not fetch open orders to cancel: ${err.message}`);
|
||||
}
|
||||
|
||||
updatePosition(effectiveConditionId, { status: 'selling' });
|
||||
|
||||
Reference in New Issue
Block a user