T09 · Insecure Skill Coding Practices
- Location
trader.js:86- Finding
Leveraged Position Can Remain Open Without Protective Orders
- Content
View full analysis
Vulnerability Details
File Location:
trader.js, lines 86-114
Vulnerability Type: Failure to safely handle partial trade execution
Risk Level: HighVulnerable Code
js // 开仓 const size = Math.min(available * 0.9, 100); await exchange.setLeverage(CONFIG.LEVERAGE, best.symbol + '/USDT:USDT'); const qty = (size * CONFIG.LEVERAGE) / best.price; await exchange.createOrder(best.symbol + '/USDT:USDT', 'market', 'buy', qty, null, { positionSide: 'LONG' }); console.log('\n✅ 开仓:', qty.toFixed(4), best.symbol, '@', best.price); // 等待 await new Promise(r => setTimeout(r, 1500)); // 设置止损止盈 const positions = await exchange.fetchPositions([best.symbol + '/USDT:USDT']); const pos = positions.find(p => p.contracts > 0); if (pos) { const stopLoss = best.price * (1 - CONFIG.STOP_LOSS); const takeProfit = best.price * (1 + CONFIG.TAKE_PROFIT); await exchange.createOrder(best.symbol + '/USDT:USDT', 'STOP_MARKET', 'sell', pos.contracts, null, { positionSide: 'LONG', triggerPrice: stopLoss }); await exchange.createOrder(best.symbol + '/USDT:USDT', 'TAKE_PROFIT_MARKET', 'sell', pos.contracts, null, { positionSide: 'LONG', triggerPrice: takeProfit }); }Technical Analysis
The script opens a market position at 20× leverage before submitting either protective exit order. It then deliberately waits 1.5 seconds, requests position information, and submits the stop-loss and take-profit as separate API operations.
These operations are not atomic. The entry can succeed while any subsequent operation fails because of process termination, network interruption, exchange timeout, rate limiting, invalid order parameters, stale position information, or an API rejection. The
poslookup can also fail to identify the newly opened position, in which case no protective orders are created.If the first protective order succeeds but the second fails, the position is ...[truncated 1664 chars]
- Remediation
View remediation
Remediation Suggestions
- Treat entry and protection as a stateful transaction with explicit recovery behavior.
- Validate symbol precision, quantity, position mode, trigger prices, and supported order parameters before opening the position.
- Remove the unconditional 1.5-second exposure window and submit protection immediately after confirming the executed entry price and quantity.
- Confirm that both protective orders were accepted and persist their order IDs.
- If either protective order fails, cancel any successfully submitted sibling order and immediately close the actual remaining position with a reduce-only market order.
- Add bounded retries with backoff for transient position-query and order-submission failures.
- Re-fetch the exchange position after every failure rather than relying on local assumptions.
- Install signal and shutdown handlers that attempt safe reconciliation without assuming that a local exception means the exchange operation failed.
- On startup, reconcile existing positions and open orders so that a previous crash cannot leave exposure unmanaged.
- Use the actual average entry price returned by the exchange rather than the earlier ticker price when calculating protective triggers.
