fix(@typedtrader/exchange): send client order ids and stop retrying order placement POSTs#1178
Open
bennycode wants to merge 1 commit into
Open
fix(@typedtrader/exchange): send client order ids and stop retrying order placement POSTs#1178bennycode wants to merge 1 commit into
bennycode wants to merge 1 commit into
Conversation
…rder placement POSTs
Contributor
There was a problem hiding this comment.
Pull request overview
This PR reduces the risk of duplicate real-money positions by preventing automatic retries on non-idempotent order-placement POST requests, and (for Alpaca) adding a client_order_id to enable server-side deduplication and operator reconciliation.
Changes:
- Trading212: extracted
shouldRetryTrading212Requestand disabled retries for market/limit order placement POSTs while keeping existing retry behavior for all other requests. - Alpaca: disabled retries for
POST /v2/ordersand requiredclient_order_idinpostOrderparams. - AlpacaBroker: generates and sends a fresh UUIDv4
client_order_idfor every order placement; added/updated tests to cover both retry behavior and UUID propagation.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| packages/exchange/src/broker/trading212/api/Trading212API.ts | Stops retries for order-placement POSTs; keeps existing retry logic for other endpoints. |
| packages/exchange/src/broker/trading212/api/Trading212API.test.ts | Adds coverage ensuring order-placement POSTs are not retried while other requests remain retryable. |
| packages/exchange/src/broker/alpaca/api/AlpacaAPI.ts | Disables retries for POST /v2/orders and makes client_order_id required for postOrder. |
| packages/exchange/src/broker/alpaca/api/AlpacaAPI.test.ts | Adds tests validating retry suppression for order placement and retry retention for GET/DELETE. |
| packages/exchange/src/broker/alpaca/AlpacaBroker.ts | Generates and sends client_order_id: randomUUID() on every order placement. |
| packages/exchange/src/broker/alpaca/AlpacaBroker.test.ts | Validates UUID shape is passed to the API and is unique across placements. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Eliminates a duplicate-order risk from blind retries of order placement.
The scenario: both broker API clients wire
axios-retrywith a method-blind retry condition (Alpaca: 20 retries; Trading212:retries: Infinity). When an order-placement POST times out or returns a 5xx, the order may still have been accepted server-side — the failure only means the client never saw the response. A blind retry then re-submits the same order and can silently open a duplicate real-money position. Order placement is not idempotent, so it must never be auto-retried.Changes:
AlpacaBroker.placeOrder): every placement now generates aclient_order_idviacrypto.randomUUID()and sends it in thePOST /v2/ordersbody. Alpaca deduplicates on this id, so a manual re-submission with the same id is safe, and operators can reconcile whether a failed request actually landed. (docs)shouldRetryAlpacaRequest):POST /v2/ordersis never retried. GETs/DELETEs (including reads of/v2/ordersand order cancellations) keep the existing retry behavior, and the PDT (40310100) / no-short (40310000) business-error filtering is unchanged.shouldRetryTrading212Request, extracted + exported): POSTs to/api/v0/equity/orders/marketand/api/v0/equity/orders/limitare never retried — Trading212's public API has no idempotency-key support, so a duplicate would be unrecoverable. All other requests keep today's behavior, including the per-endpoint rate-limit delays ingetRetryDelay.A failed order POST now surfaces immediately as an error to the caller (TradingSession already converts it to an
errorevent; that path is untouched). Public package types are unchanged — neither API class is exported fromsrc/index.ts, sopostOrdercould gain a requiredclient_order_idparam to enforce the invariant at compile time.Test plan
AlpacaAPI.test.ts: order POST not retried on 503/429/network errors (incl. uppercase method); GET/DELETE on the same endpoint still retried; 429/503/EAI_AGAIN on other requests still retried; PDT/no-short rejections still not retriedTrading212API.test.ts(new): market/limit order POSTs not retried on 503/network errors; GET/DELETE order endpoints and other requests keep retry behavior; HTTP 400 not retriedAlpacaBroker.test.ts:placeOrderpasses aclient_order_idmatching the UUIDv4 shape on the mocked API call; unique across two placementspackages/exchange:npm test— 9 files, 103 tests passedpackages/exchange:npm run lint— cleannpx lerna run dist --scope trading-strategies --include-dependencies— 3 projects built