-
Notifications
You must be signed in to change notification settings - Fork 178
Description
Problem
On Feb 18 2026, a single ONDO/USDC short position ($15.75) triggered 436 on-chain transactions in 17 hours, draining ~$50 in ETH gas fees. The bot kept retrying the same doomed close order every ~15 seconds with no backoff or retry limit.
What happened
- Ichimoku strategy fires
senkou_a_4h_exit_shortsignal for ONDO - Bot submits
create_order(market buy, reduceOnly=True)→ on-chain tx succeeds (gas consumed) - GMX keeper attempts execution → contract rejects → emits
OrderCancelledevent - Bot raises
InvalidOrder, freqtrade logs "Unable to exit trade", trade stays open - Next cycle (~15s later), same exit signal fires, bot retries the exact same order
- Repeat 436 times across 3 ETH top-ups until wallet is empty
The cancellations came in bursts, each draining a top-up within minutes:
| Burst | Duration | On-chain txs | ETH burned |
|---|---|---|---|
| 1 | 3 min | 11 | ~0.001 ETH |
| 2 | 35 min | 107 | ~0.010 ETH |
| 3 | 2h 15min | 319 | ~0.010 ETH |
Why we don't know the cancel reason
Every cancellation logs the same generic message: "Order ordercancelled". The actual GMX error (e.g. OrderNotFulfillableAtAcceptablePrice, MinPositionSize) is encoded in the reasonBytes field of the OrderCancelled event but is not being decoded in the exchange polling path. The decoder exists (decode_error_reason() in events.py) but isn't wired into the CCXT adapter's event processing.
What we'd like to see
-
Circuit breaker: After N consecutive keeper cancellations for the same trade (e.g. 3-5), stop retrying and either skip the pair for a cooldown period or alert. The bot should never burn gas on the same failing order hundreds of times.
-
Decode cancel reasons: Wire
decode_error_reason()into the CCXT adapter'sOrderCancelledevent handling so we get actionable errors likeOrderNotFulfillableAtAcceptablePrice(price=274100, acceptablePrice=273280)instead of the generic fallback. -
Per-failure response: Once we have the actual reason, the bot can respond appropriately — e.g. increase slippage for
OrderNotFulfillableAtAcceptablePrice, skip pair forMinPositionSize, etc.