Skip to content

fix(liquidity): recover Scrypt trade orders from transient WS errors#3879

Draft
TaprootFreak wants to merge 1 commit into
developfrom
fix/scrypt-order-transient-error-recovery
Draft

fix(liquidity): recover Scrypt trade orders from transient WS errors#3879
TaprootFreak wants to merge 1 commit into
developfrom
fix/scrypt-order-transient-error-recovery

Conversation

@TaprootFreak

Copy link
Copy Markdown
Collaborator

Problem

When the Scrypt WebSocket connection drops or a request times out after a trade order (sell/buy) has been sent, the pending request is rejected and the liquidity management order is marked as Failed. The locally generated ClOrdID is lost (correlationId stays null), even though Scrypt may have accepted and executed the order. The resulting trades are untracked, and the financial log counts the liquidity as pending indefinitely.

Fix

  • Persist before send: the ClOrdID is generated in the adapter and saved on the order (correlationId) before the WS request goes out, so the trade stays verifiable in every failure mode.
  • Recoverable errors keep the order in progress: transient WS errors (Connection closed, unknown reqid) and request timeouts during placement and completion checks no longer fail the order. The order transitions to InProgress and the existing completion check resolves the actual state via execution reports — filled orders complete with their output, orders that were never placed still fail after the existing 60 min cutoff (now anchored on order.updated, i.e. placement time).
  • Re-execution guard: if an order already has a correlationId (retry after crash or recoverable error), the adapter first checks for an existing trade by ClOrdID before placing a new one — preventing duplicate trades. The guard runs before the price/balance checks, since an open order locks the balance.
  • Pagination fix: getOrderStatus now fetches all execution report pages (fetchAll) instead of only the first page, which the guard and completion check depend on.

Tests

New scrypt.adapter.spec.ts with 12 cases: persist-before-send ordering, recoverable placement errors (connection closed + both timeout variants), recoverable completion-check errors, rejected/insufficient-funds passthrough, idempotent re-execution (guard hit, guard miss with ClOrdID chaining, recoverable guard failure with and without existing correlationId).

Out of scope (follow-up)

The same pattern (ClOrdID/ClReqID generated without persistence before send) still exists in the withdrawal path and in the checkTrade cancel-restart/edit paths.

Scrypt sell/buy orders were marked as failed when the WebSocket
connection dropped or timed out after the order was sent, losing the
locally generated ClOrdID even though the exchange had executed the
trade.

- generate the ClOrdID in the adapter and persist it on the order
  before sending, so the trade stays verifiable
- treat transient WS errors and request timeouts during placement and
  completion checks as recoverable: keep the order in progress and let
  the completion check resolve the actual state (lost orders still fail
  after the existing 60 min cutoff)
- add a re-execution guard that checks for an existing trade by
  ClOrdID before placing a new order, preventing duplicate trades on
  retries
- fetch all execution report pages in getOrderStatus instead of only
  the first page
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant